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.

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