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.

1132 lines
35 KiB

  1. /////////////////////////////////////////////////////////////////////////////
  2. // Copyright (C) 1993-1996 Microsoft Corporation. All Rights Reserved.
  3. //
  4. // MODULE: GrpDlg.cpp
  5. //
  6. // PURPOSE: Implements the CGroupListDlg class.
  7. //
  8. #include "pch.hxx"
  9. #include <iert.h>
  10. #include <instance.h>
  11. #include "subscr.h"
  12. #include "resource.h"
  13. #include "strconst.h"
  14. #include "thormsgs.h"
  15. #include "goptions.h"
  16. #include "mailnews.h"
  17. #include "shlwapip.h"
  18. #include "imnact.h"
  19. #include "error.h"
  20. #include "acctutil.h"
  21. #include <ras.h>
  22. #include "imagelst.h"
  23. #include "conman.h"
  24. #include "xpcomm.h"
  25. #include <storutil.h>
  26. #include "demand.h"
  27. #include "menures.h"
  28. #include "storecb.h"
  29. UINT g_rgCtlMap[] = { idcFindText, idcUseDesc, idcGroupList, idcSubscribe,
  30. idcUnsubscribe, idcResetList, idcGoto, IDOK, IDCANCEL, idcServers, idcStaticNewsServers,
  31. idcStaticHorzLine, idcTabs};
  32. const static HELPMAP g_rgCtxMapGrpDlg[] = {
  33. {idcFindText, IDH_NEWS_SEARCH_GROUPS_CONTAINING},
  34. {idcDispText, IDH_NEWS_SEARCH_GROUPS_CONTAINING},
  35. {idcUseDesc, IDH_NEWS_SEARCH_GROUPS_DESC},
  36. {idcGroupList, IDH_NEWS_GROUP_LISTS},
  37. {idcSubscribe, IDH_NEWS_ADD_SELECTED_GROUP},
  38. {idcUnsubscribe, IDH_NEWS_REMOVE_SELECTED_GROUP},
  39. {idcResetList, IDH_NEWS_RESET_NEW_LIST},
  40. {idcGoto, IDH_NEWS_OPEN_SELECTED_GROUP},
  41. {idcServers, IDH_NEWS_SERVER_LIST},
  42. {idcStaticNewsServers, IDH_NEWS_SERVER_LIST},
  43. {idcTabs, IDH_NEWSGROUP_LIST_ALL},
  44. {0, 0}
  45. };
  46. const static HELPMAP g_rgCtxMapGrpDlgIMAP[] = {
  47. {idcFindText, 50505},
  48. {idcDispText, 50505},
  49. {idcGroupList, 50510},
  50. {idcSubscribe, 50520},
  51. {idcUnsubscribe, 50525},
  52. {idcResetList, 50530},
  53. {idcGoto, 50515},
  54. {idcServers, 50500},
  55. {idcStaticNewsServers, 50500},
  56. {0, 0}
  57. };
  58. HRESULT DoSubscriptionDialog(HWND hwnd, BOOL fNews, FOLDERID idFolder, BOOL fShowNew)
  59. {
  60. CGroupListDlg *pDlg;
  61. FOLDERID id, idDefault;
  62. FOLDERINFO info;
  63. HRESULT hr;
  64. FOLDERTYPE type;
  65. #ifdef DEBUG
  66. if (fShowNew)
  67. Assert(fNews);
  68. #endif // DEBUG
  69. type = fNews ? FOLDER_NEWS : FOLDER_IMAP;
  70. pDlg = new CGroupListDlg;
  71. if (pDlg == NULL)
  72. return(E_OUTOFMEMORY);
  73. if (idFolder == FOLDERID_ROOT)
  74. {
  75. idFolder = FOLDERID_INVALID;
  76. }
  77. else if (idFolder != FOLDERID_INVALID)
  78. {
  79. hr = GetFolderStoreInfo(idFolder, &info);
  80. if (SUCCEEDED(hr))
  81. {
  82. if (type == info.tyFolder)
  83. idFolder = info.idFolder;
  84. else
  85. idFolder = FOLDERID_INVALID;
  86. g_pStore->FreeRecord(&info);
  87. }
  88. else
  89. {
  90. idFolder = FOLDERID_INVALID;
  91. }
  92. }
  93. if (idFolder == FOLDERID_INVALID)
  94. {
  95. hr = GetDefaultServerId(fNews ? ACCT_NEWS : ACCT_MAIL, &idDefault);
  96. if (SUCCEEDED(hr))
  97. {
  98. hr = g_pStore->GetFolderInfo(idDefault, &info);
  99. if (SUCCEEDED(hr))
  100. {
  101. if (type == info.tyFolder)
  102. idFolder = idDefault;
  103. g_pStore->FreeRecord(&info);
  104. }
  105. }
  106. if (FAILED(hr))
  107. {
  108. pDlg->Release();
  109. return(hr);
  110. }
  111. }
  112. if (pDlg->FCreate(hwnd, type, &id, fShowNew ? 2 : 0, TRUE, idFolder))
  113. {
  114. if (id != FOLDERID_INVALID)
  115. g_pInstance->BrowseToObject(SW_SHOWNORMAL, id);
  116. }
  117. pDlg->Release();
  118. return(S_OK);
  119. }
  120. CGroupListDlg::CGroupListDlg()
  121. {
  122. m_cRef = 1;
  123. // m_hwnd
  124. // m_hwndFindText
  125. // m_hwndOwner
  126. m_fAllowDesc = TRUE;
  127. m_pszPrevQuery = 0;
  128. m_cchPrevQuery = 0;
  129. m_cxHorzSep = 0;
  130. m_cyVertSep = 0;
  131. m_rgst = 0;
  132. m_sizeDlg.cx = 0;
  133. m_sizeDlg.cy = 0;
  134. m_ptDragMin.x = 0;
  135. m_ptDragMin.y = 0;
  136. m_himlServer = NULL;
  137. m_pGrpList = NULL;
  138. // m_type
  139. // m_iTabSelect
  140. // m_idSel
  141. m_idGoto = FOLDERID_INVALID;
  142. // m_fEnableGoto
  143. m_fServerListInited = FALSE;
  144. m_idCurrent = FOLDERID_INVALID;
  145. m_hIcon = NULL;
  146. m_pColumns = NULL;
  147. }
  148. CGroupListDlg::~CGroupListDlg()
  149. {
  150. SafeMemFree(m_pszPrevQuery);
  151. SafeMemFree(m_rgst);
  152. if (m_pColumns != NULL)
  153. m_pColumns->Release();
  154. if (m_pGrpList != NULL)
  155. m_pGrpList->Release();
  156. if (m_hIcon)
  157. SideAssert(DestroyIcon(m_hIcon));
  158. if (m_himlServer != NULL)
  159. ImageList_Destroy(m_himlServer);
  160. }
  161. HRESULT STDMETHODCALLTYPE CGroupListDlg::QueryInterface(REFIID riid, void **ppvObj)
  162. {
  163. if (IsEqualIID(riid, IID_IUnknown))
  164. *ppvObj = (void*) (IUnknown *)(IGroupListAdvise *)this;
  165. else if (IsEqualIID(riid, IID_IGroupListAdvise))
  166. *ppvObj = (void*) (IGroupListAdvise *) this;
  167. else
  168. {
  169. *ppvObj = NULL;
  170. return E_NOINTERFACE;
  171. }
  172. AddRef();
  173. return S_OK;
  174. }
  175. ULONG STDMETHODCALLTYPE CGroupListDlg::AddRef()
  176. {
  177. return ++m_cRef;
  178. }
  179. ULONG STDMETHODCALLTYPE CGroupListDlg::Release()
  180. {
  181. if (--m_cRef == 0)
  182. {
  183. delete this;
  184. return 0;
  185. }
  186. return m_cRef;
  187. }
  188. //
  189. // FUNCTION: CGroupListDlg::FCreate()
  190. //
  191. // PURPOSE: Handles initialization of the data and creation of the
  192. // Newsgroups dialog.
  193. //
  194. BOOL CGroupListDlg::FCreate(HWND hwndOwner, FOLDERTYPE type, FOLDERID *pGotoId,
  195. UINT iTabSelect, BOOL fEnableGoto, FOLDERID idSel)
  196. {
  197. MSG msg;
  198. HWND hwndDlg;
  199. UINT idd;
  200. HRESULT hr;
  201. Assert(pGotoId != NULL);
  202. m_hwndOwner = hwndOwner;
  203. Assert(type == FOLDER_IMAP || type == FOLDER_NEWS);
  204. m_type = type;
  205. m_iTabSelect = iTabSelect;
  206. m_fEnableGoto = fEnableGoto;
  207. m_idSel = idSel;
  208. m_pGrpList = new CGroupList;
  209. if (m_pGrpList == NULL)
  210. return(FALSE);
  211. idd = type == FOLDER_NEWS ? iddSubscribe : iddSubscribeImap;
  212. if (GetParent(m_hwndOwner))
  213. {
  214. while (GetParent(m_hwndOwner))
  215. m_hwndOwner = GetParent(m_hwndOwner);
  216. }
  217. // Fake this modeless dialog into behaving like a modal dialog
  218. EnableWindow(m_hwndOwner, FALSE);
  219. hwndDlg = CreateDialogParam(g_hLocRes, MAKEINTRESOURCE(idd), hwndOwner,
  220. GroupListDlgProc, (LPARAM) this);
  221. ShowWindow(hwndDlg, SW_SHOW);
  222. while (GetMessage(&msg, NULL, 0, 0))
  223. {
  224. if (IsGrpDialogMessage(msg.hwnd, &msg))
  225. continue;
  226. TranslateMessage(&msg);
  227. DispatchMessage(&msg);
  228. }
  229. if (IsWindow(m_hwnd))
  230. {
  231. // GetMessage returned FALSE (WM_QUIT), but we still exist. This
  232. // means someone else posted the WM_QUIT, so we should close and
  233. // put the WM_QUIT back in the queue
  234. SendMessage(m_hwnd, WM_COMMAND, IDCANCEL, 0L);
  235. PostQuitMessage((int)(msg.wParam)); // repost quit for next enclosing loop
  236. }
  237. EnableWindow(m_hwndOwner, TRUE);
  238. *pGotoId = m_idGoto;
  239. return(TRUE);
  240. }
  241. //
  242. // FUNCTION: CGroupListDlg::IsGrpDialogMessage()
  243. //
  244. // PURPOSE: Because there are people who think that because we have a tab
  245. // control on this dialog it should behave like a property sheet,
  246. // we need to fake the dialog modeless and filter our own
  247. // keystrokes. I've stolen this function from the comctl32 sources
  248. // so if we get any bugs saying the behavior isn't the same the
  249. // person is full of it.
  250. //
  251. // PARAMETERS:
  252. // hwnd - Handle of the window to check the messages for.
  253. // pMsg - Message to check.
  254. //
  255. // RETURN VALUE:
  256. // Returns TRUE if the message was dispatched, FALSE otherwise.
  257. //
  258. BOOL CGroupListDlg::IsGrpDialogMessage(HWND hwnd, LPMSG pMsg)
  259. {
  260. if ((pMsg->message == WM_KEYDOWN) && (GetAsyncKeyState(VK_CONTROL) < 0))
  261. {
  262. BOOL bBack = FALSE;
  263. switch (pMsg->wParam)
  264. {
  265. case VK_TAB:
  266. bBack = GetAsyncKeyState(VK_SHIFT) < 0;
  267. break;
  268. case VK_PRIOR: // VK_PAGE_UP
  269. case VK_NEXT: // VK_PAGE_DOWN
  270. bBack = (pMsg->wParam == VK_PRIOR);
  271. break;
  272. default:
  273. goto NoKeys;
  274. }
  275. int iCur = TabCtrl_GetCurSel(GetDlgItem(m_hwnd, idcTabs));
  276. // tab in reverse if shift is down
  277. if (bBack)
  278. iCur += (iTabMax - 1);
  279. else
  280. iCur++;
  281. iCur %= iTabMax;
  282. TabCtrl_SetCurSel(GetDlgItem(m_hwnd, idcTabs), iCur);
  283. OnSwitchTabs(hwnd, iCur);
  284. return TRUE;
  285. }
  286. NoKeys:
  287. if (IsWindow(m_hwnd) && IsDialogMessage(m_hwnd, pMsg))
  288. return TRUE;
  289. return (FALSE);
  290. }
  291. INT_PTR CALLBACK CGroupListDlg::GroupListDlgProc(HWND hwnd, UINT uMsg,
  292. WPARAM wParam, LPARAM lParam)
  293. {
  294. CGroupListDlg* pThis = 0;
  295. pThis = (CGroupListDlg*) GetWindowLongPtr(hwnd, DWLP_USER);
  296. LRESULT lResult;
  297. // Bug #16910 - For some reason we get messages before we set the this
  298. // pointer into the window extra bytes. If this happens
  299. // we should blow the message off.
  300. if (uMsg != WM_INITDIALOG && 0 == pThis)
  301. {
  302. return (FALSE);
  303. }
  304. switch (uMsg)
  305. {
  306. case WM_INITDIALOG:
  307. // Stash the this pointer so we can use it for all the messages.
  308. SetWindowLongPtr(hwnd, DWLP_USER, lParam);
  309. pThis = (CGroupListDlg*) lParam;
  310. return (BOOL)HANDLE_WM_INITDIALOG(hwnd, wParam, lParam,
  311. pThis->OnInitDialog);
  312. case WM_HELP:
  313. case WM_CONTEXTMENU:
  314. return OnContextHelp(hwnd, uMsg, wParam, lParam, (pThis->m_type == FOLDER_IMAP) ? g_rgCtxMapGrpDlgIMAP : g_rgCtxMapGrpDlg);
  315. case WM_COMMAND:
  316. Assert(pThis);
  317. HANDLE_WM_COMMAND(hwnd, wParam, lParam, pThis->OnCommand);
  318. return (TRUE);
  319. case WM_NOTIFY:
  320. Assert(pThis);
  321. lResult = HANDLE_WM_NOTIFY(hwnd, wParam, lParam, pThis->OnNotify);
  322. SetDlgMsgResult(hwnd, WM_NOTIFY, lResult);
  323. return (TRUE);
  324. case WM_TIMER:
  325. Assert(pThis);
  326. HANDLE_WM_TIMER(hwnd, wParam, lParam, pThis->OnTimer);
  327. return (TRUE);
  328. case WM_PAINT:
  329. Assert(pThis);
  330. HANDLE_WM_PAINT(hwnd, wParam, lParam, pThis->OnPaint);
  331. return (FALSE);
  332. case WM_SIZE:
  333. Assert(pThis);
  334. HANDLE_WM_SIZE(hwnd, wParam, lParam, pThis->OnSize);
  335. return (TRUE);
  336. case WM_GETMINMAXINFO:
  337. Assert(pThis);
  338. HANDLE_WM_GETMINMAXINFO(hwnd, wParam, lParam, pThis->OnGetMinMaxInfo);
  339. break;
  340. case WM_CLOSE:
  341. Assert(pThis);
  342. HANDLE_WM_CLOSE(hwnd, wParam, lParam, pThis->OnClose);
  343. return (TRUE);
  344. case WM_DESTROY:
  345. Assert(pThis);
  346. HANDLE_WM_DESTROY(hwnd, wParam, lParam, pThis->OnDestroy);
  347. return (TRUE);
  348. case WM_NCHITTEST:
  349. {
  350. POINT pt = { LOWORD(lParam), HIWORD(lParam) };
  351. RECT rc;
  352. GetWindowRect(hwnd, &rc);
  353. rc.left = rc.right - GetSystemMetrics(SM_CXSMICON);
  354. rc.top = rc.bottom - GetSystemMetrics(SM_CYSMICON);
  355. if (PtInRect(&rc, pt))
  356. {
  357. SetDlgMsgResult(hwnd, WM_NCHITTEST, HTBOTTOMRIGHT);
  358. return (TRUE);
  359. }
  360. else
  361. return (FALSE);
  362. }
  363. case NVM_CHANGESERVERS:
  364. pThis->OnChangeServers(hwnd);
  365. return (TRUE);
  366. }
  367. return (FALSE);
  368. }
  369. static const UINT c_rgNewsSubTab[] =
  370. {
  371. idsTabAll, idsTabSubscribed, idsTabNew
  372. };
  373. static const UINT c_rgImapSubTab[] =
  374. {
  375. idsTabAll, idsTabVisible
  376. };
  377. BOOL CGroupListDlg::OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)
  378. {
  379. HRESULT hr;
  380. HWND hwndT, hwndList;
  381. WINDOWPLACEMENT wp;
  382. TC_ITEM tci;
  383. RECT rcDlg;
  384. UINT i, cTab, *pTab;
  385. char sz[CCHMAX_STRINGRES];
  386. COLUMN_SET_TYPE set;
  387. // It's handy to have these handles available
  388. m_hwnd = hwnd;
  389. m_hwndFindText = GetDlgItem(hwnd, idcFindText);
  390. SetIntlFont(m_hwndFindText);
  391. // Add some tabs to our tab control.
  392. hwndT = GetDlgItem(hwnd, idcTabs);
  393. Assert(IsWindow(hwndT));
  394. if (m_type == FOLDER_NEWS)
  395. {
  396. set = COLUMN_SET_NEWS_SUB;
  397. pTab = (UINT *)c_rgNewsSubTab;
  398. cTab = ARRAYSIZE(c_rgNewsSubTab);
  399. }
  400. else
  401. {
  402. set = COLUMN_SET_IMAP_SUB;
  403. pTab = (UINT *)c_rgImapSubTab;
  404. cTab = ARRAYSIZE(c_rgImapSubTab);
  405. }
  406. tci.mask = TCIF_TEXT;
  407. tci.pszText = sz;
  408. for (i = 0; i < cTab; i++)
  409. {
  410. AthLoadString(*pTab, sz, ARRAYSIZE(sz));
  411. TabCtrl_InsertItem(hwndT, i, &tci);
  412. pTab++;
  413. }
  414. hwndList = GetDlgItem(hwnd, idcGroupList);
  415. m_pColumns = new CColumns;
  416. if (m_pColumns == NULL)
  417. {
  418. EnableWindow(m_hwndOwner, TRUE);
  419. DestroyWindow(m_hwnd);
  420. return (FALSE);
  421. }
  422. m_pColumns->Initialize(hwndList, set);
  423. m_pColumns->ApplyColumns(COLUMN_LOAD_REGISTRY, 0, 0);
  424. // Initialize the extended styles so we get full row select. Just because
  425. // it looks better.
  426. ListView_SetExtendedListViewStyle(hwndList, LVS_EX_FULLROWSELECT);
  427. Assert(m_pGrpList != NULL);
  428. hr = m_pGrpList->Initialize((IGroupListAdvise *)this, m_pColumns, hwndList, m_type);
  429. Assert(SUCCEEDED(hr));
  430. // Add the list of servers to the list view if and only if there is more
  431. // than one server.
  432. hwndT = GetDlgItem(hwnd, idcServers);
  433. FillServerList(hwndT, m_idSel);
  434. // Build the control map array.
  435. if (!MemAlloc((LPVOID*) &m_rgst, sizeof(SIZETABLE) * iCtlMax))
  436. {
  437. EnableWindow(m_hwndOwner, TRUE);
  438. DestroyWindow(m_hwnd);
  439. return (FALSE);
  440. }
  441. ZeroMemory(m_rgst, sizeof(SIZETABLE) * iCtlMax);
  442. // Build a table of the controls on this dialog
  443. for (i = 0; i < iCtlMax; i++)
  444. {
  445. m_rgst[i].hwndCtl = GetDlgItem(hwnd, g_rgCtlMap[i]);
  446. if (m_rgst[i].hwndCtl)
  447. {
  448. m_rgst[i].id = g_rgCtlMap[i];
  449. GetWindowRect(m_rgst[i].hwndCtl, &m_rgst[i].rc);
  450. MapWindowPoints(GetDesktopWindow(), hwnd, (LPPOINT) &m_rgst[i].rc, 2);
  451. }
  452. }
  453. GetWindowRect(hwnd, &rcDlg);
  454. m_ptDragMin.x = rcDlg.right - rcDlg.left;
  455. m_ptDragMin.y = rcDlg.bottom - rcDlg.top;
  456. // Get the distance from the button to the edge of the dialog
  457. GetClientRect(hwnd, &rcDlg);
  458. m_cxHorzSep = rcDlg.right - m_rgst[iCtlCancel].rc.right;
  459. // Get the distance from the "OK" to the "Cancel" button
  460. m_cyVertSep = m_rgst[iCtlCancel].rc.left - m_rgst[iCtlOK].rc.right;
  461. // Position the dialog
  462. wp.length = sizeof(WINDOWPLACEMENT);
  463. if (GetOption(OPT_NEWSDLGPOS, (LPVOID) &wp, sizeof(WINDOWPLACEMENT)))
  464. {
  465. SetWindowPlacement(hwnd, &wp);
  466. // Bug #19258 - If SetWindowPlacement() doesn't actually resize the dialog,
  467. // then a WM_SIZE doesn't happen automatically. We check for
  468. // this now and force the message if this is the case.
  469. GetWindowRect(hwnd, &rcDlg);
  470. if (wp.rcNormalPosition.right == rcDlg.right &&
  471. wp.rcNormalPosition.bottom == rcDlg.bottom)
  472. {
  473. GetClientRect(hwnd, &rcDlg);
  474. SendMessage(hwnd, WM_SIZE, SIZE_RESTORED, MAKELPARAM(rcDlg.right, rcDlg.bottom));
  475. }
  476. }
  477. else
  478. {
  479. CenterDialog(hwnd);
  480. SendMessage(hwnd, WM_SIZE, SIZE_RESTORED, MAKELPARAM(rcDlg.right, rcDlg.bottom));
  481. }
  482. // If the view didn't invoke this dialog, than Goto has no meaning. Hide
  483. // the button if this is the case.
  484. if (!m_fEnableGoto)
  485. ShowWindow(GetDlgItem(hwnd, idcGoto), SW_HIDE);
  486. // Bug 23685: give it the right icon
  487. m_hIcon= (HICON)LoadImage(g_hLocRes, m_type == FOLDER_NEWS ? MAKEINTRESOURCE(idiNewsGroup) : MAKEINTRESOURCE(idiFolder), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
  488. SendMessage(hwnd, WM_SETICON, FALSE, (LPARAM)m_hIcon);
  489. // Initialize the focus
  490. SetFocus(m_hwndFindText);
  491. // Tell ourself to update our group list after the dialog is done
  492. // initializing.
  493. PostMessage(hwnd, NVM_CHANGESERVERS, 0, 0L);
  494. return (FALSE);
  495. }
  496. void CGroupListDlg::OnChangeServers(HWND hwnd)
  497. {
  498. // TODO: we need to fix the initialization so the filtering is only performed
  499. // once (we should call IGroupList::Filter once and then IGroupList::SetServer once
  500. // during creation of the dialog)
  501. UpdateWindow(hwnd);
  502. TabCtrl_SetCurSel(GetDlgItem(hwnd, idcTabs), m_iTabSelect);
  503. OnSwitchTabs(hwnd, m_iTabSelect);
  504. Assert(m_idCurrent != FOLDERID_INVALID);
  505. ChangeServers(m_idCurrent, TRUE);
  506. }
  507. //
  508. // FUNCTION: CGroupListDlg::OnCommand
  509. //
  510. // PURPOSE: Handles the command messages for the dialog.
  511. //
  512. // PARAMETERS:
  513. // hwnd - Handle of the dialog box.
  514. // id - Command id that needs processing.
  515. // hwndCtl - Handle of the control that generated the command
  516. // codeNotify - Specific notification the control generated
  517. //
  518. void CGroupListDlg::OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
  519. {
  520. HRESULT hr;
  521. FOLDERID fldid;
  522. int iSel;
  523. FOLDERINFO info;
  524. DWORD dw;
  525. BOOL fDesc;
  526. IImnAccount *pAcct;
  527. switch (id)
  528. {
  529. case IDOK:
  530. case IDCANCEL:
  531. if (codeNotify != BN_CLICKED)
  532. break;
  533. hr = S_OK;
  534. if (id == IDOK)
  535. {
  536. hr = m_pGrpList->Commit(hwnd);
  537. Assert(hr != E_PENDING);
  538. if (FAILED(hr))
  539. break;
  540. }
  541. EnableWindow(m_hwndOwner, TRUE);
  542. DestroyWindow(hwnd);
  543. break;
  544. case idcSubscribe:
  545. case idcUnsubscribe:
  546. if (codeNotify != BN_CLICKED)
  547. break;
  548. hr = m_pGrpList->Exec(NULL, id == idcSubscribe ? ID_SUBSCRIBE : ID_UNSUBSCRIBE, 0, NULL, NULL);
  549. Assert(hr == S_OK);
  550. break;
  551. case idcGoto:
  552. if (codeNotify != BN_CLICKED)
  553. break;
  554. hr = m_pGrpList->GetFocused(&fldid);
  555. if (SUCCEEDED(hr))
  556. {
  557. m_idGoto = fldid;
  558. SendMessage(m_hwnd, WM_COMMAND, IDOK, 0);
  559. }
  560. break;
  561. case idcFindText:
  562. // This is generated when someone types in the find text edit box.
  563. // We set a timer and when that timer expires we assume the user is
  564. // done typing and go ahead and perform the query.
  565. if (EN_CHANGE == codeNotify)
  566. {
  567. KillTimer(hwnd, idtFindDelay);
  568. SetTimer(hwnd, idtFindDelay, dtFindDelay, NULL);
  569. }
  570. break;
  571. case idcUseDesc:
  572. if (IsDlgButtonChecked(hwnd, idcUseDesc))
  573. {
  574. hr = m_pGrpList->HasDescriptions(&fDesc);
  575. if (SUCCEEDED(hr) && !fDesc)
  576. {
  577. if (IDYES == AthMessageBoxW(hwnd, MAKEINTRESOURCEW(idsAthenaNews),
  578. MAKEINTRESOURCEW(IDS_NO_DESCRIPTIONS_DOWNLOADED), NULL,
  579. MB_YESNO | MB_ICONEXCLAMATION))
  580. {
  581. // turn on acct desc option
  582. if (SUCCEEDED(g_pStore->GetFolderInfo(m_idCurrent, &info)))
  583. {
  584. if (SUCCEEDED(g_pAcctMan->FindAccount(AP_ACCOUNT_ID, info.pszAccountId, &pAcct)))
  585. {
  586. if (SUCCEEDED(pAcct->GetPropDw(AP_NNTP_USE_DESCRIPTIONS, &dw)) && dw == 0)
  587. {
  588. pAcct->SetPropDw(AP_NNTP_USE_DESCRIPTIONS, 1);
  589. pAcct->SaveChanges();
  590. }
  591. pAcct->Release();
  592. }
  593. g_pStore->FreeRecord(&info);
  594. hr = m_pGrpList->Exec(NULL, ID_RESET_LIST, 0, NULL, NULL);
  595. }
  596. }
  597. else
  598. {
  599. SendMessage(hwndCtl, BM_SETCHECK, BST_UNCHECKED, 0);
  600. break;
  601. }
  602. }
  603. }
  604. iSel = TabCtrl_GetCurSel(GetDlgItem(hwnd, idcTabs));
  605. if (iSel != -1)
  606. OnSwitchTabs(hwnd, iSel);
  607. break;
  608. case idcResetList:
  609. hr = m_pGrpList->Exec(NULL, ID_RESET_LIST, 0, NULL, NULL);
  610. break;
  611. }
  612. }
  613. //
  614. // FUNCTION: CGroupListDlg::OnNotify
  615. //
  616. // PURPOSE: Handles notifications from the common controls on the dialog.
  617. //
  618. LRESULT CGroupListDlg::OnNotify(HWND hwnd, int idFrom, LPNMHDR pnmhdr)
  619. {
  620. int iSel;
  621. LRESULT lRes;
  622. HRESULT hr;
  623. NM_LISTVIEW *pnmlv;
  624. hr = m_pGrpList->HandleNotify(hwnd, idFrom, pnmhdr, &lRes);
  625. if (hr == S_OK)
  626. return(lRes);
  627. switch (pnmhdr->code)
  628. {
  629. case TCN_SELCHANGE:
  630. if (idFrom == idcTabs)
  631. {
  632. // Find out which tab is currently active
  633. iSel = TabCtrl_GetCurSel(pnmhdr->hwndFrom);
  634. if (iSel != -1)
  635. OnSwitchTabs(hwnd, iSel);
  636. }
  637. break;
  638. case LVN_ITEMCHANGED:
  639. Assert(idFrom == idcServers);
  640. pnmlv = (NM_LISTVIEW *)pnmhdr;
  641. if (m_fServerListInited &&
  642. !!(pnmlv->uChanged & LVIF_STATE) &&
  643. 0 == (pnmlv->uOldState & LVIS_FOCUSED) &&
  644. !!(pnmlv->uNewState & LVIS_FOCUSED))
  645. {
  646. ChangeServers((FOLDERID)pnmlv->lParam, FALSE);
  647. ItemUpdate();
  648. }
  649. break;
  650. }
  651. return (0);
  652. }
  653. void CGroupListDlg::OnClose(HWND hwnd)
  654. {
  655. int iReturn = IDNO;
  656. if (m_pGrpList->Dirty() == S_OK)
  657. iReturn = AthMessageBoxW(hwnd, MAKEINTRESOURCEW(idsAthenaNews),
  658. MAKEINTRESOURCEW(idsDoYouWantToSave), 0,
  659. MB_YESNOCANCEL | MB_ICONEXCLAMATION );
  660. if (iReturn == IDYES)
  661. OnCommand(hwnd, IDOK, 0, 0);
  662. else if (iReturn == IDNO)
  663. OnCommand(hwnd, IDCANCEL, 0, 0);
  664. }
  665. void CGroupListDlg::OnDestroy(HWND hwnd)
  666. {
  667. // Save the dialog position
  668. WINDOWPLACEMENT wp;
  669. wp.length = sizeof(WINDOWPLACEMENT);
  670. if (GetWindowPlacement(hwnd, &wp))
  671. SetOption(OPT_NEWSDLGPOS, (LPVOID) &wp, sizeof(WINDOWPLACEMENT), NULL, 0);
  672. Assert(m_pColumns != NULL);
  673. m_pColumns->Save(NULL, NULL);
  674. PostQuitMessage(0);
  675. }
  676. void CGroupListDlg::OnTimer(HWND hwnd, UINT id)
  677. {
  678. DWORD iTab;
  679. KillTimer(hwnd, id);
  680. iTab = TabCtrl_GetCurSel(GetDlgItem(m_hwnd, idcTabs));
  681. OnSwitchTabs(hwnd, iTab);
  682. }
  683. BOOL CGroupListDlg::ChangeServers(FOLDERID id, BOOL fForce)
  684. {
  685. HRESULT hr;
  686. hr = m_pGrpList->SetServer(id);
  687. Assert(SUCCEEDED(hr));
  688. m_idCurrent = id;
  689. ItemUpdate();
  690. return(TRUE);
  691. }
  692. //
  693. // FUNCTION: CGroupListDlg::FillServerList
  694. //
  695. // PURPOSE: If the user has multiple servers configured, then a variant of
  696. // this dialog appears that has a list of servers available. This
  697. // function populates that list.
  698. //
  699. // PARAMETERS:
  700. // hwndList - Handle of the list view that we add server names to.
  701. // pszSelectServer - Name of the server to select in the list view.
  702. //
  703. // RETURN VALUE:
  704. // Returns TRUE if successful, or FALSE otherwise.
  705. //
  706. BOOL CGroupListDlg::FillServerList(HWND hwndList, FOLDERID idSel)
  707. {
  708. HRESULT hr;
  709. FOLDERID id;
  710. char szServer[CCHMAX_ACCOUNT_NAME];
  711. LV_ITEM lvi;
  712. LV_COLUMN lvc;
  713. RECT rc;
  714. DWORD dw;
  715. int iItem, iSelect;
  716. IImnAccount *pAcct;
  717. IImnEnumAccounts *pEnum;
  718. Assert(!m_fServerListInited);
  719. Assert(hwndList != NULL);
  720. Assert(IsWindow(hwndList));
  721. SetIntlFont(hwndList);
  722. // Create the image list and add it to the listview.
  723. Assert(m_himlServer == NULL);
  724. m_himlServer = ImageList_LoadBitmap(g_hLocRes, MAKEINTRESOURCE(idbFoldersLarge), 32, 0, RGB(255, 0, 255));
  725. if (m_himlServer == NULL)
  726. return(FALSE);
  727. ListView_SetImageList(hwndList, m_himlServer, LVSIL_NORMAL);
  728. // Add a column to the listview.
  729. GetClientRect(hwndList, &rc);
  730. lvc.mask = LVCF_SUBITEM | LVCF_WIDTH;
  731. lvc.iSubItem = 0;
  732. lvc.cx = rc.right;
  733. dw = ListView_InsertColumn(hwndList, 0, &lvc);
  734. iSelect = -1;
  735. iItem = 0;
  736. if (SUCCEEDED(g_pAcctMan->Enumerate(m_type == FOLDER_NEWS ? SRV_NNTP : SRV_IMAP, &pEnum)))
  737. {
  738. ZeroMemory(&lvi, sizeof(LV_ITEM));
  739. lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE;
  740. lvi.iImage = m_type == FOLDER_NEWS ? iNewsServer : iMailServer;
  741. while (E_EnumFinished != pEnum->GetNext(&pAcct))
  742. {
  743. if (SUCCEEDED(pAcct->GetPropSz(AP_ACCOUNT_ID, szServer, ARRAYSIZE(szServer))) &&
  744. SUCCEEDED(g_pStore->FindServerId(szServer, &id)) &&
  745. SUCCEEDED(pAcct->GetPropSz(AP_ACCOUNT_NAME, szServer, ARRAYSIZE(szServer))))
  746. {
  747. if (idSel == FOLDERID_INVALID ||
  748. id == idSel)
  749. {
  750. Assert(iSelect == -1);
  751. idSel = id;
  752. iSelect = iItem;
  753. lvi.state = LVIS_SELECTED | LVIS_FOCUSED;
  754. lvi.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
  755. }
  756. else
  757. {
  758. lvi.state = 0;
  759. lvi.stateMask = 0;
  760. }
  761. lvi.pszText = szServer;
  762. lvi.iItem = iItem;
  763. lvi.lParam = (LPARAM)id;
  764. dw = ListView_InsertItem(hwndList, &lvi);
  765. iItem++;
  766. }
  767. pAcct->Release();
  768. }
  769. pEnum->Release();
  770. }
  771. Assert(iItem > 0);
  772. Assert(iSelect != -1);
  773. Assert(idSel != FOLDERID_INVALID);
  774. ListView_EnsureVisible(hwndList, iSelect, FALSE);
  775. m_idCurrent = idSel;
  776. m_fServerListInited = TRUE;
  777. return (TRUE);
  778. }
  779. HRESULT CGroupListDlg::ItemUpdate(void)
  780. {
  781. FOLDERID id;
  782. HRESULT hr;
  783. HWND hwndSub, hwndUnsub, hwndFocus;
  784. OLECMD rgCmds[2] = { { ID_SUBSCRIBE, 0 }, { ID_UNSUBSCRIBE, 0 } };
  785. hr = m_pGrpList->GetFocused(&id);
  786. EnableWindow(GetDlgItem(m_hwnd, idcGoto), hr == S_OK);
  787. hr = m_pGrpList->QueryStatus(NULL, ARRAYSIZE(rgCmds), rgCmds, NULL);
  788. if (SUCCEEDED(hr))
  789. {
  790. hwndFocus = GetFocus();
  791. hwndSub = GetDlgItem(m_hwnd, idcSubscribe);
  792. hwndUnsub = GetDlgItem(m_hwnd, idcUnsubscribe);
  793. EnableWindow(hwndSub, !!(rgCmds[0].cmdf & OLECMDF_ENABLED));
  794. EnableWindow(hwndUnsub, !!(rgCmds[1].cmdf & OLECMDF_ENABLED));
  795. if (!IsWindowEnabled(hwndFocus))
  796. SetFocus(!!(rgCmds[0].cmdf & OLECMDF_ENABLED) ? hwndSub : hwndUnsub);
  797. }
  798. return(S_OK);
  799. }
  800. HRESULT CGroupListDlg::ItemActivate(FOLDERID id)
  801. {
  802. m_pGrpList->Exec(NULL, ID_TOGGLE_SUBSCRIBE, 0, NULL, NULL);
  803. return(S_OK);
  804. }
  805. //
  806. // FUNCTION: CGroupListDlg::OnSwitchTabs()
  807. //
  808. // PURPOSE: This function takes care of resetting the list of groups
  809. // appropriately when the user selects a different tab.
  810. //
  811. // PARAMETERS:
  812. // hwnd - Handle of the dialog window.
  813. // iTab - Index of the tab to switch to.
  814. //
  815. // RETURN VALUE:
  816. // Returns TRUE if successful, or FALSE otherwise.
  817. //
  818. BOOL CGroupListDlg::OnSwitchTabs(HWND hwnd, UINT iTab)
  819. {
  820. UINT cch;
  821. LPSTR pszText;
  822. HRESULT hr;
  823. BOOL fUseDesc;
  824. pszText = NULL;
  825. cch = GetWindowTextLength(m_hwndFindText);
  826. if (cch > 0)
  827. {
  828. cch++;
  829. if (!MemAlloc((void **)&pszText, cch + 1))
  830. return(FALSE);
  831. GetWindowText(m_hwndFindText, pszText, cch);
  832. }
  833. fUseDesc = (m_type == FOLDER_NEWS && IsDlgButtonChecked(m_hwnd, idcUseDesc));
  834. hr = m_pGrpList->Filter(pszText, iTab, fUseDesc);
  835. Assert(SUCCEEDED(hr));
  836. if (pszText != NULL)
  837. MemFree(pszText);
  838. return(TRUE);
  839. }
  840. #define WIDTH(_rect) (_rect.right - _rect.left)
  841. #define HEIGHT(_rect) (_rect.bottom - _rect.top)
  842. void CGroupListDlg::OnSize(HWND hwnd, UINT state, int cx, int cy)
  843. {
  844. RECT rc;
  845. rc.left = m_sizeDlg.cx - GetSystemMetrics(SM_CXSMICON);
  846. rc.top = m_sizeDlg.cy - GetSystemMetrics(SM_CYSMICON);
  847. rc.right = m_sizeDlg.cx;
  848. rc.bottom = m_sizeDlg.cy;
  849. InvalidateRect(hwnd, &rc, FALSE);
  850. m_sizeDlg.cx = cx;
  851. m_sizeDlg.cy = cy;
  852. // First move the outside buttons so they are against the edge. These
  853. // buttons only move horizontally.
  854. m_rgst[iCtlSubscribe].rc.left = cx - m_cxHorzSep - WIDTH(m_rgst[iCtlSubscribe].rc);
  855. m_rgst[iCtlSubscribe].rc.right = cx - m_cxHorzSep;
  856. SetWindowPos(m_rgst[iCtlSubscribe].hwndCtl, 0, m_rgst[iCtlSubscribe].rc.left,
  857. m_rgst[iCtlSubscribe].rc.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
  858. m_rgst[iCtlUnsubscribe].rc.left = cx - m_cxHorzSep - WIDTH(m_rgst[iCtlUnsubscribe].rc);
  859. m_rgst[iCtlUnsubscribe].rc.right = cx - m_cxHorzSep;
  860. SetWindowPos(m_rgst[iCtlUnsubscribe].hwndCtl, 0, m_rgst[iCtlUnsubscribe].rc.left,
  861. m_rgst[iCtlUnsubscribe].rc.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
  862. m_rgst[iCtlResetList].rc.left = cx - m_cxHorzSep - WIDTH(m_rgst[iCtlResetList].rc);
  863. m_rgst[iCtlResetList].rc.right = cx - m_cxHorzSep;
  864. SetWindowPos(m_rgst[iCtlResetList].hwndCtl, 0, m_rgst[iCtlResetList].rc.left,
  865. m_rgst[iCtlResetList].rc.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
  866. // The Goto, OK, and Cancel buttons move both horizontally and vertically.
  867. m_rgst[iCtlCancel].rc.left = cx - m_cxHorzSep - WIDTH(m_rgst[iCtlCancel].rc);
  868. m_rgst[iCtlCancel].rc.right = cx - m_cxHorzSep;
  869. m_rgst[iCtlCancel].rc.top = cy - m_cxHorzSep - HEIGHT(m_rgst[iCtlCancel].rc);
  870. m_rgst[iCtlCancel].rc.bottom = cy - m_cxHorzSep;
  871. SetWindowPos(m_rgst[iCtlCancel].hwndCtl, 0, m_rgst[iCtlCancel].rc.left,
  872. m_rgst[iCtlCancel].rc.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
  873. m_rgst[iCtlOK].rc.left = m_rgst[iCtlCancel].rc.left - m_cxHorzSep - WIDTH(m_rgst[iCtlCancel].rc);
  874. m_rgst[iCtlOK].rc.right = m_rgst[iCtlCancel].rc.left - m_cxHorzSep;
  875. m_rgst[iCtlOK].rc.top = cy - m_cxHorzSep - HEIGHT(m_rgst[iCtlOK].rc);
  876. m_rgst[iCtlOK].rc.bottom = cy - m_cxHorzSep;
  877. SetWindowPos(m_rgst[iCtlOK].hwndCtl, 0, m_rgst[iCtlOK].rc.left,
  878. m_rgst[iCtlOK].rc.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
  879. m_rgst[iCtlGoto].rc.left = m_rgst[iCtlOK].rc.left - m_cxHorzSep - WIDTH(m_rgst[iCtlGoto].rc);
  880. m_rgst[iCtlGoto].rc.right = m_rgst[iCtlOK].rc.left - m_cxHorzSep;
  881. m_rgst[iCtlGoto].rc.top = cy - m_cxHorzSep - HEIGHT(m_rgst[iCtlGoto].rc);
  882. m_rgst[iCtlGoto].rc.bottom = cy - m_cxHorzSep;
  883. SetWindowPos(m_rgst[iCtlGoto].hwndCtl, 0, m_rgst[iCtlGoto].rc.left,
  884. m_rgst[iCtlGoto].rc.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
  885. // Update the horizontal static line
  886. m_rgst[iCtlStaticHorzLine].rc.left = m_cxHorzSep;
  887. m_rgst[iCtlStaticHorzLine].rc.right = cx - m_cxHorzSep;
  888. m_rgst[iCtlStaticHorzLine].rc.top = m_rgst[iCtlCancel].rc.top - m_cyVertSep - HEIGHT(m_rgst[iCtlStaticHorzLine].rc);
  889. m_rgst[iCtlStaticHorzLine].rc.bottom = m_rgst[iCtlCancel].rc.top - m_cyVertSep;
  890. SetWindowPos(m_rgst[iCtlStaticHorzLine].hwndCtl, 0, m_rgst[iCtlStaticHorzLine].rc.left,
  891. m_rgst[iCtlStaticHorzLine].rc.top, WIDTH(m_rgst[iCtlStaticHorzLine].rc),
  892. HEIGHT(m_rgst[iCtlStaticHorzLine].rc), SWP_NOZORDER | SWP_NOACTIVATE);
  893. // If we have a server well, then update that and the vertical static line
  894. if (m_rgst[iCtlServers].hwndCtl)
  895. {
  896. m_rgst[iCtlServers].rc.bottom = m_rgst[iCtlStaticHorzLine].rc.top - m_cyVertSep;
  897. SetWindowPos(m_rgst[iCtlServers].hwndCtl, 0, 0, 0, WIDTH(m_rgst[iCtlServers].rc),
  898. HEIGHT(m_rgst[iCtlServers].rc), SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
  899. }
  900. // Finally update the tab control and listview
  901. m_rgst[iCtlTabs].rc.right = m_rgst[iCtlSubscribe].rc.left - m_cxHorzSep;
  902. m_rgst[iCtlTabs].rc.bottom = m_rgst[iCtlStaticHorzLine].rc.top - m_cyVertSep;
  903. SetWindowPos(m_rgst[iCtlTabs].hwndCtl, 0, 0, 0, WIDTH(m_rgst[iCtlTabs].rc),
  904. HEIGHT(m_rgst[iCtlTabs].rc), SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
  905. rc = m_rgst[iCtlTabs].rc;
  906. TabCtrl_AdjustRect(m_rgst[iCtlTabs].hwndCtl, FALSE, &rc);
  907. m_rgst[iCtlGroupList].rc.right = rc.right - (m_rgst[iCtlGroupList].rc.left - rc.left);
  908. m_rgst[iCtlGroupList].rc.bottom = rc.bottom - m_cyVertSep; //(m_rgst[iCtlGroupList].rc.top - rc.top);
  909. SetWindowPos(m_rgst[iCtlGroupList].hwndCtl, 0, 0, 0, WIDTH(m_rgst[iCtlGroupList].rc),
  910. HEIGHT(m_rgst[iCtlGroupList].rc), SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
  911. rc.top = m_rgst[iCtlGroupList].rc.top;
  912. rc.left = m_rgst[iCtlServers].rc.left;
  913. rc.bottom = cy;
  914. rc.right = cx;
  915. InvalidateRect(hwnd, &rc, TRUE);
  916. }
  917. void CGroupListDlg::OnPaint(HWND hwnd)
  918. {
  919. PAINTSTRUCT ps;
  920. RECT rc;
  921. GetClientRect(hwnd, &rc);
  922. rc.left = rc.right - GetSystemMetrics(SM_CXSMICON);
  923. rc.top = rc.bottom - GetSystemMetrics(SM_CYSMICON);
  924. BeginPaint(hwnd, &ps);
  925. if (!IsZoomed(hwnd))
  926. DrawFrameControl(ps.hdc, &rc, DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
  927. EndPaint(hwnd, &ps);
  928. }
  929. void CGroupListDlg::OnGetMinMaxInfo(HWND hwnd, LPMINMAXINFO lpmmi)
  930. {
  931. DefWindowProc(hwnd, WM_GETMINMAXINFO, 0, (LPARAM) lpmmi);
  932. lpmmi->ptMinTrackSize = m_ptDragMin;
  933. }