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.

2805 lines
77 KiB

  1. // ScopeNode.cpp : Implementation of CBOMSnapApp and DLL registration.
  2. #include "stdafx.h"
  3. #include "streamio.h"
  4. #include "BOMSnap.h"
  5. #include "ScopeNode.h"
  6. #include "atlgdi.h"
  7. #include "rootprop.h"
  8. #include "qryprop.h"
  9. #include "grpprop.h"
  10. #include "namemap.h"
  11. #include "query.h"
  12. #include "compont.h"
  13. #include "compdata.h"
  14. #include "wizards.h"
  15. #include "cmndlgs.h"
  16. #include <algorithm>
  17. #include <lmcons.h> // for UNLEN
  18. extern HWND g_hwndMain;
  19. extern DWORD g_dwFileVer; // Current console file version (from compdata.cpp)
  20. // Register static clipboard format members
  21. UINT CScopeNode::m_cfDisplayName = RegisterClipboardFormat(TEXT("CCF_DISPLAY_NAME"));
  22. UINT CScopeNode::m_cfSnapInClsid = RegisterClipboardFormat(TEXT("CCF_SNAPIN_CLASSID"));
  23. UINT CScopeNode::m_cfNodeType = RegisterClipboardFormat(TEXT("CCF_NODETYPE"));
  24. UINT CScopeNode::m_cfszNodeType = RegisterClipboardFormat(TEXT("CCF_SZNODETYPE"));
  25. UINT CScopeNode::m_cfNodeID2 = RegisterClipboardFormat(TEXT("CCF_NODEID2"));
  26. UINT CScopeNode::m_cfColumnSetID = RegisterClipboardFormat(TEXT("CCF_COLUMN_SET_ID"));
  27. // {316A1EEA-C249-44e0-958B-00D2AB989D2F}
  28. static const GUID GUID_RootNode =
  29. { 0x316a1eea, 0xc249, 0x44e0, { 0x95, 0x8b, 0x0, 0xd2, 0xab, 0x98, 0x9d, 0x2f } };
  30. // {2A34413B-B565-469e-9C28-5E733768264F}
  31. static const GUID GUID_GroupNode =
  32. { 0x2a34413b, 0xb565, 0x469e, { 0x9c, 0x28, 0x5e, 0x73, 0x37, 0x68, 0x26, 0x4f } };
  33. // {1030A359-F520-4748-95CA-8C8CEFA5C63F}
  34. static const GUID GUID_QueryNode =
  35. { 0x1030a359, 0xf520, 0x4748, { 0x95, 0xca, 0x8c, 0x8c, 0xef, 0xa5, 0xc6, 0x3f } };
  36. /////////////////////////////////////////////////////////////////////////////
  37. //
  38. // CScopeNode
  39. //
  40. /////////////////////////////////////////////////////////////////////////////
  41. HRESULT CScopeNode::CreateNode(NODETYPE nodetype, CScopeNode** ppnode)
  42. {
  43. VALIDATE_POINTER( ppnode );
  44. HRESULT hr = E_FAIL;
  45. switch( nodetype )
  46. {
  47. case GROUP_NODETYPE:
  48. {
  49. CComObject<CGroupNode>* pGroupNode = NULL;
  50. hr = CComObject<CGroupNode>::CreateInstance(&pGroupNode);
  51. *ppnode = pGroupNode;
  52. }
  53. break;
  54. case QUERY_NODETYPE:
  55. {
  56. CComObject<CQueryNode>* pQueryNode = NULL;
  57. hr = CComObject<CQueryNode>::CreateInstance(&pQueryNode);
  58. *ppnode = pQueryNode;
  59. }
  60. break;
  61. case ROOT_NODETYPE:
  62. {
  63. CComObject<CRootNode>* pRootNode = NULL;
  64. hr = CComObject<CRootNode>::CreateInstance(&pRootNode);
  65. *ppnode = pRootNode;
  66. }
  67. break;
  68. default:
  69. ASSERT(0 && "Invalid node type");
  70. }
  71. // return addref'd object
  72. if( SUCCEEDED(hr) )
  73. (*ppnode)->AddRef();
  74. return hr;
  75. }
  76. // AddNewChild should only be called when a new node is created, not
  77. // to add an existing node, such as when loading the node tree from
  78. // a console file.
  79. HRESULT CScopeNode::AddNewChild(CScopeNode* pnodeChild, LPCWSTR pszName)
  80. {
  81. VALIDATE_POINTER(pnodeChild);
  82. ASSERT(pszName && pszName[0]);
  83. // Assign permanent node ID
  84. // Root node tracks last used ID in its lNodeID member
  85. CRootNode* pRootNode = GetRootNode();
  86. pnodeChild->m_lNodeID = pRootNode ? ++(pRootNode->m_lNodeID) : 0;
  87. // Assign parent node
  88. pnodeChild->m_pnodeParent = static_cast<CScopeNode*>(this);
  89. // Now that node has parent we can set the name
  90. // (needs parent to get to IStringTable)
  91. HRESULT hr = pnodeChild->SetName(pszName);
  92. ASSERT(SUCCEEDED(hr));
  93. RETURN_ON_FAILURE( hr );
  94. // In order to persist the Column Data, we'll need to get a unique ID
  95. hr = CoCreateGuid(&m_gColumnID);
  96. ASSERT(SUCCEEDED(hr));
  97. RETURN_ON_FAILURE( hr );
  98. return AddChild(pnodeChild);
  99. }
  100. // Call AddChild to add a new node or a moved node to the parent node
  101. HRESULT CScopeNode::AddChild(CScopeNode* pnodeChild)
  102. {
  103. VALIDATE_POINTER( pnodeChild );
  104. HRESULT hr = S_OK;
  105. // Add new child to end of child list
  106. if( m_pnodeChild == NULL )
  107. m_pnodeChild = pnodeChild;
  108. else
  109. {
  110. CScopeNode* pnodePrev = m_pnodeChild;
  111. while( pnodePrev->Next() )
  112. pnodePrev = pnodePrev->Next();
  113. pnodePrev->m_pnodeNext = pnodeChild;
  114. }
  115. // Assign parent node
  116. pnodeChild->m_pnodeParent = static_cast<CScopeNode*>(this);
  117. pnodeChild->AddRef();
  118. // if this node has been added to the scope pane
  119. if( m_hScopeItem != NULL )
  120. {
  121. IConsoleNameSpace* pNameSpace = GetCompData()->GetNameSpace();
  122. ASSERT( pNameSpace );
  123. if( !pNameSpace ) return E_FAIL;
  124. SCOPEDATAITEM sdi;
  125. sdi.ID = m_hScopeItem;
  126. sdi.mask = SDI_STATE;
  127. // Has it been expanded?
  128. HRESULT hr2 = pNameSpace->GetItem(&sdi);
  129. if( SUCCEEDED(hr2) && (sdi.nState & MMC_SCOPE_ITEM_STATE_EXPANDEDONCE) )
  130. {
  131. hr = pnodeChild->Insert(pNameSpace);
  132. }
  133. else
  134. {
  135. // if can't add children yet, then set children to show the '+'
  136. SCOPEDATAITEM sdi2;
  137. sdi2.ID = m_hScopeItem;
  138. sdi2.mask = SDI_CHILDREN;
  139. sdi2.cChildren = 1;
  140. pNameSpace->SetItem(&sdi2);
  141. }
  142. }
  143. // Force refresh on both child and parent because a query node may be modified
  144. // by its new parent and a group node is always modified by its children
  145. OnRefresh(NULL);
  146. pnodeChild->OnRefresh(NULL);
  147. return hr;
  148. }
  149. HRESULT CScopeNode::RemoveChild(CScopeNode* pnodeDelete)
  150. {
  151. VALIDATE_POINTER(pnodeDelete);
  152. ASSERT(pnodeDelete->Parent() == this);
  153. // if deleting the first child
  154. if( m_pnodeChild == pnodeDelete )
  155. {
  156. // just set first child to its next sibling
  157. m_pnodeChild = m_pnodeChild->Next();
  158. }
  159. else
  160. {
  161. // Locate preceding sibling
  162. CScopeNode* pnodePrev = m_pnodeChild;
  163. while( pnodePrev && pnodePrev->Next() != pnodeDelete )
  164. {
  165. pnodePrev = pnodePrev->Next();
  166. }
  167. // remove deleted node from list
  168. if( pnodePrev )
  169. {
  170. pnodePrev->m_pnodeNext = pnodeDelete->Next();
  171. }
  172. }
  173. pnodeDelete->m_pnodeNext = NULL;
  174. // release the node
  175. pnodeDelete->Release();
  176. // Do refresh in case this is a group node losing a child
  177. OnRefresh(NULL);
  178. return S_OK;
  179. }
  180. CRootNode* CScopeNode::GetRootNode()
  181. {
  182. CScopeNode* pNode = this;
  183. while( pNode && !pNode->IsRootNode() )
  184. {
  185. pNode = pNode->Parent();
  186. }
  187. return static_cast<CRootNode*>(pNode);
  188. }
  189. CComponentData* CScopeNode::GetCompData()
  190. {
  191. CRootNode* pRootNode = GetRootNode();
  192. return pRootNode ? pRootNode->GetRootCompData() : NULL;
  193. }
  194. CScopeNode::~CScopeNode()
  195. {
  196. // Release all nodes on child list
  197. OnRemoveChildren(NULL);
  198. }
  199. HRESULT CScopeNode::GetDataImpl(UINT cf, HGLOBAL* phGlobal)
  200. {
  201. VALIDATE_POINTER( phGlobal );
  202. HRESULT hr = DV_E_FORMATETC;
  203. if( cf == m_cfDisplayName )
  204. {
  205. hr = DataToGlobal(phGlobal, m_strName.c_str(), (m_strName.size() + 1) * sizeof(WCHAR));
  206. }
  207. else if( cf == m_cfSnapInClsid )
  208. {
  209. hr = DataToGlobal(phGlobal, &CLSID_BOMSnapIn, sizeof(GUID));
  210. }
  211. else if( cf == m_cfNodeType )
  212. {
  213. hr = DataToGlobal(phGlobal, NodeTypeGuid(), sizeof(GUID));
  214. }
  215. else if( cf == m_cfszNodeType )
  216. {
  217. WCHAR szGuid[GUID_STRING_LEN+1];
  218. StringFromGUID2(*NodeTypeGuid(), szGuid, GUID_STRING_LEN+1);
  219. hr = DataToGlobal(phGlobal, szGuid, GUID_STRING_SIZE);
  220. }
  221. else if( cf == m_cfNodeID2 )
  222. {
  223. // return SNodeID2 struct with the node's ID
  224. // For a root node always return 1; m_lNodeID for a root node holds the last ID
  225. // assigned to an enumerated node. It is incremented for each new child node.
  226. int nSize = sizeof(SNodeID2) + sizeof(long) - 1;
  227. SNodeID2* pNodeID = reinterpret_cast<SNodeID2*>(malloc( nSize ));
  228. if( !pNodeID ) return E_OUTOFMEMORY;
  229. pNodeID->dwFlags = 0;
  230. pNodeID->cBytes = sizeof(long);
  231. *((long*)(pNodeID->id)) = IsRootNode() ? 1 : m_lNodeID;
  232. hr = DataToGlobal( phGlobal, pNodeID, nSize );
  233. free( pNodeID );
  234. }
  235. else if( cf == m_cfColumnSetID)
  236. {
  237. int nSize2 = sizeof(SColumnSetID) + sizeof(m_gColumnID) - 1;
  238. SColumnSetID* pColumnSetID = reinterpret_cast<SColumnSetID*>(malloc( nSize2 ));
  239. if( !pColumnSetID ) return E_OUTOFMEMORY;
  240. pColumnSetID->dwFlags = 0;
  241. pColumnSetID->cBytes = sizeof(m_gColumnID);
  242. ::CopyMemory(pColumnSetID->id, &m_gColumnID, pColumnSetID->cBytes);
  243. hr = DataToGlobal( phGlobal, pColumnSetID, nSize2 );
  244. free( pColumnSetID );
  245. }
  246. return hr;
  247. }
  248. HRESULT CScopeNode::GetDisplayInfo(RESULTDATAITEM* pRDI)
  249. {
  250. VALIDATE_POINTER( pRDI );
  251. if( pRDI->bScopeItem )
  252. {
  253. ASSERT(pRDI->lParam == reinterpret_cast<LPARAM>(this));
  254. if( pRDI->mask & RDI_STR )
  255. pRDI->str = const_cast<LPWSTR>(GetName());
  256. if( pRDI->mask & RDI_IMAGE )
  257. pRDI->nImage = GetImage();
  258. return S_OK;
  259. }
  260. return E_INVALIDARG;
  261. }
  262. HRESULT CScopeNode::GetDisplayInfo(SCOPEDATAITEM* pSDI)
  263. {
  264. VALIDATE_POINTER( pSDI );
  265. if( pSDI->mask & SDI_STR )
  266. pSDI->displayname = const_cast<LPWSTR>(GetName());
  267. if( pSDI->mask & SDI_IMAGE )
  268. pSDI->nImage = GetImage();
  269. if( pSDI->mask & SDI_OPENIMAGE )
  270. pSDI->nOpenImage = GetOpenImage();
  271. if( pSDI->mask & SDI_CHILDREN )
  272. pSDI->cChildren = HasChildren() ? 1 : 0;
  273. if( pSDI->mask & SDI_PARAM )
  274. pSDI->lParam = reinterpret_cast<LPARAM>(this);
  275. return S_OK;
  276. }
  277. HRESULT CScopeNode::AttachComponent(CComponent* pComponent)
  278. {
  279. VALIDATE_POINTER( pComponent );
  280. if( std::find(m_vComponents.begin(), m_vComponents.end(), pComponent) != m_vComponents.end() )
  281. return S_FALSE;
  282. m_vComponents.push_back(pComponent);
  283. return S_OK;
  284. }
  285. HRESULT CScopeNode::DetachComponent(CComponent* pComponent)
  286. {
  287. VALIDATE_POINTER( pComponent );
  288. std::vector<CComponent*>::iterator it = std::find(m_vComponents.begin(), m_vComponents.end(), pComponent);
  289. if( it == m_vComponents.end() )
  290. return S_FALSE;
  291. m_vComponents.erase(it);
  292. return S_OK;
  293. }
  294. BOOL CScopeNode::OwnsConsoleView(LPCONSOLE2 pConsole)
  295. {
  296. if( !pConsole ) return FALSE;
  297. std::vector<CComponent*>::iterator it;
  298. for( it = m_vComponents.begin(); it != m_vComponents.end(); ++it )
  299. {
  300. if( (*it)->GetConsole() == pConsole )
  301. return TRUE;
  302. }
  303. return FALSE;
  304. }
  305. HRESULT CScopeNode::GetResultViewType(LPOLESTR* ppViewType, long* pViewOptions)
  306. {
  307. return S_FALSE;
  308. }
  309. /************************************************************************************
  310. * Notification handlers
  311. ************************************************************************************/
  312. BEGIN_NOTIFY_MAP(CScopeNode)
  313. ON_NOTIFY(MMCN_CONTEXTHELP, OnHelp)
  314. ON_SELECT()
  315. ON_EXPAND()
  316. ON_RENAME()
  317. ON_REMOVE_CHILDREN()
  318. ON_ADD_IMAGES()
  319. END_NOTIFY_MAP()
  320. HRESULT CScopeNode::OnHelp(LPCONSOLE2 pConsole, LPARAM /*arg*/, LPARAM /*param*/)
  321. {
  322. VALIDATE_POINTER( pConsole );
  323. tstring strHelpFile = _T("");
  324. tstring strHelpTopic = _T("");
  325. tstring strHelpFull = _T("");
  326. strHelpFile = StrLoadString(IDS_HELPFILE);
  327. if( strHelpFile.empty() ) return E_FAIL;
  328. // Special Hack to get a different help topic for the first two nodes.
  329. switch( m_lNodeID )
  330. {
  331. case 2:
  332. {
  333. // Users Node
  334. strHelpTopic = StrLoadString(IDS_USERSHELPTOPIC);
  335. break;
  336. }
  337. case 3:
  338. {
  339. // Printers Node
  340. strHelpTopic = StrLoadString(IDS_PRINTERSHELPTOPIC);
  341. break;
  342. }
  343. default:
  344. {
  345. strHelpTopic = StrLoadString(IDS_DEFAULTHELPTOPIC);
  346. break;
  347. }
  348. }
  349. // Verify that we got a help topic!
  350. if( strHelpTopic.empty() ) return E_FAIL;
  351. // Build path to %systemroot%\help
  352. TCHAR szWindowsDir[MAX_PATH+1] = {0};
  353. UINT nSize = GetSystemWindowsDirectory( szWindowsDir, MAX_PATH );
  354. if( nSize == 0 || nSize > MAX_PATH )
  355. {
  356. return E_FAIL;
  357. }
  358. strHelpFull = szWindowsDir;
  359. strHelpFull += _T("\\Help\\");
  360. strHelpFull += strHelpFile;
  361. strHelpFull += _T("::/");
  362. strHelpFull += strHelpTopic;
  363. // Show the Help topic
  364. CComQIPtr<IDisplayHelp> spHelp = pConsole;
  365. if( !spHelp ) return E_NOINTERFACE;
  366. return spHelp->ShowTopic( (LPTSTR)strHelpFull.c_str() );
  367. }
  368. HRESULT CScopeNode::Insert(LPCONSOLENAMESPACE pNameSpace)
  369. {
  370. if( !pNameSpace ) return E_POINTER;
  371. if( !m_pnodeParent ) return E_FAIL;
  372. ASSERT( m_pnodeParent->m_hScopeItem != 0 );
  373. ASSERT( m_hScopeItem == 0 );
  374. // if not set yet, get name from string table (mmc will ask for it after insertion)
  375. // (name will be set a new node and not set for reloaded nodes)
  376. if( m_strName.empty() )
  377. {
  378. IStringTable* pStringTable = GetCompData()->GetStringTable();
  379. ASSERT( pStringTable );
  380. if( !pStringTable ) return E_FAIL;
  381. HRESULT hr = StringTableRead(pStringTable, m_nameID, m_strName);
  382. ASSERT(SUCCEEDED(hr));
  383. RETURN_ON_FAILURE(hr);
  384. }
  385. SCOPEDATAITEM sdi;
  386. sdi.mask = SDI_STR | SDI_IMAGE | SDI_OPENIMAGE | SDI_PARAM | SDI_CHILDREN | SDI_PARENT;
  387. sdi.relativeID = m_pnodeParent->m_hScopeItem;
  388. sdi.displayname = MMC_TEXTCALLBACK; // MMC only allows callback for name
  389. sdi.nImage = GetImage();
  390. sdi.nOpenImage = GetOpenImage();
  391. sdi.cChildren = HasChildren() ? 1 : 0;
  392. sdi.lParam = reinterpret_cast<LPARAM>(this);
  393. HRESULT hr = pNameSpace->InsertItem(&sdi);
  394. if( SUCCEEDED(hr) )
  395. m_hScopeItem = sdi.ID;
  396. return hr;
  397. }
  398. HRESULT CScopeNode::OnExpand(LPCONSOLE2 pConsole, BOOL bExpand, HSCOPEITEM hScopeItem)
  399. {
  400. VALIDATE_POINTER( pConsole );
  401. // Nothing to do on collapse
  402. if( !bExpand )
  403. return S_OK;
  404. // Scope item ID shouldn't change
  405. ASSERT(m_hScopeItem == 0 || m_hScopeItem == hScopeItem);
  406. // Save scope item ID
  407. m_hScopeItem = hScopeItem;
  408. // If expanding root node
  409. if( m_pnodeParent == NULL )
  410. {
  411. // Get Scope image list interface
  412. IImageListPtr spImageList;
  413. HRESULT hr = pConsole->QueryScopeImageList(&spImageList);
  414. ASSERT(SUCCEEDED(hr));
  415. // Add standard images to the scope pane
  416. if( SUCCEEDED(hr) )
  417. {
  418. hr = OnAddImages(pConsole, spImageList);
  419. ASSERT(SUCCEEDED(hr));
  420. }
  421. }
  422. // Get namespace interface
  423. IConsoleNameSpace* pNameSpace = GetCompData()->GetNameSpace();
  424. if( pNameSpace == NULL )
  425. return E_FAIL;
  426. // Step through child list and add each one to scope pane
  427. CScopeNode* pnode = FirstChild();
  428. while( pnode != NULL )
  429. {
  430. pnode->Insert(pNameSpace);
  431. pnode = pnode->m_pnodeNext;
  432. }
  433. return S_OK;
  434. }
  435. HRESULT CScopeNode::OnRename(LPCONSOLE2 pConsole, LPCWSTR pszName)
  436. {
  437. if( pszName == NULL || pszName[0] == 0 )
  438. return E_INVALIDARG;
  439. return SetName(pszName);
  440. }
  441. HRESULT CScopeNode::SetName(LPCWSTR pszName)
  442. {
  443. if( !pszName || !pszName[0] ) return E_POINTER;
  444. IStringTable* pStringTable = GetCompData()->GetStringTable();
  445. ASSERT( pStringTable );
  446. if( !pStringTable ) return E_FAIL;
  447. HRESULT hr = StringTableWrite(pStringTable, pszName, &m_nameID);
  448. RETURN_ON_FAILURE(hr);
  449. m_strName = pszName;
  450. return S_OK;
  451. }
  452. HRESULT CScopeNode::OnRemoveChildren(LPCONSOLE2 pConsole)
  453. {
  454. // Step through child list and release each one
  455. CScopeNode* pnode = m_pnodeChild;
  456. while( pnode != NULL )
  457. {
  458. CScopeNode* pnodeNext = pnode->m_pnodeNext;
  459. pnode->Release();
  460. pnode = pnodeNext;
  461. }
  462. m_pnodeChild = NULL;
  463. return S_OK;
  464. }
  465. HRESULT CScopeNode::OnAddImages(LPCONSOLE2 pConsole, LPIMAGELIST pImageList)
  466. {
  467. VALIDATE_POINTER(pImageList);
  468. CBitmap bmp16;
  469. CBitmap bmp32;
  470. bmp16.LoadBitmap(IDB_QUERY16);
  471. bmp32.LoadBitmap(IDB_QUERY32);
  472. ASSERT(bmp16 != (HBITMAP)NULL && (HBITMAP)bmp32 != (HBITMAP)NULL);
  473. if( bmp16 == (HBITMAP)NULL || bmp32 == (HBITMAP)NULL )
  474. return E_FAIL;
  475. HRESULT hr = pImageList->ImageListSetStrip(
  476. (LONG_PTR*)static_cast<HBITMAP>(bmp16),
  477. (LONG_PTR*)static_cast<HBITMAP>(bmp32),
  478. 0, RGB(255,0,255));
  479. return hr;
  480. }
  481. HRESULT CScopeNode::OnSelect(LPCONSOLE2 pConsole, BOOL bSelect, BOOL bScope)
  482. {
  483. VALIDATE_POINTER( pConsole );
  484. // See CScopeNode::OnRefresh for explanation of m_bIgnoreSelect
  485. if( bSelect && !m_bIgnoreSelect )
  486. {
  487. CComPtr<IConsoleVerb> spConsVerb;
  488. pConsole->QueryConsoleVerb(&spConsVerb);
  489. ASSERT(spConsVerb != NULL);
  490. if( !spConsVerb ) return E_NOINTERFACE;
  491. BOOL bOwnsView = OwnsConsoleView(pConsole);
  492. if( spConsVerb != NULL )
  493. {
  494. EnableVerbs(spConsVerb, bOwnsView);
  495. spConsVerb->SetVerbState(MMC_VERB_PROPERTIES, ENABLED, FALSE);
  496. spConsVerb->SetVerbState(MMC_VERB_PROPERTIES, HIDDEN, TRUE);
  497. spConsVerb->SetVerbState(MMC_VERB_RENAME, ENABLED, FALSE);
  498. spConsVerb->SetVerbState(MMC_VERB_RENAME, HIDDEN, TRUE);
  499. spConsVerb->SetVerbState(MMC_VERB_DELETE, ENABLED, FALSE);
  500. spConsVerb->SetVerbState(MMC_VERB_DELETE, HIDDEN, TRUE);
  501. // default verb for scope nodes is open
  502. spConsVerb->SetDefaultVerb(MMC_VERB_OPEN);
  503. }
  504. }
  505. if( bSelect )
  506. {
  507. m_bIgnoreSelect = FALSE;
  508. }
  509. return S_OK;
  510. }
  511. /******************************************************************************************
  512. * Menus and verbs
  513. ******************************************************************************************/
  514. BOOL AddMenuItem(LPCONTEXTMENUCALLBACK pCallback, long nID, long lInsertID, long lFlags, TCHAR* szNoLocName)
  515. {
  516. if( !pCallback ) return FALSE;
  517. CComQIPtr<IContextMenuCallback2> spContext2 = pCallback;
  518. if( !spContext2 ) return FALSE;
  519. CONTEXTMENUITEM2 item;
  520. CString strItem;
  521. strItem.LoadString(nID);
  522. ASSERT(!strItem.IsEmpty());
  523. int iSep = strItem.Find(L'\n');
  524. ASSERT(iSep != -1);
  525. CString strName = strItem.Left(iSep);
  526. CString strDescr = strItem.Right(strItem.GetLength() - iSep);
  527. item.strName = const_cast<LPWSTR>((LPCWSTR)strName);
  528. item.strStatusBarText = const_cast<LPWSTR>((LPCWSTR)strDescr);
  529. item.lCommandID = nID;
  530. item.lInsertionPointID = lInsertID;
  531. item.fFlags = lFlags;
  532. item.fSpecialFlags = 0;
  533. item.strLanguageIndependentName = szNoLocName;
  534. return SUCCEEDED(spContext2->AddItem(&item));
  535. }
  536. /*******************************************************************************************
  537. * Persistance methods
  538. ******************************************************************************************/
  539. HRESULT CScopeNode::LoadNode(IStream& stm)
  540. {
  541. stm >> m_nameID;
  542. ASSERT(m_nameID != 0);
  543. stm >> m_lNodeID;
  544. stm >> m_gColumnID;
  545. return S_OK;
  546. }
  547. HRESULT CScopeNode:: SaveNode(IStream& stm)
  548. {
  549. ASSERT(m_nameID != 0);
  550. stm << m_nameID;
  551. stm << m_lNodeID;
  552. stm << m_gColumnID;
  553. return S_OK;
  554. }
  555. HRESULT CScopeNode::Load(IStream& stm)
  556. {
  557. HRESULT hr = LoadNode(stm);
  558. RETURN_ON_FAILURE(hr);
  559. // if container node, then load children
  560. if( IsContainer() )
  561. {
  562. NODETYPE nodetype;
  563. stm >> *(int*)&nodetype;
  564. // If container has a child node
  565. if( nodetype != NULL_NODETYPE )
  566. {
  567. hr = CreateNode(nodetype, &m_pnodeChild);
  568. RETURN_ON_FAILURE(hr);
  569. // Set parent before loading, so node can pass it on when
  570. // it loads its siblings
  571. m_pnodeChild->m_pnodeParent = static_cast<CScopeNode*>(this);
  572. // Load first child only; it will load its siblings
  573. hr = m_pnodeChild->Load(stm);
  574. RETURN_ON_FAILURE(hr);
  575. }
  576. }
  577. // if this is the first child of a node, then load siblings
  578. // (Iteration rather than recursion to avoid a potentially
  579. // very deep stack.)
  580. if( m_pnodeParent && m_pnodeParent->FirstChild() == this )
  581. {
  582. CScopeNode* pnodePrev = static_cast<CScopeNode*>(this);
  583. NODETYPE nodetype;
  584. stm >> *(int*)&nodetype;
  585. // Loop until terminating null node type encountered
  586. while( nodetype != NULL_NODETYPE )
  587. {
  588. CScopeNodePtr spnode;
  589. hr = CreateNode(nodetype, &spnode);
  590. RETURN_ON_FAILURE(hr);
  591. spnode->m_pnodeParent = m_pnodeParent;
  592. hr = spnode->Load(stm);
  593. RETURN_ON_FAILURE(hr);
  594. // Link to previous sibling
  595. pnodePrev->m_pnodeNext = spnode.Detach();
  596. pnodePrev = pnodePrev->m_pnodeNext;
  597. stm >> *(int*)&nodetype;
  598. }
  599. }
  600. return hr;
  601. }
  602. HRESULT CScopeNode::Save(IStream& stm)
  603. {
  604. // Save the node's data
  605. HRESULT hr = SaveNode(stm);
  606. RETURN_ON_FAILURE(hr)
  607. // if container type node
  608. if( IsContainer() )
  609. {
  610. // Save children (first child saves all its siblings)
  611. if( FirstChild() )
  612. {
  613. stm << (int)FirstChild()->NodeType();
  614. hr = FirstChild()->Save(stm);
  615. RETURN_ON_FAILURE(hr)
  616. }
  617. // Terminate child list with null node
  618. stm << (int)NULL_NODETYPE;
  619. }
  620. // if this is the first child, save its siblings
  621. if( m_pnodeParent && m_pnodeParent->FirstChild() == this )
  622. {
  623. CScopeNode* pnode = m_pnodeNext;
  624. while( pnode != NULL )
  625. {
  626. stm << (int)pnode->NodeType();
  627. hr = pnode->Save(stm);
  628. BREAK_ON_FAILURE(hr);
  629. pnode = pnode->m_pnodeNext;
  630. }
  631. }
  632. return S_OK;
  633. }
  634. HRESULT CScopeNode::AddQueryNode(LPCONSOLE2 pConsole)
  635. {
  636. VALIDATE_POINTER( pConsole );
  637. ASSERT(NodeType() != QUERY_NODETYPE);
  638. HRESULT hr;
  639. do
  640. {
  641. // Create a new query node
  642. CQueryNodePtr spnode;
  643. hr = CreateNode(QUERY_NODETYPE, reinterpret_cast<CScopeNode**>(&spnode));
  644. BREAK_ON_FAILURE(hr);
  645. // Create and init wizard
  646. CAddQueryWizard queryWiz;
  647. queryWiz.Initialize(spnode, GetRootNode(), GetCompData()->GetStringTable());
  648. // Run the wizard
  649. IPropertySheetProviderPtr spProvider = pConsole;
  650. if( spProvider == NULL ) return E_NOINTERFACE;
  651. HWND hwndMain;
  652. pConsole->GetMainWindow(&hwndMain);
  653. hr = queryWiz.Run(spProvider, hwndMain);
  654. if( hr != S_OK )
  655. break;
  656. // Add any new classes to root node
  657. CRootNode* pRootNode = GetRootNode();
  658. if( pRootNode )
  659. {
  660. std::vector<CClassInfo*>::iterator itpClass;
  661. for( itpClass = queryWiz.GetNewClassInfo().begin(); itpClass != queryWiz.GetNewClassInfo().end(); ++itpClass )
  662. {
  663. pRootNode->AddClass(*itpClass);
  664. }
  665. }
  666. // Add the new node
  667. hr = AddNewChild(spnode, queryWiz.GetQueryName());
  668. }
  669. while( FALSE );
  670. return hr;
  671. }
  672. HRESULT
  673. CScopeNode::AddGroupNode(LPCONSOLE2 pConsole)
  674. {
  675. ASSERT(NodeType() == ROOT_NODETYPE);
  676. HRESULT hr;
  677. do
  678. {
  679. // Create a new group node
  680. CGroupNodePtr spnode;
  681. hr = CreateNode(GROUP_NODETYPE, reinterpret_cast<CScopeNode**>(&spnode));
  682. BREAK_ON_FAILURE(hr);
  683. // Create Add Group Node dialog
  684. CAddGroupNodeDlg GrpDlg;
  685. // run dialog and add node as child if successful
  686. if( GrpDlg.DoModal(spnode, g_hwndMain) == IDOK )
  687. hr = AddNewChild(spnode, GrpDlg.GetNodeName());
  688. }
  689. while( FALSE );
  690. return hr;
  691. }
  692. ////////////////////////////////////////////////////////////////////////////////////////////////
  693. //
  694. // CRootNode
  695. //
  696. ////////////////////////////////////////////////////////////////////////////////////////////////
  697. BEGIN_NOTIFY_MAP(CRootNode)
  698. ON_NOTIFY(MMCN_CONTEXTHELP, OnHelp)
  699. ON_PROPERTY_CHANGE()
  700. CHAIN_NOTIFY_MAP(CScopeNode)
  701. END_NOTIFY_MAP()
  702. HRESULT CRootNode::Initialize(CComponentData* pCompData)
  703. {
  704. VALIDATE_POINTER( pCompData );
  705. m_pCompData = pCompData;
  706. tstring strName = StrLoadString(IDS_ROOTNODE);
  707. RETURN_ON_FAILURE(SetName(strName.c_str()));
  708. // Set creation/modify times to now
  709. GetSystemTimeAsFileTime(&m_ftCreateTime);
  710. m_ftModifyTime = m_ftCreateTime;
  711. WCHAR szName[UNLEN+1];
  712. DWORD cName = UNLEN+1;
  713. // Set owner to current user
  714. if( GetUserName(szName, &cName) )
  715. m_strOwner = szName;
  716. return S_OK;
  717. }
  718. HRESULT CRootNode::OnHelp(LPCONSOLE2 pConsole, LPARAM /*arg*/, LPARAM /*param*/)
  719. {
  720. VALIDATE_POINTER( pConsole );
  721. tstring strHelpFile = _T("");
  722. tstring strHelpTopic = _T("");
  723. tstring strHelpFull = _T("");
  724. strHelpFile = StrLoadString(IDS_HELPFILE);
  725. if( strHelpFile.empty() ) return E_FAIL;
  726. // Verify that we got a help topic!
  727. strHelpTopic = StrLoadString(IDS_DEFAULTHELPTOPIC);
  728. if( strHelpTopic.empty() ) return E_FAIL;
  729. // Build path to %systemroot%\help
  730. TCHAR szWindowsDir[MAX_PATH+1] = {0};
  731. UINT nSize = GetSystemWindowsDirectory( szWindowsDir, MAX_PATH );
  732. if( nSize == 0 || nSize > MAX_PATH )
  733. {
  734. return E_FAIL;
  735. }
  736. strHelpFull = szWindowsDir;
  737. strHelpFull += _T("\\Help\\");
  738. strHelpFull += strHelpFile;
  739. strHelpFull += _T("::/");
  740. strHelpFull += strHelpTopic;
  741. // Show the Help topic
  742. CComQIPtr<IDisplayHelp> spHelp = pConsole;
  743. if( !spHelp ) return E_NOINTERFACE;
  744. return spHelp->ShowTopic( (LPTSTR)strHelpFull.c_str() );
  745. }
  746. HRESULT CRootNode::OnPropertyChange(LPCONSOLE2 pConsole, LPARAM lParam)
  747. {
  748. VALIDATE_POINTER( lParam );
  749. string_vector* pvstrClassesChanged = reinterpret_cast<string_vector*>(lParam);
  750. // Notify all child nodes of class change
  751. CScopeNode* pNode = FirstChild();
  752. while( pNode != NULL )
  753. {
  754. ASSERT(pNode->NodeType() == QUERY_NODETYPE || pNode->NodeType() == GROUP_NODETYPE);
  755. static_cast<CQueryableNode*>(pNode)->OnClassChange(*pvstrClassesChanged);
  756. pNode = pNode->Next();
  757. }
  758. delete pvstrClassesChanged;
  759. return S_OK;
  760. }
  761. HRESULT CRootNode::GetResultViewType(LPOLESTR* ppViewType, long* pViewOptions)
  762. {
  763. VALIDATE_POINTER( ppViewType );
  764. VALIDATE_POINTER( pViewOptions );
  765. //Show our homepage snapin in this console
  766. TCHAR szWindowsDir[MAX_PATH+1] = {0};
  767. UINT nSize = GetSystemWindowsDirectory( szWindowsDir, MAX_PATH );
  768. if( nSize == 0 || nSize > MAX_PATH )
  769. {
  770. return E_FAIL;
  771. }
  772. tstring strHomePage = szWindowsDir;
  773. strHomePage += _T("\\system32\\administration\\servhome.htm");
  774. *ppViewType = (TCHAR*)CoTaskMemAlloc((strHomePage.length() + 1) * sizeof(OLECHAR));
  775. VALIDATE_POINTER( *ppViewType );
  776. ocscpy( *ppViewType, T2OLE((LPTSTR)strHomePage.c_str()) );
  777. return S_OK;
  778. }
  779. HRESULT CRootNode::LoadNode(IStream& stm)
  780. {
  781. HRESULT hr = CScopeNode::LoadNode(stm);
  782. RETURN_ON_FAILURE(hr);
  783. stm >> m_ftCreateTime;
  784. stm >> m_ftModifyTime;
  785. stm >> m_strOwner;
  786. stm >> m_commentID;
  787. stm >> m_vClassInfo;
  788. // Root node's Insert() method is never called, so load the name string here
  789. IStringTable* pStringTable = GetCompData()->GetStringTable();
  790. ASSERT( pStringTable );
  791. if( !pStringTable ) return E_FAIL;
  792. hr = StringTableRead(pStringTable, m_nameID, m_strName);
  793. RETURN_ON_FAILURE(hr);
  794. return S_OK;
  795. }
  796. HRESULT CRootNode::SaveNode(IStream& stm)
  797. {
  798. HRESULT hr = CScopeNode::SaveNode(stm);
  799. RETURN_ON_FAILURE(hr);
  800. stm << m_ftCreateTime;
  801. stm << m_ftModifyTime;
  802. stm << m_strOwner;
  803. stm << m_commentID;
  804. stm << m_vClassInfo;
  805. return S_OK;
  806. }
  807. HRESULT CRootNode::GetComment(tstring& strComment)
  808. {
  809. if( m_commentID == 0 )
  810. {
  811. strComment.erase();
  812. return S_OK;
  813. }
  814. else
  815. {
  816. IStringTable* pStringTable = GetCompData()->GetStringTable();
  817. ASSERT( pStringTable );
  818. if( !pStringTable ) return E_FAIL;
  819. return StringTableRead(pStringTable, m_commentID, strComment);
  820. }
  821. }
  822. HRESULT CRootNode::SetComment(LPCWSTR pszComment)
  823. {
  824. VALIDATE_POINTER(pszComment);
  825. IStringTable* pStringTable = GetCompData()->GetStringTable();
  826. ASSERT( pStringTable );
  827. if( !pStringTable ) return E_FAIL;
  828. return StringTableWrite(pStringTable, pszComment, &m_commentID);
  829. }
  830. CClassInfo* CRootNode::FindClass(LPCWSTR pszClassName)
  831. {
  832. if( !pszClassName ) return NULL;
  833. classInfo_vector::iterator itClass;
  834. for( itClass = m_vClassInfo.begin(); itClass != m_vClassInfo.end(); ++itClass )
  835. {
  836. if( wcscmp(pszClassName, itClass->Name()) == 0 )
  837. break;
  838. }
  839. if( itClass == m_vClassInfo.end() )
  840. return NULL;
  841. // Load any strings before returning the class info, so they will be
  842. // available when referenced
  843. IStringTable* pStringTable = GetRootCompData()->GetStringTable();
  844. if( !pStringTable ) return NULL;
  845. itClass->LoadStrings(pStringTable);
  846. return itClass;
  847. }
  848. HRESULT CRootNode::AddMenuItems(LPCONTEXTMENUCALLBACK pCallback, long* plAllowed)
  849. {
  850. VALIDATE_POINTER( pCallback );
  851. VALIDATE_POINTER( plAllowed );
  852. HRESULT hr = S_OK;
  853. if( *plAllowed & CCM_INSERTIONALLOWED_NEW )
  854. {
  855. //hr = AddMenuItem(pCallback, MID_ADDGROUPNODE, CCM_INSERTIONPOINTID_PRIMARY_NEW, 0, _T("NEWGROUPFROMROOT"));
  856. //ASSERT(SUCCEEDED(hr));
  857. //hr = AddMenuItem(pCallback, MID_ADDQUERYNODE, CCM_INSERTIONPOINTID_PRIMARY_NEW, 0, _T("NEWQUERYFROMROOT"));
  858. //ASSERT(SUCCEEDED(hr));
  859. }
  860. return hr;
  861. }
  862. HRESULT CRootNode::MenuCommand(LPCONSOLE2 pConsole, long lCommand)
  863. {
  864. VALIDATE_POINTER(pConsole);
  865. HRESULT hr;
  866. switch( lCommand )
  867. {
  868. case MID_ADDGROUPNODE:
  869. hr = AddGroupNode(pConsole);
  870. break;
  871. case MID_ADDQUERYNODE:
  872. hr = AddQueryNode(pConsole);
  873. break;
  874. default:
  875. ASSERT(0 && "Unknown menu command");
  876. hr = E_INVALIDARG;
  877. }
  878. return hr;
  879. }
  880. HRESULT CRootNode::QueryPagesFor()
  881. {
  882. return S_OK;
  883. }
  884. HRESULT CRootNode::CreatePropertyPages(LPPROPERTYSHEETCALLBACK pProvider, LONG_PTR lNotifyHandle)
  885. {
  886. // Create a share edit list for all the prop pages to reference
  887. CEditObjList* pObjList = new CEditObjList();
  888. if( pObjList == NULL ) return E_OUTOFMEMORY;
  889. // Keep it alive until prop pages ref it
  890. pObjList->AddRef();
  891. // Create an instance of each prop page class and call Create on each.
  892. // General page
  893. HPROPSHEETPAGE hpageGen = NULL;
  894. CRootGeneralPage* pGenPage = new CRootGeneralPage(*pObjList);
  895. if( pGenPage != NULL )
  896. {
  897. hpageGen = pGenPage->Create();
  898. }
  899. // Object page
  900. HPROPSHEETPAGE hpageObj = NULL;
  901. CRootObjectPage* pObjPage = new CRootObjectPage(*pObjList);
  902. if( pObjPage != NULL )
  903. {
  904. hpageObj = pObjPage->Create();
  905. }
  906. // Context menu page
  907. HPROPSHEETPAGE hpageMenu = NULL;
  908. CRootMenuPage* pMenuPage = new CRootMenuPage(*pObjList);
  909. if( pMenuPage != NULL )
  910. {
  911. hpageMenu = pMenuPage->Create();
  912. }
  913. // Listview page
  914. HPROPSHEETPAGE hpageView = NULL;
  915. CRootViewPage* pViewPage = new CRootViewPage(*pObjList);
  916. if( pViewPage != NULL )
  917. {
  918. hpageView = pViewPage->Create();
  919. }
  920. HRESULT hr = E_OUTOFMEMORY;
  921. // if all pages were created, add each one to the prop sheet
  922. if( hpageGen && hpageObj && hpageMenu && hpageView )
  923. {
  924. hr = pProvider->AddPage(hpageGen);
  925. if( SUCCEEDED(hr) )
  926. hr = pProvider->AddPage(hpageObj);
  927. if( SUCCEEDED(hr) )
  928. hr = pProvider->AddPage(hpageMenu);
  929. if( SUCCEEDED(hr) )
  930. hr = pProvider->AddPage(hpageView);
  931. }
  932. // If ok so far, initialilze the common edit list
  933. // It is now responsible for freeing the notify handle (and itself)
  934. if( SUCCEEDED(hr) )
  935. hr = pObjList->Initialize(this, m_vClassInfo, lNotifyHandle);
  936. // On failure, destroy the pages. If a page failed to create
  937. // then delete the page class object instead (the object is
  938. // automatically deleted when the page is destroyed)
  939. if( FAILED(hr) )
  940. {
  941. if( hpageGen )
  942. DestroyPropertySheetPage(hpageGen);
  943. else
  944. SAFE_DELETE(pGenPage);
  945. if( hpageObj )
  946. DestroyPropertySheetPage(hpageObj);
  947. else
  948. SAFE_DELETE(pObjPage);
  949. if( hpageMenu )
  950. DestroyPropertySheetPage(hpageMenu);
  951. else
  952. SAFE_DELETE(pMenuPage);
  953. if( hpageView )
  954. DestroyPropertySheetPage(hpageView);
  955. else
  956. SAFE_DELETE(pViewPage);
  957. }
  958. // Release temp ref on edit list
  959. // it will go away when the prop pages release it
  960. pObjList->Release();
  961. return hr;
  962. }
  963. HRESULT CRootNode::GetWatermarks(HBITMAP* phWatermark, HBITMAP* phHeader,
  964. HPALETTE* phPalette, BOOL* bStreach)
  965. {
  966. return S_FALSE;
  967. }
  968. ///////////////////////////////////////////////////////////////////////////////////
  969. //
  970. // CQueryableNode
  971. //
  972. ///////////////////////////////////////////////////////////////////////////////////
  973. HRESULT CQueryableNode::AttachComponent(CComponent* pComponent)
  974. {
  975. VALIDATE_POINTER( pComponent );
  976. HRESULT hr = CScopeNode::AttachComponent(pComponent);
  977. if( hr != S_OK )
  978. return hr;
  979. // Get attributes query will collect
  980. attrib_map mapAttr;
  981. hr = GetQueryAttributes(mapAttr);
  982. RETURN_ON_FAILURE(hr);
  983. // Add column header for each attribute
  984. IHeaderCtrl* pHdrCtrl = pComponent->GetHeaderCtrl();
  985. ASSERT( pHdrCtrl );
  986. if( !pHdrCtrl ) return E_FAIL;
  987. int iPos = 0;
  988. // Always add Name and Type columns first
  989. CString strName;
  990. strName.LoadString(IDS_NAME);
  991. pHdrCtrl->InsertColumn(iPos++, strName, LVCFMT_LEFT, 200);
  992. strName.LoadString(IDS_TYPE);
  993. pHdrCtrl->InsertColumn(iPos++, strName, LVCFMT_LEFT, 100);
  994. // Add user selected attributes next (use display name which is the map value)
  995. attrib_map::iterator itCol;
  996. for( itCol = mapAttr.begin(); itCol != mapAttr.end(); itCol++ )
  997. {
  998. pHdrCtrl->InsertColumn(iPos++, itCol->second, LVCFMT_LEFT, 150);
  999. }
  1000. // if need to execute query and one is not in progress
  1001. if( m_bQueryChange && m_pQueryReq == NULL )
  1002. {
  1003. // Create vector of query attribute names
  1004. m_vstrColumns.clear();
  1005. if( mapAttr.size() != 0 )
  1006. {
  1007. m_vstrColumns.reserve(mapAttr.size());
  1008. attrib_map::iterator itAttr;
  1009. for( itAttr = mapAttr.begin(); itAttr != mapAttr.end(); itAttr++ )
  1010. m_vstrColumns.push_back(itAttr->first);
  1011. }
  1012. // Clear previous query items
  1013. ClearQueryRowItems();
  1014. // Start the query
  1015. hr = StartQuery(m_vstrColumns, this, &m_pQueryReq);
  1016. // if query started (note group node returns S_FALSE if no children)
  1017. if( hr == S_OK )
  1018. {
  1019. // Enable stop query button for all attached components
  1020. std::vector<CComponent*>::iterator itComp;
  1021. for( itComp = m_vComponents.begin(); itComp != m_vComponents.end(); itComp++ )
  1022. {
  1023. IToolbar* pToolbar = (*itComp)->GetToolbar();
  1024. if( pToolbar )
  1025. pToolbar->SetButtonState(MID_STOPQUERY, ENABLED, TRUE);
  1026. }
  1027. }
  1028. // allow node to be attached even if query fails
  1029. hr = S_OK;
  1030. }
  1031. else
  1032. {
  1033. // Replace component's row items with ours
  1034. pComponent->ClearRowItems();
  1035. pComponent->AddRowItems(m_vRowItems);
  1036. }
  1037. return hr;
  1038. }
  1039. void CQueryableNode::ClearQueryRowItems()
  1040. {
  1041. // discard local row items
  1042. m_vRowItems.clear();
  1043. // Clear all attached components rows
  1044. std::vector<CComponent*>::iterator itComp;
  1045. for( itComp = m_vComponents.begin(); itComp != m_vComponents.end(); itComp++ )
  1046. (*itComp)->ClearRowItems();
  1047. }
  1048. void CQueryableNode::QueryCallback(QUERY_NOTIFY event, CQueryRequest* pQueryReq, LPARAM lUserParam)
  1049. {
  1050. ASSERT(pQueryReq && pQueryReq == m_pQueryReq);
  1051. if( !pQueryReq || pQueryReq != m_pQueryReq ) return;
  1052. CString strMsgFmt;
  1053. switch( event )
  1054. {
  1055. case QRYN_NEWROWITEMS:
  1056. {
  1057. // Get new row items
  1058. RowItemVector& newRows = pQueryReq->GetNewRowItems();
  1059. DisplayNameMap* pNameMap = DisplayNames::GetClassMap(); //use for icon/class lookup
  1060. if( !pNameMap ) return;
  1061. LPCWSTR strClassName; //holds the name of the current lookup class
  1062. static tstring strLastName; //holds the last lookup class name
  1063. static ICONHOLDER* pLastIcons = NULL; //holds the indices of the last icon lookup
  1064. // Attach owner query node (passed as user param) to each row item
  1065. for( RowItemVector::iterator itRow = newRows.begin(); itRow != newRows.end(); ++itRow )
  1066. {
  1067. itRow->SetOwnerParam(lUserParam);
  1068. //establish icon virtual index
  1069. strClassName = (*itRow)[ROWITEM_CLASS_INDEX];
  1070. if( strLastName.compare(strClassName) != 0 )
  1071. {
  1072. //new class type requested. Load from namemap and cache.
  1073. pNameMap->GetIcons(strClassName, &pLastIcons);
  1074. strLastName = strClassName;
  1075. }
  1076. //use the cached normal/disabled icon depending on object state
  1077. if( pLastIcons )
  1078. {
  1079. if( itRow->Disabled() )
  1080. itRow->SetIconIndex(pLastIcons->iDisabled);
  1081. else
  1082. itRow->SetIconIndex(pLastIcons->iNormal);
  1083. }
  1084. }
  1085. // Add to node's vector
  1086. m_vRowItems.insert(m_vRowItems.end(), newRows.begin(), newRows.end());
  1087. // Add to all attach components
  1088. std::vector<CComponent*>::iterator itComp;
  1089. for( itComp = m_vComponents.begin(); itComp != m_vComponents.end(); itComp++ )
  1090. (*itComp)->AddRowItems(newRows);
  1091. // Free the rows
  1092. pQueryReq->ReleaseNewRowItems();
  1093. strMsgFmt.LoadString(IDS_SEARCHING);
  1094. break;
  1095. }
  1096. case QRYN_COMPLETED:
  1097. m_bQueryChange = FALSE;
  1098. strMsgFmt.LoadString(IDS_QUERYDONE);
  1099. break;
  1100. case QRYN_STOPPED:
  1101. strMsgFmt.LoadString(IDS_QUERYSTOPPED);
  1102. break;
  1103. case QRYN_FAILED:
  1104. strMsgFmt.LoadString(IDS_QUERYFAILED);
  1105. break;
  1106. default:
  1107. ASSERT(FALSE);
  1108. return;
  1109. }
  1110. // if components attached, display query progress
  1111. if( m_vComponents.size() != 0 )
  1112. {
  1113. CString strMsg;
  1114. strMsg.Format(strMsgFmt, m_vRowItems.size());
  1115. std::vector<CComponent*>::iterator itComp;
  1116. for( itComp = m_vComponents.begin(); itComp != m_vComponents.end(); ++itComp )
  1117. {
  1118. (*itComp)->GetConsole()->SetStatusText((LPWSTR)(LPCWSTR)strMsg);
  1119. }
  1120. }
  1121. // if query terminated, do cleanup
  1122. if( event != QRYN_NEWROWITEMS )
  1123. {
  1124. pQueryReq->Release();
  1125. m_pQueryReq = NULL;
  1126. // disable query stop button for all components
  1127. std::vector<CComponent*>::iterator itComp;
  1128. for( itComp = m_vComponents.begin(); itComp != m_vComponents.end(); ++itComp )
  1129. {
  1130. IToolbar* pToolbar = (*itComp)->GetToolbar();
  1131. if( pToolbar )
  1132. pToolbar->SetButtonState(MID_STOPQUERY, ENABLED, FALSE);
  1133. }
  1134. }
  1135. }
  1136. HRESULT CQueryableNode::DetachComponent(CComponent* pComponent)
  1137. {
  1138. VALIDATE_POINTER( pComponent );
  1139. HRESULT hr = CScopeNode::DetachComponent(pComponent);
  1140. if( hr != S_OK )
  1141. {
  1142. return FAILED(hr) ? hr : E_FAIL;
  1143. }
  1144. // if that was the last one, stop active query
  1145. if( m_vComponents.size() == 0 && m_pQueryReq != NULL )
  1146. m_pQueryReq->Stop(TRUE);
  1147. return S_OK;
  1148. }
  1149. HRESULT CQueryableNode::OnRefresh(LPCONSOLE2 pCons)
  1150. {
  1151. // if query in progress stop it
  1152. if( m_pQueryReq != NULL )
  1153. {
  1154. m_pQueryReq->Stop(TRUE);
  1155. }
  1156. // Set change flag to force new query
  1157. m_bQueryChange = TRUE;
  1158. // Have each attached component reselect this node
  1159. std::vector<CComponent*>::iterator itComp;
  1160. for( itComp = m_vComponents.begin(); itComp != m_vComponents.end(); itComp++ )
  1161. {
  1162. // Here's a kludge to get around an MMC bug. If the snap-in reselects its scope node
  1163. // while the focus is on a taskpad background, then MMC sends a deselect/select
  1164. // sequence to the snap-in causing it to enable its verbs. But if the user then clicks
  1165. // an enabled tool button (e.g., Rename) nothing happens other than an MMC assert
  1166. // because MMC thinks nothing is selected.
  1167. //
  1168. // The fix is to check the state of the properties verbs prior to doing a reselect.
  1169. // If the verb is disabled then don't enabled it (or any other verbs) when the select
  1170. // notify is received. This has to be done per component because each may have a
  1171. // different pane focused.
  1172. CComPtr<IConsoleVerb> spConsVerb;
  1173. (*itComp)->GetConsole()->QueryConsoleVerb(&spConsVerb);
  1174. ASSERT(spConsVerb != NULL);
  1175. if( spConsVerb != NULL )
  1176. {
  1177. // Ignore select notify if verbs are disabled before the reselect
  1178. static BOOL bEnabled;
  1179. if( spConsVerb->GetVerbState(MMC_VERB_PROPERTIES, ENABLED, &bEnabled) == S_OK )
  1180. {
  1181. m_bIgnoreSelect = !bEnabled;
  1182. }
  1183. }
  1184. (*itComp)->GetConsole()->SelectScopeItem(m_hScopeItem);
  1185. // Go back to normal select processing
  1186. ASSERT(!m_bIgnoreSelect);
  1187. m_bIgnoreSelect = FALSE;
  1188. }
  1189. return S_OK;
  1190. }
  1191. ////////////////////////////////////////////////////////////////////////////////////////////////
  1192. //
  1193. // CGroupNode
  1194. //
  1195. ////////////////////////////////////////////////////////////////////////////////////////////////
  1196. BEGIN_NOTIFY_MAP(CGroupNode)
  1197. ON_REFRESH()
  1198. ON_DELETE()
  1199. ON_ADD_IMAGES()
  1200. CHAIN_NOTIFY_MAP(CScopeNode)
  1201. END_NOTIFY_MAP()
  1202. HRESULT CGroupNode::AddMenuItems(LPCONTEXTMENUCALLBACK pCallback, long* plAllowed)
  1203. {
  1204. VALIDATE_POINTER( plAllowed );
  1205. BOOL bRes = TRUE;
  1206. if( *plAllowed & CCM_INSERTIONALLOWED_NEW )
  1207. {
  1208. bRes = AddMenuItem(pCallback, MID_ADDQUERYNODE, CCM_INSERTIONPOINTID_PRIMARY_NEW, 0, _T("NEWQUERYFROMGROUP"));
  1209. ASSERT(bRes);
  1210. }
  1211. return bRes ? S_OK : E_FAIL;
  1212. }
  1213. HRESULT CGroupNode::MenuCommand(LPCONSOLE2 pConsole, long lCommand)
  1214. {
  1215. VALIDATE_POINTER(pConsole);
  1216. HRESULT hr;
  1217. switch( lCommand )
  1218. {
  1219. case MID_ADDQUERYNODE:
  1220. hr = AddQueryNode(pConsole);
  1221. break;
  1222. default:
  1223. ASSERT(0 && "Unknown menu command");
  1224. hr = E_INVALIDARG;
  1225. }
  1226. return hr;
  1227. }
  1228. HRESULT CGroupNode::GetResultViewType(LPOLESTR* ppViewType, long* pViewOptions)
  1229. {
  1230. VALIDATE_POINTER( pViewOptions );
  1231. *pViewOptions = MMC_VIEW_OPTIONS_OWNERDATALIST;
  1232. return S_FALSE;
  1233. }
  1234. HRESULT CGroupNode::QueryPagesFor()
  1235. {
  1236. return S_OK;
  1237. }
  1238. HRESULT CGroupNode::CreatePropertyPages(LPPROPERTYSHEETCALLBACK pProvider, LONG_PTR handle)
  1239. {
  1240. // Create a group edit object
  1241. CGroupEditObj* pEditObj = new CGroupEditObj(this);
  1242. if( !pEditObj ) return E_OUTOFMEMORY;
  1243. // Create an instance of each prop page class and call Create on each.
  1244. // Keep it alive until prop pages ref it
  1245. pEditObj->AddRef();
  1246. // General page
  1247. HPROPSHEETPAGE hpageGen = NULL;
  1248. CGroupGeneralPage* pGenPage = new CGroupGeneralPage(pEditObj);
  1249. if( pGenPage != NULL )
  1250. {
  1251. hpageGen = pGenPage->Create();
  1252. }
  1253. HRESULT hr = E_FAIL;
  1254. // if all pages were created, add each one to the prop sheet
  1255. if( hpageGen )
  1256. {
  1257. hr = pProvider->AddPage(hpageGen);
  1258. }
  1259. // On failure, destroy the pages. If a page failed to create
  1260. // then delete the page class object instead (the object is
  1261. // automatically deleted when the page is destroyed)
  1262. if( FAILED(hr) )
  1263. {
  1264. if( hpageGen )
  1265. DestroyPropertySheetPage(hpageGen);
  1266. else
  1267. SAFE_DELETE(pGenPage);
  1268. }
  1269. // Release temp ref on edit list
  1270. // it will go away when the prop pages release it
  1271. pEditObj->Release();
  1272. return hr;
  1273. }
  1274. HRESULT CGroupNode::GetQueryAttributes(attrib_map& mapAttr)
  1275. {
  1276. // Get union of attributes for child query nodes
  1277. CScopeNode* pNode = FirstChild();
  1278. while( pNode != NULL )
  1279. {
  1280. ASSERT(pNode->NodeType() == QUERY_NODETYPE);
  1281. CQueryNode* pQNode = static_cast<CQueryNode*>(pNode);
  1282. // if query defined for this node, get the attributes
  1283. if( pQNode->Query() && pQNode->Query()[0] )
  1284. pQNode->GetQueryAttributes(mapAttr);
  1285. pNode = pNode->Next();
  1286. }
  1287. return S_OK;
  1288. }
  1289. HRESULT CGroupNode::StartQuery(string_vector& vstrColumns, CQueryCallback* pCallback, CQueryRequest** ppReq)
  1290. {
  1291. VALIDATE_POINTER( pCallback );
  1292. VALIDATE_POINTER( ppReq );
  1293. *ppReq = NULL;
  1294. // if no children, there is no query to perform
  1295. if( FirstChild() == NULL )
  1296. return S_FALSE;
  1297. ASSERT(FirstChild()->NodeType() == QUERY_NODETYPE);
  1298. CQueryNode* pQNode = static_cast<CQueryNode*>(FirstChild());
  1299. // Start a query one the first one
  1300. HRESULT hr = pQNode->StartQuery(vstrColumns, pCallback, ppReq);
  1301. // Save pointer to active query node for callback handler
  1302. if( SUCCEEDED(hr) )
  1303. m_pQNodeActive = pQNode;
  1304. return hr;
  1305. }
  1306. void CGroupNode::QueryCallback(QUERY_NOTIFY event, CQueryRequest* pQueryReq, LPARAM lUserParam)
  1307. {
  1308. if( !pQueryReq || !m_pQNodeActive || (pQueryReq != m_pQueryReq) ) return;
  1309. // if current query is complete and there are more child query nodes
  1310. if( event == QRYN_COMPLETED && m_pQNodeActive->Next() != NULL )
  1311. {
  1312. CQueryNode* pQNodeNext = static_cast<CQueryNode*>(m_pQNodeActive->Next());
  1313. // Start a query on the next child node
  1314. CQueryRequest* pReqNew = NULL;
  1315. HRESULT hr = pQNodeNext->StartQuery(m_vstrColumns, this, &pReqNew);
  1316. if( SUCCEEDED(hr) )
  1317. {
  1318. // Release the current query node and save new query and node
  1319. pQueryReq->Release();
  1320. m_pQueryReq = pReqNew;
  1321. m_pQNodeActive = pQNodeNext;
  1322. // Bypass normal query termination processing
  1323. return;
  1324. }
  1325. }
  1326. // Do common callback processing
  1327. CQueryableNode::QueryCallback(event, pQueryReq, lUserParam);
  1328. }
  1329. void CGroupNode::EnableVerbs(IConsoleVerb* pConsVerb, BOOL bOwnsView)
  1330. {
  1331. if( bOwnsView && pConsVerb )
  1332. {
  1333. pConsVerb->SetVerbState(MMC_VERB_REFRESH, ENABLED, TRUE);
  1334. }
  1335. }
  1336. HRESULT CGroupNode::OnDelete(LPCONSOLE2 pConsole)
  1337. {
  1338. // Get namespace interface
  1339. IConsoleNameSpacePtr spNameSpace = pConsole;
  1340. ASSERT(spNameSpace != NULL);
  1341. if( spNameSpace == NULL )
  1342. return E_FAIL;
  1343. // Get confirmation from user before deleting node
  1344. CString strTitle;
  1345. strTitle.LoadString(IDS_DELETENODE_TITLE);
  1346. CString strMsgFmt;
  1347. strMsgFmt.LoadString(IDS_DELETEGROUPNODE);
  1348. CString strMsg;
  1349. strMsg.Format(strMsgFmt, GetName());
  1350. int iRet;
  1351. HRESULT hr = pConsole->MessageBox(strMsg, strTitle, MB_YESNOCANCEL|MB_ICONWARNING, &iRet);
  1352. if( SUCCEEDED(hr) && (iRet == IDYES || iRet == IDNO) )
  1353. {
  1354. // if No, move child nodes up one level before deleting this node
  1355. if( iRet == IDNO )
  1356. {
  1357. // Move each child to the parent of this node
  1358. CScopeNode* pnodeChild = m_pnodeChild;
  1359. while( pnodeChild != NULL )
  1360. {
  1361. // Detach each child from old list before adding it
  1362. CScopeNode* pnodeNext = pnodeChild->m_pnodeNext;
  1363. pnodeChild->m_pnodeNext = NULL;
  1364. // clear the item handle associated with the old position
  1365. // MMC will provide a new one when the node is added
  1366. pnodeChild->m_hScopeItem = NULL;
  1367. Parent()->AddChild(pnodeChild);
  1368. // Release because new parent has ref'd it
  1369. pnodeChild->Release();
  1370. pnodeChild = pnodeNext;
  1371. }
  1372. // Set child list to NULL
  1373. m_pnodeChild = NULL;
  1374. }
  1375. // Tell MMC to delete this node and all the children
  1376. ASSERT(m_hScopeItem != 0);
  1377. hr = spNameSpace->DeleteItem(m_hScopeItem, TRUE);
  1378. // Caution: this call will usually delete this object,
  1379. // so don't access any members after making it
  1380. if( SUCCEEDED(hr) )
  1381. hr = Parent()->RemoveChild(this);
  1382. }
  1383. return hr;
  1384. }
  1385. HRESULT
  1386. CGroupNode::OnAddImages(LPCONSOLE2 pConsole, LPIMAGELIST pImageList)
  1387. {
  1388. CScopeNode* pNode = FirstChild();
  1389. while( pNode != NULL )
  1390. {
  1391. ASSERT(pNode->NodeType() == QUERY_NODETYPE);
  1392. static_cast<CQueryNode*>(pNode)->OnAddImages(pConsole, pImageList);
  1393. pNode = pNode->Next();
  1394. }
  1395. return S_OK;
  1396. }
  1397. BOOL
  1398. CGroupNode::OnClassChange(string_vector& vstrClasses)
  1399. {
  1400. BOOL bChanged = FALSE;
  1401. // Notify all child query nodes of class change
  1402. CScopeNode* pnode = FirstChild();
  1403. while( pnode != NULL )
  1404. {
  1405. // Set change flag if any child node has changed
  1406. ASSERT(pnode->NodeType() == QUERY_NODETYPE);
  1407. bChanged |= static_cast<CQueryNode*>(pnode)->OnClassChange(vstrClasses);
  1408. pnode = pnode->m_pnodeNext;
  1409. }
  1410. // if any child has changed, need to rerun the group query
  1411. if( bChanged )
  1412. OnRefresh(NULL);
  1413. return bChanged;
  1414. }
  1415. HRESULT
  1416. CGroupNode::LoadNode(IStream& stm)
  1417. {
  1418. HRESULT hr = CScopeNode::LoadNode(stm);
  1419. RETURN_ON_FAILURE(hr);
  1420. stm >> m_strScope;
  1421. stm >> m_strFilter;
  1422. stm >> m_bApplyScope;
  1423. stm >> m_bApplyFilter;
  1424. stm >> m_bLocalScope;
  1425. return S_OK;
  1426. }
  1427. HRESULT CGroupNode::SaveNode(IStream& stm)
  1428. {
  1429. HRESULT hr = CScopeNode::SaveNode(stm);
  1430. RETURN_ON_FAILURE(hr);
  1431. stm << m_strScope;
  1432. stm << m_strFilter;
  1433. stm << m_bApplyScope;
  1434. stm << m_bApplyFilter;
  1435. stm << m_bLocalScope;
  1436. return S_OK;
  1437. }
  1438. ////////////////////////////////////////////////////////////////////////////////////////////////
  1439. //
  1440. // CQueryNode
  1441. //
  1442. ////////////////////////////////////////////////////////////////////////////////////////////////
  1443. BEGIN_NOTIFY_MAP(CQueryNode)
  1444. ON_REFRESH()
  1445. ON_DELETE()
  1446. ON_ADD_IMAGES()
  1447. CHAIN_NOTIFY_MAP(CScopeNode)
  1448. END_NOTIFY_MAP()
  1449. HRESULT CQueryNode::GetResultViewType(LPOLESTR* ppViewType, long* pViewOptions)
  1450. {
  1451. VALIDATE_POINTER( pViewOptions );
  1452. *pViewOptions = MMC_VIEW_OPTIONS_OWNERDATALIST;
  1453. return S_FALSE;
  1454. }
  1455. HRESULT CQueryNode::GetDisplayInfo(RESULTDATAITEM* pRDI)
  1456. {
  1457. VALIDATE_POINTER( pRDI );
  1458. if( !pRDI->bScopeItem )
  1459. {
  1460. if( pRDI->nIndex < 0 || pRDI->nIndex >= m_vRowItems.size() )
  1461. return E_INVALIDARG;
  1462. if( pRDI->mask & RDI_STR )
  1463. pRDI->str = const_cast<LPWSTR>(m_vRowItems[pRDI->nIndex][pRDI->nCol]);
  1464. if( pRDI->mask & RDI_IMAGE )
  1465. pRDI->nImage = RESULT_ITEM_IMAGE;
  1466. return S_OK;
  1467. }
  1468. else
  1469. {
  1470. return CScopeNode::GetDisplayInfo(pRDI);
  1471. }
  1472. }
  1473. HRESULT CQueryNode::GetClassMenuItems(LPCWSTR pszClass, menucmd_vector& vMenus, int* piDefault, BOOL* pbPropertyMenu)
  1474. {
  1475. VALIDATE_POINTER( pszClass );
  1476. VALIDATE_POINTER( piDefault );
  1477. VALIDATE_POINTER( pbPropertyMenu );
  1478. *piDefault = -1;
  1479. *pbPropertyMenu = TRUE;
  1480. QueryObjVector::iterator itQObj;
  1481. for( itQObj = Objects().begin(); itQObj != Objects().end(); ++itQObj )
  1482. {
  1483. if( wcscmp(itQObj->Name(), pszClass) == 0 )
  1484. break;
  1485. }
  1486. if( itQObj == Objects().end() )
  1487. return S_FALSE;
  1488. CRootNode* pRootNode = GetRootNode();
  1489. if( pRootNode == NULL )
  1490. return S_FALSE;
  1491. CClassInfo* pClassInfo = pRootNode->FindClass(pszClass);
  1492. if( pClassInfo == NULL )
  1493. return S_FALSE;
  1494. menuref_vector& vMenuRefs = itQObj->MenuRefs();
  1495. menuref_vector::iterator itMenuRef;
  1496. menucmd_vector& vMenuCmds = pClassInfo->Menus();
  1497. menucmd_vector::iterator itMenuCmd;
  1498. // First add all root menu items that preceed the first query menu item
  1499. for( itMenuCmd = vMenuCmds.begin(); itMenuCmd != vMenuCmds.end(); ++itMenuCmd )
  1500. {
  1501. if( std::find(vMenuRefs.begin(), vMenuRefs.end(), (*itMenuCmd)->ID()) != vMenuRefs.end() )
  1502. break;
  1503. vMenus.push_back(*itMenuCmd);
  1504. }
  1505. // For each query menu item
  1506. for( itMenuRef = vMenuRefs.begin(); itMenuRef != vMenuRefs.end(); ++itMenuRef )
  1507. {
  1508. // Find the root menu item by name
  1509. for( itMenuCmd = vMenuCmds.begin(); itMenuCmd != vMenuCmds.end(); ++itMenuCmd )
  1510. {
  1511. if( (*itMenuCmd)->ID() == itMenuRef->ID() )
  1512. break;
  1513. }
  1514. // if item was deleted at the root node, then skip it
  1515. if( itMenuCmd == vMenuCmds.end() )
  1516. continue;
  1517. // If item is enabled at query level add it to the list
  1518. if( itMenuRef->IsEnabled() )
  1519. {
  1520. vMenus.push_back(*itMenuCmd);
  1521. if( itMenuRef->IsDefault() )
  1522. *piDefault = vMenus.size() - 1;
  1523. }
  1524. ++itMenuCmd;
  1525. // Add any following root items that aren't in the query list
  1526. while( itMenuCmd != vMenuCmds.end() &&
  1527. std::find(vMenuRefs.begin(), vMenuRefs.end(), (*itMenuCmd)->ID()) == vMenuRefs.end() )
  1528. {
  1529. vMenus.push_back(*itMenuCmd);
  1530. ++itMenuCmd;
  1531. }
  1532. }
  1533. *pbPropertyMenu = itQObj->HasPropertyMenu();
  1534. return S_OK;
  1535. }
  1536. HRESULT CQueryNode::GetQueryAttributes(attrib_map& mapAttr)
  1537. {
  1538. CRootNode* pRootNode = GetRootNode();
  1539. if( !pRootNode ) return E_UNEXPECTED;
  1540. QueryObjVector::iterator itQObj;
  1541. for( itQObj = m_vObjInfo.begin(); itQObj != m_vObjInfo.end(); ++itQObj )
  1542. {
  1543. // skip classes that aren't defined at the root
  1544. CClassInfo* pClassInfo = pRootNode->FindClass(itQObj->Name());
  1545. if( pClassInfo == NULL )
  1546. continue;
  1547. // get display name map for this class
  1548. DisplayNameMap* pNameMap = DisplayNames::GetMap(itQObj->Name());
  1549. ASSERT(pNameMap != NULL);
  1550. if( pNameMap == NULL )
  1551. continue;
  1552. // Use all attributes defined at the root level that aren't disabled at the query level
  1553. string_vector& vstrDisabled = itQObj->DisabledColumns();
  1554. string_vector::iterator itCol;
  1555. for( itCol = pClassInfo->Columns().begin(); itCol != pClassInfo->Columns().end(); ++itCol )
  1556. {
  1557. if( std::find(vstrDisabled.begin(), vstrDisabled.end(), *itCol) == vstrDisabled.end() )
  1558. {
  1559. mapAttr.insert(attrib_map::value_type
  1560. (itCol->c_str(), pNameMap->GetAttributeDisplayName(itCol->c_str())));
  1561. }
  1562. }
  1563. }
  1564. return S_OK;
  1565. }
  1566. HRESULT CQueryNode::StartQuery(string_vector& vstrColumns, CQueryCallback* pQueryCallback, CQueryRequest** ppQueryReq)
  1567. {
  1568. VALIDATE_POINTER( pQueryCallback );
  1569. VALIDATE_POINTER( ppQueryReq );
  1570. *ppQueryReq = NULL;
  1571. // Create a query request object
  1572. CQueryRequest* pQueryReq = NULL;
  1573. HRESULT hr = S_OK;
  1574. // Get query scope and filter
  1575. LPCWSTR pszScope = Scope();
  1576. tstring strTempFilter;
  1577. ExpandQuery(strTempFilter);
  1578. LPCWSTR pszFilter = strTempFilter.c_str();
  1579. CString strJointFilter;
  1580. // Check for scope or filter override by parent group node
  1581. if( Parent()->NodeType() == GROUP_NODETYPE )
  1582. {
  1583. CGroupNode* pGrpNode = static_cast<CGroupNode*>(Parent());
  1584. // if group imposed scope, use it instead
  1585. if( pGrpNode->ApplyScope() )
  1586. pszScope = pGrpNode->Scope();
  1587. // if group imposed filter, AND it with query filter
  1588. if( pGrpNode->ApplyFilter() )
  1589. {
  1590. strJointFilter.Format(L"(&(%s)(%s))", strTempFilter.c_str(), pGrpNode->Filter());
  1591. pszFilter = strJointFilter;
  1592. }
  1593. }
  1594. // Get list of object classes expected from query
  1595. string_vector vstrClasses;
  1596. QueryObjVector::iterator itQObj;
  1597. for( itQObj = m_vObjInfo.begin(); itQObj != m_vObjInfo.end(); ++itQObj )
  1598. vstrClasses.push_back(itQObj->Name());
  1599. // Set query parameters
  1600. hr = CQueryRequest::CreateInstance(&pQueryReq);
  1601. if( SUCCEEDED(hr) )
  1602. {
  1603. pQueryReq->SetQueryParameters(pszScope, pszFilter, &vstrClasses, &vstrColumns);
  1604. // Set search preferences
  1605. ADS_SEARCHPREF_INFO srchPrefs[3];
  1606. srchPrefs[0].dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE;
  1607. srchPrefs[0].vValue.dwType = ADSTYPE_INTEGER;
  1608. srchPrefs[0].vValue.Integer = ADS_SCOPE_SUBTREE;
  1609. srchPrefs[1].dwSearchPref = ADS_SEARCHPREF_PAGESIZE;
  1610. srchPrefs[1].vValue.dwType = ADSTYPE_INTEGER;
  1611. srchPrefs[1].vValue.Integer = 32;
  1612. srchPrefs[2].dwSearchPref = ADS_SEARCHPREF_ASYNCHRONOUS;
  1613. srchPrefs[2].vValue.dwType = ADSTYPE_BOOLEAN;
  1614. srchPrefs[2].vValue.Boolean = TRUE;
  1615. pQueryReq->SetSearchPreferences(srchPrefs, lengthof(srchPrefs));
  1616. // Set callback info (pass query node ptr as parameter)
  1617. pQueryReq->SetCallback(pQueryCallback, (LPARAM)this);
  1618. // Start query
  1619. hr = pQueryReq->Start();
  1620. }
  1621. if( SUCCEEDED(hr) )
  1622. {
  1623. // Return active query pointer
  1624. *ppQueryReq = pQueryReq;
  1625. }
  1626. if( FAILED(hr) && pQueryReq )
  1627. {
  1628. pQueryReq->Release();
  1629. pQueryReq = NULL;
  1630. }
  1631. return hr;
  1632. }
  1633. HRESULT
  1634. CQueryNode::GetComment(tstring& strComment)
  1635. {
  1636. if( m_commentID == 0 )
  1637. {
  1638. strComment.erase();
  1639. return S_OK;
  1640. }
  1641. else
  1642. {
  1643. IStringTable* pStringTable = GetCompData()->GetStringTable();
  1644. ASSERT(pStringTable != NULL);
  1645. return StringTableRead(pStringTable, m_commentID, strComment);
  1646. }
  1647. }
  1648. HRESULT CQueryNode::SetComment(LPCWSTR pszComment)
  1649. {
  1650. VALIDATE_POINTER(pszComment);
  1651. IStringTable* pStringTable = GetCompData()->GetStringTable();
  1652. ASSERT( pStringTable );
  1653. if( !pStringTable ) return E_FAIL;
  1654. return StringTableWrite(pStringTable, pszComment, &m_commentID);
  1655. }
  1656. HRESULT CQueryNode::OnDelete(LPCONSOLE2 pConsole)
  1657. {
  1658. // Get namespace interface
  1659. IConsoleNameSpacePtr spNameSpace = pConsole;
  1660. ASSERT(spNameSpace != NULL);
  1661. if( spNameSpace == NULL )
  1662. return E_FAIL;
  1663. // Get confirmation from user before deleting node
  1664. CString strTitle;
  1665. strTitle.LoadString(IDS_DELETENODE_TITLE);
  1666. CString strMsgFmt;
  1667. strMsgFmt.LoadString(IDS_DELETEQUERYNODE);
  1668. CString strMsg;
  1669. strMsg.Format(strMsgFmt, GetName());
  1670. int iRet;
  1671. HRESULT hr = pConsole->MessageBox(strMsg, strTitle, MB_YESNO|MB_ICONWARNING, &iRet);
  1672. if( SUCCEEDED(hr) && iRet == IDYES )
  1673. {
  1674. ASSERT(m_hScopeItem != 0);
  1675. hr = spNameSpace->DeleteItem(m_hScopeItem, TRUE);
  1676. // Caution: this call will usually delete this object,
  1677. // so don't access any members after making it
  1678. if( SUCCEEDED(hr) )
  1679. hr = Parent()->RemoveChild(this);
  1680. }
  1681. return hr;
  1682. }
  1683. BOOL CQueryNode::OnClassChange(string_vector& vstrClasses)
  1684. {
  1685. BOOL bChanged = FALSE;
  1686. // Check if this node's query returns objects of a changed class
  1687. // (object types returned by the query will be in the ObjInfo vector)
  1688. QueryObjVector::iterator itQObj;
  1689. for( itQObj = m_vObjInfo.begin(); itQObj != m_vObjInfo.end(); ++itQObj )
  1690. {
  1691. if( std::find(vstrClasses.begin(), vstrClasses.end(), itQObj->Name()) != vstrClasses.end() )
  1692. {
  1693. bChanged = TRUE;
  1694. break;
  1695. }
  1696. }
  1697. // if a queried class has changed, refresh the query
  1698. if( bChanged )
  1699. OnRefresh(NULL);
  1700. return bChanged;
  1701. }
  1702. HRESULT CQueryNode::OnAddImages(LPCONSOLE2 pConsole, LPIMAGELIST pImageList)
  1703. {
  1704. VALIDATE_POINTER(pImageList);
  1705. std::vector<CQueryObjInfo>::iterator vecIter;
  1706. DisplayNameMap* pNameMap = DisplayNames::GetClassMap();
  1707. if( !pNameMap ) return E_FAIL;
  1708. ICONHOLDER* pIH = NULL;
  1709. // iterate through classes to be displayed. Call the global namemap
  1710. // to determine icons for each class. Load both large and small icons.
  1711. for( vecIter = m_vObjInfo.begin(); vecIter != m_vObjInfo.end(); vecIter++ )
  1712. {
  1713. //check for class name in namemap
  1714. if( pNameMap->GetIcons(pNameMap->GetFriendlyName(vecIter->Name()), &pIH) && pIH )
  1715. {
  1716. //verify normal icon exists
  1717. if( pIH->hSmall )
  1718. {
  1719. pImageList->ImageListSetIcon((LONG_PTR *)pIH->hSmall, pIH->iNormal); // add small icon
  1720. pImageList->ImageListSetIcon((LONG_PTR *)pIH->hLarge, ILSI_LARGE_ICON(pIH->iNormal)); // add large icon
  1721. }
  1722. //verify disabled icon exists
  1723. if( pIH->hSmallDis )
  1724. {
  1725. pImageList->ImageListSetIcon((LONG_PTR *)pIH->hSmallDis, pIH->iDisabled); // add small disabled icon
  1726. pImageList->ImageListSetIcon((LONG_PTR *)pIH->hLargeDis, ILSI_LARGE_ICON(pIH->iDisabled)); // add large disabled icon
  1727. }
  1728. }
  1729. }
  1730. return CScopeNode::OnAddImages(pConsole, pImageList); //add default images too
  1731. }
  1732. HRESULT
  1733. CQueryNode::LoadNode(IStream& stm)
  1734. {
  1735. HRESULT hr = CScopeNode::LoadNode(stm);
  1736. RETURN_ON_FAILURE(hr);
  1737. stm >> m_strScope;
  1738. stm >> m_strQuery;
  1739. stm >> m_bsQueryData;
  1740. stm >> m_commentID;
  1741. stm >> m_vObjInfo;
  1742. stm >> m_bLocalScope;
  1743. stm >> m_vMenus;
  1744. if( g_dwFileVer >= 150 )
  1745. {
  1746. stm >> m_nIconIndex; //Load the icon
  1747. }
  1748. return S_OK;
  1749. }
  1750. HRESULT CQueryNode::SaveNode(IStream& stm)
  1751. {
  1752. HRESULT hr = CScopeNode::SaveNode(stm);
  1753. RETURN_ON_FAILURE(hr);
  1754. stm << m_strScope;
  1755. stm << m_strQuery;
  1756. stm << m_bsQueryData;
  1757. stm << m_commentID;
  1758. stm << m_vObjInfo;
  1759. stm << m_bLocalScope;
  1760. stm << m_vMenus;
  1761. stm << m_nIconIndex;
  1762. return S_OK;
  1763. }
  1764. void CQueryNode::EnableVerbs(IConsoleVerb* pConsVerb, BOOL bOwnsView)
  1765. {
  1766. if( bOwnsView && pConsVerb )
  1767. {
  1768. pConsVerb->SetVerbState(MMC_VERB_REFRESH, ENABLED, TRUE);
  1769. }
  1770. }
  1771. HRESULT CQueryNode::AddMenuItems(LPCONTEXTMENUCALLBACK pCallback, long* plAllowed)
  1772. {
  1773. VALIDATE_POINTER( plAllowed );
  1774. HRESULT hr = S_OK;
  1775. CComQIPtr<IContextMenuCallback2> spContext2 = pCallback;
  1776. if( !spContext2 ) return E_NOINTERFACE;
  1777. if( *plAllowed & CCM_INSERTIONALLOWED_TOP )
  1778. {
  1779. // Add our new Querynode menus
  1780. // Make sure our strings are loaded.
  1781. CRootNode* pRootNode = GetRootNode();
  1782. if( !pRootNode ) return E_FAIL;
  1783. CComponentData* pCompData = pRootNode->GetCompData();
  1784. if( !pCompData ) return E_FAIL;
  1785. IStringTable* pStringTable = pCompData->GetStringTable();
  1786. ASSERT( pStringTable );
  1787. if( !pStringTable ) return E_FAIL;
  1788. LoadStrings(pStringTable);
  1789. menucmd_vector::iterator itMenu;
  1790. long lCmdID = 0;
  1791. for( itMenu = m_vMenus.begin(); itMenu != m_vMenus.end(); ++itMenu, ++lCmdID )
  1792. {
  1793. CONTEXTMENUITEM2 item;
  1794. OLECHAR szGuid[50] = {0};
  1795. ::StringFromGUID2((*itMenu)->NoLocID(), szGuid, 50);
  1796. item.strName = const_cast<LPWSTR>((*itMenu)->Name());
  1797. item.strStatusBarText = L"";
  1798. item.lCommandID = lCmdID;
  1799. item.lInsertionPointID = CCM_INSERTIONPOINTID_PRIMARY_TOP;
  1800. item.fFlags = 0;
  1801. item.fSpecialFlags = 0;
  1802. item.strLanguageIndependentName = szGuid;
  1803. hr = spContext2->AddItem(&item);
  1804. ASSERT(SUCCEEDED(hr));
  1805. }
  1806. //hr = AddMenuItem(pCallback, MID_EDITQUERY, CCM_INSERTIONPOINTID_PRIMARY_TOP, 0, _T("EDITQUERY"));
  1807. //ASSERT(SUCCEEDED(hr));
  1808. long lFlags = (m_pQueryReq != NULL) ? MF_ENABLED : MF_GRAYED;
  1809. BOOL bRes = AddMenuItem(pCallback, MID_STOPQUERY, CCM_INSERTIONPOINTID_PRIMARY_TOP, lFlags, _T("STOPQUERY"));
  1810. hr = bRes ? S_OK : E_FAIL;
  1811. ASSERT(SUCCEEDED(hr));
  1812. // Show the "Move to" menu item if there is at least one group node
  1813. CScopeNode* pnode = pRootNode->FirstChild();
  1814. while( pnode != NULL )
  1815. {
  1816. if( pnode->NodeType() == GROUP_NODETYPE )
  1817. {
  1818. bRes = AddMenuItem(pCallback, MID_MOVEQUERYNODE, CCM_INSERTIONPOINTID_PRIMARY_TOP, 0, _T("MOVEQUERY"));
  1819. hr = bRes ? S_OK : E_FAIL;
  1820. ASSERT(SUCCEEDED(hr));
  1821. break;
  1822. }
  1823. pnode = pnode->Next();
  1824. }
  1825. }
  1826. return hr;
  1827. }
  1828. HRESULT CQueryNode::SetToolButtons(LPTOOLBAR pToolbar)
  1829. {
  1830. VALIDATE_POINTER( pToolbar );
  1831. pToolbar->SetButtonState(MID_EDITQUERY, ENABLED, TRUE);
  1832. pToolbar->SetButtonState(MID_EDITQUERY, HIDDEN, FALSE);
  1833. pToolbar->SetButtonState(MID_STOPQUERY, ENABLED, (m_pQueryReq != NULL));
  1834. pToolbar->SetButtonState(MID_STOPQUERY, HIDDEN, FALSE);
  1835. return S_OK;
  1836. }
  1837. HRESULT CQueryNode::EditQuery(HWND hWndParent)
  1838. {
  1839. tstring strQueryTmp;
  1840. tstring strScopeTmp = Scope();
  1841. ExpandQuery(strQueryTmp);
  1842. HRESULT hr = GetQuery(strScopeTmp, strQueryTmp, m_bsQueryData, hWndParent);
  1843. if( FAILED(hr) )
  1844. {
  1845. DisplayMessageBox(NULL, IDS_ERRORTITLE_EDITQUERY, IDS_ERROR_EDITQUERY,
  1846. MB_OK|MB_ICONEXCLAMATION, GetName());
  1847. }
  1848. if( hr != S_OK )
  1849. return hr;
  1850. m_strQuery = strQueryTmp;
  1851. // if user changed the scope setting
  1852. if( strScopeTmp != Scope() )
  1853. {
  1854. // Update the node scope and turn off local scope option (i.e., use query specified scope)
  1855. SetScope(strScopeTmp.c_str());
  1856. SetLocalScope(FALSE);
  1857. }
  1858. // Determine the classes this query can return
  1859. std::set<tstring> setClasses;
  1860. GetQueryClasses(m_strQuery, setClasses);
  1861. // Delete current objects that aren't in new query
  1862. QueryObjVector::iterator itObj = m_vObjInfo.begin();
  1863. while( itObj != m_vObjInfo.end() )
  1864. {
  1865. if( setClasses.find(itObj->Name()) == setClasses.end() )
  1866. {
  1867. // delete item from list and leave iterator at this position
  1868. m_vObjInfo.erase(itObj);
  1869. }
  1870. else
  1871. {
  1872. // if found delete from set, so only new ones remain
  1873. setClasses.erase(itObj->Name());
  1874. ++itObj;
  1875. }
  1876. }
  1877. DisplayNameMap* pNameMap = DisplayNames::GetClassMap();
  1878. // Add any new objects
  1879. std::set<tstring>::iterator itClass;
  1880. for( itClass = setClasses.begin(); itClass != setClasses.end(); itClass++ )
  1881. {
  1882. if( pNameMap == NULL ||
  1883. pNameMap->GetAttributeDisplayName(itClass->c_str()) != itClass->c_str() )
  1884. {
  1885. CQueryObjInfo* pQueryObj = new CQueryObjInfo(itClass->c_str());
  1886. if( pQueryObj )
  1887. {
  1888. m_vObjInfo.push_back(*pQueryObj);
  1889. }
  1890. }
  1891. }
  1892. return S_OK;
  1893. }
  1894. class CRefreshCallback : public CEventCallback
  1895. {
  1896. public:
  1897. CRefreshCallback(HANDLE hProcess, CQueryNode* pQueryNode)
  1898. : m_hProcess(hProcess), m_spQueryNode(pQueryNode)
  1899. {
  1900. }
  1901. virtual void Execute()
  1902. {
  1903. if( m_spQueryNode )
  1904. {
  1905. m_spQueryNode->OnRefresh(NULL);
  1906. }
  1907. CloseHandle(m_hProcess);
  1908. }
  1909. HANDLE m_hProcess;
  1910. CQueryNodePtr m_spQueryNode;
  1911. };
  1912. class CNoLookup : public CParamLookup
  1913. {
  1914. public:
  1915. virtual BOOL operator() (tstring& strParam, tstring& strValue)
  1916. {
  1917. return FALSE;
  1918. };
  1919. };
  1920. HRESULT CQueryNode::MenuCommand(LPCONSOLE2 pConsole, long lCommand)
  1921. {
  1922. VALIDATE_POINTER(pConsole);
  1923. HRESULT hr = S_OK;
  1924. switch( lCommand )
  1925. {
  1926. case MID_EDITQUERY:
  1927. {
  1928. HWND hWndMain;
  1929. hr = pConsole->GetMainWindow(&hWndMain);
  1930. BREAK_ON_FAILURE(hr);
  1931. hr = EditQuery(hWndMain);
  1932. if( hr == S_FALSE )
  1933. {
  1934. hr = S_OK;
  1935. break;
  1936. }
  1937. m_bQueryChange = TRUE;
  1938. OnRefresh(pConsole);
  1939. }
  1940. break;
  1941. case MID_STOPQUERY:
  1942. if( m_pQueryReq != NULL )
  1943. m_pQueryReq->Stop(TRUE);
  1944. break;
  1945. case MID_MOVEQUERYNODE:
  1946. {
  1947. CScopeNode* pnodeDest = NULL;
  1948. CMoveQueryDlg dlg;
  1949. if( dlg.DoModal(Parent(), &pnodeDest) == IDOK )
  1950. {
  1951. ASSERT( pnodeDest );
  1952. if( !pnodeDest ) return E_FAIL;
  1953. // Ref node during move to prevent deletion
  1954. AddRef();
  1955. // Tell MMC to remove the node
  1956. IConsoleNameSpace* pNameSpace = GetCompData()->GetNameSpace();
  1957. ASSERT( pNameSpace );
  1958. if( !pNameSpace ) return E_FAIL;
  1959. pNameSpace->DeleteItem(m_hScopeItem, TRUE);
  1960. // clear item handle becuase it's no longer valid
  1961. m_hScopeItem = NULL;
  1962. // Now remove the node internally (MMC does not send a delete notify)
  1963. Parent()->RemoveChild(this);
  1964. // Add back to the new parent
  1965. hr = pnodeDest->AddChild(this);
  1966. Release();
  1967. }
  1968. }
  1969. break;
  1970. default:
  1971. {
  1972. // Must be one of the Querynode menus
  1973. ASSERT(lCommand < m_vMenus.size());
  1974. if( lCommand >= m_vMenus.size() )
  1975. return E_INVALIDARG;
  1976. HANDLE hProcess = NULL;
  1977. CNoLookup lookup;
  1978. hr = static_cast<CShellMenuCmd*>((CMenuCmd*)m_vMenus[lCommand])->Execute(&lookup, &hProcess);
  1979. // if process started and auto-refresh wanted, setup event-triggered callback
  1980. if( SUCCEEDED(hr) && hProcess != NULL && m_vMenus[lCommand]->IsAutoRefresh() )
  1981. {
  1982. CallbackOnEvent(hProcess, new CRefreshCallback(hProcess, this));
  1983. }
  1984. }
  1985. hr = S_FALSE;
  1986. }
  1987. return hr;
  1988. }
  1989. HRESULT CQueryNode::QueryPagesFor()
  1990. {
  1991. return S_OK;
  1992. }
  1993. HRESULT CQueryNode::CreatePropertyPages(LPPROPERTYSHEETCALLBACK pProvider, LONG_PTR handle)
  1994. {
  1995. VALIDATE_POINTER( pProvider );
  1996. // Create a query edit object
  1997. CQueryEditObj* pEditObj = new CQueryEditObj(this);
  1998. if( !pEditObj ) return E_OUTOFMEMORY;
  1999. // Create an instance of each prop page class and call Create on each.
  2000. // Keep it alive until prop pages ref it
  2001. pEditObj->AddRef();
  2002. // General page
  2003. HPROPSHEETPAGE hpageGen = NULL;
  2004. CQueryGeneralPage* pGenPage = new CQueryGeneralPage(pEditObj);
  2005. if( pGenPage != NULL )
  2006. {
  2007. hpageGen = pGenPage->Create();
  2008. }
  2009. // Context menu page
  2010. HPROPSHEETPAGE hpageMenu = NULL;
  2011. CQueryMenuPage* pMenuPage = new CQueryMenuPage(pEditObj);
  2012. if( pMenuPage != NULL )
  2013. {
  2014. hpageMenu = pMenuPage->Create();
  2015. }
  2016. // Listview page
  2017. HPROPSHEETPAGE hpageView = NULL;
  2018. CQueryViewPage* pViewPage = new CQueryViewPage(pEditObj);
  2019. if( pViewPage != NULL )
  2020. hpageView = pViewPage->Create();
  2021. // Node Menu page
  2022. HPROPSHEETPAGE hpageNodeMenu = NULL;
  2023. CQueryNodeMenuPage* pNodeMenuPage = new CQueryNodeMenuPage(pEditObj);
  2024. if( pNodeMenuPage != NULL )
  2025. {
  2026. hpageNodeMenu = pNodeMenuPage->Create();
  2027. }
  2028. HRESULT hr = E_OUTOFMEMORY;
  2029. // if all pages were created, add each one to the prop sheet
  2030. if( hpageGen && hpageMenu && hpageView )
  2031. {
  2032. hr = pProvider->AddPage(hpageGen);
  2033. if( SUCCEEDED(hr) )
  2034. hr = pProvider->AddPage(hpageMenu);
  2035. if( SUCCEEDED(hr) )
  2036. hr = pProvider->AddPage(hpageView);
  2037. if( SUCCEEDED(hr) )
  2038. hr = pProvider->AddPage(hpageNodeMenu);
  2039. }
  2040. // On failure, destroy the pages. If a page failed to create
  2041. // then delete the page class object instead (the object is
  2042. // automatically deleted when the page is destroyed)
  2043. if( FAILED(hr) )
  2044. {
  2045. if( hpageGen )
  2046. DestroyPropertySheetPage(hpageGen);
  2047. else
  2048. SAFE_DELETE(pGenPage);
  2049. if( hpageMenu )
  2050. DestroyPropertySheetPage(hpageMenu);
  2051. else
  2052. SAFE_DELETE(pMenuPage);
  2053. if( hpageView )
  2054. DestroyPropertySheetPage(hpageView);
  2055. else
  2056. SAFE_DELETE(pViewPage);
  2057. if( hpageNodeMenu )
  2058. DestroyPropertySheetPage(hpageNodeMenu);
  2059. else
  2060. SAFE_DELETE(pNodeMenuPage);
  2061. }
  2062. // Release temp ref on edit list
  2063. // it will go away when the prop pages release it
  2064. pEditObj->Release();
  2065. return hr;
  2066. }
  2067. //////////////////////////////////////////////////////////////////////
  2068. // CQueryLookup
  2069. //
  2070. BOOL CQueryLookup::operator() (tstring& strParam, tstring& strValue)
  2071. {
  2072. if( !m_pRowItem )
  2073. {
  2074. strValue = _T("");
  2075. return FALSE;
  2076. }
  2077. // Check for single digit parameter ID
  2078. if( strParam.size() == 1 && strParam[0] <= MENU_PARAM_LAST )
  2079. {
  2080. switch( strParam[0] )
  2081. {
  2082. case MENU_PARAM_SCOPE:
  2083. strValue = reinterpret_cast<CQueryNode*>(m_pRowItem->GetOwnerParam())->Scope();
  2084. break;
  2085. case MENU_PARAM_FILTER:
  2086. reinterpret_cast<CQueryNode*>(m_pRowItem->GetOwnerParam())->ExpandQuery(strValue);
  2087. break;
  2088. case MENU_PARAM_NAME:
  2089. strValue = (*m_pRowItem)[ROWITEM_NAME_INDEX];
  2090. break;
  2091. case MENU_PARAM_TYPE:
  2092. strValue = (*m_pRowItem)[ROWITEM_CLASS_INDEX];
  2093. break;
  2094. }
  2095. }
  2096. else
  2097. {
  2098. // see if parameter name matches column name
  2099. string_vector& vstrColumns = m_pQNode->QueryColumns();
  2100. string_vector::iterator itCol = std::find(vstrColumns.begin(), vstrColumns.end(), strParam);
  2101. // if so, substitue row item value at that position
  2102. if( itCol != vstrColumns.end() )
  2103. strValue = (*m_pRowItem)[(itCol - vstrColumns.begin()) + ROWITEM_USER_INDEX];
  2104. }
  2105. return !strValue.empty();
  2106. }
  2107. //////////////////////////////////////////////////////////////
  2108. // Stream operators (<< >>)
  2109. IStream& operator<< (IStream& stm, CClassInfo& classInfo)
  2110. {
  2111. stm << classInfo.m_strName;
  2112. stm << classInfo.m_vstrColumns;
  2113. stm << classInfo.m_vMenus;
  2114. return stm;
  2115. }
  2116. IStream& operator>> (IStream& stm, CClassInfo& classInfo)
  2117. {
  2118. stm >> classInfo.m_strName;
  2119. stm >> classInfo.m_vstrColumns;
  2120. stm >> classInfo.m_vMenus;
  2121. return stm;
  2122. }
  2123. IStream& operator<< (IStream& stm, CQueryObjInfo& objInfo)
  2124. {
  2125. stm << objInfo.m_strName;
  2126. stm << objInfo.m_vMenuRefs;
  2127. stm << objInfo.m_vstrDisabledColumns;
  2128. DWORD dwFlags = objInfo.m_bPropertyMenu ? 1 : 0;
  2129. stm << dwFlags;
  2130. return stm;
  2131. }
  2132. IStream& operator>> (IStream& stm, CQueryObjInfo& objInfo)
  2133. {
  2134. stm >> objInfo.m_strName;
  2135. stm >> objInfo.m_vMenuRefs;
  2136. stm >> objInfo.m_vstrDisabledColumns;
  2137. // File versions >= 102 include flag word
  2138. // Bit 0 enables the property menu
  2139. if( g_dwFileVer >= 102 )
  2140. {
  2141. DWORD dwFlags;
  2142. stm >> dwFlags;
  2143. objInfo.m_bPropertyMenu = (dwFlags & 1);
  2144. }
  2145. else
  2146. {
  2147. objInfo.m_bPropertyMenu = TRUE;
  2148. }
  2149. return stm;
  2150. }
  2151. IStream& operator>> (IStream& stm, CMenuRef& menuref)
  2152. {
  2153. stm >> menuref.m_menuID;
  2154. stm >> menuref.m_flags;
  2155. return stm;
  2156. }
  2157. IStream& operator<< (IStream& stm, CMenuRef& menuref)
  2158. {
  2159. stm << menuref.m_menuID;
  2160. stm << menuref.m_flags;
  2161. return stm;
  2162. }