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.

5437 lines
147 KiB

  1. //--------------------------------------------------------------------------
  2. // MsgTable.cpp
  3. //--------------------------------------------------------------------------
  4. #include "pch.hxx"
  5. #include "instance.h"
  6. #include "msgtable.h"
  7. #include "findfold.h"
  8. #include "storutil.h"
  9. #include "ruleutil.h"
  10. #include "newsutil.h"
  11. #include "xpcomm.h"
  12. //--------------------------------------------------------------------------
  13. // CGROWTABLE
  14. //--------------------------------------------------------------------------
  15. #define CGROWTABLE 256
  16. #define INVALID_ROWINDEX 0xffffffff
  17. #define ROWSET_FETCH 100
  18. //--------------------------------------------------------------------------
  19. // GETTHREADSTATE
  20. //--------------------------------------------------------------------------
  21. typedef struct tagGETTHREADSTATE {
  22. MESSAGEFLAGS dwFlags;
  23. DWORD cHasFlags;
  24. DWORD cChildren;
  25. } GETTHREADSTATE, *LPGETTHREADSTATE;
  26. //--------------------------------------------------------------------------
  27. // THREADISFROMME
  28. //--------------------------------------------------------------------------
  29. typedef struct tagTHREADISFROMME {
  30. BOOL fResult;
  31. LPROWINFO pRow;
  32. } THREADISFROMME, *LPTHREADISFROMME;
  33. //--------------------------------------------------------------------------
  34. // THREADHIDE
  35. //--------------------------------------------------------------------------
  36. typedef struct tagTHREADHIDE {
  37. BOOL fNotify;
  38. } THREADHIDE, *LPTHREADHIDE;
  39. //--------------------------------------------------------------------------
  40. // GETSELECTIONSTATE
  41. //--------------------------------------------------------------------------
  42. typedef struct tagGETSELECTIONSTATE {
  43. SELECTIONSTATE dwMask;
  44. SELECTIONSTATE dwState;
  45. } GETSELECTIONSTATE, *LPGETSELECTIONSTATE;
  46. //--------------------------------------------------------------------------
  47. // GETTHREADPARENT
  48. //--------------------------------------------------------------------------
  49. typedef struct tagGETTHREADPARENT {
  50. IDatabase *pDatabase;
  51. IHashTable *pHash;
  52. LPVOID pvResult;
  53. } GETTHREADPARENT, *LPGETTHREADPARENT;
  54. //--------------------------------------------------------------------------
  55. // IsInitialized
  56. //--------------------------------------------------------------------------
  57. #define IsInitialized(_pThis) \
  58. (_pThis->m_pFolder && _pThis->m_pDB)
  59. //--------------------------------------------------------------------------
  60. // EnumRefsGetThreadParent
  61. //--------------------------------------------------------------------------
  62. HRESULT EnumRefsGetThreadParent(LPCSTR pszMessageId, DWORD_PTR dwCookie,
  63. BOOL *pfDone)
  64. {
  65. // Locals
  66. LPGETTHREADPARENT pGetParent = (LPGETTHREADPARENT)dwCookie;
  67. // Trace
  68. TraceCall("EnumRefsGetThreadParent");
  69. // Find Message Id
  70. if (SUCCEEDED(pGetParent->pHash->Find((LPSTR)pszMessageId, FALSE, &pGetParent->pvResult)))
  71. {
  72. // Ok
  73. *pfDone = TRUE;
  74. }
  75. // Done
  76. return(S_OK);
  77. }
  78. //--------------------------------------------------------------------------
  79. // CMessageTable::CMessageTable
  80. //--------------------------------------------------------------------------
  81. CMessageTable::CMessageTable(void)
  82. {
  83. TraceCall("CMessageTable::CMessageTable");
  84. m_cRef = 1;
  85. m_fSynching = FALSE;
  86. m_pFolder = NULL;
  87. m_pDB = NULL;
  88. m_cRows = 0;
  89. m_cView = 0;
  90. m_cFiltered = 0;
  91. m_cUnread = 0;
  92. m_cAllocated = 0;
  93. m_prgpRow = NULL;
  94. m_prgpView = NULL;
  95. m_pFindFolder = NULL;
  96. m_pNotify = NULL;
  97. m_fRelNotify = FALSE;
  98. m_pThreadMsgId = NULL;
  99. m_pThreadSubject = NULL;
  100. m_pQuery = NULL;
  101. m_cDelayed = 0;
  102. m_fRegistered = FALSE;
  103. m_clrWatched = 0;
  104. m_pszEmail = NULL;
  105. m_fLoaded = FALSE;
  106. ZeroMemory(&m_SortInfo, sizeof(FOLDERSORTINFO));
  107. ZeroMemory(&m_Notify, sizeof(NOTIFYQUEUE));
  108. ZeroMemory(&m_Folder, sizeof(FOLDERINFO));
  109. m_Notify.iRowMin = 0xffffffff;
  110. m_Notify.fClean = TRUE;
  111. }
  112. //--------------------------------------------------------------------------
  113. // CMessageTable::~CMessageTable - Don't put any Asserts in this function
  114. //--------------------------------------------------------------------------
  115. CMessageTable::~CMessageTable()
  116. {
  117. // Trace
  118. TraceCall("CMessageTable::~CMessageTable");
  119. // Free Folder Info
  120. g_pStore->FreeRecord(&m_Folder);
  121. // Free Cached Rows
  122. _FreeTable();
  123. // Release the Folder
  124. SafeRelease(m_pFolder);
  125. // Release Query Object...
  126. SafeRelease(m_pQuery);
  127. // Release DB after folder, because releasing folder can cause call chain: ~CFolderSync->~CServerQ->
  128. // CMessageList::OnComplete->CMessageTable::GetCount, for which we need a m_pDB.
  129. if (m_pDB)
  130. {
  131. // Unregister
  132. m_pDB->UnregisterNotify((IDatabaseNotify *)this);
  133. // Release the Folder
  134. m_pDB->Release();
  135. // Null
  136. m_pDB = NULL;
  137. }
  138. // Release the Find Folder
  139. SafeRelease(m_pFindFolder);
  140. // Set pCurrent
  141. if (m_pNotify)
  142. {
  143. if (m_fRelNotify)
  144. m_pNotify->Release();
  145. m_pNotify = NULL;
  146. }
  147. // Free m_pszEmail
  148. SafeMemFree(m_pszEmail);
  149. // Free the Notification Queue
  150. SafeMemFree(m_Notify.prgiRow);
  151. }
  152. //--------------------------------------------------------------------------
  153. // CMessageTable::AddRef
  154. //--------------------------------------------------------------------------
  155. STDMETHODIMP_(ULONG) CMessageTable::AddRef(void)
  156. {
  157. TraceCall("CMessageTable::AddRef");
  158. return InterlockedIncrement(&m_cRef);
  159. }
  160. //--------------------------------------------------------------------------
  161. // CMessageTable::Release
  162. //--------------------------------------------------------------------------
  163. STDMETHODIMP_(ULONG) CMessageTable::Release(void)
  164. {
  165. TraceCall("CMessageTable::Release");
  166. LONG cRef = InterlockedDecrement(&m_cRef);
  167. if (0 == cRef)
  168. delete this;
  169. return (ULONG)cRef;
  170. }
  171. //--------------------------------------------------------------------------
  172. // CMessageTable::QueryInterface
  173. //--------------------------------------------------------------------------
  174. STDMETHODIMP CMessageTable::QueryInterface(REFIID riid, LPVOID *ppv)
  175. {
  176. // Locals
  177. HRESULT hr=S_OK;
  178. // Stack
  179. TraceCall("CMessageTable::QueryInterface");
  180. // Invalid Arg
  181. Assert(ppv);
  182. // Find IID
  183. if (IID_IUnknown == riid)
  184. *ppv = (IUnknown *)(IMessageTable *)this;
  185. else if (IID_IMessageTable == riid)
  186. *ppv = (IMessageTable *)this;
  187. else if (IID_IServiceProvider == riid)
  188. *ppv = (IServiceProvider *)this;
  189. else
  190. {
  191. *ppv = NULL;
  192. hr = TraceResult(E_NOINTERFACE);
  193. goto exit;
  194. }
  195. // AddRef It
  196. ((IUnknown *)*ppv)->AddRef();
  197. exit:
  198. // Done
  199. return(hr);
  200. }
  201. //--------------------------------------------------------------------------
  202. // CMessageTable::_FIsHidden
  203. //--------------------------------------------------------------------------
  204. BOOL CMessageTable::_FIsHidden(LPROWINFO pRow)
  205. {
  206. // Trace
  207. TraceCall("CMessageTable::_FIsHidden");
  208. // Hide Deleted ?
  209. if (FALSE == m_SortInfo.fShowDeleted && ISFLAGSET(pRow->Message.dwFlags, ARF_ENDANGERED))
  210. return(TRUE);
  211. // Hide Offline Deleted ?
  212. if (ISFLAGSET(pRow->Message.dwFlags, ARF_DELETED_OFFLINE))
  213. return(TRUE);
  214. // Not Hidden
  215. return(FALSE);
  216. }
  217. //--------------------------------------------------------------------------
  218. // CMessageTable::_FIsFiltered
  219. //--------------------------------------------------------------------------
  220. BOOL CMessageTable::_FIsFiltered(LPROWINFO pRow)
  221. {
  222. // Trace
  223. TraceCall("CMessageTable::_FIsFiltered");
  224. // No Query Object
  225. if (NULL == m_pQuery)
  226. return(FALSE);
  227. // No m_pQuery
  228. return(S_OK == m_pQuery->Evaluate(&pRow->Message) ? FALSE : TRUE);
  229. }
  230. //--------------------------------------------------------------------------
  231. // CMessageTable::Initialize
  232. //--------------------------------------------------------------------------
  233. STDMETHODIMP CMessageTable::Initialize(FOLDERID idFolder, IMessageServer *pServer,
  234. BOOL fFindTable, IStoreCallback *pCallback)
  235. {
  236. // Locals
  237. HRESULT hr=S_OK;
  238. // Trace
  239. TraceCall("CMessageTable::Initialize");
  240. // Already Open ?
  241. if (m_pFolder)
  242. {
  243. hr = TraceResult(E_UNEXPECTED);
  244. goto exit;
  245. }
  246. // Search Folder ?
  247. if (fFindTable)
  248. {
  249. // Create a Find Folder
  250. IF_NULLEXIT(m_pFindFolder = new CFindFolder);
  251. // Initialize
  252. IF_FAILEXIT(hr = m_pFindFolder->Initialize(g_pStore, NULL, NOFLAGS, idFolder));
  253. // Get an IMessageFolder
  254. IF_FAILEXIT(hr = m_pFindFolder->QueryInterface(IID_IMessageFolder, (LPVOID *)&m_pFolder));
  255. }
  256. // Otherwise
  257. else
  258. {
  259. // Are there children
  260. IF_FAILEXIT(hr = g_pStore->OpenFolder(idFolder, pServer, NOFLAGS, &m_pFolder));
  261. }
  262. // Get the folder id, it might have changed if this is a find folder
  263. IF_FAILEXIT(hr = m_pFolder->GetFolderId(&idFolder));
  264. // Get Folder Info
  265. IF_FAILEXIT(hr = g_pStore->GetFolderInfo(idFolder, &m_Folder));
  266. // Get the Database
  267. IF_FAILEXIT(hr = m_pFolder->GetDatabase(&m_pDB));
  268. // Set m_clrWatched
  269. m_clrWatched = (WORD)DwGetOption(OPT_WATCHED_COLOR);
  270. exit:
  271. // Done
  272. return(hr);
  273. }
  274. //--------------------------------------------------------------------------
  275. // CMessageTable::StartFind
  276. //--------------------------------------------------------------------------
  277. STDMETHODIMP CMessageTable::StartFind(LPFINDINFO pCriteria, IStoreCallback *pCallback)
  278. {
  279. // Locals
  280. HRESULT hr=S_OK;
  281. // Trace
  282. TraceCall("CMessageTable::StartFind");
  283. // Validate State
  284. if (!IsInitialized(this) || NULL == m_pFindFolder)
  285. return(TraceResult(E_UNEXPECTED));
  286. // Initialize the Find Folder
  287. IF_FAILEXIT(hr = m_pFindFolder->StartFind(pCriteria, pCallback));
  288. exit:
  289. // Done
  290. return(hr);
  291. }
  292. //--------------------------------------------------------------------------
  293. // CMessageTable::_GetSortChangeInfo
  294. //--------------------------------------------------------------------------
  295. HRESULT CMessageTable::_GetSortChangeInfo(LPFOLDERSORTINFO pSortInfo,
  296. LPFOLDERUSERDATA pUserData, LPSORTCHANGEINFO pChange)
  297. {
  298. // Locals
  299. HRESULT hr;
  300. DWORD dwVersion;
  301. // Trace
  302. TraceCall("CMessageTable::_GetSortChangeInfo");
  303. // INitialize
  304. ZeroMemory(pChange, sizeof(SORTCHANGEINFO));
  305. // Invalid ?
  306. if (pSortInfo->ridFilter == RULEID_INVALID)
  307. {
  308. // Reset
  309. pSortInfo->ridFilter = RULEID_VIEW_ALL;
  310. }
  311. // Get the filter version
  312. hr = RuleUtil_HrGetFilterVersion(pSortInfo->ridFilter, &dwVersion);
  313. // Bummer, that failed, so lets revert back to the default filter
  314. if (FAILED(hr))
  315. {
  316. // View All filter
  317. pSortInfo->ridFilter = RULEID_VIEW_ALL;
  318. // Filter Changed...
  319. pChange->fFilter = TRUE;
  320. }
  321. // Ohterwise, If this is a different filter
  322. else if (pUserData->ridFilter != pSortInfo->ridFilter)
  323. {
  324. // Reset Version
  325. pUserData->dwFilterVersion = dwVersion;
  326. // Filter Changed...
  327. pChange->fFilter = TRUE;
  328. }
  329. // Otherwise, did the version of this filter change
  330. else if (pUserData->dwFilterVersion != dwVersion)
  331. {
  332. // Reset Version
  333. pUserData->dwFilterVersion = dwVersion;
  334. // Filter Changed...
  335. pChange->fFilter = TRUE;
  336. }
  337. // Other filtering changes
  338. if (pSortInfo->fShowDeleted != (BOOL)pUserData->fShowDeleted || pSortInfo->fShowReplies != (BOOL)pUserData->fShowReplies)
  339. {
  340. // Filter Changed...
  341. pChange->fFilter = TRUE;
  342. }
  343. // Sort Order Change
  344. if (pSortInfo->idColumn != (COLUMN_ID)pUserData->idSort || pSortInfo->fAscending != (BOOL)pUserData->fAscending)
  345. {
  346. // Sort Changed
  347. pChange->fSort = TRUE;
  348. }
  349. // Thread Change...
  350. if (pSortInfo->fThreaded != (BOOL)pUserData->fThreaded)
  351. {
  352. // Thread Change
  353. pChange->fThread = TRUE;
  354. }
  355. // Expand Change
  356. if (pSortInfo->fExpandAll != (BOOL)pUserData->fExpandAll)
  357. {
  358. // Expand Change
  359. pChange->fExpand = TRUE;
  360. }
  361. // Done
  362. return(S_OK);
  363. }
  364. //--------------------------------------------------------------------------
  365. // CMessageTable::OnSynchronizeComplete
  366. //--------------------------------------------------------------------------
  367. STDMETHODIMP CMessageTable::OnSynchronizeComplete(void)
  368. {
  369. // Locals
  370. DWORD i;
  371. SORTCHANGEINFO Change={0};
  372. // Trace
  373. TraceCall("CMessageTable::OnSynchronizeComplete");
  374. // If Not New...
  375. if (FOLDER_NEWS != m_Folder.tyFolder)
  376. goto exit;
  377. // Finish any insert notifications
  378. m_pDB->DispatchNotify(this);
  379. // Nothing to do...
  380. if (0 == m_cDelayed)
  381. goto exit;
  382. // Reset m_cDelayed
  383. m_cDelayed = 0;
  384. // ChangeSortOrThreading
  385. _SortThreadFilterTable(&Change, m_SortInfo.fShowReplies);
  386. // Remove fDelayed Bit...
  387. for (i = 0; i < m_cRows; i++)
  388. {
  389. // Remove Delayed Bit
  390. m_prgpRow[i]->fDelayed = FALSE;
  391. }
  392. exit:
  393. // Reset m_fSynching
  394. m_fSynching = FALSE;
  395. // Done
  396. return(S_OK);
  397. }
  398. //--------------------------------------------------------------------------
  399. // CMessageTable::SetSortInfo
  400. //--------------------------------------------------------------------------
  401. STDMETHODIMP CMessageTable::SetSortInfo(LPFOLDERSORTINFO pSortInfo,
  402. IStoreCallback *pCallback)
  403. {
  404. // Locals
  405. HRESULT hr=S_OK;
  406. HCURSOR hCursor=NULL;
  407. HLOCK hLock=NULL;
  408. FOLDERUSERDATA UserData;
  409. SORTCHANGEINFO Change;
  410. IF_DEBUG(DWORD dwTickStart=GetTickCount());
  411. // Trace
  412. TraceCall("CMessageTable::SetSortInfo");
  413. // Validate State
  414. if (!IsInitialized(this))
  415. return(TraceResult(E_UNEXPECTED));
  416. // Wait Cursor
  417. hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
  418. // If this isn't a news folder then don't allow fShowReplies
  419. if (FOLDER_NEWS != m_Folder.tyFolder)
  420. {
  421. // Clear fShowReplies
  422. pSortInfo->fShowReplies = FALSE;
  423. }
  424. // Lock
  425. IF_FAILEXIT(hr = m_pDB->Lock(&hLock));
  426. // Get UserData
  427. IF_FAILEXIT(hr = m_pDB->GetUserData(&UserData, sizeof(FOLDERUSERDATA)));
  428. // Get Sort Change Information...
  429. IF_FAILEXIT(hr = _GetSortChangeInfo(pSortInfo, &UserData, &Change));
  430. // Save the SortInfo
  431. CopyMemory(&m_SortInfo, pSortInfo, sizeof(FOLDERSORTINFO));
  432. // Total Rebuild ?
  433. if (NULL == m_prgpRow)
  434. {
  435. // Build RowTable
  436. IF_FAILEXIT(hr = _BuildTable(pCallback));
  437. }
  438. // Sort or Threading Change Only
  439. else if (Change.fSort || Change.fThread || Change.fFilter)
  440. {
  441. // ChangeSortOrThreading
  442. _SortThreadFilterTable(&Change, Change.fFilter);
  443. }
  444. // Expand State Change
  445. else if (Change.fExpand && m_SortInfo.fThreaded)
  446. {
  447. // Expand All ?
  448. if (m_SortInfo.fExpandAll)
  449. {
  450. // Expand Everything
  451. _ExpandThread(INVALID_ROWINDEX, FALSE, FALSE);
  452. }
  453. // Otherwise, collapse all
  454. else
  455. {
  456. // Collapse Everything
  457. _CollapseThread(INVALID_ROWINDEX, FALSE);
  458. }
  459. }
  460. // Otherwise, just refresh the filter
  461. else
  462. {
  463. // RefreshFilter
  464. _RefreshFilter();
  465. }
  466. // Save Sort Order
  467. UserData.fAscending = pSortInfo->fAscending;
  468. UserData.idSort = pSortInfo->idColumn;
  469. UserData.fThreaded = pSortInfo->fThreaded;
  470. UserData.ridFilter = pSortInfo->ridFilter;
  471. UserData.fExpandAll = pSortInfo->fExpandAll;
  472. UserData.fShowDeleted = (BYTE) !!(pSortInfo->fShowDeleted);
  473. UserData.fShowReplies = (BYTE) !!(pSortInfo->fShowReplies);
  474. // Get UserData
  475. IF_FAILEXIT(hr = m_pDB->SetUserData(&UserData, sizeof(FOLDERUSERDATA)));
  476. // Have I Registered For Notifications Yet ?
  477. if (FALSE == m_fRegistered)
  478. {
  479. // Register for Notifications
  480. IF_FAILEXIT(hr = m_pDB->RegisterNotify(IINDEX_PRIMARY, REGISTER_NOTIFY_NOADDREF, 0, (IDatabaseNotify *)this));
  481. // Registered
  482. m_fRegistered = TRUE;
  483. }
  484. exit:
  485. // Unlock
  486. m_pDB->Unlock(&hLock);
  487. // Reset Cursor
  488. SetCursor(hCursor);
  489. // Time to Sort
  490. TraceInfo(_MSG("Table Sort Time: %d Milli-Seconds", GetTickCount() - dwTickStart));
  491. // Done
  492. return(hr);
  493. }
  494. //--------------------------------------------------------------------------
  495. // CMessageTable::GetSortInfo
  496. //--------------------------------------------------------------------------
  497. STDMETHODIMP CMessageTable::GetSortInfo(LPFOLDERSORTINFO pSortInfo)
  498. {
  499. // Locals
  500. HRESULT hr=S_OK;
  501. FOLDERUSERDATA UserData;
  502. // Trace
  503. TraceCall("CMessageTable::GetSortInfo");
  504. // Validate State
  505. if (!IsInitialized(this))
  506. return(TraceResult(E_UNEXPECTED));
  507. // Initialize
  508. ZeroMemory(pSortInfo, sizeof(FOLDERSORTINFO));
  509. // Get Sort Information
  510. IF_FAILEXIT(hr = m_pDB->GetUserData(&UserData, sizeof(FOLDERUSERDATA)));
  511. // Save Sort Order if not threaded
  512. pSortInfo->fAscending = UserData.fAscending;
  513. // Threaded
  514. pSortInfo->fThreaded = UserData.fThreaded;
  515. // Save Sort Column
  516. pSortInfo->idColumn = (COLUMN_ID)UserData.idSort;
  517. // Expand All
  518. pSortInfo->fExpandAll = UserData.fExpandAll;
  519. // Set rid Filter
  520. pSortInfo->ridFilter = UserData.ridFilter;
  521. // Set deleted state
  522. pSortInfo->fShowDeleted = UserData.fShowDeleted;
  523. // Set replies
  524. pSortInfo->fShowReplies = UserData.fShowReplies;
  525. exit:
  526. // Done
  527. return(hr);
  528. }
  529. //--------------------------------------------------------------------------
  530. // CMessageTable::_GetRowFromIndex
  531. //--------------------------------------------------------------------------
  532. HRESULT CMessageTable::_GetRowFromIndex(ROWINDEX iRow, LPROWINFO *ppRow)
  533. {
  534. // Locals
  535. HRESULT hr=S_OK;
  536. LPROWINFO pRow;
  537. // Trace
  538. TraceCall("CMessageTable::_GetRowFromIndex");
  539. // Out of View Range ?
  540. if (iRow >= m_cView)
  541. {
  542. hr = E_FAIL;
  543. goto exit;
  544. }
  545. // Bad Row Index
  546. if (NULL == m_prgpView[iRow])
  547. {
  548. hr = TraceResult(E_FAIL);
  549. goto exit;
  550. }
  551. // Set pRow
  552. pRow = m_prgpView[iRow];
  553. // Validate Reserved...
  554. IxpAssert(pRow->Message.dwReserved == (DWORD_PTR)pRow);
  555. // Must have pAllocated
  556. IxpAssert(pRow->Message.pAllocated);
  557. // Must have References
  558. IxpAssert(pRow->cRefs > 0);
  559. // Set pprow
  560. *ppRow = pRow;
  561. exit:
  562. // Return the Row
  563. return(hr);
  564. }
  565. //--------------------------------------------------------------------------
  566. // CMessageTable::_CreateRow
  567. //--------------------------------------------------------------------------
  568. HRESULT CMessageTable::_CreateRow(LPMESSAGEINFO pMessage, LPROWINFO *ppRow)
  569. {
  570. // Locals
  571. HRESULT hr=S_OK;
  572. LPROWINFO pRow;
  573. // Trace
  574. TraceCall("CMessageTable::_CreateRow");
  575. // Allocate the Row
  576. IF_FAILEXIT(hr = m_pDB->HeapAllocate(HEAP_ZERO_MEMORY, sizeof(ROWINFO), (LPVOID *)&pRow));
  577. // Save the Highlight
  578. pRow->wHighlight = pMessage->wHighlight;
  579. // Copy the message
  580. CopyMemory(&pRow->Message, pMessage, sizeof(MESSAGEINFO));
  581. // Set pRow into
  582. pRow->Message.dwReserved = (DWORD_PTR)pRow;
  583. // OneRef
  584. pRow->cRefs = 1;
  585. // Return the Row
  586. *ppRow = pRow;
  587. exit:
  588. // Done
  589. return(hr);
  590. }
  591. //--------------------------------------------------------------------------
  592. // CMessageTable::_DeleteRowFromThread
  593. //--------------------------------------------------------------------------
  594. HRESULT CMessageTable::_DeleteRowFromThread(LPROWINFO pRow, BOOL fNotify)
  595. {
  596. // Locals
  597. LPROWINFO pCurrent;
  598. LPROWINFO pNewRow;
  599. ROWINDEX iMin;
  600. ROWINDEX iMax;
  601. // Trace
  602. TraceCall("CMessageTable::_DeleteRowFromThread");
  603. // Abort
  604. if (FALSE == m_SortInfo.fThreaded || pRow->fFiltered || pRow->fHidden)
  605. return(S_OK);
  606. // Notify
  607. if (fNotify)
  608. {
  609. // _RefreshThread
  610. _GetThreadIndexRange(pRow, TRUE, &iMin, &iMax);
  611. }
  612. // If there is a messageid
  613. if (pRow->Message.pszMessageId)
  614. {
  615. // Remove pRow from both threading indexes!!!
  616. if (SUCCEEDED(m_pThreadMsgId->Find(pRow->Message.pszMessageId, TRUE, (LPVOID *)&pCurrent)))
  617. {
  618. // If this isn't this row, then put it back...
  619. if (pRow != pCurrent)
  620. {
  621. // Put It Back
  622. m_pThreadMsgId->Insert(pRow->Message.pszMessageId, (LPVOID)pCurrent, HF_NO_DUPLICATES);
  623. }
  624. }
  625. }
  626. // If there is a normalized subject and a subject hash table
  627. if (NULL == pRow->pParent && pRow->Message.pszNormalSubj && m_pThreadSubject)
  628. {
  629. // Remove pRow from both threading indexes!!!
  630. if (SUCCEEDED(m_pThreadSubject->Find(pRow->Message.pszNormalSubj, TRUE, (LPVOID *)&pCurrent)))
  631. {
  632. // If this isn't this row, then put it back...
  633. if (pRow != pCurrent)
  634. {
  635. // Put It Back
  636. m_pThreadSubject->Insert(pRow->Message.pszNormalSubj, (LPVOID)pCurrent, HF_NO_DUPLICATES);
  637. }
  638. }
  639. }
  640. // If we have a Child
  641. if (pRow->pChild)
  642. {
  643. // Set pNewRow
  644. pNewRow = pRow->pChild;
  645. // Promote Children of pNewRow to be at the same level as the children of pRow
  646. if (pNewRow->pChild)
  647. {
  648. // Walk until I find the last sibling
  649. pCurrent = pNewRow->pChild;
  650. // Continue
  651. while (pCurrent->pSibling)
  652. {
  653. // Validate Parent
  654. Assert(pCurrent->pParent == pNewRow);
  655. // Goto Next
  656. pCurrent = pCurrent->pSibling;
  657. }
  658. // Make pLastSibling->pSibling
  659. pCurrent->pSibling = pNewRow->pSibling;
  660. }
  661. // Otherwise, Child is the first sibling of pNewRow
  662. else
  663. {
  664. // Set First Child
  665. pNewRow->pChild = pNewRow->pSibling;
  666. }
  667. // Fixup other children of pRow to have a new parent of pNewRow...
  668. pCurrent = pNewRow->pSibling;
  669. // While we have siblings...
  670. while (pCurrent)
  671. {
  672. // Current Parent is pRow
  673. Assert(pRow == pCurrent->pParent);
  674. // Reset the parent...
  675. pCurrent->pParent = pNewRow;
  676. // Goto Next Sibling
  677. pCurrent = pCurrent->pSibling;
  678. }
  679. // Set the Sibling of pNewRow to be the same sibling as pRow
  680. pNewRow->pSibling = pRow->pSibling;
  681. // Reset Parent of pNewRow
  682. pNewRow->pParent = pRow->pParent;
  683. // Assume Expanded Flags...
  684. pNewRow->fExpanded = pRow->fExpanded;
  685. // Clear dwState
  686. pNewRow->dwState = 0;
  687. // If pNewRow is now a Root.. Need to adjust the subject hash table..
  688. if (NULL == pNewRow->pParent && pNewRow->Message.pszNormalSubj && m_pThreadSubject)
  689. {
  690. // Remove pRow from both threading indexes!!!
  691. m_pThreadSubject->Insert(pNewRow->Message.pszNormalSubj, (LPVOID)pNewRow, HF_NO_DUPLICATES);
  692. }
  693. }
  694. // Otherwise...
  695. else
  696. {
  697. // Set pNewRow for doing sibling/parent fixup
  698. pNewRow = pRow->pSibling;
  699. }
  700. // Otherwise, if there is a parent...
  701. if (pRow->pParent)
  702. {
  703. // Parent must have children
  704. Assert(pRow->pParent->pChild);
  705. // First Child of pRow->pParent
  706. if (pRow == pRow->pParent->pChild)
  707. {
  708. // Set new first child to pRow's Sibling
  709. pRow->pParent->pChild = pNewRow;
  710. }
  711. // Otherwise, Walk pParent's Child and remove pRow from Sibling List
  712. else
  713. {
  714. // Set pPrevious
  715. LPROWINFO pPrevious=NULL;
  716. // Set pCurrent
  717. pCurrent = pRow->pParent->pChild;
  718. // Loop
  719. while (pCurrent)
  720. {
  721. // Is this the row to remove!
  722. if (pRow == pCurrent)
  723. {
  724. // Better be a previous
  725. Assert(pPrevious);
  726. // pPrevious's Sibling better be pRow
  727. Assert(pPrevious->pSibling == pRow);
  728. // Set New Sibling
  729. pPrevious->pSibling = pNewRow;
  730. // Done
  731. break;
  732. }
  733. // Set pPrevious
  734. pPrevious = pCurrent;
  735. // Set pCurrent
  736. pCurrent = pCurrent->pSibling;
  737. }
  738. // Validate
  739. Assert(pRow == pCurrent);
  740. }
  741. // Set row state
  742. pRow->pParent->dwState = 0;
  743. }
  744. // UpdateRows
  745. if (fNotify && INVALID_ROWINDEX != iMin && INVALID_ROWINDEX != iMax)
  746. {
  747. // Queue the Notification
  748. _QueueNotification(TRANSACTION_UPDATE, iMin, iMax);
  749. }
  750. // Clear the row
  751. pRow->pParent = pRow->pChild = pRow->pSibling = NULL;
  752. // done
  753. return(S_OK);
  754. }
  755. //--------------------------------------------------------------------------
  756. // CMessageTable::_PGetThreadRoot
  757. //--------------------------------------------------------------------------
  758. LPROWINFO CMessageTable::_PGetThreadRoot(LPROWINFO pRow)
  759. {
  760. // Trace
  761. TraceCall("CMessageTable::_PGetThreadRoot");
  762. // Validate
  763. Assert(pRow);
  764. // Set Root
  765. LPROWINFO pRoot = pRow;
  766. // While there is a parent
  767. while (pRoot->pParent)
  768. {
  769. // Go Up One
  770. pRoot = pRoot->pParent;
  771. }
  772. // Done
  773. return(pRoot);
  774. }
  775. //--------------------------------------------------------------------------
  776. // CMessageTable::_GetThreadIndexRange
  777. //--------------------------------------------------------------------------
  778. HRESULT CMessageTable::_GetThreadIndexRange(LPROWINFO pRow, BOOL fClearState,
  779. LPROWINDEX piMin, LPROWINDEX piMax)
  780. {
  781. // Locals
  782. LPROWINFO pRoot;
  783. ROWINDEX iRow;
  784. // Trace
  785. TraceCall("CMessageTable::_GetThreadIndexRange");
  786. // Validate Args
  787. Assert(pRow && piMin && piMax);
  788. // Initialize
  789. *piMin = *piMax = INVALID_ROWINDEX;
  790. // Get the Root
  791. pRoot = _PGetThreadRoot(pRow);
  792. // If the root isn't visible, then don't bother...
  793. if (FALSE == pRoot->fVisible)
  794. return(S_OK);
  795. // The Root Must be Visible, not filtered and not hidden
  796. Assert(FALSE == pRoot->fFiltered && FALSE == pRoot->fHidden);
  797. // Get the Row Index
  798. SideAssert(SUCCEEDED(GetRowIndex(pRoot->Message.idMessage, piMin)));
  799. // Init piMax
  800. (*piMax) = (*piMin);
  801. // Loop until I hit the next row in the view who is the root
  802. while (1)
  803. {
  804. // Set irow
  805. iRow = (*piMax) + 1;
  806. // Dont
  807. if (iRow >= m_cView)
  808. break;
  809. // Look at the Next Row
  810. if (NULL == m_prgpView[iRow]->pParent)
  811. break;
  812. // Increment piMax
  813. (*piMax) = iRow;
  814. }
  815. // ClearState
  816. if (fClearState)
  817. {
  818. // If Clear State
  819. _WalkMessageThread(pRoot, WALK_THREAD_CURRENT, NULL, _WalkThreadClearState);
  820. }
  821. // Done
  822. return(S_OK);
  823. }
  824. //--------------------------------------------------------------------------
  825. // CMessageTable::_LinkRowIntoThread
  826. //--------------------------------------------------------------------------
  827. HRESULT CMessageTable::_LinkRowIntoThread(LPROWINFO pParent, LPROWINFO pRow,
  828. BOOL fNotify)
  829. {
  830. // Locals
  831. BOOL fHadChildren=(pParent->pChild ? TRUE : FALSE);
  832. LPROWINFO pCurrent;
  833. LPROWINFO pPrevious=NULL;
  834. // Trace
  835. TraceCall("CMessageTable::_LinkRowIntoThread");
  836. // Set Parent
  837. pRow->pParent = pParent;
  838. // Loop through the children and find the right place to insert this child
  839. pCurrent = pParent->pChild;
  840. // Loop
  841. while (pCurrent)
  842. {
  843. // Compare Received Time...
  844. if (CompareFileTime(&pRow->Message.ftReceived, &pCurrent->Message.ftReceived) <= 0)
  845. break;
  846. // Set Previous
  847. pPrevious = pCurrent;
  848. // Goto Next
  849. pCurrent = pCurrent->pSibling;
  850. }
  851. // If there is a pPrevious
  852. if (pPrevious)
  853. {
  854. // Set Sibling of pRow
  855. pRow->pSibling = pPrevious->pSibling;
  856. // Point pPrevious to pRow
  857. pPrevious->pSibling = pRow;
  858. }
  859. // Otherwise, set parent child
  860. else
  861. {
  862. // Set Sibling of pRow
  863. pRow->pSibling = pParent->pChild;
  864. // First Row ?
  865. if (NULL == pParent->pChild && FALSE == m_fLoaded)
  866. {
  867. // Set Expanded
  868. pParent->fExpanded = m_SortInfo.fExpandAll;
  869. }
  870. // Set Parent Child
  871. pParent->pChild = pRow;
  872. }
  873. // Not Loaded
  874. if (FALSE == m_fLoaded || TRUE == pRow->fDelayed)
  875. {
  876. // Set Expanded Bit on this row...
  877. pRow->fExpanded = pParent->fExpanded;
  878. }
  879. // If this is the first child and we have expand all on
  880. if (fNotify)
  881. {
  882. // First Child...
  883. if (pParent->fVisible && (m_SortInfo.fExpandAll || pParent->fExpanded))
  884. {
  885. // Locals
  886. ROWINDEX iParent;
  887. // Expand this thread...
  888. SideAssert(SUCCEEDED(GetRowIndex(pParent->Message.idMessage, &iParent)));
  889. // Expand...
  890. _ExpandThread(iParent, TRUE, FALSE);
  891. }
  892. // Otherwise, update this thread range...
  893. else if (m_pNotify)
  894. {
  895. // Locals
  896. ROWINDEX iMin;
  897. ROWINDEX iMax;
  898. // _RefreshThread
  899. _GetThreadIndexRange(pParent, TRUE, &iMin, &iMax);
  900. // UpdateRows
  901. if (INVALID_ROWINDEX != iMin && INVALID_ROWINDEX != iMax)
  902. {
  903. // Queue It
  904. _QueueNotification(TRANSACTION_UPDATE, iMin, iMax);
  905. }
  906. }
  907. }
  908. // Done
  909. return(S_OK);
  910. }
  911. //--------------------------------------------------------------------------
  912. // CMessageTable::_FindThreadParentByRef
  913. //--------------------------------------------------------------------------
  914. HRESULT CMessageTable::_FindThreadParentByRef(LPCSTR pszReferences,
  915. LPROWINFO *ppParent)
  916. {
  917. // Locals
  918. HRESULT hr=S_OK;
  919. GETTHREADPARENT GetParent;
  920. // Trace
  921. TraceCall("CMessageTable::_FindThreadParentByRef");
  922. // Init
  923. *ppParent = NULL;
  924. // Setup GetParent
  925. GetParent.pDatabase = m_pDB;
  926. GetParent.pHash = m_pThreadMsgId;
  927. GetParent.pvResult = NULL;
  928. // EnumerateReferences
  929. IF_FAILEXIT(hr = EnumerateRefs(pszReferences, (DWORD_PTR)&GetParent, EnumRefsGetThreadParent));
  930. // Not Found
  931. if (NULL == GetParent.pvResult)
  932. {
  933. hr = S_FALSE;
  934. goto exit;
  935. }
  936. // Return the Row
  937. *ppParent = (LPROWINFO)GetParent.pvResult;
  938. exit:
  939. // Done
  940. return(hr);
  941. }
  942. //--------------------------------------------------------------------------
  943. // CMessageTable::_InsertRowIntoThread
  944. //--------------------------------------------------------------------------
  945. HRESULT CMessageTable::_InsertRowIntoThread(LPROWINFO pRow, BOOL fNotify)
  946. {
  947. // Locals
  948. HRESULT hr=S_OK;
  949. LPROWINFO pParent;
  950. LPMESSAGEINFO pMessage=&pRow->Message;
  951. // Trace
  952. TraceCall("CMessageTable::_InsertRowIntoThread");
  953. // Better not be hidden or filtered
  954. Assert(FALSE == pRow->fFiltered && FALSE == pRow->fHidden);
  955. // Find Parent by References Line
  956. if (S_OK == _FindThreadParentByRef(pMessage->pszReferences, &pParent))
  957. {
  958. // Link row into thread
  959. _LinkRowIntoThread(pParent, pRow, fNotify);
  960. // Ok
  961. hr = S_OK;
  962. // Done
  963. goto exit;
  964. }
  965. // Subject Threading
  966. if (m_pThreadSubject)
  967. {
  968. // If there is a subject
  969. if (NULL == pRow->Message.pszNormalSubj)
  970. {
  971. hr = S_FALSE;
  972. goto exit;
  973. }
  974. // Try to find a message who has the same normalized subject....
  975. if (SUCCEEDED(m_pThreadSubject->Find(pRow->Message.pszNormalSubj, FALSE, (LPVOID *)&pParent)))
  976. {
  977. // Should we Swap the parent and pRow ?
  978. if (CompareFileTime(&pRow->Message.ftReceived, &pParent->Message.ftReceived) <= 0)
  979. {
  980. // Locals
  981. ROWINDEX iRow;
  982. // Make pRow be the Root
  983. IxpAssert(NULL == pParent->pParent && NULL == pParent->pSibling && pParent->fVisible);
  984. // No Parent for pRow
  985. pRow->pParent = NULL;
  986. // Set Expanded
  987. pRow->fExpanded = pParent->fExpanded;
  988. // Get the Row Index
  989. SideAssert(SUCCEEDED(GetRowIndex(pParent->Message.idMessage, &iRow)));
  990. // Validate
  991. Assert(m_prgpView[iRow] == pParent);
  992. // Replace with pRow
  993. m_prgpView[iRow] = pRow;
  994. // Visible
  995. pRow->fVisible = TRUE;
  996. // Clear Visible...
  997. pParent->fVisible = FALSE;
  998. // Replace the Subject Token
  999. SideAssert(SUCCEEDED(m_pThreadSubject->Replace(pRow->Message.pszNormalSubj, (LPVOID *)pRow)));
  1000. // Link row into thread
  1001. _LinkRowIntoThread(pRow, pParent, fNotify);
  1002. }
  1003. // Otherwise..
  1004. else
  1005. {
  1006. // Link row into thread
  1007. _LinkRowIntoThread(pParent, pRow, fNotify);
  1008. }
  1009. // Success
  1010. hr = S_OK;
  1011. // Done
  1012. goto exit;
  1013. }
  1014. }
  1015. // Not Found
  1016. hr = S_FALSE;
  1017. exit:
  1018. // Done
  1019. return(hr);
  1020. }
  1021. //--------------------------------------------------------------------------
  1022. // CMessageTable::_RefreshFilter
  1023. //--------------------------------------------------------------------------
  1024. HRESULT CMessageTable::_RefreshFilter(void)
  1025. {
  1026. // Locals
  1027. HRESULT hr=S_OK;
  1028. DWORD i;
  1029. LPROWINFO pRow;
  1030. SORTCHANGEINFO Change={0};
  1031. // Trace
  1032. TraceCall("CMessageTable::_RefreshFilter");
  1033. // No filter currently enabled
  1034. if (NULL == m_pQuery)
  1035. goto exit;
  1036. // Loop through current rows...
  1037. for (i = 0; i < m_cRows; i++)
  1038. {
  1039. // Set pRow
  1040. pRow = m_prgpRow[i];
  1041. // If Not Hidden and Not Filtered
  1042. if (pRow->fFiltered)
  1043. continue;
  1044. // Set Filtered Bit
  1045. if (FALSE == _FIsFiltered(pRow))
  1046. continue;
  1047. // Adjust m_cUnread
  1048. _AdjustUnreadCount(pRow, -1);
  1049. // Hide the Row
  1050. _HideRow(pRow, FALSE);
  1051. // Filtered
  1052. pRow->fFiltered = TRUE;
  1053. // Increment m_cFiltered
  1054. m_cFiltered++;
  1055. }
  1056. exit:
  1057. // Done
  1058. return(hr);
  1059. }
  1060. //--------------------------------------------------------------------------
  1061. // CMessageTable::_SortThreadFilterTable
  1062. //--------------------------------------------------------------------------
  1063. HRESULT CMessageTable::_SortThreadFilterTable(LPSORTCHANGEINFO pChange,
  1064. BOOL fApplyFilter)
  1065. {
  1066. // Locals
  1067. HRESULT hr=S_OK;
  1068. DWORD i;
  1069. LPROWINFO pRow;
  1070. QUERYINFO Query={0};
  1071. // Trace
  1072. TraceCall("CMessageTable::_SortThreadFilterTable");
  1073. // Nothing to resort ?
  1074. if (0 == m_cRows)
  1075. goto exit;
  1076. // Nuke the View Index
  1077. m_cView = 0;
  1078. // Do the Filter
  1079. if (pChange->fFilter)
  1080. {
  1081. // Get m_pQuery
  1082. SafeRelease(m_pQuery);
  1083. // Build a Query Object
  1084. if (SUCCEEDED(RuleUtil_HrBuildQuerysFromFilter(m_SortInfo.ridFilter, &Query)) && Query.pszQuery)
  1085. {
  1086. // Get the Query Object
  1087. IF_FAILEXIT(hr = g_pDBSession->OpenQuery(m_pDB, Query.pszQuery, &m_pQuery));
  1088. }
  1089. }
  1090. // Drop the Threading Indexes
  1091. SafeRelease(m_pThreadMsgId);
  1092. SafeRelease(m_pThreadSubject);
  1093. // If Threaded
  1094. if (m_SortInfo.fThreaded)
  1095. {
  1096. // Create a New Hash TAble
  1097. IF_FAILEXIT(hr = MimeOleCreateHashTable(max(1024, m_cRows), FALSE, &m_pThreadMsgId));
  1098. // Don't do Subject threading?
  1099. if (DwGetOption(OPT_SUBJECT_THREADING) || (FOLDER_NEWS != m_Folder.tyFolder))
  1100. {
  1101. // Create a Subject Hash Table
  1102. IF_FAILEXIT(hr = MimeOleCreateHashTable(max(1024, m_cRows), FALSE, &m_pThreadSubject));
  1103. }
  1104. }
  1105. // Reset Unread and Filtered
  1106. m_cUnread = m_cFiltered = 0;
  1107. // Loop through current rows...
  1108. for (i = 0; i < m_cRows; i++)
  1109. {
  1110. // Set pRow
  1111. pRow = m_prgpRow[i];
  1112. // Reset Visible
  1113. pRow->fVisible = FALSE;
  1114. // Clear Threading
  1115. pRow->pParent = pRow->pChild = pRow->pSibling = NULL;
  1116. // Clear dwState
  1117. pRow->dwState = 0;
  1118. // If Threaded..
  1119. if (FALSE == m_SortInfo.fThreaded)
  1120. {
  1121. // Clear Expanded
  1122. pRow->fExpanded = FALSE;
  1123. }
  1124. // Otherwise, if the row is hidden
  1125. else if (pRow->fFiltered || pRow->fHidden)
  1126. {
  1127. // Reset Expanded State
  1128. pRow->fExpanded = m_SortInfo.fExpandAll;
  1129. }
  1130. // Do filter
  1131. if (fApplyFilter)
  1132. {
  1133. // Reset the Highlight
  1134. pRow->Message.wHighlight = pRow->wHighlight;
  1135. // If not doing show repiles
  1136. if (FALSE == m_SortInfo.fShowReplies)
  1137. {
  1138. // Set Filtered Bit
  1139. pRow->fFiltered = _FIsFiltered(pRow);
  1140. // Set Hidden Bit
  1141. pRow->fHidden = _FIsHidden(pRow);
  1142. }
  1143. // Otherwise, clear the filtered bits
  1144. else
  1145. {
  1146. // Clear the Bits
  1147. pRow->fFiltered = pRow->fHidden = FALSE;
  1148. }
  1149. }
  1150. // If Not Filtered
  1151. if (FALSE == pRow->fFiltered && FALSE == pRow->fHidden)
  1152. {
  1153. // Hash the MessageId
  1154. if (m_SortInfo.fThreaded)
  1155. {
  1156. // Insert Message Id into the hash table
  1157. if (pRow->Message.pszMessageId)
  1158. {
  1159. // Insert It
  1160. m_pThreadMsgId->Insert(pRow->Message.pszMessageId, (LPVOID)pRow, HF_NO_DUPLICATES);
  1161. }
  1162. }
  1163. // Otherwise, add entry to view index
  1164. else
  1165. {
  1166. // Visible
  1167. pRow->fVisible = TRUE;
  1168. // Put into m_prgpView
  1169. m_prgpView[m_cView] = pRow;
  1170. // Increment View Count
  1171. m_cView++;
  1172. }
  1173. // Adjust m_cUnread
  1174. _AdjustUnreadCount(pRow, 1);
  1175. }
  1176. // Otherwise, free the record
  1177. else
  1178. {
  1179. // Count Filtered
  1180. m_cFiltered++;
  1181. }
  1182. }
  1183. // Sort the Table
  1184. _SortAndThreadTable(fApplyFilter);
  1185. // If Threaded
  1186. if (m_SortInfo.fThreaded)
  1187. {
  1188. // If the Filter Changed, then re-apply collapse and expand...
  1189. if (pChange->fThread)
  1190. {
  1191. // Expand All ?
  1192. if (m_SortInfo.fExpandAll)
  1193. {
  1194. // Expand Everything
  1195. _ExpandThread(INVALID_ROWINDEX, FALSE, FALSE);
  1196. }
  1197. // Otherwise, collapse all
  1198. else
  1199. {
  1200. // Collapse Everything
  1201. _CollapseThread(INVALID_ROWINDEX, FALSE);
  1202. }
  1203. }
  1204. // Otherwise, re-expand threads that were expanded and expand newly deferred inserted rows
  1205. else
  1206. {
  1207. // Re-Expand Threads that were expanded...
  1208. _ExpandThread(INVALID_ROWINDEX, FALSE, TRUE);
  1209. }
  1210. }
  1211. exit:
  1212. // Cleanup
  1213. SafeMemFree(Query.pszQuery);
  1214. // Done
  1215. return(hr);
  1216. }
  1217. //--------------------------------------------------------------------------
  1218. // CMessageTable::_BuildTable
  1219. //--------------------------------------------------------------------------
  1220. HRESULT CMessageTable::_BuildTable(IStoreCallback *pCallback)
  1221. {
  1222. // Locals
  1223. HRESULT hr=S_OK;
  1224. LPROWINFO pRow;
  1225. QUERYINFO Query={0};
  1226. DWORD cRecords;
  1227. DWORD cFetched;
  1228. DWORD i;
  1229. DWORD cMessages=0;
  1230. DWORD cUnread=0;
  1231. DWORD cWatched=0;
  1232. DWORD cWatchedUnread=0;
  1233. HROWSET hRowset=NULL;
  1234. LPMESSAGEINFO pMessage;
  1235. MESSAGEINFO rgMessage[ROWSET_FETCH];
  1236. // Trace
  1237. TraceCall("CMessageTable::_BuildTable");
  1238. // Free my current row table
  1239. _FreeTable();
  1240. // Get m_pQuery
  1241. SafeRelease(m_pQuery);
  1242. // Build a Query Object
  1243. if (SUCCEEDED(RuleUtil_HrBuildQuerysFromFilter(m_SortInfo.ridFilter, &Query)) && Query.pszQuery)
  1244. {
  1245. // Get the Query Object
  1246. IF_FAILEXIT(hr = g_pDBSession->OpenQuery(m_pDB, Query.pszQuery, &m_pQuery));
  1247. }
  1248. // Get the Row Count
  1249. IF_FAILEXIT(hr = m_pDB->GetRecordCount(IINDEX_PRIMARY, &cRecords));
  1250. // Do OnBegin
  1251. if (pCallback)
  1252. pCallback->OnBegin(SOT_SORTING, NULL, (IOperationCancel *)this);
  1253. // If Threaded
  1254. if (m_SortInfo.fThreaded)
  1255. {
  1256. // Create a New Hash TAble
  1257. IF_FAILEXIT(hr = MimeOleCreateHashTable(max(1024, cRecords), FALSE, &m_pThreadMsgId));
  1258. // Don't do Subject threading?
  1259. if (DwGetOption(OPT_SUBJECT_THREADING) || (FOLDER_NEWS != m_Folder.tyFolder))
  1260. {
  1261. // Create a Subject Hash Table
  1262. IF_FAILEXIT(hr = MimeOleCreateHashTable(max(1024, cRecords), FALSE, &m_pThreadSubject));
  1263. }
  1264. }
  1265. // Allocate the Row Table
  1266. IF_FAILEXIT(hr = HrAlloc((LPVOID *)&m_prgpRow, sizeof(LPROWINFO) * (cRecords + CGROWTABLE)));
  1267. // Allocate the View Table
  1268. IF_FAILEXIT(hr = HrAlloc((LPVOID *)&m_prgpView, sizeof(LPROWINFO) * (cRecords + CGROWTABLE)));
  1269. // Set m_cAllocated
  1270. m_cAllocated = cRecords + CGROWTABLE;
  1271. // Create a Rowset
  1272. IF_FAILEXIT(hr = m_pDB->CreateRowset(IINDEX_PRIMARY, 0, &hRowset));
  1273. // Walk the Rowset
  1274. while (S_OK == m_pDB->QueryRowset(hRowset, ROWSET_FETCH, (LPVOID *)rgMessage, &cFetched))
  1275. {
  1276. // Loop through the Rows
  1277. for (i=0; i<cFetched; i++)
  1278. {
  1279. // Set pMessage
  1280. pMessage = &rgMessage[i];
  1281. // Count Messages
  1282. cMessages++;
  1283. // Create a Row
  1284. IF_FAILEXIT(hr = _CreateRow(pMessage, &pRow));
  1285. // Unread ?
  1286. if (!ISFLAGSET(pRow->Message.dwFlags, ARF_READ))
  1287. {
  1288. // Increment cUnread
  1289. cUnread++;
  1290. // Watched
  1291. if (ISFLAGSET(pRow->Message.dwFlags, ARF_WATCH))
  1292. cWatchedUnread++;
  1293. }
  1294. // Watched
  1295. if (ISFLAGSET(pRow->Message.dwFlags, ARF_WATCH))
  1296. cWatched++;
  1297. // If not showing repiles
  1298. if (FALSE == m_SortInfo.fShowReplies)
  1299. {
  1300. // Set Filtered Bit
  1301. pRow->fFiltered = _FIsFiltered(pRow);
  1302. // Set Hidden Bit
  1303. pRow->fHidden = _FIsHidden(pRow);
  1304. }
  1305. // If Not Filtered
  1306. if (FALSE == pRow->fFiltered && FALSE == pRow->fHidden)
  1307. {
  1308. // Hash the MessageId
  1309. if (m_SortInfo.fThreaded)
  1310. {
  1311. // Insert Message Id into the hash table
  1312. if (pRow->Message.pszMessageId)
  1313. {
  1314. // Insert It
  1315. m_pThreadMsgId->Insert(pRow->Message.pszMessageId, (LPVOID)pRow, HF_NO_DUPLICATES);
  1316. }
  1317. }
  1318. // Otherwise, add entry to view index
  1319. else
  1320. {
  1321. // Visible
  1322. pRow->fVisible = TRUE;
  1323. // Put into m_prgpView
  1324. m_prgpView[m_cView] = pRow;
  1325. // Increment View Count
  1326. m_cView++;
  1327. }
  1328. // Adjust m_cUnread
  1329. _AdjustUnreadCount(pRow, 1);
  1330. }
  1331. // Otherwise, free the record
  1332. else
  1333. {
  1334. // Count Filtered
  1335. m_cFiltered++;
  1336. }
  1337. // Store the Row
  1338. m_prgpRow[m_cRows] = pRow;
  1339. // Increment Row Count
  1340. m_cRows++;
  1341. }
  1342. // Do OnBegin
  1343. if (pCallback)
  1344. pCallback->OnProgress(SOT_SORTING, m_cRows, cRecords, NULL);
  1345. }
  1346. // Reset the folder count
  1347. m_pFolder->ResetFolderCounts(cMessages, cUnread, cWatchedUnread, cWatched);
  1348. // Sort the Table
  1349. _SortAndThreadTable(TRUE);
  1350. // Threaded
  1351. if (m_SortInfo.fThreaded)
  1352. {
  1353. // Expand All ?
  1354. if (m_SortInfo.fExpandAll)
  1355. {
  1356. // Expand Everything
  1357. _ExpandThread(INVALID_ROWINDEX, FALSE, FALSE);
  1358. }
  1359. // Otherwise, collapse all
  1360. else
  1361. {
  1362. // Collapse Everything
  1363. _CollapseThread(INVALID_ROWINDEX, FALSE);
  1364. }
  1365. }
  1366. // Set Bit to denote that m_fBuiltTable
  1367. m_fLoaded = TRUE;
  1368. exit:
  1369. // Free rgMessage?
  1370. for (; i<cFetched; i++)
  1371. {
  1372. // Free this record
  1373. m_pDB->FreeRecord(&rgMessage[i]);
  1374. }
  1375. // Close the Rowset
  1376. m_pDB->CloseRowset(&hRowset);
  1377. // Cleanup
  1378. SafeMemFree(Query.pszQuery);
  1379. // Do OnBegin
  1380. if (pCallback)
  1381. pCallback->OnComplete(SOT_SORTING, S_OK, NULL, NULL);
  1382. // Done
  1383. return(hr);
  1384. }
  1385. //--------------------------------------------------------------------------
  1386. // CMessageTable::_SortAndThreadTable
  1387. //--------------------------------------------------------------------------
  1388. HRESULT CMessageTable::_SortAndThreadTable(BOOL fApplyFilter)
  1389. {
  1390. // Locals
  1391. DWORD i;
  1392. LPROWINFO pRow;
  1393. // Trace
  1394. TraceCall("CMessageTable::_SortAndThreadTable");
  1395. // If there are rows...
  1396. if (0 == m_cRows)
  1397. goto exit;
  1398. // Threaded
  1399. if (m_SortInfo.fThreaded)
  1400. {
  1401. // Build Thread Roots
  1402. for (i = 0; i < m_cRows; i++)
  1403. {
  1404. // Set pRow
  1405. pRow = m_prgpRow[i];
  1406. // If Not Filtered...
  1407. if (FALSE == pRow->fFiltered && FALSE == pRow->fHidden)
  1408. {
  1409. // Insert this row into a thread...
  1410. if (S_FALSE == _InsertRowIntoThread(pRow, FALSE))
  1411. {
  1412. // Subject Threading ?
  1413. if (m_pThreadSubject && pRow->Message.pszNormalSubj)
  1414. {
  1415. // Insert Subject into Hash Table...
  1416. m_pThreadSubject->Insert(pRow->Message.pszNormalSubj, (LPVOID)pRow, HF_NO_DUPLICATES);
  1417. }
  1418. // Visible
  1419. pRow->fVisible = TRUE;
  1420. // Its a Root
  1421. m_prgpView[m_cView++] = pRow;
  1422. }
  1423. }
  1424. }
  1425. // Show Replies Only ?
  1426. if (fApplyFilter && m_SortInfo.fShowReplies)
  1427. {
  1428. // PruneToReplies
  1429. _PruneToReplies();
  1430. }
  1431. }
  1432. // If there are rows...
  1433. if (0 == m_cView)
  1434. goto exit;
  1435. // Sort the View
  1436. _SortView(0, m_cView - 1);
  1437. // Refresh Filter
  1438. if (fApplyFilter && m_SortInfo.fShowReplies)
  1439. {
  1440. // Refresh Any filter (I have to do this after I've pruned replies
  1441. _RefreshFilter();
  1442. }
  1443. exit:
  1444. // Done
  1445. return(S_OK);
  1446. }
  1447. //--------------------------------------------------------------------------
  1448. // CMessageTable::_PruneToReplies
  1449. //--------------------------------------------------------------------------
  1450. HRESULT CMessageTable::_PruneToReplies(void)
  1451. {
  1452. // Locals
  1453. HRESULT hr=S_OK;
  1454. DWORD iRow;
  1455. LPROWINFO pRow;
  1456. FOLDERINFO Server={0};
  1457. IImnAccount *pAccount=NULL;
  1458. CHAR szEmail[CCHMAX_EMAIL_ADDRESS];
  1459. THREADISFROMME IsFromMe;
  1460. THREADHIDE HideThread={0};
  1461. // Trace
  1462. TraceCall("CMessageTable::_PruneToReplies");
  1463. // Validate
  1464. Assert(FOLDER_NEWS == m_Folder.tyFolder && TRUE == m_SortInfo.fThreaded);
  1465. // Free m_pszEmail
  1466. SafeMemFree(m_pszEmail);
  1467. // Get Folder Store Info
  1468. IF_FAILEXIT(hr = GetFolderStoreInfo(m_Folder.idFolder, &Server));
  1469. // Better have an account id
  1470. Assert(Server.pszAccountId);
  1471. // Find the Account for the id for this Server
  1472. IF_FAILEXIT(hr = g_pAcctMan->FindAccount(AP_ACCOUNT_ID, Server.pszAccountId, &pAccount));
  1473. // Try the NNTP Email Address
  1474. IF_FAILEXIT(hr = pAccount->GetPropSz(AP_NNTP_EMAIL_ADDRESS, szEmail, CCHMAX_EMAIL_ADDRESS));
  1475. // Duplicate szEmail
  1476. IF_NULLEXIT(m_pszEmail = PszDupA(szEmail));
  1477. // Don't notify on hide thread
  1478. HideThread.fNotify = FALSE;
  1479. // Init iRow...
  1480. iRow = 0;
  1481. // Walk through the Roots
  1482. while (iRow < m_cView)
  1483. {
  1484. // Set pRow
  1485. pRow = m_prgpView[iRow];
  1486. // Not a Root ?
  1487. if (NULL == pRow->pParent)
  1488. {
  1489. // Reset
  1490. IsFromMe.fResult = FALSE;
  1491. IsFromMe.pRow = NULL;
  1492. // Find the first message that is from me in this thread...
  1493. _WalkMessageThread(pRow, WALK_THREAD_CURRENT, (DWORD_PTR)&IsFromMe, _WalkThreadIsFromMe);
  1494. // If Not From Me, then hide this thread...
  1495. if (FALSE == IsFromMe.fResult)
  1496. {
  1497. // Find the first message that is from me in this thread...
  1498. _WalkMessageThread(pRow, WALK_THREAD_CURRENT | WALK_THREAD_BOTTOMUP, (DWORD_PTR)&HideThread, _WalkThreadHide);
  1499. }
  1500. // Otherwise, increment iRow
  1501. else
  1502. iRow++;
  1503. }
  1504. // Otherwise, increment iRow
  1505. else
  1506. iRow++;
  1507. }
  1508. exit:
  1509. // Clearnup
  1510. SafeRelease(pAccount);
  1511. g_pStore->FreeRecord(&Server);
  1512. // Done
  1513. return(hr);
  1514. }
  1515. //--------------------------------------------------------------------------
  1516. // CMessageTable::_AdjustUnreadCount
  1517. //--------------------------------------------------------------------------
  1518. HRESULT CMessageTable::_AdjustUnreadCount(LPROWINFO pRow, LONG lCount)
  1519. {
  1520. // Not Filtered
  1521. if (FALSE == pRow->fFiltered && FALSE == pRow->fHidden)
  1522. {
  1523. // Not Read
  1524. if (FALSE == ISFLAGSET(pRow->Message.dwFlags, ARF_READ))
  1525. {
  1526. // Adjust Unread Count
  1527. m_cUnread += lCount;
  1528. }
  1529. }
  1530. // Done
  1531. return(S_OK);
  1532. }
  1533. //--------------------------------------------------------------------------
  1534. // SafeStrCmpI
  1535. //--------------------------------------------------------------------------
  1536. inline SafeStrCmpI(LPCSTR psz1, LPCSTR psz2)
  1537. {
  1538. // Null
  1539. if (NULL == psz1)
  1540. {
  1541. // Equal
  1542. if (NULL == psz2)
  1543. return(0);
  1544. // Less Than
  1545. return(-1);
  1546. }
  1547. // Greater than
  1548. if (NULL == psz2)
  1549. return(1);
  1550. // Return Comparison
  1551. return(lstrcmpi(psz1, psz2));
  1552. }
  1553. //--------------------------------------------------------------------------
  1554. // CMessageTable::_CompareMessages
  1555. //--------------------------------------------------------------------------
  1556. LONG CMessageTable::_CompareMessages(LPMESSAGEINFO pMsg1, LPMESSAGEINFO pMsg2)
  1557. {
  1558. // Locals
  1559. LONG lRet = 0;
  1560. // Trace
  1561. TraceCall("CMessageTable::_CompareMessages");
  1562. switch (m_SortInfo.idColumn)
  1563. {
  1564. case COLUMN_TO:
  1565. lRet = SafeStrCmpI(pMsg1->pszDisplayTo, pMsg2->pszDisplayTo);
  1566. if (0 == lRet)
  1567. {
  1568. lRet = SafeStrCmpI(pMsg1->pszNormalSubj, pMsg2->pszNormalSubj);
  1569. if (0 == lRet)
  1570. lRet = CompareFileTime(&pMsg1->ftSent, &pMsg2->ftSent);
  1571. }
  1572. break;
  1573. case COLUMN_FROM:
  1574. lRet = SafeStrCmpI(pMsg1->pszDisplayFrom, pMsg2->pszDisplayFrom);
  1575. if (0 == lRet)
  1576. {
  1577. lRet = CompareFileTime(&pMsg1->ftSent, &pMsg2->ftSent);
  1578. if (0 == lRet)
  1579. lRet = SafeStrCmpI(pMsg1->pszNormalSubj, pMsg2->pszNormalSubj);
  1580. }
  1581. break;
  1582. case COLUMN_SUBJECT:
  1583. lRet = SafeStrCmpI(pMsg1->pszNormalSubj, pMsg2->pszNormalSubj);
  1584. if (0 == lRet)
  1585. {
  1586. lRet = CompareFileTime(&pMsg1->ftSent, &pMsg2->ftSent);
  1587. if (0 == lRet)
  1588. lRet = SafeStrCmpI(pMsg1->pszDisplayFrom, pMsg2->pszDisplayFrom);
  1589. }
  1590. break;
  1591. case COLUMN_RECEIVED:
  1592. lRet = CompareFileTime(&pMsg1->ftReceived, &pMsg2->ftReceived);
  1593. if (0 == lRet)
  1594. {
  1595. lRet = SafeStrCmpI(pMsg1->pszNormalSubj, pMsg2->pszNormalSubj);
  1596. if (0 == lRet)
  1597. lRet = SafeStrCmpI(pMsg1->pszDisplayFrom, pMsg2->pszDisplayFrom);
  1598. }
  1599. break;
  1600. case COLUMN_SENT:
  1601. lRet = CompareFileTime(&pMsg1->ftSent, &pMsg2->ftSent);
  1602. if (0 == lRet)
  1603. {
  1604. lRet = SafeStrCmpI(pMsg1->pszNormalSubj, pMsg2->pszNormalSubj);
  1605. if (0 == lRet)
  1606. lRet = SafeStrCmpI(pMsg1->pszDisplayFrom, pMsg2->pszDisplayFrom);
  1607. }
  1608. break;
  1609. case COLUMN_SIZE:
  1610. lRet = (pMsg1->cbMessage - pMsg2->cbMessage);
  1611. if (0 == lRet)
  1612. {
  1613. lRet = SafeStrCmpI(pMsg1->pszNormalSubj, pMsg2->pszNormalSubj);
  1614. if (0 == lRet)
  1615. lRet = CompareFileTime(&pMsg1->ftSent, &pMsg2->ftSent);
  1616. }
  1617. break;
  1618. case COLUMN_FOLDER:
  1619. lRet = SafeStrCmpI(pMsg1->pszFolder, pMsg2->pszFolder);
  1620. if (0 == lRet)
  1621. {
  1622. lRet = CompareFileTime(&pMsg1->ftSent, &pMsg2->ftSent);
  1623. if (0 == lRet)
  1624. lRet = SafeStrCmpI(pMsg1->pszDisplayFrom, pMsg2->pszDisplayFrom);
  1625. }
  1626. break;
  1627. case COLUMN_LINES:
  1628. lRet = (pMsg1->cLines - pMsg2->cLines);
  1629. if (0 == lRet)
  1630. {
  1631. lRet = SafeStrCmpI(pMsg1->pszNormalSubj, pMsg2->pszNormalSubj);
  1632. if (0 == lRet)
  1633. lRet = CompareFileTime(&pMsg1->ftSent, &pMsg2->ftSent);
  1634. }
  1635. break;
  1636. case COLUMN_ACCOUNT:
  1637. lRet = SafeStrCmpI(pMsg1->pszAcctName, pMsg2->pszAcctName);
  1638. if (0 == lRet)
  1639. {
  1640. lRet = CompareFileTime(&pMsg1->ftReceived, &pMsg2->ftReceived);
  1641. if (0 == lRet)
  1642. lRet = SafeStrCmpI(pMsg1->pszNormalSubj, pMsg2->pszNormalSubj);
  1643. }
  1644. break;
  1645. case COLUMN_ATTACHMENT:
  1646. lRet = (pMsg1->dwFlags & ARF_HASATTACH) - (pMsg2->dwFlags & ARF_HASATTACH);
  1647. if (0 == lRet)
  1648. {
  1649. lRet = CompareFileTime(&pMsg1->ftSent, &pMsg2->ftSent);
  1650. if (0 == lRet)
  1651. {
  1652. lRet = SafeStrCmpI(pMsg1->pszNormalSubj, pMsg2->pszNormalSubj);
  1653. if (0 == lRet)
  1654. lRet = SafeStrCmpI(pMsg1->pszDisplayFrom, pMsg2->pszDisplayFrom);
  1655. }
  1656. }
  1657. break;
  1658. case COLUMN_PRIORITY:
  1659. lRet = (pMsg1->wPriority - pMsg2->wPriority);
  1660. if (0 == lRet)
  1661. {
  1662. lRet = CompareFileTime(&pMsg1->ftSent, &pMsg2->ftSent);
  1663. if (0 == lRet)
  1664. {
  1665. lRet = SafeStrCmpI(pMsg1->pszNormalSubj, pMsg2->pszNormalSubj);
  1666. if (0 == lRet)
  1667. lRet = SafeStrCmpI(pMsg1->pszDisplayFrom, pMsg2->pszDisplayFrom);
  1668. }
  1669. }
  1670. break;
  1671. case COLUMN_FLAG:
  1672. lRet = (pMsg1->dwFlags & ARF_FLAGGED) - (pMsg2->dwFlags & ARF_FLAGGED);
  1673. if (0 == lRet)
  1674. {
  1675. lRet = CompareFileTime(&pMsg1->ftSent, &pMsg2->ftSent);
  1676. if (0 == lRet)
  1677. {
  1678. lRet = SafeStrCmpI(pMsg1->pszNormalSubj, pMsg2->pszNormalSubj);
  1679. if (0 == lRet)
  1680. lRet = SafeStrCmpI(pMsg1->pszDisplayFrom, pMsg2->pszDisplayFrom);
  1681. }
  1682. }
  1683. break;
  1684. case COLUMN_DOWNLOADMSG:
  1685. lRet = (pMsg1->dwFlags & ARF_DOWNLOAD) - (pMsg2->dwFlags & ARF_DOWNLOAD);
  1686. if (0 == lRet)
  1687. {
  1688. lRet = CompareFileTime(&pMsg1->ftSent, &pMsg2->ftSent);
  1689. if (0 == lRet)
  1690. {
  1691. lRet = SafeStrCmpI(pMsg1->pszNormalSubj, pMsg2->pszNormalSubj);
  1692. if (0 == lRet)
  1693. lRet = SafeStrCmpI(pMsg1->pszDisplayFrom, pMsg2->pszDisplayFrom);
  1694. }
  1695. }
  1696. break;
  1697. case COLUMN_THREADSTATE:
  1698. lRet = (pMsg1->dwFlags & (ARF_WATCH | ARF_IGNORE)) - (pMsg2->dwFlags & (ARF_WATCH | ARF_IGNORE));
  1699. if (0 == lRet)
  1700. {
  1701. lRet = CompareFileTime(&pMsg1->ftSent, &pMsg2->ftSent);
  1702. if (0 == lRet)
  1703. {
  1704. lRet = SafeStrCmpI(pMsg1->pszNormalSubj, pMsg2->pszNormalSubj);
  1705. if (0 == lRet)
  1706. lRet = SafeStrCmpI(pMsg1->pszDisplayFrom, pMsg2->pszDisplayFrom);
  1707. }
  1708. }
  1709. break;
  1710. default:
  1711. Assert(FALSE);
  1712. break;
  1713. }
  1714. // Done
  1715. return (m_SortInfo.fAscending ? lRet : -lRet);
  1716. }
  1717. //--------------------------------------------------------------------------
  1718. // CMessageTable::_SortView
  1719. //--------------------------------------------------------------------------
  1720. VOID CMessageTable::_SortView(LONG left, LONG right)
  1721. {
  1722. // Locals
  1723. register LONG i;
  1724. register LONG j;
  1725. LPROWINFO pRow;
  1726. LPROWINFO y;
  1727. i = left;
  1728. j = right;
  1729. pRow = m_prgpView[(left + right) / 2];
  1730. do
  1731. {
  1732. while (_CompareMessages(&m_prgpView[i]->Message, &pRow->Message) < 0 && i < right)
  1733. i++;
  1734. while (_CompareMessages(&m_prgpView[j]->Message, &pRow->Message) > 0 && j > left)
  1735. j--;
  1736. if (i <= j)
  1737. {
  1738. y = m_prgpView[i];
  1739. m_prgpView[i] = m_prgpView[j];
  1740. m_prgpView[j] = y;
  1741. i++; j--;
  1742. }
  1743. } while (i <= j);
  1744. if (left < j)
  1745. _SortView(left, j);
  1746. if (i < right)
  1747. _SortView(i, right);
  1748. }
  1749. //--------------------------------------------------------------------------
  1750. // CMessageTable::GetCount
  1751. //--------------------------------------------------------------------------
  1752. STDMETHODIMP CMessageTable::GetCount(GETCOUNTTYPE tyCount, DWORD *pcRows)
  1753. {
  1754. // Locals
  1755. HRESULT hr=S_OK;
  1756. FOLDERID idFolder;
  1757. FOLDERINFO Folder;
  1758. // Trace
  1759. TraceCall("CMessageTable::GetCount");
  1760. // Invalid Args
  1761. Assert(pcRows);
  1762. // Validate State
  1763. if (!IsInitialized(this))
  1764. return(TraceResult(E_UNEXPECTED));
  1765. // Initialize
  1766. *pcRows = 0;
  1767. // Get the Folder Id
  1768. IF_FAILEXIT(hr = m_pFolder->GetFolderId(&idFolder));
  1769. // Handle Type
  1770. switch(tyCount)
  1771. {
  1772. case MESSAGE_COUNT_VISIBLE:
  1773. *pcRows = m_cView;
  1774. break;
  1775. case MESSAGE_COUNT_ALL:
  1776. *pcRows = (m_cRows - m_cFiltered);
  1777. break;
  1778. case MESSAGE_COUNT_FILTERED:
  1779. *pcRows = m_cFiltered;
  1780. break;
  1781. case MESSAGE_COUNT_UNREAD:
  1782. *pcRows = m_cUnread;
  1783. break;
  1784. case MESSAGE_COUNT_NOTDOWNLOADED:
  1785. if (SUCCEEDED(g_pStore->GetFolderInfo(idFolder, &Folder)))
  1786. {
  1787. if (Folder.tyFolder == FOLDER_NEWS)
  1788. *pcRows = NewsUtil_GetNotDownloadCount(&Folder);
  1789. g_pStore->FreeRecord(&Folder);
  1790. }
  1791. break;
  1792. default:
  1793. hr = TraceResult(E_INVALIDARG);
  1794. goto exit;
  1795. }
  1796. exit:
  1797. // Done
  1798. return(hr);
  1799. }
  1800. //--------------------------------------------------------------------------
  1801. // CMessageTable::_FreeTable
  1802. //--------------------------------------------------------------------------
  1803. HRESULT CMessageTable::_FreeTable(void)
  1804. {
  1805. // Trace
  1806. TraceCall("CMessageTable::_FreeTable");
  1807. // Free Hash Tables
  1808. SafeRelease(m_pThreadMsgId);
  1809. SafeRelease(m_pThreadSubject);
  1810. // Free Elements
  1811. _FreeTableElements();
  1812. // Fre the Array
  1813. SafeMemFree(m_prgpRow);
  1814. // Free the View Index
  1815. SafeMemFree(m_prgpView);
  1816. // Set m_cAllocated
  1817. m_cFiltered = m_cUnread = m_cRows = m_cView = m_cAllocated = 0;
  1818. // Done
  1819. return(S_OK);
  1820. }
  1821. //--------------------------------------------------------------------------
  1822. // CMessageTable::_FreeTableElements
  1823. //--------------------------------------------------------------------------
  1824. HRESULT CMessageTable::_FreeTableElements(void)
  1825. {
  1826. // Trace
  1827. TraceCall("CMessageTable::_FreeTableElements");
  1828. // If we have an m_prgpRow
  1829. if (m_prgpRow)
  1830. {
  1831. // Free Cache
  1832. for (DWORD i=0; i<m_cRows; i++)
  1833. {
  1834. // Not Null ?
  1835. if (m_prgpRow[i])
  1836. {
  1837. // Release the Row
  1838. ReleaseRow(&m_prgpRow[i]->Message);
  1839. // Null It
  1840. m_prgpRow[i] = NULL;
  1841. }
  1842. }
  1843. }
  1844. // Done
  1845. return(S_OK);
  1846. }
  1847. //--------------------------------------------------------------------------
  1848. // CMessageTable::GetRow
  1849. //--------------------------------------------------------------------------
  1850. STDMETHODIMP CMessageTable::GetRow(ROWINDEX iRow, LPMESSAGEINFO *ppInfo)
  1851. {
  1852. // Locals
  1853. HRESULT hr=S_OK;
  1854. LPROWINFO pRow;
  1855. // Trace
  1856. TraceCall("CMessageTable::GetRow");
  1857. // Invalid Args
  1858. Assert(ppInfo);
  1859. // Validate State
  1860. if (!IsInitialized(this))
  1861. return(TraceResult(E_UNEXPECTED));
  1862. // Initialize
  1863. *ppInfo = NULL;
  1864. // Failure
  1865. hr = _GetRowFromIndex(iRow, &pRow);
  1866. if (FAILED(hr))
  1867. goto exit;
  1868. // Copy the Record to pInfo...
  1869. *ppInfo = &pRow->Message;
  1870. // Increment Refs
  1871. pRow->cRefs++;
  1872. exit:
  1873. // Done
  1874. return(hr);
  1875. }
  1876. //--------------------------------------------------------------------------
  1877. // CMessageTable::ReleaseRow
  1878. //--------------------------------------------------------------------------
  1879. STDMETHODIMP CMessageTable::ReleaseRow(LPMESSAGEINFO pMessage)
  1880. {
  1881. // Locals
  1882. LPROWINFO pRow;
  1883. // Trace
  1884. TraceCall("CMessageTable::ReleaseRow");
  1885. // Validate State
  1886. if (!IsInitialized(this))
  1887. return(TraceResult(E_UNEXPECTED));
  1888. // Release ?
  1889. if (pMessage)
  1890. {
  1891. // Get pRow
  1892. pRow = (LPROWINFO)pMessage->dwReserved;
  1893. // Must have at least one ref
  1894. IxpAssert(pRow->cRefs);
  1895. // Decrement Refs
  1896. pRow->cRefs--;
  1897. // No more refs
  1898. if (0 == pRow->cRefs)
  1899. {
  1900. // Free
  1901. m_pDB->FreeRecord(&pRow->Message);
  1902. // Free pMessage
  1903. m_pDB->HeapFree(pRow);
  1904. }
  1905. }
  1906. // Done
  1907. return(S_OK);
  1908. }
  1909. //--------------------------------------------------------------------------
  1910. // CMessageTable::GetRelativeRow
  1911. //--------------------------------------------------------------------------
  1912. STDMETHODIMP CMessageTable::GetRelativeRow(ROWINDEX iRow, RELATIVEROWTYPE tyRelative,
  1913. LPROWINDEX piRelative)
  1914. {
  1915. // Locals
  1916. HRESULT hr=S_OK;
  1917. LPROWINFO pRow;
  1918. // Trace
  1919. TraceCall("CMessageTable::GetRelativeRow");
  1920. // Invalid Args
  1921. Assert(piRelative);
  1922. // Validate State
  1923. if (!IsInitialized(this))
  1924. return(TraceResult(E_UNEXPECTED));
  1925. // Initialize
  1926. *piRelative = INVALID_ROWINDEX;
  1927. // Failure
  1928. IF_FAILEXIT(hr = _GetRowFromIndex(iRow, &pRow));
  1929. // Parent
  1930. if (RELATIVE_ROW_PARENT == tyRelative)
  1931. {
  1932. // If this row is expanded...
  1933. if (TRUE == pRow->fExpanded)
  1934. {
  1935. // Expand...
  1936. _CollapseThread(iRow, TRUE);
  1937. // Return iRow
  1938. *piRelative = iRow;
  1939. }
  1940. // If there is a Parent
  1941. else if (pRow->pParent)
  1942. {
  1943. // Get Row Index
  1944. IF_FAILEXIT(hr = GetRowIndex(pRow->pParent->Message.idMessage, piRelative));
  1945. }
  1946. }
  1947. // Child
  1948. else if (RELATIVE_ROW_CHILD == tyRelative)
  1949. {
  1950. // If there is a Parent
  1951. if (pRow->pChild)
  1952. {
  1953. // If not Expanded, expand...
  1954. if (FALSE == pRow->fExpanded)
  1955. {
  1956. // Expand...
  1957. _ExpandThread(iRow, TRUE, FALSE);
  1958. // Return iRow
  1959. *piRelative = iRow;
  1960. }
  1961. // Otherwise...
  1962. else
  1963. {
  1964. // Get Row Index
  1965. IF_FAILEXIT(hr = GetRowIndex(pRow->pChild->Message.idMessage, piRelative));
  1966. }
  1967. }
  1968. }
  1969. // Root
  1970. else if (RELATIVE_ROW_ROOT == tyRelative)
  1971. {
  1972. // While
  1973. while (pRow->pParent)
  1974. {
  1975. // Walk to the root
  1976. pRow = pRow->pParent;
  1977. }
  1978. // Get Row Index
  1979. IF_FAILEXIT(hr = GetRowIndex(pRow->Message.idMessage, piRelative));
  1980. }
  1981. // Failure
  1982. else
  1983. {
  1984. hr = TraceResult(E_FAIL);
  1985. goto exit;
  1986. }
  1987. exit:
  1988. // Done
  1989. return(hr);
  1990. }
  1991. //--------------------------------------------------------------------------
  1992. // CMessageTable::GetLanguage
  1993. //--------------------------------------------------------------------------
  1994. STDMETHODIMP CMessageTable::GetLanguage(ROWINDEX iRow, LPDWORD pdwCodePage)
  1995. {
  1996. // Locals
  1997. HRESULT hr=S_OK;
  1998. LPMESSAGEINFO pMessage=NULL;
  1999. // Trace
  2000. TraceCall("CMessageTable::GetLanguage");
  2001. // Invalid Args
  2002. Assert(pdwCodePage);
  2003. // Validate State
  2004. if (!IsInitialized(this))
  2005. return(TraceResult(E_UNEXPECTED));
  2006. // Get the Row
  2007. IF_FAILEXIT(hr = GetRow(iRow, &pMessage));
  2008. // Get the Charset
  2009. *pdwCodePage = pMessage->wLanguage;
  2010. exit:
  2011. // Cleanup
  2012. SafeReleaseRow(this, pMessage);
  2013. // Done
  2014. return(hr);
  2015. }
  2016. //--------------------------------------------------------------------------
  2017. // CMessageTable::SetLanguage
  2018. //--------------------------------------------------------------------------
  2019. STDMETHODIMP CMessageTable::SetLanguage(DWORD cRows, LPROWINDEX prgiRow,
  2020. DWORD dwCodePage)
  2021. {
  2022. // Locals
  2023. HRESULT hr=S_OK;
  2024. DWORD i;
  2025. HLOCK hLock=NULL;
  2026. LPROWINFO pRow;
  2027. // Trace
  2028. TraceCall("CMessageTable::SetLanguage");
  2029. // Validate State
  2030. if (!IsInitialized(this))
  2031. return(TraceResult(E_UNEXPECTED));
  2032. // Lock Notify
  2033. IF_FAILEXIT(hr = m_pDB->Lock(&hLock));
  2034. // Loop
  2035. for (i=0; i<cRows; i++)
  2036. {
  2037. // Get Row
  2038. if (SUCCEEDED(_GetRowFromIndex(prgiRow[i], &pRow)))
  2039. {
  2040. // Set the Language
  2041. pRow->Message.wLanguage = (WORD)dwCodePage;
  2042. // Update the Record
  2043. IF_FAILEXIT(hr = m_pDB->UpdateRecord(&pRow->Message));
  2044. }
  2045. }
  2046. exit:
  2047. // Lock Notify
  2048. m_pDB->Unlock(&hLock);
  2049. // Done
  2050. return(hr);
  2051. }
  2052. //--------------------------------------------------------------------------
  2053. // CMessageTable::OpenMessage
  2054. //--------------------------------------------------------------------------
  2055. STDMETHODIMP CMessageTable::OpenMessage(ROWINDEX iRow, OPENMESSAGEFLAGS dwFlags,
  2056. IMimeMessage **ppMessage, IStoreCallback *pCallback)
  2057. {
  2058. // Locals
  2059. HRESULT hr=S_OK;
  2060. LPMESSAGEINFO pMessage=NULL;
  2061. // Trace
  2062. TraceCall("CMessageTable::GetMessage");
  2063. // Invalid Args
  2064. Assert(ppMessage);
  2065. // Validate State
  2066. if (!IsInitialized(this))
  2067. return(TraceResult(E_UNEXPECTED));
  2068. // Initialize
  2069. *ppMessage = NULL;
  2070. // Get the message info
  2071. IF_FAILEXIT(hr = GetRow(iRow, &pMessage));
  2072. // Open the message
  2073. IF_FAILEXIT(hr = m_pFolder->OpenMessage(pMessage->idMessage, dwFlags, ppMessage, pCallback));
  2074. exit:
  2075. // Clenaup
  2076. SafeReleaseRow(this, pMessage);
  2077. // Done
  2078. return(hr);
  2079. }
  2080. //--------------------------------------------------------------------------
  2081. // CMessageTable::GetRowMessageId
  2082. //--------------------------------------------------------------------------
  2083. STDMETHODIMP CMessageTable::GetRowMessageId(ROWINDEX iRow, LPMESSAGEID pidMessage)
  2084. {
  2085. // Locals
  2086. HRESULT hr=S_OK;
  2087. LPMESSAGEINFO pMessage=NULL;
  2088. // Trace
  2089. TraceCall("CMessageTable::GetRowMessageId");
  2090. // Invalid Args
  2091. Assert(pidMessage);
  2092. // Validate State
  2093. if (!IsInitialized(this))
  2094. return(TraceResult(E_UNEXPECTED));
  2095. // Initialize
  2096. *pidMessage = 0;
  2097. // Get the Row Info
  2098. IF_FAILEXIT(hr = GetRow(iRow, &pMessage));
  2099. // Store the id
  2100. *pidMessage = pMessage->idMessage;
  2101. exit:
  2102. // Free
  2103. SafeReleaseRow(this, pMessage);
  2104. // Done
  2105. return(hr);
  2106. }
  2107. //--------------------------------------------------------------------------
  2108. // CMessageTable::GetRowIndex
  2109. //--------------------------------------------------------------------------
  2110. STDMETHODIMP CMessageTable::GetRowIndex(MESSAGEID idMessage, LPROWINDEX piRow)
  2111. {
  2112. // Locals
  2113. HRESULT hr=S_OK;
  2114. ROWINDEX iRow;
  2115. LPROWINFO pRow;
  2116. // Trace
  2117. TraceCall("CMessageTable::GetRowIndex");
  2118. // Invalid Args
  2119. Assert(idMessage && piRow);
  2120. // Validate State
  2121. if (!IsInitialized(this))
  2122. return(TraceResult(E_UNEXPECTED));
  2123. // INit
  2124. *piRow = INVALID_ROWINDEX;
  2125. // Loop through the view index
  2126. for (iRow=0; iRow<m_cView; iRow++)
  2127. {
  2128. // Is This It ?
  2129. if (m_prgpView[iRow]->Message.idMessage == idMessage)
  2130. {
  2131. // Done
  2132. *piRow = iRow;
  2133. // Done
  2134. goto exit;
  2135. }
  2136. }
  2137. // Not Found
  2138. hr = DB_E_NOTFOUND;
  2139. exit:
  2140. // Done
  2141. return(hr);
  2142. }
  2143. //--------------------------------------------------------------------------
  2144. // CMessageTable::GetIndentLevel
  2145. //--------------------------------------------------------------------------
  2146. STDMETHODIMP CMessageTable::GetIndentLevel(ROWINDEX iRow, LPDWORD pcIndent)
  2147. {
  2148. // Locals
  2149. HRESULT hr=S_OK;
  2150. LPROWINFO pRow;
  2151. // Trace
  2152. TraceCall("CMessageTable::GetIndentLevel");
  2153. // Invalid Args
  2154. Assert(pcIndent);
  2155. // Validate State
  2156. if (!IsInitialized(this))
  2157. return(TraceResult(E_UNEXPECTED));
  2158. // Don't Call Unless Threaded
  2159. Assert(m_SortInfo.fThreaded);
  2160. // Init
  2161. *pcIndent = 0;
  2162. // Valid irow
  2163. IF_FAILEXIT(hr = _GetRowFromIndex(iRow, &pRow));
  2164. // Walk the Parent Chain...
  2165. while (pRow->pParent)
  2166. {
  2167. // Increment Index
  2168. (*pcIndent)++;
  2169. // Set pRow
  2170. pRow = pRow->pParent;
  2171. }
  2172. exit:
  2173. // Done
  2174. return(hr);
  2175. }
  2176. //--------------------------------------------------------------------------
  2177. // CMessageTable::_WalkMessageThread
  2178. //--------------------------------------------------------------------------
  2179. HRESULT CMessageTable::_WalkMessageThread(LPROWINFO pRow, WALKTHREADFLAGS dwFlags,
  2180. DWORD_PTR dwCookie, PFWALKTHREADCALLBACK pfnCallback)
  2181. {
  2182. // Locals
  2183. HRESULT hr=S_OK;
  2184. LPROWINFO pCurrent;
  2185. LPROWINFO pTemp;
  2186. BOOL fCurrent=FALSE;
  2187. // Trace
  2188. TraceCall("CMessageTable::_WalkMessageThread");
  2189. // Invalid Args
  2190. Assert(pfnCallback);
  2191. // Include idMessage ?
  2192. if (ISFLAGSET(dwFlags, WALK_THREAD_CURRENT))
  2193. {
  2194. // This is the first iteration
  2195. fCurrent = TRUE;
  2196. }
  2197. // Don't include current anymore
  2198. FLAGCLEAR(dwFlags, WALK_THREAD_CURRENT);
  2199. // Bottom Up Recursion...
  2200. if (ISFLAGSET(dwFlags, WALK_THREAD_BOTTOMUP))
  2201. {
  2202. // Set iCurrent
  2203. pCurrent = pRow->pChild;
  2204. // Loop
  2205. while (pCurrent)
  2206. {
  2207. // Enumerate Children
  2208. IF_FAILEXIT(hr = _WalkMessageThread(pCurrent, dwFlags, dwCookie, pfnCallback));
  2209. // Set iCurrent
  2210. pTemp = pCurrent->pSibling;
  2211. // Call the Callback
  2212. (*(pfnCallback))(this, pCurrent, dwCookie);
  2213. // Set pCurrent
  2214. pCurrent = pTemp;
  2215. }
  2216. // Can't Support these flags with bottom up...
  2217. if (TRUE == fCurrent)
  2218. {
  2219. // Call the Callback
  2220. (*(pfnCallback))(this, pRow, dwCookie);
  2221. }
  2222. }
  2223. // Otherwise.
  2224. else
  2225. {
  2226. // Include idMessage ?
  2227. if (TRUE == fCurrent)
  2228. {
  2229. // Call the Callback
  2230. (*(pfnCallback))(this, pRow, dwCookie);
  2231. }
  2232. // Set iCurrent
  2233. pCurrent = pRow->pChild;
  2234. // Loop
  2235. while (pCurrent)
  2236. {
  2237. // Call the Callback
  2238. (*(pfnCallback))(this, pCurrent, dwCookie);
  2239. // Enumerate Children
  2240. IF_FAILEXIT(hr = _WalkMessageThread(pCurrent, dwFlags, dwCookie, pfnCallback));
  2241. // Set iCurrent
  2242. pCurrent = pCurrent->pSibling;
  2243. }
  2244. }
  2245. exit:
  2246. // Done
  2247. return(hr);
  2248. }
  2249. //--------------------------------------------------------------------------
  2250. // CMessageTable::GetSelectionState
  2251. //--------------------------------------------------------------------------
  2252. STDMETHODIMP CMessageTable::GetSelectionState(DWORD cRows, LPROWINDEX prgiRow,
  2253. SELECTIONSTATE dwMask, BOOL fIncludeChildren, SELECTIONSTATE *pdwState)
  2254. {
  2255. // Locals
  2256. HRESULT hr=S_OK;
  2257. FOLDERID idFolder;
  2258. FOLDERINFO Folder={0};
  2259. LPROWINFO pRow;
  2260. FOLDERTYPE tyFolder;
  2261. DWORD i;
  2262. GETSELECTIONSTATE Selection={0};
  2263. // Trace
  2264. TraceCall("CMessageTable::GetSelectionState");
  2265. // Validate State
  2266. if (!IsInitialized(this))
  2267. return(TraceResult(E_UNEXPECTED));
  2268. // Initialize
  2269. *pdwState = 0;
  2270. // SELECTION_STATE_DELETABLE
  2271. if (ISFLAGSET(dwMask, SELECTION_STATE_DELETABLE))
  2272. {
  2273. // Not a Find Folder ?
  2274. if (NULL == m_pFindFolder)
  2275. {
  2276. // Get the Folder Id from pidFolder
  2277. IF_FAILEXIT(hr = m_pFolder->GetFolderId(&idFolder));
  2278. // Get Folder Info
  2279. IF_FAILEXIT(hr = g_pStore->GetFolderInfo(idFolder, &Folder));
  2280. // BUGBUG @bug [PaulHi] 4/23/99 This is backwards. The FOLDER_NEWS is the only folder
  2281. // that CAN'T delete messages. The CMessageList::_IsSelectionDeletable() function
  2282. // reverses this so that deletion is available correctly. I don't want to mess with
  2283. // this now, in case other code compensates for this.
  2284. // $HACK$ We know that the only folder types that can delete messages are FOLDER_NEWS
  2285. if (FOLDER_NEWS == Folder.tyFolder)
  2286. {
  2287. // Set the Flag
  2288. FLAGSET(*pdwState, SELECTION_STATE_DELETABLE);
  2289. }
  2290. #if 0
  2291. // [PaulHi] 4/25/99 Only HotMail HTTP servers don't allow deletion of items in the
  2292. // 'deleted' folders. Excehange servers do, so back this fix out.
  2293. // [PaulHi] 4/23/99 Raid 62883.
  2294. if ( (FOLDER_HTTPMAIL == Folder.tyFolder) && (FOLDER_DELETED == Folder.tySpecial) )
  2295. {
  2296. FLAGSET(*pdwState, SELECTION_STATE_DELETABLE); // Not deletable see above @bug comment
  2297. }
  2298. #endif
  2299. }
  2300. // Otherwise, ask the find folder...
  2301. else
  2302. {
  2303. // Setup Selection
  2304. Selection.dwMask = dwMask;
  2305. Selection.dwState = 0;
  2306. // Mark things that are in this folder...
  2307. for (i=0; i<cRows; i++)
  2308. {
  2309. // Good Row Index
  2310. if (SUCCEEDED(_GetRowFromIndex(prgiRow[i], &pRow)))
  2311. {
  2312. // Get the Folder Type
  2313. IF_FAILEXIT(hr = m_pFindFolder->GetMessageFolderType(pRow->Message.idMessage, &tyFolder));
  2314. // Get the State
  2315. if (FOLDER_NEWS == tyFolder)
  2316. {
  2317. // Set the State
  2318. FLAGSET(*pdwState, SELECTION_STATE_DELETABLE);
  2319. // Done
  2320. break;
  2321. }
  2322. // Threaded
  2323. if (m_SortInfo.fThreaded)
  2324. {
  2325. // Do Children ?
  2326. if (fIncludeChildren && !pRow->fExpanded && pRow->pChild)
  2327. {
  2328. // Walk the Thread
  2329. IF_FAILEXIT(hr = _WalkMessageThread(pRow, NOFLAGS, (DWORD_PTR)&Selection, _WalkThreadGetSelectionState));
  2330. // Optimize so that we can finish early
  2331. if (ISFLAGSET(Selection.dwState, SELECTION_STATE_DELETABLE))
  2332. break;
  2333. }
  2334. }
  2335. }
  2336. }
  2337. }
  2338. }
  2339. exit:
  2340. // Free
  2341. g_pStore->FreeRecord(&Folder);
  2342. // Done
  2343. return(hr);
  2344. }
  2345. //--------------------------------------------------------------------------
  2346. // CMessageTable::_IsThreadImportance
  2347. //--------------------------------------------------------------------------
  2348. HRESULT CMessageTable::_IsThreadImportance(LPROWINFO pRow, MESSAGEFLAGS dwFlag,
  2349. ROWSTATE dwState, ROWSTATE *pdwState)
  2350. {
  2351. // Locals
  2352. LPROWINFO pRoot;
  2353. GETTHREADSTATE GetState={0};
  2354. // Trace
  2355. TraceCall("CMessageTable::_IsThreadImportance");
  2356. // Validate
  2357. Assert(ARF_WATCH == dwFlag || ARF_IGNORE == dwFlag);
  2358. // Does this row have the flag set ?
  2359. if (ISFLAGSET(pRow->Message.dwFlags, dwFlag))
  2360. {
  2361. // Set the State
  2362. FLAGSET(*pdwState, dwState);
  2363. // Done
  2364. return(S_OK);
  2365. }
  2366. // Get the Root of this thread
  2367. pRoot = _PGetThreadRoot(pRow);
  2368. // Does this row have the flag set ?
  2369. if (ISFLAGSET(pRoot->Message.dwFlags, dwFlag))
  2370. {
  2371. // Set the State
  2372. FLAGSET(*pdwState, dwState);
  2373. // Done
  2374. return(S_OK);
  2375. }
  2376. // Set Flags to Count
  2377. GetState.dwFlags = dwFlag;
  2378. // Enumerate Immediate Children
  2379. _WalkMessageThread(pRoot, NOFLAGS, (DWORD_PTR)&GetState, _WalkThreadGetState);
  2380. // If This is row is marked as read
  2381. if (GetState.cHasFlags > 0)
  2382. {
  2383. // Set the Bit
  2384. FLAGSET(*pdwState, dwState);
  2385. // Done
  2386. return(S_OK);
  2387. }
  2388. // Not Found
  2389. return(S_FALSE);
  2390. }
  2391. //--------------------------------------------------------------------------
  2392. // CMessageTable::GetRowState
  2393. //--------------------------------------------------------------------------
  2394. STDMETHODIMP CMessageTable::GetRowState(ROWINDEX iRow, ROWSTATE dwStateMask,
  2395. ROWSTATE *pdwState)
  2396. {
  2397. // Locals
  2398. HRESULT hr=S_OK;
  2399. LPROWINFO pRow;
  2400. // Trace
  2401. TraceCall("CMessageTable::GetRowState");
  2402. // Invalid Args
  2403. Assert(pdwState);
  2404. // Validate State
  2405. if (!IsInitialized(this) || iRow >= m_cRows)
  2406. return(E_UNEXPECTED);
  2407. // Initialzie
  2408. *pdwState = 0;
  2409. // Get the row
  2410. IF_FAILEXIT(hr = _GetRowFromIndex(iRow, &pRow));
  2411. // Is the state Cached Yet?
  2412. if (ISFLAGSET(pRow->dwState, ROW_STATE_VALID))
  2413. {
  2414. // Return the State
  2415. *pdwState = pRow->dwState;
  2416. // Done
  2417. return(S_OK);
  2418. }
  2419. // Reset
  2420. pRow->dwState = 0;
  2421. // Get Thread State
  2422. if (m_SortInfo.fThreaded && pRow->pChild && !pRow->fExpanded && ISFLAGSET(pRow->Message.dwFlags, ARF_READ))
  2423. {
  2424. // Locals
  2425. GETTHREADSTATE GetState={0};
  2426. // Set Flags to Count
  2427. GetState.dwFlags = ARF_READ;
  2428. // Enumerate Immediate Children
  2429. _WalkMessageThread(pRow, NOFLAGS, (DWORD_PTR)&GetState, _WalkThreadGetState);
  2430. // If This is row is marked as read
  2431. if (GetState.cHasFlags == GetState.cChildren)
  2432. FLAGSET(pRow->dwState, ROW_STATE_READ);
  2433. }
  2434. // Otherwise, just check the message
  2435. else if (ISFLAGSET(pRow->Message.dwFlags, ARF_READ))
  2436. FLAGSET(pRow->dwState, ROW_STATE_READ);
  2437. // If single watched row
  2438. if (ISFLAGSET(pRow->Message.dwFlags, ARF_WATCH))
  2439. FLAGSET(pRow->dwState, ROW_STATE_WATCHED);
  2440. // If single ignored row
  2441. else if (ISFLAGSET(pRow->Message.dwFlags, ARF_IGNORE))
  2442. FLAGSET(pRow->dwState, ROW_STATE_IGNORED);
  2443. // ROW_STATE_DELETED
  2444. if (ISFLAGSET(pRow->Message.dwFlags, ARF_ENDANGERED) || ISFLAGSET(pRow->Message.dwFlags, ARF_ARTICLE_EXPIRED))
  2445. FLAGSET(pRow->dwState, ROW_STATE_DELETED);
  2446. // ROW_STATE_HAS_BODY
  2447. if (ISFLAGSET(pRow->Message.dwFlags, ARF_HASBODY))
  2448. FLAGSET(pRow->dwState, ROW_STATE_HAS_BODY);
  2449. // ROW_STATE_FLAGGED
  2450. if (ISFLAGSET(pRow->Message.dwFlags, ARF_FLAGGED))
  2451. FLAGSET(pRow->dwState, ROW_STATE_FLAGGED);
  2452. // ROW_STATE_EXPANDED
  2453. if (m_SortInfo.fThreaded && pRow->fExpanded)
  2454. FLAGSET(pRow->dwState, ROW_STATE_EXPANDED);
  2455. // ROW_STATE_HAS_CHILDREN
  2456. if (m_SortInfo.fThreaded && pRow->pChild)
  2457. FLAGSET(pRow->dwState, ROW_STATE_HAS_CHILDREN);
  2458. // ROW_STATE_MARKED_DOWNLOAD
  2459. if (ISFLAGSET(pRow->Message.dwFlags, ARF_DOWNLOAD))
  2460. FLAGSET(pRow->dwState, ROW_STATE_MARKED_DOWNLOAD);
  2461. // Cache the State
  2462. FLAGSET(pRow->dwState, ROW_STATE_VALID);
  2463. // Return the State
  2464. *pdwState = pRow->dwState;
  2465. exit:
  2466. // Done
  2467. return(hr);
  2468. }
  2469. //--------------------------------------------------------------------------
  2470. // CMessageTable::Mark
  2471. //--------------------------------------------------------------------------
  2472. STDMETHODIMP CMessageTable::Mark(LPROWINDEX prgiRow, DWORD cRows,
  2473. APPLYCHILDRENTYPE tyApply, MARK_TYPE tyMark, IStoreCallback *pCallback)
  2474. {
  2475. // Locals
  2476. HRESULT hr=S_OK;
  2477. ULONG i;
  2478. LPMESSAGEINFO pMessage=NULL;
  2479. ADJUSTFLAGS Flags={0};
  2480. MESSAGEIDLIST List={0};
  2481. LPROWINFO pRow;
  2482. HCURSOR hCursor=NULL;
  2483. // Trace
  2484. TraceCall("CMessageTable::Mark");
  2485. // Validate State
  2486. if (!IsInitialized(this))
  2487. return(TraceResult(E_UNEXPECTED));
  2488. // Handle Mark Type
  2489. switch(tyMark)
  2490. {
  2491. case MARK_MESSAGE_READ:
  2492. Flags.dwAdd = ARF_READ;
  2493. break;
  2494. case MARK_MESSAGE_UNREAD:
  2495. Flags.dwRemove = ARF_READ;
  2496. break;
  2497. case MARK_MESSAGE_DELETED:
  2498. Flags.dwAdd = ARF_ENDANGERED;
  2499. break;
  2500. case MARK_MESSAGE_UNDELETED:
  2501. Flags.dwRemove = ARF_ENDANGERED;
  2502. break;
  2503. case MARK_MESSAGE_DOWNLOAD:
  2504. Flags.dwAdd = ARF_DOWNLOAD;
  2505. break;
  2506. case MARK_MESSAGE_UNDOWNLOAD:
  2507. Flags.dwRemove = ARF_DOWNLOAD;
  2508. break;
  2509. case MARK_MESSAGE_FLAGGED:
  2510. Flags.dwAdd = ARF_FLAGGED;
  2511. break;
  2512. case MARK_MESSAGE_UNFLAGGED:
  2513. Flags.dwRemove = ARF_FLAGGED;
  2514. break;
  2515. case MARK_MESSAGE_FORWARDED:
  2516. Flags.dwAdd = ARF_FORWARDED;
  2517. break;
  2518. case MARK_MESSAGE_UNFORWARDED:
  2519. Flags.dwRemove = ARF_FORWARDED;
  2520. break;
  2521. case MARK_MESSAGE_REPLIED:
  2522. Flags.dwAdd = ARF_REPLIED;
  2523. break;
  2524. case MARK_MESSAGE_UNREPLIED:
  2525. Flags.dwRemove = ARF_REPLIED;
  2526. break;
  2527. case MARK_MESSAGE_NOSECUI:
  2528. Flags.dwAdd = ARF_NOSECUI;
  2529. break;
  2530. case MARK_MESSAGE_SECUI:
  2531. Flags.dwRemove = ARF_NOSECUI;
  2532. break;
  2533. case MARK_MESSAGE_WATCH:
  2534. Flags.dwAdd = ARF_WATCH;
  2535. Flags.dwRemove = ARF_IGNORE;
  2536. break;
  2537. case MARK_MESSAGE_IGNORE:
  2538. Flags.dwAdd = ARF_IGNORE;
  2539. Flags.dwRemove = ARF_WATCH;
  2540. break;
  2541. case MARK_MESSAGE_NORMALTHREAD:
  2542. Flags.dwRemove = ARF_WATCH | ARF_IGNORE;
  2543. break;
  2544. case MARK_MESSAGE_RCPT_PROCESSED:
  2545. Flags.dwAdd = ARF_RCPT_PROCESSED;
  2546. break;
  2547. default:
  2548. Assert(FALSE);
  2549. hr = TraceResult(E_INVALIDARG);
  2550. goto exit;
  2551. }
  2552. // Wait Cursor
  2553. hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
  2554. // Not Mark All...
  2555. if (prgiRow && cRows)
  2556. {
  2557. // Allocate an Array
  2558. IF_FAILEXIT(hr = _GrowIdList(&List, cRows + 32));
  2559. // Mark things that are in this folder...
  2560. for (i=0; i<cRows; i++)
  2561. {
  2562. // Valid Row Index
  2563. if (SUCCEEDED(_GetRowFromIndex(prgiRow[i], &pRow)))
  2564. {
  2565. // Allocate an Array
  2566. IF_FAILEXIT(hr = _GrowIdList(&List, 1));
  2567. // Set id
  2568. List.prgidMsg[List.cMsgs++] = pRow->Message.idMessage;
  2569. // Do the children
  2570. if (APPLY_CHILDREN == tyApply || (APPLY_COLLAPSED == tyApply && !pRow->fExpanded))
  2571. {
  2572. // Only if there are children
  2573. if (pRow->pChild)
  2574. {
  2575. // Walk the Thread
  2576. IF_FAILEXIT(hr = _WalkMessageThread(pRow, NOFLAGS, (DWORD_PTR)&List, _WalkThreadGetIdList));
  2577. }
  2578. }
  2579. }
  2580. }
  2581. // Are there messages
  2582. if (List.cMsgs > 0)
  2583. {
  2584. // Adjust the Flags
  2585. IF_FAILEXIT(hr = m_pFolder->SetMessageFlags(&List, &Flags, NULL, pCallback));
  2586. }
  2587. }
  2588. // Mark All
  2589. else
  2590. {
  2591. // Adjust the Flags
  2592. IF_FAILEXIT(hr = m_pFolder->SetMessageFlags(NULL, &Flags, NULL, pCallback));
  2593. }
  2594. // Re-Register for notifications
  2595. m_pDB->DispatchNotify((IDatabaseNotify *)this);
  2596. exit:
  2597. // Reset Cursor
  2598. SetCursor(hCursor);
  2599. // Cleanup
  2600. SafeMemFree(List.prgidMsg);
  2601. // Done
  2602. return(hr);
  2603. }
  2604. //--------------------------------------------------------------------------
  2605. // CMessageTable::ConnectionRelease
  2606. //--------------------------------------------------------------------------
  2607. HRESULT CMessageTable::ConnectionAddRef(void)
  2608. {
  2609. if (m_pFolder)
  2610. m_pFolder->ConnectionAddRef();
  2611. return S_OK;
  2612. }
  2613. //--------------------------------------------------------------------------
  2614. // CMessageTable::ConnectionRelease
  2615. //--------------------------------------------------------------------------
  2616. HRESULT CMessageTable::ConnectionRelease(void)
  2617. {
  2618. if (m_pFolder)
  2619. m_pFolder->ConnectionRelease();
  2620. return S_OK;
  2621. }
  2622. //--------------------------------------------------------------------------
  2623. // CMessageTable::Synchronize
  2624. //--------------------------------------------------------------------------
  2625. STDMETHODIMP CMessageTable::Synchronize(SYNCFOLDERFLAGS dwFlags,
  2626. DWORD cHeaders,
  2627. IStoreCallback *pCallback)
  2628. {
  2629. // Locals
  2630. HRESULT hr=S_OK;
  2631. // Trace
  2632. TraceCall("CMessageTable::Synchronize");
  2633. // Validate State
  2634. if (!IsInitialized(this))
  2635. return(TraceResult(E_UNEXPECTED));
  2636. // Tell the Folder to Synch
  2637. hr = m_pFolder->Synchronize(dwFlags, cHeaders, pCallback);
  2638. // Success
  2639. if (E_PENDING == hr)
  2640. {
  2641. // We are synching
  2642. m_fSynching = TRUE;
  2643. }
  2644. // Done
  2645. return(hr);
  2646. }
  2647. //--------------------------------------------------------------------------
  2648. // CMessageTable::SetOwner
  2649. //--------------------------------------------------------------------------
  2650. STDMETHODIMP CMessageTable::SetOwner(IStoreCallback *pDefaultCallback)
  2651. {
  2652. // Locals
  2653. HRESULT hr=S_OK;
  2654. // Trace
  2655. TraceCall("CMessageTable::SetOwner");
  2656. // Validate State
  2657. if (!IsInitialized(this))
  2658. return(TraceResult(E_UNEXPECTED));
  2659. // Set the Owner
  2660. hr = m_pFolder->SetOwner(pDefaultCallback);
  2661. if (FAILED(hr))
  2662. goto exit;
  2663. exit:
  2664. // Done
  2665. return(hr);
  2666. }
  2667. //--------------------------------------------------------------------------
  2668. // CMessageTable::Close
  2669. //--------------------------------------------------------------------------
  2670. STDMETHODIMP CMessageTable::Close(void)
  2671. {
  2672. // Locals
  2673. HRESULT hr = S_OK;
  2674. //Trace
  2675. TraceCall("CMessageTable::Close");
  2676. // Pass it on
  2677. if (m_pFolder)
  2678. hr = m_pFolder->Close();
  2679. // Done
  2680. return hr;
  2681. }
  2682. //--------------------------------------------------------------------------
  2683. // CMessageTable::GetRowFolderId
  2684. //--------------------------------------------------------------------------
  2685. STDMETHODIMP CMessageTable::GetRowFolderId(ROWINDEX iRow, LPFOLDERID pidFolder)
  2686. {
  2687. // Locals
  2688. HRESULT hr=S_OK;
  2689. LPMESSAGEINFO pMessage=NULL;
  2690. // Trace
  2691. TraceCall("CMessageTable::GetRowFolderId");
  2692. // Validate State
  2693. if (!IsInitialized(this))
  2694. return(TraceResult(E_UNEXPECTED));
  2695. // Not a Find Folder ?
  2696. if (NULL == m_pFindFolder)
  2697. {
  2698. // Get the Folder Id from pidFolder
  2699. IF_FAILEXIT(hr = m_pFolder->GetFolderId(pidFolder));
  2700. }
  2701. // Otherwise, ask the find folder...
  2702. else
  2703. {
  2704. // Get the Row
  2705. IF_FAILEXIT(hr = GetRow(iRow, &pMessage));
  2706. // Call into the find folder
  2707. IF_FAILEXIT(hr = m_pFindFolder->GetMessageFolderId(pMessage->idMessage, pidFolder));
  2708. }
  2709. exit:
  2710. // Free the Row
  2711. SafeReleaseRow(this, pMessage);
  2712. // Done
  2713. return(hr);
  2714. }
  2715. //--------------------------------------------------------------------------
  2716. // CMessageTable::RegisterNotify
  2717. //--------------------------------------------------------------------------
  2718. STDMETHODIMP CMessageTable::RegisterNotify(REGISTERNOTIFYFLAGS dwFlags,
  2719. IMessageTableNotify *pNotify)
  2720. {
  2721. // Trace
  2722. TraceCall("CMessageTable::RegisterNotify");
  2723. // Invalid Args
  2724. if (NULL == pNotify)
  2725. return TraceResult(E_INVALIDARG);
  2726. // Only One is allowed
  2727. AssertSz(NULL == m_pNotify, "Only one person can register for notifications on my object");
  2728. // Save It
  2729. m_pNotify = pNotify;
  2730. // No Release
  2731. m_fRelNotify = FALSE;
  2732. // AddRef ?
  2733. if (FALSE == ISFLAGSET(dwFlags, REGISTER_NOTIFY_NOADDREF))
  2734. {
  2735. m_pNotify->AddRef();
  2736. m_fRelNotify = TRUE;
  2737. }
  2738. // Done
  2739. return(S_OK);
  2740. }
  2741. //--------------------------------------------------------------------------
  2742. // CMessageTable::UnregisterNotify
  2743. //--------------------------------------------------------------------------
  2744. STDMETHODIMP CMessageTable::UnregisterNotify(IMessageTableNotify *pNotify)
  2745. {
  2746. // Trace
  2747. TraceCall("CMessageTable::UnregisterNotify");
  2748. // Invalid Args
  2749. if (NULL == pNotify)
  2750. return TraceResult(E_INVALIDARG);
  2751. // Otherwise, remove
  2752. if (m_pNotify)
  2753. {
  2754. // Validate
  2755. Assert(m_pNotify == pNotify);
  2756. // Release It
  2757. if (m_fRelNotify)
  2758. m_pNotify->Release();
  2759. m_pNotify = NULL;
  2760. }
  2761. // Done
  2762. return(S_OK);
  2763. }
  2764. //--------------------------------------------------------------------------
  2765. // CMessageTable::GetNextRow
  2766. //--------------------------------------------------------------------------
  2767. STDMETHODIMP CMessageTable::GetNextRow(ROWINDEX iCurrentRow,
  2768. GETNEXTTYPE tyDirection, ROWMESSAGETYPE tyMessage, GETNEXTFLAGS dwFlags,
  2769. LPROWINDEX piNewRow)
  2770. {
  2771. // Locals
  2772. HRESULT hr=S_OK;
  2773. ROWINDEX iRow=iCurrentRow;
  2774. ROWINDEX iStartRow=iCurrentRow;
  2775. BOOL fWrapAround=FALSE;
  2776. BYTE fThreadHasUnread;
  2777. LPROWINFO pRow;
  2778. // Trace
  2779. TraceCall("CMessageTable::GetNextRow");
  2780. // Invalid Args
  2781. Assert(piNewRow);
  2782. // Validate State
  2783. if (!IsInitialized(this) || iCurrentRow >= m_cView)
  2784. return(TraceResult(E_UNEXPECTED));
  2785. // Initialize
  2786. *piNewRow = INVALID_ROWINDEX;
  2787. // Loop
  2788. while (1)
  2789. {
  2790. // Threaded
  2791. if (m_SortInfo.fThreaded)
  2792. {
  2793. // Get pRow
  2794. IF_FAILEXIT(hr = _GetRowFromIndex(iRow, &pRow));
  2795. // If not expanded
  2796. if (FALSE == pRow->fExpanded && pRow->pChild)
  2797. {
  2798. // Imay need to expand this row...
  2799. if (ROWMSG_ALL == tyMessage || (ROWMSG_NEWS == tyMessage && ISFLAGSET(pRow->Message.dwFlags, ARF_NEWSMSG)) || (ROWMSG_MAIL == tyMessage && !ISFLAGSET(pRow->Message.dwFlags, ARF_NEWSMSG)))
  2800. {
  2801. // If looking for unread, see if the thread has unread messages in it
  2802. if (ISFLAGSET(dwFlags, GETNEXT_UNREAD) && !ISFLAGSET(dwFlags, GETNEXT_THREAD))
  2803. {
  2804. // Locals
  2805. GETTHREADSTATE GetState={0};
  2806. // Set Flags to Count
  2807. GetState.dwFlags = ARF_READ;
  2808. // Root that isn't totally read...
  2809. _WalkMessageThread(pRow, NOFLAGS, (DWORD_PTR)&GetState, _WalkThreadGetState);
  2810. // If there are unread children of this
  2811. if (GetState.cHasFlags != GetState.cChildren)
  2812. {
  2813. // Expand This thread
  2814. _ExpandThread(iRow, TRUE, FALSE);
  2815. }
  2816. }
  2817. }
  2818. }
  2819. }
  2820. // Next ?
  2821. if (GETNEXT_NEXT == tyDirection)
  2822. {
  2823. // Increment
  2824. iRow++;
  2825. // Start back at zero
  2826. if (iRow >= m_cView)
  2827. {
  2828. // Done
  2829. if (!ISFLAGSET(dwFlags, GETNEXT_UNREAD))
  2830. {
  2831. hr = E_FAIL;
  2832. goto exit;
  2833. }
  2834. // We Wrapped Around
  2835. fWrapAround = TRUE;
  2836. // Start back at zero
  2837. iRow = 0;
  2838. }
  2839. }
  2840. // Otherwise, backwards
  2841. else
  2842. {
  2843. // Start back at zero
  2844. if (0 == iRow)
  2845. {
  2846. // Done
  2847. if (!ISFLAGSET(dwFlags, GETNEXT_UNREAD))
  2848. {
  2849. hr = E_FAIL;
  2850. goto exit;
  2851. }
  2852. // We Wrapped Around
  2853. fWrapAround = TRUE;
  2854. // Start back at zero
  2855. iRow = m_cView - 1;
  2856. }
  2857. // Otherwise, decrement iRow
  2858. else
  2859. iRow--;
  2860. }
  2861. // Wrapped and back to original row
  2862. if (fWrapAround && iRow == iStartRow)
  2863. break;
  2864. // Validate iRow
  2865. Assert(iRow < m_cView);
  2866. // Get pRow
  2867. IF_FAILEXIT(hr = _GetRowFromIndex(iRow, &pRow));
  2868. // Good time to Stop ?
  2869. if (ROWMSG_ALL == tyMessage || (ROWMSG_NEWS == tyMessage && ISFLAGSET(pRow->Message.dwFlags, ARF_NEWSMSG)) || (ROWMSG_MAIL == tyMessage && !ISFLAGSET(pRow->Message.dwFlags, ARF_NEWSMSG)))
  2870. {
  2871. // Set fThreadHasUnread
  2872. fThreadHasUnread = FALSE;
  2873. // If looking for unread, see if the thread has unread messages in it
  2874. if (ISFLAGSET(dwFlags, GETNEXT_UNREAD))
  2875. {
  2876. // Locals
  2877. GETTHREADSTATE GetState={0};
  2878. // Set Flags to Count
  2879. GetState.dwFlags = ARF_READ;
  2880. // Root that isn't totally read...
  2881. _WalkMessageThread(pRow, NOFLAGS, (DWORD_PTR)&GetState, _WalkThreadGetState);
  2882. // If there are unread children of this
  2883. if (GetState.cHasFlags != GetState.cChildren)
  2884. {
  2885. // This thread has unread stuff
  2886. fThreadHasUnread = TRUE;
  2887. }
  2888. }
  2889. // Looking for the next thread with unread messages in it
  2890. if (ISFLAGSET(dwFlags, GETNEXT_THREAD) && ISFLAGSET(dwFlags, GETNEXT_UNREAD))
  2891. {
  2892. // If this is a root thread...
  2893. if (NULL == pRow->pParent)
  2894. {
  2895. // If this row is unread
  2896. if (!ISFLAGSET(pRow->Message.dwFlags, ARF_READ))
  2897. {
  2898. // This is It
  2899. *piNewRow = iRow;
  2900. // Done
  2901. goto exit;
  2902. }
  2903. // Otherwise...
  2904. else if (fThreadHasUnread)
  2905. {
  2906. // This is It
  2907. *piNewRow = iRow;
  2908. // Done
  2909. goto exit;
  2910. }
  2911. }
  2912. }
  2913. // Looking for a thread root
  2914. else if (ISFLAGSET(dwFlags, GETNEXT_THREAD) && !ISFLAGSET(dwFlags, GETNEXT_UNREAD))
  2915. {
  2916. // If this is a root thread...
  2917. if (NULL == pRow->pParent)
  2918. {
  2919. // This is It
  2920. *piNewRow = iRow;
  2921. // Done
  2922. goto exit;
  2923. }
  2924. }
  2925. // Looking for the next unread message
  2926. else if (!ISFLAGSET(dwFlags, GETNEXT_THREAD) && ISFLAGSET(dwFlags, GETNEXT_UNREAD))
  2927. {
  2928. // If this is a thread that has unread children, then expand It.
  2929. if (m_SortInfo.fThreaded && FALSE == pRow->fExpanded && pRow->pChild && fThreadHasUnread)
  2930. {
  2931. // Expand This thread
  2932. _ExpandThread(iRow, TRUE, FALSE);
  2933. }
  2934. // If this is a root thread...
  2935. if (FALSE == ISFLAGSET(pRow->Message.dwFlags, ARF_READ))
  2936. {
  2937. // This is It
  2938. *piNewRow = iRow;
  2939. // Done
  2940. goto exit;
  2941. }
  2942. }
  2943. // Otherwise, this is it
  2944. else
  2945. {
  2946. // This is It
  2947. *piNewRow = iRow;
  2948. // Done
  2949. goto exit;
  2950. }
  2951. }
  2952. }
  2953. exit:
  2954. // Done
  2955. return(hr);
  2956. }
  2957. //--------------------------------------------------------------------------
  2958. // CMessageTable::GetMessageIdList
  2959. //--------------------------------------------------------------------------
  2960. STDMETHODIMP CMessageTable::GetMessageIdList(BOOL fRootsOnly, DWORD cRows,
  2961. LPROWINDEX prgiRow, LPMESSAGEIDLIST pIdList)
  2962. {
  2963. // Locals
  2964. HRESULT hr=S_OK;
  2965. DWORD i;
  2966. LPROWINFO pRow;
  2967. // Trace
  2968. TraceCall("CMessageTable::GetMessageIdList");
  2969. // Validate State
  2970. if (!IsInitialized(this))
  2971. return(TraceResult(E_UNEXPECTED));
  2972. // Initialize
  2973. ZeroMemory(pIdList, sizeof(MESSAGEIDLIST));
  2974. // Allocate an Array
  2975. IF_FAILEXIT(hr = _GrowIdList(pIdList, cRows + 32));
  2976. // Mark things that are in this folder...
  2977. for (i=0; i<cRows; i++)
  2978. {
  2979. // Good View Index
  2980. if (SUCCEEDED(_GetRowFromIndex(prgiRow[i], &pRow)))
  2981. {
  2982. // _GrowIdList
  2983. IF_FAILEXIT(hr = _GrowIdList(pIdList, 1));
  2984. // Set id
  2985. pIdList->prgidMsg[pIdList->cMsgs++] = pRow->Message.idMessage;
  2986. // If Not Expanded and Has children, insert the children...
  2987. if (!fRootsOnly && m_SortInfo.fThreaded && !pRow->fExpanded && pRow->pChild)
  2988. {
  2989. // Walk the Thread
  2990. IF_FAILEXIT(hr = _WalkMessageThread(pRow, NOFLAGS, (DWORD_PTR)pIdList, _WalkThreadGetIdList));
  2991. }
  2992. }
  2993. }
  2994. exit:
  2995. // Done
  2996. return(hr);
  2997. }
  2998. #if 0
  2999. //--------------------------------------------------------------------------
  3000. // CMessageTable::_GetRowOrdinal
  3001. //--------------------------------------------------------------------------
  3002. HRESULT CMessageTable::_GetRowOrdinal(MESSAGEID idMessage, LPROWORDINAL piOrdinal)
  3003. {
  3004. // Locals
  3005. LONG lLower=0;
  3006. LONG lUpper=m_cRows - 1;
  3007. LONG lCompare;
  3008. DWORD dwMiddle;
  3009. LPROWINFO pRow;
  3010. // Do binary search / insert
  3011. while (lLower <= lUpper)
  3012. {
  3013. // Set lMiddle
  3014. dwMiddle = (DWORD)((lLower + lUpper) / 2);
  3015. // Compute middle record to compare against
  3016. pRow = m_prgpRow[dwMiddle];
  3017. // Get string to compare against
  3018. lCompare = ((DWORD)idMessage - (DWORD)pRow->Message.idMessage);
  3019. // If Equal, then were done
  3020. if (lCompare == 0)
  3021. {
  3022. *piOrdinal = dwMiddle;
  3023. return(S_OK);
  3024. }
  3025. // Compute upper and lower
  3026. if (lCompare > 0)
  3027. lLower = (LONG)(dwMiddle + 1);
  3028. else
  3029. lUpper = (LONG)(dwMiddle - 1);
  3030. }
  3031. // Not Found
  3032. return(TraceResult(DB_E_NOTFOUND));
  3033. }
  3034. //--------------------------------------------------------------------------
  3035. // CMessageTable::_ProcessResults
  3036. //--------------------------------------------------------------------------
  3037. HRESULT CMessageTable::_ProcessResults(TRANSACTIONTYPE tyTransaction,
  3038. DWORD cRows, LPROWINDEX prgiRow, LPRESULTLIST pResults)
  3039. {
  3040. // Locals
  3041. DWORD i;
  3042. ROWORDINAL iOrdinal;
  3043. LPROWINFO pRow;
  3044. // Trace
  3045. TraceCall("CMessageTable::_ProcessResults");
  3046. // Validate
  3047. Assert(TRANSACTION_UPDATE == tyTransaction || TRANSACTION_DELETE == tyTransaction);
  3048. // Another Validation
  3049. Assert(cRows == pResults->cMsgs);
  3050. // No Results
  3051. if (NULL == pResults || NULL == pResults->prgResult)
  3052. return(S_OK);
  3053. // Do Row Updates Myself...
  3054. for (i=0; i<pResults->cValid; i++)
  3055. {
  3056. // If this row was deleted
  3057. if (S_OK == pResults->prgResult[i].hrResult)
  3058. {
  3059. // Get Row From Index
  3060. if (SUCCEEDED(_GetRowFromIndex(prgiRow[i], &pRow)))
  3061. {
  3062. // Validate
  3063. Assert(pResults->prgResult[i].idMessage == pRow->Message.idMessage);
  3064. // Find the Row Ordinal
  3065. SideAssert(SUCCEEDED(_GetRowOrdinal(pRow->Message.idMessage, &iOrdinal)));
  3066. // We better have found it
  3067. Assert(iOrdinal < m_cRows);
  3068. // Update
  3069. else if (TRANSACTION_UPDATE == tyTransaction)
  3070. {
  3071. // Get pRow
  3072. _RowTableUpdate(iOrdinal, &pRow->Message, &pResults->prgResult[i]);
  3073. }
  3074. }
  3075. }
  3076. }
  3077. // Flush Notification Queue
  3078. _FlushNotificationQueue(TRUE);
  3079. // Done
  3080. return(S_OK);
  3081. }
  3082. #endif
  3083. //--------------------------------------------------------------------------
  3084. // CMessageTable::DeleteRows
  3085. //--------------------------------------------------------------------------
  3086. STDMETHODIMP CMessageTable::DeleteRows(DELETEMESSAGEFLAGS dwFlags, DWORD cRows,
  3087. LPROWINDEX prgiRow, BOOL fIncludeChildren, IStoreCallback *pCallback)
  3088. {
  3089. // Locals
  3090. HRESULT hr=S_OK;
  3091. MESSAGEIDLIST List={0};
  3092. HCURSOR hCursor=NULL;
  3093. // Trace
  3094. TraceCall("CMessageTable::DeleteRows");
  3095. // Validate State
  3096. if (!IsInitialized(this))
  3097. return(TraceResult(E_UNEXPECTED));
  3098. // Wait Cursor
  3099. hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
  3100. // Get MessageID List
  3101. IF_FAILEXIT(hr = GetMessageIdList((FALSE == fIncludeChildren), cRows, prgiRow, &List));
  3102. // Adjust the Flags
  3103. IF_FAILEXIT(hr = m_pFolder->DeleteMessages(dwFlags, &List, NULL, pCallback));
  3104. // Re-Register for notifications
  3105. m_pDB->DispatchNotify((IDatabaseNotify *)this);
  3106. exit:
  3107. // Reset Cursor
  3108. SetCursor(hCursor);
  3109. // Cleanup
  3110. SafeMemFree(List.prgidMsg);
  3111. // Done
  3112. return(hr);
  3113. }
  3114. //--------------------------------------------------------------------------
  3115. // CMessageTable::CopyRows
  3116. //--------------------------------------------------------------------------
  3117. STDMETHODIMP CMessageTable::CopyRows(FOLDERID idFolder,
  3118. COPYMESSAGEFLAGS dwOptions, DWORD cRows, LPROWINDEX prgiRow,
  3119. LPADJUSTFLAGS pFlags, IStoreCallback *pCallback)
  3120. {
  3121. // Locals
  3122. HRESULT hr=S_OK;
  3123. MESSAGEIDLIST List={0};
  3124. HCURSOR hCursor=NULL;
  3125. IMessageFolder *pDstFolder=NULL;
  3126. // Trace
  3127. TraceCall("CMessageTable::CopyRows");
  3128. // Validate State
  3129. if (!IsInitialized(this))
  3130. return(TraceResult(E_UNEXPECTED));
  3131. // Wait Cursor
  3132. hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
  3133. // Open the Destination Folder
  3134. IF_FAILEXIT(hr = g_pStore->OpenFolder(idFolder, NULL, NOFLAGS, &pDstFolder));
  3135. // Get MessageID List
  3136. IF_FAILEXIT(hr = GetMessageIdList(FALSE, cRows, prgiRow, &List));
  3137. // Adjust the Flags
  3138. IF_FAILEXIT(hr = m_pFolder->CopyMessages(pDstFolder, dwOptions, &List, pFlags, NULL, pCallback));
  3139. // Re-Register for notifications
  3140. m_pDB->DispatchNotify((IDatabaseNotify *)this);
  3141. exit:
  3142. // Reset Cursor
  3143. SetCursor(hCursor);
  3144. // Cleanup
  3145. SafeRelease(pDstFolder);
  3146. SafeMemFree(List.prgidMsg);
  3147. // Done
  3148. return(hr);
  3149. }
  3150. //--------------------------------------------------------------------------
  3151. // CMessageTable::QueryService
  3152. //--------------------------------------------------------------------------
  3153. HRESULT CMessageTable::QueryService(REFGUID guidService, REFIID riid, LPVOID *ppvObject)
  3154. {
  3155. // Locals
  3156. HRESULT hr=E_NOINTERFACE;
  3157. IServiceProvider *pSP;
  3158. // Trace
  3159. TraceCall("CMessageTable::QueryService");
  3160. // Validate State
  3161. if (!IsInitialized(this))
  3162. return(TraceResult(E_UNEXPECTED));
  3163. // Currently the msgtable doesn't expose any objects, but will delegate to the folder to see if it can handle it
  3164. if (guidService == IID_IMessageFolder)
  3165. {
  3166. if (m_pFolder)
  3167. hr = m_pFolder->QueryInterface(riid, ppvObject);
  3168. }
  3169. else if (m_pFolder && m_pFolder->QueryInterface(IID_IServiceProvider, (LPVOID *)&pSP) == S_OK)
  3170. {
  3171. // Query Service This
  3172. hr = pSP->QueryService(guidService, riid, ppvObject);
  3173. // Release It
  3174. pSP->Release();
  3175. }
  3176. // Done
  3177. return(hr);
  3178. }
  3179. //--------------------------------------------------------------------------
  3180. // CMessageTable::FindNextRow
  3181. //--------------------------------------------------------------------------
  3182. HRESULT CMessageTable::FindNextRow(ROWINDEX iStartRow, LPCTSTR pszFindString,
  3183. FINDNEXTFLAGS dwFlags, BOOL fIncludeBody, ROWINDEX *piNextRow, BOOL *pfWrapped)
  3184. {
  3185. // Locals
  3186. HRESULT hr=S_OK;
  3187. LPMESSAGEINFO pMessage=NULL;
  3188. ROWINDEX iCurrent;
  3189. DWORD cchFindString;
  3190. BOOL fWrapAround=FALSE;
  3191. HLOCK hLock=NULL;
  3192. // Trace
  3193. TraceCall("CMessageTable::QueryService");
  3194. // Invalid Args
  3195. Assert(pszFindString && piNextRow);
  3196. // Validate State
  3197. if (!IsInitialized(this))
  3198. return(TraceResult(E_UNEXPECTED));
  3199. // Initialize
  3200. *piNextRow = -1;
  3201. if (pfWrapped)
  3202. *pfWrapped = FALSE;
  3203. // Get Prefix Length
  3204. cchFindString = lstrlen(pszFindString);
  3205. // Lock the Folder
  3206. IF_FAILEXIT(hr = m_pDB->Lock(&hLock));
  3207. // Set iCurrent
  3208. iCurrent = iStartRow >= m_cRows ? 0 : iStartRow;
  3209. // COLUMN_TO
  3210. if (FINDNEXT_TYPEAHEAD != dwFlags )
  3211. iCurrent++;
  3212. // Start my Loop
  3213. while (1)
  3214. {
  3215. // Start back at zero
  3216. if (iCurrent >= m_cRows)
  3217. {
  3218. // We Wrapped Around
  3219. fWrapAround = TRUE;
  3220. if (pfWrapped)
  3221. *pfWrapped = TRUE;
  3222. // Start back at zero
  3223. iCurrent = 0;
  3224. }
  3225. // Get the Row Info
  3226. IF_FAILEXIT(hr = GetRow(iCurrent, &pMessage));
  3227. // How to search...
  3228. if (FINDNEXT_ALLCOLUMNS == dwFlags)
  3229. {
  3230. // Display to
  3231. if (pMessage->pszDisplayTo && StrStrIA(pMessage->pszDisplayTo, pszFindString))
  3232. {
  3233. *piNextRow = iCurrent;
  3234. goto exit;
  3235. }
  3236. // Email To
  3237. if (pMessage->pszEmailTo && StrStrIA(pMessage->pszEmailTo, pszFindString))
  3238. {
  3239. *piNextRow = iCurrent;
  3240. goto exit;
  3241. }
  3242. // Display From
  3243. if (pMessage->pszDisplayFrom && StrStrIA(pMessage->pszDisplayFrom, pszFindString))
  3244. {
  3245. *piNextRow = iCurrent;
  3246. goto exit;
  3247. }
  3248. // Email From
  3249. if (pMessage->pszEmailFrom && StrStrIA(pMessage->pszEmailFrom, pszFindString))
  3250. {
  3251. *piNextRow = iCurrent;
  3252. goto exit;
  3253. }
  3254. // Subject
  3255. if (pMessage->pszNormalSubj && StrStrIA(pMessage->pszNormalSubj, pszFindString))
  3256. {
  3257. *piNextRow = iCurrent;
  3258. goto exit;
  3259. }
  3260. // Folder
  3261. if (pMessage->pszFolder && StrStrIA(pMessage->pszFolder, pszFindString))
  3262. {
  3263. *piNextRow = iCurrent;
  3264. goto exit;
  3265. }
  3266. // Account name
  3267. if (pMessage->pszAcctName && StrStrIA(pMessage->pszAcctName, pszFindString))
  3268. {
  3269. *piNextRow = iCurrent;
  3270. goto exit;
  3271. }
  3272. // Search the Body ?
  3273. if (fIncludeBody && pMessage->faStream)
  3274. {
  3275. // Locals
  3276. BOOL fMatch=FALSE;
  3277. IMimeMessage *pMessageObject;
  3278. IStream *pStream;
  3279. // Open the Stream
  3280. if (SUCCEEDED(m_pFolder->OpenMessage(pMessage->idMessage, OPEN_MESSAGE_CACHEDONLY, &pMessageObject, NOSTORECALLBACK)))
  3281. {
  3282. // Try to Get the Plain Text Stream
  3283. if (FAILED(pMessageObject->GetTextBody(TXT_PLAIN, IET_DECODED, &pStream, NULL)))
  3284. {
  3285. // Try to get the HTML stream
  3286. if (FAILED(pMessageObject->GetTextBody(TXT_HTML, IET_DECODED, &pStream, NULL)))
  3287. pStream = NULL;
  3288. }
  3289. // Do we have a strema
  3290. if (pStream)
  3291. {
  3292. // Search the Stream
  3293. fMatch = StreamSubStringMatch(pStream, (LPSTR)pszFindString);
  3294. // Release the Stream
  3295. pStream->Release();
  3296. }
  3297. // Cleanup
  3298. pMessageObject->Release();
  3299. }
  3300. // Found a Match ?
  3301. if (fMatch)
  3302. {
  3303. *piNextRow = iCurrent;
  3304. goto exit;
  3305. }
  3306. }
  3307. }
  3308. // Otherwise
  3309. else
  3310. {
  3311. // Handle the column to search on...
  3312. switch(m_SortInfo.idColumn)
  3313. {
  3314. case COLUMN_TO:
  3315. if (pMessage->pszDisplayTo && 0 == StrCmpNI(pszFindString, pMessage->pszDisplayTo, cchFindString))
  3316. {
  3317. *piNextRow = iCurrent;
  3318. goto exit;
  3319. }
  3320. break;
  3321. case COLUMN_FROM:
  3322. if (pMessage->pszDisplayFrom && 0 == StrCmpNI(pszFindString, pMessage->pszDisplayFrom, cchFindString))
  3323. {
  3324. *piNextRow = iCurrent;
  3325. goto exit;
  3326. }
  3327. break;
  3328. case COLUMN_SUBJECT:
  3329. if (pMessage->pszNormalSubj && 0 == StrCmpNI(pszFindString, pMessage->pszNormalSubj, cchFindString))
  3330. {
  3331. *piNextRow = iCurrent;
  3332. goto exit;
  3333. }
  3334. break;
  3335. case COLUMN_FOLDER:
  3336. if (pMessage->pszFolder && 0 == StrCmpNI(pszFindString, pMessage->pszFolder, cchFindString))
  3337. {
  3338. *piNextRow = iCurrent;
  3339. goto exit;
  3340. }
  3341. break;
  3342. case COLUMN_ACCOUNT:
  3343. if (pMessage->pszAcctName && 0 == StrCmpNI(pszFindString, pMessage->pszAcctName, cchFindString))
  3344. {
  3345. *piNextRow = iCurrent;
  3346. goto exit;
  3347. }
  3348. break;
  3349. default:
  3350. goto exit;
  3351. }
  3352. }
  3353. // Cleanup
  3354. SafeReleaseRow(this, pMessage);
  3355. // Increment iCurrent
  3356. iCurrent++;
  3357. // Wrapped and back to original row
  3358. if (fWrapAround && iCurrent >= iStartRow)
  3359. break;
  3360. }
  3361. exit:
  3362. // Cleanup
  3363. SafeReleaseRow(this, pMessage);
  3364. // Unlock
  3365. m_pDB->Unlock(&hLock);
  3366. // Done
  3367. return(hr);
  3368. }
  3369. //--------------------------------------------------------------------------
  3370. // CMessageTable::Collapse
  3371. //--------------------------------------------------------------------------
  3372. STDMETHODIMP CMessageTable::Collapse(ROWINDEX iRow)
  3373. {
  3374. // Trace
  3375. TraceCall("CMessageTable::Collapse");
  3376. // Call Internal Function
  3377. return(_CollapseThread(iRow, TRUE));
  3378. }
  3379. //--------------------------------------------------------------------------
  3380. // CMessageTable::_CollapseThread
  3381. //--------------------------------------------------------------------------
  3382. HRESULT CMessageTable::_CollapseThread(ROWINDEX iRow, BOOL fNotify)
  3383. {
  3384. // Locals
  3385. HRESULT hr=S_OK;
  3386. ROWINDEX iParent;
  3387. LPROWINFO pRow;
  3388. // Trace
  3389. TraceCall("CMessageTable::_CollapseThread");
  3390. // Expand All ?
  3391. if (INVALID_ROWINDEX == iRow)
  3392. {
  3393. // Walk through the Roots in the View...
  3394. for (iRow = 0; iRow < m_cView; iRow++)
  3395. {
  3396. // Set pRow
  3397. if (NULL == m_prgpView[iRow]->pParent)
  3398. {
  3399. // Set iParent
  3400. iParent = iRow;
  3401. // _CollapseSingleThread
  3402. IF_FAILEXIT(hr = _CollapseSingleThread(&iRow, m_prgpView[iRow], fNotify));
  3403. // Notify ?
  3404. if (fNotify)
  3405. {
  3406. // Queue It
  3407. _QueueNotification(TRANSACTION_UPDATE, iParent, iParent);
  3408. }
  3409. }
  3410. }
  3411. }
  3412. // Otherwise, expand one row
  3413. else
  3414. {
  3415. // Get the Row
  3416. IF_FAILEXIT(hr = _GetRowFromIndex(iRow, &pRow));
  3417. // Set iParent
  3418. iParent = iRow;
  3419. // _ExpandSingleThread
  3420. IF_FAILEXIT(hr = _CollapseSingleThread(&iRow, pRow, fNotify));
  3421. // Notify ?
  3422. if (fNotify)
  3423. {
  3424. // Queue It
  3425. _QueueNotification(TRANSACTION_UPDATE, iParent, iParent);
  3426. }
  3427. }
  3428. exit:
  3429. // Flush
  3430. if (fNotify)
  3431. _FlushNotificationQueue(TRUE);
  3432. // Done
  3433. return(hr);
  3434. }
  3435. //--------------------------------------------------------------------------
  3436. // CMessageTable::_CollapseSingleThread
  3437. //--------------------------------------------------------------------------
  3438. HRESULT CMessageTable::_CollapseSingleThread(LPROWINDEX piCurrent,
  3439. LPROWINFO pParent, BOOL fNotify)
  3440. {
  3441. // Locals
  3442. HRESULT hr=S_OK;
  3443. LPROWINFO pCurrent;
  3444. // Trace
  3445. TraceCall("CMessageTable::_CollapseSingleThread");
  3446. // Mark Parent as Expanded...
  3447. pParent->fExpanded = FALSE;
  3448. // Set row state
  3449. pParent->dwState = 0;
  3450. // If no children
  3451. if (NULL == pParent->pChild)
  3452. return(S_OK);
  3453. // Loop through the children...
  3454. for (pCurrent = pParent->pChild; pCurrent != NULL; pCurrent = pCurrent->pSibling)
  3455. {
  3456. // If not visible
  3457. if (pCurrent->fVisible)
  3458. {
  3459. // Increment
  3460. (*piCurrent)++;
  3461. // Validate
  3462. Assert(m_prgpView[(*piCurrent)] == pCurrent);
  3463. // Insert pCurrent's Children
  3464. IF_FAILEXIT(hr = _CollapseSingleThread(piCurrent, pCurrent, fNotify));
  3465. // Insert into View
  3466. _DeleteFromView((*piCurrent), pCurrent);
  3467. // Insert the Row
  3468. if (fNotify)
  3469. {
  3470. // Queue It
  3471. _QueueNotification(TRANSACTION_DELETE, *piCurrent, INVALID_ROWINDEX, TRUE);
  3472. }
  3473. // Decrement
  3474. (*piCurrent)--;
  3475. }
  3476. }
  3477. exit:
  3478. // Done
  3479. return(hr);
  3480. }
  3481. //--------------------------------------------------------------------------
  3482. // CMessageTable::Expand
  3483. //--------------------------------------------------------------------------
  3484. STDMETHODIMP CMessageTable::Expand(ROWINDEX iRow)
  3485. {
  3486. // Trace
  3487. TraceCall("CMessageTable::Collapse");
  3488. // Call Internal Function
  3489. return(_ExpandThread(iRow, TRUE, FALSE));
  3490. }
  3491. //--------------------------------------------------------------------------
  3492. // CMessageTable::_ExpandThread
  3493. //--------------------------------------------------------------------------
  3494. HRESULT CMessageTable::_ExpandThread(ROWINDEX iRow, BOOL fNotify, BOOL fReExpand)
  3495. {
  3496. // Locals
  3497. HRESULT hr=S_OK;
  3498. ROWINDEX iParent;
  3499. LPROWINFO pRow;
  3500. // Trace
  3501. TraceCall("CMessageTable::_ExpandThread");
  3502. // Expand All ?
  3503. if (INVALID_ROWINDEX == iRow)
  3504. {
  3505. // Walk through the Roots in the View...
  3506. for (iRow = 0; iRow < m_cView; iRow++)
  3507. {
  3508. // Set pRow
  3509. if (NULL == m_prgpView[iRow]->pParent)
  3510. {
  3511. // Set iParent
  3512. iParent = iRow;
  3513. // _ExpandSingleThread
  3514. IF_FAILEXIT(hr = _ExpandSingleThread(&iRow, m_prgpView[iRow], fNotify, fReExpand));
  3515. // Notify ?
  3516. if (fNotify)
  3517. {
  3518. // Queue It
  3519. _QueueNotification(TRANSACTION_UPDATE, iParent, iParent);
  3520. }
  3521. }
  3522. }
  3523. }
  3524. // Otherwise, expand one row
  3525. else
  3526. {
  3527. // Get the Row
  3528. IF_FAILEXIT(hr = _GetRowFromIndex(iRow, &pRow));
  3529. // Set iParent
  3530. iParent = iRow;
  3531. // _ExpandSingleThread
  3532. IF_FAILEXIT(hr = _ExpandSingleThread(&iRow, pRow, fNotify, fReExpand));
  3533. // Notify ?
  3534. if (fNotify)
  3535. {
  3536. // Queue It
  3537. _QueueNotification(TRANSACTION_UPDATE, iParent, iParent);
  3538. }
  3539. }
  3540. exit:
  3541. // Flush
  3542. if (fNotify)
  3543. _FlushNotificationQueue(TRUE);
  3544. // Done
  3545. return(hr);
  3546. }
  3547. //--------------------------------------------------------------------------
  3548. // CMessageTable::_ExpandSingleThread
  3549. //--------------------------------------------------------------------------
  3550. HRESULT CMessageTable::_ExpandSingleThread(LPROWINDEX piCurrent,
  3551. LPROWINFO pParent, BOOL fNotify, BOOL fReExpand)
  3552. {
  3553. // Locals
  3554. HRESULT hr=S_OK;
  3555. LPROWINFO pCurrent;
  3556. // Trace
  3557. TraceCall("CMessageTable::_ExpandSingleThread");
  3558. // If not delayed inserted...
  3559. if (fReExpand && FALSE == pParent->fExpanded)
  3560. return(S_OK);
  3561. // Mark Parent as Expanded...
  3562. pParent->fExpanded = TRUE;
  3563. // Set row state
  3564. pParent->dwState = 0;
  3565. // If no children
  3566. if (NULL == pParent->pChild)
  3567. return(S_OK);
  3568. // Loop through the children...
  3569. for (pCurrent = pParent->pChild; pCurrent != NULL; pCurrent = pCurrent->pSibling)
  3570. {
  3571. // Increment piCurrent
  3572. (*piCurrent)++;
  3573. // If not visible
  3574. if (FALSE == pCurrent->fVisible)
  3575. {
  3576. // Insert into View
  3577. _InsertIntoView((*piCurrent), pCurrent);
  3578. // Insert the Row
  3579. if (fNotify)
  3580. {
  3581. // Queue It
  3582. _QueueNotification(TRANSACTION_INSERT, *piCurrent, INVALID_ROWINDEX, TRUE);
  3583. }
  3584. }
  3585. // Otherwise, valident entry in view index
  3586. else
  3587. Assert(m_prgpView[(*piCurrent)] == pCurrent);
  3588. // Insert pCurrent's Children
  3589. IF_FAILEXIT(hr = _ExpandSingleThread(piCurrent, pCurrent, fNotify, fReExpand));
  3590. }
  3591. exit:
  3592. // Done
  3593. return(hr);
  3594. }
  3595. //--------------------------------------------------------------------------
  3596. // CMessageTable::_DeleteFromView
  3597. //--------------------------------------------------------------------------
  3598. HRESULT CMessageTable::_DeleteFromView(ROWINDEX iRow, LPROWINFO pRow)
  3599. {
  3600. // Better not be visible yet
  3601. Assert(TRUE == pRow->fVisible);
  3602. // Correct Row
  3603. Assert(m_prgpView[iRow] == pRow);
  3604. // Visible...
  3605. pRow->fVisible = FALSE;
  3606. // Collapse the Array
  3607. MoveMemory(&m_prgpView[iRow], &m_prgpView[iRow + 1], sizeof(LPROWINFO) * (m_cView - (iRow + 1)));
  3608. // Decrement m_cView
  3609. m_cView--;
  3610. // Done
  3611. return(S_OK);
  3612. }
  3613. //--------------------------------------------------------------------------
  3614. // CMessageTable::_InsertIntoView
  3615. //--------------------------------------------------------------------------
  3616. HRESULT CMessageTable::_InsertIntoView(ROWINDEX iRow, LPROWINFO pRow)
  3617. {
  3618. // Better not be visible yet
  3619. Assert(FALSE == pRow->fVisible);
  3620. // Visible...
  3621. pRow->fVisible = TRUE;
  3622. // Increment view Count
  3623. m_cView++;
  3624. // Shift The Array
  3625. MoveMemory(&m_prgpView[iRow + 1], &m_prgpView[iRow], sizeof(LPROWINFO) * (m_cView - iRow));
  3626. // Set the Index
  3627. m_prgpView[iRow] = pRow;
  3628. // Done
  3629. return(S_OK);
  3630. }
  3631. //--------------------------------------------------------------------------
  3632. // CMessageTable::_RowTableInsert
  3633. //--------------------------------------------------------------------------
  3634. HRESULT CMessageTable::_RowTableInsert(ROWORDINAL iOrdinal, LPMESSAGEINFO pMessage)
  3635. {
  3636. // Locals
  3637. HRESULT hr=S_OK;
  3638. DWORD i;
  3639. LPROWINFO pRow;
  3640. ROWINDEX iRow;
  3641. // Trace
  3642. TraceCall("CMessageTable::_RowTableInsert");
  3643. // Failure
  3644. if (iOrdinal >= m_cRows + 1)
  3645. {
  3646. Assert(FALSE);
  3647. return(TraceResult(E_FAIL));
  3648. }
  3649. // Do I need to grow the table
  3650. if (m_cRows + 1 >= m_cAllocated)
  3651. {
  3652. // Realloc
  3653. IF_FAILEXIT(hr = HrRealloc((LPVOID *)&m_prgpRow, sizeof(LPROWINFO) * (m_cRows + CGROWTABLE)));
  3654. // Realloc
  3655. IF_FAILEXIT(hr = HrRealloc((LPVOID *)&m_prgpView, sizeof(LPROWINFO) * (m_cRows + CGROWTABLE)));
  3656. // Set m_cAllocated
  3657. m_cAllocated = m_cRows + CGROWTABLE;
  3658. }
  3659. // Create a Row
  3660. IF_FAILEXIT(hr = _CreateRow(pMessage, &pRow));
  3661. // Don't Free
  3662. pMessage->pAllocated = NULL;
  3663. // Increment Row Count
  3664. m_cRows++;
  3665. // Shift The Array
  3666. MoveMemory(&m_prgpRow[iOrdinal + 1], &m_prgpRow[iOrdinal], sizeof(LPROWINFO) * (m_cRows - iOrdinal));
  3667. // Set pRow
  3668. m_prgpRow[iOrdinal] = pRow;
  3669. // If the row is Filtered, then just return
  3670. pRow->fFiltered = _FIsFiltered(pRow);
  3671. // Get Hidden Bit
  3672. pRow->fHidden = _FIsHidden(pRow);
  3673. // If not filtered and not hidden
  3674. if (pRow->fFiltered || pRow->fHidden)
  3675. {
  3676. // Update Filtered Count
  3677. m_cFiltered++;
  3678. // Done
  3679. goto exit;
  3680. }
  3681. // If this is a news folder, then lets just wait for a while...we will get hit with a force sort later...
  3682. if (TRUE == m_fSynching && FOLDER_NEWS == m_Folder.tyFolder)
  3683. {
  3684. // Set Expanded
  3685. pRow->fExpanded = m_SortInfo.fExpandAll;
  3686. // Set fDelayed
  3687. pRow->fDelayed = TRUE;
  3688. // Count Skiped
  3689. m_cDelayed++;
  3690. // Done
  3691. goto exit;
  3692. }
  3693. // If not filtered
  3694. _AdjustUnreadCount(pRow, 1);
  3695. // Show the Row
  3696. _ShowRow(pRow);
  3697. exit:
  3698. // Done
  3699. return(hr);
  3700. }
  3701. //--------------------------------------------------------------------------
  3702. // CMessageTable::_ShowRow
  3703. //--------------------------------------------------------------------------
  3704. HRESULT CMessageTable::_ShowRow(LPROWINFO pRow)
  3705. {
  3706. // Locals
  3707. ROWINDEX iRow = INVALID_ROWINDEX;
  3708. // Compare
  3709. if (m_SortInfo.fShowReplies)
  3710. {
  3711. // Have Addresses
  3712. if (pRow->Message.pszEmailFrom && m_pszEmail)
  3713. {
  3714. // From Me
  3715. if (0 == lstrcmpi(m_pszEmail, pRow->Message.pszEmailFrom))
  3716. {
  3717. // Set the Highlight
  3718. pRow->Message.wHighlight = m_clrWatched;
  3719. }
  3720. }
  3721. }
  3722. // Threaded ?
  3723. if (m_SortInfo.fThreaded)
  3724. {
  3725. // Insert Message Id into the hash table
  3726. if (pRow->Message.pszMessageId)
  3727. {
  3728. // Insert It
  3729. m_pThreadMsgId->Insert(pRow->Message.pszMessageId, (LPVOID)pRow, HF_NO_DUPLICATES);
  3730. }
  3731. // Insert this row into a thread...
  3732. if (S_OK == _InsertRowIntoThread(pRow, TRUE))
  3733. return(S_OK);
  3734. // Subject Threading ?
  3735. // [PaulHi] 6/22/99 Raid 81081
  3736. // Make sure we have a non-NULL subject string pointer before trying to hash it.
  3737. if (m_pThreadSubject && pRow->Message.pszNormalSubj)
  3738. {
  3739. // Insert Subject into Hash Table...
  3740. m_pThreadSubject->Insert(pRow->Message.pszNormalSubj, (LPVOID)pRow, HF_NO_DUPLICATES);
  3741. }
  3742. }
  3743. // If no parent, then just insert sorted into the view
  3744. Assert(NULL == pRow->pParent);
  3745. // Insert into View
  3746. for (iRow=0; iRow<m_cView; iRow++)
  3747. {
  3748. // Only Compare Against Roots
  3749. if (NULL == m_prgpView[iRow]->pParent)
  3750. {
  3751. // Insert Here...
  3752. if (_CompareMessages(&pRow->Message, &m_prgpView[iRow]->Message) <= 0)
  3753. break;
  3754. }
  3755. }
  3756. // Insert into the view
  3757. _InsertIntoView(iRow, pRow);
  3758. // Queue It
  3759. _QueueNotification(TRANSACTION_INSERT, iRow, INVALID_ROWINDEX);
  3760. // Done
  3761. return(S_OK);
  3762. }
  3763. //--------------------------------------------------------------------------
  3764. // CMessageTable::_GetRowFromOrdinal
  3765. //--------------------------------------------------------------------------
  3766. HRESULT CMessageTable::_GetRowFromOrdinal(ROWORDINAL iOrdinal,
  3767. LPMESSAGEINFO pExpected, LPROWINFO *ppRow)
  3768. {
  3769. // Trace
  3770. TraceCall("CMessageTable::_GetRowFromOrdinal");
  3771. // Failure
  3772. if (iOrdinal >= m_cRows)
  3773. {
  3774. Assert(FALSE);
  3775. return(TraceResult(E_FAIL));
  3776. }
  3777. // Set pRow
  3778. (*ppRow) = m_prgpRow[iOrdinal];
  3779. // Valid Row
  3780. if ((*ppRow)->Message.idMessage != pExpected->idMessage)
  3781. {
  3782. Assert(FALSE);
  3783. return(TraceResult(E_FAIL));
  3784. }
  3785. // Done
  3786. return(S_OK);
  3787. }
  3788. //--------------------------------------------------------------------------
  3789. // CMessageTable::_RowTableDelete
  3790. //--------------------------------------------------------------------------
  3791. HRESULT CMessageTable::_RowTableDelete(ROWORDINAL iOrdinal, LPMESSAGEINFO pMessage)
  3792. {
  3793. // Set pRow
  3794. HRESULT hr=S_OK;
  3795. LPROWINFO pRow;
  3796. // Trace
  3797. TraceCall("CMessageTable::_RowTableDelete");
  3798. // Get Row From Ordinal
  3799. IF_FAILEXIT(hr = _GetRowFromOrdinal(iOrdinal, pMessage, &pRow));
  3800. // Shift The Array
  3801. MoveMemory(&m_prgpRow[iOrdinal], &m_prgpRow[iOrdinal + 1], sizeof(LPROWINFO) * (m_cRows - (iOrdinal + 1)));
  3802. // Decrement row Count
  3803. m_cRows--;
  3804. // If the message was filtered
  3805. if (pRow->fFiltered || pRow->fHidden)
  3806. {
  3807. // One less filtered item
  3808. m_cFiltered--;
  3809. }
  3810. // If not filtered
  3811. _AdjustUnreadCount(pRow, -1);
  3812. // Call Utility
  3813. _HideRow(pRow, TRUE);
  3814. // Release the Row
  3815. ReleaseRow(&pRow->Message);
  3816. exit:
  3817. // Done
  3818. return(S_OK);
  3819. }
  3820. //--------------------------------------------------------------------------
  3821. // CMessageTable::_HideRow
  3822. //--------------------------------------------------------------------------
  3823. HRESULT CMessageTable::_HideRow(LPROWINFO pRow, BOOL fNotify)
  3824. {
  3825. // Locals
  3826. LPROWINFO pReplace=NULL;
  3827. ROWINDEX iRow;
  3828. // Trace
  3829. TraceCall("CMessageTable::_HideRow");
  3830. // Threaded
  3831. if (m_SortInfo.fThreaded)
  3832. {
  3833. // Save First Child
  3834. pReplace = pRow->pChild;
  3835. }
  3836. // Delete the row from the thread
  3837. _DeleteRowFromThread(pRow, fNotify);
  3838. // Locate pRow in m_prgpView
  3839. if (FALSE == pRow->fVisible)
  3840. return(S_OK);
  3841. // Better not be hidden or filtered
  3842. Assert(FALSE == pRow->fHidden && FALSE == pRow->fFiltered);
  3843. // Must Succeed
  3844. SideAssert(SUCCEEDED(GetRowIndex(pRow->Message.idMessage, &iRow)));
  3845. // Replace ?
  3846. if (pReplace && TRUE == pRow->fVisible && FALSE == pReplace->fVisible)
  3847. {
  3848. // Validate
  3849. Assert(m_prgpView[iRow] == pRow);
  3850. // Insert into View
  3851. m_prgpView[iRow] = pReplace;
  3852. // Visible...
  3853. pReplace->fVisible = TRUE;
  3854. // Insert the Row
  3855. if (fNotify)
  3856. {
  3857. // Queue It
  3858. _QueueNotification(TRANSACTION_UPDATE, iRow, iRow, TRUE);
  3859. }
  3860. }
  3861. // Otherwise, just delete it...
  3862. else
  3863. {
  3864. // Delete from view
  3865. _DeleteFromView(iRow, pRow);
  3866. // Notify ?
  3867. if (fNotify)
  3868. {
  3869. // Queue It
  3870. _QueueNotification(TRANSACTION_DELETE, iRow, INVALID_ROWINDEX);
  3871. }
  3872. }
  3873. // Not Visible
  3874. pRow->fVisible = FALSE;
  3875. // Done
  3876. return(S_OK);
  3877. }
  3878. //--------------------------------------------------------------------------
  3879. // CMessageTable::_RowTableUpdate
  3880. //--------------------------------------------------------------------------
  3881. HRESULT CMessageTable::_RowTableUpdate(ROWORDINAL iOrdinal, LPMESSAGEINFO pMessage)
  3882. {
  3883. // Locals
  3884. HRESULT hr=S_OK;
  3885. LPROWINFO pRow;
  3886. ROWINDEX iMin;
  3887. ROWINDEX iMax;
  3888. BOOL fDone=FALSE;
  3889. BOOL fHidden;
  3890. // Trace
  3891. TraceCall("CMessageTable::_RowTableUpdate");
  3892. // Get Row From Ordinal
  3893. IF_FAILEXIT(hr = _GetRowFromOrdinal(iOrdinal, pMessage, &pRow));
  3894. // If not filtered
  3895. _AdjustUnreadCount(pRow, -1);
  3896. // Free pRow->Message
  3897. m_pDB->FreeRecord(&pRow->Message);
  3898. // Copy the Message Info
  3899. CopyMemory(&pRow->Message, pMessage, sizeof(MESSAGEINFO));
  3900. // Set dwReserved
  3901. pRow->Message.dwReserved = (DWORD_PTR)pRow;
  3902. // Don't Free
  3903. pMessage->pAllocated = NULL;
  3904. // Save the Highlight
  3905. pRow->wHighlight = pRow->Message.wHighlight;
  3906. // Clear this rows state...
  3907. pRow->dwState = 0;
  3908. // Compare
  3909. if (m_SortInfo.fShowReplies)
  3910. {
  3911. // Have Addresses
  3912. if (pRow->Message.pszEmailFrom && m_pszEmail)
  3913. {
  3914. // From Me
  3915. if (0 == lstrcmpi(m_pszEmail, pRow->Message.pszEmailFrom))
  3916. {
  3917. // Set the Highlight
  3918. pRow->Message.wHighlight = m_clrWatched;
  3919. }
  3920. }
  3921. }
  3922. // Hidden
  3923. fHidden = _FIsHidden(pRow);
  3924. // If the message was filtered, but isn't filtered now...
  3925. if (TRUE == pRow->fFiltered)
  3926. {
  3927. // Reset the Filtered Bit
  3928. if (FALSE == _FIsFiltered(pRow))
  3929. {
  3930. // Set fFiltered
  3931. pRow->fFiltered = FALSE;
  3932. // If Not Hidden
  3933. if (FALSE == pRow->fHidden)
  3934. {
  3935. // Need to do something so that it gets shown
  3936. pRow->fHidden = !fHidden;
  3937. // Decrement m_cFiltered
  3938. m_cFiltered--;
  3939. }
  3940. }
  3941. }
  3942. // If not filtered
  3943. if (FALSE == pRow->fFiltered)
  3944. {
  3945. // Is it hidden now
  3946. if (FALSE == pRow->fHidden && TRUE == fHidden)
  3947. {
  3948. // If not filtered
  3949. _AdjustUnreadCount(pRow, -1);
  3950. // Hide the Row
  3951. _HideRow(pRow, TRUE);
  3952. // Its Hidden
  3953. pRow->fHidden = TRUE;
  3954. // Increment Filtered
  3955. m_cFiltered++;
  3956. // Done
  3957. fDone = TRUE;
  3958. }
  3959. // Otherwise, if it was hidden, and now its not...
  3960. else if (TRUE == pRow->fHidden && FALSE == fHidden)
  3961. {
  3962. // Its Hidden
  3963. pRow->fHidden = FALSE;
  3964. // If not filtered
  3965. _AdjustUnreadCount(pRow, 1);
  3966. // Increment Filtered
  3967. m_cFiltered--;
  3968. // Show the row
  3969. _ShowRow(pRow);
  3970. // Done
  3971. fDone = TRUE;
  3972. }
  3973. }
  3974. // If not hidden and not filtered
  3975. if (FALSE == fDone && FALSE == pRow->fHidden && FALSE == pRow->fFiltered)
  3976. {
  3977. // If not filtered
  3978. _AdjustUnreadCount(pRow, 1);
  3979. // If this row is visible, then I just need to update this row...
  3980. if (pRow->fVisible)
  3981. {
  3982. // Get the Row Index
  3983. SideAssert(SUCCEEDED(GetRowIndex(pRow->Message.idMessage, &iMin)));
  3984. // Queue It
  3985. _QueueNotification(TRANSACTION_UPDATE, iMin, iMin);
  3986. }
  3987. // Otherwise, update the thread range
  3988. else
  3989. {
  3990. // Get the index range of this thread
  3991. _GetThreadIndexRange(pRow, TRUE, &iMin, &iMax);
  3992. // Queue It
  3993. _QueueNotification(TRANSACTION_UPDATE, iMin, iMax);
  3994. }
  3995. }
  3996. exit:
  3997. // Done
  3998. return(S_OK);
  3999. }
  4000. //--------------------------------------------------------------------------
  4001. // CMessageTable::_FlushNotificationQueue
  4002. //--------------------------------------------------------------------------
  4003. HRESULT CMessageTable::_FlushNotificationQueue(BOOL fFinal)
  4004. {
  4005. // Nothing to Notify
  4006. if (NULL == m_pNotify)
  4007. return(S_OK);
  4008. // Have Delete or Inserted rows ?
  4009. if (m_Notify.cRows > 0)
  4010. {
  4011. // TRANSACTION_INSERT
  4012. if (TRANSACTION_INSERT == m_Notify.tyCurrent)
  4013. {
  4014. // Is this It ?
  4015. m_pNotify->OnInsertRows(m_Notify.cRows, m_Notify.prgiRow, m_Notify.fIsExpandCollapse);
  4016. }
  4017. // TRANSACTION_DELETE
  4018. else if (TRANSACTION_DELETE == m_Notify.tyCurrent)
  4019. {
  4020. // Is this It ?
  4021. m_pNotify->OnDeleteRows(m_Notify.cRows, m_Notify.prgiRow, m_Notify.fIsExpandCollapse);
  4022. }
  4023. }
  4024. // Have Updated Rows ?
  4025. if (m_Notify.cUpdate > 0)
  4026. {
  4027. // Is this It ?
  4028. m_pNotify->OnUpdateRows(m_Notify.iRowMin, m_Notify.iRowMax);
  4029. // Reset Update Range
  4030. m_Notify.cUpdate = 0;
  4031. m_Notify.iRowMin = 0xffffffff;
  4032. m_Notify.iRowMax = 0;
  4033. }
  4034. // Nothing to Notify About
  4035. m_Notify.cRows = 0;
  4036. // Final ?
  4037. m_Notify.fClean = fFinal;
  4038. // Done
  4039. return(S_OK);
  4040. }
  4041. //--------------------------------------------------------------------------
  4042. // CMessageTable::_QueueNotification
  4043. //--------------------------------------------------------------------------
  4044. HRESULT CMessageTable::_QueueNotification(TRANSACTIONTYPE tyTransaction,
  4045. ROWINDEX iRowMin, ROWINDEX iRowMax, BOOL fIsExpandCollapse /*=FALSE*/)
  4046. {
  4047. // Locals
  4048. HRESULT hr=S_OK;
  4049. // Trace
  4050. TraceCall("CMessageTable::_QueueNotification");
  4051. // Nothing to Notify
  4052. if (NULL == m_pNotify)
  4053. return(S_OK);
  4054. // Not Clearn
  4055. m_Notify.fClean = FALSE;
  4056. // If Update
  4057. if (TRANSACTION_UPDATE == tyTransaction)
  4058. {
  4059. // Min
  4060. if (iRowMin < m_Notify.iRowMin)
  4061. m_Notify.iRowMin = iRowMin;
  4062. // Max
  4063. if (iRowMax > m_Notify.iRowMax)
  4064. m_Notify.iRowMax = iRowMax;
  4065. // Count Notify
  4066. m_Notify.cUpdate++;
  4067. }
  4068. // Otherwise...
  4069. else
  4070. {
  4071. // Queue It
  4072. if (tyTransaction != m_Notify.tyCurrent || m_Notify.fIsExpandCollapse != fIsExpandCollapse)
  4073. {
  4074. // Flush
  4075. _FlushNotificationQueue(FALSE);
  4076. // Save the New Type
  4077. m_Notify.tyCurrent = tyTransaction;
  4078. // Count fIsExpandCollapse
  4079. m_Notify.fIsExpandCollapse = (BYTE) !!fIsExpandCollapse;
  4080. }
  4081. // Grow the Queue Size
  4082. if (m_Notify.cRows + 1 > m_Notify.cAllocated)
  4083. {
  4084. // Realloc
  4085. IF_FAILEXIT(hr = HrRealloc((LPVOID *)&m_Notify.prgiRow, (m_Notify.cAllocated + 256) * sizeof(ROWINDEX)));
  4086. // Set cAlloc
  4087. m_Notify.cAllocated = (m_Notify.cAllocated + 256);
  4088. }
  4089. // Append the iRow
  4090. m_Notify.prgiRow[m_Notify.cRows] = iRowMin;
  4091. // Increment Row count
  4092. m_Notify.cRows++;
  4093. }
  4094. exit:
  4095. // Done
  4096. return(hr);
  4097. }
  4098. //--------------------------------------------------------------------------
  4099. // CMessageTable::OnTransaction
  4100. //--------------------------------------------------------------------------
  4101. STDMETHODIMP CMessageTable::OnTransaction(HTRANSACTION hTransaction,
  4102. DWORD_PTR dwCookie, IDatabase *pDB)
  4103. {
  4104. // Locals
  4105. HRESULT hr=S_OK;
  4106. ORDINALLIST Ordinals;
  4107. INDEXORDINAL iIndex;
  4108. MESSAGEINFO Message1={0};
  4109. MESSAGEINFO Message2={0};
  4110. TRANSACTIONTYPE tyTransaction;
  4111. // Trace
  4112. TraceCall("CMessageTable::OnTransaction");
  4113. // Should have final bit set
  4114. IxpAssert(m_Notify.fClean == TRUE);
  4115. // Loop Through Notifications
  4116. while (hTransaction)
  4117. {
  4118. // Get the Transaction Info
  4119. IF_FAILEXIT(hr = pDB->GetTransaction(&hTransaction, &tyTransaction, &Message1, &Message2, &iIndex, &Ordinals));
  4120. // Insert
  4121. if (TRANSACTION_INSERT == tyTransaction)
  4122. {
  4123. // Good Ordinal
  4124. Assert(INVALID_ROWORDINAL != Ordinals.rgiRecord1[IINDEX_PRIMARY] && Ordinals.rgiRecord1[IINDEX_PRIMARY] > 0);
  4125. // Insert Row Into Table
  4126. _RowTableInsert(Ordinals.rgiRecord1[IINDEX_PRIMARY] - 1, &Message1);
  4127. }
  4128. // Delete
  4129. else if (TRANSACTION_DELETE == tyTransaction)
  4130. {
  4131. // Good Ordinal
  4132. Assert(INVALID_ROWORDINAL != Ordinals.rgiRecord1[IINDEX_PRIMARY] && Ordinals.rgiRecord1[IINDEX_PRIMARY] > 0);
  4133. // Delete Row From Table
  4134. _RowTableDelete(Ordinals.rgiRecord1[IINDEX_PRIMARY] - 1, &Message1);
  4135. }
  4136. // Update
  4137. else if (TRANSACTION_UPDATE == tyTransaction)
  4138. {
  4139. // Deleted
  4140. Assert(INVALID_ROWORDINAL != Ordinals.rgiRecord1[IINDEX_PRIMARY] && INVALID_ROWORDINAL != Ordinals.rgiRecord2[IINDEX_PRIMARY] && Ordinals.rgiRecord1[IINDEX_PRIMARY] == Ordinals.rgiRecord2[IINDEX_PRIMARY] && Ordinals.rgiRecord1[IINDEX_PRIMARY] > 0 && Ordinals.rgiRecord2[IINDEX_PRIMARY] > 0);
  4141. // Delete Row From Table
  4142. _RowTableUpdate(Ordinals.rgiRecord1[IINDEX_PRIMARY] - 1, &Message2);
  4143. }
  4144. }
  4145. exit:
  4146. // Cleanup
  4147. pDB->FreeRecord(&Message1);
  4148. pDB->FreeRecord(&Message2);
  4149. // Flush the Queue
  4150. _FlushNotificationQueue(TRUE);
  4151. // Done
  4152. return(S_OK);
  4153. }
  4154. //--------------------------------------------------------------------------
  4155. // CMessageTable::_WalkThreadGetSelectionState
  4156. //--------------------------------------------------------------------------
  4157. HRESULT CMessageTable::_WalkThreadGetSelectionState(CMessageTable *pThis,
  4158. LPROWINFO pRow, DWORD_PTR dwCookie)
  4159. {
  4160. // Locals
  4161. HRESULT hr=S_OK;
  4162. FOLDERTYPE tyFolder;
  4163. LPGETSELECTIONSTATE pState = (LPGETSELECTIONSTATE)dwCookie;
  4164. // Trace
  4165. TraceCall("CMessageTable::_WalkThreadGetSelectionState");
  4166. // Is Deletetable
  4167. if (ISFLAGSET(pState->dwMask, SELECTION_STATE_DELETABLE))
  4168. {
  4169. // Validate
  4170. Assert(pThis->m_pFindFolder);
  4171. // Get the Folder Type
  4172. IF_FAILEXIT(hr = pThis->m_pFindFolder->GetMessageFolderType(pRow->Message.idMessage, &tyFolder));
  4173. // Get the State
  4174. if (FOLDER_NEWS == tyFolder)
  4175. {
  4176. // Set the State
  4177. FLAGSET(pState->dwState, SELECTION_STATE_DELETABLE);
  4178. }
  4179. }
  4180. exit:
  4181. // Done
  4182. return(hr);
  4183. }
  4184. //--------------------------------------------------------------------------
  4185. // CMessageTable::_WalkThreadGetIdList
  4186. //--------------------------------------------------------------------------
  4187. HRESULT CMessageTable::_WalkThreadGetIdList(CMessageTable *pThis,
  4188. LPROWINFO pRow, DWORD_PTR dwCookie)
  4189. {
  4190. // Locals
  4191. HRESULT hr=S_OK;
  4192. LPMESSAGEIDLIST pList=(LPMESSAGEIDLIST)dwCookie;
  4193. // Trace
  4194. TraceCall("CMessageTable::_WalkThreadGetIdList");
  4195. // Grow the Id List
  4196. IF_FAILEXIT(hr = pThis->_GrowIdList(pList, 1));
  4197. // Insert Id
  4198. pList->prgidMsg[pList->cMsgs++] = pRow->Message.idMessage;
  4199. exit:
  4200. // Done
  4201. return(hr);
  4202. }
  4203. //--------------------------------------------------------------------------
  4204. // CMessageTable::_WalkThreadGetState
  4205. //--------------------------------------------------------------------------
  4206. HRESULT CMessageTable::_WalkThreadGetState(CMessageTable *pThis,
  4207. LPROWINFO pRow, DWORD_PTR dwCookie)
  4208. {
  4209. // Locals
  4210. LPGETTHREADSTATE pGetState = (LPGETTHREADSTATE)dwCookie;
  4211. // Trace
  4212. TraceCall("CMessageTable::_WalkThreadGetState");
  4213. // Children
  4214. pGetState->cChildren++;
  4215. // Is Unread
  4216. if (0 != (pRow->Message.dwFlags & pGetState->dwFlags))
  4217. pGetState->cHasFlags++;
  4218. // Done
  4219. return(S_OK);
  4220. }
  4221. //--------------------------------------------------------------------------
  4222. // CMessageTable::_WalkThreadClearState
  4223. //--------------------------------------------------------------------------
  4224. HRESULT CMessageTable::_WalkThreadClearState(CMessageTable *pThis,
  4225. LPROWINFO pRow, DWORD_PTR dwCookie)
  4226. {
  4227. // Trace
  4228. TraceCall("CMessageTable::_WalkThreadClearState");
  4229. // Clear State
  4230. pRow->dwState = 0;
  4231. // Done
  4232. return(S_OK);
  4233. }
  4234. //--------------------------------------------------------------------------
  4235. // CMessageTable::_WalkThreadIsFromMe
  4236. //--------------------------------------------------------------------------
  4237. HRESULT CMessageTable::_WalkThreadIsFromMe(CMessageTable *pThis,
  4238. LPROWINFO pRow, DWORD_PTR dwCookie)
  4239. {
  4240. // Locals
  4241. LPTHREADISFROMME pIsFromMe = (LPTHREADISFROMME)dwCookie;
  4242. // Trace
  4243. TraceCall("CMessageTable::_WalkThreadIsFromMe");
  4244. // m_pszEmail or pszEmailFrom is null
  4245. if (NULL == pRow->Message.pszEmailFrom)
  4246. return(S_OK);
  4247. // Compare
  4248. if (pThis->m_pszEmail && 0 == lstrcmpi(pThis->m_pszEmail, pRow->Message.pszEmailFrom))
  4249. {
  4250. // This thread is from me
  4251. pIsFromMe->fResult = TRUE;
  4252. // Set the Row
  4253. pIsFromMe->pRow = pRow;
  4254. // Override the highlight
  4255. pRow->Message.wHighlight = pThis->m_clrWatched;
  4256. }
  4257. // Done
  4258. return(S_OK);
  4259. }
  4260. //--------------------------------------------------------------------------
  4261. // CMessageTable::_WalkThreadHide
  4262. //--------------------------------------------------------------------------
  4263. HRESULT CMessageTable::_WalkThreadHide(CMessageTable *pThis,
  4264. LPROWINFO pRow, DWORD_PTR dwCookie)
  4265. {
  4266. // Locals
  4267. LPTHREADHIDE pHide = (LPTHREADHIDE)dwCookie;
  4268. // Trace
  4269. TraceCall("CMessageTable::_WalkThreadHide");
  4270. // Hide this row
  4271. pThis->_HideRow(pRow, pHide->fNotify);
  4272. // If not filtered
  4273. pThis->_AdjustUnreadCount(pRow, -1);
  4274. // Mark Row as Filtered
  4275. pRow->fFiltered = TRUE;
  4276. // Increment m_cFiltered
  4277. pThis->m_cFiltered++;
  4278. // Done
  4279. return(S_OK);
  4280. }
  4281. //--------------------------------------------------------------------------
  4282. // CMessageTable::_GrowIdList
  4283. //--------------------------------------------------------------------------
  4284. HRESULT CMessageTable::_GrowIdList(LPMESSAGEIDLIST pList, DWORD cNeeded)
  4285. {
  4286. // Locals
  4287. HRESULT hr=S_OK;
  4288. // Trace
  4289. TraceCall("CMessageTable::_GrowIdList");
  4290. // Allocate
  4291. if (pList->cMsgs + cNeeded > pList->cAllocated)
  4292. {
  4293. // Compute cGrow
  4294. DWORD cGrow = max(32, cNeeded);
  4295. // Realloc
  4296. IF_FAILEXIT(hr = HrRealloc((LPVOID *)&pList->prgidMsg, sizeof(MESSAGEID) * (pList->cAllocated + cGrow)));
  4297. // Increment dwREserved
  4298. pList->cAllocated += cGrow;
  4299. }
  4300. exit:
  4301. // Done
  4302. return(hr);
  4303. }
  4304. // --------------------------------------------------------------------------------
  4305. // EnumerateRefs
  4306. // --------------------------------------------------------------------------------
  4307. HRESULT EnumerateRefs(LPCSTR pszReferences, DWORD_PTR dwCookie, PFNENUMREFS pfnEnumRefs)
  4308. {
  4309. // Locals
  4310. HRESULT hr=S_OK;
  4311. DWORD cchRefs;
  4312. LPSTR pszRefs;
  4313. LPSTR pszFree=NULL;
  4314. LPSTR pszT;
  4315. BOOL fDone=FALSE;
  4316. CHAR szBuffer[1024];
  4317. // Trace
  4318. TraceCall("EnumerateRefs");
  4319. // If the message has a references line
  4320. if (NULL == pszReferences || '\0' == *pszReferences)
  4321. return(S_OK);
  4322. // Get Length
  4323. cchRefs = lstrlen(pszReferences);
  4324. // Use Buffer ?
  4325. if (cchRefs + 1 <= ARRAYSIZE(szBuffer))
  4326. pszRefs = szBuffer;
  4327. // Otherwise, duplicate it
  4328. else
  4329. {
  4330. // Allocate Memory
  4331. IF_NULLEXIT(pszFree = (LPSTR)g_pMalloc->Alloc(cchRefs + 1));
  4332. // Set pszRefs
  4333. pszRefs = pszFree;
  4334. }
  4335. // Copy It
  4336. CopyMemory(pszRefs, pszReferences, cchRefs + 1);
  4337. // Set pszT
  4338. pszT = (LPSTR)(pszRefs + cchRefs - 1);
  4339. // Strip
  4340. while (pszT > pszRefs && *pszT != '>')
  4341. *pszT-- = '\0';
  4342. // We have have ids
  4343. while (pszT >= pszRefs)
  4344. {
  4345. // Start of message Id ?
  4346. if (*pszT == '<')
  4347. {
  4348. // Callback function
  4349. (*pfnEnumRefs)(pszT, dwCookie, &fDone);
  4350. // Done
  4351. if (fDone)
  4352. goto exit;
  4353. // Strip
  4354. while (pszT > pszRefs && *pszT != '>')
  4355. *pszT-- = '\0';
  4356. }
  4357. // Decrement
  4358. pszT--;
  4359. }
  4360. exit:
  4361. // Cleanup
  4362. SafeMemFree(pszFree);
  4363. // Done
  4364. return(hr);
  4365. }
  4366. //--------------------------------------------------------------------------
  4367. // CMessageTable::GetRelativeRow
  4368. //--------------------------------------------------------------------------
  4369. STDMETHODIMP CMessageTable::IsChild(ROWINDEX iRowParent, ROWINDEX iRowChild)
  4370. {
  4371. // Locals
  4372. HRESULT hr=S_FALSE;
  4373. LPROWINFO pRow;
  4374. LPROWINFO pRowParent;
  4375. // Trace
  4376. TraceCall("CMessageTable::IsChild");
  4377. // Validate State
  4378. if (!IsInitialized(this))
  4379. return(TraceResult(E_UNEXPECTED));
  4380. // Failure
  4381. IF_FAILEXIT(hr = _GetRowFromIndex(iRowChild, &pRow));
  4382. IF_FAILEXIT(hr = _GetRowFromIndex(iRowParent, &pRowParent));
  4383. // Loop through all the parents of the child row to see if we find the
  4384. // specified parent row.
  4385. while (pRow->pParent)
  4386. {
  4387. if (pRow->pParent == pRowParent)
  4388. {
  4389. hr = S_OK;
  4390. goto exit;
  4391. }
  4392. pRow = pRow->pParent;
  4393. }
  4394. hr = S_FALSE;
  4395. exit:
  4396. return (hr);
  4397. }
  4398. STDMETHODIMP CMessageTable::GetAdBarUrl(IStoreCallback *pCallback)
  4399. {
  4400. HRESULT hr = S_OK;
  4401. // Trace
  4402. TraceCall("CMessageTable::GetAdBarUrl");
  4403. // Validate State
  4404. if (!IsInitialized(this))
  4405. return(TraceResult(E_UNEXPECTED));
  4406. // Tell the Folder to Synch
  4407. IF_FAILEXIT(hr = m_pFolder->GetAdBarUrl(pCallback));
  4408. exit:
  4409. return(hr);
  4410. }