Source code of Windows XP (NT5)
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.

1038 lines
27 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1998 - 1999
  6. //
  7. // File: treedat_.cpp
  8. //
  9. //--------------------------------------------------------------------------
  10. /////////////////////////////////////////////////////////////////////////////
  11. // Miscellanea
  12. LPCWSTR g_lpszNullString = L"\0";
  13. ///////////////////////////////////////////////////////////////////////
  14. // Global Helper functions
  15. BOOL LoadContextMenuResources(MENUMAP* pMenuMap)
  16. {
  17. HINSTANCE hInstance = _Module.GetModuleInstance();
  18. for (int i = 0; pMenuMap->ctxMenu[i].strName; i++)
  19. {
  20. if (0 == ::LoadString(hInstance, pMenuMap->dataRes[i].uResID, pMenuMap->dataRes[i].szBuffer, MAX_CONTEXT_MENU_STRLEN*2))
  21. return FALSE;
  22. pMenuMap->ctxMenu[i].strName = pMenuMap->dataRes[i].szBuffer;
  23. for (WCHAR* pCh = pMenuMap->dataRes[i].szBuffer; (*pCh) != NULL; pCh++)
  24. {
  25. if ( (*pCh) == L'\n')
  26. {
  27. pMenuMap->ctxMenu[i].strStatusBarText = (pCh+1);
  28. (*pCh) = NULL;
  29. break;
  30. }
  31. }
  32. }
  33. return TRUE;
  34. }
  35. BOOL LoadResultHeaderResources(RESULT_HEADERMAP* pHeaderMap, int nCols)
  36. {
  37. HINSTANCE hInstance = _Module.GetModuleInstance();
  38. for ( int i = 0; i < nCols ; i++)
  39. {
  40. if ( 0 == ::LoadString(hInstance, pHeaderMap[i].uResID, pHeaderMap[i].szBuffer, MAX_RESULT_HEADER_STRLEN))
  41. return TRUE;
  42. }
  43. return TRUE;
  44. }
  45. ////////////////////////////////////////////////////////////////////////
  46. // CTreeNode
  47. BEGIN_TOOLBAR_MAP(CTreeNode)
  48. END_TOOLBAR_MAP()
  49. BOOL CTreeNode::HasContainer(CContainerNode* pContainerNode)
  50. {
  51. if (m_pContainer == NULL)
  52. return FALSE; // root
  53. if (m_pContainer == pContainerNode)
  54. return TRUE; // got it
  55. return m_pContainer->HasContainer(pContainerNode);
  56. }
  57. HRESULT CTreeNode::GetResultViewType(CComponentDataObject* pComponentData,
  58. LPOLESTR* ppViewType,
  59. long* pViewOptions)
  60. {
  61. if (pComponentData->IsMultiSelect())
  62. {
  63. *pViewOptions = MMC_VIEW_OPTIONS_MULTISELECT;
  64. }
  65. else
  66. {
  67. *pViewOptions = MMC_VIEW_OPTIONS_NONE;
  68. }
  69. *ppViewType = NULL;
  70. return S_FALSE;
  71. }
  72. void CTreeNode::Show(BOOL bShow, CComponentDataObject* pComponentData)
  73. {
  74. if (bShow)
  75. {
  76. ASSERT(m_dwNodeFlags & TN_FLAG_HIDDEN); // must be currently hidden
  77. SetFlagsDown(TN_FLAG_HIDDEN,FALSE); // mark it visible
  78. VERIFY(SUCCEEDED(pComponentData->AddNode(this)));
  79. }
  80. else
  81. {
  82. ASSERT(!(m_dwNodeFlags & TN_FLAG_HIDDEN)); // must be currently visible
  83. SetFlagsDown(TN_FLAG_HIDDEN,TRUE); // mark it hidden
  84. VERIFY(SUCCEEDED(pComponentData->DeleteNode(this)));
  85. if (IsContainer())
  86. {
  87. ((CContainerNode*)this)->RemoveAllChildrenFromList();
  88. ((CContainerNode*)this)->MarkEnumerated(FALSE);
  89. }
  90. }
  91. }
  92. void CTreeNode::SetFlagsDown(DWORD dwNodeFlags, BOOL bSet)
  93. {
  94. if (bSet)
  95. m_dwNodeFlags |= dwNodeFlags;
  96. else
  97. m_dwNodeFlags &= ~dwNodeFlags;
  98. }
  99. void CTreeNode::SetFlagsUp(DWORD dwNodeFlags, BOOL bSet)
  100. {
  101. if (bSet)
  102. m_dwNodeFlags |= dwNodeFlags;
  103. else
  104. m_dwNodeFlags &= ~dwNodeFlags;
  105. if (m_pContainer != NULL)
  106. {
  107. ASSERT(m_pContainer != this);
  108. m_pContainer->SetFlagsUp(dwNodeFlags, bSet);
  109. }
  110. }
  111. //
  112. // Property Page methods
  113. //
  114. void CTreeNode::ShowPageForNode(CComponentDataObject* pComponentDataObject)
  115. {
  116. ASSERT(pComponentDataObject != NULL);
  117. pComponentDataObject->GetPropertyPageHolderTable()->BroadcastSelectPage(this, -1);
  118. }
  119. BOOL CTreeNode::HasPropertyPages(DATA_OBJECT_TYPES,
  120. BOOL* pbHideVerb,
  121. CNodeList*)
  122. {
  123. *pbHideVerb = TRUE;
  124. return FALSE;
  125. }
  126. //
  127. // Menu Item methods
  128. //
  129. HRESULT CTreeNode::OnAddMenuItems(IContextMenuCallback2* pContextMenuCallback2,
  130. DATA_OBJECT_TYPES type,
  131. long *pInsertionAllowed,
  132. CNodeList* pNodeList)
  133. {
  134. HRESULT hr = S_OK;
  135. LPCONTEXTMENUITEM2 pContextMenuItem = NULL;
  136. if (pNodeList->GetCount() == 1) // single selection
  137. {
  138. pContextMenuItem = OnGetContextMenuItemTable();
  139. if (pContextMenuItem == NULL)
  140. return hr;
  141. //
  142. // Loop through and add each of the menu items
  143. //
  144. for (LPCONTEXTMENUITEM2 m = pContextMenuItem; m->strName; m++)
  145. {
  146. if (
  147. ( (*pInsertionAllowed & CCM_INSERTIONALLOWED_NEW) &&
  148. (m->lInsertionPointID == CCM_INSERTIONPOINTID_PRIMARY_NEW) ) ||
  149. ( (*pInsertionAllowed & CCM_INSERTIONALLOWED_TASK) &&
  150. (m->lInsertionPointID == CCM_INSERTIONPOINTID_PRIMARY_TASK) ) ||
  151. ( (*pInsertionAllowed & CCM_INSERTIONALLOWED_VIEW) &&
  152. (m->lInsertionPointID == CCM_INSERTIONPOINTID_PRIMARY_VIEW) ) ||
  153. ( (*pInsertionAllowed & CCM_INSERTIONALLOWED_TOP) &&
  154. (m->lInsertionPointID == CCM_INSERTIONPOINTID_PRIMARY_TOP) )
  155. )
  156. {
  157. // make a temporary copy that can be modified
  158. CONTEXTMENUITEM2 tempItem;
  159. ::memcpy(&tempItem, m, sizeof(CONTEXTMENUITEM2));
  160. if (OnAddMenuItem(&tempItem, pInsertionAllowed))
  161. {
  162. hr = pContextMenuCallback2->AddItem(&tempItem);
  163. if (FAILED(hr))
  164. break;
  165. }
  166. }
  167. }
  168. }
  169. else if (pNodeList->GetCount() > 1) // multiple selection
  170. {
  171. hr = OnAddMenuItemsMultipleSelect(pContextMenuCallback2,
  172. type,
  173. pInsertionAllowed,
  174. pNodeList);
  175. }
  176. return hr;
  177. }
  178. BOOL CTreeNode::OnSetRenameVerbState(DATA_OBJECT_TYPES,
  179. BOOL* pbHide,
  180. CNodeList*)
  181. {
  182. *pbHide = TRUE;
  183. return FALSE;
  184. }
  185. BOOL CTreeNode::OnSetDeleteVerbState(DATA_OBJECT_TYPES,
  186. BOOL* pbHide,
  187. CNodeList*)
  188. {
  189. *pbHide = TRUE;
  190. return FALSE;
  191. }
  192. BOOL CTreeNode::OnSetRefreshVerbState(DATA_OBJECT_TYPES,
  193. BOOL* pbHide,
  194. CNodeList*)
  195. {
  196. *pbHide = TRUE;
  197. return FALSE;
  198. }
  199. BOOL CTreeNode::OnSetCutVerbState(DATA_OBJECT_TYPES,
  200. BOOL* pbHide,
  201. CNodeList*)
  202. {
  203. *pbHide = TRUE;
  204. return FALSE;
  205. }
  206. BOOL CTreeNode::OnSetCopyVerbState(DATA_OBJECT_TYPES,
  207. BOOL* pbHide,
  208. CNodeList*)
  209. {
  210. *pbHide = TRUE;
  211. return FALSE;
  212. }
  213. BOOL CTreeNode::OnSetPasteVerbState(DATA_OBJECT_TYPES,
  214. BOOL* pbHide,
  215. CNodeList*)
  216. {
  217. *pbHide = TRUE;
  218. return FALSE;
  219. }
  220. BOOL CTreeNode::OnSetPrintVerbState(DATA_OBJECT_TYPES,
  221. BOOL* pbHide,
  222. CNodeList*)
  223. {
  224. *pbHide = TRUE;
  225. return FALSE;
  226. }
  227. MMC_CONSOLE_VERB CTreeNode::GetDefaultVerb(DATA_OBJECT_TYPES type,
  228. CNodeList* pNodeList)
  229. {
  230. ASSERT((type == CCT_SCOPE) || (type == CCT_RESULT));
  231. if (type == CCT_SCOPE)
  232. return MMC_VERB_OPEN;
  233. BOOL bHideVerbDummy;
  234. if (HasPropertyPages(type, &bHideVerbDummy, pNodeList))
  235. return MMC_VERB_PROPERTIES;
  236. return MMC_VERB_NONE;
  237. }
  238. void CTreeNode::OnSetVerbState(LPCONSOLEVERB pConsoleVerb,
  239. DATA_OBJECT_TYPES type,
  240. CNodeList* pNodeList)
  241. {
  242. //
  243. // Use the virtual functions to get the verb state
  244. //
  245. BOOL bHideCut;
  246. BOOL bCanCut = OnSetCutVerbState(type, &bHideCut, pNodeList);
  247. pConsoleVerb->SetVerbState(MMC_VERB_CUT, HIDDEN, bHideCut);
  248. pConsoleVerb->SetVerbState(MMC_VERB_CUT, ENABLED, bCanCut);
  249. BOOL bHideCopy;
  250. BOOL bCanCopy = OnSetCopyVerbState(type, &bHideCopy, pNodeList);
  251. pConsoleVerb->SetVerbState(MMC_VERB_COPY, HIDDEN, bHideCopy);
  252. pConsoleVerb->SetVerbState(MMC_VERB_COPY, ENABLED, bCanCopy);
  253. BOOL bHidePaste;
  254. BOOL bCanPaste = OnSetPasteVerbState(type, &bHidePaste, pNodeList);
  255. pConsoleVerb->SetVerbState(MMC_VERB_PASTE, HIDDEN, bHidePaste);
  256. pConsoleVerb->SetVerbState(MMC_VERB_PASTE, ENABLED, bCanPaste);
  257. BOOL bHidePrint;
  258. BOOL bCanPrint = OnSetPrintVerbState(type, &bHidePrint, pNodeList);
  259. pConsoleVerb->SetVerbState(MMC_VERB_PRINT, HIDDEN, bHidePrint);
  260. pConsoleVerb->SetVerbState(MMC_VERB_PRINT, ENABLED, bCanPrint);
  261. BOOL bHideRename;
  262. BOOL bCanRename = OnSetRenameVerbState(type, &bHideRename, pNodeList);
  263. pConsoleVerb->SetVerbState(MMC_VERB_RENAME, HIDDEN, bHideRename);
  264. pConsoleVerb->SetVerbState(MMC_VERB_RENAME, ENABLED, bCanRename);
  265. // MMC_VERB_PROPERTIES
  266. BOOL bHideProperties;
  267. BOOL bHasProperties = HasPropertyPages(type, &bHideProperties, pNodeList);
  268. pConsoleVerb->SetVerbState(MMC_VERB_PROPERTIES, ENABLED, bHasProperties);
  269. pConsoleVerb->SetVerbState(MMC_VERB_PROPERTIES, HIDDEN, bHideProperties);
  270. // MMC_VERB_DELETE
  271. BOOL bHideDelete;
  272. BOOL bCanDelete = OnSetDeleteVerbState(type, &bHideDelete, pNodeList);
  273. pConsoleVerb->SetVerbState(MMC_VERB_DELETE, ENABLED, bCanDelete);
  274. pConsoleVerb->SetVerbState(MMC_VERB_DELETE, HIDDEN, bHideDelete);
  275. // MMC_VERB_REFRESH
  276. BOOL bHideRefresh;
  277. BOOL bCanRefresh = OnSetRefreshVerbState(type, &bHideRefresh, pNodeList);
  278. pConsoleVerb->SetVerbState(MMC_VERB_REFRESH, ENABLED, bCanRefresh);
  279. pConsoleVerb->SetVerbState(MMC_VERB_REFRESH, HIDDEN, bHideRefresh);
  280. }
  281. HRESULT CTreeNode::OnSetToolbarVerbState(IToolbar*,
  282. CNodeList*)
  283. {
  284. HRESULT hr = S_OK;
  285. //
  286. // Set the button state for each button on the toolbar using
  287. // hr = pToolbar->SetButtonState(event, MMC_BUTTON_STATE, bState);
  288. //
  289. return hr;
  290. }
  291. void CTreeNode::DeleteHelper(CComponentDataObject* pComponentData)
  292. {
  293. ASSERT(pComponentData != NULL);
  294. ASSERT(m_pContainer != NULL);
  295. ASSERT((CTreeNode*)m_pContainer != this);
  296. CContainerNode* pCont = m_pContainer;
  297. VERIFY(m_pContainer->RemoveChildFromList(this));
  298. ASSERT(m_pContainer == NULL);
  299. m_pContainer = pCont; // not in the container's list of children, but still needed
  300. // remove from UI only if the container is visible
  301. if (pCont->IsVisible())
  302. VERIFY(SUCCEEDED(pComponentData->DeleteNode(this))); // remove from the UI
  303. }
  304. void CTreeNode::IncrementSheetLockCount()
  305. {
  306. ++m_nSheetLockCount;
  307. if (m_pContainer != NULL)
  308. m_pContainer->IncrementSheetLockCount();
  309. }
  310. void CTreeNode::DecrementSheetLockCount()
  311. {
  312. --m_nSheetLockCount;
  313. if (m_pContainer != NULL)
  314. m_pContainer->DecrementSheetLockCount();
  315. }
  316. void CTreeNode::OnPropertyChange(CComponentDataObject* pComponentData,
  317. BOOL, long changeMask)
  318. {
  319. // function called when the PPHolder successfully updated the node
  320. ASSERT(pComponentData != NULL);
  321. VERIFY(SUCCEEDED(pComponentData->ChangeNode(this, changeMask)));
  322. }
  323. void CTreeNode::OnCreateSheet()
  324. {
  325. ++m_nSheetCount;
  326. IncrementSheetLockCount();
  327. SetFlagsUp(TN_FLAG_HAS_SHEET, TRUE);
  328. }
  329. void CTreeNode::OnDeleteSheet()
  330. {
  331. DecrementSheetLockCount();
  332. --m_nSheetCount;
  333. SetFlagsUp(TN_FLAG_HAS_SHEET,FALSE);
  334. }
  335. ////////////////////////////////////////////////////////////////////////
  336. // CContainerNode
  337. void CContainerNode::IncrementThreadLockCount()
  338. {
  339. ++m_nThreadLockCount;
  340. if (m_pContainer != NULL)
  341. m_pContainer->IncrementThreadLockCount();
  342. }
  343. void CContainerNode::DecrementThreadLockCount()
  344. {
  345. --m_nThreadLockCount;
  346. if (m_pContainer != NULL)
  347. m_pContainer->DecrementThreadLockCount();
  348. }
  349. BOOL CContainerNode::OnRefresh(CComponentDataObject* pComponentData,
  350. CNodeList* pNodeList)
  351. {
  352. BOOL bRet = TRUE;
  353. if (pNodeList->GetCount() == 1) // single selection
  354. {
  355. if (IsSheetLocked())
  356. {
  357. if (!CanCloseSheets())
  358. return FALSE;
  359. pComponentData->GetPropertyPageHolderTable()->DeleteSheetsOfNode(this);
  360. }
  361. ASSERT(!IsSheetLocked());
  362. RemoveAllChildrenHelper(pComponentData);
  363. ASSERT(!HasChildren());
  364. OnEnumerate(pComponentData);
  365. AddCurrentChildrenToUI(pComponentData);
  366. MarkEnumerated();
  367. }
  368. else // multiple selection
  369. {
  370. POSITION pos = pNodeList->GetHeadPosition();
  371. while (pos != NULL)
  372. {
  373. CTreeNode* pNode = pNodeList->GetNext(pos);
  374. ASSERT(pNode != NULL);
  375. //
  376. // Have each node refresh itself
  377. //
  378. CNodeList nodeList;
  379. nodeList.AddTail(pNode);
  380. if (!pNode->OnRefresh(pComponentData, &nodeList))
  381. {
  382. bRet = FALSE;
  383. }
  384. }
  385. }
  386. return bRet;
  387. }
  388. BOOL CContainerNode::RemoveChildFromList(CTreeNode* p)
  389. {
  390. if (p->IsContainer())
  391. {
  392. if (m_containerChildList.RemoveNode(p))
  393. {
  394. p->m_pContainer = NULL;
  395. return TRUE;
  396. }
  397. }
  398. else
  399. {
  400. if (m_leafChildList.RemoveNode(p))
  401. {
  402. p->m_pContainer = NULL;
  403. return TRUE;
  404. }
  405. }
  406. return FALSE;
  407. }
  408. void CContainerNode::RemoveAllChildrenHelper(CComponentDataObject* pComponentData)
  409. {
  410. ASSERT(pComponentData != NULL);
  411. // remove from the UI
  412. VERIFY(SUCCEEDED(pComponentData->RemoveAllChildren(this)));
  413. // remove from memory, recursively from the bottom
  414. RemoveAllChildrenFromList();
  415. }
  416. void CContainerNode::AddCurrentChildrenToUI(CComponentDataObject* pComponentData)
  417. {
  418. POSITION pos;
  419. //
  420. // Add leaves
  421. //
  422. for( pos = m_leafChildList.GetHeadPosition(); pos != NULL; )
  423. {
  424. CTreeNode* pCurrentChild = m_leafChildList.GetNext(pos);
  425. VERIFY(SUCCEEDED(pComponentData->AddNode(pCurrentChild)));
  426. }
  427. //
  428. // Add Containers
  429. //
  430. for( pos = m_containerChildList.GetHeadPosition(); pos != NULL; )
  431. {
  432. CTreeNode* pCurrentChild = m_containerChildList.GetNext(pos);
  433. VERIFY(SUCCEEDED(pComponentData->AddNode(pCurrentChild)));
  434. }
  435. }
  436. void CContainerNode::SetFlagsDown(DWORD dwNodeFlags, BOOL bSet)
  437. {
  438. CTreeNode::SetFlagsDown(dwNodeFlags,bSet);
  439. // scan the list of children
  440. POSITION pos;
  441. for( pos = m_containerChildList.GetHeadPosition(); pos != NULL; )
  442. {
  443. CTreeNode* pCurrentChild = m_containerChildList.GetNext(pos);
  444. pCurrentChild->SetFlagsDown(dwNodeFlags,bSet);
  445. }
  446. for( pos = m_leafChildList.GetHeadPosition(); pos != NULL; )
  447. {
  448. CTreeNode* pCurrentChild = m_leafChildList.GetNext(pos);
  449. pCurrentChild->SetFlagsDown(dwNodeFlags,bSet);
  450. }
  451. }
  452. void CContainerNode::SetFlagsOnNonContainers(DWORD dwNodeFlags, BOOL bSet)
  453. {
  454. // do not set on urselves, we are a container
  455. // scan the list of children
  456. POSITION pos;
  457. for( pos = m_leafChildList.GetHeadPosition(); pos != NULL; )
  458. {
  459. CTreeNode* pCurrentChild = m_leafChildList.GetNext(pos);
  460. pCurrentChild->SetFlagsDown(dwNodeFlags,bSet);
  461. }
  462. for (pos = m_containerChildList.GetHeadPosition(); pos != NULL; )
  463. {
  464. CTreeNode* pCurrentChild = m_containerChildList.GetNext(pos);
  465. ((CContainerNode*)pCurrentChild)->SetFlagsOnNonContainers(dwNodeFlags,bSet);
  466. }
  467. }
  468. BOOL CContainerNode::AddChildToList(CTreeNode* p)
  469. {
  470. BOOL bRet = FALSE;
  471. p->m_pContainer = this;
  472. if (p->IsContainer())
  473. {
  474. bRet = NULL != m_containerChildList.AddTail(p);
  475. }
  476. else
  477. {
  478. bRet = NULL != m_leafChildList.AddTail(p);
  479. }
  480. return bRet;
  481. }
  482. BOOL CContainerNode::FindChild(CTreeNode* pNode, CTreeNode** ppContainer)
  483. {
  484. *ppContainer = NULL;
  485. if (pNode == NULL)
  486. return FALSE; // no sense in continuing
  487. if (pNode == this)
  488. {
  489. *ppContainer = m_pContainer;
  490. return TRUE; // the node is ourselves
  491. }
  492. //
  493. // If we are looking for a leaf node search the list of leaves first
  494. //
  495. if (!pNode->IsContainer())
  496. {
  497. POSITION pos;
  498. for (pos = m_leafChildList.GetHeadPosition(); pos != NULL; )
  499. {
  500. CLeafNode* pCurrentLeafNode = (CLeafNode*)m_leafChildList.GetNext(pos);
  501. ASSERT(pCurrentLeafNode != NULL);
  502. if (pCurrentLeafNode == pNode)
  503. {
  504. *ppContainer = this;
  505. return TRUE;
  506. }
  507. }
  508. }
  509. //
  510. // scan and recurse the containers if necessary
  511. //
  512. POSITION contPos;
  513. for( contPos = m_containerChildList.GetHeadPosition(); contPos != NULL; )
  514. {
  515. CContainerNode* pCurrentChild = (CContainerNode*)m_containerChildList.GetNext(contPos);
  516. ASSERT(pCurrentChild != NULL);
  517. if (pCurrentChild == pNode)
  518. {
  519. *ppContainer = this;
  520. return TRUE; // we directly contain the node
  521. }
  522. //
  523. // if the current node is a container, look inside it
  524. //
  525. if (pCurrentChild->FindChild(pNode,ppContainer))
  526. {
  527. return TRUE; // got it in the recursion
  528. }
  529. }
  530. return FALSE; // not found
  531. }
  532. BOOL CContainerNode::AddChildToListAndUI(CTreeNode* pChildToAdd, CComponentDataObject* pComponentData)
  533. {
  534. ASSERT(pComponentData != NULL);
  535. VERIFY(AddChildToList(pChildToAdd)); // at the end of the list of children
  536. ASSERT(pChildToAdd->GetContainer() == this); // inserted underneath
  537. // add to UI only if currently visible and already expanded
  538. if (!IsVisible() || !IsExpanded())
  539. return TRUE;
  540. return SUCCEEDED(pComponentData->AddNode(pChildToAdd)); // add to the UI
  541. }
  542. BOOL CContainerNode::AddChildToListAndUISorted(CTreeNode* pChildToAdd, CComponentDataObject* pComponentData)
  543. {
  544. ASSERT(pComponentData != NULL);
  545. VERIFY(AddChildToListSorted(pChildToAdd, pComponentData));
  546. ASSERT(pChildToAdd->GetContainer() == this); // inserted underneath
  547. // add to UI only if currently visible and already expanded
  548. if (!IsVisible() || !IsExpanded())
  549. return TRUE;
  550. return SUCCEEDED(pComponentData->AddNodeSorted(pChildToAdd)); // add to the UI
  551. }
  552. BOOL CContainerNode::AddChildToListSorted(CTreeNode* p, CComponentDataObject*)
  553. {
  554. //
  555. // Containers will be sorted with respect to containers and leaves will be
  556. // sorted with respect to leaves but they won't be intermingled.
  557. //
  558. p->m_pContainer = this;
  559. CNodeList* pChildNodeList = NULL;
  560. if (p->IsContainer())
  561. {
  562. pChildNodeList = &m_containerChildList;
  563. }
  564. else
  565. {
  566. pChildNodeList = &m_leafChildList;
  567. }
  568. //
  569. // Find the position to insert the node in the list in sorted order
  570. //
  571. POSITION pos = pChildNodeList->GetHeadPosition();
  572. while (pos != NULL)
  573. {
  574. CTreeNode* pNodeInList = pChildNodeList->GetAt(pos);
  575. if (_wcsicoll(p->GetDisplayName(), pNodeInList->GetDisplayName()) < 0)
  576. {
  577. break;
  578. }
  579. pChildNodeList->GetNext(pos);
  580. }
  581. if (pos == NULL)
  582. {
  583. return NULL != pChildNodeList->AddTail(p);
  584. }
  585. return NULL != pChildNodeList->InsertBefore(pos, p);
  586. }
  587. void CContainerNode::RemoveAllChildrenFromList()
  588. {
  589. RemoveAllContainersFromList();
  590. RemoveAllLeavesFromList();
  591. }
  592. int CContainerNode::Compare(CTreeNode* pNodeA, CTreeNode* pNodeB, int nCol, LPARAM)
  593. {
  594. // default sorting behavior
  595. LPCTSTR lpszA = pNodeA->GetString(nCol);
  596. LPCTSTR lpszB = pNodeB->GetString(nCol);
  597. // cannot process NULL strings, have to use ""
  598. ASSERT(lpszA != NULL);
  599. ASSERT(lpszB != NULL);
  600. return _tcsicoll( (lpszA != NULL) ? lpszA : g_lpszNullString, (lpszB != NULL) ? lpszB : g_lpszNullString);
  601. }
  602. void CContainerNode::ForceEnumeration(CComponentDataObject* pComponentData)
  603. {
  604. if (IsEnumerated())
  605. return;
  606. OnEnumerate(pComponentData);
  607. MarkEnumerated();
  608. }
  609. void CContainerNode::MarkEnumerated(BOOL bEnum)
  610. {
  611. ASSERT(IsContainer());
  612. if (bEnum)
  613. m_dwNodeFlags |= TN_FLAG_CONTAINER_ENUM;
  614. else
  615. m_dwNodeFlags &= ~TN_FLAG_CONTAINER_ENUM;
  616. }
  617. void CContainerNode::MarkEnumeratedAndLoaded(CComponentDataObject* pComponentData)
  618. {
  619. MarkEnumerated();
  620. OnChangeState(pComponentData); // move to loading
  621. OnChangeState(pComponentData); // move to loaded
  622. }
  623. /////////////////////////////////////////////////////////////////////////////
  624. // CBackgroundThread
  625. CBackgroundThread::CBackgroundThread()
  626. {
  627. m_pQueryObj = NULL;
  628. m_bAutoDelete = FALSE;
  629. m_bAbandoned = FALSE;
  630. m_pContNode = NULL;
  631. m_hEventHandle = NULL;
  632. ExceptionPropagatingInitializeCriticalSection(&m_cs);
  633. m_nQueueCountMax = 10;
  634. }
  635. CBackgroundThread::~CBackgroundThread()
  636. {
  637. TRACE(_T("CBackgroundThread::~CBackgroundThread()\n"));
  638. ASSERT(IsAbandoned() || IsQueueEmpty());
  639. ::DeleteCriticalSection(&m_cs);
  640. if (m_hEventHandle != NULL)
  641. {
  642. VERIFY(::CloseHandle(m_hEventHandle));
  643. m_hEventHandle = NULL;
  644. }
  645. if (m_pQueryObj != NULL)
  646. {
  647. delete m_pQueryObj;
  648. m_pQueryObj = NULL;
  649. }
  650. }
  651. void CBackgroundThread::SetQueryObj(CQueryObj* pQueryObj)
  652. {
  653. ASSERT(pQueryObj != NULL);
  654. m_pQueryObj = pQueryObj;
  655. m_pQueryObj->SetThread(this);
  656. }
  657. BOOL CBackgroundThread::Start(CMTContainerNode* pNode, CComponentDataObject* pComponentData)
  658. {
  659. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  660. ASSERT(m_pContNode == NULL);
  661. m_pContNode = pNode;
  662. m_hHiddenWnd = pComponentData->GetHiddenWindow();
  663. ASSERT(m_hEventHandle == NULL); // cannot call start twice or reuse the same C++ object
  664. m_hEventHandle = ::CreateEvent(NULL,TRUE /*bManualReset*/,FALSE /*signalled*/, NULL);
  665. if (m_hEventHandle == NULL)
  666. return FALSE;
  667. return CreateThread();
  668. }
  669. int CBackgroundThread::Run()
  670. {
  671. ASSERT(m_pContNode != NULL);
  672. ASSERT(m_pQueryObj != NULL);
  673. TRACE(_T("CBackgroundThread::Run() started\n"));
  674. while (m_pQueryObj->Enumerate());
  675. // before exiting, have to make sure there are no items in the queue
  676. if (!IsQueueEmpty())
  677. VERIFY(PostHaveData());
  678. VERIFY(PostExiting());
  679. // wait for the main thread to acknowledge the exiting message
  680. WaitForExitAcknowledge();
  681. ASSERT(IsAbandoned() || IsQueueEmpty()); // we cannot lose items in the queue
  682. TRACE(_T("CBackgroundThread::Run() terminated\n"));
  683. return 0;
  684. }
  685. void CBackgroundThread::Abandon()
  686. {
  687. Lock();
  688. TRACE(_T("CBackgroundThread::Abandon()\n"));
  689. m_bAutoDelete = TRUE;
  690. m_bAbandoned = TRUE;
  691. Unlock();
  692. VERIFY(0 != ::SetEvent(m_hEventHandle));
  693. }
  694. BOOL CBackgroundThread::IsAbandoned()
  695. {
  696. Lock();
  697. BOOL b = m_bAbandoned;
  698. Unlock();
  699. return b;
  700. }
  701. BOOL CBackgroundThread::OnAddToQueue(INT_PTR nCount)
  702. {
  703. BOOL bPostedMessage = FALSE;
  704. if (nCount >= m_nQueueCountMax)
  705. {
  706. VERIFY(PostHaveData());
  707. bPostedMessage = TRUE;
  708. }
  709. return bPostedMessage;
  710. }
  711. CObjBase* CBackgroundThread::RemoveFromQueue()
  712. {
  713. Lock();
  714. ASSERT(m_pQueryObj != NULL);
  715. CObjBaseList* pQueue = m_pQueryObj->GetQueue();
  716. CObjBase* p = pQueue->IsEmpty() ? NULL : pQueue->RemoveHead();
  717. Unlock();
  718. return p;
  719. }
  720. BOOL CBackgroundThread::IsQueueEmpty()
  721. {
  722. Lock();
  723. ASSERT(m_pQueryObj != NULL);
  724. CObjBaseList* pQueue = m_pQueryObj->GetQueue();
  725. BOOL bRes = pQueue->IsEmpty();
  726. Unlock();
  727. return bRes;
  728. }
  729. BOOL CBackgroundThread::PostHaveData()
  730. {
  731. return PostMessageToComponentDataRaw(CHiddenWnd::s_NodeThreadHaveDataNotificationMessage,
  732. (WPARAM)m_pContNode, (LPARAM)0);
  733. }
  734. BOOL CBackgroundThread::PostError(DWORD dwErr)
  735. {
  736. return PostMessageToComponentDataRaw(CHiddenWnd::s_NodeThreadErrorNotificationMessage,
  737. (WPARAM)m_pContNode, (LPARAM)dwErr);
  738. }
  739. BOOL CBackgroundThread::PostExiting()
  740. {
  741. return PostMessageToComponentDataRaw(CHiddenWnd::s_NodeThreadExitingNotificationMessage,
  742. (WPARAM)m_pContNode, (LPARAM)0);
  743. }
  744. BOOL CBackgroundThread::PostMessageToComponentDataRaw(UINT Msg, WPARAM wParam, LPARAM lParam)
  745. {
  746. BOOL b = IsAbandoned();
  747. if (b)
  748. {
  749. return TRUE; // no need to post
  750. }
  751. ASSERT(m_pContNode != NULL);
  752. ASSERT(m_hHiddenWnd != NULL);
  753. ASSERT(::IsWindow(m_hHiddenWnd));
  754. return ::PostMessage(m_hHiddenWnd, Msg, wParam, lParam);
  755. }
  756. void CBackgroundThread::WaitForExitAcknowledge()
  757. {
  758. VERIFY(WAIT_OBJECT_0 == ::WaitForSingleObject(m_hEventHandle,INFINITE));
  759. }
  760. ////////////////////////////////////////////////////////////////////////
  761. // CMTContainerNode
  762. CMTContainerNode::~CMTContainerNode()
  763. {
  764. ASSERT(m_pThread == NULL);
  765. }
  766. BOOL CMTContainerNode::OnEnumerate(CComponentDataObject* pComponentData, BOOL bAsync)
  767. {
  768. OnChangeState(pComponentData);
  769. VERIFY(StartBackgroundThread(pComponentData, bAsync));
  770. return FALSE; // children not added, the thread will add them later
  771. }
  772. BOOL CMTContainerNode::OnRefresh(CComponentDataObject* pComponentData,
  773. CNodeList* pNodeList)
  774. {
  775. BOOL bRet = TRUE;
  776. if (pNodeList->GetCount() == 1) // single selection
  777. {
  778. BOOL bLocked = IsThreadLocked();
  779. ASSERT(!bLocked); // cannot do refresh on locked node, the UI should prevent this
  780. if (bLocked)
  781. return FALSE;
  782. if (IsSheetLocked())
  783. {
  784. if (!CanCloseSheets())
  785. {
  786. pComponentData->GetPropertyPageHolderTable()->BroadcastSelectPage(this, -1);
  787. return FALSE;
  788. }
  789. pComponentData->GetPropertyPageHolderTable()->DeleteSheetsOfNode(this);
  790. }
  791. ASSERT(!IsSheetLocked());
  792. RemoveAllChildrenHelper(pComponentData);
  793. ASSERT(!HasChildren());
  794. OnEnumerate(pComponentData); // will spawn a thread to do enumeration
  795. MarkEnumerated();
  796. }
  797. else // multiple selection
  798. {
  799. POSITION pos = pNodeList->GetHeadPosition();
  800. while (pos != NULL)
  801. {
  802. CTreeNode* pNode = pNodeList->GetNext(pos);
  803. ASSERT(pNode != NULL);
  804. CNodeList nodeList;
  805. nodeList.AddTail(pNode);
  806. if (!pNode->OnRefresh(pComponentData, &nodeList))
  807. {
  808. bRet = FALSE;
  809. }
  810. }
  811. }
  812. return TRUE;
  813. }
  814. void CMTContainerNode::AbandonThread(CComponentDataObject* pComponentData)
  815. {
  816. if(m_pThread == NULL) // nothing running
  817. return;
  818. m_pThread->Abandon();
  819. m_pThread = NULL;
  820. pComponentData->GetRunningThreadTable()->Remove(this);
  821. }
  822. BOOL CMTContainerNode::StartBackgroundThread(CComponentDataObject* pComponentData, BOOL bAsync)
  823. {
  824. ASSERT(m_pThread == NULL); // nothing running
  825. // notify the UI to change icon, if needed
  826. VERIFY(SUCCEEDED(pComponentData->ChangeNode(this, CHANGE_RESULT_ITEM_ICON)));
  827. m_pThread = CreateThreadObject();
  828. ASSERT(m_pThread != NULL);
  829. m_pThread->SetQueryObj(OnCreateQuery());
  830. BOOL bRes = m_pThread->Start(this, pComponentData);
  831. if (bRes)
  832. {
  833. pComponentData->GetRunningThreadTable()->Add(this);
  834. // we need to call UpdateVerbState() because the lock count changed
  835. // by adding the node from the running thread table
  836. VERIFY(SUCCEEDED(pComponentData->UpdateVerbState(this)));
  837. }
  838. //
  839. // If we don't want this call to be asynchronous then we have to wait for
  840. // the thread to finish
  841. //
  842. if (!bAsync)
  843. {
  844. pComponentData->WaitForThreadExitMessage(this);
  845. }
  846. return bRes;
  847. }
  848. void CMTContainerNode::OnThreadHaveDataNotification(CComponentDataObject* pComponentDataObject)
  849. {
  850. ASSERT(m_pThread != NULL);
  851. ASSERT(IsThreadLocked());
  852. // do data transfer from thread queue
  853. CObjBase* p = m_pThread->RemoveFromQueue();
  854. while (p)
  855. {
  856. // add new node to the list of children and propagate to the UI
  857. OnHaveData(p,pComponentDataObject);
  858. p = m_pThread->RemoveFromQueue();
  859. }
  860. }
  861. void CMTContainerNode::OnThreadErrorNotification(DWORD dwErr, CComponentDataObject*)
  862. {
  863. ASSERT(m_pThread != NULL);
  864. ASSERT(IsThreadLocked());
  865. OnError(dwErr);
  866. }
  867. void CMTContainerNode::OnThreadExitingNotification(CComponentDataObject* pComponentDataObject)
  868. {
  869. ASSERT(m_pThread != NULL);
  870. ASSERT(IsThreadLocked());
  871. #if (TRUE)
  872. // let the thread know it can shut down
  873. m_pThread->AcknowledgeExiting();
  874. VERIFY(WAIT_OBJECT_0 == ::WaitForSingleObject(m_pThread->m_hThread,INFINITE));
  875. OnChangeState(pComponentDataObject);
  876. delete m_pThread;
  877. m_pThread = NULL;
  878. pComponentDataObject->GetRunningThreadTable()->Remove(this);
  879. // we need to call UpdateVerbState() because the lock count changed
  880. // by removing the node from the running thread table
  881. VERIFY(SUCCEEDED(pComponentDataObject->UpdateVerbState(this)));
  882. TRACE(_T("OnThreadExitingNotification()\n"));
  883. #else // maybe better way of doing it???
  884. // we are going to detach from the thread, so make copies of variables
  885. HANDLE hThread = m_pThread->m_hThread;
  886. CBackgroundThread* pThread = m_pThread;
  887. AbandonThread(pComponentDataObject); // sets m_pThread = NULL
  888. // acknowledge to thread
  889. pThread->AcknowledgeExiting();
  890. VERIFY(WAIT_OBJECT_0 == ::WaitForSingleObject(hThread,INFINITE));
  891. OnChangeState(pComponentDataObject);
  892. #endif
  893. VERIFY(SUCCEEDED(pComponentDataObject->SortResultPane(this)));
  894. }
  895. ///////////////////////////////////////////////////////////////////////////////