Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2198 lines
62 KiB

/////////////////////////////////////////////////////////////////////////////
// Copyright (C) 1993-1998 Microsoft Corporation. All Rights Reserved.
//
// MODULE: outbar.cpp
//
// PURPOSE: Implements the Outlook Bar
//
#include "pch.hxx"
#include "resource.h"
#include "outbar.h"
#include "goptions.h"
#include "ourguid.h"
#include <inpobj.h>
#include <browser.h>
#include <notify.h>
#include <strconst.h>
#include <thormsgs.h>
#include <shlwapi.h>
#include "shlwapip.h"
#include "storutil.h"
#include "menures.h"
#include "menuutil.h"
#include "dragdrop.h"
#include "newfldr.h"
#include "finder.h"
#include "instance.h"
ASSERTDATA
#define IDC_FRAME 100
#define IDC_PAGER 101
#define IDC_TOOLBAR 102
#define HT_ENTER 1
#define HT_OVER 2
#define HT_LEAVE 3
// Special HitTest results
#define IBHT_SOURCE (-32768)
#define IBHT_BACKGROUND (-32767)
#define IBHT_PAGER (-32766)
////////////////////////////////////////////////////////////////////////
//
// Prototypes
//
////////////////////////////////////////////////////////////////////////
HRESULT OutlookBar_LoadSettings(BAR_PERSIST_INFO **ppPersist);
HRESULT OutlookBar_SaveSettings(BAR_PERSIST_INFO *pPersist, DWORD cbData);
extern DWORD CUnread(FOLDERINFO *pfi);
////////////////////////////////////////////////////////////////////////
//
// Module Data
//
////////////////////////////////////////////////////////////////////////
static const TCHAR s_szOutBarWndClass[] = TEXT("Outlook Express Outlook Bar");
static const TCHAR s_szOutBarFrameClass[] = TEXT("Outlook Express Outlook Bar Frame");
static const TCHAR c_szOutBarNotifyName[] = TEXT("Outlook Express Outlook Bar Notify");
////////////////////////////////////////////////////////////////////////
//
// Constructors, Destructors, and other initialization stuff
//
////////////////////////////////////////////////////////////////////////
COutBar::COutBar()
{
m_cRef = 1;
m_hwndParent = NULL;
m_hwnd = NULL;
m_hwndFrame = NULL;
m_hwndPager = NULL;
m_hwndTools = NULL;
m_ptbSite = NULL;
m_fShow = FALSE;
m_pBrowser = NULL;
m_pStNotify = NULL;
m_idCommand = 0;
m_fResizing = FALSE;
m_idSel = -1;
// load the width from resource
m_cxWidth = 70;
TCHAR szBuffer[64];
if (AthLoadString(idsMaxOutbarBtnWidth, szBuffer, ARRAYSIZE(szBuffer)))
{
m_cxWidth = StrToInt(szBuffer);
if (m_cxWidth == 0)
m_cxWidth = 70;
}
m_fLarge = TRUE;
m_himlLarge = NULL;
m_himlSmall = NULL;
m_pOutBarNotify = NULL;
m_pDataObject = NULL;
m_grfKeyState = 0;
m_dwEffectCur = DROPEFFECT_NONE;
m_idCur = -1;
m_pTargetCur = NULL;
m_idDropHilite = 0;
m_fInsertMark = FALSE;
m_fOnce = TRUE;
}
COutBar::~COutBar()
{
Assert(NULL == m_pStNotify);
if (m_hwnd)
DestroyWindow(m_hwnd);
if (m_himlLarge)
ImageList_Destroy(m_himlLarge);
if (m_himlSmall)
ImageList_Destroy(m_himlSmall);
SafeRelease(m_pDataObject);
}
HRESULT COutBar::HrInit(LPSHELLFOLDER psf, IAthenaBrowser *psb)
{
HRESULT hr;
m_pBrowser = psb;
hr = CreateNotify(&m_pStNotify);
if (FAILED(hr))
return(hr);
return m_pStNotify->Initialize((TCHAR *)c_szMailFolderNotify);
}
////////////////////////////////////////////////////////////////////////
//
// IUnknown
//
////////////////////////////////////////////////////////////////////////
HRESULT COutBar::QueryInterface(REFIID riid, LPVOID * ppvObj)
{
if (IsEqualIID(riid, IID_IUnknown) ||
IsEqualIID(riid, IID_IOleWindow) ||
IsEqualIID(riid, IID_IDockingWindow) ||
IsEqualIID(riid, IID_IDatabaseNotify))
{
*ppvObj = (void*)(IDockingWindow*)this;
}
else if (IsEqualIID(riid, IID_IObjectWithSite))
{
*ppvObj = (void*)(IObjectWithSite*)this;
}
else
{
*ppvObj = NULL;
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
ULONG COutBar::AddRef()
{
return ++m_cRef;
}
ULONG COutBar::Release()
{
if (--m_cRef == 0)
{
delete this;
return 0;
}
return m_cRef;
}
////////////////////////////////////////////////////////////////////////
//
// IOleWindow
//
////////////////////////////////////////////////////////////////////////
HRESULT COutBar::GetWindow(HWND * lphwnd)
{
*lphwnd = m_hwnd;
return (*lphwnd ? S_OK : E_FAIL);
}
HRESULT COutBar::ContextSensitiveHelp(BOOL fEnterMode)
{
return E_NOTIMPL;
}
//
// FUNCTION: COutBar::ShowDW()
//
// PURPOSE: Causes the bar to be displayed. If it has not yet been
// created, we do that here too.
//
// PARAMETERS:
// [in] fShow - TRUE to make the bar visible, FALSE to hide.
//
// RETURN VALUE:
// HRESULT
//
HRESULT COutBar::ShowDW(BOOL fShow)
{
// Make sure we have a site pointer first
if (!m_ptbSite)
{
AssertSz(0, _T("COutBar::ShowDW() - Can't show without calling SetSite() first."));
return E_FAIL;
}
// Decide if we need to create a new window or show a currently existing
// window
if (!m_hwnd)
{
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
if (!GetClassInfoEx(g_hInst, s_szOutBarWndClass, &wc))
{
// We need to register the outlook bar class
wc.style = 0;
wc.lpfnWndProc = COutBar::OutBarWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = g_hInst;
wc.hCursor = LoadCursor(NULL, IDC_SIZEWE);
wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = s_szOutBarWndClass;
wc.hIcon = NULL;
wc.hIconSm = NULL;
if (RegisterClassEx(&wc) == 0 && GetLastError() != ERROR_CLASS_ALREADY_EXISTS)
return E_FAIL;
// Also need to register the frame class
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpfnWndProc = COutBar::ExtFrameWndProc;
wc.lpszClassName = s_szOutBarFrameClass;
wc.hbrBackground = (HBRUSH)(COLOR_3DSHADOW + 1);
if (RegisterClassEx(&wc) == 0 && GetLastError() != ERROR_CLASS_ALREADY_EXISTS)
return E_FAIL;
}
// Get the handle of the parent window
if (FAILED(m_ptbSite->GetWindow(&m_hwndParent)))
return E_FAIL;
// Create the window
m_hwnd = CreateWindowEx(0, s_szOutBarWndClass, NULL, WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
0, 0, 0, 0, m_hwndParent, NULL, g_hInst, (LPVOID) this);
if (!m_hwnd)
{
AssertSz(0, _T("COutBar::ShowDW() - Failed to create window."));
return E_FAIL;
}
if (FAILED(_CreateToolbar()))
return E_FAIL;
}
// Show or hide the window and resize the parent windows accordingly
m_fShow = fShow;
ResizeBorderDW(NULL, NULL, FALSE);
ShowWindow(m_hwnd, fShow ? SW_SHOW : SW_HIDE);
// Do notifications
if (SUCCEEDED(CreateNotify(&m_pOutBarNotify)))
{
if (SUCCEEDED(m_pOutBarNotify->Initialize(c_szOutBarNotifyName)))
{
m_pOutBarNotify->Register(m_hwnd, g_hwndInit, FALSE);
}
}
// Drag Drop
RegisterDragDrop(m_hwndTools, this);
g_pStore->RegisterNotify(IINDEX_SUBSCRIBED, REGISTER_NOTIFY_NOADDREF, 0, (IDatabaseNotify *)this);
return S_OK;
}
//
// FUNCTION: COutBar::CloseDW()
//
// PURPOSE: Destroys the bar and cleans up.
//
HRESULT COutBar::CloseDW(DWORD dwReserved)
{
// Save our settings
_SaveSettings();
RevokeDragDrop(m_hwndTools);
g_pStore->UnregisterNotify((IDatabaseNotify *) this);
// Release
if (m_pOutBarNotify != NULL)
{
if (m_hwnd != NULL)
m_pOutBarNotify->Unregister(m_hwnd);
m_pOutBarNotify->Release();
m_pOutBarNotify = NULL;
}
// Release our notification interface
if (m_pStNotify != NULL)
{
if (m_hwnd != NULL)
m_pStNotify->Unregister(m_hwnd);
m_pStNotify->Release();
m_pStNotify = NULL;
}
// Clean up the toolbar and other child windows
if (m_hwnd)
{
if (m_hwndTools)
_EmptyToolbar(FALSE);
DestroyWindow(m_hwnd);
m_hwnd = NULL;
}
return S_OK;
}
//
// FUNCTION: COutBar::ResizeBorderDW()
//
// PURPOSE:
//
// PARAMETERS:
// LPCRECT prcBorder
// IUnknown *punkToolbarSite
// BOOL fReserved
//
// RETURN VALUE:
// HRESULT
//
HRESULT COutBar::ResizeBorderDW(LPCRECT prcBorder, IUnknown *punkToolbarSite, BOOL fReserved)
{
RECT rcRequest = { 0, 0, 0, 0 };
RECT rcFrame;
if (!m_ptbSite)
{
AssertSz(0, _T("COutBar::ResizeBorderDW() - Can't resize without calling SetSite() first."));
return E_FAIL;
}
if (m_fShow)
{
RECT rcBorder;
if (!prcBorder)
{
// Find out how big our parent's border space is
m_ptbSite->GetBorderDW((IDockingWindow*) this, &rcBorder);
prcBorder = &rcBorder;
}
// Figure out how much border space to ask the site for
GetWindowRect(m_hwndFrame, &rcFrame);
rcFrame.right = min(m_cxWidth - GetSystemMetrics(SM_CXFRAME) + 1,
prcBorder->right - prcBorder->left);
rcRequest.left = min(m_cxWidth, prcBorder->right - prcBorder->left - 32);
// Set our new window position
SetWindowPos(m_hwndFrame, NULL, 0, 0,
rcFrame.right, prcBorder->bottom - prcBorder->top,
SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE);
SetWindowPos(m_hwnd, NULL, prcBorder->left, prcBorder->top,
rcRequest.left, prcBorder->bottom - prcBorder->top,
SWP_NOACTIVATE | SWP_NOZORDER);
}
m_ptbSite->SetBorderSpaceDW((IDockingWindow*) this, &rcRequest);
return S_OK;
}
//
// FUNCTION: COutBar::SetSite()
//
// PURPOSE: Set's a site pointer for the class
//
HRESULT COutBar::SetSite(IUnknown* punkSite)
{
// If we already have a site pointer, release it now
if (m_ptbSite)
{
m_ptbSite->Release();
m_ptbSite = NULL;
}
// If the caller provided a new site interface, get the IDockingWindowSite
// and keep a pointer to it.
if (punkSite)
{
if (FAILED(punkSite->QueryInterface(IID_IDockingWindowSite, (void **)&m_ptbSite)))
return E_FAIL;
}
return S_OK;
}
HRESULT COutBar::GetSite(REFIID riid, LPVOID *ppvSite)
{
return E_NOTIMPL;
}
//
// FUNCTION: COutBar::DragEnter()
//
// PURPOSE: This get's called when the user starts dragging an object
// over our target area.
//
// PARAMETERS:
// <in> pDataObject - Pointer to the data object being dragged
// <in> grfKeyState - Pointer to the current key states
// <in> pt - Point in screen coordinates of the mouse
// <out> pdwEffect - Where we return whether this is a valid place for
// pDataObject to be dropped and if so what type of
// drop.
//
// RETURN VALUE:
// S_OK - The function succeeded.
//
HRESULT STDMETHODCALLTYPE COutBar::DragEnter(IDataObject* pDataObject,
DWORD grfKeyState,
POINTL pt, DWORD* pdwEffect)
{
FORMATETC fe;
POINT ptTemp = {pt.x, pt.y};
// Initialize our state
SafeRelease(m_pDataObject);
// Hold on to this new object
m_pDataObject = pDataObject;
m_pDataObject->AddRef();
// The big question here is whether or not this data object is an OE folder
// or is it something else.
SETDefFormatEtc(fe, CF_OEFOLDER, TYMED_HGLOBAL);
m_fDropShortcut = SUCCEEDED(m_pDataObject->QueryGetData(&fe));
if (!m_fDropShortcut)
{
SETDefFormatEtc(fe, CF_OESHORTCUT, TYMED_HGLOBAL);
m_fDropShortcut = SUCCEEDED(m_pDataObject->QueryGetData(&fe));
}
if (m_fDropShortcut)
{
m_fDropShortcut = _IsTempNewsgroup(m_pDataObject);
}
DOUTL(32, "COutBar::DragEnter() - Data is %s shortcut", m_fDropShortcut ? "a" : "not a");
// Hang on to this little gem
m_grfKeyState = grfKeyState;
// Initialize some other stuff
m_idCur = -1;
Assert(m_pTargetCur == NULL);
m_tbim.iButton = -1;
m_tbim.dwFlags = 0;
// Set the default return value here
m_dwEffectCur = *pdwEffect = DROPEFFECT_NONE;
// Update the highlight point
_UpdateDragDropHilite(&ptTemp);
return (S_OK);
}
int COutBar::_GetItemFromPoint(POINT pt)
{
int iPos;
TBBUTTON tb;
// Figure out which button this is over
ScreenToClient(m_hwndTools, &pt);
iPos = ToolBar_HitTest(m_hwndTools, &pt);
// If this is over a button, convert that button position to a command
if (iPos >= 0)
{
ToolBar_GetButton(m_hwndTools, iPos, &tb);
return (tb.idCommand);
}
return (-1);
}
void COutBar::_UpdateDragDropHilite(LPPOINT ppt)
{
TBINSERTMARK tbim;
int iPos;
// If this is a shortcut we do one thing, if it's anything else we do another
if (m_fDropShortcut)
{
if (m_fInsertMark)
{
tbim.iButton = -1;
tbim.dwFlags = 0;
ToolBar_SetInsertMark(m_hwndTools, &tbim);
m_fInsertMark = FALSE;
}
if (ppt)
{
ScreenToClient(m_hwndTools, ppt);
ToolBar_InsertMarkHitTest(m_hwndTools, ppt, &tbim);
ToolBar_SetInsertMark(m_hwndTools, &tbim);
m_fInsertMark = TRUE;
}
}
else
{
// Remove any previous marks
if (m_idDropHilite)
{
ToolBar_MarkButton(m_hwndTools, m_idDropHilite, FALSE);
m_idDropHilite = 0;
}
// Hilite the new button
if (ppt)
{
// First check to see if we're over a button or in between
m_idDropHilite = _GetItemFromPoint(*ppt);
ToolBar_MarkButton(m_hwndTools, m_idDropHilite, TRUE);
#ifdef DEBUG
FOLDERINFO rInfo;
FOLDERID idFolder;
idFolder = _FolderIdFromCmd(m_idDropHilite);
if (SUCCEEDED(g_pStore->GetFolderInfo(idFolder, &rInfo)))
{
DOUTL(32, "COutBar::_UpdateDragDropHilite() - Hiliting %s", rInfo.pszName);
g_pStore->FreeRecord(&rInfo);
}
#endif
}
}
}
FOLDERID COutBar::_FolderIdFromCmd(int idCmd)
{
TBBUTTON tbb;
int iPos;
iPos = (int) SendMessage(m_hwndTools, TB_COMMANDTOINDEX, idCmd, 0);
ToolBar_GetButton(m_hwndTools, iPos, &tbb);
return ((FOLDERID) tbb.dwData);
}
//
// FUNCTION: COutBar::DragOver()
//
// PURPOSE: This is called as the user drags an object over our target.
// If we allow this object to be dropped on us, then we will have
// a pointer in m_pDataObject.
//
// PARAMETERS:
// <in> grfKeyState - Pointer to the current key states
// <in> pt - Point in screen coordinates of the mouse
// <out> pdwEffect - Where we return whether this is a valid place for
// pDataObject to be dropped and if so what type of
// drop.
//
// RETURN VALUE:
// S_OK - The function succeeded.
//
HRESULT STDMETHODCALLTYPE COutBar::DragOver(DWORD grfKeyState, POINTL pt,
DWORD* pdwEffect)
{
DWORD idCur;
HRESULT hr = E_FAIL;
// If we don't have a data object from DragEnter, bail
if (NULL == m_pDataObject)
return (S_OK);
// If this is a shortcut we do one thing, if it's anything else we do another
if (m_fDropShortcut)
{
TBINSERTMARK tbim;
POINT ptTemp = {pt.x, pt.y};
ScreenToClient(m_hwndTools, &ptTemp);
ToolBar_InsertMarkHitTest(m_hwndTools, &ptTemp, &tbim);
if (tbim.iButton != m_tbim.iButton || tbim.dwFlags != m_tbim.dwFlags)
{
m_tbim = tbim;
ptTemp.x = pt.x;
ptTemp.y = pt.y;
_UpdateDragDropHilite(&ptTemp);
}
if (DROPEFFECT_LINK & *pdwEffect)
*pdwEffect = DROPEFFECT_LINK;
else
*pdwEffect = DROPEFFECT_MOVE;
return (S_OK);
}
else
{
// Figure out which item we're over
POINT ptTemp = {pt.x, pt.y};
if (-1 == (idCur = _GetItemFromPoint(ptTemp)))
{
DOUTL(32, "COutBar::DragOver() - _GetItemFromPoint() returns -1.");
}
DOUTL(32, "COutBar::DragOver() - m_idCur = %d, id = %d", m_idCur, idCur);
// If we're over a new button, then get the drop target for that button
if (m_idCur != idCur)
{
// Release any previous drop target, if any.
SafeRelease(m_pTargetCur);
// Update our current object marker
m_idCur = idCur;
// Assume error
m_dwEffectCur = DROPEFFECT_NONE;
// Update the UI
_UpdateDragDropHilite(&ptTemp);
// If we're over a button
if (m_idCur != -1)
{
FOLDERID id = _FolderIdFromCmd(m_idCur);
// Create the drop target object
m_pTargetCur = new CDropTarget();
if (m_pTargetCur)
{
hr = m_pTargetCur->Initialize(m_hwnd, id);
}
// If we have an initialized drop target, call DragEnter()
if (SUCCEEDED(hr) && m_pTargetCur)
{
hr = m_pTargetCur->DragEnter(m_pDataObject, grfKeyState, pt, pdwEffect);
m_dwEffectCur = *pdwEffect;
}
}
else
{
m_dwEffectCur = DROPEFFECT_NONE;
}
}
else
{
// No target change, but did the key state change?
if ((m_grfKeyState != grfKeyState) && m_pTargetCur)
{
m_dwEffectCur = *pdwEffect;
hr = m_pTargetCur->DragOver(grfKeyState, pt, &m_dwEffectCur);
}
else
{
hr = S_OK;
}
}
*pdwEffect = m_dwEffectCur;
m_grfKeyState = grfKeyState;
}
return (hr);
}
//
// FUNCTION: COutBar::DragLeave()
//
// PURPOSE: Allows us to release any stored data we have from a successful
// DragEnter()
//
// RETURN VALUE:
// S_OK - Everything is groovy
//
HRESULT STDMETHODCALLTYPE COutBar::DragLeave(void)
{
SafeRelease(m_pDataObject);
SafeRelease(m_pTargetCur);
_UpdateDragDropHilite(NULL);
return (S_OK);
}
//
// FUNCTION: COutBar::Drop()
//
// PURPOSE: The user has let go of the object over our target. If we
// can accept this object we will already have the pDataObject
// stored in m_pDataObject. If this is a copy or move, then
// we go ahead and update the store. Otherwise, we bring up
// a send note with the object attached.
//
// PARAMETERS:
// <in> pDataObject - Pointer to the data object being dragged
// <in> grfKeyState - Pointer to the current key states
// <in> pt - Point in screen coordinates of the mouse
// <out> pdwEffect - Where we return whether this is a valid place for
// pDataObject to be dropped and if so what type of
// drop.
//
// RETURN VALUE:
// S_OK - Everything worked OK
//
HRESULT STDMETHODCALLTYPE COutBar::Drop(IDataObject* pDataObject,
DWORD grfKeyState, POINTL pt,
DWORD* pdwEffect)
{
HRESULT hr = E_FAIL;
Assert(m_pDataObject == pDataObject);
if (m_fDropShortcut)
{
hr = _AddShortcut(pDataObject);
}
else
{
if (m_pTargetCur)
{
hr = m_pTargetCur->Drop(pDataObject, grfKeyState, pt, pdwEffect);
}
else
{
*pdwEffect = DROPEFFECT_NONE;
hr = S_OK;
}
}
_UpdateDragDropHilite(NULL);
SafeRelease(m_pTargetCur);
SafeRelease(m_pDataObject);
return (hr);
}
HRESULT COutBar::_AddShortcut(IDataObject *pObject)
{
FORMATETC fe;
STGMEDIUM stm;
FOLDERID *pidFolder;
HRESULT hr = E_UNEXPECTED;
TBINSERTMARK tbim;
if (!pObject)
return (E_INVALIDARG);
// Get the data from the data object
SETDefFormatEtc(fe, CF_OEFOLDER, TYMED_HGLOBAL);
if (SUCCEEDED(pObject->GetData(&fe, &stm)))
{
pidFolder = (FOLDERID *) GlobalLock(stm.hGlobal);
ToolBar_GetInsertMark(m_hwndTools, &tbim);
_InsertButton(tbim.iButton + tbim.dwFlags, *pidFolder);
_SaveSettings();
_EmptyToolbar(TRUE);
_FillToolbar();
m_pOutBarNotify->Lock(m_hwnd);
m_pOutBarNotify->DoNotification(WM_RELOADSHORTCUTS, 0, 0, SNF_POSTMSG);
m_pOutBarNotify->Unlock();
GlobalUnlock(stm.hGlobal);
ReleaseStgMedium(&stm);
}
else
{
SETDefFormatEtc(fe, CF_OESHORTCUT, TYMED_HGLOBAL);
if (SUCCEEDED(pObject->GetData(&fe, &stm)))
{
UINT *piPosOld = (UINT *) GlobalLock(stm.hGlobal);
UINT iPosNew;
ToolBar_GetInsertMark(m_hwndTools, &tbim);
iPosNew = tbim.iButton;
if (tbim.dwFlags & TBIMHT_AFTER)
iPosNew++;
TBBUTTON tbb;
ToolBar_GetButton(m_hwndTools, *piPosOld, &tbb);
SendMessage(m_hwndTools, TB_INSERTBUTTON, iPosNew, (LPARAM)&tbb);
if (iPosNew < *piPosOld)
(*piPosOld)++;
SendMessage(m_hwndTools, TB_DELETEBUTTON, *piPosOld, 0);
_SaveSettings();
_EmptyToolbar(TRUE);
_FillToolbar();
m_pOutBarNotify->Lock(m_hwnd);
m_pOutBarNotify->DoNotification(WM_RELOADSHORTCUTS, 0, 0, SNF_POSTMSG);
m_pOutBarNotify->Unlock();
GlobalUnlock(stm.hGlobal);
ReleaseStgMedium(&stm);
}
}
return (hr);
}
HRESULT STDMETHODCALLTYPE COutBar::QueryContinueDrag(BOOL fEscapePressed,
DWORD grfKeyState)
{
if (fEscapePressed)
return (DRAGDROP_S_CANCEL);
if (grfKeyState & MK_RBUTTON)
return (DRAGDROP_S_CANCEL);
if (!(grfKeyState & MK_LBUTTON))
return (DRAGDROP_S_DROP);
return (S_OK);
}
HRESULT STDMETHODCALLTYPE COutBar::GiveFeedback(DWORD dwEffect)
{
return (DRAGDROP_S_USEDEFAULTCURSORS);
}
LRESULT CALLBACK COutBar::OutBarWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
COutBar *pThis;
if (uMsg == WM_NCCREATE)
{
pThis = (COutBar *) LPCREATESTRUCT(lParam)->lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM) pThis);
}
else
{
pThis = (COutBar *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
}
Assert(pThis);
return pThis->WndProc(hwnd, uMsg, wParam, lParam);
}
LRESULT CALLBACK COutBar::ExtFrameWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
COutBar *pThis;
if (uMsg == WM_NCCREATE)
{
pThis = (COutBar *) LPCREATESTRUCT(lParam)->lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM) pThis);
}
else
{
pThis = (COutBar *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
}
Assert(pThis);
return pThis->FrameWndProc(hwnd, uMsg, wParam, lParam);
}
LRESULT COutBar::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
HANDLE_MSG(hwnd, WM_MOUSEMOVE, OnMouseMove);
HANDLE_MSG(hwnd, WM_LBUTTONDOWN, OnLButtonDown);
HANDLE_MSG(hwnd, WM_LBUTTONUP, OnLButtonUp);
case WM_SYSCOLORCHANGE:
case WM_WININICHANGE:
case WM_FONTCHANGE:
{
SendMessage(m_hwndPager, msg, wParam, lParam);
SendMessage(m_hwndTools, msg, wParam, lParam);
ResizeBorderDW(NULL, NULL, FALSE);
return (0);
}
case WM_RELOADSHORTCUTS:
{
_EmptyToolbar(TRUE);
_FillToolbar();
return (0);
}
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
LRESULT COutBar::FrameWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
HANDLE_MSG(hwnd, WM_NOTIFY, Frame_OnNotify);
HANDLE_MSG(hwnd, WM_SIZE, Frame_OnSize);
HANDLE_MSG(hwnd, WM_COMMAND, Frame_OnCommand);
HANDLE_MSG(hwnd, WM_NCDESTROY, Frame_OnNCDestroy);
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
void COutBar::OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags)
{
if (!m_fResizing)
{
SetCapture(hwnd);
m_fResizing = TRUE;
}
}
void COutBar::OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags)
{
POINT pt = { x, y };
RECT rcClient;
if (m_fResizing)
{
if (pt.x > 32)
{
GetClientRect(m_hwndParent, &rcClient);
m_cxWidth = min(pt.x, rcClient.right - 32);
ResizeBorderDW(0, 0, FALSE);
}
}
}
void COutBar::OnLButtonUp(HWND hwnd, int x, int y, UINT keyFlags)
{
if (m_fResizing)
{
ReleaseCapture();
m_fResizing = FALSE;
}
}
void COutBar::Frame_OnNCDestroy(HWND hwnd)
{
SetWindowLong(hwnd, GWLP_USERDATA, NULL);
m_hwndFrame = m_hwndPager = m_hwndTools = NULL;
}
void COutBar::Frame_OnSize(HWND hwnd, UINT state, int cx, int cy)
{
// When we get resized, we resize our children and update the toolbar button width
if (m_hwndPager)
{
SetWindowPos(m_hwndPager, NULL, 0, 0, cx, cy, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE);
SendMessage(m_hwndTools, TB_SETBUTTONWIDTH, 0, MAKELONG(cx, cx));
}
}
void COutBar::Frame_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
{
int iPos;
TBBUTTONINFO tbbi;
if (id < ID_FIRST)
{
tbbi.cbSize = sizeof(tbbi);
tbbi.dwMask = TBIF_LPARAM;
iPos = (int) SendMessage(m_hwndTools, TB_GETBUTTONINFO, (WPARAM) id, (LPARAM)&tbbi);
if (iPos >= 0)
m_pBrowser->BrowseObject((FOLDERID) tbbi.lParam, 0);
}
}
LRESULT COutBar::Frame_OnNotify(HWND hwnd, int idFrom, NMHDR *pnmhdr)
{
if (pnmhdr->code <= PGN_FIRST && pnmhdr->code >= PGN_LAST)
return SendMessage(m_hwndTools, WM_NOTIFY, 0, (LPARAM) pnmhdr);
switch (pnmhdr->code)
{
case NM_CUSTOMDRAW:
{
NMCUSTOMDRAW *pnmcd = (NMCUSTOMDRAW*) pnmhdr;
if (pnmcd->dwDrawStage == CDDS_PREPAINT)
return CDRF_NOTIFYITEMDRAW;
if (pnmcd->dwDrawStage == CDDS_ITEMPREPAINT)
{
NMTBCUSTOMDRAW * ptbcd = (NMTBCUSTOMDRAW *)pnmcd;
ptbcd->clrText = GetSysColor(COLOR_WINDOW);
return CDRF_NEWFONT;
}
}
break;
case NM_RCLICK:
{
if (pnmhdr->hwndFrom == m_hwndTools)
{
DWORD dwPos = GetMessagePos();
_OnContextMenu(GET_X_LPARAM(dwPos), GET_Y_LPARAM(dwPos));
return 1;
}
}
case TBN_DRAGOUT:
{
NMTOOLBAR *pnmtb = (NMTOOLBAR *) pnmhdr;
DWORD dwEffect = DROPEFFECT_NONE;
UINT id = ToolBar_CommandToIndex(m_hwndTools, pnmtb->iItem);
// Create a new data object
CShortcutDataObject *pDataObj = new CShortcutDataObject(id);
if (pDataObj)
{
DoDragDrop(pDataObj, (IDropSource *) this, DROPEFFECT_MOVE, &dwEffect);
pDataObj->Release();
}
return 0;
}
}
return (FALSE);
}
HRESULT COutBar::_CreateToolbar()
{
HIMAGELIST himl, himlOld;
LRESULT lButtonSize;
RECT rc;
int iButtonWidth = 70;
TCHAR szName[CCHMAX_STRINGRES];
// Create the frame window
m_hwndFrame = CreateWindowEx(WS_EX_CLIENTEDGE, s_szOutBarFrameClass, NULL,
WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
0, 0, 0, 0, m_hwnd, (HMENU) IDC_FRAME, g_hInst, this);
if (!m_hwndFrame)
return E_FAIL;
// Create the pager
m_hwndPager = CreateWindowEx(0, WC_PAGESCROLLER, NULL,
WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | PGS_VERT | PGS_DRAGNDROP,
0, 0, 0, 0, m_hwndFrame, (HMENU) IDC_PAGER, g_hInst, NULL);
if (!m_hwndPager)
return E_FAIL;
ZeroMemory(szName, ARRAYSIZE(szName));
LoadString(g_hLocRes, idsOutlookBar, szName, ARRAYSIZE(szName));
// Create the toolbar
m_hwndTools = CreateWindowEx(WS_EX_TOOLWINDOW, TOOLBARCLASSNAME, szName,
WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS |
TBSTYLE_FLAT | TBSTYLE_TOOLTIPS |
CCS_NODIVIDER | CCS_NOPARENTALIGN | CCS_NORESIZE | CCS_VERT,
0, 0, 0, 0, m_hwndPager, (HMENU) IDC_TOOLBAR, g_hInst, NULL);
if (!m_hwndTools)
return E_FAIL;
// This tells the toolbar what version we are
SendMessage(m_hwndTools, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
_FillToolbar();
_SetButtonStyle(!m_fLarge);
SendMessage(m_hwndTools, TB_SETBUTTONWIDTH, 0, MAKELONG(0, m_cxWidth));
m_pStNotify->Register(m_hwnd, g_hwndInit, FALSE);
SendMessage(m_hwndPager, PGM_SETCHILD, 0, (LPARAM)m_hwndTools);
// Let's try this
COLORSCHEME cs;
cs.dwSize = sizeof(COLORSCHEME);
cs.clrBtnHighlight = GetSysColor(COLOR_3DFACE);
cs.clrBtnShadow = GetSysColor(COLOR_WINDOWFRAME);
SendMessage(m_hwndTools, TB_SETCOLORSCHEME, 0, (LPARAM) &cs);
return S_OK;
}
void COutBar::_FillToolbar()
{
if (FAILED(_LoadSettings()))
_CreateDefaultButtons();
SendMessage(m_hwndPager, PGM_RECALCSIZE, 0, 0L);
}
void COutBar::_EmptyToolbar(BOOL fDelete)
{
if (fDelete)
while (SendMessage(m_hwndTools, TB_DELETEBUTTON, 0, 0))
;
}
BOOL COutBar::_FindButton(int *piBtn, LPITEMIDLIST pidl)
{
BOOL fFound = FALSE;
#if 0
int iBtn, cBtn, iCmp;
TBBUTTON tbb;
Assert(pidl);
cBtn = (int)SendMessage(m_hwndTools, TB_BUTTONCOUNT, 0, 0L);
// skip the root, so start at index 1
for (iBtn = 1; iBtn < cBtn; iBtn++)
{
if (SendMessage(m_hwndTools, TB_GETBUTTON, iBtn, (LPARAM)&tbb))
{
Assert(tbb.dwData);
iCmp = ShortFromResult(m_pShellFolder->CompareIDs(0, pidl, (LPITEMIDLIST)(tbb.dwData)));
if (iCmp <= 0)
{
fFound = (iCmp == 0);
break;
}
}
}
*piBtn = iBtn;
#endif
return fFound;
}
BOOL COutBar::_InsertButton(int index, FOLDERINFO *pInfo)
{
TBBUTTON tbb;
TCHAR szName[2 * MAX_PATH];
LPTSTR pszFree = NULL;
BOOL fRet;
tbb.fsState = TBSTATE_ENABLED | TBSTATE_WRAP;
tbb.fsStyle = TBSTYLE_BUTTON | TBSTYLE_NOPREFIX;
tbb.idCommand = m_idCommand++;
tbb.dwData = (DWORD_PTR) pInfo->idFolder;
tbb.iBitmap = GetFolderIcon(pInfo);
tbb.iString = (INT_PTR) pInfo->pszName;
if (pInfo->cUnread)
{
DWORD cchSize = ARRAYSIZE(szName);;
if (lstrlen(pInfo->pszName) + 13 < ARRAYSIZE(szName))
tbb.iString = (INT_PTR)szName;
else
{
cchSize = (lstrlen(pInfo->pszName) + 14);
if (!MemAlloc((LPVOID*) &pszFree, cchSize * sizeof(TCHAR)))
return FALSE;
tbb.iString = (INT_PTR)pszFree;
}
wnsprintf((LPTSTR)tbb.iString, cchSize, "%s (%d)", pInfo->pszName, CUnread(pInfo));
}
// Check to see if we're inserting at the end
if (index == -1)
{
index = ToolBar_ButtonCount(m_hwndTools);
}
// insert the root
fRet = (BOOL)SendMessage(m_hwndTools, TB_INSERTBUTTON, index, (LPARAM)&tbb);
SafeMemFree(pszFree);
return fRet;
}
BOOL COutBar::_InsertButton(int iIndex, FOLDERID id)
{
FOLDERINFO rInfo = {0};
if (SUCCEEDED(g_pStore->GetFolderInfo(id, &rInfo)))
{
_InsertButton(iIndex, &rInfo);
g_pStore->FreeRecord(&rInfo);
}
return (TRUE);
}
BOOL COutBar::_DeleteButton(int iBtn)
{
TBBUTTON tbb;
if (SendMessage(m_hwndTools, TB_GETBUTTON, iBtn, (LPARAM)&tbb))
{
if (SendMessage(m_hwndTools, TB_DELETEBUTTON, iBtn, 0L))
{
_SaveSettings();
m_pOutBarNotify->Lock(m_hwnd);
m_pOutBarNotify->DoNotification(WM_RELOADSHORTCUTS, 0, 0, SNF_POSTMSG);
m_pOutBarNotify->Unlock();
return (TRUE);
}
}
return FALSE;
}
BOOL COutBar::_UpdateButton(int iBtn, LPITEMIDLIST pidl)
{
#if 0
TBBUTTON tbb;
TBBUTTONINFO tbbi;
TCHAR szName[2 * MAX_PATH];
LPTSTR pszFree = NULL;
BOOL fRet = FALSE;
if (SendMessage(m_hwndTools, TB_GETBUTTON, iBtn, (LPARAM)&tbb))
{
tbbi.cbSize = sizeof(tbbi);
tbbi.dwMask = TBIF_TEXT | TBIF_IMAGE | TBIF_LPARAM;
tbbi.iImage = FIDL_ICONID(pidl);
tbbi.lParam = (DWORD)pidl;
if (FIDL_UNREAD(pidl))
{
DWORD cchSize = ARRAYSIZE(szName);
if (lstrlen(FIDL_NAME(pidl)) + 13 < ARRAYSIZE(szName))
tbbi.pszText = szName;
else
{
cchSize = (lstrlen(FIDL_NAME(pidl)) + 14);
if (!MemAlloc((LPVOID*)&pszFree, (cchSize * sizeof(TCHAR))))
return FALSE;
tbbi.pszText = pszFree;
}
wnsprintf(tbbi.pszText, cchSize, "%s (%d)", FIDL_NAME(pidl), FIDL_UNREAD(pidl));
}
else
tbbi.pszText = FIDL_NAME(pidl);
fRet = SendMessage(m_hwndTools, TB_SETBUTTONINFO, (WPARAM)tbb.idCommand, (LPARAM)&tbbi);
if (tbb.dwData)
PidlFree((LPITEMIDLIST)(tbb.dwData));
if (pszFree)
MemFree(pszFree);
}
return fRet;
#endif
return 0;
}
#if 0
void COutBar::_OnFolderNotify(FOLDERNOTIFY *pnotify)
{
LPITEMIDLIST pidl;
int iBtn;
BOOL fRecalc = FALSE;
Assert(pnotify != NULL);
Assert(pnotify->pidlNew != NULL);
switch (pnotify->msg)
{
case NEW_FOLDER:
// only insert if it is a root level pidl
if (0 == NEXTID(pnotify->pidlNew)->mkid.cb)
{
// check for dups and figure out where to insert
if (!FindButton(&iBtn, pnotify->pidlNew))
{
if (pidl = PidlDupIdList(pnotify->pidlNew))
fRecalc = InsertButton(iBtn, pidl);
}
}
break ;
case DELETE_FOLDER:
// only look for it if it is a root level pidl
if (0 == NEXTID(pnotify->pidlNew)->mkid.cb)
{
if (FindButton(&iBtn, pnotify->pidlNew))
fRecalc = DeleteButton(iBtn);
}
break ;
case RENAME_FOLDER:
case MOVE_FOLDER:
// only look for it if it is a root level pidl
if (0 == NEXTID(pnotify->pidlOld)->mkid.cb)
{
if (FindButton(&iBtn, pnotify->pidlOld))
fRecalc = DeleteButton(iBtn);
}
// only insert if it is a root level pidl
if (0 == NEXTID(pnotify->pidlNew)->mkid.cb)
{
// check for dups and figure out where to insert
if (!FindButton(&iBtn, pnotify->pidlNew))
{
if (pidl = PidlDupIdList(pnotify->pidlNew))
fRecalc = InsertButton(iBtn, pidl);
}
}
break ;
case UNREAD_CHANGE:
case UPDATEFLAG_CHANGE:
// only look for it if it is a root level pidl
if (0 == NEXTID(pnotify->pidlNew)->mkid.cb)
{
// check for dups and figure out where to insert
if (FindButton(&iBtn, pnotify->pidlNew))
{
if (pidl = PidlDupIdList(pnotify->pidlNew))
UpdateButton(iBtn, pidl);
}
}
break ;
case IMAPFLAG_CHANGE:
// don't care
break ;
case FOLDER_PROPS_CHANGED:
//Don't care
break ;
default:
AssertSz(FALSE, "Unhandled CFolderCache notification!");
break;
}
if (fRecalc)
SendMessage(m_hwndPager, PGM_RECALCSIZE, 0, 0L);
}
#endif
//
// FUNCTION: COutBar::_OnContextMenu
//
// PURPOSE: If the WM_CONTEXTMENU message is generated from the keyboard
// then figure out a pos to invoke the menu. Then dispatch the
// request to the handler.
//
// PARAMETERS:
// hwnd - Handle of the view window.
// hwndClick - Handle of the window the user clicked in.
// x, y - Position of the mouse click in screen coordinates.
//
void COutBar::_OnContextMenu(int x, int y)
{
HRESULT hr;
HMENU hMenu;
int id = 0;
int i;
POINT pt = { x, y };
TBBUTTON tbb;
// Figure out where the click was
ScreenToClient(m_hwndTools, &pt);
i = ToolBar_HitTest(m_hwndTools, &pt);
// If the click was on a button, then bring up the item context menu
if (i >= 0)
{
// Get the button info
SendMessage(m_hwndTools, TB_GETBUTTON, i, (LPARAM) &tbb);
// Load the context menu
hMenu = LoadPopupMenu(IDR_OUTLOOKBAR_ITEM_POPUP);
if (!hMenu)
return;
// Mark the button
SendMessage(m_hwndTools, TB_SETSTATE, (WPARAM)tbb.idCommand, (LPARAM)(TBSTATE_ENABLED | TBSTATE_WRAP | TBSTATE_MARKED));
m_idSel = tbb.idCommand;
// If this is the deleted items folder, add the "empty" menu item
TBBUTTONINFO tbbi;
tbbi.cbSize = sizeof(tbbi);
tbbi.dwMask = TBIF_LPARAM;
if (-1 != SendMessage(m_hwndTools, TB_GETBUTTONINFO, (WPARAM) m_idSel, (LPARAM)&tbbi))
{
FOLDERINFO rInfo;
if (SUCCEEDED(g_pStore->GetFolderInfo((FOLDERID) tbbi.lParam, &rInfo)))
{
if (rInfo.tySpecial != FOLDER_DELETED)
{
DeleteMenu(hMenu, ID_EMPTY_WASTEBASKET, MF_BYCOMMAND);
}
g_pStore->FreeRecord(&rInfo);
}
}
// Do the enable-disable thing
MenuUtil_EnablePopupMenu(hMenu, this);
// Display the context menu
id = TrackPopupMenuEx(hMenu, TPM_RETURNCMD | TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
x, y, m_hwnd, NULL);
// Unmark the button
SendMessage(m_hwndTools, TB_SETSTATE, (WPARAM)tbb.idCommand, (LPARAM)(TBSTATE_ENABLED | TBSTATE_WRAP));
// See if the user chose a menu item
if (id != 0)
{
Exec(NULL, id, OLECMDEXECOPT_DODEFAULT, NULL, NULL);
}
m_idSel = -1;
// Clean this up
DestroyMenu(hMenu);
}
// Else if the click was in the empty space, show the bar context menu
else
{
// Load the context menu
hMenu = LoadPopupMenu(IDR_OUTLOOKBAR_POPUP);
if (!hMenu)
return;
// Do the enable-disable thing
MenuUtil_EnablePopupMenu(hMenu, this);
// Display the context menu
id = TrackPopupMenuEx(hMenu, TPM_RETURNCMD | TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
x, y, m_hwnd, NULL);
// See if the user chose a menu item
if (id != 0)
{
Exec(NULL, id, OLECMDEXECOPT_DODEFAULT, NULL, NULL);
}
// Clean this up
DestroyMenu(hMenu);
}
}
HRESULT COutBar::_CreateDefaultButtons()
{
IEnumerateFolders *pEnum = NULL;
FOLDERINFO rFolder;
UINT iIndex = 0;
FOLDERID idFolderDefault;
// Figure out the default server first
if (FAILED(GetDefaultServerId(ACCT_MAIL, &idFolderDefault)))
idFolderDefault = FOLDERID_LOCAL_STORE;
if (!(g_dwAthenaMode & MODE_NEWSONLY))
{
// Inbox first
if (SUCCEEDED(g_pStore->GetSpecialFolderInfo(idFolderDefault, FOLDER_INBOX, &rFolder)))
{
_InsertButton(iIndex++, &rFolder);
g_pStore->FreeRecord(&rFolder);
}
}
// Outbox
if (SUCCEEDED(g_pStore->GetSpecialFolderInfo(FOLDERID_LOCAL_STORE, FOLDER_OUTBOX, &rFolder)))
{
_InsertButton(iIndex++, &rFolder);
g_pStore->FreeRecord(&rFolder);
}
// Sent Items
if (SUCCEEDED(g_pStore->GetSpecialFolderInfo(idFolderDefault, FOLDER_SENT, &rFolder)))
{
_InsertButton(iIndex++, &rFolder);
g_pStore->FreeRecord(&rFolder);
}
// Deleted
if (SUCCEEDED(g_pStore->GetSpecialFolderInfo(FOLDERID_LOCAL_STORE, FOLDER_DELETED, &rFolder)))
{
_InsertButton(iIndex++, &rFolder);
g_pStore->FreeRecord(&rFolder);
}
// Drafts
if (SUCCEEDED(g_pStore->GetSpecialFolderInfo(idFolderDefault, FOLDER_DRAFT, &rFolder)))
{
_InsertButton(iIndex++, &rFolder);
g_pStore->FreeRecord(&rFolder);
}
// Save at this point so everyone will be in sync
_SaveSettings();
return (S_OK);
}
HRESULT COutBar::_LoadSettings(void)
{
BAR_PERSIST_INFO *pPersist = NULL;
HRESULT hr = E_FAIL;
DWORD iIndex = 0;
FOLDERINFO rInfo;
UINT i;
// Load the settings
if (FAILED(hr = OutlookBar_LoadSettings(&pPersist)))
goto exit;
// Load the bar from the saved folder ID's
for (i = 0; i < pPersist->cItems; i++)
{
// Get the folder info for this folder
if (SUCCEEDED(g_pStore->GetFolderInfo(pPersist->rgFolders[i], &rInfo)))
{
if (_InsertButton(iIndex, &rInfo))
iIndex++;
g_pStore->FreeRecord(&rInfo);
}
}
// If the bar is empty, and the user didn't save it empty, use the defaults
if (iIndex == 0 && pPersist->cItems)
hr = E_FAIL;
else
hr = S_OK;
// Also restore the width while we're at it
if (pPersist->cxWidth >= 28)
{
m_cxWidth = pPersist->cxWidth;
}
if (m_fOnce)
{
m_fLarge = !pPersist->fSmall;
m_fOnce = FALSE;
}
exit:
SafeMemFree(pPersist);
return (hr);
}
HRESULT COutBar::_SaveSettings(void)
{
BAR_PERSIST_INFO *pPersist = NULL;
DWORD cbData;
DWORD iIndex = 0;
FOLDERINFO rInfo;
UINT i;
DWORD cButtons;
TBBUTTON tbb;
RECT rcClient;
// Get the count of buttons from the outlook bar
cButtons = (DWORD) SendMessage(m_hwndTools, TB_BUTTONCOUNT, 0, 0);
// Allocate a persist info struct big enough for everything
cbData = sizeof(BAR_PERSIST_INFO) + ((cButtons - 1) * sizeof(FOLDERID));
if (!MemAlloc((LPVOID *) &pPersist, cbData))
return (E_OUTOFMEMORY);
// Fill in the persist info
pPersist->dwVersion = GetOutlookBarVersion();
pPersist->cItems = cButtons;
pPersist->fSmall = !m_fLarge;
pPersist->ftSaved.dwHighDateTime = 0;
pPersist->ftSaved.dwLowDateTime = 0;
GetClientRect(m_hwnd, &rcClient);
pPersist->cxWidth = rcClient.right;
// Loop through the buttons on the toolbar and get the info from each
for (i = 0; i < cButtons; i++)
{
SendMessage(m_hwndTools, TB_GETBUTTON, i, (LPARAM) &tbb);
pPersist->rgFolders[i] = (FOLDERID) tbb.dwData;
}
// Now open the registry and save the blob
AthUserSetValue(NULL, GetRegKey(), REG_BINARY, (const LPBYTE) pPersist, cbData);
// Free up the struct
SafeMemFree(pPersist);
return (S_OK);
}
HRESULT COutBar::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT *pCmdText)
{
BOOL fSpecial = FALSE;
BOOL fServer = FALSE;
BOOL fRoot = FALSE;
BOOL fNews = FALSE;
BOOL fIMAP = FALSE;
FOLDERINFO rFolder = {0};
FOLDERID idFolder = FOLDERID_INVALID;
if (m_idSel != -1)
{
// Get the ID of the folder that is selected
TBBUTTONINFO tbbi;
tbbi.cbSize = sizeof(tbbi);
tbbi.dwMask = TBIF_LPARAM;
if (-1 == SendMessage(m_hwndTools, TB_GETBUTTONINFO, (WPARAM) m_idSel, (LPARAM)&tbbi))
return (E_UNEXPECTED);
// Get the Folder Info
idFolder = (FOLDERID) tbbi.lParam;
if (FAILED(g_pStore->GetFolderInfo(idFolder, &rFolder)))
return (E_UNEXPECTED);
// Break some of this down for readability
fSpecial = rFolder.tySpecial != FOLDER_NOTSPECIAL;
fServer = rFolder.dwFlags & FOLDER_SERVER;
fRoot = FOLDERID_ROOT == idFolder;
fNews = rFolder.tyFolder == FOLDER_NEWS;
fIMAP = rFolder.tyFolder == FOLDER_IMAP;
}
// Loop through the commands in the prgCmds array looking for ones that haven't been handled
for (UINT i = 0; i < cCmds; i++)
{
if (prgCmds[i].cmdf == 0)
{
switch (prgCmds[i].cmdID)
{
case ID_OPEN_FOLDER:
case ID_REMOVE_SHORTCUT:
case ID_NEW_SHORTCUT:
case ID_HIDE:
case ID_FIND_MESSAGE:
prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
break;
case ID_LARGE_ICONS:
prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
if (m_fLarge)
prgCmds[i].cmdf |= OLECMDF_NINCHED;
break;
case ID_SMALL_ICONS:
prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
if (!m_fLarge)
prgCmds[i].cmdf |= OLECMDF_NINCHED;
break;
case ID_RENAME_SHORTCUT:
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
break;
case ID_PROPERTIES:
{
Assert(idFolder != FOLDERID_INVALID);
prgCmds[i].cmdf |= OLECMDF_SUPPORTED;
// Everything except the root and the personal folders node
if (!fRoot && ((fServer && (fNews || fIMAP)) || !fServer))
prgCmds[i].cmdf |= OLECMDF_SUPPORTED | OLECMDF_ENABLED;
break;
}
case ID_EMPTY_WASTEBASKET:
{
if (rFolder.cMessages > 0 || FHasChildren(&rFolder, SUBSCRIBED))
prgCmds[i].cmdf = OLECMDF_ENABLED | OLECMDF_SUPPORTED;
break;
}
}
}
}
g_pStore->FreeRecord(&rFolder);
return (S_OK);
}
HRESULT COutBar::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG *pvaIn, VARIANTARG *pvaOut)
{
TBBUTTONINFO tbbi = { 0 };
FOLDERID id = FOLDERID_INVALID;
int iPos = -1;
// Get the ID of the folder that was selected
tbbi.cbSize = sizeof(tbbi);
tbbi.dwMask = TBIF_LPARAM;
tbbi.lParam = 0;
if (-1 != (iPos = (int) SendMessage(m_hwndTools, TB_GETBUTTONINFO, m_idSel, (LPARAM) &tbbi)))
{
id = (FOLDERID) tbbi.lParam;
}
switch (nCmdID)
{
case ID_OPEN_FOLDER:
{
if (id != FOLDERID_INVALID)
m_pBrowser->BrowseObject((FOLDERID) tbbi.lParam, 0);
return (S_OK);
}
case ID_REMOVE_SHORTCUT:
{
_DeleteButton(iPos);
return (S_OK);
}
case ID_RENAME_SHORTCUT:
break;
case ID_PROPERTIES:
{
if (id != FOLDERID_INVALID)
MenuUtil_OnProperties(m_hwndParent, id);
return (S_OK);
}
case ID_LARGE_ICONS:
case ID_SMALL_ICONS:
{
_SetButtonStyle(nCmdID == ID_SMALL_ICONS);
return (S_OK);
}
case ID_NEW_SHORTCUT:
{
FOLDERID idFolderDest;
HRESULT hr;
hr = SelectFolderDialog(m_hwnd, SFD_SELECTFOLDER, FOLDERID_ROOT,
FD_NONEWFOLDERS, (LPCTSTR) idsNewShortcutTitle,
(LPCTSTR) idsNewShortcutCaption, &idFolderDest);
if (SUCCEEDED(hr))
{
OutlookBar_AddShortcut(idFolderDest);
}
return (S_OK);
}
case ID_HIDE:
{
if (m_pBrowser)
{
m_pBrowser->SetViewLayout(DISPID_MSGVIEW_OUTLOOK_BAR, LAYOUT_POS_NA, FALSE, 0, 0);
}
return (S_OK);
}
case ID_EMPTY_WASTEBASKET:
{
if (AthMessageBoxW(m_hwnd, MAKEINTRESOURCEW(idsAthenaMail), MAKEINTRESOURCEW(idsWarnEmptyDeletedItems),
NULL, MB_YESNO | MB_DEFBUTTON2) == IDYES)
{
EmptyFolder(m_hwnd, id);
}
return (S_OK);
}
case ID_FIND_MESSAGE:
{
DoFindMsg(id, 0);
return (S_OK);
}
}
return (OLECMDERR_E_NOTSUPPORTED);
}
BOOL COutBar::_SetButtonStyle(BOOL fSmall)
{
LONG lStyle;
SIZE s1, s2;
// Get the current style
lStyle = (LONG) SendMessage(m_hwndTools, TB_GETSTYLE, 0, 0);
// Make sure we have the right image list loaded
if (fSmall && !m_himlSmall)
{
// Load the image list for the toolbar
m_himlSmall = ImageList_LoadBitmap(g_hLocRes, MAKEINTRESOURCE(idbFolders), 16, 0, RGB(255, 0, 255));
if (!m_himlSmall)
return FALSE;
}
if (!fSmall && !m_himlLarge)
{
// Load the image list for the toolbar
m_himlLarge = ImageList_LoadBitmap(g_hLocRes, MAKEINTRESOURCE(idbFoldersLarge), 32, 0, RGB(255, 0, 255));
if (!m_himlLarge)
return FALSE;
}
// Get the size
RECT rc;
GetClientRect(m_hwndTools, &rc);
NMPGCALCSIZE nm;
nm.hdr.code = PGN_CALCSIZE;
nm.dwFlag = PGF_CALCHEIGHT;
nm.iWidth = 0;
nm.iHeight = 0;
SendMessage(m_hwndTools, WM_NOTIFY, 0, (LPARAM) &nm);
// Now swap styles
if (fSmall)
{
lStyle |= TBSTYLE_LIST;
SendMessage(m_hwndTools, TB_SETSTYLE, 0, lStyle);
SendMessage(m_hwndTools, TB_SETBITMAPSIZE, 0, MAKELONG(16, 16));
SendMessage(m_hwndTools, TB_SETIMAGELIST, 0, (LPARAM) m_himlSmall);
SendMessage(m_hwndTools, TB_SETMAXTEXTROWS, 1, 0L);
SendMessage(m_hwndTools, TB_SETBUTTONWIDTH, 0, MAKELONG(rc.right, rc.right));
}
else
{
lStyle &= ~TBSTYLE_LIST;
SendMessage(m_hwndTools, TB_SETSTYLE, 0, lStyle);
SendMessage(m_hwndTools, TB_SETIMAGELIST, 0, (LPARAM) m_himlLarge);
SendMessage(m_hwndTools, TB_SETBITMAPSIZE, 0, MAKELONG(32, 32));
SendMessage(m_hwndTools, TB_SETMAXTEXTROWS, 2, 0L);
SendMessage(m_hwndTools, TB_SETBUTTONWIDTH, 0, MAKELONG(rc.right, rc.right));
}
PostMessage(m_hwndPager, PGM_RECALCSIZE, 0, 0L);
InvalidateRect(m_hwndTools, NULL, TRUE);
m_fLarge = !fSmall;
return (TRUE);
}
HRESULT OutlookBar_AddShortcut(FOLDERID idFolder)
{
HRESULT hr;
BAR_PERSIST_INFO *pPersist = NULL;
DWORD cbData = 0;
INotify *pNotify = NULL;
// Load the current settings out of the registry. If it fails, that means
// we've never saved our settings before.
if (SUCCEEDED(hr = OutlookBar_LoadSettings(&pPersist)))
{
// Get the size of the current struct and add room for a new folder
cbData = sizeof(BAR_PERSIST_INFO) + (pPersist->cItems * sizeof(FOLDERID));
// Realloc the structure
if (MemRealloc((LPVOID *) &pPersist, cbData))
{
// Add our new button to the end
pPersist->rgFolders[pPersist->cItems] = idFolder;
pPersist->cItems++;
// Save the new settings out
if (SUCCEEDED(OutlookBar_SaveSettings(pPersist, cbData)))
{
// Send notifications
if (SUCCEEDED(CreateNotify(&pNotify)))
{
if (SUCCEEDED(pNotify->Initialize(c_szOutBarNotifyName)))
{
pNotify->Lock(NULL);
pNotify->DoNotification(WM_RELOADSHORTCUTS, 0, 0, SNF_POSTMSG);
pNotify->Unlock();
}
pNotify->Release();
}
}
}
SafeMemFree(pPersist);
}
return (hr);
}
HRESULT OutlookBar_LoadSettings(BAR_PERSIST_INFO **ppPersist)
{
HKEY hKey = 0;
LONG lResult;
DWORD dwType;
BAR_PERSIST_INFO *pPersist = NULL;
DWORD cbData;
HRESULT hr = E_FAIL;
if (!ppPersist)
return (E_INVALIDARG);
// Get the reg key for this user
if (ERROR_SUCCESS != AthUserOpenKey(NULL, KEY_READ, &hKey))
return (hr);
// Get the size of the blob in the registry
lResult = RegQueryValueEx(hKey, COutBar::GetRegKey(), 0, &dwType, NULL, &cbData);
if (ERROR_SUCCESS != lResult)
goto exit;
// Allocate a buffer for the blob in the registry
if (!MemAlloc((LPVOID *) &pPersist, cbData + 1))
{
hr = E_OUTOFMEMORY;
goto exit;
}
// Now get the data from the registry
lResult = RegQueryValueEx(hKey, COutBar::GetRegKey(), 0, &dwType, (LPBYTE) pPersist, &cbData);
if (ERROR_SUCCESS != lResult)
goto exit;
// Check to see if this version matches our version
if (pPersist->dwVersion != COutBar::GetOutlookBarVersion())
goto exit;
// Check to see if the saved time is valid
// $REVIEW - How?
// Double check that the size is correct
if (cbData != (sizeof(BAR_PERSIST_INFO) + ((pPersist->cItems - 1) * sizeof(FOLDERID))))
goto exit;
hr = S_OK;
exit:
if (hKey)
RegCloseKey(hKey);
if (FAILED(hr))
SafeMemFree(pPersist);
*ppPersist = pPersist;
return (hr);
}
HRESULT OutlookBar_SaveSettings(BAR_PERSIST_INFO *pPersist, DWORD cbData)
{
// Open the registry and save the blob
AthUserSetValue(NULL, COutBar::GetRegKey(), REG_BINARY, (const LPBYTE) pPersist, cbData);
return (S_OK);
}
HRESULT COutBar::OnTransaction(HTRANSACTION hTransaction, DWORD_PTR dwCookie, IDatabase *pDB)
{
TRANSACTIONTYPE tyTransaction;
ORDINALLIST Ordinals;
FOLDERINFO Folder1={0};
FOLDERINFO Folder2={0};
DWORD cButtons;
TBBUTTON tbb;
TCHAR szName[2 * MAX_PATH];
LPTSTR pszFree = NULL;
BOOL fChanged;
INDEXORDINAL iIndex;
TBBUTTONINFO tbbi;
int iButton;
if (!IsWindow(m_hwnd))
return (S_OK);
// Get the number of buttons on our bar
cButtons = (DWORD) SendMessage(m_hwndTools, TB_BUTTONCOUNT, 0, 0);
// Walk through the notifications
while (hTransaction)
{
// Get Transact
if (FAILED(pDB->GetTransaction(&hTransaction, &tyTransaction, &Folder1, &Folder2, &iIndex, &Ordinals)))
break;
// Delete
if (TRANSACTION_DELETE == tyTransaction)
{
for (iButton = cButtons - 1; iButton >= 0; iButton--)
{
// Get the button information
ToolBar_GetButton(m_hwndTools, iButton, &tbb);
// If the ID of this button matches the ID that changed
if ((FOLDERID) tbb.dwData == Folder1.idFolder)
{
// Blow it away
SendMessage(m_hwndTools, TB_DELETEBUTTON, iButton, 0);
}
}
}
// Update
else if (TRANSACTION_UPDATE == tyTransaction)
{
// Loop through all our buttons since we might have dupes
for (iButton = cButtons - 1; iButton >= 0; iButton--)
{
fChanged = FALSE;
// Get the button information
ToolBar_GetButton(m_hwndTools, iButton, &tbb);
// If the ID of this button matches the ID that changed
if ((FOLDERID) tbb.dwData == Folder1.idFolder)
{
tbbi.cbSize = sizeof(TBBUTTONINFO);
tbbi.dwMask = TBIF_TEXT | TBIF_IMAGE;
tbbi.pszText = szName;
tbbi.cchText = ARRAYSIZE(szName);
ToolBar_GetButtonInfo(m_hwndTools, tbb.idCommand, &tbbi);
// Unread Change || Folder Renamed
if (Folder1.cUnread != Folder2.cUnread ||
lstrcmp(Folder1.pszName, Folder2.pszName) != 0)
{
if (Folder2.cUnread)
{
DWORD cchSize = ARRAYSIZE(szName);
if (lstrlen(Folder2.pszName) + 13 < ARRAYSIZE(szName))
tbbi.pszText = szName;
else
{
cchSize = (lstrlen(Folder2.pszName) + 14);
if (!MemAlloc((LPVOID*) &pszFree, cchSize * sizeof(TCHAR)))
return FALSE;
tbbi.pszText = pszFree;
}
wnsprintf(tbbi.pszText, cchSize, "%s (%d)", Folder2.pszName, CUnread(&Folder2));
}
else
{
tbbi.pszText = Folder2.pszName;
}
fChanged = TRUE;
}
// synchronize state changed ?
if ((0 == (Folder1.dwFlags & (FOLDER_DOWNLOADHEADERS | FOLDER_DOWNLOADNEW | FOLDER_DOWNLOADALL))) ^
(0 == (Folder2.dwFlags & (FOLDER_DOWNLOADHEADERS | FOLDER_DOWNLOADNEW | FOLDER_DOWNLOADALL))))
{
tbbi.iImage = GetFolderIcon(&Folder2);
fChanged = TRUE;
}
if (ISFLAGSET(Folder1.dwFlags, FOLDER_SUBSCRIBED) != ISFLAGSET(Folder2.dwFlags, FOLDER_SUBSCRIBED))
{
if (ISFLAGSET(Folder2.dwFlags, FOLDER_SUBSCRIBED))
{
tbbi.iImage = GetFolderIcon(&Folder2);
fChanged = TRUE;
}
else
{
SendMessage(m_hwndTools, TB_DELETEBUTTON, iButton, 0);
fChanged = FALSE;
}
}
}
if (fChanged)
{
ToolBar_SetButtonInfo(m_hwndTools, tbb.idCommand, &tbbi);
}
SafeMemFree(pszFree);
}
}
}
pDB->FreeRecord(&Folder1);
pDB->FreeRecord(&Folder2);
return (S_OK);
}
BOOL COutBar::_IsTempNewsgroup(IDataObject *pObject)
{
FORMATETC fe;
STGMEDIUM stm;
FOLDERID *pidFolder;
FOLDERINFO rInfo;
BOOL fReturn = TRUE;
SETDefFormatEtc(fe, CF_OEFOLDER, TYMED_HGLOBAL);
if (SUCCEEDED(pObject->GetData(&fe, &stm)))
{
pidFolder = (FOLDERID *) GlobalLock(stm.hGlobal);
if (SUCCEEDED(g_pStore->GetFolderInfo(*pidFolder, &rInfo)))
{
if ((rInfo.tySpecial == FOLDER_NOTSPECIAL) &&
(rInfo.tyFolder == FOLDER_NEWS) &&
(0 == (rInfo.dwFlags & FOLDER_SUBSCRIBED)))
{
fReturn = FALSE;
}
g_pStore->FreeRecord(&rInfo);
}
GlobalUnlock(stm.hGlobal);
ReleaseStgMedium(&stm);
}
return (fReturn);
}
LPCTSTR COutBar::GetRegKey()
{
LPCTSTR retval;
if (g_dwAthenaMode & MODE_NEWSONLY)
{
retval = c_szRegOutlookBarNewsOnly;
}
else
{
retval = c_szRegOutlookBar;
}
return retval;
}
DWORD COutBar::GetOutlookBarVersion()
{
DWORD retval;
if (g_dwAthenaMode & MODE_NEWSONLY)
{
retval = OUTLOOK_BAR_NEWSONLY_VERSION;
}
else
{
retval = OUTLOOK_BAR_VERSION;
}
return retval;
}