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

  1. #include "pch.hxx"
  2. #include "treeview.h"
  3. #include "resource.h"
  4. #include "error.h"
  5. #include "fonts.h"
  6. #include "thormsgs.h"
  7. #include "strconst.h"
  8. #include "imagelst.h"
  9. #include "goptions.h"
  10. #include <notify.h>
  11. #include "imnact.h"
  12. #include "menuutil.h"
  13. #include <imnxport.h>
  14. #include <inpobj.h>
  15. #include "fldbar.h"
  16. #include "instance.h"
  17. #include "imnglobl.h"
  18. #include "ddfldbar.h"
  19. #include "ourguid.h"
  20. #include "storutil.h"
  21. #include "shlwapip.h"
  22. #include "demand.h"
  23. #include "newfldr.h"
  24. #include <store.h>
  25. #include "subscr.h"
  26. #include "acctutil.h"
  27. #include "menures.h"
  28. #include "mailutil.h"
  29. #include "dragdrop.h"
  30. #include <storecb.h>
  31. #include "outbar.h"
  32. #include "navpane.h"
  33. #include "finder.h"
  34. #include "goptions.h"
  35. ASSERTDATA
  36. #define idtSelChangeTimer 5
  37. #define C_RGBCOLORS 16
  38. extern const DWORD rgrgbColors16[C_RGBCOLORS];
  39. int CALLBACK TreeViewCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);
  40. ////////////////////////////////////////////////////////////////////////
  41. //
  42. // Module Data
  43. //
  44. ////////////////////////////////////////////////////////////////////////
  45. static const TCHAR s_szTreeViewWndClass[] = TEXT("ThorTreeViewWndClass");
  46. DWORD CUnread(FOLDERINFO *pfi)
  47. {
  48. DWORD dwUnread = pfi->cUnread;
  49. if (pfi->tyFolder == FOLDER_NEWS)
  50. dwUnread += pfi->dwNotDownloaded;
  51. return(dwUnread);
  52. }
  53. inline BOOL ITreeView_SelectItem(HWND hwnd, HTREEITEM hitem)
  54. {
  55. TreeView_EnsureVisible(hwnd, hitem);
  56. return((BOOL)TreeView_SelectItem(hwnd, hitem));
  57. }
  58. ////////////////////////////////////////////////////////////////////////
  59. //
  60. // Constructors, Destructors, and other initialization stuff
  61. //
  62. ////////////////////////////////////////////////////////////////////////
  63. CTreeView::CTreeView(ITreeViewNotify *pNotify)
  64. {
  65. m_cRef = 1;
  66. m_hwndParent = NULL;
  67. m_hwnd = NULL;
  68. m_hwndTree = NULL;
  69. m_pBrowser = NULL;
  70. m_hwndUIParent = NULL;
  71. Assert(pNotify);
  72. m_pNotify = pNotify;
  73. m_pObjSite = NULL;
  74. m_xWidth = DwGetOption(OPT_TREEWIDTH);
  75. m_fExpandUnread = DwGetOption(OPT_EXPAND_UNREAD);
  76. m_fShow = FALSE;
  77. m_idSelTimer = 0;
  78. m_htiMenu = NULL;
  79. m_fEditLabel = 0;
  80. m_hitemEdit = NULL;
  81. m_fIgnoreNotify = FALSE;
  82. m_pDataObject = NULL;
  83. m_pFolderBar = NULL;
  84. m_pDTCur = NULL;
  85. m_dwAcctConnIndex = 0;
  86. m_pPaneFrame = NULL;
  87. m_hwndPaneFrame = 0;
  88. m_clrWatched = 0;
  89. }
  90. CTreeView::~CTreeView()
  91. {
  92. if (m_hwnd)
  93. DestroyWindow(m_hwnd);
  94. Assert(g_pStore);
  95. g_pStore->UnregisterNotify((IDatabaseNotify *)this);
  96. if (m_dwAcctConnIndex != 0 && g_pAcctMan != NULL)
  97. g_pAcctMan->Unadvise(m_dwAcctConnIndex);
  98. SafeRelease (m_pObjSite);
  99. SafeRelease(m_pPaneFrame);
  100. }
  101. ////////////////////////////////////////////////////////////////////////
  102. //
  103. // IUnknown
  104. //
  105. ////////////////////////////////////////////////////////////////////////
  106. HRESULT CTreeView::QueryInterface(REFIID riid, LPVOID * ppvObj)
  107. {
  108. if (IsEqualIID(riid, IID_IUnknown) ||
  109. IsEqualIID(riid, IID_IInputObject))
  110. {
  111. *ppvObj = (void*)(IInputObject*)this;
  112. }
  113. else if (IsEqualIID(riid, IID_IDockingWindow))
  114. {
  115. *ppvObj = (void*)(IDockingWindow *) this;
  116. }
  117. else if (IsEqualIID(riid, IID_IOleWindow))
  118. {
  119. *ppvObj = (void*)(IOleWindow*)this;
  120. }
  121. else if (IsEqualIID(riid, IID_IObjectWithSite))
  122. {
  123. *ppvObj = (void*)(IObjectWithSite*)this;
  124. }
  125. else if (IsEqualIID(riid, IID_IDropDownFldrBar))
  126. {
  127. *ppvObj = (void*)(IDropDownFldrBar*)this;
  128. }
  129. else if (IsEqualIID(riid, IID_IDropTarget))
  130. {
  131. *ppvObj = (LPVOID) (IDropTarget*) this;
  132. }
  133. else if (IsEqualIID(riid, IID_IOleCommandTarget))
  134. {
  135. *ppvObj = (LPVOID) (IOleCommandTarget *) this;
  136. }
  137. else
  138. {
  139. *ppvObj = NULL;
  140. return E_NOINTERFACE;
  141. }
  142. AddRef();
  143. return S_OK;
  144. }
  145. ULONG CTreeView::AddRef()
  146. {
  147. DOUTL(4, TEXT("CTreeView::AddRef() - m_cRef = %d"), m_cRef + 1);
  148. return ++m_cRef;
  149. }
  150. ULONG CTreeView::Release()
  151. {
  152. DOUTL(4, TEXT("CTreeView::Release() - m_cRef = %d"), m_cRef - 1);
  153. if (--m_cRef==0)
  154. {
  155. delete this;
  156. return 0;
  157. }
  158. return m_cRef;
  159. }
  160. ////////////////////////////////////////////////////////////////////////
  161. //
  162. // IOleWindow
  163. //
  164. ////////////////////////////////////////////////////////////////////////
  165. HRESULT CTreeView::GetWindow(HWND * lphwnd)
  166. {
  167. *lphwnd = (m_hwndPaneFrame ? m_hwndPaneFrame : m_hwnd);
  168. return (*lphwnd ? S_OK : E_FAIL);
  169. }
  170. HRESULT CTreeView::ContextSensitiveHelp(BOOL fEnterMode)
  171. {
  172. return E_NOTIMPL;
  173. }
  174. ////////////////////////////////////////////////////////////////////////
  175. //
  176. // IDockingWindow
  177. //
  178. ////////////////////////////////////////////////////////////////////////
  179. HWND CTreeView::Create(HWND hwndParent, IInputObjectSite *pSiteFrame, BOOL fFrame)
  180. {
  181. m_hwndParent = hwndParent;
  182. if (m_pBrowser)
  183. m_pBrowser->GetWindow(&m_hwndUIParent);
  184. else
  185. m_hwndUIParent = m_hwndParent;
  186. // Decide if we need to create a new window or show a currently existing
  187. // window
  188. if (!m_hwnd)
  189. {
  190. WNDCLASSEX wc;
  191. wc.cbSize = sizeof(WNDCLASSEX);
  192. if (!GetClassInfoEx(g_hInst, s_szTreeViewWndClass, &wc))
  193. {
  194. // We need to register the class
  195. wc.style = 0;
  196. wc.lpfnWndProc = CTreeView::TreeViewWndProc;
  197. wc.cbClsExtra = 0;
  198. wc.cbWndExtra = 0;
  199. wc.hInstance = g_hInst;
  200. wc.hCursor = LoadCursor(NULL, IDC_SIZEWE);
  201. wc.hbrBackground = (HBRUSH)(COLOR_3DFACE+1);
  202. wc.lpszMenuName = NULL;
  203. wc.lpszClassName = s_szTreeViewWndClass;
  204. wc.hIcon = NULL;
  205. wc.hIconSm = NULL;
  206. if (RegisterClassEx(&wc) == 0 && GetLastError() != ERROR_CLASS_ALREADY_EXISTS)
  207. return 0;
  208. }
  209. // Get the handle of the parent window
  210. if (!m_hwndParent)
  211. return 0;
  212. if (fFrame)
  213. {
  214. m_pPaneFrame = new CPaneFrame();
  215. if (!m_pPaneFrame)
  216. return (0);
  217. m_hwndPaneFrame = m_pPaneFrame->Initialize(m_hwndParent, pSiteFrame, idsMNBandTitle);
  218. hwndParent = m_hwndPaneFrame;
  219. }
  220. m_hwnd = CreateWindowEx(WS_EX_CONTROLPARENT, s_szTreeViewWndClass, NULL,
  221. WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CHILD,
  222. 0, 0, 0, 0, hwndParent, NULL, g_hInst, (LPVOID) this);
  223. if (!m_hwnd)
  224. {
  225. AssertSz(0, _T("CTreeView::Create() - Failed to create window."));
  226. return 0;
  227. }
  228. if (fFrame)
  229. {
  230. m_pPaneFrame->SetChild(m_hwnd, DISPID_MSGVIEW_FOLDERLIST, m_pBrowser, this);
  231. ShowWindow(m_hwndPaneFrame, SW_SHOW);
  232. }
  233. ShowWindow(m_hwnd, SW_SHOW);
  234. }
  235. return (fFrame ? m_hwndPaneFrame : m_hwnd);
  236. }
  237. ////////////////////////////////////////////////////////////////////////
  238. //
  239. // IInputObject
  240. //
  241. ////////////////////////////////////////////////////////////////////////
  242. HRESULT CTreeView::UIActivateIO(BOOL fActivate, LPMSG lpMsg)
  243. {
  244. if (fActivate)
  245. {
  246. UnkOnFocusChangeIS(m_pObjSite, (IInputObject*)this, TRUE);
  247. SetFocus(m_hwndTree);
  248. }
  249. return S_OK;
  250. }
  251. HRESULT CTreeView::HasFocusIO(void)
  252. {
  253. HWND hwndFocus = GetFocus();
  254. return (m_fEditLabel || (hwndFocus && (hwndFocus == m_hwndTree || IsChild(m_hwndTree, hwndFocus)))) ? S_OK : S_FALSE;
  255. }
  256. HRESULT CTreeView::TranslateAcceleratorIO(LPMSG pMsg)
  257. {
  258. if (m_fEditLabel)
  259. {
  260. TranslateMessage(pMsg);
  261. DispatchMessage(pMsg);
  262. return (S_OK);
  263. }
  264. return (S_FALSE);
  265. }
  266. ////////////////////////////////////////////////////////////////////////
  267. //
  268. // IObjectWithSite
  269. //
  270. ////////////////////////////////////////////////////////////////////////
  271. HRESULT CTreeView::SetSite(IUnknown* punkSite)
  272. {
  273. // If we already have a site pointer, release it now
  274. if (m_pObjSite)
  275. {
  276. m_pObjSite->Release();
  277. m_pObjSite = NULL;
  278. }
  279. // If the caller provided a new site interface, get the IDockingWindowSite
  280. // and keep a pointer to it.
  281. if (punkSite)
  282. {
  283. if (FAILED(punkSite->QueryInterface(IID_IInputObjectSite, (void **)&m_pObjSite)))
  284. return E_FAIL;
  285. }
  286. return S_OK;
  287. }
  288. HRESULT CTreeView::GetSite(REFIID riid, LPVOID *ppvSite)
  289. {
  290. return E_NOTIMPL;
  291. }
  292. ////////////////////////////////////////////////////////////////////////
  293. //
  294. // Public Methods
  295. //
  296. ////////////////////////////////////////////////////////////////////////
  297. HRESULT CTreeView::HrInit(DWORD dwFlags, IAthenaBrowser *pBrowser)
  298. {
  299. DWORD dwConnection = 0;
  300. // Locals
  301. HRESULT hr=S_OK;
  302. // Validate
  303. Assert(0 == (~TREEVIEW_FLAGS & dwFlags));
  304. // Save Flags
  305. m_dwFlags = dwFlags;
  306. #ifdef DEBUG
  307. // we have to have a browser if we have context menus
  308. if (0 == (TREEVIEW_DIALOG & dwFlags))
  309. Assert(pBrowser != NULL);
  310. #endif // DEBUG
  311. // Save the Browser, but don't addref (must be a circular refcount problem)
  312. m_pBrowser = pBrowser;
  313. // Register for notifications on the global folder manager
  314. Assert(g_pStore);
  315. hr = g_pStore->RegisterNotify(IINDEX_SUBSCRIBED, REGISTER_NOTIFY_NOADDREF, 0, (IDatabaseNotify *)this);
  316. if (FAILED(hr))
  317. return hr;
  318. if (0 == (TREEVIEW_DIALOG & dwFlags))
  319. {
  320. //Register for notifications from account manager
  321. hr = g_pAcctMan->Advise((IImnAdviseAccount*)this, &m_dwAcctConnIndex);
  322. }
  323. // Done
  324. return S_OK;
  325. }
  326. HRESULT CTreeView::DeInit()
  327. {
  328. HTREEITEM hitem;
  329. Assert(0 == (m_dwFlags & TREEVIEW_DIALOG));
  330. hitem = TreeView_GetRoot(m_hwndTree);
  331. if (hitem != NULL)
  332. SaveExpandState(hitem);
  333. return(S_OK);
  334. }
  335. void CTreeView::HandleMsg(UINT msg, WPARAM wParam, LPARAM lParam)
  336. {
  337. switch (msg)
  338. {
  339. case WM_SYSCOLORCHANGE:
  340. case WM_WININICHANGE:
  341. SendMessage(m_hwndTree, msg, wParam, lParam);
  342. break;
  343. }
  344. }
  345. /////////////////////////////////////////////////////////////////////////////
  346. //
  347. // Window procedure and Message handling routines
  348. //
  349. /////////////////////////////////////////////////////////////////////////////
  350. LRESULT CALLBACK EXPORT_16 CTreeView::TreeViewWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
  351. {
  352. CTreeView *pmv;
  353. if (msg == WM_NCCREATE)
  354. {
  355. pmv = (CTreeView *)LPCREATESTRUCT(lp)->lpCreateParams;
  356. SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM)pmv);
  357. }
  358. else
  359. {
  360. pmv = (CTreeView *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
  361. }
  362. Assert(pmv);
  363. return pmv->WndProc(hwnd, msg, wp, lp);
  364. }
  365. ////////////////////////////////////////////////////////////////////////
  366. //
  367. // Private Methods
  368. //
  369. ////////////////////////////////////////////////////////////////////////
  370. LRESULT CTreeView::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  371. {
  372. MSG xmsg;
  373. switch (msg)
  374. {
  375. HANDLE_MSG(hwnd, WM_CREATE, OnCreate);
  376. HANDLE_MSG(hwnd, WM_CONTEXTMENU, OnContextMenu);
  377. HANDLE_MSG(hwnd, WM_NOTIFY, OnNotify);
  378. HANDLE_MSG(hwnd, WM_SIZE, OnSize);
  379. case WM_DESTROY:
  380. //Before the image list is destroyed we should unresgister with the connection manager to avoid
  381. //ending up with a null image list when we get a disconnected notification
  382. if (g_pConMan)
  383. {
  384. g_pConMan->Unadvise(this);
  385. }
  386. if (m_dwAcctConnIndex != 0 && g_pAcctMan != NULL)
  387. {
  388. g_pAcctMan->Unadvise(m_dwAcctConnIndex);
  389. m_dwAcctConnIndex = 0;
  390. }
  391. OptionUnadvise(hwnd);
  392. ImageList_Destroy( TreeView_GetImageList( m_hwndTree, TVSIL_NORMAL ) );
  393. break;
  394. case CM_OPTIONADVISE:
  395. m_fExpandUnread = DwGetOption(OPT_EXPAND_UNREAD);
  396. m_clrWatched = DwGetOption(OPT_WATCHED_COLOR);
  397. break;
  398. case WM_NCDESTROY:
  399. RevokeDragDrop(hwnd);
  400. SetWindowLongPtr(hwnd, GWLP_USERDATA, NULL);
  401. m_hwnd = m_hwndTree = NULL;
  402. break;
  403. case WM_SYSCOLORCHANGE:
  404. case WM_WININICHANGE:
  405. case WM_FONTCHANGE:
  406. SendMessage(m_hwndTree, msg, wParam, lParam);
  407. AdjustItemHeight();
  408. return (0);
  409. case WM_SETFOCUS:
  410. if (m_hwndTree && ((HWND)wParam) != m_hwndTree)
  411. SetFocus(m_hwndTree);
  412. return 0;
  413. case WM_TIMER:
  414. Assert(wParam == idtSelChangeTimer);
  415. KillTimer(hwnd, idtSelChangeTimer);
  416. m_idSelTimer = 0;
  417. if (m_pNotify)
  418. {
  419. FOLDERID idFolder = GetSelection();
  420. m_pNotify->OnSelChange(idFolder);
  421. }
  422. break;
  423. case WMR_CLICKOUTSIDE:
  424. {
  425. BOOL fHide = FALSE;
  426. if (wParam == CLK_OUT_KEYBD || wParam == CLK_OUT_DEACTIVATE)
  427. fHide = TRUE;
  428. else if (wParam == CLK_OUT_MOUSE)
  429. {
  430. HWND hwndParent = GetParent(m_hwnd);
  431. fHide = ((HWND) lParam != hwndParent) && !IsChild(hwndParent, (HWND) lParam);
  432. }
  433. if (fHide)
  434. m_pFolderBar->KillScopeDropDown();
  435. return (fHide);
  436. }
  437. }
  438. return DefWindowProc(hwnd, msg, wParam, lParam);
  439. }
  440. HRESULT CTreeView::ForceSelectionChange()
  441. {
  442. if (m_idSelTimer != 0)
  443. {
  444. KillTimer(m_hwnd, idtSelChangeTimer);
  445. m_idSelTimer = 0;
  446. if (m_pNotify)
  447. {
  448. FOLDERID idFolder = GetSelection();
  449. m_pNotify->OnSelChange(idFolder);
  450. }
  451. return(S_OK);
  452. }
  453. return(S_FALSE);
  454. }
  455. void CTreeView::AdjustItemHeight()
  456. {
  457. int cyItem, cyText, cyBorder;
  458. HDC hdc;
  459. TCHAR c = TEXT('J');
  460. SIZE size;
  461. if (0 == (m_dwFlags & TREEVIEW_DIALOG))
  462. {
  463. hdc = GetDC(m_hwndTree);
  464. cyBorder = GetSystemMetrics(SM_CYBORDER);
  465. GetTextExtentPoint(hdc, &c, 1, &size);
  466. cyText = size.cy;
  467. if (cyText < 16)
  468. cyText = 16; // icon size
  469. cyText = cyText + (cyBorder * 2);
  470. ReleaseDC(m_hwndTree, hdc);
  471. cyItem = TreeView_GetItemHeight(m_hwndTree);
  472. if (cyText > cyItem)
  473. TreeView_SetItemHeight(m_hwndTree, cyText);
  474. }
  475. }
  476. BOOL CTreeView::OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
  477. {
  478. HIMAGELIST himl;
  479. TCHAR szName[CCHMAX_STRINGRES];
  480. ZeroMemory(szName, sizeof(szName));
  481. LoadString(g_hLocRes, idsFolderListTT, szName, ARRAYSIZE(szName));
  482. m_hwndTree = CreateWindowEx((0 == (m_dwFlags & TREEVIEW_DIALOG)) ? 0 : WS_EX_CLIENTEDGE,
  483. WC_TREEVIEW, szName,
  484. WS_CHILD | WS_VISIBLE | WS_TABSTOP | TVS_SHOWSELALWAYS |
  485. TVS_HASBUTTONS | TVS_HASLINES |
  486. (0 == (m_dwFlags & TREEVIEW_DIALOG) ? TVS_EDITLABELS : TVS_DISABLEDRAGDROP),
  487. 0, 0, 0, 0, hwnd, NULL, g_hInst, NULL);
  488. if (!m_hwndTree)
  489. return FALSE;
  490. himl = ImageList_LoadBitmap(g_hLocRes, MAKEINTRESOURCE(idbFolders), 16, 0, RGB(255, 0, 255)); // small icons
  491. TreeView_SetImageList(m_hwndTree, himl, TVSIL_NORMAL);
  492. AdjustItemHeight();
  493. m_clrWatched = DwGetOption(OPT_WATCHED_COLOR);
  494. // Register ourselves as a drop target
  495. if (0 == (m_dwFlags & TREEVIEW_DIALOG))
  496. {
  497. RegisterDragDrop(hwnd, this);
  498. // Register ourselves with th connection manager so we can overlay
  499. // the disconnected image when it notifies us of a disconnection
  500. if (g_pConMan)
  501. g_pConMan->Advise((IConnectionNotify *) this);
  502. OptionAdvise(hwnd);
  503. }
  504. return TRUE;
  505. }
  506. //
  507. // FUNCTION: CTreeView::OnNotify
  508. //
  509. // PURPOSE: Processes the various notifications we receive from our child
  510. // controls.
  511. //
  512. // PARAMETERS:
  513. // hwnd - Handle of the msgview window.
  514. // idCtl - identifies the control sending the notification
  515. // pnmh - points to a NMHDR struct with more information regarding the
  516. // notification
  517. //
  518. // RETURN VALUE:
  519. // Dependant on the specific notification.
  520. //
  521. LRESULT CTreeView::OnNotify(HWND hwnd, int idFrom, LPNMHDR pnmhdr)
  522. {
  523. LPFOLDERNODE pNode;
  524. NM_TREEVIEW *pnmtv;
  525. // This is necessary to prevent handling of notification after the
  526. // listview window has been destroyed.
  527. if (!m_hwndTree)
  528. return 0;
  529. switch (pnmhdr->code)
  530. {
  531. case TVN_BEGINLABELEDIT:
  532. return OnBeginLabelEdit((TV_DISPINFO *) pnmhdr);
  533. case TVN_ENDLABELEDIT:
  534. return (OnEndLabelEdit((TV_DISPINFO *) pnmhdr));
  535. case TVN_BEGINDRAG:
  536. Assert(0 == (m_dwFlags & TREEVIEW_DIALOG));
  537. return OnBeginDrag((NM_TREEVIEW*) pnmhdr);
  538. case TVN_DELETEITEM:
  539. pnmtv = (NM_TREEVIEW *)pnmhdr;
  540. pNode = (LPFOLDERNODE)pnmtv->itemOld.lParam;
  541. if (pNode)
  542. {
  543. g_pStore->FreeRecord(&pNode->Folder);
  544. g_pMalloc->Free(pNode);
  545. }
  546. break;
  547. case TVN_SELCHANGED:
  548. pnmtv = (NM_TREEVIEW *)pnmhdr;
  549. if (0 == (m_dwFlags & TREEVIEW_DIALOG))
  550. {
  551. if (m_idSelTimer)
  552. KillTimer(m_hwnd, idtSelChangeTimer);
  553. m_idSelTimer = SetTimer(m_hwnd,
  554. idtSelChangeTimer,
  555. (pnmtv->action == TVC_BYKEYBOARD) ? GetDoubleClickTime()*3/2 : 1,
  556. NULL);
  557. if (m_pFolderBar)
  558. m_pFolderBar->KillScopeDropDown();
  559. }
  560. else
  561. {
  562. if (m_pNotify)
  563. {
  564. FOLDERID idFolder = GetSelection();
  565. m_pNotify->OnSelChange(idFolder);
  566. }
  567. }
  568. break;
  569. case TVN_ITEMEXPANDING:
  570. // TODO: remove this as soon as the folder enumerator
  571. // returns us the folders in the proper sorted order
  572. pnmtv = (NM_TREEVIEW *)pnmhdr;
  573. if (pnmtv->action == TVE_EXPAND)
  574. SortChildren(pnmtv->itemNew.hItem);
  575. break;
  576. case NM_CUSTOMDRAW:
  577. return(OnCustomDraw((NMCUSTOMDRAW *)pnmhdr));
  578. case NM_SETFOCUS:
  579. if (IsWindowVisible(m_hwnd))
  580. UnkOnFocusChangeIS(m_pObjSite, (IInputObject*)this, TRUE);
  581. break;
  582. case NM_KILLFOCUS:
  583. if (m_pFolderBar)
  584. m_pFolderBar->KillScopeDropDown();
  585. break;
  586. case NM_DBLCLK:
  587. if (m_pNotify)
  588. {
  589. FOLDERID idFolder = GetSelection();
  590. m_pNotify->OnDoubleClick(idFolder);
  591. }
  592. break;
  593. }
  594. return 0;
  595. }
  596. LRESULT CTreeView::OnCustomDraw(NMCUSTOMDRAW *pnmcd)
  597. {
  598. TCHAR szNum[CCHMAX_STRINGRES];
  599. TCHAR szText[CCHMAX_STRINGRES];
  600. RECT rc;
  601. COLORREF cr;
  602. int cb, cb1;
  603. COLORREF crOldBkColr = 0;
  604. COLORREF crOldTxtColr = 0;
  605. COLORREF crBkColor = 0;
  606. COLORREF crTxtColor = 0;
  607. TV_ITEM tv;
  608. LPFOLDERNODE pNode;
  609. FOLDERINFO Folder;
  610. NMTVCUSTOMDRAW *ptvcd = (NMTVCUSTOMDRAW *) pnmcd;
  611. switch (pnmcd->dwDrawStage)
  612. {
  613. case CDDS_PREPAINT:
  614. // if we're in dialog-mode, we don't unread count displayed
  615. return((0 == (m_dwFlags & TREEVIEW_DIALOG)) ? (CDRF_NOTIFYPOSTPAINT | CDRF_NOTIFYITEMDRAW) : CDRF_DODEFAULT);
  616. case CDDS_ITEMPREPAINT:
  617. //If this item is disconnected then we gray out the text
  618. pNode = (LPFOLDERNODE)pnmcd->lItemlParam;
  619. if (pNode && 0 == (pnmcd->uItemState & CDIS_SELECTED))
  620. {
  621. if ((pNode->Folder.cWatchedUnread) && (m_clrWatched > 0 && m_clrWatched <= 16))
  622. {
  623. ptvcd->clrText = rgrgbColors16[m_clrWatched - 1];
  624. }
  625. else if (!!(FIDF_DISCONNECTED & pNode->dwFlags))
  626. {
  627. crTxtColor = GetSysColor(COLOR_GRAYTEXT);
  628. crBkColor = GetSysColor(COLOR_BACKGROUND);
  629. if ((crTxtColor) || (crBkColor != crTxtColor))
  630. {
  631. ptvcd->clrText = crTxtColor;
  632. }
  633. else if ((crBkColor == crTxtColor) && (crTxtColor = GetSysColor(COLOR_INACTIVECAPTIONTEXT)))
  634. {
  635. ptvcd->clrText = crTxtColor;
  636. }
  637. }
  638. }
  639. // if we're editing the label for this item, we don't want to paint the unread count
  640. return((m_fEditLabel && m_hitemEdit == (HTREEITEM)pnmcd->dwItemSpec) ? CDRF_DODEFAULT : CDRF_NOTIFYPOSTPAINT);
  641. case CDDS_ITEMPOSTPAINT:
  642. // now we need to paint the unread count, if any...
  643. pNode = (LPFOLDERNODE)pnmcd->lItemlParam;
  644. if (CUnread(&pNode->Folder) > 0)
  645. {
  646. HFONT hf = (HFONT) ::SendMessage(m_hwndTree, WM_GETFONT, 0, 0);
  647. HFONT hf2 = SelectFont(pnmcd->hdc, hf);
  648. cr = SetTextColor(pnmcd->hdc, RGB(0, 0, 0xff));
  649. if (cr != CLR_INVALID)
  650. {
  651. if (TreeView_GetItemRect(m_hwndTree, (HTREEITEM)pnmcd->dwItemSpec, &rc, TRUE))
  652. {
  653. TCHAR c = TEXT('J');
  654. SIZE size;
  655. //$REVIEW - this size could be cached, but it doesn't seem to be a problem
  656. GetTextExtentPoint(pnmcd->hdc, &c, 1, &size);
  657. cb = wnsprintf(szNum, ARRAYSIZE(szNum), "(%d)", CUnread(&pNode->Folder));
  658. TextOut(pnmcd->hdc, rc.right + 2, rc.top + (rc.bottom - rc.top - size.cy) / 2, szNum, cb);
  659. }
  660. SetTextColor(pnmcd->hdc, cr);
  661. }
  662. SelectFont(pnmcd->hdc, hf2);
  663. }
  664. break;
  665. }
  666. return (CDRF_DODEFAULT);
  667. }
  668. //
  669. // FUNCTION: CTreeView::OnContextMenu
  670. //
  671. // PURPOSE: If the WM_CONTEXTMENU message is generated from the keyboard
  672. // then figure out a pos to invoke the menu. Then dispatch the
  673. // request to the handler.
  674. //
  675. // PARAMETERS:
  676. // hwnd - Handle of the view window.
  677. // hwndClick - Handle of the window the user clicked in.
  678. // x, y - Position of the mouse click in screen coordinates.
  679. //
  680. void CTreeView::OnContextMenu(HWND hwnd, HWND hwndClick, int x, int y)
  681. {
  682. IContextMenu *pContextMenu;
  683. LPFOLDERNODE pNode;
  684. HRESULT hr;
  685. CMINVOKECOMMANDINFO ici;
  686. HMENU hmenu;
  687. int id = 0;
  688. RECT rc;
  689. POINT pt = {(int)(short) x, (int)(short) y};
  690. TV_HITTESTINFO tvhti;
  691. HTREEITEM hti = 0;
  692. int idMenu = 0;
  693. HWND hwndBrowser = 0;
  694. if (!!(m_dwFlags & TREEVIEW_DIALOG))
  695. return;
  696. // Get the browser window from the IAthenaBrowser interface. If we don't
  697. // use the browser window to pass to IContextMenu, then when the treeview
  698. // is in autohide mode, the mouse capture goes beserk.
  699. if (FAILED(m_pBrowser->GetWindow(&hwndBrowser)))
  700. return;
  701. if (MAKELPARAM(x, y) == -1) // invoked from keyboard: figure out pos.
  702. {
  703. Assert(hwndClick == m_hwndTree);
  704. hti = TreeView_GetSelection(m_hwndTree);
  705. TreeView_GetItemRect(m_hwndTree, hti, &rc, FALSE);
  706. ClientToScreen(m_hwndTree, (POINT *)&rc);
  707. x = rc.left;
  708. y = rc.top;
  709. }
  710. else
  711. {
  712. ScreenToClient(m_hwndTree, &pt);
  713. tvhti.pt = pt;
  714. hti = TreeView_HitTest(m_hwndTree, &tvhti);
  715. }
  716. if (hti == NULL)
  717. return;
  718. TreeView_SelectDropTarget(m_hwndTree, hti);
  719. // Get the ID of the selected item
  720. pNode = GetFolderNode(hti);
  721. if (pNode)
  722. {
  723. // Load the appropriate context menu
  724. if (SUCCEEDED(MenuUtil_GetContextMenu(pNode->Folder.idFolder, this, &hmenu)))
  725. {
  726. // Display the context menu
  727. id = (int) TrackPopupMenuEx(hmenu,
  728. TPM_RETURNCMD | TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
  729. x, y, m_hwnd, NULL);
  730. // If an ID was returned, process it
  731. if (id != 0)
  732. Exec(NULL, id, OLECMDEXECOPT_DODEFAULT, NULL, NULL);
  733. DestroyMenu(hmenu);
  734. }
  735. }
  736. TreeView_SelectDropTarget(m_hwndTree, NULL);
  737. }
  738. //
  739. // FUNCTION: CTreeView::OnSize
  740. //
  741. // PURPOSE: Notification that the window has been resized. In
  742. // response we update the positions of our child windows and
  743. // controls.
  744. //
  745. // PARAMETERS:
  746. // hwnd - Handle of the view window being resized.
  747. // state - Type of resizing requested.
  748. // cxClient - New width of the client area.
  749. // cyClient - New height of the client area.
  750. //
  751. void CTreeView::OnSize(HWND hwnd, UINT state, int cxClient, int cyClient)
  752. {
  753. SetWindowPos(m_hwndTree, NULL, 0, 0,
  754. cxClient, cyClient,
  755. SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOMOVE);
  756. }
  757. enum
  758. {
  759. UNINIT = 0,
  760. LOCAL,
  761. CONNECTED,
  762. DISCONNECTED
  763. };
  764. HRESULT CTreeView::GetConnectedState(FOLDERINFO *pFolder, int *pconn)
  765. {
  766. HRESULT hr;
  767. char szAcctId[CCHMAX_ACCOUNT_NAME];
  768. Assert(pFolder != NULL);
  769. Assert(pconn != NULL);
  770. *pconn = UNINIT;
  771. if (!!(m_dwFlags & TREEVIEW_DIALOG))
  772. {
  773. // we don't care about connect state in dialogs
  774. // only in the folder list
  775. *pconn = LOCAL;
  776. }
  777. else
  778. {
  779. Assert(pFolder->idFolder != FOLDERID_ROOT);
  780. if (pFolder->tyFolder == FOLDER_LOCAL)
  781. {
  782. *pconn = LOCAL;
  783. }
  784. /*
  785. else if (!!(pFolder->dwFlags & FOLDER_SERVER))
  786. {
  787. *pconn = (g_pConMan->CanConnect(pFolder->pszAccountId) == S_OK) ? CONNECTED : DISCONNECTED;
  788. }
  789. else
  790. {
  791. hr = GetFolderAccountId(pFolder, szAcctId);
  792. if (SUCCEEDED(hr))
  793. *pconn = (g_pConMan->CanConnect(szAcctId) == S_OK) ? CONNECTED : DISCONNECTED;
  794. }
  795. */
  796. else
  797. {
  798. *pconn = g_pConMan->IsGlobalOffline() ? DISCONNECTED : CONNECTED;
  799. }
  800. }
  801. return(S_OK);
  802. }
  803. HRESULT CTreeView::HrFillTreeView()
  804. {
  805. HRESULT hr;
  806. TV_INSERTSTRUCT tvis;
  807. HTREEITEM hitem;
  808. TCHAR sz[CCHMAX_STRINGRES];
  809. BOOL fUnread;
  810. LPFOLDERNODE pNode;
  811. if (MODE_OUTLOOKNEWS == (g_dwAthenaMode & MODE_OUTLOOKNEWS))
  812. LoadString(g_hLocRes, idsOutlookNewsReader, sz, ARRAYSIZE(sz));
  813. else
  814. LoadString(g_hLocRes, idsAthena, sz, ARRAYSIZE(sz));
  815. tvis.hParent = TVI_ROOT;
  816. tvis.hInsertAfter = TVI_LAST;
  817. tvis.item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM | TVIF_TEXT;
  818. tvis.item.pszText = sz;
  819. if (g_dwAthenaMode & MODE_NEWSONLY)
  820. tvis.item.iImage = iNewsRoot;
  821. else
  822. tvis.item.iImage = iMailNews;
  823. tvis.item.iSelectedImage = tvis.item.iImage;
  824. pNode = (LPFOLDERNODE)ZeroAllocate(sizeof(FOLDERNODE));
  825. if (NULL == pNode)
  826. return E_OUTOFMEMORY;
  827. tvis.item.lParam = (LPARAM)pNode;
  828. hitem = TreeView_InsertItem(m_hwndTree, &tvis);
  829. g_pStore->GetFolderInfo(FOLDERID_ROOT, &pNode->Folder);
  830. hr = FillTreeView2(hitem, &pNode->Folder, 0 == (m_dwFlags & TREEVIEW_DIALOG), UNINIT, &fUnread);
  831. TreeView_Expand(m_hwndTree, hitem, TVE_EXPAND);
  832. return (hr);
  833. }
  834. HRESULT CTreeView::FillTreeView2(HTREEITEM hParent, LPFOLDERINFO pParent,
  835. BOOL fInitExpand, int conn, BOOL *pfUnread)
  836. {
  837. // Locals
  838. HRESULT hr=S_OK;
  839. int connT;
  840. TV_INSERTSTRUCT tvis;
  841. HTREEITEM hitem;
  842. BOOL fNoLocal;
  843. BOOL fNoNews;
  844. BOOL fNoImap;
  845. BOOL fNoHttp;
  846. BOOL fExpand = FALSE;
  847. BOOL fUnread = FALSE;
  848. FOLDERINFO Folder;
  849. LPFOLDERNODE pNode;
  850. IEnumerateFolders *pEnum=NULL;
  851. // Trace
  852. TraceCall("CTreeView::FillTreeView2");
  853. // Initialize
  854. *pfUnread = FALSE;
  855. // Enumerate Children
  856. IF_FAILEXIT(hr = g_pStore->EnumChildren(pParent->idFolder, TRUE, &pEnum));
  857. // Determine what to show
  858. fNoNews = ISFLAGSET(m_dwFlags, TREEVIEW_NONEWS);
  859. fNoImap = ISFLAGSET(m_dwFlags, TREEVIEW_NOIMAP);
  860. fNoHttp = ISFLAGSET(m_dwFlags, TREEVIEW_NOHTTP);
  861. fNoLocal = ISFLAGSET(m_dwFlags, TREEVIEW_NOLOCAL);
  862. // Enumerate the Sub Folders
  863. while (S_OK == pEnum->Next(1, &Folder, NULL))
  864. {
  865. // Is this node hidden ?
  866. if ((fNoNews && Folder.tyFolder == FOLDER_NEWS) ||
  867. (fNoImap && Folder.tyFolder == FOLDER_IMAP) ||
  868. (fNoHttp && Folder.tyFolder == FOLDER_HTTPMAIL) ||
  869. (fNoLocal && Folder.tyFolder == FOLDER_LOCAL) ||
  870. ((g_dwAthenaMode & MODE_OUTLOOKNEWS) && (Folder.tySpecial == FOLDER_INBOX)))
  871. {
  872. // Goto next
  873. g_pStore->FreeRecord(&Folder);
  874. continue;
  875. }
  876. // Some Connection management Thing?
  877. if (conn == UNINIT)
  878. GetConnectedState(&Folder, &connT);
  879. else
  880. connT = conn;
  881. // Set the insert item struct
  882. tvis.hParent = hParent;
  883. tvis.hInsertAfter = TVI_LAST;
  884. //
  885. if (ISFLAGCLEAR(Folder.dwFlags, FOLDER_HIDDEN) &&
  886. (ISFLAGSET(Folder.dwFlags, FOLDER_HASCHILDREN) || ISFLAGSET(Folder.dwFlags, FOLDER_SERVER) ||
  887. ISFLAGSET(Folder.dwFlags, FOLDER_SUBSCRIBED)))
  888. {
  889. // Allocate a pNode
  890. IF_NULLEXIT(pNode = (LPFOLDERNODE)ZeroAllocate(sizeof(FOLDERNODE)));
  891. // Set the Folder Info
  892. CopyMemory(&pNode->Folder, &Folder, sizeof(FOLDERINFO));
  893. // Set the Flags
  894. pNode->dwFlags = (connT == DISCONNECTED ? FIDF_DISCONNECTED : 0);
  895. // Don't free Folder
  896. Folder.pAllocated = NULL;
  897. // Insert this item
  898. hitem = ITreeView_InsertItem(&tvis, pNode);
  899. // Has unread
  900. fUnread = (fUnread || (CUnread(&Folder) > 0));
  901. // Insert this node's children ?
  902. if (ISFLAGSET(pNode->Folder.dwFlags, FOLDER_HASCHILDREN))
  903. {
  904. // Fill with children
  905. FillTreeView2(hitem, &pNode->Folder, fExpand, connT, pfUnread);
  906. }
  907. // Need to expand ?
  908. fExpand = (fInitExpand && ISFLAGSET(pNode->Folder.dwFlags, FOLDER_HASCHILDREN) && !!(pNode->Folder.dwFlags & FOLDER_EXPANDTREE));
  909. }
  910. // Otherwise, the node was not inserted
  911. else
  912. hitem = NULL;
  913. // Expand this node ?
  914. if (hitem && *pfUnread && m_fExpandUnread)
  915. {
  916. // Expand this node
  917. TreeView_Expand(m_hwndTree, hitem, TVE_EXPAND);
  918. // There are unread
  919. fUnread = TRUE;
  920. }
  921. // Expand this node ?
  922. else if (hitem && fExpand)
  923. {
  924. // Expand this node
  925. TreeView_Expand(m_hwndTree, hitem, TVE_EXPAND);
  926. }
  927. // Free Current
  928. g_pStore->FreeRecord(&Folder);
  929. }
  930. // Was there unread folders ?
  931. *pfUnread = fUnread;
  932. exit:
  933. // Cleanup
  934. SafeRelease(pEnum);
  935. // Done
  936. return(hr);
  937. }
  938. HTREEITEM CTreeView::ITreeView_InsertItem(TV_INSERTSTRUCT *ptvis, LPFOLDERNODE pNode)
  939. {
  940. Assert(ptvis != NULL);
  941. Assert(pNode != NULL);
  942. ptvis->item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM | TVIF_TEXT;
  943. ptvis->item.iImage = GetFolderIcon(&pNode->Folder, !!(m_dwFlags & TREEVIEW_DIALOG));
  944. ptvis->item.iSelectedImage = ptvis->item.iImage;
  945. ptvis->item.lParam = (LPARAM)pNode;
  946. if (0 == (m_dwFlags & TREEVIEW_DIALOG) && CUnread(&pNode->Folder) > 0)
  947. {
  948. ptvis->item.mask |= TVIF_STATE;
  949. ptvis->item.state = TVIS_BOLD;
  950. ptvis->item.stateMask = TVIS_BOLD;
  951. }
  952. ptvis->item.pszText = pNode->Folder.pszName;
  953. return(TreeView_InsertItem(m_hwndTree, ptvis));
  954. }
  955. BOOL CTreeView::ITreeView_SetItem(TV_ITEM *ptvi, LPFOLDERINFO pFolder)
  956. {
  957. BOOL f;
  958. Assert(ptvi != NULL);
  959. ptvi->mask |= (TVIF_STATE | TVIF_TEXT);
  960. ptvi->stateMask = TVIS_BOLD;
  961. if (0 == (m_dwFlags & TREEVIEW_DIALOG) && CUnread(pFolder) > 0)
  962. ptvi->state = TVIS_BOLD;
  963. else
  964. ptvi->state = 0;
  965. ptvi->pszText = pFolder->pszName;
  966. f = TreeView_SetItem(m_hwndTree, ptvi);
  967. return f;
  968. }
  969. HRESULT CTreeView::Refresh(void)
  970. {
  971. TV_ITEM item;
  972. FOLDERID idFolder;
  973. if (IsWindow(m_hwndTree))
  974. {
  975. idFolder = GetSelection();
  976. TreeView_DeleteAllItems(m_hwndTree);
  977. m_fIgnoreNotify = TRUE;
  978. HrFillTreeView();
  979. if (FOLDERID_INVALID != idFolder)
  980. {
  981. SetSelection(idFolder, 0);
  982. }
  983. m_fIgnoreNotify = FALSE;
  984. }
  985. return (S_OK);
  986. }
  987. FOLDERID CTreeView::GetSelection()
  988. {
  989. HTREEITEM hitem;
  990. FOLDERID idFolder=FOLDERID_INVALID;
  991. if (IsWindow(m_hwndTree))
  992. {
  993. hitem = TreeView_GetSelection(m_hwndTree);
  994. if (hitem != NULL)
  995. {
  996. TV_ITEM tvi;
  997. tvi.mask = TVIF_PARAM;
  998. tvi.lParam = 0;
  999. tvi.hItem = hitem;
  1000. if (TreeView_GetItem(m_hwndTree, &tvi))
  1001. {
  1002. LPFOLDERNODE pNode=(LPFOLDERNODE)tvi.lParam;
  1003. if (pNode)
  1004. idFolder = pNode->Folder.idFolder;
  1005. }
  1006. }
  1007. }
  1008. return (idFolder);
  1009. }
  1010. HRESULT CTreeView::SetSelection(FOLDERID idFolder, DWORD dwFlags)
  1011. {
  1012. HRESULT hr;
  1013. HTREEITEM hitem;
  1014. hr = E_FAIL;
  1015. if (IsWindow(m_hwndTree))
  1016. {
  1017. hitem = GetItemFromId(idFolder);
  1018. if (hitem == NULL && !!(dwFlags & TVSS_INSERTIFNOTFOUND))
  1019. {
  1020. hitem = InsertNode(idFolder, 0);
  1021. if (NULL != hitem)
  1022. hr = S_OK;
  1023. }
  1024. if (hitem != NULL && ITreeView_SelectItem(m_hwndTree, hitem))
  1025. hr = S_OK;
  1026. }
  1027. return(hr);
  1028. }
  1029. HRESULT CTreeView::SelectParent()
  1030. {
  1031. HRESULT hr = E_FAIL;
  1032. HTREEITEM hitem;
  1033. if (hitem = TreeView_GetSelection(m_hwndTree))
  1034. {
  1035. if (hitem = TreeView_GetParent(m_hwndTree, hitem))
  1036. {
  1037. if (ITreeView_SelectItem(m_hwndTree, hitem))
  1038. hr = S_OK;
  1039. }
  1040. }
  1041. return(hr);
  1042. }
  1043. struct TREEUNREAD
  1044. {
  1045. HWND hwndTree;
  1046. HTREEITEM hitemSel;
  1047. BOOL fFoundSel;
  1048. FOLDERTYPE tyFolder;
  1049. };
  1050. HTREEITEM FindNextUnreadItem(HTREEITEM hitemCur, TREEUNREAD *ptu)
  1051. {
  1052. HTREEITEM hitem;
  1053. if (hitemCur == ptu->hitemSel)
  1054. ptu->fFoundSel = TRUE;
  1055. else if (ptu->fFoundSel)
  1056. {
  1057. TV_ITEM tvi;
  1058. tvi.mask = TVIF_PARAM;
  1059. tvi.lParam = 0;
  1060. tvi.hItem = hitemCur;
  1061. if (TreeView_GetItem(ptu->hwndTree, &tvi))
  1062. {
  1063. LPFOLDERNODE pNode = (LPFOLDERNODE)tvi.lParam;
  1064. if (pNode)
  1065. {
  1066. if (CUnread(&pNode->Folder))
  1067. return hitemCur;
  1068. }
  1069. }
  1070. }
  1071. if (hitem = TreeView_GetChild(ptu->hwndTree, hitemCur))
  1072. {
  1073. if (hitem = FindNextUnreadItem(hitem, ptu))
  1074. return hitem;
  1075. }
  1076. if (hitemCur = TreeView_GetNextSibling(ptu->hwndTree, hitemCur))
  1077. {
  1078. if (hitem = FindNextUnreadItem(hitemCur, ptu))
  1079. return hitem;
  1080. }
  1081. return NULL;
  1082. }
  1083. HRESULT CTreeView::SelectNextUnreadItem()
  1084. {
  1085. HRESULT hr = E_FAIL;
  1086. TREEUNREAD tu;
  1087. HTREEITEM hitem;
  1088. TV_ITEM tvi;
  1089. if (tu.hitemSel = TreeView_GetSelection(m_hwndTree))
  1090. {
  1091. tvi.mask = TVIF_PARAM;
  1092. tvi.lParam = 0;
  1093. tvi.hItem = tu.hitemSel;
  1094. if (TreeView_GetItem(m_hwndTree, &tvi) && tvi.lParam)
  1095. {
  1096. LPFOLDERNODE pNode = (LPFOLDERNODE)tvi.lParam;
  1097. tu.hwndTree = m_hwndTree;
  1098. tu.fFoundSel = FALSE;
  1099. tu.tyFolder = pNode->Folder.tyFolder;
  1100. if (hitem = TreeView_GetRoot(m_hwndTree))
  1101. {
  1102. if (hitem = FindNextUnreadItem(hitem, &tu))
  1103. {
  1104. if (ITreeView_SelectItem(m_hwndTree, hitem))
  1105. hr = S_OK;
  1106. }
  1107. }
  1108. }
  1109. }
  1110. if (FAILED(hr))
  1111. {
  1112. AthMessageBoxW(m_hwnd, MAKEINTRESOURCEW(idsAthena), MAKEINTRESOURCEW(idsNoMoreUnreadFolders),
  1113. 0, MB_ICONINFORMATION | MB_OK);
  1114. }
  1115. return hr;
  1116. }
  1117. HTREEITEM CTreeView::GetItemFromId(FOLDERID idFolder)
  1118. {
  1119. if (FOLDERID_ROOT == idFolder)
  1120. return TreeView_GetRoot(m_hwndTree);
  1121. return FindKid(TreeView_GetRoot(m_hwndTree), idFolder);
  1122. }
  1123. HTREEITEM CTreeView::FindKid(HTREEITEM hitem, FOLDERID idFolder)
  1124. {
  1125. TV_ITEM item;
  1126. HTREEITEM hChild;
  1127. HTREEITEM hFound;
  1128. hitem = TreeView_GetChild(m_hwndTree, hitem);
  1129. while (hitem != NULL)
  1130. {
  1131. item.hItem = hitem;
  1132. item.mask = TVIF_PARAM;
  1133. if (TreeView_GetItem(m_hwndTree, &item))
  1134. {
  1135. LPFOLDERNODE pNode=(LPFOLDERNODE)item.lParam;
  1136. if (pNode && pNode->Folder.idFolder == idFolder)
  1137. return hitem;
  1138. }
  1139. hFound = FindKid(hitem, idFolder);
  1140. if (hFound)
  1141. return hFound;
  1142. hitem = TreeView_GetNextSibling(m_hwndTree, hitem);
  1143. }
  1144. return(NULL);
  1145. }
  1146. HTREEITEM CTreeView::InsertNode(FOLDERID idFolder, DWORD dwFlags)
  1147. {
  1148. // Locals
  1149. HRESULT hr=S_OK;
  1150. LPFOLDERNODE pNode=NULL;
  1151. INT conn;
  1152. TV_ITEM item;
  1153. BOOL fUnread;
  1154. HTREEITEM hitem;
  1155. HTREEITEM hitemChild;
  1156. HTREEITEM hitemNew=NULL;
  1157. TV_INSERTSTRUCT tvis;
  1158. RECT rc;
  1159. FOLDERINFO Folder={0};
  1160. // Trace
  1161. TraceCall("CTreeView::InsertNode");
  1162. // Get parent of idFolder
  1163. IF_FAILEXIT(hr = g_pStore->GetFolderInfo(idFolder, &Folder));
  1164. // If this is unsubscribed IMAP fldr and we're in show subscribed only, don't show
  1165. if (ISFLAGSET(dwFlags, TVIN_CHECKVISIBILITY))
  1166. {
  1167. // Hidden
  1168. if (ISFLAGSET(Folder.dwFlags, FOLDER_HIDDEN) || !ISFLAGSET(Folder.dwFlags, FOLDER_SUBSCRIBED))
  1169. goto exit;
  1170. // IE5 Bug #55075: We should never insert a folder which has any unsubscribed ancestors
  1171. if (FOLDER_IMAP == Folder.tyFolder)
  1172. {
  1173. FOLDERINFO fiCurrent;
  1174. HRESULT hrTemp;
  1175. fiCurrent.idParent = Folder.idParent;
  1176. while (FOLDERID_INVALID != fiCurrent.idParent && FOLDERID_ROOT != fiCurrent.idParent)
  1177. {
  1178. hrTemp = g_pStore->GetFolderInfo(fiCurrent.idParent, &fiCurrent);
  1179. TraceError(hrTemp);
  1180. if (SUCCEEDED(hrTemp))
  1181. {
  1182. DWORD dwCurrentFlags = fiCurrent.dwFlags;
  1183. g_pStore->FreeRecord(&fiCurrent);
  1184. if (FOLDER_SERVER & dwCurrentFlags)
  1185. // Do not take server node subscription status into account: stop right here
  1186. break;
  1187. if (ISFLAGCLEAR(dwCurrentFlags, FOLDER_SUBSCRIBED))
  1188. goto exit; // Unsubscribed ancestor! NOT VISIBLE
  1189. }
  1190. } // while
  1191. }
  1192. }
  1193. // Check for duplicates ?
  1194. if (ISFLAGSET(dwFlags, TVIN_CHECKFORDUPS))
  1195. {
  1196. // Find the hitem for idFolder
  1197. hitem = GetItemFromId(idFolder);
  1198. // Does it Exist ?
  1199. if (hitem != NULL)
  1200. {
  1201. // Setup an Item
  1202. item.hItem = hitem;
  1203. item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
  1204. // Get the Icon
  1205. LONG iIcon = GetFolderIcon(&Folder, !!(m_dwFlags & TREEVIEW_DIALOG));
  1206. // Change the Icon
  1207. if (TreeView_GetItem(m_hwndTree, &item) && item.iImage != iIcon)
  1208. {
  1209. // Set the Icon
  1210. item.iImage = iIcon;
  1211. item.iSelectedImage = item.iImage;
  1212. // Update the Item
  1213. TreeView_SetItem(m_hwndTree, &item);
  1214. }
  1215. // Set hitemNew
  1216. hitemNew = hitem;
  1217. // Done
  1218. goto exit;
  1219. }
  1220. }
  1221. // If the parent of this new node is the root ?
  1222. if (FOLDERID_INVALID == Folder.idParent)
  1223. {
  1224. // The root is the parent
  1225. hitem = TreeView_GetRoot(m_hwndTree);
  1226. }
  1227. // Otherwise, get the parent...
  1228. else
  1229. {
  1230. // Find the hitem for the parent
  1231. hitem = GetItemFromId(Folder.idParent);
  1232. // No parent found, insert a parent
  1233. if (hitem == NULL)
  1234. {
  1235. // Insert the parent, but don't insert any of its children or there will be duplicates
  1236. hitem = InsertNode(Folder.idParent, TVIN_DONTINSERTCHILDREN);
  1237. // Can't be NULL
  1238. Assert(hitem != NULL);
  1239. }
  1240. }
  1241. // Do we have a parent
  1242. if (hitem != NULL)
  1243. {
  1244. // Get the First Child
  1245. hitemChild = TreeView_GetChild(m_hwndTree, hitem);
  1246. // Setup an Insert struct
  1247. tvis.hParent = hitem;
  1248. tvis.hInsertAfter = TVI_LAST;
  1249. // Get the Connected State
  1250. GetConnectedState(&Folder, &conn);
  1251. // Allocate a pNode
  1252. IF_NULLEXIT(pNode = (LPFOLDERNODE)ZeroAllocate(sizeof(FOLDERNODE)));
  1253. // Set the Folder Info
  1254. CopyMemory(&pNode->Folder, &Folder, sizeof(FOLDERINFO));
  1255. // Set the Flags
  1256. pNode->dwFlags = (conn == DISCONNECTED ? FIDF_DISCONNECTED : 0);
  1257. // Don't free Folder
  1258. Folder.pAllocated = NULL;
  1259. // Insert a new item
  1260. hitemNew = ITreeView_InsertItem(&tvis, pNode);
  1261. // Better not fail
  1262. Assert(hitemNew != NULL);
  1263. // If there are children
  1264. if (0 == (dwFlags & TVIN_DONTINSERTCHILDREN) && !!(Folder.dwFlags & FOLDER_HASCHILDREN))
  1265. FillTreeView2(hitemNew, &Folder, FALSE, conn, &fUnread);
  1266. // Sort this parent children
  1267. SortChildren(hitem);
  1268. // TODO: we shouldn't have to do this. figure out why the parent isn't getting '+' after the insert
  1269. if (hitemChild == NULL)
  1270. {
  1271. // Get the Rect of the parent
  1272. TreeView_GetItemRect(m_hwndTree, hitem, &rc, FALSE);
  1273. // Invalidate it so that it repaints
  1274. InvalidateRect(m_hwndTree, &rc, TRUE);
  1275. }
  1276. // Make sure the new node is visible
  1277. if (hitemNew)
  1278. {
  1279. // Is visible
  1280. TreeView_EnsureVisible(m_hwndTree, hitemNew);
  1281. }
  1282. }
  1283. exit:
  1284. // Cleanup
  1285. g_pStore->FreeRecord(&Folder);
  1286. // Done
  1287. return(hitemNew);
  1288. }
  1289. HTREEITEM CTreeView::MoveNode(FOLDERID idFolder, FOLDERID idParentNew)
  1290. {
  1291. // Locals
  1292. HRESULT hr=S_OK;
  1293. INT conn;
  1294. BOOL fUnread;
  1295. LPFOLDERNODE pNode;
  1296. HTREEITEM hParent;
  1297. HTREEITEM hItemNew=NULL;
  1298. TV_INSERTSTRUCT tvis;
  1299. // Trace
  1300. TraceCall("CTreeView::MoveNode");
  1301. // Delete the Current Node
  1302. DeleteNode(idFolder);
  1303. // Allocate a pNode
  1304. IF_NULLEXIT(pNode = (LPFOLDERNODE)ZeroAllocate(sizeof(FOLDERNODE)));
  1305. // Get the Parent Info
  1306. IF_FAILEXIT(hr = g_pStore->GetFolderInfo(idFolder, &pNode->Folder));
  1307. // Get the Parent hTreeItem
  1308. hParent = GetItemFromId(idParentNew);
  1309. // Fill Insert Item
  1310. tvis.hParent = hParent;
  1311. tvis.hInsertAfter = TVI_LAST;
  1312. // Get Connected State
  1313. GetConnectedState(&pNode->Folder, &conn);
  1314. // Set the Flags
  1315. pNode->dwFlags = (conn == DISCONNECTED ? FIDF_DISCONNECTED : 0);
  1316. // Insert a new item
  1317. hItemNew = ITreeView_InsertItem(&tvis, pNode);
  1318. // Fill the treeview with the children of Folder
  1319. FillTreeView2(hItemNew, &pNode->Folder, FALSE, conn, &fUnread);
  1320. // Sort the Parent
  1321. SortChildren(hParent);
  1322. // Return the new hitem
  1323. Assert(hItemNew == GetItemFromId(idFolder));
  1324. exit:
  1325. // Done
  1326. return(hItemNew);
  1327. }
  1328. BOOL CTreeView::DeleteNode(FOLDERID idFolder)
  1329. {
  1330. HTREEITEM hitem;
  1331. BOOL fRet;
  1332. hitem = GetItemFromId(idFolder);
  1333. if (hitem != NULL)
  1334. {
  1335. fRet = TreeView_DeleteItem(m_hwndTree, hitem);
  1336. Assert(fRet);
  1337. }
  1338. return(hitem != NULL);
  1339. }
  1340. STDMETHODIMP CTreeView::OnTransaction(HTRANSACTION hTransaction, DWORD_PTR dwCookie, IDatabase *pDB)
  1341. {
  1342. // Locals
  1343. HTREEITEM hitem;
  1344. HTREEITEM hitemSelected;
  1345. HTREEITEM hitemNew;
  1346. TV_ITEM tvi;
  1347. FOLDERINFO Folder1={0};
  1348. FOLDERINFO Folder2={0};
  1349. TRANSACTIONTYPE tyTransaction;
  1350. ORDINALLIST Ordinals;
  1351. INDEXORDINAL iIndex;
  1352. // Trace
  1353. TraceCall("CTreeView::OnRecordNotify");
  1354. if (m_hwndTree == NULL)
  1355. return(S_OK);
  1356. // Walk Through Notifications
  1357. while (hTransaction)
  1358. {
  1359. // Set Notification Stuff
  1360. if (FAILED(pDB->GetTransaction(&hTransaction, &tyTransaction, &Folder1, &Folder2, &iIndex, &Ordinals)))
  1361. break;
  1362. // Insert (new Folder notification)
  1363. if (TRANSACTION_INSERT == tyTransaction)
  1364. {
  1365. // Insert the node
  1366. InsertNode(Folder1.idFolder, TVIN_CHECKFORDUPS | TVIN_CHECKVISIBILITY);
  1367. }
  1368. // Update
  1369. else if (TRANSACTION_UPDATE == tyTransaction)
  1370. {
  1371. // Visibility change (subscription or hidden)
  1372. if (ISFLAGSET(Folder1.dwFlags, FOLDER_SUBSCRIBED) != ISFLAGSET(Folder2.dwFlags, FOLDER_SUBSCRIBED) ||
  1373. ISFLAGSET(Folder1.dwFlags, FOLDER_HIDDEN) != ISFLAGSET(Folder2.dwFlags, FOLDER_HIDDEN))
  1374. {
  1375. // Insert the node
  1376. if (ISFLAGSET(Folder2.dwFlags, FOLDER_SUBSCRIBED) && ISFLAGCLEAR(Folder2.dwFlags, FOLDER_HIDDEN))
  1377. {
  1378. // Show the node
  1379. InsertNode(Folder2.idFolder, TVIN_CHECKFORDUPS | TVIN_CHECKVISIBILITY);
  1380. }
  1381. // Remove the Node
  1382. else
  1383. {
  1384. // Delete the Node
  1385. OnNotifyDeleteNode(Folder2.idFolder);
  1386. }
  1387. }
  1388. // Otherwise
  1389. else
  1390. {
  1391. // Unread Change
  1392. if (CUnread(&Folder1) != CUnread(&Folder2))
  1393. {
  1394. // Get the Item
  1395. hitem = GetItemFromId(Folder1.idFolder);
  1396. // If we found it
  1397. if (hitem != NULL)
  1398. {
  1399. // Initialize the item
  1400. tvi.hItem = hitem;
  1401. tvi.mask = TVIF_PARAM | TVIF_STATE;
  1402. // Get the Items
  1403. if (TreeView_GetItem(m_hwndTree, &tvi) && tvi.lParam)
  1404. {
  1405. // This sets bold state for us and forces a repaint...
  1406. ITreeView_SetItem(&tvi, &Folder2);
  1407. // Expand ?
  1408. if (!ISFLAGSET(tvi.state, TVIS_EXPANDED) && m_fExpandUnread && CUnread(&Folder2) > 0)
  1409. {
  1410. // Expand the Node
  1411. ExpandToVisible(m_hwndTree, hitem);
  1412. }
  1413. }
  1414. }
  1415. }
  1416. // Folder Moved ?
  1417. if (Folder1.idParent != Folder2.idParent)
  1418. {
  1419. // Get Current Selection
  1420. hitemSelected = TreeView_GetSelection(m_hwndTree);
  1421. // Better not be NULL
  1422. Assert(hitemSelected != NULL);
  1423. // Get the handle of the old item
  1424. hitem = GetItemFromId(Folder1.idFolder);
  1425. HTREEITEM htiParent = TreeView_GetParent(m_hwndTree, hitem);
  1426. // Move the Node
  1427. hitemNew = MoveNode(Folder1.idFolder, Folder2.idParent);
  1428. // Reset Selection.
  1429. if (hitem == hitemSelected)
  1430. {
  1431. // If the new parent is the deleted items folder, we should
  1432. // select the old node's parent.
  1433. FOLDERINFO rInfo;
  1434. if (SUCCEEDED(g_pStore->GetFolderInfo(Folder2.idParent, &rInfo)))
  1435. {
  1436. if (rInfo.tySpecial == FOLDER_DELETED)
  1437. {
  1438. hitemNew = htiParent;
  1439. }
  1440. g_pStore->FreeRecord(&rInfo);
  1441. }
  1442. ITreeView_SelectItem(m_hwndTree, hitemNew);
  1443. }
  1444. }
  1445. // Folder Renamed ?
  1446. if (lstrcmp(Folder1.pszName, Folder2.pszName) != 0)
  1447. {
  1448. // Get current Selection
  1449. hitemSelected = TreeView_GetSelection(m_hwndTree);
  1450. // Better not be null
  1451. Assert(hitemSelected != NULL);
  1452. // Get the hitem of the folder
  1453. hitem = GetItemFromId(Folder1.idFolder);
  1454. if (hitem != NULL)
  1455. {
  1456. // Reset the Tree view item
  1457. tvi.hItem = hitem;
  1458. tvi.mask = 0;
  1459. // This will reset the folder name
  1460. ITreeView_SetItem(&tvi, &Folder2);
  1461. // Get the Parent
  1462. HTREEITEM hitemParent = TreeView_GetParent(m_hwndTree, hitem);
  1463. // Sort the Children
  1464. SortChildren(hitemParent);
  1465. }
  1466. // Current Selection Changed
  1467. if (hitemSelected == hitem)
  1468. m_pNotify->OnRename(Folder1.idFolder);
  1469. }
  1470. // synchronize state changed ?
  1471. if ((0 == (Folder1.dwFlags & (FOLDER_DOWNLOADHEADERS | FOLDER_DOWNLOADNEW | FOLDER_DOWNLOADALL))) ^
  1472. (0 == (Folder2.dwFlags & (FOLDER_DOWNLOADHEADERS | FOLDER_DOWNLOADNEW | FOLDER_DOWNLOADALL))))
  1473. {
  1474. hitem = GetItemFromId(Folder1.idFolder);
  1475. if (hitem != NULL)
  1476. {
  1477. tvi.hItem = hitem;
  1478. tvi.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
  1479. tvi.iImage = GetFolderIcon(&Folder2, !!(m_dwFlags & TREEVIEW_DIALOG));
  1480. tvi.iSelectedImage = tvi.iImage;
  1481. TreeView_SetItem(m_hwndTree, &tvi);
  1482. }
  1483. }
  1484. // Special folder type changed?
  1485. if (Folder1.tySpecial != Folder2.tySpecial)
  1486. {
  1487. hitem = GetItemFromId(Folder1.idFolder);
  1488. if (hitem != NULL)
  1489. {
  1490. tvi.hItem = hitem;
  1491. tvi.mask = TVIF_PARAM;
  1492. if (TreeView_GetItem(m_hwndTree, &tvi) && tvi.lParam)
  1493. ((LPFOLDERNODE)tvi.lParam)->Folder.tySpecial = Folder2.tySpecial;
  1494. else
  1495. tvi.mask = 0; // I guess we won't be able to change special fldr type!
  1496. tvi.mask |= TVIF_IMAGE | TVIF_SELECTEDIMAGE;
  1497. tvi.iImage = GetFolderIcon(&Folder2, !!(m_dwFlags & TREEVIEW_DIALOG));
  1498. tvi.iSelectedImage = tvi.iImage;
  1499. TreeView_SetItem(m_hwndTree, &tvi);
  1500. hitem = GetItemFromId(Folder2.idParent);
  1501. SortChildren(hitem);
  1502. }
  1503. }
  1504. // Get the item
  1505. hitem = GetItemFromId(Folder1.idFolder);
  1506. // If we found it
  1507. if (hitem != NULL)
  1508. {
  1509. // Initialize the item
  1510. tvi.hItem = hitem;
  1511. tvi.mask = TVIF_PARAM;
  1512. // Get the Items
  1513. if (TreeView_GetItem(m_hwndTree, &tvi) && tvi.lParam)
  1514. {
  1515. // Cast folder node
  1516. LPFOLDERNODE pNode = (LPFOLDERNODE)tvi.lParam;
  1517. // Validate
  1518. Assert(pNode->Folder.idFolder == Folder1.idFolder);
  1519. // Free current folder
  1520. g_pStore->FreeRecord(&pNode->Folder);
  1521. // Copy New Folder
  1522. CopyMemory(&pNode->Folder, &Folder2, sizeof(FOLDERINFO));
  1523. // Don't free Folder2)
  1524. ZeroMemory(&Folder2, sizeof(FOLDERINFO));
  1525. }
  1526. }
  1527. }
  1528. }
  1529. // Delete
  1530. else if (TRANSACTION_DELETE == tyTransaction)
  1531. {
  1532. // Delete the Node
  1533. OnNotifyDeleteNode(Folder1.idFolder);
  1534. }
  1535. }
  1536. // Cleanup
  1537. g_pStore->FreeRecord(&Folder1);
  1538. g_pStore->FreeRecord(&Folder2);
  1539. // Done
  1540. return(S_OK);
  1541. }
  1542. void CTreeView::OnNotifyDeleteNode(FOLDERID idFolder)
  1543. {
  1544. // Locals
  1545. HTREEITEM hitem;
  1546. HTREEITEM hitemSelected;
  1547. HTREEITEM hitemNew;
  1548. // It's OK if this folder is not currently visible (IMAP)
  1549. hitemSelected = TreeView_GetSelection(m_hwndTree);
  1550. // Better not be NULL
  1551. Assert(hitemSelected != NULL);
  1552. // Get the item being deleted
  1553. hitem = GetItemFromId(idFolder);
  1554. // Reset selection if we deleted the currently selected item
  1555. if (hitemSelected == hitem)
  1556. hitemSelected = TreeView_GetParent(m_hwndTree, hitemSelected);
  1557. else
  1558. hitemSelected = NULL;
  1559. // Delete this node
  1560. DeleteNode(idFolder);
  1561. // Reset the Selection
  1562. if (hitemSelected != NULL)
  1563. ITreeView_SelectItem(m_hwndTree, hitemSelected);
  1564. }
  1565. HRESULT CTreeView::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT *pCmdText)
  1566. {
  1567. // Collect some information up front so we only have to ask once.
  1568. ULONG cServer;
  1569. FOLDERID idFolder = GetSelection();
  1570. FOLDERINFO rFolder;
  1571. HTREEITEM htiDrop;
  1572. // Check to see if there's a drop highlight
  1573. if (NULL != (htiDrop = TreeView_GetDropHilight(m_hwndTree)))
  1574. {
  1575. TV_ITEM tvi;
  1576. tvi.mask = TVIF_PARAM;
  1577. tvi.lParam = 0;
  1578. tvi.hItem = htiDrop;
  1579. if (TreeView_GetItem(m_hwndTree, &tvi))
  1580. {
  1581. LPFOLDERNODE pNode = (LPFOLDERNODE)tvi.lParam;
  1582. if (pNode)
  1583. idFolder = pNode->Folder.idFolder;
  1584. }
  1585. }
  1586. // If nothing is selected, we just disable everything
  1587. if (idFolder == FOLDERID_INVALID)
  1588. return (E_UNEXPECTED);
  1589. // Get the Folder Info
  1590. if (FAILED(g_pStore->GetFolderInfo(idFolder, &rFolder)))
  1591. return (E_UNEXPECTED);
  1592. // Break some of this down for readability
  1593. BOOL fSpecial = rFolder.tySpecial != FOLDER_NOTSPECIAL;
  1594. BOOL fServer = rFolder.dwFlags & FOLDER_SERVER;
  1595. BOOL fRoot = FOLDERID_ROOT == idFolder;
  1596. BOOL fNews = rFolder.tyFolder == FOLDER_NEWS;
  1597. BOOL fIMAP = rFolder.tyFolder == FOLDER_IMAP;
  1598. BOOL fFocus = (m_hwndTree == GetFocus());
  1599. BOOL fLocal = rFolder.tyFolder == FOLDER_LOCAL;
  1600. BOOL fSubscribed = rFolder.dwFlags & FOLDER_SUBSCRIBED;
  1601. BOOL fHotMailDisabled = FALSE;
  1602. // Remove Synchronization/subscription from disabled Hotmail
  1603. if(rFolder.tyFolder == FOLDER_HTTPMAIL)
  1604. {
  1605. FOLDERINFO SvrFolderInfo = {0};
  1606. IImnAccount *pAccount = NULL;
  1607. CHAR szAccountId[CCHMAX_ACCOUNT_NAME];
  1608. HRESULT hr = S_OK;
  1609. DWORD dwShow = 0;
  1610. // Get the server for this folder
  1611. IF_FAILEXIT(hr = GetFolderServer(idFolder, &SvrFolderInfo));
  1612. // Get the account ID for the server
  1613. *szAccountId = 0;
  1614. IF_FAILEXIT(hr = GetFolderAccountId(&SvrFolderInfo, szAccountId, ARRAYSIZE(szAccountId)));
  1615. // Get the account interface
  1616. IF_FAILEXIT(hr = g_pAcctMan->FindAccount(AP_ACCOUNT_ID, szAccountId, &pAccount));
  1617. IF_FAILEXIT(hr = pAccount->GetPropDw(AP_HTTPMAIL_DOMAIN_MSN, &dwShow));
  1618. if(dwShow)
  1619. {
  1620. if(HideHotmail())
  1621. {
  1622. fSubscribed = FALSE;
  1623. fHotMailDisabled = TRUE;
  1624. }
  1625. }
  1626. }
  1627. exit:
  1628. for (ULONG i = 0; i < cCmds; i++)
  1629. {
  1630. // Only deal with commands that haven't yet been marked as supported
  1631. if (prgCmds[i].cmdf == 0)
  1632. {
  1633. switch (prgCmds[i].cmdID)
  1634. {
  1635. case ID_OPEN_FOLDER:
  1636. {
  1637. // Always
  1638. prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
  1639. break;
  1640. }
  1641. case ID_NEW_FOLDER:
  1642. case ID_NEW_FOLDER2:
  1643. {
  1644. // Enabled under personal folders and IMAP.
  1645. if (!fNews && !fRoot && rFolder.tySpecial != FOLDER_DELETED)
  1646. prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
  1647. else
  1648. prgCmds[i].cmdf = OLECMDF_SUPPORTED;
  1649. break;
  1650. }
  1651. case ID_COMPACT_ALL:
  1652. case ID_NEXT_UNREAD_FOLDER:
  1653. {
  1654. // This is always enabled.
  1655. prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
  1656. break;
  1657. }
  1658. case ID_RENAME:
  1659. case ID_MOVE:
  1660. {
  1661. // This is only enabled if we don't have a special folder
  1662. // selected and it's not a account or root note.
  1663. if (!fSpecial && !fServer && !fRoot && !fNews)
  1664. prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
  1665. else
  1666. prgCmds[i].cmdf = OLECMDF_SUPPORTED;
  1667. break;
  1668. }
  1669. case ID_COMPACT:
  1670. {
  1671. // This is enabled whenever we have a non-server folder
  1672. if (!fServer && !fRoot)
  1673. prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
  1674. else
  1675. prgCmds[i].cmdf = OLECMDF_SUPPORTED;
  1676. break;
  1677. }
  1678. case ID_DELETE_ACCEL:
  1679. case ID_DELETE_NO_TRASH_ACCEL:
  1680. {
  1681. if (fFocus)
  1682. {
  1683. if (!fServer && !fRoot && !fSpecial)
  1684. prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
  1685. else
  1686. prgCmds[i].cmdf = OLECMDF_SUPPORTED;
  1687. }
  1688. break;
  1689. }
  1690. case ID_DELETE_FOLDER:
  1691. case ID_DELETE_NO_TRASH:
  1692. {
  1693. if (!fServer && !fRoot && !fNews && !fSpecial)
  1694. prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
  1695. else
  1696. prgCmds[i].cmdf = OLECMDF_SUPPORTED;
  1697. break;
  1698. }
  1699. case ID_SUBSCRIBE:
  1700. case ID_UNSUBSCRIBE:
  1701. {
  1702. if (!fServer && !fRoot && (fNews || (fIMAP && !fSpecial)))
  1703. {
  1704. if (fSubscribed ^ (prgCmds[i].cmdID == ID_SUBSCRIBE))
  1705. prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
  1706. else
  1707. prgCmds[i].cmdf = OLECMDF_SUPPORTED;
  1708. }
  1709. else
  1710. {
  1711. prgCmds[i].cmdf = OLECMDF_SUPPORTED;
  1712. }
  1713. break;
  1714. }
  1715. case ID_PROPERTIES:
  1716. {
  1717. // we only handle this if we have the focus
  1718. if (fFocus)
  1719. {
  1720. if (!fRoot && !(fLocal && fServer))
  1721. prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
  1722. else
  1723. prgCmds[i].cmdf = OLECMDF_SUPPORTED;
  1724. }
  1725. break;
  1726. }
  1727. case ID_NEWSGROUPS:
  1728. case ID_IMAP_FOLDERS:
  1729. {
  1730. if (SUCCEEDED(AcctUtil_GetServerCount(prgCmds[i].cmdID == ID_NEWSGROUPS ? SRV_NNTP : SRV_IMAP, &cServer)) &&
  1731. cServer > 0)
  1732. prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
  1733. else
  1734. prgCmds[i].cmdf = OLECMDF_SUPPORTED;
  1735. break;
  1736. }
  1737. case ID_EMPTY_JUNKMAIL:
  1738. {
  1739. FOLDERINFO rInfo;
  1740. // Here's the default value
  1741. prgCmds[i].cmdf = OLECMDF_SUPPORTED;
  1742. // Get the delete items folder
  1743. if (g_pStore)
  1744. {
  1745. if (SUCCEEDED(g_pStore->GetSpecialFolderInfo(FOLDERID_LOCAL_STORE, prgCmds[i].cmdID == ID_EMPTY_JUNKMAIL ? FOLDER_JUNK : FOLDER_DELETED, &rInfo)))
  1746. {
  1747. if (rInfo.cMessages > 0 || FHasChildren(&rInfo, SUBSCRIBED))
  1748. prgCmds[i].cmdf |= OLECMDF_ENABLED;
  1749. g_pStore->FreeRecord(&rInfo);
  1750. }
  1751. }
  1752. break;
  1753. }
  1754. case ID_EMPTY_WASTEBASKET:
  1755. {
  1756. // What we want to do here is see if the account for the currently
  1757. // selected folder has a Deleted Items folder. If it does, and that
  1758. // folder is not empty then this command is enabled.
  1759. FOLDERINFO rInfo;
  1760. FOLDERID idServer;
  1761. prgCmds[i].cmdf = OLECMDF_SUPPORTED;
  1762. if (SUCCEEDED(GetFolderServerId(idFolder, &idServer)))
  1763. {
  1764. if (SUCCEEDED(g_pStore->GetSpecialFolderInfo(idServer, FOLDER_DELETED, &rInfo)))
  1765. {
  1766. if (rInfo.cMessages > 0 || FHasChildren(&rInfo, SUBSCRIBED))
  1767. prgCmds[i].cmdf |= OLECMDF_ENABLED;
  1768. g_pStore->FreeRecord(&rInfo);
  1769. }
  1770. }
  1771. break;
  1772. }
  1773. case ID_SET_DEFAULT_SERVER:
  1774. {
  1775. if (fServer && !fLocal)
  1776. {
  1777. // Check to see if _this_ account is the default
  1778. if (IsDefaultAccount(&rFolder))
  1779. prgCmds[i].cmdf = OLECMDF_LATCHED | OLECMDF_SUPPORTED;
  1780. else
  1781. prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
  1782. }
  1783. else
  1784. {
  1785. prgCmds[i].cmdf = OLECMDF_SUPPORTED;
  1786. }
  1787. break;
  1788. }
  1789. case ID_REFRESH:
  1790. case ID_RESET_LIST:
  1791. case ID_REMOVE_SERVER:
  1792. {
  1793. if (fServer && !fLocal)
  1794. prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
  1795. else
  1796. prgCmds[i].cmdf = OLECMDF_SUPPORTED;
  1797. break;
  1798. }
  1799. case ID_ADD_SHORTCUT:
  1800. {
  1801. BOOL fVisible = FALSE;
  1802. prgCmds[i].cmdf = OLECMDF_SUPPORTED;
  1803. if (fSubscribed && SUCCEEDED(g_pBrowser->GetViewLayout(DISPID_MSGVIEW_OUTLOOK_BAR,
  1804. NULL, &fVisible, NULL, NULL))
  1805. && fVisible)
  1806. {
  1807. prgCmds[i].cmdf |= OLECMDF_ENABLED;
  1808. }
  1809. break;
  1810. }
  1811. case ID_POPUP_SYNCHRONIZE:
  1812. {
  1813. if (!fServer && !fLocal && fSubscribed)
  1814. prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
  1815. else
  1816. prgCmds[i].cmdf = OLECMDF_SUPPORTED;
  1817. break;
  1818. }
  1819. case ID_UNMARK_RETRIEVE_FLD:
  1820. {
  1821. if (!fServer && !fLocal && fSubscribed)
  1822. {
  1823. if (0 == (rFolder.dwFlags & (FOLDER_DOWNLOADHEADERS | FOLDER_DOWNLOADNEW | FOLDER_DOWNLOADALL)))
  1824. prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED | OLECMDF_NINCHED;
  1825. else
  1826. prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
  1827. }
  1828. else
  1829. {
  1830. prgCmds[i].cmdf = OLECMDF_SUPPORTED;
  1831. }
  1832. break;
  1833. }
  1834. case ID_MARK_RETRIEVE_FLD_NEW_HDRS:
  1835. {
  1836. if (!fServer && !fLocal && fSubscribed)
  1837. {
  1838. if (!!(rFolder.dwFlags & FOLDER_DOWNLOADHEADERS))
  1839. prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED | OLECMDF_NINCHED;
  1840. else
  1841. prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
  1842. }
  1843. else
  1844. {
  1845. prgCmds[i].cmdf = OLECMDF_SUPPORTED;
  1846. }
  1847. break;
  1848. }
  1849. case ID_MARK_RETRIEVE_FLD_NEW_MSGS:
  1850. {
  1851. if (!fServer && !fLocal && fSubscribed)
  1852. {
  1853. if (!!(rFolder.dwFlags & FOLDER_DOWNLOADNEW))
  1854. prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED | OLECMDF_NINCHED;
  1855. else
  1856. prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
  1857. }
  1858. else
  1859. {
  1860. prgCmds[i].cmdf = OLECMDF_SUPPORTED;
  1861. }
  1862. break;
  1863. }
  1864. case ID_MARK_RETRIEVE_FLD_ALL_MSGS:
  1865. {
  1866. if (!fServer && !fLocal && fSubscribed)
  1867. {
  1868. if (!!(rFolder.dwFlags & FOLDER_DOWNLOADALL))
  1869. prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED | OLECMDF_NINCHED;
  1870. else
  1871. prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
  1872. }
  1873. else
  1874. {
  1875. prgCmds[i].cmdf = OLECMDF_SUPPORTED;
  1876. }
  1877. break;
  1878. }
  1879. case ID_SYNC_THIS_NOW:
  1880. {
  1881. if (!fLocal && !fHotMailDisabled)
  1882. prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
  1883. else
  1884. prgCmds[i].cmdf = OLECMDF_SUPPORTED;
  1885. break;
  1886. }
  1887. case ID_CATCH_UP:
  1888. {
  1889. if (!fServer && fNews)
  1890. prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
  1891. else
  1892. prgCmds[i].cmdf = OLECMDF_SUPPORTED;
  1893. break;
  1894. break;
  1895. }
  1896. case ID_FIND_FOLDER:
  1897. {
  1898. prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
  1899. break;
  1900. }
  1901. }
  1902. }
  1903. }
  1904. g_pStore->FreeRecord(&rFolder);
  1905. return (S_OK);
  1906. }
  1907. HRESULT CTreeView::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG *pvaIn, VARIANTARG *pvaOut)
  1908. {
  1909. HRESULT hr;
  1910. FOLDERINFO info;
  1911. BOOL fNews, fUnsubscribed;
  1912. FOLDERID id, idFolder = GetSelection();
  1913. HTREEITEM htiDrop = TreeView_GetDropHilight(m_hwndTree);
  1914. // Note - If you do or call anything in here that display's UI, you must
  1915. // parent the UI to m_hwndUIParent. The treeview window might not
  1916. // be visible when this is called.
  1917. if (htiDrop)
  1918. {
  1919. // Get the folder id from the drop highlighted folder
  1920. TV_ITEM tvi;
  1921. tvi.mask = TVIF_PARAM;
  1922. tvi.lParam = 0;
  1923. tvi.hItem = htiDrop;
  1924. if (TreeView_GetItem(m_hwndTree, &tvi))
  1925. {
  1926. LPFOLDERNODE pNode = (LPFOLDERNODE)tvi.lParam;
  1927. if (pNode)
  1928. idFolder = pNode->Folder.idFolder;
  1929. }
  1930. }
  1931. switch (nCmdID)
  1932. {
  1933. case ID_OPEN_FOLDER:
  1934. {
  1935. // Check to see if there's a drop highlight
  1936. if (NULL != htiDrop)
  1937. {
  1938. // Select the item that we're highlighed on
  1939. ITreeView_SelectItem(m_hwndTree, htiDrop);
  1940. }
  1941. return (S_OK);
  1942. }
  1943. case ID_NEW_FOLDER:
  1944. case ID_NEW_FOLDER2:
  1945. {
  1946. SelectFolderDialog(m_hwndUIParent, SFD_NEWFOLDER, idFolder, TREEVIEW_NONEWS | TREEVIEW_DIALOG | FD_DISABLEROOT | FD_FORCEINITSELFOLDER,
  1947. NULL, NULL, NULL);
  1948. return (S_OK);
  1949. }
  1950. case ID_MOVE:
  1951. {
  1952. SelectFolderDialog(m_hwndUIParent, SFD_MOVEFOLDER, idFolder, TREEVIEW_NONEWS | TREEVIEW_DIALOG | FD_DISABLEROOT,
  1953. MAKEINTRESOURCE(idsMove), MAKEINTRESOURCE(idsMoveCaption), NULL);
  1954. return (S_OK);
  1955. }
  1956. case ID_RENAME:
  1957. {
  1958. RenameFolderDlg(m_hwndUIParent, idFolder);
  1959. return (S_OK);
  1960. }
  1961. case ID_DELETE_FOLDER:
  1962. case ID_DELETE_NO_TRASH:
  1963. fUnsubscribed = FALSE;
  1964. hr = g_pStore->GetFolderInfo(idFolder, &info);
  1965. if (SUCCEEDED(hr))
  1966. {
  1967. if (info.tyFolder == FOLDER_NEWS &&
  1968. 0 == (info.dwFlags & FOLDER_SERVER) &&
  1969. 0 == (info.dwFlags & FOLDER_SUBSCRIBED))
  1970. {
  1971. fUnsubscribed = TRUE;
  1972. }
  1973. g_pStore->FreeRecord(&info);
  1974. }
  1975. if (fUnsubscribed)
  1976. {
  1977. DeleteNode(idFolder);
  1978. return(S_OK);
  1979. }
  1980. // fall through...
  1981. case ID_REMOVE_SERVER:
  1982. {
  1983. // Delete it
  1984. MenuUtil_OnDelete(m_hwndUIParent, idFolder, nCmdID == ID_DELETE_NO_TRASH);
  1985. return (S_OK);
  1986. }
  1987. case ID_SUBSCRIBE:
  1988. case ID_UNSUBSCRIBE:
  1989. {
  1990. MenuUtil_OnSubscribeGroups(m_hwndUIParent, &idFolder, 1, nCmdID == ID_SUBSCRIBE);
  1991. return (S_OK);
  1992. }
  1993. case ID_COMPACT:
  1994. {
  1995. CompactFolders(m_hwndUIParent, RECURSE_INCLUDECURRENT, idFolder);
  1996. return (S_OK);
  1997. }
  1998. case ID_COMPACT_ALL:
  1999. {
  2000. CompactFolders(m_hwndUIParent, RECURSE_ONLYSUBSCRIBED | RECURSE_SUBFOLDERS, FOLDERID_ROOT);
  2001. return (S_OK);
  2002. }
  2003. case ID_NEXT_UNREAD_FOLDER:
  2004. {
  2005. SelectNextUnreadItem();
  2006. return (S_OK);
  2007. }
  2008. case ID_PROPERTIES:
  2009. {
  2010. if (m_hwndTree == GetFocus())
  2011. {
  2012. MenuUtil_OnProperties(m_hwndUIParent, idFolder);
  2013. return(S_OK);
  2014. }
  2015. break;
  2016. }
  2017. case ID_EMPTY_JUNKMAIL:
  2018. {
  2019. if (AthMessageBoxW(m_hwndUIParent, MAKEINTRESOURCEW(idsAthenaMail), MAKEINTRESOURCEW(idsWarnEmptyJunkMail),
  2020. NULL, MB_YESNO | MB_DEFBUTTON2) == IDYES)
  2021. {
  2022. EmptySpecialFolder(m_hwndUIParent, FOLDER_JUNK);
  2023. }
  2024. return(S_OK);
  2025. }
  2026. case ID_EMPTY_WASTEBASKET:
  2027. {
  2028. if (AthMessageBoxW(m_hwndUIParent, MAKEINTRESOURCEW(idsAthenaMail), MAKEINTRESOURCEW(idsWarnEmptyDeletedItems),
  2029. NULL, MB_YESNO | MB_DEFBUTTON2) == IDYES)
  2030. {
  2031. FOLDERINFO rInfo;
  2032. FOLDERID idServer;
  2033. if (SUCCEEDED(GetFolderServerId(idFolder, &idServer)))
  2034. {
  2035. if (SUCCEEDED(g_pStore->GetSpecialFolderInfo(idServer, FOLDER_DELETED, &rInfo)))
  2036. {
  2037. if (rInfo.cMessages > 0 || FHasChildren(&rInfo, SUBSCRIBED))
  2038. EmptyFolder(m_hwndUIParent, rInfo.idFolder);
  2039. g_pStore->FreeRecord(&rInfo);
  2040. }
  2041. }
  2042. }
  2043. return (S_OK);
  2044. }
  2045. case ID_SET_DEFAULT_SERVER:
  2046. {
  2047. MenuUtil_OnSetDefaultServer(idFolder);
  2048. return (S_OK);
  2049. }
  2050. case ID_REFRESH:
  2051. case ID_RESET_LIST:
  2052. {
  2053. hr = g_pStore->GetFolderInfo(idFolder, &info);
  2054. if (SUCCEEDED(hr))
  2055. {
  2056. if (info.tyFolder != FOLDER_LOCAL &&
  2057. !!(info.dwFlags & FOLDER_SERVER))
  2058. {
  2059. DownloadNewsgroupList(m_hwndUIParent, idFolder);
  2060. }
  2061. g_pStore->FreeRecord(&info);
  2062. }
  2063. return(S_OK);
  2064. }
  2065. case ID_NEWSGROUPS:
  2066. case ID_IMAP_FOLDERS:
  2067. {
  2068. fNews = (nCmdID == ID_NEWSGROUPS);
  2069. hr = g_pStore->GetFolderInfo(idFolder, &info);
  2070. if (SUCCEEDED(hr))
  2071. {
  2072. if ((fNews && info.tyFolder != FOLDER_NEWS) ||
  2073. (!fNews && info.tyFolder != FOLDER_IMAP) ||
  2074. FAILED(GetFolderServerId(idFolder, &id)))
  2075. {
  2076. id = FOLDERID_INVALID;
  2077. }
  2078. g_pStore->FreeRecord(&info);
  2079. DoSubscriptionDialog(m_hwndUIParent, fNews, id);
  2080. }
  2081. return(S_OK);
  2082. }
  2083. case ID_ADD_SHORTCUT:
  2084. {
  2085. OutlookBar_AddShortcut(idFolder);
  2086. return (S_OK);
  2087. }
  2088. case ID_UNMARK_RETRIEVE_FLD:
  2089. {
  2090. SetSynchronizeFlags(idFolder, 0);
  2091. return(S_OK);
  2092. }
  2093. case ID_MARK_RETRIEVE_FLD_NEW_HDRS:
  2094. {
  2095. SetSynchronizeFlags(idFolder, FOLDER_DOWNLOADHEADERS);
  2096. return(S_OK);
  2097. }
  2098. case ID_MARK_RETRIEVE_FLD_NEW_MSGS:
  2099. {
  2100. SetSynchronizeFlags(idFolder, FOLDER_DOWNLOADNEW);
  2101. return(S_OK);
  2102. }
  2103. case ID_MARK_RETRIEVE_FLD_ALL_MSGS:
  2104. {
  2105. SetSynchronizeFlags(idFolder, FOLDER_DOWNLOADALL);
  2106. return(S_OK);
  2107. }
  2108. case ID_SYNC_THIS_NOW:
  2109. {
  2110. MenuUtil_SyncThisNow(m_hwndUIParent, idFolder);
  2111. return(S_OK);
  2112. }
  2113. case ID_CATCH_UP:
  2114. {
  2115. MenuUtil_OnCatchUp(idFolder);
  2116. return(S_OK);
  2117. }
  2118. case ID_FIND_FOLDER:
  2119. {
  2120. DoFindMsg(idFolder, FOLDER_LOCAL);
  2121. return (S_OK);
  2122. }
  2123. }
  2124. return (OLECMDERR_E_NOTSUPPORTED);
  2125. }
  2126. int CALLBACK TreeViewCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
  2127. {
  2128. INT cmp;
  2129. LPFOLDERNODE pNode1=(LPFOLDERNODE)lParam1;
  2130. LPFOLDERNODE pNode2=(LPFOLDERNODE)lParam2;
  2131. LPFOLDERINFO pFolder1=&pNode1->Folder;
  2132. LPFOLDERINFO pFolder2=&pNode2->Folder;
  2133. Assert(pNode1);
  2134. Assert(pNode2);
  2135. Assert(pFolder1);
  2136. Assert(pFolder2);
  2137. if (!!(pFolder1->dwFlags & FOLDER_SERVER))
  2138. {
  2139. Assert(!!(pFolder2->dwFlags & FOLDER_SERVER));
  2140. if (pFolder1->tyFolder == pFolder2->tyFolder)
  2141. cmp = lstrcmpi(pFolder1->pszName, pFolder2->pszName);
  2142. else
  2143. {
  2144. cmp = pFolder1->tyFolder - pFolder2->tyFolder;
  2145. cmp = (cmp < 0) ? 1 : -1;
  2146. }
  2147. }
  2148. else if (pFolder1->tySpecial != FOLDER_NOTSPECIAL)
  2149. {
  2150. if (pFolder2->tySpecial != FOLDER_NOTSPECIAL)
  2151. cmp = pFolder1->tySpecial - pFolder2->tySpecial;
  2152. else
  2153. cmp = -1;
  2154. }
  2155. else
  2156. {
  2157. if (pFolder2->tySpecial != FOLDER_NOTSPECIAL)
  2158. cmp = 1;
  2159. else
  2160. cmp = lstrcmpi(pFolder1->pszName, pFolder2->pszName);
  2161. }
  2162. return(cmp);
  2163. }
  2164. void CTreeView::SortChildren(HTREEITEM hitem)
  2165. {
  2166. TV_SORTCB sort;
  2167. HRESULT hr;
  2168. // sort the branch that is expanding
  2169. // TODO: find out if it really needs to be sorted
  2170. LPFOLDERNODE pNode = GetFolderNode(hitem);
  2171. if (NULL == pNode)
  2172. return;
  2173. sort.hParent = hitem;
  2174. sort.lpfnCompare = TreeViewCompare;
  2175. sort.lParam = (LPARAM)&pNode->Folder;
  2176. TreeView_SortChildrenCB(m_hwndTree, &sort, TRUE);
  2177. }
  2178. //
  2179. // FUNCTION: CTreeView::DragEnter()
  2180. //
  2181. // PURPOSE: This get's called when the user starts dragging an object
  2182. // over our target area.
  2183. //
  2184. // PARAMETERS:
  2185. // <in> pDataObject - Pointer to the data object being dragged
  2186. // <in> grfKeyState - Pointer to the current key states
  2187. // <in> pt - Point in screen coordinates of the mouse
  2188. // <out> pdwEffect - Where we return whether this is a valid place for
  2189. // pDataObject to be dropped and if so what type of
  2190. // drop.
  2191. //
  2192. // RETURN VALUE:
  2193. // S_OK - The function succeeded.
  2194. //
  2195. HRESULT STDMETHODCALLTYPE CTreeView::DragEnter(IDataObject* pDataObject,
  2196. DWORD grfKeyState,
  2197. POINTL pt, DWORD* pdwEffect)
  2198. {
  2199. Assert(m_pDataObject == NULL);
  2200. DOUTL(32, _T("CTreeView::DragEnter() - Starting"));
  2201. // Initialize our state
  2202. SafeRelease(m_pDTCur);
  2203. m_pDataObject = pDataObject;
  2204. m_pDataObject->AddRef();
  2205. m_grfKeyState = grfKeyState;
  2206. m_htiCur = NULL;
  2207. Assert(m_pDTCur == NULL);
  2208. // Set the default return value to be failure
  2209. m_dwEffectCur = *pdwEffect = DROPEFFECT_NONE;
  2210. if (m_pFolderBar)
  2211. m_pFolderBar->KillScopeCloseTimer();
  2212. UpdateDragDropHilite(&pt);
  2213. return (S_OK);
  2214. }
  2215. //
  2216. // FUNCTION: CTreeView::DragOver()
  2217. //
  2218. // PURPOSE: This is called as the user drags an object over our target.
  2219. // If we allow this object to be dropped on us, then we will have
  2220. // a pointer in m_pDataObject.
  2221. //
  2222. // PARAMETERS:
  2223. // <in> grfKeyState - Pointer to the current key states
  2224. // <in> pt - Point in screen coordinates of the mouse
  2225. // <out> pdwEffect - Where we return whether this is a valid place for
  2226. // pDataObject to be dropped and if so what type of
  2227. // drop.
  2228. //
  2229. // RETURN VALUE:
  2230. // S_OK - The function succeeded.
  2231. //
  2232. HRESULT STDMETHODCALLTYPE CTreeView::DragOver(DWORD grfKeyState, POINTL pt,
  2233. DWORD* pdwEffect)
  2234. {
  2235. FORMATETC fe;
  2236. ULONG celtFetched;
  2237. TV_ITEM tvi;
  2238. HTREEITEM hti;
  2239. HRESULT hr = E_FAIL;
  2240. DWORD dwEffectScroll = 0;
  2241. HWND hwndBrowser;
  2242. // If we don't have a stored data object from DragEnter()
  2243. if (NULL == m_pDataObject)
  2244. return (S_OK);
  2245. // Get the browser window from the IAthenaBrowser interface. If we don't
  2246. // use the browser window to pass to IContextMenu, then when the treeview
  2247. // is in autohide mode, the mouse capture goes beserk.
  2248. if (FAILED(m_pBrowser->GetWindow(&hwndBrowser)))
  2249. return (S_OK);
  2250. // Autoscroll if we need to
  2251. if (AutoScroll((const LPPOINT) &pt))
  2252. dwEffectScroll = DROPEFFECT_SCROLL;
  2253. // Find out which item the mouse is currently over
  2254. if (NULL == (hti = GetItemFromPoint(pt)))
  2255. {
  2256. DOUTL(32, _T("CTreeView::DragOver() - GetItemFromPoint() returns NULL."));
  2257. }
  2258. // If we're over a new tree node, then bind to the folder
  2259. if (m_htiCur != hti)
  2260. {
  2261. // Keep track of this for autoexpand
  2262. m_dwExpandTime = GetTickCount();
  2263. // Release our previous drop target if any
  2264. SafeRelease(m_pDTCur);
  2265. // Update the current object
  2266. m_htiCur = hti;
  2267. // Assume there's no drop target and assume error
  2268. Assert(m_pDTCur == NULL);
  2269. m_dwEffectCur = DROPEFFECT_NONE;
  2270. // Update the treeview UI
  2271. UpdateDragDropHilite(&pt);
  2272. if (hti)
  2273. {
  2274. FOLDERINFO Folder={0};
  2275. // Get Folder Node
  2276. LPFOLDERNODE pNode = GetFolderNode(hti);
  2277. // Get information about this folder
  2278. if (pNode)
  2279. {
  2280. m_pDTCur = new CDropTarget();
  2281. if (m_pDTCur)
  2282. {
  2283. hr = ((CDropTarget *) m_pDTCur)->Initialize(m_hwndUIParent, pNode->Folder.idFolder);
  2284. }
  2285. }
  2286. // If we have a drop target now, call DragEnter()
  2287. if (SUCCEEDED(hr) && m_pDTCur)
  2288. {
  2289. hr = m_pDTCur->DragEnter(m_pDataObject, grfKeyState, pt,
  2290. pdwEffect);
  2291. m_dwEffectCur = *pdwEffect;
  2292. }
  2293. }
  2294. else
  2295. {
  2296. m_dwEffectCur = 0;
  2297. }
  2298. }
  2299. else
  2300. {
  2301. // No target change
  2302. if (m_htiCur)
  2303. {
  2304. DWORD dwNow = GetTickCount();
  2305. // If the person is hovering, expand the node
  2306. if ((dwNow - m_dwExpandTime) >= 1000)
  2307. {
  2308. m_dwExpandTime = dwNow;
  2309. TreeView_Expand(m_hwndTree, m_htiCur, TVE_EXPAND);
  2310. }
  2311. }
  2312. // If the keys changed, we need to re-query the drop target
  2313. if ((m_grfKeyState != grfKeyState) && m_pDTCur)
  2314. {
  2315. m_dwEffectCur = *pdwEffect;
  2316. hr = m_pDTCur->DragOver(grfKeyState, pt, &m_dwEffectCur);
  2317. }
  2318. else
  2319. {
  2320. hr = S_OK;
  2321. }
  2322. }
  2323. *pdwEffect = m_dwEffectCur | dwEffectScroll;
  2324. m_grfKeyState = grfKeyState;
  2325. return (hr);
  2326. }
  2327. //
  2328. // FUNCTION: CTreeView::DragLeave()
  2329. //
  2330. // PURPOSE: Allows us to release any stored data we have from a successful
  2331. // DragEnter()
  2332. //
  2333. // RETURN VALUE:
  2334. // S_OK - Everything is groovy
  2335. //
  2336. HRESULT STDMETHODCALLTYPE CTreeView::DragLeave(void)
  2337. {
  2338. DOUTL(32, _T("CTreeView::DragLeave()"));
  2339. SafeRelease(m_pDTCur);
  2340. SafeRelease(m_pDataObject);
  2341. UpdateDragDropHilite(NULL);
  2342. if (m_pFolderBar)
  2343. m_pFolderBar->SetScopeCloseTimer();
  2344. return (S_OK);
  2345. }
  2346. //
  2347. // FUNCTION: CTreeView::Drop()
  2348. //
  2349. // PURPOSE: The user has let go of the object over our target. If we
  2350. // can accept this object we will already have the pDataObject
  2351. // stored in m_pDataObject. If this is a copy or move, then
  2352. // we go ahead and update the store. Otherwise, we bring up
  2353. // a send note with the object attached.
  2354. //
  2355. // PARAMETERS:
  2356. // <in> pDataObject - Pointer to the data object being dragged
  2357. // <in> grfKeyState - Pointer to the current key states
  2358. // <in> pt - Point in screen coordinates of the mouse
  2359. // <out> pdwEffect - Where we return whether this is a valid place for
  2360. // pDataObject to be dropped and if so what type of
  2361. // drop.
  2362. //
  2363. // RETURN VALUE:
  2364. // S_OK - Everything worked OK
  2365. //
  2366. HRESULT STDMETHODCALLTYPE CTreeView::Drop(IDataObject* pDataObject,
  2367. DWORD grfKeyState, POINTL pt,
  2368. DWORD* pdwEffect)
  2369. {
  2370. HRESULT hr;
  2371. Assert(m_pDataObject == pDataObject);
  2372. DOUTL(32, _T("CTreeView::Drop() - Starting"));
  2373. if (m_pDTCur)
  2374. {
  2375. hr = m_pDTCur->Drop(pDataObject, grfKeyState, pt, pdwEffect);
  2376. }
  2377. else
  2378. {
  2379. DOUTL(32, "CTreeView::Drop() - no drop target.");
  2380. *pdwEffect = 0;
  2381. hr = S_OK;
  2382. }
  2383. UpdateDragDropHilite(NULL);
  2384. if (m_pFolderBar)
  2385. {
  2386. m_pFolderBar->KillScopeDropDown();
  2387. }
  2388. SafeRelease(m_pDataObject);
  2389. SafeRelease(m_pDTCur);
  2390. return (hr);
  2391. }
  2392. //
  2393. // FUNCTION: CTreeView::UpdateDragDropHilite()
  2394. //
  2395. // PURPOSE: Called by the various IDropTarget interfaces to move the drop
  2396. // selection to the correct place in our listview.
  2397. //
  2398. // PARAMETERS:
  2399. // <in> *ppt - Contains the point that the mouse is currently at. If this
  2400. // is NULL, then the function removes any previous UI.
  2401. //
  2402. void CTreeView::UpdateDragDropHilite(POINTL *ppt)
  2403. {
  2404. TV_HITTESTINFO tvhti;
  2405. HTREEITEM htiTarget = NULL;
  2406. // Unlock the treeview and let it repaint. Then update the selected
  2407. // item. If htiTarget is NULL, the the drag highlight goes away.
  2408. // ImageList_DragLeave(m_hwndTree);
  2409. // If a position was provided
  2410. if (ppt)
  2411. {
  2412. // Figure out which item is selected
  2413. tvhti.pt.x = ppt->x;
  2414. tvhti.pt.y = ppt->y;
  2415. ScreenToClient(m_hwndTree, &tvhti.pt);
  2416. htiTarget = TreeView_HitTest(m_hwndTree, &tvhti);
  2417. // Only if the cursor is over something do we relock the window.
  2418. if (htiTarget)
  2419. {
  2420. TreeView_SelectDropTarget(m_hwndTree, htiTarget);
  2421. }
  2422. }
  2423. else
  2424. TreeView_SelectDropTarget(m_hwndTree, NULL);
  2425. }
  2426. BOOL CTreeView::AutoScroll(const POINT *ppt)
  2427. {
  2428. // Find out if the point is above or below the tree
  2429. RECT rcTree;
  2430. GetWindowRect(m_hwndTree, &rcTree);
  2431. // Reduce the rect so we have a scroll margin all the way around
  2432. InflateRect(&rcTree, -32, -32);
  2433. if (rcTree.top > ppt->y)
  2434. {
  2435. // Scroll down
  2436. FORWARD_WM_VSCROLL(m_hwndTree, NULL, SB_LINEUP, 1, SendMessage);
  2437. return (TRUE);
  2438. }
  2439. else if (rcTree.bottom < ppt->y)
  2440. {
  2441. // Scroll Up
  2442. FORWARD_WM_VSCROLL(m_hwndTree, NULL, SB_LINEDOWN, 1, SendMessage);
  2443. return (TRUE);
  2444. }
  2445. return (FALSE);
  2446. }
  2447. //
  2448. // FUNCTION: CTreeView::GetItemFromPoint()
  2449. //
  2450. // PURPOSE: Given a point, this function returns the listview item index
  2451. // and HFOLDER for the item under that point.
  2452. //
  2453. // PARAMETERS:
  2454. // <in> pt - Position in screen coordinates to check for.
  2455. // <in> phFolder - Returns the HFOLDER for the item under pt. If there
  2456. // isn't an item under pt, then NULL is returned.
  2457. //
  2458. // RETURN VALUE:
  2459. // Returns the handle of the item under the specified point. If no item
  2460. // exists, then NULL is returned.
  2461. //
  2462. HTREEITEM CTreeView::GetItemFromPoint(POINTL pt)
  2463. {
  2464. TV_HITTESTINFO tvhti;
  2465. TV_ITEM tvi;
  2466. HTREEITEM htiTarget;
  2467. // Find out from the ListView what item are we over
  2468. tvhti.pt.x = pt.x;
  2469. tvhti.pt.y = pt.y;
  2470. ScreenToClient(m_hwndTree, &(tvhti.pt));
  2471. htiTarget = TreeView_HitTest(m_hwndTree, &tvhti);
  2472. return (tvhti.hItem);
  2473. }
  2474. HRESULT STDMETHODCALLTYPE CTreeView::QueryContinueDrag(BOOL fEscapePressed,
  2475. DWORD grfKeyState)
  2476. {
  2477. if (fEscapePressed)
  2478. return (DRAGDROP_S_CANCEL);
  2479. if (grfKeyState & MK_RBUTTON)
  2480. return (DRAGDROP_S_CANCEL);
  2481. if (!(grfKeyState & MK_LBUTTON))
  2482. return (DRAGDROP_S_DROP);
  2483. return (S_OK);
  2484. }
  2485. HRESULT STDMETHODCALLTYPE CTreeView::GiveFeedback(DWORD dwEffect)
  2486. {
  2487. return (DRAGDROP_S_USEDEFAULTCURSORS);
  2488. }
  2489. //
  2490. // FUNCTION: CTreeView::OnBeginDrag()
  2491. //
  2492. // PURPOSE: This function is called when the user begins dragging an item
  2493. // in the ListView. If the item selected is a draggable item,
  2494. // then we create an IDataObject for the item and then call OLE's
  2495. // DoDragDrop().
  2496. //
  2497. // PARAMETERS:
  2498. // <in> pnmlv - Pointer to an NM_LISTIVEW struct which tells us which item
  2499. // has been selected to be dragged.
  2500. //
  2501. // RETURN VALUE:
  2502. // Returns zero always.
  2503. //
  2504. LRESULT CTreeView::OnBeginDrag(NM_TREEVIEW* pnmtv)
  2505. {
  2506. FOLDERID idFolderSel=FOLDERID_INVALID;
  2507. DWORD cSel = 0;
  2508. int iSel = -1;
  2509. FOLDERID *pidFolder = 0;
  2510. PDATAOBJINFO pdoi = 0;
  2511. DWORD dwEffect;
  2512. IDataObject *pDataObj = 0;
  2513. HTREEITEM htiSel;
  2514. LPFOLDERNODE pNode;
  2515. // Bug #17491 - Check to see if this is the root node. If so, we don't drag.
  2516. if (0 == pnmtv->itemNew.lParam)
  2517. return (0);
  2518. // Get the Node
  2519. pNode = (LPFOLDERNODE)pnmtv->itemNew.lParam;
  2520. if (NULL == pNode)
  2521. return (0);
  2522. CFolderDataObject *pDataObject = new CFolderDataObject(pNode->Folder.idFolder);
  2523. if (!pDataObject)
  2524. return (0);
  2525. DoDragDrop(pDataObject, (IDropSource*) this, DROPEFFECT_MOVE | DROPEFFECT_COPY | DROPEFFECT_LINK, &dwEffect);
  2526. pDataObject->Release();
  2527. return (0);
  2528. }
  2529. LRESULT CTreeView::OnBeginLabelEdit(TV_DISPINFO* ptvdi)
  2530. {
  2531. RECT rc, rcT;
  2532. // Get Folder Node
  2533. LPFOLDERNODE pNode = (LPFOLDERNODE)ptvdi->item.lParam;
  2534. if (NULL == pNode)
  2535. return (FALSE);
  2536. // Can Rename
  2537. if (ISFLAGSET(pNode->Folder.dwFlags, FOLDER_CANRENAME))
  2538. {
  2539. m_fEditLabel = TRUE;
  2540. m_hitemEdit = ptvdi->item.hItem;
  2541. if (TreeView_GetItemRect(m_hwndTree, ptvdi->item.hItem, &rc, TRUE))
  2542. {
  2543. GetClientRect(m_hwndTree, &rcT);
  2544. rc.left = rc.right;
  2545. rc.right = rcT.right;
  2546. InvalidateRect(m_hwndTree, &rc, TRUE);
  2547. }
  2548. return (FALSE);
  2549. }
  2550. else
  2551. {
  2552. return (TRUE);
  2553. }
  2554. }
  2555. BOOL CTreeView::OnEndLabelEdit(TV_DISPINFO* ptvdi)
  2556. {
  2557. HRESULT hr;
  2558. LPFOLDERNODE pNode;
  2559. IImnAccount *pAcct, *pAcctT;
  2560. BOOL fReturn = FALSE;
  2561. HWND hwndBrowser;
  2562. m_fEditLabel = FALSE;
  2563. m_hitemEdit = NULL;
  2564. // First check to see if label editing was canceled
  2565. if (0 == ptvdi->item.pszText)
  2566. return (FALSE);
  2567. // Get the browser window from the IAthenaBrowser interface. If we don't
  2568. // use the browser window to pass to IContextMenu, then when the treeview
  2569. // is in autohide mode, the mouse capture goes beserk.
  2570. if (FAILED(m_pBrowser->GetWindow(&hwndBrowser)))
  2571. return (FALSE);
  2572. // Get the node
  2573. pNode = (LPFOLDERNODE)ptvdi->item.lParam;
  2574. if (NULL == pNode)
  2575. return (FALSE);
  2576. // Local Folder
  2577. if (FALSE == ISFLAGSET(pNode->Folder.dwFlags, FOLDER_SERVER))
  2578. {
  2579. if (FAILED(hr = RenameFolderProgress(hwndBrowser, pNode->Folder.idFolder, ptvdi->item.pszText, NOFLAGS)))
  2580. {
  2581. // Display an error message
  2582. AthErrorMessageW(hwndBrowser, MAKEINTRESOURCEW(idsAthenaMail),
  2583. MAKEINTRESOURCEW(idsErrRenameFld), hr);
  2584. return (FALSE);
  2585. }
  2586. }
  2587. // Servers
  2588. else
  2589. {
  2590. Assert(g_pAcctMan);
  2591. Assert(!FIsEmptyA(pNode->Folder.pszAccountId));
  2592. if (!FIsEmpty(ptvdi->item.pszText) &&
  2593. 0 != lstrcmpi(pNode->Folder.pszName, ptvdi->item.pszText) &&
  2594. SUCCEEDED(g_pAcctMan->FindAccount(AP_ACCOUNT_ID, pNode->Folder.pszAccountId, &pAcct)))
  2595. {
  2596. if (SUCCEEDED(g_pAcctMan->FindAccount(AP_ACCOUNT_NAME, ptvdi->item.pszText, &pAcctT)))
  2597. {
  2598. Assert(!fReturn);
  2599. pAcctT->Release();
  2600. hr = E_DuplicateAccountName;
  2601. }
  2602. else
  2603. {
  2604. fReturn = SUCCEEDED(hr = pAcct->SetPropSz(AP_ACCOUNT_NAME, ptvdi->item.pszText));
  2605. if (fReturn)
  2606. fReturn = SUCCEEDED(hr = pAcct->SaveChanges());
  2607. }
  2608. if (hr == E_DuplicateAccountName)
  2609. {
  2610. TCHAR szRes[CCHMAX_STRINGRES], szBuf[CCHMAX_STRINGRES];
  2611. AthLoadString(idsErrDuplicateAccount, szRes, ARRAYSIZE(szRes));
  2612. wnsprintf(szBuf, ARRAYSIZE(szBuf), szRes, ptvdi->item.pszText);
  2613. AthMessageBox(hwndBrowser, MAKEINTRESOURCE(idsAthena), szBuf, 0, MB_ICONSTOP | MB_OK);
  2614. }
  2615. else if (FAILED(hr))
  2616. {
  2617. AthMessageBoxW(hwndBrowser, MAKEINTRESOURCEW(idsAthena), MAKEINTRESOURCEW(idsErrRenameAccountFailed),
  2618. 0, MB_ICONSTOP | MB_OK);
  2619. }
  2620. pAcct->Release();
  2621. return (fReturn);
  2622. }
  2623. else
  2624. {
  2625. return (FALSE);
  2626. }
  2627. }
  2628. return (TRUE);
  2629. }
  2630. HRESULT CTreeView::RegisterFlyOut(CFolderBar *pFolderBar)
  2631. {
  2632. Assert(m_pFolderBar == NULL);
  2633. m_pFolderBar = pFolderBar;
  2634. m_pFolderBar->AddRef();
  2635. RegisterGlobalDropDown(m_hwnd);
  2636. return S_OK;
  2637. }
  2638. HRESULT CTreeView::RevokeFlyOut(void)
  2639. {
  2640. if (m_pFolderBar)
  2641. {
  2642. m_pFolderBar->Release();
  2643. m_pFolderBar = NULL;
  2644. }
  2645. UnregisterGlobalDropDown(m_hwnd);
  2646. return S_OK;
  2647. }
  2648. HRESULT CTreeView::OnConnectionNotify(CONNNOTIFY nCode, LPVOID pvData, CConnectionManager *pConMan)
  2649. {
  2650. UpdateLabelColors();
  2651. return (S_OK);
  2652. }
  2653. void CTreeView::UpdateLabelColors()
  2654. {
  2655. BOOL fConn;
  2656. HTREEITEM treeitem;
  2657. LPFOLDERNODE pNode;
  2658. TVITEM item;
  2659. if (m_hwndTree != NULL)
  2660. {
  2661. treeitem = TreeView_GetRoot(m_hwndTree);
  2662. if (treeitem != NULL)
  2663. {
  2664. treeitem = TreeView_GetChild(m_hwndTree, treeitem);
  2665. while (treeitem != NULL)
  2666. {
  2667. item.hItem = treeitem;
  2668. item.mask = TVIF_PARAM;
  2669. if (TreeView_GetItem (m_hwndTree, &item) && (pNode = (LPFOLDERNODE)item.lParam) != NULL)
  2670. {
  2671. Assert(!!(pNode->Folder.dwFlags & FOLDER_SERVER));
  2672. if (pNode->Folder.tyFolder != FOLDER_LOCAL)
  2673. {
  2674. Assert(!FIsEmptyA(pNode->Folder.pszAccountId));
  2675. //fConn = (g_pConMan->CanConnect(Folder.pszAccountId) == S_OK);
  2676. fConn = g_pConMan->IsGlobalOffline();
  2677. //if (fConn ^ (0 == (pNode->dwFlags & FIDF_DISCONNECTED)))
  2678. if (fConn ^ (!!(pNode->dwFlags & FIDF_DISCONNECTED)))
  2679. {
  2680. // if the connect state has changed, then let's update
  2681. UpdateChildren(treeitem, !fConn, FALSE);
  2682. }
  2683. }
  2684. treeitem = TreeView_GetNextSibling(m_hwndTree, treeitem);
  2685. }
  2686. }
  2687. }
  2688. }
  2689. }
  2690. void CTreeView::UpdateChildren(HTREEITEM treeitem, BOOL canconn, BOOL fSiblings)
  2691. {
  2692. HTREEITEM hitem;
  2693. RECT rect;
  2694. LPFOLDERNODE pNode;
  2695. Assert(treeitem != NULL);
  2696. while (treeitem != NULL)
  2697. {
  2698. pNode = GetFolderNode(treeitem);
  2699. Assert(pNode != NULL);
  2700. if (canconn)
  2701. pNode->dwFlags &= ~FIDF_DISCONNECTED;
  2702. else
  2703. pNode->dwFlags |= FIDF_DISCONNECTED;
  2704. TreeView_GetItemRect(m_hwndTree, treeitem, &rect, TRUE);
  2705. InvalidateRect(m_hwndTree, &rect, FALSE);
  2706. hitem = TreeView_GetChild(m_hwndTree, treeitem);
  2707. if (hitem != NULL)
  2708. UpdateChildren(hitem, canconn, TRUE);
  2709. if (!fSiblings)
  2710. break;
  2711. treeitem = TreeView_GetNextSibling(m_hwndTree, treeitem);
  2712. }
  2713. }
  2714. LPFOLDERNODE CTreeView::GetFolderNode(HTREEITEM hItem)
  2715. {
  2716. TVITEM item;
  2717. BOOL retval;
  2718. if (!hItem)
  2719. return NULL;
  2720. item.hItem = hItem;
  2721. item.mask = TVIF_PARAM;
  2722. retval = TreeView_GetItem (m_hwndTree, &item);
  2723. return (retval ? (LPFOLDERNODE)item.lParam : NULL);
  2724. }
  2725. HRESULT CTreeView::AdviseAccount(DWORD dwAdviseType, ACTX *pactx)
  2726. {
  2727. //We are only interested in this type
  2728. if (dwAdviseType == AN_ACCOUNT_CHANGED)
  2729. {
  2730. UpdateLabelColors();
  2731. }
  2732. return S_OK;
  2733. }
  2734. //
  2735. //
  2736. // CTreeViewFrame
  2737. //
  2738. //
  2739. CTreeViewFrame::CTreeViewFrame()
  2740. {
  2741. m_cRef = 1;
  2742. m_hwnd = NULL;
  2743. m_ptv = NULL;
  2744. }
  2745. CTreeViewFrame::~CTreeViewFrame()
  2746. {
  2747. if (m_ptv != NULL)
  2748. m_ptv->Release();
  2749. }
  2750. HRESULT CTreeViewFrame::Initialize(HWND hwnd, RECT *prc, DWORD dwFlags)
  2751. {
  2752. HRESULT hr;
  2753. Assert(hwnd != 0);
  2754. Assert(prc != NULL);
  2755. m_hwnd = hwnd;
  2756. m_rect = *prc;
  2757. m_ptv = new CTreeView(this);
  2758. if (m_ptv == NULL)
  2759. return(hrMemory);
  2760. hr = m_ptv->HrInit(dwFlags, NULL);
  2761. if (!FAILED(hr))
  2762. {
  2763. // m_ptv->SetSite((IInputObjectSite*)this);
  2764. // m_ptv->UIActivateIO(TRUE, NULL);
  2765. //We also set the tree view window position here since the actual treeview is re-sized by the rebar
  2766. HWND hChild;
  2767. TCHAR Classname[30];
  2768. int Count;
  2769. CTreeView *ptv;
  2770. hChild = m_ptv->Create(m_hwnd, NULL, FALSE);
  2771. /*
  2772. while (hChild)
  2773. {
  2774. if ((ptv = (CTreeView*)GetWindowLong(hChild, GWL_USERDATA)) == m_ptv)
  2775. {
  2776. */
  2777. SetWindowPos(hChild, NULL, m_rect.left, m_rect.top,
  2778. m_rect.right - m_rect.left,
  2779. m_rect.bottom - m_rect.top,
  2780. SWP_NOZORDER | SWP_SHOWWINDOW);
  2781. return (hr);
  2782. /*
  2783. }
  2784. hChild = ::GetNextWindow(hChild, GW_HWNDNEXT);
  2785. } //while(hChild)
  2786. hr = E_FAIL;
  2787. */
  2788. } //if (!FAILED(hr))
  2789. return(hr);
  2790. }
  2791. void CTreeViewFrame::CloseTreeView()
  2792. {
  2793. if (m_ptv != NULL)
  2794. {
  2795. m_ptv->UIActivateIO(FALSE, NULL);
  2796. m_ptv->SetSite(NULL);
  2797. }
  2798. }
  2799. HRESULT STDMETHODCALLTYPE CTreeViewFrame::QueryInterface(REFIID riid, void **ppvObj)
  2800. {
  2801. if ((IsEqualIID(riid, IID_IInputObjectSite)) || (IsEqualIID(riid, IID_IUnknown)))
  2802. {
  2803. *ppvObj = (void*) (IInputObjectSite *) this;
  2804. }
  2805. else if (IsEqualIID(riid, IID_IOleWindow))
  2806. {
  2807. *ppvObj = (void*) (IOleWindow *)this;
  2808. }
  2809. else
  2810. {
  2811. *ppvObj = NULL;
  2812. return E_NOINTERFACE;
  2813. }
  2814. AddRef();
  2815. return NOERROR;
  2816. }
  2817. ULONG STDMETHODCALLTYPE CTreeViewFrame::AddRef()
  2818. {
  2819. return ++m_cRef;
  2820. }
  2821. ULONG STDMETHODCALLTYPE CTreeViewFrame::Release()
  2822. {
  2823. if (--m_cRef == 0)
  2824. {
  2825. delete this;
  2826. return 0;
  2827. }
  2828. return m_cRef;
  2829. }
  2830. HRESULT STDMETHODCALLTYPE CTreeViewFrame::GetWindow(HWND * lphwnd)
  2831. {
  2832. *lphwnd = m_hwnd;
  2833. return (m_hwnd ? S_OK : E_FAIL);
  2834. }
  2835. HRESULT STDMETHODCALLTYPE CTreeViewFrame::ContextSensitiveHelp(BOOL fEnterMode)
  2836. {
  2837. return E_NOTIMPL;
  2838. }
  2839. HRESULT CTreeViewFrame::OnFocusChangeIS(IUnknown *punk, BOOL fSetFocus)
  2840. {
  2841. return S_OK;
  2842. }
  2843. void CTreeViewFrame::OnSelChange(FOLDERID idFolder)
  2844. {
  2845. SendMessage(m_hwnd, TVM_SELCHANGED, 0, (LPARAM)idFolder);
  2846. return;
  2847. }
  2848. void CTreeViewFrame::OnRename(FOLDERID idFolder)
  2849. {
  2850. return;
  2851. }
  2852. void CTreeViewFrame::OnDoubleClick(FOLDERID idFolder)
  2853. {
  2854. SendMessage(m_hwnd, TVM_DBLCLICK, 0, (LPARAM)idFolder);
  2855. return;
  2856. }
  2857. HRESULT CTreeView::SaveExpandState(HTREEITEM hitem)
  2858. {
  2859. HRESULT hr;
  2860. TV_ITEM item;
  2861. HTREEITEM hitemT;
  2862. LPFOLDERNODE pNode;
  2863. while (hitem != NULL)
  2864. {
  2865. hitemT = TreeView_GetChild(m_hwndTree, hitem);
  2866. if (hitemT != NULL)
  2867. {
  2868. item.hItem = hitem;
  2869. item.mask = TVIF_STATE;
  2870. item.stateMask = TVIS_EXPANDED;
  2871. if (TreeView_GetItem(m_hwndTree, &item))
  2872. {
  2873. pNode = GetFolderNode(hitem);
  2874. if (pNode)
  2875. {
  2876. if (!!(pNode->Folder.dwFlags & FOLDER_EXPANDTREE) ^ !!(item.state & TVIS_EXPANDED))
  2877. {
  2878. pNode->Folder.dwFlags ^= FOLDER_EXPANDTREE;
  2879. g_pStore->UpdateRecord(&pNode->Folder);
  2880. }
  2881. }
  2882. if (!!(item.state & TVIS_EXPANDED))
  2883. {
  2884. // we only care about saving the expanded state for children
  2885. // of expanded nodes
  2886. hr = SaveExpandState(hitemT);
  2887. if (FAILED(hr))
  2888. return(hr);
  2889. }
  2890. }
  2891. }
  2892. hitem = TreeView_GetNextSibling(m_hwndTree, hitem);
  2893. }
  2894. return(S_OK);
  2895. }
  2896. void CTreeView::ExpandToVisible(HWND hwnd, HTREEITEM hti)
  2897. {
  2898. HTREEITEM htiParent;
  2899. htiParent = TreeView_GetParent(hwnd, hti);
  2900. TV_ITEM tvi;
  2901. tvi.mask = TVIF_STATE;
  2902. tvi.hItem = htiParent;
  2903. TreeView_GetItem(hwnd, &tvi);
  2904. if (0 == (tvi.state & TVIS_EXPANDED))
  2905. {
  2906. TreeView_EnsureVisible(hwnd, hti);
  2907. }
  2908. }
  2909. BOOL CTreeView::IsDefaultAccount(FOLDERINFO *pInfo)
  2910. {
  2911. IImnAccount *pAccount = NULL;
  2912. ACCTTYPE type = ACCT_MAIL;
  2913. TCHAR szDefault[CCHMAX_ACCOUNT_NAME];
  2914. BOOL fReturn = FALSE;
  2915. // Figure out what account type to ask for
  2916. if (pInfo->tyFolder == FOLDER_NEWS)
  2917. type = ACCT_NEWS;
  2918. // Ask the account manager to give us the account
  2919. if (SUCCEEDED(g_pAcctMan->GetDefaultAccountName(type, szDefault, ARRAYSIZE(szDefault))))
  2920. {
  2921. if (0 == lstrcmpi(szDefault, pInfo->pszName))
  2922. {
  2923. fReturn = TRUE;
  2924. }
  2925. }
  2926. return (fReturn);
  2927. }