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.
 
 
 
 
 
 

3500 lines
105 KiB

#include "pch.hxx"
#include "treeview.h"
#include "resource.h"
#include "error.h"
#include "fonts.h"
#include "thormsgs.h"
#include "strconst.h"
#include "imagelst.h"
#include "goptions.h"
#include <notify.h>
#include "imnact.h"
#include "menuutil.h"
#include <imnxport.h>
#include <inpobj.h>
#include "fldbar.h"
#include "instance.h"
#include "imnglobl.h"
#include "ddfldbar.h"
#include "ourguid.h"
#include "storutil.h"
#include "shlwapip.h"
#include "demand.h"
#include "newfldr.h"
#include <store.h>
#include "subscr.h"
#include "acctutil.h"
#include "menures.h"
#include "mailutil.h"
#include "dragdrop.h"
#include <storecb.h>
#include "outbar.h"
#include "navpane.h"
#include "finder.h"
#include "goptions.h"
ASSERTDATA
#define idtSelChangeTimer 5
#define C_RGBCOLORS 16
extern const DWORD rgrgbColors16[C_RGBCOLORS];
int CALLBACK TreeViewCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);
////////////////////////////////////////////////////////////////////////
//
// Module Data
//
////////////////////////////////////////////////////////////////////////
static const TCHAR s_szTreeViewWndClass[] = TEXT("ThorTreeViewWndClass");
DWORD CUnread(FOLDERINFO *pfi)
{
DWORD dwUnread = pfi->cUnread;
if (pfi->tyFolder == FOLDER_NEWS)
dwUnread += pfi->dwNotDownloaded;
return(dwUnread);
}
inline BOOL ITreeView_SelectItem(HWND hwnd, HTREEITEM hitem)
{
TreeView_EnsureVisible(hwnd, hitem);
return((BOOL)TreeView_SelectItem(hwnd, hitem));
}
////////////////////////////////////////////////////////////////////////
//
// Constructors, Destructors, and other initialization stuff
//
////////////////////////////////////////////////////////////////////////
CTreeView::CTreeView(ITreeViewNotify *pNotify)
{
m_cRef = 1;
m_hwndParent = NULL;
m_hwnd = NULL;
m_hwndTree = NULL;
m_pBrowser = NULL;
m_hwndUIParent = NULL;
Assert(pNotify);
m_pNotify = pNotify;
m_pObjSite = NULL;
m_xWidth = DwGetOption(OPT_TREEWIDTH);
m_fExpandUnread = DwGetOption(OPT_EXPAND_UNREAD);
m_fShow = FALSE;
m_idSelTimer = 0;
m_htiMenu = NULL;
m_fEditLabel = 0;
m_hitemEdit = NULL;
m_fIgnoreNotify = FALSE;
m_pDataObject = NULL;
m_pFolderBar = NULL;
m_pDTCur = NULL;
m_dwAcctConnIndex = 0;
m_pPaneFrame = NULL;
m_hwndPaneFrame = 0;
m_clrWatched = 0;
}
CTreeView::~CTreeView()
{
if (m_hwnd)
DestroyWindow(m_hwnd);
Assert(g_pStore);
g_pStore->UnregisterNotify((IDatabaseNotify *)this);
if (m_dwAcctConnIndex != 0 && g_pAcctMan != NULL)
g_pAcctMan->Unadvise(m_dwAcctConnIndex);
SafeRelease (m_pObjSite);
SafeRelease(m_pPaneFrame);
}
////////////////////////////////////////////////////////////////////////
//
// IUnknown
//
////////////////////////////////////////////////////////////////////////
HRESULT CTreeView::QueryInterface(REFIID riid, LPVOID * ppvObj)
{
if (IsEqualIID(riid, IID_IUnknown) ||
IsEqualIID(riid, IID_IInputObject))
{
*ppvObj = (void*)(IInputObject*)this;
}
else if (IsEqualIID(riid, IID_IDockingWindow))
{
*ppvObj = (void*)(IDockingWindow *) this;
}
else if (IsEqualIID(riid, IID_IOleWindow))
{
*ppvObj = (void*)(IOleWindow*)this;
}
else if (IsEqualIID(riid, IID_IObjectWithSite))
{
*ppvObj = (void*)(IObjectWithSite*)this;
}
else if (IsEqualIID(riid, IID_IDropDownFldrBar))
{
*ppvObj = (void*)(IDropDownFldrBar*)this;
}
else if (IsEqualIID(riid, IID_IDropTarget))
{
*ppvObj = (LPVOID) (IDropTarget*) this;
}
else if (IsEqualIID(riid, IID_IOleCommandTarget))
{
*ppvObj = (LPVOID) (IOleCommandTarget *) this;
}
else
{
*ppvObj = NULL;
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
ULONG CTreeView::AddRef()
{
DOUTL(4, TEXT("CTreeView::AddRef() - m_cRef = %d"), m_cRef + 1);
return ++m_cRef;
}
ULONG CTreeView::Release()
{
DOUTL(4, TEXT("CTreeView::Release() - m_cRef = %d"), m_cRef - 1);
if (--m_cRef==0)
{
delete this;
return 0;
}
return m_cRef;
}
////////////////////////////////////////////////////////////////////////
//
// IOleWindow
//
////////////////////////////////////////////////////////////////////////
HRESULT CTreeView::GetWindow(HWND * lphwnd)
{
*lphwnd = (m_hwndPaneFrame ? m_hwndPaneFrame : m_hwnd);
return (*lphwnd ? S_OK : E_FAIL);
}
HRESULT CTreeView::ContextSensitiveHelp(BOOL fEnterMode)
{
return E_NOTIMPL;
}
////////////////////////////////////////////////////////////////////////
//
// IDockingWindow
//
////////////////////////////////////////////////////////////////////////
HWND CTreeView::Create(HWND hwndParent, IInputObjectSite *pSiteFrame, BOOL fFrame)
{
m_hwndParent = hwndParent;
if (m_pBrowser)
m_pBrowser->GetWindow(&m_hwndUIParent);
else
m_hwndUIParent = m_hwndParent;
// 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_szTreeViewWndClass, &wc))
{
// We need to register the class
wc.style = 0;
wc.lpfnWndProc = CTreeView::TreeViewWndProc;
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_szTreeViewWndClass;
wc.hIcon = NULL;
wc.hIconSm = NULL;
if (RegisterClassEx(&wc) == 0 && GetLastError() != ERROR_CLASS_ALREADY_EXISTS)
return 0;
}
// Get the handle of the parent window
if (!m_hwndParent)
return 0;
if (fFrame)
{
m_pPaneFrame = new CPaneFrame();
if (!m_pPaneFrame)
return (0);
m_hwndPaneFrame = m_pPaneFrame->Initialize(m_hwndParent, pSiteFrame, idsMNBandTitle);
hwndParent = m_hwndPaneFrame;
}
m_hwnd = CreateWindowEx(WS_EX_CONTROLPARENT, s_szTreeViewWndClass, NULL,
WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CHILD,
0, 0, 0, 0, hwndParent, NULL, g_hInst, (LPVOID) this);
if (!m_hwnd)
{
AssertSz(0, _T("CTreeView::Create() - Failed to create window."));
return 0;
}
if (fFrame)
{
m_pPaneFrame->SetChild(m_hwnd, DISPID_MSGVIEW_FOLDERLIST, m_pBrowser, this);
ShowWindow(m_hwndPaneFrame, SW_SHOW);
}
ShowWindow(m_hwnd, SW_SHOW);
}
return (fFrame ? m_hwndPaneFrame : m_hwnd);
}
////////////////////////////////////////////////////////////////////////
//
// IInputObject
//
////////////////////////////////////////////////////////////////////////
HRESULT CTreeView::UIActivateIO(BOOL fActivate, LPMSG lpMsg)
{
if (fActivate)
{
UnkOnFocusChangeIS(m_pObjSite, (IInputObject*)this, TRUE);
SetFocus(m_hwndTree);
}
return S_OK;
}
HRESULT CTreeView::HasFocusIO(void)
{
HWND hwndFocus = GetFocus();
return (m_fEditLabel || (hwndFocus && (hwndFocus == m_hwndTree || IsChild(m_hwndTree, hwndFocus)))) ? S_OK : S_FALSE;
}
HRESULT CTreeView::TranslateAcceleratorIO(LPMSG pMsg)
{
if (m_fEditLabel)
{
TranslateMessage(pMsg);
DispatchMessage(pMsg);
return (S_OK);
}
return (S_FALSE);
}
////////////////////////////////////////////////////////////////////////
//
// IObjectWithSite
//
////////////////////////////////////////////////////////////////////////
HRESULT CTreeView::SetSite(IUnknown* punkSite)
{
// If we already have a site pointer, release it now
if (m_pObjSite)
{
m_pObjSite->Release();
m_pObjSite = 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_IInputObjectSite, (void **)&m_pObjSite)))
return E_FAIL;
}
return S_OK;
}
HRESULT CTreeView::GetSite(REFIID riid, LPVOID *ppvSite)
{
return E_NOTIMPL;
}
////////////////////////////////////////////////////////////////////////
//
// Public Methods
//
////////////////////////////////////////////////////////////////////////
HRESULT CTreeView::HrInit(DWORD dwFlags, IAthenaBrowser *pBrowser)
{
DWORD dwConnection = 0;
// Locals
HRESULT hr=S_OK;
// Validate
Assert(0 == (~TREEVIEW_FLAGS & dwFlags));
// Save Flags
m_dwFlags = dwFlags;
#ifdef DEBUG
// we have to have a browser if we have context menus
if (0 == (TREEVIEW_DIALOG & dwFlags))
Assert(pBrowser != NULL);
#endif // DEBUG
// Save the Browser, but don't addref (must be a circular refcount problem)
m_pBrowser = pBrowser;
// Register for notifications on the global folder manager
Assert(g_pStore);
hr = g_pStore->RegisterNotify(IINDEX_SUBSCRIBED, REGISTER_NOTIFY_NOADDREF, 0, (IDatabaseNotify *)this);
if (FAILED(hr))
return hr;
if (0 == (TREEVIEW_DIALOG & dwFlags))
{
//Register for notifications from account manager
hr = g_pAcctMan->Advise((IImnAdviseAccount*)this, &m_dwAcctConnIndex);
}
// Done
return S_OK;
}
HRESULT CTreeView::DeInit()
{
HTREEITEM hitem;
Assert(0 == (m_dwFlags & TREEVIEW_DIALOG));
hitem = TreeView_GetRoot(m_hwndTree);
if (hitem != NULL)
SaveExpandState(hitem);
return(S_OK);
}
void CTreeView::HandleMsg(UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_SYSCOLORCHANGE:
case WM_WININICHANGE:
SendMessage(m_hwndTree, msg, wParam, lParam);
break;
}
}
/////////////////////////////////////////////////////////////////////////////
//
// Window procedure and Message handling routines
//
/////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK EXPORT_16 CTreeView::TreeViewWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
CTreeView *pmv;
if (msg == WM_NCCREATE)
{
pmv = (CTreeView *)LPCREATESTRUCT(lp)->lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM)pmv);
}
else
{
pmv = (CTreeView *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
}
Assert(pmv);
return pmv->WndProc(hwnd, msg, wp, lp);
}
////////////////////////////////////////////////////////////////////////
//
// Private Methods
//
////////////////////////////////////////////////////////////////////////
LRESULT CTreeView::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
MSG xmsg;
switch (msg)
{
HANDLE_MSG(hwnd, WM_CREATE, OnCreate);
HANDLE_MSG(hwnd, WM_CONTEXTMENU, OnContextMenu);
HANDLE_MSG(hwnd, WM_NOTIFY, OnNotify);
HANDLE_MSG(hwnd, WM_SIZE, OnSize);
case WM_DESTROY:
//Before the image list is destroyed we should unresgister with the connection manager to avoid
//ending up with a null image list when we get a disconnected notification
if (g_pConMan)
{
g_pConMan->Unadvise(this);
}
if (m_dwAcctConnIndex != 0 && g_pAcctMan != NULL)
{
g_pAcctMan->Unadvise(m_dwAcctConnIndex);
m_dwAcctConnIndex = 0;
}
OptionUnadvise(hwnd);
ImageList_Destroy( TreeView_GetImageList( m_hwndTree, TVSIL_NORMAL ) );
break;
case CM_OPTIONADVISE:
m_fExpandUnread = DwGetOption(OPT_EXPAND_UNREAD);
m_clrWatched = DwGetOption(OPT_WATCHED_COLOR);
break;
case WM_NCDESTROY:
RevokeDragDrop(hwnd);
SetWindowLongPtr(hwnd, GWLP_USERDATA, NULL);
m_hwnd = m_hwndTree = NULL;
break;
case WM_SYSCOLORCHANGE:
case WM_WININICHANGE:
case WM_FONTCHANGE:
SendMessage(m_hwndTree, msg, wParam, lParam);
AdjustItemHeight();
return (0);
case WM_SETFOCUS:
if (m_hwndTree && ((HWND)wParam) != m_hwndTree)
SetFocus(m_hwndTree);
return 0;
case WM_TIMER:
Assert(wParam == idtSelChangeTimer);
KillTimer(hwnd, idtSelChangeTimer);
m_idSelTimer = 0;
if (m_pNotify)
{
FOLDERID idFolder = GetSelection();
m_pNotify->OnSelChange(idFolder);
}
break;
case WMR_CLICKOUTSIDE:
{
BOOL fHide = FALSE;
if (wParam == CLK_OUT_KEYBD || wParam == CLK_OUT_DEACTIVATE)
fHide = TRUE;
else if (wParam == CLK_OUT_MOUSE)
{
HWND hwndParent = GetParent(m_hwnd);
fHide = ((HWND) lParam != hwndParent) && !IsChild(hwndParent, (HWND) lParam);
}
if (fHide)
m_pFolderBar->KillScopeDropDown();
return (fHide);
}
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
HRESULT CTreeView::ForceSelectionChange()
{
if (m_idSelTimer != 0)
{
KillTimer(m_hwnd, idtSelChangeTimer);
m_idSelTimer = 0;
if (m_pNotify)
{
FOLDERID idFolder = GetSelection();
m_pNotify->OnSelChange(idFolder);
}
return(S_OK);
}
return(S_FALSE);
}
void CTreeView::AdjustItemHeight()
{
int cyItem, cyText, cyBorder;
HDC hdc;
TCHAR c = TEXT('J');
SIZE size;
if (0 == (m_dwFlags & TREEVIEW_DIALOG))
{
hdc = GetDC(m_hwndTree);
cyBorder = GetSystemMetrics(SM_CYBORDER);
GetTextExtentPoint(hdc, &c, 1, &size);
cyText = size.cy;
if (cyText < 16)
cyText = 16; // icon size
cyText = cyText + (cyBorder * 2);
ReleaseDC(m_hwndTree, hdc);
cyItem = TreeView_GetItemHeight(m_hwndTree);
if (cyText > cyItem)
TreeView_SetItemHeight(m_hwndTree, cyText);
}
}
BOOL CTreeView::OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
{
HIMAGELIST himl;
TCHAR szName[CCHMAX_STRINGRES];
ZeroMemory(szName, sizeof(szName));
LoadString(g_hLocRes, idsFolderListTT, szName, ARRAYSIZE(szName));
m_hwndTree = CreateWindowEx((0 == (m_dwFlags & TREEVIEW_DIALOG)) ? 0 : WS_EX_CLIENTEDGE,
WC_TREEVIEW, szName,
WS_CHILD | WS_VISIBLE | WS_TABSTOP | TVS_SHOWSELALWAYS |
TVS_HASBUTTONS | TVS_HASLINES |
(0 == (m_dwFlags & TREEVIEW_DIALOG) ? TVS_EDITLABELS : TVS_DISABLEDRAGDROP),
0, 0, 0, 0, hwnd, NULL, g_hInst, NULL);
if (!m_hwndTree)
return FALSE;
himl = ImageList_LoadBitmap(g_hLocRes, MAKEINTRESOURCE(idbFolders), 16, 0, RGB(255, 0, 255)); // small icons
TreeView_SetImageList(m_hwndTree, himl, TVSIL_NORMAL);
AdjustItemHeight();
m_clrWatched = DwGetOption(OPT_WATCHED_COLOR);
// Register ourselves as a drop target
if (0 == (m_dwFlags & TREEVIEW_DIALOG))
{
RegisterDragDrop(hwnd, this);
// Register ourselves with th connection manager so we can overlay
// the disconnected image when it notifies us of a disconnection
if (g_pConMan)
g_pConMan->Advise((IConnectionNotify *) this);
OptionAdvise(hwnd);
}
return TRUE;
}
//
// FUNCTION: CTreeView::OnNotify
//
// PURPOSE: Processes the various notifications we receive from our child
// controls.
//
// PARAMETERS:
// hwnd - Handle of the msgview window.
// idCtl - identifies the control sending the notification
// pnmh - points to a NMHDR struct with more information regarding the
// notification
//
// RETURN VALUE:
// Dependant on the specific notification.
//
LRESULT CTreeView::OnNotify(HWND hwnd, int idFrom, LPNMHDR pnmhdr)
{
LPFOLDERNODE pNode;
NM_TREEVIEW *pnmtv;
// This is necessary to prevent handling of notification after the
// listview window has been destroyed.
if (!m_hwndTree)
return 0;
switch (pnmhdr->code)
{
case TVN_BEGINLABELEDIT:
return OnBeginLabelEdit((TV_DISPINFO *) pnmhdr);
case TVN_ENDLABELEDIT:
return (OnEndLabelEdit((TV_DISPINFO *) pnmhdr));
case TVN_BEGINDRAG:
Assert(0 == (m_dwFlags & TREEVIEW_DIALOG));
return OnBeginDrag((NM_TREEVIEW*) pnmhdr);
case TVN_DELETEITEM:
pnmtv = (NM_TREEVIEW *)pnmhdr;
pNode = (LPFOLDERNODE)pnmtv->itemOld.lParam;
if (pNode)
{
g_pStore->FreeRecord(&pNode->Folder);
g_pMalloc->Free(pNode);
}
break;
case TVN_SELCHANGED:
pnmtv = (NM_TREEVIEW *)pnmhdr;
if (0 == (m_dwFlags & TREEVIEW_DIALOG))
{
if (m_idSelTimer)
KillTimer(m_hwnd, idtSelChangeTimer);
m_idSelTimer = SetTimer(m_hwnd,
idtSelChangeTimer,
(pnmtv->action == TVC_BYKEYBOARD) ? GetDoubleClickTime()*3/2 : 1,
NULL);
if (m_pFolderBar)
m_pFolderBar->KillScopeDropDown();
}
else
{
if (m_pNotify)
{
FOLDERID idFolder = GetSelection();
m_pNotify->OnSelChange(idFolder);
}
}
break;
case TVN_ITEMEXPANDING:
// TODO: remove this as soon as the folder enumerator
// returns us the folders in the proper sorted order
pnmtv = (NM_TREEVIEW *)pnmhdr;
if (pnmtv->action == TVE_EXPAND)
SortChildren(pnmtv->itemNew.hItem);
break;
case NM_CUSTOMDRAW:
return(OnCustomDraw((NMCUSTOMDRAW *)pnmhdr));
case NM_SETFOCUS:
if (IsWindowVisible(m_hwnd))
UnkOnFocusChangeIS(m_pObjSite, (IInputObject*)this, TRUE);
break;
case NM_KILLFOCUS:
if (m_pFolderBar)
m_pFolderBar->KillScopeDropDown();
break;
case NM_DBLCLK:
if (m_pNotify)
{
FOLDERID idFolder = GetSelection();
m_pNotify->OnDoubleClick(idFolder);
}
break;
}
return 0;
}
LRESULT CTreeView::OnCustomDraw(NMCUSTOMDRAW *pnmcd)
{
TCHAR szNum[CCHMAX_STRINGRES];
TCHAR szText[CCHMAX_STRINGRES];
RECT rc;
COLORREF cr;
int cb, cb1;
COLORREF crOldBkColr = 0;
COLORREF crOldTxtColr = 0;
COLORREF crBkColor = 0;
COLORREF crTxtColor = 0;
TV_ITEM tv;
LPFOLDERNODE pNode;
FOLDERINFO Folder;
NMTVCUSTOMDRAW *ptvcd = (NMTVCUSTOMDRAW *) pnmcd;
switch (pnmcd->dwDrawStage)
{
case CDDS_PREPAINT:
// if we're in dialog-mode, we don't unread count displayed
return((0 == (m_dwFlags & TREEVIEW_DIALOG)) ? (CDRF_NOTIFYPOSTPAINT | CDRF_NOTIFYITEMDRAW) : CDRF_DODEFAULT);
case CDDS_ITEMPREPAINT:
//If this item is disconnected then we gray out the text
pNode = (LPFOLDERNODE)pnmcd->lItemlParam;
if (pNode && 0 == (pnmcd->uItemState & CDIS_SELECTED))
{
if ((pNode->Folder.cWatchedUnread) && (m_clrWatched > 0 && m_clrWatched <= 16))
{
ptvcd->clrText = rgrgbColors16[m_clrWatched - 1];
}
else if (!!(FIDF_DISCONNECTED & pNode->dwFlags))
{
crTxtColor = GetSysColor(COLOR_GRAYTEXT);
crBkColor = GetSysColor(COLOR_BACKGROUND);
if ((crTxtColor) || (crBkColor != crTxtColor))
{
ptvcd->clrText = crTxtColor;
}
else if ((crBkColor == crTxtColor) && (crTxtColor = GetSysColor(COLOR_INACTIVECAPTIONTEXT)))
{
ptvcd->clrText = crTxtColor;
}
}
}
// if we're editing the label for this item, we don't want to paint the unread count
return((m_fEditLabel && m_hitemEdit == (HTREEITEM)pnmcd->dwItemSpec) ? CDRF_DODEFAULT : CDRF_NOTIFYPOSTPAINT);
case CDDS_ITEMPOSTPAINT:
// now we need to paint the unread count, if any...
pNode = (LPFOLDERNODE)pnmcd->lItemlParam;
if (CUnread(&pNode->Folder) > 0)
{
HFONT hf = (HFONT) ::SendMessage(m_hwndTree, WM_GETFONT, 0, 0);
HFONT hf2 = SelectFont(pnmcd->hdc, hf);
cr = SetTextColor(pnmcd->hdc, RGB(0, 0, 0xff));
if (cr != CLR_INVALID)
{
if (TreeView_GetItemRect(m_hwndTree, (HTREEITEM)pnmcd->dwItemSpec, &rc, TRUE))
{
TCHAR c = TEXT('J');
SIZE size;
//$REVIEW - this size could be cached, but it doesn't seem to be a problem
GetTextExtentPoint(pnmcd->hdc, &c, 1, &size);
cb = wnsprintf(szNum, ARRAYSIZE(szNum), "(%d)", CUnread(&pNode->Folder));
TextOut(pnmcd->hdc, rc.right + 2, rc.top + (rc.bottom - rc.top - size.cy) / 2, szNum, cb);
}
SetTextColor(pnmcd->hdc, cr);
}
SelectFont(pnmcd->hdc, hf2);
}
break;
}
return (CDRF_DODEFAULT);
}
//
// FUNCTION: CTreeView::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 CTreeView::OnContextMenu(HWND hwnd, HWND hwndClick, int x, int y)
{
IContextMenu *pContextMenu;
LPFOLDERNODE pNode;
HRESULT hr;
CMINVOKECOMMANDINFO ici;
HMENU hmenu;
int id = 0;
RECT rc;
POINT pt = {(int)(short) x, (int)(short) y};
TV_HITTESTINFO tvhti;
HTREEITEM hti = 0;
int idMenu = 0;
HWND hwndBrowser = 0;
if (!!(m_dwFlags & TREEVIEW_DIALOG))
return;
// Get the browser window from the IAthenaBrowser interface. If we don't
// use the browser window to pass to IContextMenu, then when the treeview
// is in autohide mode, the mouse capture goes beserk.
if (FAILED(m_pBrowser->GetWindow(&hwndBrowser)))
return;
if (MAKELPARAM(x, y) == -1) // invoked from keyboard: figure out pos.
{
Assert(hwndClick == m_hwndTree);
hti = TreeView_GetSelection(m_hwndTree);
TreeView_GetItemRect(m_hwndTree, hti, &rc, FALSE);
ClientToScreen(m_hwndTree, (POINT *)&rc);
x = rc.left;
y = rc.top;
}
else
{
ScreenToClient(m_hwndTree, &pt);
tvhti.pt = pt;
hti = TreeView_HitTest(m_hwndTree, &tvhti);
}
if (hti == NULL)
return;
TreeView_SelectDropTarget(m_hwndTree, hti);
// Get the ID of the selected item
pNode = GetFolderNode(hti);
if (pNode)
{
// Load the appropriate context menu
if (SUCCEEDED(MenuUtil_GetContextMenu(pNode->Folder.idFolder, this, &hmenu)))
{
// Display the context menu
id = (int) TrackPopupMenuEx(hmenu,
TPM_RETURNCMD | TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
x, y, m_hwnd, NULL);
// If an ID was returned, process it
if (id != 0)
Exec(NULL, id, OLECMDEXECOPT_DODEFAULT, NULL, NULL);
DestroyMenu(hmenu);
}
}
TreeView_SelectDropTarget(m_hwndTree, NULL);
}
//
// FUNCTION: CTreeView::OnSize
//
// PURPOSE: Notification that the window has been resized. In
// response we update the positions of our child windows and
// controls.
//
// PARAMETERS:
// hwnd - Handle of the view window being resized.
// state - Type of resizing requested.
// cxClient - New width of the client area.
// cyClient - New height of the client area.
//
void CTreeView::OnSize(HWND hwnd, UINT state, int cxClient, int cyClient)
{
SetWindowPos(m_hwndTree, NULL, 0, 0,
cxClient, cyClient,
SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOMOVE);
}
enum
{
UNINIT = 0,
LOCAL,
CONNECTED,
DISCONNECTED
};
HRESULT CTreeView::GetConnectedState(FOLDERINFO *pFolder, int *pconn)
{
HRESULT hr;
char szAcctId[CCHMAX_ACCOUNT_NAME];
Assert(pFolder != NULL);
Assert(pconn != NULL);
*pconn = UNINIT;
if (!!(m_dwFlags & TREEVIEW_DIALOG))
{
// we don't care about connect state in dialogs
// only in the folder list
*pconn = LOCAL;
}
else
{
Assert(pFolder->idFolder != FOLDERID_ROOT);
if (pFolder->tyFolder == FOLDER_LOCAL)
{
*pconn = LOCAL;
}
/*
else if (!!(pFolder->dwFlags & FOLDER_SERVER))
{
*pconn = (g_pConMan->CanConnect(pFolder->pszAccountId) == S_OK) ? CONNECTED : DISCONNECTED;
}
else
{
hr = GetFolderAccountId(pFolder, szAcctId);
if (SUCCEEDED(hr))
*pconn = (g_pConMan->CanConnect(szAcctId) == S_OK) ? CONNECTED : DISCONNECTED;
}
*/
else
{
*pconn = g_pConMan->IsGlobalOffline() ? DISCONNECTED : CONNECTED;
}
}
return(S_OK);
}
HRESULT CTreeView::HrFillTreeView()
{
HRESULT hr;
TV_INSERTSTRUCT tvis;
HTREEITEM hitem;
TCHAR sz[CCHMAX_STRINGRES];
BOOL fUnread;
LPFOLDERNODE pNode;
if (MODE_OUTLOOKNEWS == (g_dwAthenaMode & MODE_OUTLOOKNEWS))
LoadString(g_hLocRes, idsOutlookNewsReader, sz, ARRAYSIZE(sz));
else
LoadString(g_hLocRes, idsAthena, sz, ARRAYSIZE(sz));
tvis.hParent = TVI_ROOT;
tvis.hInsertAfter = TVI_LAST;
tvis.item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM | TVIF_TEXT;
tvis.item.pszText = sz;
if (g_dwAthenaMode & MODE_NEWSONLY)
tvis.item.iImage = iNewsRoot;
else
tvis.item.iImage = iMailNews;
tvis.item.iSelectedImage = tvis.item.iImage;
pNode = (LPFOLDERNODE)ZeroAllocate(sizeof(FOLDERNODE));
if (NULL == pNode)
return E_OUTOFMEMORY;
tvis.item.lParam = (LPARAM)pNode;
hitem = TreeView_InsertItem(m_hwndTree, &tvis);
g_pStore->GetFolderInfo(FOLDERID_ROOT, &pNode->Folder);
hr = FillTreeView2(hitem, &pNode->Folder, 0 == (m_dwFlags & TREEVIEW_DIALOG), UNINIT, &fUnread);
TreeView_Expand(m_hwndTree, hitem, TVE_EXPAND);
return (hr);
}
HRESULT CTreeView::FillTreeView2(HTREEITEM hParent, LPFOLDERINFO pParent,
BOOL fInitExpand, int conn, BOOL *pfUnread)
{
// Locals
HRESULT hr=S_OK;
int connT;
TV_INSERTSTRUCT tvis;
HTREEITEM hitem;
BOOL fNoLocal;
BOOL fNoNews;
BOOL fNoImap;
BOOL fNoHttp;
BOOL fExpand = FALSE;
BOOL fUnread = FALSE;
FOLDERINFO Folder;
LPFOLDERNODE pNode;
IEnumerateFolders *pEnum=NULL;
// Trace
TraceCall("CTreeView::FillTreeView2");
// Initialize
*pfUnread = FALSE;
// Enumerate Children
IF_FAILEXIT(hr = g_pStore->EnumChildren(pParent->idFolder, TRUE, &pEnum));
// Determine what to show
fNoNews = ISFLAGSET(m_dwFlags, TREEVIEW_NONEWS);
fNoImap = ISFLAGSET(m_dwFlags, TREEVIEW_NOIMAP);
fNoHttp = ISFLAGSET(m_dwFlags, TREEVIEW_NOHTTP);
fNoLocal = ISFLAGSET(m_dwFlags, TREEVIEW_NOLOCAL);
// Enumerate the Sub Folders
while (S_OK == pEnum->Next(1, &Folder, NULL))
{
// Is this node hidden ?
if ((fNoNews && Folder.tyFolder == FOLDER_NEWS) ||
(fNoImap && Folder.tyFolder == FOLDER_IMAP) ||
(fNoHttp && Folder.tyFolder == FOLDER_HTTPMAIL) ||
(fNoLocal && Folder.tyFolder == FOLDER_LOCAL) ||
((g_dwAthenaMode & MODE_OUTLOOKNEWS) && (Folder.tySpecial == FOLDER_INBOX)))
{
// Goto next
g_pStore->FreeRecord(&Folder);
continue;
}
// Some Connection management Thing?
if (conn == UNINIT)
GetConnectedState(&Folder, &connT);
else
connT = conn;
// Set the insert item struct
tvis.hParent = hParent;
tvis.hInsertAfter = TVI_LAST;
//
if (ISFLAGCLEAR(Folder.dwFlags, FOLDER_HIDDEN) &&
(ISFLAGSET(Folder.dwFlags, FOLDER_HASCHILDREN) || ISFLAGSET(Folder.dwFlags, FOLDER_SERVER) ||
ISFLAGSET(Folder.dwFlags, FOLDER_SUBSCRIBED)))
{
// Allocate a pNode
IF_NULLEXIT(pNode = (LPFOLDERNODE)ZeroAllocate(sizeof(FOLDERNODE)));
// Set the Folder Info
CopyMemory(&pNode->Folder, &Folder, sizeof(FOLDERINFO));
// Set the Flags
pNode->dwFlags = (connT == DISCONNECTED ? FIDF_DISCONNECTED : 0);
// Don't free Folder
Folder.pAllocated = NULL;
// Insert this item
hitem = ITreeView_InsertItem(&tvis, pNode);
// Has unread
fUnread = (fUnread || (CUnread(&Folder) > 0));
// Insert this node's children ?
if (ISFLAGSET(pNode->Folder.dwFlags, FOLDER_HASCHILDREN))
{
// Fill with children
FillTreeView2(hitem, &pNode->Folder, fExpand, connT, pfUnread);
}
// Need to expand ?
fExpand = (fInitExpand && ISFLAGSET(pNode->Folder.dwFlags, FOLDER_HASCHILDREN) && !!(pNode->Folder.dwFlags & FOLDER_EXPANDTREE));
}
// Otherwise, the node was not inserted
else
hitem = NULL;
// Expand this node ?
if (hitem && *pfUnread && m_fExpandUnread)
{
// Expand this node
TreeView_Expand(m_hwndTree, hitem, TVE_EXPAND);
// There are unread
fUnread = TRUE;
}
// Expand this node ?
else if (hitem && fExpand)
{
// Expand this node
TreeView_Expand(m_hwndTree, hitem, TVE_EXPAND);
}
// Free Current
g_pStore->FreeRecord(&Folder);
}
// Was there unread folders ?
*pfUnread = fUnread;
exit:
// Cleanup
SafeRelease(pEnum);
// Done
return(hr);
}
HTREEITEM CTreeView::ITreeView_InsertItem(TV_INSERTSTRUCT *ptvis, LPFOLDERNODE pNode)
{
Assert(ptvis != NULL);
Assert(pNode != NULL);
ptvis->item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM | TVIF_TEXT;
ptvis->item.iImage = GetFolderIcon(&pNode->Folder, !!(m_dwFlags & TREEVIEW_DIALOG));
ptvis->item.iSelectedImage = ptvis->item.iImage;
ptvis->item.lParam = (LPARAM)pNode;
if (0 == (m_dwFlags & TREEVIEW_DIALOG) && CUnread(&pNode->Folder) > 0)
{
ptvis->item.mask |= TVIF_STATE;
ptvis->item.state = TVIS_BOLD;
ptvis->item.stateMask = TVIS_BOLD;
}
ptvis->item.pszText = pNode->Folder.pszName;
return(TreeView_InsertItem(m_hwndTree, ptvis));
}
BOOL CTreeView::ITreeView_SetItem(TV_ITEM *ptvi, LPFOLDERINFO pFolder)
{
BOOL f;
Assert(ptvi != NULL);
ptvi->mask |= (TVIF_STATE | TVIF_TEXT);
ptvi->stateMask = TVIS_BOLD;
if (0 == (m_dwFlags & TREEVIEW_DIALOG) && CUnread(pFolder) > 0)
ptvi->state = TVIS_BOLD;
else
ptvi->state = 0;
ptvi->pszText = pFolder->pszName;
f = TreeView_SetItem(m_hwndTree, ptvi);
return f;
}
HRESULT CTreeView::Refresh(void)
{
TV_ITEM item;
FOLDERID idFolder;
if (IsWindow(m_hwndTree))
{
idFolder = GetSelection();
TreeView_DeleteAllItems(m_hwndTree);
m_fIgnoreNotify = TRUE;
HrFillTreeView();
if (FOLDERID_INVALID != idFolder)
{
SetSelection(idFolder, 0);
}
m_fIgnoreNotify = FALSE;
}
return (S_OK);
}
FOLDERID CTreeView::GetSelection()
{
HTREEITEM hitem;
FOLDERID idFolder=FOLDERID_INVALID;
if (IsWindow(m_hwndTree))
{
hitem = TreeView_GetSelection(m_hwndTree);
if (hitem != NULL)
{
TV_ITEM tvi;
tvi.mask = TVIF_PARAM;
tvi.lParam = 0;
tvi.hItem = hitem;
if (TreeView_GetItem(m_hwndTree, &tvi))
{
LPFOLDERNODE pNode=(LPFOLDERNODE)tvi.lParam;
if (pNode)
idFolder = pNode->Folder.idFolder;
}
}
}
return (idFolder);
}
HRESULT CTreeView::SetSelection(FOLDERID idFolder, DWORD dwFlags)
{
HRESULT hr;
HTREEITEM hitem;
hr = E_FAIL;
if (IsWindow(m_hwndTree))
{
hitem = GetItemFromId(idFolder);
if (hitem == NULL && !!(dwFlags & TVSS_INSERTIFNOTFOUND))
{
hitem = InsertNode(idFolder, 0);
if (NULL != hitem)
hr = S_OK;
}
if (hitem != NULL && ITreeView_SelectItem(m_hwndTree, hitem))
hr = S_OK;
}
return(hr);
}
HRESULT CTreeView::SelectParent()
{
HRESULT hr = E_FAIL;
HTREEITEM hitem;
if (hitem = TreeView_GetSelection(m_hwndTree))
{
if (hitem = TreeView_GetParent(m_hwndTree, hitem))
{
if (ITreeView_SelectItem(m_hwndTree, hitem))
hr = S_OK;
}
}
return(hr);
}
struct TREEUNREAD
{
HWND hwndTree;
HTREEITEM hitemSel;
BOOL fFoundSel;
FOLDERTYPE tyFolder;
};
HTREEITEM FindNextUnreadItem(HTREEITEM hitemCur, TREEUNREAD *ptu)
{
HTREEITEM hitem;
if (hitemCur == ptu->hitemSel)
ptu->fFoundSel = TRUE;
else if (ptu->fFoundSel)
{
TV_ITEM tvi;
tvi.mask = TVIF_PARAM;
tvi.lParam = 0;
tvi.hItem = hitemCur;
if (TreeView_GetItem(ptu->hwndTree, &tvi))
{
LPFOLDERNODE pNode = (LPFOLDERNODE)tvi.lParam;
if (pNode)
{
if (CUnread(&pNode->Folder))
return hitemCur;
}
}
}
if (hitem = TreeView_GetChild(ptu->hwndTree, hitemCur))
{
if (hitem = FindNextUnreadItem(hitem, ptu))
return hitem;
}
if (hitemCur = TreeView_GetNextSibling(ptu->hwndTree, hitemCur))
{
if (hitem = FindNextUnreadItem(hitemCur, ptu))
return hitem;
}
return NULL;
}
HRESULT CTreeView::SelectNextUnreadItem()
{
HRESULT hr = E_FAIL;
TREEUNREAD tu;
HTREEITEM hitem;
TV_ITEM tvi;
if (tu.hitemSel = TreeView_GetSelection(m_hwndTree))
{
tvi.mask = TVIF_PARAM;
tvi.lParam = 0;
tvi.hItem = tu.hitemSel;
if (TreeView_GetItem(m_hwndTree, &tvi) && tvi.lParam)
{
LPFOLDERNODE pNode = (LPFOLDERNODE)tvi.lParam;
tu.hwndTree = m_hwndTree;
tu.fFoundSel = FALSE;
tu.tyFolder = pNode->Folder.tyFolder;
if (hitem = TreeView_GetRoot(m_hwndTree))
{
if (hitem = FindNextUnreadItem(hitem, &tu))
{
if (ITreeView_SelectItem(m_hwndTree, hitem))
hr = S_OK;
}
}
}
}
if (FAILED(hr))
{
AthMessageBoxW(m_hwnd, MAKEINTRESOURCEW(idsAthena), MAKEINTRESOURCEW(idsNoMoreUnreadFolders),
0, MB_ICONINFORMATION | MB_OK);
}
return hr;
}
HTREEITEM CTreeView::GetItemFromId(FOLDERID idFolder)
{
if (FOLDERID_ROOT == idFolder)
return TreeView_GetRoot(m_hwndTree);
return FindKid(TreeView_GetRoot(m_hwndTree), idFolder);
}
HTREEITEM CTreeView::FindKid(HTREEITEM hitem, FOLDERID idFolder)
{
TV_ITEM item;
HTREEITEM hChild;
HTREEITEM hFound;
hitem = TreeView_GetChild(m_hwndTree, hitem);
while (hitem != NULL)
{
item.hItem = hitem;
item.mask = TVIF_PARAM;
if (TreeView_GetItem(m_hwndTree, &item))
{
LPFOLDERNODE pNode=(LPFOLDERNODE)item.lParam;
if (pNode && pNode->Folder.idFolder == idFolder)
return hitem;
}
hFound = FindKid(hitem, idFolder);
if (hFound)
return hFound;
hitem = TreeView_GetNextSibling(m_hwndTree, hitem);
}
return(NULL);
}
HTREEITEM CTreeView::InsertNode(FOLDERID idFolder, DWORD dwFlags)
{
// Locals
HRESULT hr=S_OK;
LPFOLDERNODE pNode=NULL;
INT conn;
TV_ITEM item;
BOOL fUnread;
HTREEITEM hitem;
HTREEITEM hitemChild;
HTREEITEM hitemNew=NULL;
TV_INSERTSTRUCT tvis;
RECT rc;
FOLDERINFO Folder={0};
// Trace
TraceCall("CTreeView::InsertNode");
// Get parent of idFolder
IF_FAILEXIT(hr = g_pStore->GetFolderInfo(idFolder, &Folder));
// If this is unsubscribed IMAP fldr and we're in show subscribed only, don't show
if (ISFLAGSET(dwFlags, TVIN_CHECKVISIBILITY))
{
// Hidden
if (ISFLAGSET(Folder.dwFlags, FOLDER_HIDDEN) || !ISFLAGSET(Folder.dwFlags, FOLDER_SUBSCRIBED))
goto exit;
// IE5 Bug #55075: We should never insert a folder which has any unsubscribed ancestors
if (FOLDER_IMAP == Folder.tyFolder)
{
FOLDERINFO fiCurrent;
HRESULT hrTemp;
fiCurrent.idParent = Folder.idParent;
while (FOLDERID_INVALID != fiCurrent.idParent && FOLDERID_ROOT != fiCurrent.idParent)
{
hrTemp = g_pStore->GetFolderInfo(fiCurrent.idParent, &fiCurrent);
TraceError(hrTemp);
if (SUCCEEDED(hrTemp))
{
DWORD dwCurrentFlags = fiCurrent.dwFlags;
g_pStore->FreeRecord(&fiCurrent);
if (FOLDER_SERVER & dwCurrentFlags)
// Do not take server node subscription status into account: stop right here
break;
if (ISFLAGCLEAR(dwCurrentFlags, FOLDER_SUBSCRIBED))
goto exit; // Unsubscribed ancestor! NOT VISIBLE
}
} // while
}
}
// Check for duplicates ?
if (ISFLAGSET(dwFlags, TVIN_CHECKFORDUPS))
{
// Find the hitem for idFolder
hitem = GetItemFromId(idFolder);
// Does it Exist ?
if (hitem != NULL)
{
// Setup an Item
item.hItem = hitem;
item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
// Get the Icon
LONG iIcon = GetFolderIcon(&Folder, !!(m_dwFlags & TREEVIEW_DIALOG));
// Change the Icon
if (TreeView_GetItem(m_hwndTree, &item) && item.iImage != iIcon)
{
// Set the Icon
item.iImage = iIcon;
item.iSelectedImage = item.iImage;
// Update the Item
TreeView_SetItem(m_hwndTree, &item);
}
// Set hitemNew
hitemNew = hitem;
// Done
goto exit;
}
}
// If the parent of this new node is the root ?
if (FOLDERID_INVALID == Folder.idParent)
{
// The root is the parent
hitem = TreeView_GetRoot(m_hwndTree);
}
// Otherwise, get the parent...
else
{
// Find the hitem for the parent
hitem = GetItemFromId(Folder.idParent);
// No parent found, insert a parent
if (hitem == NULL)
{
// Insert the parent, but don't insert any of its children or there will be duplicates
hitem = InsertNode(Folder.idParent, TVIN_DONTINSERTCHILDREN);
// Can't be NULL
Assert(hitem != NULL);
}
}
// Do we have a parent
if (hitem != NULL)
{
// Get the First Child
hitemChild = TreeView_GetChild(m_hwndTree, hitem);
// Setup an Insert struct
tvis.hParent = hitem;
tvis.hInsertAfter = TVI_LAST;
// Get the Connected State
GetConnectedState(&Folder, &conn);
// Allocate a pNode
IF_NULLEXIT(pNode = (LPFOLDERNODE)ZeroAllocate(sizeof(FOLDERNODE)));
// Set the Folder Info
CopyMemory(&pNode->Folder, &Folder, sizeof(FOLDERINFO));
// Set the Flags
pNode->dwFlags = (conn == DISCONNECTED ? FIDF_DISCONNECTED : 0);
// Don't free Folder
Folder.pAllocated = NULL;
// Insert a new item
hitemNew = ITreeView_InsertItem(&tvis, pNode);
// Better not fail
Assert(hitemNew != NULL);
// If there are children
if (0 == (dwFlags & TVIN_DONTINSERTCHILDREN) && !!(Folder.dwFlags & FOLDER_HASCHILDREN))
FillTreeView2(hitemNew, &Folder, FALSE, conn, &fUnread);
// Sort this parent children
SortChildren(hitem);
// TODO: we shouldn't have to do this. figure out why the parent isn't getting '+' after the insert
if (hitemChild == NULL)
{
// Get the Rect of the parent
TreeView_GetItemRect(m_hwndTree, hitem, &rc, FALSE);
// Invalidate it so that it repaints
InvalidateRect(m_hwndTree, &rc, TRUE);
}
// Make sure the new node is visible
if (hitemNew)
{
// Is visible
TreeView_EnsureVisible(m_hwndTree, hitemNew);
}
}
exit:
// Cleanup
g_pStore->FreeRecord(&Folder);
// Done
return(hitemNew);
}
HTREEITEM CTreeView::MoveNode(FOLDERID idFolder, FOLDERID idParentNew)
{
// Locals
HRESULT hr=S_OK;
INT conn;
BOOL fUnread;
LPFOLDERNODE pNode;
HTREEITEM hParent;
HTREEITEM hItemNew=NULL;
TV_INSERTSTRUCT tvis;
// Trace
TraceCall("CTreeView::MoveNode");
// Delete the Current Node
DeleteNode(idFolder);
// Allocate a pNode
IF_NULLEXIT(pNode = (LPFOLDERNODE)ZeroAllocate(sizeof(FOLDERNODE)));
// Get the Parent Info
IF_FAILEXIT(hr = g_pStore->GetFolderInfo(idFolder, &pNode->Folder));
// Get the Parent hTreeItem
hParent = GetItemFromId(idParentNew);
// Fill Insert Item
tvis.hParent = hParent;
tvis.hInsertAfter = TVI_LAST;
// Get Connected State
GetConnectedState(&pNode->Folder, &conn);
// Set the Flags
pNode->dwFlags = (conn == DISCONNECTED ? FIDF_DISCONNECTED : 0);
// Insert a new item
hItemNew = ITreeView_InsertItem(&tvis, pNode);
// Fill the treeview with the children of Folder
FillTreeView2(hItemNew, &pNode->Folder, FALSE, conn, &fUnread);
// Sort the Parent
SortChildren(hParent);
// Return the new hitem
Assert(hItemNew == GetItemFromId(idFolder));
exit:
// Done
return(hItemNew);
}
BOOL CTreeView::DeleteNode(FOLDERID idFolder)
{
HTREEITEM hitem;
BOOL fRet;
hitem = GetItemFromId(idFolder);
if (hitem != NULL)
{
fRet = TreeView_DeleteItem(m_hwndTree, hitem);
Assert(fRet);
}
return(hitem != NULL);
}
STDMETHODIMP CTreeView::OnTransaction(HTRANSACTION hTransaction, DWORD_PTR dwCookie, IDatabase *pDB)
{
// Locals
HTREEITEM hitem;
HTREEITEM hitemSelected;
HTREEITEM hitemNew;
TV_ITEM tvi;
FOLDERINFO Folder1={0};
FOLDERINFO Folder2={0};
TRANSACTIONTYPE tyTransaction;
ORDINALLIST Ordinals;
INDEXORDINAL iIndex;
// Trace
TraceCall("CTreeView::OnRecordNotify");
if (m_hwndTree == NULL)
return(S_OK);
// Walk Through Notifications
while (hTransaction)
{
// Set Notification Stuff
if (FAILED(pDB->GetTransaction(&hTransaction, &tyTransaction, &Folder1, &Folder2, &iIndex, &Ordinals)))
break;
// Insert (new Folder notification)
if (TRANSACTION_INSERT == tyTransaction)
{
// Insert the node
InsertNode(Folder1.idFolder, TVIN_CHECKFORDUPS | TVIN_CHECKVISIBILITY);
}
// Update
else if (TRANSACTION_UPDATE == tyTransaction)
{
// Visibility change (subscription or hidden)
if (ISFLAGSET(Folder1.dwFlags, FOLDER_SUBSCRIBED) != ISFLAGSET(Folder2.dwFlags, FOLDER_SUBSCRIBED) ||
ISFLAGSET(Folder1.dwFlags, FOLDER_HIDDEN) != ISFLAGSET(Folder2.dwFlags, FOLDER_HIDDEN))
{
// Insert the node
if (ISFLAGSET(Folder2.dwFlags, FOLDER_SUBSCRIBED) && ISFLAGCLEAR(Folder2.dwFlags, FOLDER_HIDDEN))
{
// Show the node
InsertNode(Folder2.idFolder, TVIN_CHECKFORDUPS | TVIN_CHECKVISIBILITY);
}
// Remove the Node
else
{
// Delete the Node
OnNotifyDeleteNode(Folder2.idFolder);
}
}
// Otherwise
else
{
// Unread Change
if (CUnread(&Folder1) != CUnread(&Folder2))
{
// Get the Item
hitem = GetItemFromId(Folder1.idFolder);
// If we found it
if (hitem != NULL)
{
// Initialize the item
tvi.hItem = hitem;
tvi.mask = TVIF_PARAM | TVIF_STATE;
// Get the Items
if (TreeView_GetItem(m_hwndTree, &tvi) && tvi.lParam)
{
// This sets bold state for us and forces a repaint...
ITreeView_SetItem(&tvi, &Folder2);
// Expand ?
if (!ISFLAGSET(tvi.state, TVIS_EXPANDED) && m_fExpandUnread && CUnread(&Folder2) > 0)
{
// Expand the Node
ExpandToVisible(m_hwndTree, hitem);
}
}
}
}
// Folder Moved ?
if (Folder1.idParent != Folder2.idParent)
{
// Get Current Selection
hitemSelected = TreeView_GetSelection(m_hwndTree);
// Better not be NULL
Assert(hitemSelected != NULL);
// Get the handle of the old item
hitem = GetItemFromId(Folder1.idFolder);
HTREEITEM htiParent = TreeView_GetParent(m_hwndTree, hitem);
// Move the Node
hitemNew = MoveNode(Folder1.idFolder, Folder2.idParent);
// Reset Selection.
if (hitem == hitemSelected)
{
// If the new parent is the deleted items folder, we should
// select the old node's parent.
FOLDERINFO rInfo;
if (SUCCEEDED(g_pStore->GetFolderInfo(Folder2.idParent, &rInfo)))
{
if (rInfo.tySpecial == FOLDER_DELETED)
{
hitemNew = htiParent;
}
g_pStore->FreeRecord(&rInfo);
}
ITreeView_SelectItem(m_hwndTree, hitemNew);
}
}
// Folder Renamed ?
if (lstrcmp(Folder1.pszName, Folder2.pszName) != 0)
{
// Get current Selection
hitemSelected = TreeView_GetSelection(m_hwndTree);
// Better not be null
Assert(hitemSelected != NULL);
// Get the hitem of the folder
hitem = GetItemFromId(Folder1.idFolder);
if (hitem != NULL)
{
// Reset the Tree view item
tvi.hItem = hitem;
tvi.mask = 0;
// This will reset the folder name
ITreeView_SetItem(&tvi, &Folder2);
// Get the Parent
HTREEITEM hitemParent = TreeView_GetParent(m_hwndTree, hitem);
// Sort the Children
SortChildren(hitemParent);
}
// Current Selection Changed
if (hitemSelected == hitem)
m_pNotify->OnRename(Folder1.idFolder);
}
// synchronize state changed ?
if ((0 == (Folder1.dwFlags & (FOLDER_DOWNLOADHEADERS | FOLDER_DOWNLOADNEW | FOLDER_DOWNLOADALL))) ^
(0 == (Folder2.dwFlags & (FOLDER_DOWNLOADHEADERS | FOLDER_DOWNLOADNEW | FOLDER_DOWNLOADALL))))
{
hitem = GetItemFromId(Folder1.idFolder);
if (hitem != NULL)
{
tvi.hItem = hitem;
tvi.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
tvi.iImage = GetFolderIcon(&Folder2, !!(m_dwFlags & TREEVIEW_DIALOG));
tvi.iSelectedImage = tvi.iImage;
TreeView_SetItem(m_hwndTree, &tvi);
}
}
// Special folder type changed?
if (Folder1.tySpecial != Folder2.tySpecial)
{
hitem = GetItemFromId(Folder1.idFolder);
if (hitem != NULL)
{
tvi.hItem = hitem;
tvi.mask = TVIF_PARAM;
if (TreeView_GetItem(m_hwndTree, &tvi) && tvi.lParam)
((LPFOLDERNODE)tvi.lParam)->Folder.tySpecial = Folder2.tySpecial;
else
tvi.mask = 0; // I guess we won't be able to change special fldr type!
tvi.mask |= TVIF_IMAGE | TVIF_SELECTEDIMAGE;
tvi.iImage = GetFolderIcon(&Folder2, !!(m_dwFlags & TREEVIEW_DIALOG));
tvi.iSelectedImage = tvi.iImage;
TreeView_SetItem(m_hwndTree, &tvi);
hitem = GetItemFromId(Folder2.idParent);
SortChildren(hitem);
}
}
// Get the item
hitem = GetItemFromId(Folder1.idFolder);
// If we found it
if (hitem != NULL)
{
// Initialize the item
tvi.hItem = hitem;
tvi.mask = TVIF_PARAM;
// Get the Items
if (TreeView_GetItem(m_hwndTree, &tvi) && tvi.lParam)
{
// Cast folder node
LPFOLDERNODE pNode = (LPFOLDERNODE)tvi.lParam;
// Validate
Assert(pNode->Folder.idFolder == Folder1.idFolder);
// Free current folder
g_pStore->FreeRecord(&pNode->Folder);
// Copy New Folder
CopyMemory(&pNode->Folder, &Folder2, sizeof(FOLDERINFO));
// Don't free Folder2)
ZeroMemory(&Folder2, sizeof(FOLDERINFO));
}
}
}
}
// Delete
else if (TRANSACTION_DELETE == tyTransaction)
{
// Delete the Node
OnNotifyDeleteNode(Folder1.idFolder);
}
}
// Cleanup
g_pStore->FreeRecord(&Folder1);
g_pStore->FreeRecord(&Folder2);
// Done
return(S_OK);
}
void CTreeView::OnNotifyDeleteNode(FOLDERID idFolder)
{
// Locals
HTREEITEM hitem;
HTREEITEM hitemSelected;
HTREEITEM hitemNew;
// It's OK if this folder is not currently visible (IMAP)
hitemSelected = TreeView_GetSelection(m_hwndTree);
// Better not be NULL
Assert(hitemSelected != NULL);
// Get the item being deleted
hitem = GetItemFromId(idFolder);
// Reset selection if we deleted the currently selected item
if (hitemSelected == hitem)
hitemSelected = TreeView_GetParent(m_hwndTree, hitemSelected);
else
hitemSelected = NULL;
// Delete this node
DeleteNode(idFolder);
// Reset the Selection
if (hitemSelected != NULL)
ITreeView_SelectItem(m_hwndTree, hitemSelected);
}
HRESULT CTreeView::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT *pCmdText)
{
// Collect some information up front so we only have to ask once.
ULONG cServer;
FOLDERID idFolder = GetSelection();
FOLDERINFO rFolder;
HTREEITEM htiDrop;
// Check to see if there's a drop highlight
if (NULL != (htiDrop = TreeView_GetDropHilight(m_hwndTree)))
{
TV_ITEM tvi;
tvi.mask = TVIF_PARAM;
tvi.lParam = 0;
tvi.hItem = htiDrop;
if (TreeView_GetItem(m_hwndTree, &tvi))
{
LPFOLDERNODE pNode = (LPFOLDERNODE)tvi.lParam;
if (pNode)
idFolder = pNode->Folder.idFolder;
}
}
// If nothing is selected, we just disable everything
if (idFolder == FOLDERID_INVALID)
return (E_UNEXPECTED);
// Get the Folder Info
if (FAILED(g_pStore->GetFolderInfo(idFolder, &rFolder)))
return (E_UNEXPECTED);
// Break some of this down for readability
BOOL fSpecial = rFolder.tySpecial != FOLDER_NOTSPECIAL;
BOOL fServer = rFolder.dwFlags & FOLDER_SERVER;
BOOL fRoot = FOLDERID_ROOT == idFolder;
BOOL fNews = rFolder.tyFolder == FOLDER_NEWS;
BOOL fIMAP = rFolder.tyFolder == FOLDER_IMAP;
BOOL fFocus = (m_hwndTree == GetFocus());
BOOL fLocal = rFolder.tyFolder == FOLDER_LOCAL;
BOOL fSubscribed = rFolder.dwFlags & FOLDER_SUBSCRIBED;
BOOL fHotMailDisabled = FALSE;
// Remove Synchronization/subscription from disabled Hotmail
if(rFolder.tyFolder == FOLDER_HTTPMAIL)
{
FOLDERINFO SvrFolderInfo = {0};
IImnAccount *pAccount = NULL;
CHAR szAccountId[CCHMAX_ACCOUNT_NAME];
HRESULT hr = S_OK;
DWORD dwShow = 0;
// Get the server for this folder
IF_FAILEXIT(hr = GetFolderServer(idFolder, &SvrFolderInfo));
// Get the account ID for the server
*szAccountId = 0;
IF_FAILEXIT(hr = GetFolderAccountId(&SvrFolderInfo, szAccountId, ARRAYSIZE(szAccountId)));
// Get the account interface
IF_FAILEXIT(hr = g_pAcctMan->FindAccount(AP_ACCOUNT_ID, szAccountId, &pAccount));
IF_FAILEXIT(hr = pAccount->GetPropDw(AP_HTTPMAIL_DOMAIN_MSN, &dwShow));
if(dwShow)
{
if(HideHotmail())
{
fSubscribed = FALSE;
fHotMailDisabled = TRUE;
}
}
}
exit:
for (ULONG i = 0; i < cCmds; i++)
{
// Only deal with commands that haven't yet been marked as supported
if (prgCmds[i].cmdf == 0)
{
switch (prgCmds[i].cmdID)
{
case ID_OPEN_FOLDER:
{
// Always
prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
break;
}
case ID_NEW_FOLDER:
case ID_NEW_FOLDER2:
{
// Enabled under personal folders and IMAP.
if (!fNews && !fRoot && rFolder.tySpecial != FOLDER_DELETED)
prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
else
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
break;
}
case ID_COMPACT_ALL:
case ID_NEXT_UNREAD_FOLDER:
{
// This is always enabled.
prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
break;
}
case ID_RENAME:
case ID_MOVE:
{
// This is only enabled if we don't have a special folder
// selected and it's not a account or root note.
if (!fSpecial && !fServer && !fRoot && !fNews)
prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
else
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
break;
}
case ID_COMPACT:
{
// This is enabled whenever we have a non-server folder
if (!fServer && !fRoot)
prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
else
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
break;
}
case ID_DELETE_ACCEL:
case ID_DELETE_NO_TRASH_ACCEL:
{
if (fFocus)
{
if (!fServer && !fRoot && !fSpecial)
prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
else
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
}
break;
}
case ID_DELETE_FOLDER:
case ID_DELETE_NO_TRASH:
{
if (!fServer && !fRoot && !fNews && !fSpecial)
prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
else
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
break;
}
case ID_SUBSCRIBE:
case ID_UNSUBSCRIBE:
{
if (!fServer && !fRoot && (fNews || (fIMAP && !fSpecial)))
{
if (fSubscribed ^ (prgCmds[i].cmdID == ID_SUBSCRIBE))
prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
else
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
}
else
{
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
}
break;
}
case ID_PROPERTIES:
{
// we only handle this if we have the focus
if (fFocus)
{
if (!fRoot && !(fLocal && fServer))
prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
else
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
}
break;
}
case ID_NEWSGROUPS:
case ID_IMAP_FOLDERS:
{
if (SUCCEEDED(AcctUtil_GetServerCount(prgCmds[i].cmdID == ID_NEWSGROUPS ? SRV_NNTP : SRV_IMAP, &cServer)) &&
cServer > 0)
prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
else
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
break;
}
case ID_EMPTY_JUNKMAIL:
{
FOLDERINFO rInfo;
// Here's the default value
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
// Get the delete items folder
if (g_pStore)
{
if (SUCCEEDED(g_pStore->GetSpecialFolderInfo(FOLDERID_LOCAL_STORE, prgCmds[i].cmdID == ID_EMPTY_JUNKMAIL ? FOLDER_JUNK : FOLDER_DELETED, &rInfo)))
{
if (rInfo.cMessages > 0 || FHasChildren(&rInfo, SUBSCRIBED))
prgCmds[i].cmdf |= OLECMDF_ENABLED;
g_pStore->FreeRecord(&rInfo);
}
}
break;
}
case ID_EMPTY_WASTEBASKET:
{
// What we want to do here is see if the account for the currently
// selected folder has a Deleted Items folder. If it does, and that
// folder is not empty then this command is enabled.
FOLDERINFO rInfo;
FOLDERID idServer;
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
if (SUCCEEDED(GetFolderServerId(idFolder, &idServer)))
{
if (SUCCEEDED(g_pStore->GetSpecialFolderInfo(idServer, FOLDER_DELETED, &rInfo)))
{
if (rInfo.cMessages > 0 || FHasChildren(&rInfo, SUBSCRIBED))
prgCmds[i].cmdf |= OLECMDF_ENABLED;
g_pStore->FreeRecord(&rInfo);
}
}
break;
}
case ID_SET_DEFAULT_SERVER:
{
if (fServer && !fLocal)
{
// Check to see if _this_ account is the default
if (IsDefaultAccount(&rFolder))
prgCmds[i].cmdf = OLECMDF_LATCHED | OLECMDF_SUPPORTED;
else
prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
}
else
{
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
}
break;
}
case ID_REFRESH:
case ID_RESET_LIST:
case ID_REMOVE_SERVER:
{
if (fServer && !fLocal)
prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
else
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
break;
}
case ID_ADD_SHORTCUT:
{
BOOL fVisible = FALSE;
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
if (fSubscribed && SUCCEEDED(g_pBrowser->GetViewLayout(DISPID_MSGVIEW_OUTLOOK_BAR,
NULL, &fVisible, NULL, NULL))
&& fVisible)
{
prgCmds[i].cmdf |= OLECMDF_ENABLED;
}
break;
}
case ID_POPUP_SYNCHRONIZE:
{
if (!fServer && !fLocal && fSubscribed)
prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
else
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
break;
}
case ID_UNMARK_RETRIEVE_FLD:
{
if (!fServer && !fLocal && fSubscribed)
{
if (0 == (rFolder.dwFlags & (FOLDER_DOWNLOADHEADERS | FOLDER_DOWNLOADNEW | FOLDER_DOWNLOADALL)))
prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED | OLECMDF_NINCHED;
else
prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
}
else
{
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
}
break;
}
case ID_MARK_RETRIEVE_FLD_NEW_HDRS:
{
if (!fServer && !fLocal && fSubscribed)
{
if (!!(rFolder.dwFlags & FOLDER_DOWNLOADHEADERS))
prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED | OLECMDF_NINCHED;
else
prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
}
else
{
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
}
break;
}
case ID_MARK_RETRIEVE_FLD_NEW_MSGS:
{
if (!fServer && !fLocal && fSubscribed)
{
if (!!(rFolder.dwFlags & FOLDER_DOWNLOADNEW))
prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED | OLECMDF_NINCHED;
else
prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
}
else
{
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
}
break;
}
case ID_MARK_RETRIEVE_FLD_ALL_MSGS:
{
if (!fServer && !fLocal && fSubscribed)
{
if (!!(rFolder.dwFlags & FOLDER_DOWNLOADALL))
prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED | OLECMDF_NINCHED;
else
prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
}
else
{
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
}
break;
}
case ID_SYNC_THIS_NOW:
{
if (!fLocal && !fHotMailDisabled)
prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
else
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
break;
}
case ID_CATCH_UP:
{
if (!fServer && fNews)
prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
else
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
break;
break;
}
case ID_FIND_FOLDER:
{
prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
break;
}
}
}
}
g_pStore->FreeRecord(&rFolder);
return (S_OK);
}
HRESULT CTreeView::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG *pvaIn, VARIANTARG *pvaOut)
{
HRESULT hr;
FOLDERINFO info;
BOOL fNews, fUnsubscribed;
FOLDERID id, idFolder = GetSelection();
HTREEITEM htiDrop = TreeView_GetDropHilight(m_hwndTree);
// Note - If you do or call anything in here that display's UI, you must
// parent the UI to m_hwndUIParent. The treeview window might not
// be visible when this is called.
if (htiDrop)
{
// Get the folder id from the drop highlighted folder
TV_ITEM tvi;
tvi.mask = TVIF_PARAM;
tvi.lParam = 0;
tvi.hItem = htiDrop;
if (TreeView_GetItem(m_hwndTree, &tvi))
{
LPFOLDERNODE pNode = (LPFOLDERNODE)tvi.lParam;
if (pNode)
idFolder = pNode->Folder.idFolder;
}
}
switch (nCmdID)
{
case ID_OPEN_FOLDER:
{
// Check to see if there's a drop highlight
if (NULL != htiDrop)
{
// Select the item that we're highlighed on
ITreeView_SelectItem(m_hwndTree, htiDrop);
}
return (S_OK);
}
case ID_NEW_FOLDER:
case ID_NEW_FOLDER2:
{
SelectFolderDialog(m_hwndUIParent, SFD_NEWFOLDER, idFolder, TREEVIEW_NONEWS | TREEVIEW_DIALOG | FD_DISABLEROOT | FD_FORCEINITSELFOLDER,
NULL, NULL, NULL);
return (S_OK);
}
case ID_MOVE:
{
SelectFolderDialog(m_hwndUIParent, SFD_MOVEFOLDER, idFolder, TREEVIEW_NONEWS | TREEVIEW_DIALOG | FD_DISABLEROOT,
MAKEINTRESOURCE(idsMove), MAKEINTRESOURCE(idsMoveCaption), NULL);
return (S_OK);
}
case ID_RENAME:
{
RenameFolderDlg(m_hwndUIParent, idFolder);
return (S_OK);
}
case ID_DELETE_FOLDER:
case ID_DELETE_NO_TRASH:
fUnsubscribed = FALSE;
hr = g_pStore->GetFolderInfo(idFolder, &info);
if (SUCCEEDED(hr))
{
if (info.tyFolder == FOLDER_NEWS &&
0 == (info.dwFlags & FOLDER_SERVER) &&
0 == (info.dwFlags & FOLDER_SUBSCRIBED))
{
fUnsubscribed = TRUE;
}
g_pStore->FreeRecord(&info);
}
if (fUnsubscribed)
{
DeleteNode(idFolder);
return(S_OK);
}
// fall through...
case ID_REMOVE_SERVER:
{
// Delete it
MenuUtil_OnDelete(m_hwndUIParent, idFolder, nCmdID == ID_DELETE_NO_TRASH);
return (S_OK);
}
case ID_SUBSCRIBE:
case ID_UNSUBSCRIBE:
{
MenuUtil_OnSubscribeGroups(m_hwndUIParent, &idFolder, 1, nCmdID == ID_SUBSCRIBE);
return (S_OK);
}
case ID_COMPACT:
{
CompactFolders(m_hwndUIParent, RECURSE_INCLUDECURRENT, idFolder);
return (S_OK);
}
case ID_COMPACT_ALL:
{
CompactFolders(m_hwndUIParent, RECURSE_ONLYSUBSCRIBED | RECURSE_SUBFOLDERS, FOLDERID_ROOT);
return (S_OK);
}
case ID_NEXT_UNREAD_FOLDER:
{
SelectNextUnreadItem();
return (S_OK);
}
case ID_PROPERTIES:
{
if (m_hwndTree == GetFocus())
{
MenuUtil_OnProperties(m_hwndUIParent, idFolder);
return(S_OK);
}
break;
}
case ID_EMPTY_JUNKMAIL:
{
if (AthMessageBoxW(m_hwndUIParent, MAKEINTRESOURCEW(idsAthenaMail), MAKEINTRESOURCEW(idsWarnEmptyJunkMail),
NULL, MB_YESNO | MB_DEFBUTTON2) == IDYES)
{
EmptySpecialFolder(m_hwndUIParent, FOLDER_JUNK);
}
return(S_OK);
}
case ID_EMPTY_WASTEBASKET:
{
if (AthMessageBoxW(m_hwndUIParent, MAKEINTRESOURCEW(idsAthenaMail), MAKEINTRESOURCEW(idsWarnEmptyDeletedItems),
NULL, MB_YESNO | MB_DEFBUTTON2) == IDYES)
{
FOLDERINFO rInfo;
FOLDERID idServer;
if (SUCCEEDED(GetFolderServerId(idFolder, &idServer)))
{
if (SUCCEEDED(g_pStore->GetSpecialFolderInfo(idServer, FOLDER_DELETED, &rInfo)))
{
if (rInfo.cMessages > 0 || FHasChildren(&rInfo, SUBSCRIBED))
EmptyFolder(m_hwndUIParent, rInfo.idFolder);
g_pStore->FreeRecord(&rInfo);
}
}
}
return (S_OK);
}
case ID_SET_DEFAULT_SERVER:
{
MenuUtil_OnSetDefaultServer(idFolder);
return (S_OK);
}
case ID_REFRESH:
case ID_RESET_LIST:
{
hr = g_pStore->GetFolderInfo(idFolder, &info);
if (SUCCEEDED(hr))
{
if (info.tyFolder != FOLDER_LOCAL &&
!!(info.dwFlags & FOLDER_SERVER))
{
DownloadNewsgroupList(m_hwndUIParent, idFolder);
}
g_pStore->FreeRecord(&info);
}
return(S_OK);
}
case ID_NEWSGROUPS:
case ID_IMAP_FOLDERS:
{
fNews = (nCmdID == ID_NEWSGROUPS);
hr = g_pStore->GetFolderInfo(idFolder, &info);
if (SUCCEEDED(hr))
{
if ((fNews && info.tyFolder != FOLDER_NEWS) ||
(!fNews && info.tyFolder != FOLDER_IMAP) ||
FAILED(GetFolderServerId(idFolder, &id)))
{
id = FOLDERID_INVALID;
}
g_pStore->FreeRecord(&info);
DoSubscriptionDialog(m_hwndUIParent, fNews, id);
}
return(S_OK);
}
case ID_ADD_SHORTCUT:
{
OutlookBar_AddShortcut(idFolder);
return (S_OK);
}
case ID_UNMARK_RETRIEVE_FLD:
{
SetSynchronizeFlags(idFolder, 0);
return(S_OK);
}
case ID_MARK_RETRIEVE_FLD_NEW_HDRS:
{
SetSynchronizeFlags(idFolder, FOLDER_DOWNLOADHEADERS);
return(S_OK);
}
case ID_MARK_RETRIEVE_FLD_NEW_MSGS:
{
SetSynchronizeFlags(idFolder, FOLDER_DOWNLOADNEW);
return(S_OK);
}
case ID_MARK_RETRIEVE_FLD_ALL_MSGS:
{
SetSynchronizeFlags(idFolder, FOLDER_DOWNLOADALL);
return(S_OK);
}
case ID_SYNC_THIS_NOW:
{
MenuUtil_SyncThisNow(m_hwndUIParent, idFolder);
return(S_OK);
}
case ID_CATCH_UP:
{
MenuUtil_OnCatchUp(idFolder);
return(S_OK);
}
case ID_FIND_FOLDER:
{
DoFindMsg(idFolder, FOLDER_LOCAL);
return (S_OK);
}
}
return (OLECMDERR_E_NOTSUPPORTED);
}
int CALLBACK TreeViewCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
INT cmp;
LPFOLDERNODE pNode1=(LPFOLDERNODE)lParam1;
LPFOLDERNODE pNode2=(LPFOLDERNODE)lParam2;
LPFOLDERINFO pFolder1=&pNode1->Folder;
LPFOLDERINFO pFolder2=&pNode2->Folder;
Assert(pNode1);
Assert(pNode2);
Assert(pFolder1);
Assert(pFolder2);
if (!!(pFolder1->dwFlags & FOLDER_SERVER))
{
Assert(!!(pFolder2->dwFlags & FOLDER_SERVER));
if (pFolder1->tyFolder == pFolder2->tyFolder)
cmp = lstrcmpi(pFolder1->pszName, pFolder2->pszName);
else
{
cmp = pFolder1->tyFolder - pFolder2->tyFolder;
cmp = (cmp < 0) ? 1 : -1;
}
}
else if (pFolder1->tySpecial != FOLDER_NOTSPECIAL)
{
if (pFolder2->tySpecial != FOLDER_NOTSPECIAL)
cmp = pFolder1->tySpecial - pFolder2->tySpecial;
else
cmp = -1;
}
else
{
if (pFolder2->tySpecial != FOLDER_NOTSPECIAL)
cmp = 1;
else
cmp = lstrcmpi(pFolder1->pszName, pFolder2->pszName);
}
return(cmp);
}
void CTreeView::SortChildren(HTREEITEM hitem)
{
TV_SORTCB sort;
HRESULT hr;
// sort the branch that is expanding
// TODO: find out if it really needs to be sorted
LPFOLDERNODE pNode = GetFolderNode(hitem);
if (NULL == pNode)
return;
sort.hParent = hitem;
sort.lpfnCompare = TreeViewCompare;
sort.lParam = (LPARAM)&pNode->Folder;
TreeView_SortChildrenCB(m_hwndTree, &sort, TRUE);
}
//
// FUNCTION: CTreeView::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 CTreeView::DragEnter(IDataObject* pDataObject,
DWORD grfKeyState,
POINTL pt, DWORD* pdwEffect)
{
Assert(m_pDataObject == NULL);
DOUTL(32, _T("CTreeView::DragEnter() - Starting"));
// Initialize our state
SafeRelease(m_pDTCur);
m_pDataObject = pDataObject;
m_pDataObject->AddRef();
m_grfKeyState = grfKeyState;
m_htiCur = NULL;
Assert(m_pDTCur == NULL);
// Set the default return value to be failure
m_dwEffectCur = *pdwEffect = DROPEFFECT_NONE;
if (m_pFolderBar)
m_pFolderBar->KillScopeCloseTimer();
UpdateDragDropHilite(&pt);
return (S_OK);
}
//
// FUNCTION: CTreeView::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 CTreeView::DragOver(DWORD grfKeyState, POINTL pt,
DWORD* pdwEffect)
{
FORMATETC fe;
ULONG celtFetched;
TV_ITEM tvi;
HTREEITEM hti;
HRESULT hr = E_FAIL;
DWORD dwEffectScroll = 0;
HWND hwndBrowser;
// If we don't have a stored data object from DragEnter()
if (NULL == m_pDataObject)
return (S_OK);
// Get the browser window from the IAthenaBrowser interface. If we don't
// use the browser window to pass to IContextMenu, then when the treeview
// is in autohide mode, the mouse capture goes beserk.
if (FAILED(m_pBrowser->GetWindow(&hwndBrowser)))
return (S_OK);
// Autoscroll if we need to
if (AutoScroll((const LPPOINT) &pt))
dwEffectScroll = DROPEFFECT_SCROLL;
// Find out which item the mouse is currently over
if (NULL == (hti = GetItemFromPoint(pt)))
{
DOUTL(32, _T("CTreeView::DragOver() - GetItemFromPoint() returns NULL."));
}
// If we're over a new tree node, then bind to the folder
if (m_htiCur != hti)
{
// Keep track of this for autoexpand
m_dwExpandTime = GetTickCount();
// Release our previous drop target if any
SafeRelease(m_pDTCur);
// Update the current object
m_htiCur = hti;
// Assume there's no drop target and assume error
Assert(m_pDTCur == NULL);
m_dwEffectCur = DROPEFFECT_NONE;
// Update the treeview UI
UpdateDragDropHilite(&pt);
if (hti)
{
FOLDERINFO Folder={0};
// Get Folder Node
LPFOLDERNODE pNode = GetFolderNode(hti);
// Get information about this folder
if (pNode)
{
m_pDTCur = new CDropTarget();
if (m_pDTCur)
{
hr = ((CDropTarget *) m_pDTCur)->Initialize(m_hwndUIParent, pNode->Folder.idFolder);
}
}
// If we have a drop target now, call DragEnter()
if (SUCCEEDED(hr) && m_pDTCur)
{
hr = m_pDTCur->DragEnter(m_pDataObject, grfKeyState, pt,
pdwEffect);
m_dwEffectCur = *pdwEffect;
}
}
else
{
m_dwEffectCur = 0;
}
}
else
{
// No target change
if (m_htiCur)
{
DWORD dwNow = GetTickCount();
// If the person is hovering, expand the node
if ((dwNow - m_dwExpandTime) >= 1000)
{
m_dwExpandTime = dwNow;
TreeView_Expand(m_hwndTree, m_htiCur, TVE_EXPAND);
}
}
// If the keys changed, we need to re-query the drop target
if ((m_grfKeyState != grfKeyState) && m_pDTCur)
{
m_dwEffectCur = *pdwEffect;
hr = m_pDTCur->DragOver(grfKeyState, pt, &m_dwEffectCur);
}
else
{
hr = S_OK;
}
}
*pdwEffect = m_dwEffectCur | dwEffectScroll;
m_grfKeyState = grfKeyState;
return (hr);
}
//
// FUNCTION: CTreeView::DragLeave()
//
// PURPOSE: Allows us to release any stored data we have from a successful
// DragEnter()
//
// RETURN VALUE:
// S_OK - Everything is groovy
//
HRESULT STDMETHODCALLTYPE CTreeView::DragLeave(void)
{
DOUTL(32, _T("CTreeView::DragLeave()"));
SafeRelease(m_pDTCur);
SafeRelease(m_pDataObject);
UpdateDragDropHilite(NULL);
if (m_pFolderBar)
m_pFolderBar->SetScopeCloseTimer();
return (S_OK);
}
//
// FUNCTION: CTreeView::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 CTreeView::Drop(IDataObject* pDataObject,
DWORD grfKeyState, POINTL pt,
DWORD* pdwEffect)
{
HRESULT hr;
Assert(m_pDataObject == pDataObject);
DOUTL(32, _T("CTreeView::Drop() - Starting"));
if (m_pDTCur)
{
hr = m_pDTCur->Drop(pDataObject, grfKeyState, pt, pdwEffect);
}
else
{
DOUTL(32, "CTreeView::Drop() - no drop target.");
*pdwEffect = 0;
hr = S_OK;
}
UpdateDragDropHilite(NULL);
if (m_pFolderBar)
{
m_pFolderBar->KillScopeDropDown();
}
SafeRelease(m_pDataObject);
SafeRelease(m_pDTCur);
return (hr);
}
//
// FUNCTION: CTreeView::UpdateDragDropHilite()
//
// PURPOSE: Called by the various IDropTarget interfaces to move the drop
// selection to the correct place in our listview.
//
// PARAMETERS:
// <in> *ppt - Contains the point that the mouse is currently at. If this
// is NULL, then the function removes any previous UI.
//
void CTreeView::UpdateDragDropHilite(POINTL *ppt)
{
TV_HITTESTINFO tvhti;
HTREEITEM htiTarget = NULL;
// Unlock the treeview and let it repaint. Then update the selected
// item. If htiTarget is NULL, the the drag highlight goes away.
// ImageList_DragLeave(m_hwndTree);
// If a position was provided
if (ppt)
{
// Figure out which item is selected
tvhti.pt.x = ppt->x;
tvhti.pt.y = ppt->y;
ScreenToClient(m_hwndTree, &tvhti.pt);
htiTarget = TreeView_HitTest(m_hwndTree, &tvhti);
// Only if the cursor is over something do we relock the window.
if (htiTarget)
{
TreeView_SelectDropTarget(m_hwndTree, htiTarget);
}
}
else
TreeView_SelectDropTarget(m_hwndTree, NULL);
}
BOOL CTreeView::AutoScroll(const POINT *ppt)
{
// Find out if the point is above or below the tree
RECT rcTree;
GetWindowRect(m_hwndTree, &rcTree);
// Reduce the rect so we have a scroll margin all the way around
InflateRect(&rcTree, -32, -32);
if (rcTree.top > ppt->y)
{
// Scroll down
FORWARD_WM_VSCROLL(m_hwndTree, NULL, SB_LINEUP, 1, SendMessage);
return (TRUE);
}
else if (rcTree.bottom < ppt->y)
{
// Scroll Up
FORWARD_WM_VSCROLL(m_hwndTree, NULL, SB_LINEDOWN, 1, SendMessage);
return (TRUE);
}
return (FALSE);
}
//
// FUNCTION: CTreeView::GetItemFromPoint()
//
// PURPOSE: Given a point, this function returns the listview item index
// and HFOLDER for the item under that point.
//
// PARAMETERS:
// <in> pt - Position in screen coordinates to check for.
// <in> phFolder - Returns the HFOLDER for the item under pt. If there
// isn't an item under pt, then NULL is returned.
//
// RETURN VALUE:
// Returns the handle of the item under the specified point. If no item
// exists, then NULL is returned.
//
HTREEITEM CTreeView::GetItemFromPoint(POINTL pt)
{
TV_HITTESTINFO tvhti;
TV_ITEM tvi;
HTREEITEM htiTarget;
// Find out from the ListView what item are we over
tvhti.pt.x = pt.x;
tvhti.pt.y = pt.y;
ScreenToClient(m_hwndTree, &(tvhti.pt));
htiTarget = TreeView_HitTest(m_hwndTree, &tvhti);
return (tvhti.hItem);
}
HRESULT STDMETHODCALLTYPE CTreeView::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 CTreeView::GiveFeedback(DWORD dwEffect)
{
return (DRAGDROP_S_USEDEFAULTCURSORS);
}
//
// FUNCTION: CTreeView::OnBeginDrag()
//
// PURPOSE: This function is called when the user begins dragging an item
// in the ListView. If the item selected is a draggable item,
// then we create an IDataObject for the item and then call OLE's
// DoDragDrop().
//
// PARAMETERS:
// <in> pnmlv - Pointer to an NM_LISTIVEW struct which tells us which item
// has been selected to be dragged.
//
// RETURN VALUE:
// Returns zero always.
//
LRESULT CTreeView::OnBeginDrag(NM_TREEVIEW* pnmtv)
{
FOLDERID idFolderSel=FOLDERID_INVALID;
DWORD cSel = 0;
int iSel = -1;
FOLDERID *pidFolder = 0;
PDATAOBJINFO pdoi = 0;
DWORD dwEffect;
IDataObject *pDataObj = 0;
HTREEITEM htiSel;
LPFOLDERNODE pNode;
// Bug #17491 - Check to see if this is the root node. If so, we don't drag.
if (0 == pnmtv->itemNew.lParam)
return (0);
// Get the Node
pNode = (LPFOLDERNODE)pnmtv->itemNew.lParam;
if (NULL == pNode)
return (0);
CFolderDataObject *pDataObject = new CFolderDataObject(pNode->Folder.idFolder);
if (!pDataObject)
return (0);
DoDragDrop(pDataObject, (IDropSource*) this, DROPEFFECT_MOVE | DROPEFFECT_COPY | DROPEFFECT_LINK, &dwEffect);
pDataObject->Release();
return (0);
}
LRESULT CTreeView::OnBeginLabelEdit(TV_DISPINFO* ptvdi)
{
RECT rc, rcT;
// Get Folder Node
LPFOLDERNODE pNode = (LPFOLDERNODE)ptvdi->item.lParam;
if (NULL == pNode)
return (FALSE);
// Can Rename
if (ISFLAGSET(pNode->Folder.dwFlags, FOLDER_CANRENAME))
{
m_fEditLabel = TRUE;
m_hitemEdit = ptvdi->item.hItem;
if (TreeView_GetItemRect(m_hwndTree, ptvdi->item.hItem, &rc, TRUE))
{
GetClientRect(m_hwndTree, &rcT);
rc.left = rc.right;
rc.right = rcT.right;
InvalidateRect(m_hwndTree, &rc, TRUE);
}
return (FALSE);
}
else
{
return (TRUE);
}
}
BOOL CTreeView::OnEndLabelEdit(TV_DISPINFO* ptvdi)
{
HRESULT hr;
LPFOLDERNODE pNode;
IImnAccount *pAcct, *pAcctT;
BOOL fReturn = FALSE;
HWND hwndBrowser;
m_fEditLabel = FALSE;
m_hitemEdit = NULL;
// First check to see if label editing was canceled
if (0 == ptvdi->item.pszText)
return (FALSE);
// Get the browser window from the IAthenaBrowser interface. If we don't
// use the browser window to pass to IContextMenu, then when the treeview
// is in autohide mode, the mouse capture goes beserk.
if (FAILED(m_pBrowser->GetWindow(&hwndBrowser)))
return (FALSE);
// Get the node
pNode = (LPFOLDERNODE)ptvdi->item.lParam;
if (NULL == pNode)
return (FALSE);
// Local Folder
if (FALSE == ISFLAGSET(pNode->Folder.dwFlags, FOLDER_SERVER))
{
if (FAILED(hr = RenameFolderProgress(hwndBrowser, pNode->Folder.idFolder, ptvdi->item.pszText, NOFLAGS)))
{
// Display an error message
AthErrorMessageW(hwndBrowser, MAKEINTRESOURCEW(idsAthenaMail),
MAKEINTRESOURCEW(idsErrRenameFld), hr);
return (FALSE);
}
}
// Servers
else
{
Assert(g_pAcctMan);
Assert(!FIsEmptyA(pNode->Folder.pszAccountId));
if (!FIsEmpty(ptvdi->item.pszText) &&
0 != lstrcmpi(pNode->Folder.pszName, ptvdi->item.pszText) &&
SUCCEEDED(g_pAcctMan->FindAccount(AP_ACCOUNT_ID, pNode->Folder.pszAccountId, &pAcct)))
{
if (SUCCEEDED(g_pAcctMan->FindAccount(AP_ACCOUNT_NAME, ptvdi->item.pszText, &pAcctT)))
{
Assert(!fReturn);
pAcctT->Release();
hr = E_DuplicateAccountName;
}
else
{
fReturn = SUCCEEDED(hr = pAcct->SetPropSz(AP_ACCOUNT_NAME, ptvdi->item.pszText));
if (fReturn)
fReturn = SUCCEEDED(hr = pAcct->SaveChanges());
}
if (hr == E_DuplicateAccountName)
{
TCHAR szRes[CCHMAX_STRINGRES], szBuf[CCHMAX_STRINGRES];
AthLoadString(idsErrDuplicateAccount, szRes, ARRAYSIZE(szRes));
wnsprintf(szBuf, ARRAYSIZE(szBuf), szRes, ptvdi->item.pszText);
AthMessageBox(hwndBrowser, MAKEINTRESOURCE(idsAthena), szBuf, 0, MB_ICONSTOP | MB_OK);
}
else if (FAILED(hr))
{
AthMessageBoxW(hwndBrowser, MAKEINTRESOURCEW(idsAthena), MAKEINTRESOURCEW(idsErrRenameAccountFailed),
0, MB_ICONSTOP | MB_OK);
}
pAcct->Release();
return (fReturn);
}
else
{
return (FALSE);
}
}
return (TRUE);
}
HRESULT CTreeView::RegisterFlyOut(CFolderBar *pFolderBar)
{
Assert(m_pFolderBar == NULL);
m_pFolderBar = pFolderBar;
m_pFolderBar->AddRef();
RegisterGlobalDropDown(m_hwnd);
return S_OK;
}
HRESULT CTreeView::RevokeFlyOut(void)
{
if (m_pFolderBar)
{
m_pFolderBar->Release();
m_pFolderBar = NULL;
}
UnregisterGlobalDropDown(m_hwnd);
return S_OK;
}
HRESULT CTreeView::OnConnectionNotify(CONNNOTIFY nCode, LPVOID pvData, CConnectionManager *pConMan)
{
UpdateLabelColors();
return (S_OK);
}
void CTreeView::UpdateLabelColors()
{
BOOL fConn;
HTREEITEM treeitem;
LPFOLDERNODE pNode;
TVITEM item;
if (m_hwndTree != NULL)
{
treeitem = TreeView_GetRoot(m_hwndTree);
if (treeitem != NULL)
{
treeitem = TreeView_GetChild(m_hwndTree, treeitem);
while (treeitem != NULL)
{
item.hItem = treeitem;
item.mask = TVIF_PARAM;
if (TreeView_GetItem (m_hwndTree, &item) && (pNode = (LPFOLDERNODE)item.lParam) != NULL)
{
Assert(!!(pNode->Folder.dwFlags & FOLDER_SERVER));
if (pNode->Folder.tyFolder != FOLDER_LOCAL)
{
Assert(!FIsEmptyA(pNode->Folder.pszAccountId));
//fConn = (g_pConMan->CanConnect(Folder.pszAccountId) == S_OK);
fConn = g_pConMan->IsGlobalOffline();
//if (fConn ^ (0 == (pNode->dwFlags & FIDF_DISCONNECTED)))
if (fConn ^ (!!(pNode->dwFlags & FIDF_DISCONNECTED)))
{
// if the connect state has changed, then let's update
UpdateChildren(treeitem, !fConn, FALSE);
}
}
treeitem = TreeView_GetNextSibling(m_hwndTree, treeitem);
}
}
}
}
}
void CTreeView::UpdateChildren(HTREEITEM treeitem, BOOL canconn, BOOL fSiblings)
{
HTREEITEM hitem;
RECT rect;
LPFOLDERNODE pNode;
Assert(treeitem != NULL);
while (treeitem != NULL)
{
pNode = GetFolderNode(treeitem);
Assert(pNode != NULL);
if (canconn)
pNode->dwFlags &= ~FIDF_DISCONNECTED;
else
pNode->dwFlags |= FIDF_DISCONNECTED;
TreeView_GetItemRect(m_hwndTree, treeitem, &rect, TRUE);
InvalidateRect(m_hwndTree, &rect, FALSE);
hitem = TreeView_GetChild(m_hwndTree, treeitem);
if (hitem != NULL)
UpdateChildren(hitem, canconn, TRUE);
if (!fSiblings)
break;
treeitem = TreeView_GetNextSibling(m_hwndTree, treeitem);
}
}
LPFOLDERNODE CTreeView::GetFolderNode(HTREEITEM hItem)
{
TVITEM item;
BOOL retval;
if (!hItem)
return NULL;
item.hItem = hItem;
item.mask = TVIF_PARAM;
retval = TreeView_GetItem (m_hwndTree, &item);
return (retval ? (LPFOLDERNODE)item.lParam : NULL);
}
HRESULT CTreeView::AdviseAccount(DWORD dwAdviseType, ACTX *pactx)
{
//We are only interested in this type
if (dwAdviseType == AN_ACCOUNT_CHANGED)
{
UpdateLabelColors();
}
return S_OK;
}
//
//
// CTreeViewFrame
//
//
CTreeViewFrame::CTreeViewFrame()
{
m_cRef = 1;
m_hwnd = NULL;
m_ptv = NULL;
}
CTreeViewFrame::~CTreeViewFrame()
{
if (m_ptv != NULL)
m_ptv->Release();
}
HRESULT CTreeViewFrame::Initialize(HWND hwnd, RECT *prc, DWORD dwFlags)
{
HRESULT hr;
Assert(hwnd != 0);
Assert(prc != NULL);
m_hwnd = hwnd;
m_rect = *prc;
m_ptv = new CTreeView(this);
if (m_ptv == NULL)
return(hrMemory);
hr = m_ptv->HrInit(dwFlags, NULL);
if (!FAILED(hr))
{
// m_ptv->SetSite((IInputObjectSite*)this);
// m_ptv->UIActivateIO(TRUE, NULL);
//We also set the tree view window position here since the actual treeview is re-sized by the rebar
HWND hChild;
TCHAR Classname[30];
int Count;
CTreeView *ptv;
hChild = m_ptv->Create(m_hwnd, NULL, FALSE);
/*
while (hChild)
{
if ((ptv = (CTreeView*)GetWindowLong(hChild, GWL_USERDATA)) == m_ptv)
{
*/
SetWindowPos(hChild, NULL, m_rect.left, m_rect.top,
m_rect.right - m_rect.left,
m_rect.bottom - m_rect.top,
SWP_NOZORDER | SWP_SHOWWINDOW);
return (hr);
/*
}
hChild = ::GetNextWindow(hChild, GW_HWNDNEXT);
} //while(hChild)
hr = E_FAIL;
*/
} //if (!FAILED(hr))
return(hr);
}
void CTreeViewFrame::CloseTreeView()
{
if (m_ptv != NULL)
{
m_ptv->UIActivateIO(FALSE, NULL);
m_ptv->SetSite(NULL);
}
}
HRESULT STDMETHODCALLTYPE CTreeViewFrame::QueryInterface(REFIID riid, void **ppvObj)
{
if ((IsEqualIID(riid, IID_IInputObjectSite)) || (IsEqualIID(riid, IID_IUnknown)))
{
*ppvObj = (void*) (IInputObjectSite *) this;
}
else if (IsEqualIID(riid, IID_IOleWindow))
{
*ppvObj = (void*) (IOleWindow *)this;
}
else
{
*ppvObj = NULL;
return E_NOINTERFACE;
}
AddRef();
return NOERROR;
}
ULONG STDMETHODCALLTYPE CTreeViewFrame::AddRef()
{
return ++m_cRef;
}
ULONG STDMETHODCALLTYPE CTreeViewFrame::Release()
{
if (--m_cRef == 0)
{
delete this;
return 0;
}
return m_cRef;
}
HRESULT STDMETHODCALLTYPE CTreeViewFrame::GetWindow(HWND * lphwnd)
{
*lphwnd = m_hwnd;
return (m_hwnd ? S_OK : E_FAIL);
}
HRESULT STDMETHODCALLTYPE CTreeViewFrame::ContextSensitiveHelp(BOOL fEnterMode)
{
return E_NOTIMPL;
}
HRESULT CTreeViewFrame::OnFocusChangeIS(IUnknown *punk, BOOL fSetFocus)
{
return S_OK;
}
void CTreeViewFrame::OnSelChange(FOLDERID idFolder)
{
SendMessage(m_hwnd, TVM_SELCHANGED, 0, (LPARAM)idFolder);
return;
}
void CTreeViewFrame::OnRename(FOLDERID idFolder)
{
return;
}
void CTreeViewFrame::OnDoubleClick(FOLDERID idFolder)
{
SendMessage(m_hwnd, TVM_DBLCLICK, 0, (LPARAM)idFolder);
return;
}
HRESULT CTreeView::SaveExpandState(HTREEITEM hitem)
{
HRESULT hr;
TV_ITEM item;
HTREEITEM hitemT;
LPFOLDERNODE pNode;
while (hitem != NULL)
{
hitemT = TreeView_GetChild(m_hwndTree, hitem);
if (hitemT != NULL)
{
item.hItem = hitem;
item.mask = TVIF_STATE;
item.stateMask = TVIS_EXPANDED;
if (TreeView_GetItem(m_hwndTree, &item))
{
pNode = GetFolderNode(hitem);
if (pNode)
{
if (!!(pNode->Folder.dwFlags & FOLDER_EXPANDTREE) ^ !!(item.state & TVIS_EXPANDED))
{
pNode->Folder.dwFlags ^= FOLDER_EXPANDTREE;
g_pStore->UpdateRecord(&pNode->Folder);
}
}
if (!!(item.state & TVIS_EXPANDED))
{
// we only care about saving the expanded state for children
// of expanded nodes
hr = SaveExpandState(hitemT);
if (FAILED(hr))
return(hr);
}
}
}
hitem = TreeView_GetNextSibling(m_hwndTree, hitem);
}
return(S_OK);
}
void CTreeView::ExpandToVisible(HWND hwnd, HTREEITEM hti)
{
HTREEITEM htiParent;
htiParent = TreeView_GetParent(hwnd, hti);
TV_ITEM tvi;
tvi.mask = TVIF_STATE;
tvi.hItem = htiParent;
TreeView_GetItem(hwnd, &tvi);
if (0 == (tvi.state & TVIS_EXPANDED))
{
TreeView_EnsureVisible(hwnd, hti);
}
}
BOOL CTreeView::IsDefaultAccount(FOLDERINFO *pInfo)
{
IImnAccount *pAccount = NULL;
ACCTTYPE type = ACCT_MAIL;
TCHAR szDefault[CCHMAX_ACCOUNT_NAME];
BOOL fReturn = FALSE;
// Figure out what account type to ask for
if (pInfo->tyFolder == FOLDER_NEWS)
type = ACCT_NEWS;
// Ask the account manager to give us the account
if (SUCCEEDED(g_pAcctMan->GetDefaultAccountName(type, szDefault, ARRAYSIZE(szDefault))))
{
if (0 == lstrcmpi(szDefault, pInfo->pszName))
{
fReturn = TRUE;
}
}
return (fReturn);
}