Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5363 lines
170 KiB

  1. #include "pch.h"
  2. #include "stddef.h"
  3. #pragma hdrstop
  4. // class that implements the query UI
  5. class CDsQuery : public IQueryHandler, IQueryForm, IObjectWithSite, IDsQueryHandler, IShellFolder
  6. {
  7. public:
  8. CDsQuery();
  9. ~CDsQuery();
  10. // IUnknown
  11. STDMETHOD(QueryInterface)(REFIID riid, void **ppvObject);
  12. STDMETHOD_(ULONG, AddRef)();
  13. STDMETHOD_(ULONG, Release)();
  14. // IQueryForms
  15. STDMETHOD(Initialize)(HKEY hkForm);
  16. STDMETHOD(AddForms)(LPCQADDFORMSPROC pAddFormsProc, LPARAM lParam);
  17. STDMETHOD(AddPages)(LPCQADDPAGESPROC pAddPagesProc, LPARAM lParam);
  18. // IQueryHandler
  19. STDMETHOD(Initialize)(IQueryFrame* pQueryFrame, DWORD dwOQWFlags, LPVOID pParameters);
  20. STDMETHOD(GetViewInfo)(LPCQVIEWINFO pViewInfo);
  21. STDMETHOD(AddScopes)();
  22. STDMETHOD(BrowseForScope)(HWND hwndParent, LPCQSCOPE pCurrentScope, LPCQSCOPE* ppScope);
  23. STDMETHOD(CreateResultView)(HWND hwndParent, HWND* phWndView);
  24. STDMETHOD(ActivateView)(UINT uState, WPARAM wParam, LPARAM lParam);
  25. STDMETHOD(InvokeCommand)(HWND hwndParent, UINT uID);
  26. STDMETHOD(GetCommandString)(UINT uID, DWORD dwFlags, LPTSTR pBuffer, INT cchBuffer);
  27. STDMETHOD(IssueQuery)(LPCQPARAMS pQueryParams);
  28. STDMETHOD(StopQuery)();
  29. STDMETHOD(GetViewObject)(UINT uScope, REFIID riid, void **ppvOut);
  30. STDMETHOD(LoadQuery)(IPersistQuery* pPersistQuery);
  31. STDMETHOD(SaveQuery)(IPersistQuery* pPersistQuery, LPCQSCOPE pScope);
  32. // IObjectWithSite
  33. STDMETHODIMP SetSite(IUnknown* punk);
  34. STDMETHODIMP GetSite(REFIID riid, void **ppv);
  35. // IDsQueryHandler
  36. STDMETHOD(UpdateView)(DWORD dwType, LPDSOBJECTNAMES pdon);
  37. // IShellFolder
  38. STDMETHOD(ParseDisplayName)(HWND hwnd, LPBC pbc, LPOLESTR pszName, ULONG * pchEaten, LPITEMIDLIST * ppidl, ULONG *pdwAttributes)
  39. { return E_NOTIMPL; }
  40. STDMETHOD(EnumObjects)(HWND hwndOwner, DWORD grfFlags, LPENUMIDLIST * ppEnumIDList)
  41. { return E_NOTIMPL; }
  42. STDMETHOD(BindToObject)(LPCITEMIDLIST pidl, LPBC pbcReserved, REFIID riid, void **ppv)
  43. { return E_NOTIMPL; }
  44. STDMETHOD(BindToStorage)(LPCITEMIDLIST pidl, LPBC pbcReserved, REFIID riid, void **ppv)
  45. { return E_NOTIMPL; }
  46. STDMETHOD(CompareIDs)(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  47. { return E_NOTIMPL; }
  48. STDMETHOD(CreateViewObject)(HWND hwndOwner, REFIID riid, void **ppv)
  49. { return E_NOTIMPL; }
  50. STDMETHOD(GetAttributesOf)(UINT cidl, LPCITEMIDLIST * apidl, ULONG * rgfInOut)
  51. { *rgfInOut &= SFGAO_HASPROPSHEET; return S_OK; }
  52. STDMETHOD(GetUIObjectOf)(HWND hwndOwner, UINT cidl, LPCITEMIDLIST * apidl, REFIID riid, UINT * prgfInOut, void **ppv)
  53. { return GetViewObject(0x0, riid, ppv); }
  54. STDMETHOD(GetDisplayNameOf)(LPCITEMIDLIST pidl, DWORD uFlags, LPSTRRET pName)
  55. { return E_NOTIMPL; }
  56. STDMETHOD(SetNameOf)(HWND hwndOwner, LPCITEMIDLIST pidl, LPCOLESTR pszName, DWORD uFlags, LPITEMIDLIST* ppidlOut)
  57. { return E_NOTIMPL; }
  58. private:
  59. LRESULT OnSize(INT cx, INT cy);
  60. LRESULT OnNotify(HWND hWnd, WPARAM wParam, LPARAM lParam);
  61. HRESULT OnAddResults(DWORD dwQueryReference, HDPA hdpaResults);
  62. LRESULT OnContextMenu(HWND hwndMenu, LPARAM lParam);
  63. HRESULT OnFileProperties(VOID);
  64. HRESULT OnFileSaveQuery(VOID);
  65. HRESULT OnEditSelectAll(VOID);
  66. HRESULT OnEditInvertSelection(VOID);
  67. HRESULT OnPickColumns(HWND hwndParent);
  68. static int s_BrowseForScopeCB(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData);
  69. static LRESULT s_ResultViewWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  70. static LRESULT s_BannerWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  71. HRESULT _SetDataObjectData(IDataObject* pDataObject, UINT cf, LPVOID pData, DWORD cbSize);
  72. HRESULT _SetDispSpecOptions(IDataObject *pdo);
  73. HRESULT _InitNewQuery(LPDSQUERYPARAMS pDsQueryParams, BOOL fRefreshColumnTable);
  74. HRESULT _GetFilterValue(INT iColumn, HD_ITEM* pitem);
  75. HRESULT _FilterView(BOOL fCheck);
  76. HRESULT _PopulateView(INT iFirstItem, INT iLast);
  77. VOID _FreeResults(VOID);
  78. DWORD _SetViewMode(INT uID);
  79. VOID _SortResults(INT iColumn);
  80. VOID _SetFilter(BOOL fFilter);
  81. VOID _ShowBanner(UINT flags, UINT idPrompt);
  82. VOID _InitViewMenuItems(HMENU hMenu);
  83. HRESULT _GetQueryFormKey(REFCLSID clsidForm, HKEY* phKey);
  84. HRESULT _GetColumnTable(REFCLSID clsidForm, LPDSQUERYPARAMS pDsQueryParams, HDSA* pHDSA, BOOL fSetInView);
  85. VOID _SaveColumnTable(VOID);
  86. HRESULT _SaveColumnTable(REFCLSID clsidForm, HDSA hdsaColumns);
  87. HRESULT _AddResultToDataObject(HDSA hdsa, INT i);
  88. HRESULT _GetDataObjectFromSelection(BOOL fGetAll, IDataObject **pdo);
  89. HRESULT _GetContextMenu();
  90. VOID _GetContextMenuVerbs(HMENU hMenu, DWORD dwFlags);
  91. HRESULT _CopyCredentials(LPWSTR *ppszUserName, LPWSTR *ppszPassword, LPWSTR *ppszServer);
  92. VOID _DeleteViewItems(LPDSOBJECTNAMES pdon);
  93. private:
  94. LONG _cRef; // lifetime
  95. IQueryFrame* _pqf; // our parent window
  96. IUnknown* _punkSite; // site object
  97. IContextMenu* _pcm; // Curerntly displayed context menu / == NULL if none
  98. DWORD _dwOQWFlags; // flags passed to OpenQueryWindow
  99. DWORD _dwFlags; // flags as part of the ds query parameters
  100. LPWSTR _pDefaultScope; // default scope passed
  101. LPWSTR _pDefaultSaveLocation; // directory to save queries into by default
  102. LPTSTR _pDefaultSaveName; // default save name (from the query form)
  103. LPWSTR _pServer; // server to target
  104. LPWSTR _pUserName; // user name and password to authenticate with
  105. LPWSTR _pPassword;
  106. BOOL _fNoSelection:1; // the IContextMenu was from no selection
  107. BOOL _fColumnsModified:1; // settings of the view modified
  108. BOOL _fSortDescending:1; // sort the results descending
  109. BOOL _fFilter:1; // filter enabled
  110. BOOL _fFilterSupported:1; // is the filter available, eg: comctl32 > 5.0
  111. INT _idViewMode; // default view mode
  112. INT _iSortColumn; // sort column
  113. HWND _hwnd; // container window
  114. HWND _hwndView; // listview window (child of parent)
  115. HWND _hwndBanner; // banner window which is a child of the list view
  116. DWORD _dwQueryReference; // reference value passed to query
  117. HANDLE _hThread; // worker thread handle
  118. DWORD _dwThreadId; // thread ID for the Query processing thread
  119. CLSID _clsidForm; // form being used for column table
  120. HDSA _hdsaColumns; // column information (size, filters, etc)
  121. HDPA _hdpaResults; // results for tr the query we have issued
  122. LPTSTR _pFilter; // current filter
  123. HMENU _hFrameMenuBar; // stored frame menu bar, stored from activate
  124. HMENU _hFileMenu; // added to the frames view menu
  125. HMENU _hEditMenu; // inserted into the menu bar
  126. HMENU _hViewMenu; // inserted into the menu bar
  127. HMENU _hHelpMenu; // inserted into the menu bar
  128. };
  129. //
  130. // Window classes we create to show the results
  131. //
  132. #define VIEW_CLASS TEXT("ActiveDsQueryView")
  133. #define BANNER_CLASS TEXT("ActiveDsQueryBanner")
  134. //
  135. // Registry values used for the settings
  136. //
  137. #define VIEW_SETTINGS_VALUE TEXT("ViewSettings")
  138. #define ADMIN_VIEW_SETTINGS_VALUE TEXT("AdminViewSettings");
  139. //
  140. // When filtering we populate the view using PostMessage, doing so many items
  141. // at a time
  142. //
  143. #define FILTER_UPDATE_COUNT 128
  144. //
  145. // All items within the list view contain the following LPARAM structure used
  146. // for storing the magic properties we are interested in.
  147. //
  148. #define ENABLE_MENU_ITEM(hMenu, id, fEnabled) \
  149. EnableMenuItem(hMenu, id, (fEnabled) ? (MF_BYCOMMAND|MF_ENABLED):(MF_BYCOMMAND|MF_GRAYED))
  150. //
  151. // Persisted column data, this is stored in the registry under the CLSID for the
  152. // form we are interested in.
  153. //
  154. typedef struct
  155. {
  156. DWORD cbSize; // offset to the next column / == 0 if none
  157. DWORD dwFlags; // flags
  158. DWORD offsetProperty; // offset to property name (UNICODE)
  159. DWORD offsetHeading; // offset to column heading
  160. INT cx; // pixel width of the column
  161. INT fmt; // format of the column
  162. } SAVEDCOLUMN, * LPSAVEDCOLUMN;
  163. //
  164. // Table to map property types to useful information
  165. //
  166. struct
  167. {
  168. LPCTSTR pMenuName;
  169. INT idOperator;
  170. INT hdft;
  171. }
  172. property_type_table[] =
  173. {
  174. 0, 0, 0,
  175. MAKEINTRESOURCE(IDR_OP_STRING), FILTER_CONTAINS, HDFT_ISSTRING,
  176. MAKEINTRESOURCE(IDR_OP_STRING), FILTER_CONTAINS, HDFT_ISSTRING,
  177. MAKEINTRESOURCE(IDR_OP_NUMBER), FILTER_IS, HDFT_ISNUMBER,
  178. MAKEINTRESOURCE(IDR_OP_NUMBER), FILTER_IS, HDFT_ISNUMBER, // PROPERTY_ISBOOL
  179. };
  180. //
  181. // Help information for the frame and the control
  182. //
  183. static DWORD const aHelpIDs[] =
  184. {
  185. CQID_LOOKFORLABEL, IDH_FIND,
  186. CQID_LOOKFOR, IDH_FIND,
  187. CQID_LOOKINLABEL, IDH_IN,
  188. CQID_LOOKIN, IDH_IN,
  189. CQID_BROWSE, IDH_BROWSE,
  190. CQID_FINDNOW, IDH_FIND_NOW,
  191. CQID_STOP, IDH_STOP,
  192. CQID_CLEARALL, IDH_CLEAR_ALL,
  193. IDC_RESULTS, IDH_RESULTS,
  194. IDC_STATUS, IDH_NO_HELP,
  195. 0, 0,
  196. };
  197. static DWORD const aBrowseHelpIDs[] =
  198. {
  199. DSBID_BANNER, (DWORD)-1,
  200. DSBID_CONTAINERLIST, IDH_BROWSE_CONTAINER,
  201. 0, 0,
  202. };
  203. // Query object
  204. CDsQuery::CDsQuery() :
  205. _cRef(1), _fNoSelection(TRUE), _iSortColumn(-1), _idViewMode(DSQH_VIEW_DETAILS)
  206. {
  207. if ( CheckDsPolicy(NULL, c_szEnableFilter) )
  208. {
  209. TraceMsg("QuickFilter enabled in policy");
  210. _fFilter = TRUE;
  211. }
  212. DllAddRef();
  213. }
  214. CDsQuery::~CDsQuery()
  215. {
  216. // persist the column information if we need to
  217. if ( _hdsaColumns )
  218. {
  219. if ( _fColumnsModified )
  220. {
  221. _SaveColumnTable(_clsidForm, _hdsaColumns);
  222. _fColumnsModified = FALSE;
  223. }
  224. _SaveColumnTable();
  225. }
  226. // discard all the other random state we have
  227. LocalFreeStringW(&_pDefaultScope);
  228. LocalFreeStringW(&_pDefaultSaveLocation);
  229. LocalFreeString(&_pDefaultSaveName);
  230. LocalFreeStringW(&_pUserName);
  231. LocalFreeStringW(&_pPassword);
  232. LocalFreeStringW(&_pServer);
  233. if ( IsWindow(_hwnd) )
  234. DestroyWindow(_hwnd);
  235. if ( IsMenu(_hFileMenu) )
  236. DestroyMenu(_hFileMenu);
  237. if ( IsMenu(_hEditMenu) )
  238. DestroyMenu(_hEditMenu);
  239. if ( IsMenu(_hViewMenu) )
  240. DestroyMenu(_hViewMenu);
  241. if ( IsMenu(_hHelpMenu) )
  242. DestroyMenu(_hHelpMenu);
  243. // tell the thread its time to die
  244. if ( _hThread )
  245. {
  246. PostThreadMessage(_dwThreadId, RVTM_STOPQUERY, 0, 0);
  247. PostThreadMessage(_dwThreadId, WM_QUIT, 0, 0);
  248. CloseHandle(_hThread);
  249. }
  250. DoRelease(_pqf);
  251. DoRelease(_punkSite);
  252. DoRelease(_pcm);
  253. DllRelease();
  254. }
  255. // IUnknown bits
  256. ULONG CDsQuery::AddRef()
  257. {
  258. return InterlockedIncrement(&_cRef);
  259. }
  260. ULONG CDsQuery::Release()
  261. {
  262. if (InterlockedDecrement(&_cRef))
  263. return _cRef;
  264. delete this;
  265. return 0;
  266. }
  267. HRESULT CDsQuery::QueryInterface(REFIID riid, void **ppv)
  268. {
  269. static const QITAB qit[] =
  270. {
  271. QITABENT(CDsQuery, IQueryForm), // IID_IQueryForm
  272. QITABENT(CDsQuery, IQueryHandler), // IID_IQueryHandler
  273. QITABENT(CDsQuery, IObjectWithSite), // IID_IObjectWIthSite
  274. QITABENT(CDsQuery, IDsQueryHandler), // IID_IDsQueryHandler
  275. QITABENT(CDsQuery, IShellFolder), // IID_IShellFolder
  276. {0, 0 },
  277. };
  278. return QISearch(this, qit, riid, ppv);
  279. }
  280. //
  281. // Handle creating an instance of CLSID_DsQuery
  282. //
  283. STDAPI CDsQuery_CreateInstance(IUnknown* punkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
  284. {
  285. CDsQuery *pdq = new CDsQuery();
  286. if ( !pdq )
  287. return E_OUTOFMEMORY;
  288. HRESULT hres = pdq->QueryInterface(IID_IUnknown, (void **)ppunk);
  289. pdq->Release();
  290. return hres;
  291. }
  292. /// IQueryForm
  293. STDMETHODIMP CDsQuery::Initialize(HKEY hkForm)
  294. {
  295. return S_OK;
  296. }
  297. // query forms exposed from this object
  298. struct
  299. {
  300. CLSID const * clsidForm;
  301. INT idsTitle;
  302. DWORD dwFlags;
  303. }
  304. forms[] =
  305. {
  306. &CLSID_DsFindPeople, IDS_FINDUSER, 0,
  307. &CLSID_DsFindComputer, IDS_FINDCOMPUTER, 0,
  308. &CLSID_DsFindPrinter, IDS_FINDPRINTERS, 0,
  309. &CLSID_DsFindVolume, IDS_FINDSHAREDFOLDERS, 0,
  310. &CLSID_DsFindContainer, IDS_FINDOU, 0,
  311. &CLSID_DsFindAdvanced, IDS_CUSTOMSEARCH, CQFF_NOGLOBALPAGES,
  312. &CLSID_DsFindDomainController, IDS_FINDDOMCTL, CQFF_ISNEVERLISTED|CQFF_NOGLOBALPAGES,
  313. &CLSID_DsFindFrsMembers, IDS_FINDFRSMEMBER, CQFF_ISNEVERLISTED|CQFF_NOGLOBALPAGES,
  314. };
  315. STDMETHODIMP CDsQuery::AddForms(LPCQADDFORMSPROC pAddFormsProc, LPARAM lParam)
  316. {
  317. HRESULT hres;
  318. TCHAR szBuffer[MAX_PATH];
  319. INT i;
  320. TraceEnter(TRACE_FORMS, "CDsQuery::AddForms");
  321. if ( !pAddFormsProc )
  322. ExitGracefully(hres, E_INVALIDARG, "No AddFormsProc");
  323. for ( i = 0; i < ARRAYSIZE(forms); i++ )
  324. {
  325. CQFORM qf = { 0 };
  326. qf.cbStruct = SIZEOF(qf);
  327. qf.dwFlags = forms[i].dwFlags;
  328. qf.clsid = *forms[i].clsidForm;
  329. qf.pszTitle = szBuffer;
  330. LoadString(GLOBAL_HINSTANCE, forms[i].idsTitle, szBuffer, ARRAYSIZE(szBuffer));
  331. hres = (*pAddFormsProc)(lParam, &qf);
  332. FailGracefully(hres, "Failed to add form (calling pAddFormsFunc)");
  333. }
  334. hres = S_OK; // success
  335. exit_gracefully:
  336. TraceLeaveResult(hres);
  337. }
  338. // page information for this object
  339. struct
  340. {
  341. CLSID const * clisdForm;
  342. LPCQPAGEPROC pPageProc;
  343. DLGPROC pDlgProc;
  344. INT idPageTemplate;
  345. INT idPageName;
  346. DWORD dwFlags;
  347. }
  348. pages[] =
  349. {
  350. //
  351. // Page list for the default forms that we add
  352. //
  353. &CLSID_DsFindPeople, PageProc_User, DlgProc_User, IDD_FINDUSER, IDS_FINDUSER, 0,
  354. &CLSID_DsFindComputer, PageProc_Computer, DlgProc_Computer, IDD_FINDCOMPUTER, IDS_FINDCOMPUTER, 0,
  355. &CLSID_DsFindPrinter, PageProc_Printers, DlgProc_Printers, IDD_FINDPRINT1, IDS_FINDPRINTERS, 0,
  356. &CLSID_DsFindPrinter, PageProc_PrintersMore, DlgProc_PrintersMore, IDD_FINDPRINT2, IDS_MORECHOICES, 0,
  357. &CLSID_DsFindVolume, PageProc_Volume, DlgProc_Volume, IDD_FINDVOLUME, IDS_FINDSHAREDFOLDERS, 0,
  358. &CLSID_DsFindContainer, PageProc_Container, DlgProc_Container, IDD_FINDCONTAINER, IDS_FINDOU, 0,
  359. &CLSID_DsFindAdvanced, PageProc_PropertyWell, DlgProc_PropertyWell, IDD_PROPERTYWELL, IDS_CUSTOMSEARCH, 0,
  360. &CLSID_DsFindAdvanced, PageProc_RawLDAP, DlgProc_RawLDAP, IDD_FINDUSINGLDAP, IDS_ADVANCED, 0,
  361. &CLSID_DsFindDomainController, PageProc_DomainController, DlgProc_DomainController, IDD_FINDDOMCTL, IDS_FINDDOMCTL, 0,
  362. &CLSID_DsFindFrsMembers, PageProc_FrsMember, DlgProc_FrsMember, IDD_FINDFRSMEMBER, IDS_FINDFRSMEMBER, 0,
  363. //
  364. // Make the property well available on all pages (using the magic CQPF_ADDTOALLFORMS bit)
  365. //
  366. &CLSID_DsFindAdvanced, PageProc_PropertyWell, DlgProc_PropertyWell, IDD_PROPERTYWELL, IDS_ADVANCED, CQPF_ISGLOBAL,
  367. };
  368. STDMETHODIMP CDsQuery::AddPages(LPCQADDPAGESPROC pAddPagesProc, LPARAM lParam)
  369. {
  370. HRESULT hres;
  371. INT i;
  372. TraceEnter(TRACE_FORMS, "CDsQuery::AddPages");
  373. if ( !pAddPagesProc )
  374. ExitGracefully(hres, E_INVALIDARG, "No AddPagesProc");
  375. for ( i = 0 ; i < ARRAYSIZE(pages) ; i++ )
  376. {
  377. CQPAGE qp = { 0 };
  378. qp.cbStruct = SIZEOF(qp);
  379. qp.dwFlags = pages[i].dwFlags;
  380. qp.pPageProc = pages[i].pPageProc;
  381. qp.hInstance = GLOBAL_HINSTANCE;
  382. qp.idPageName = pages[i].idPageName;
  383. qp.idPageTemplate = pages[i].idPageTemplate;
  384. qp.pDlgProc = pages[i].pDlgProc;
  385. hres = (*pAddPagesProc)(lParam, *pages[i].clisdForm, &qp);
  386. FailGracefully(hres, "Failed to add page (calling pAddPagesFunc)");
  387. }
  388. hres = S_OK;
  389. exit_gracefully:
  390. TraceLeaveResult(S_OK);
  391. }
  392. // IQueryHandler
  393. STDMETHODIMP CDsQuery::Initialize(IQueryFrame* pQueryFrame, DWORD dwOQWFlags, LPVOID pParameters)
  394. {
  395. HRESULT hres;
  396. LPDSQUERYINITPARAMS pDsQueryInitParams = (LPDSQUERYINITPARAMS)pParameters;
  397. TCHAR szGUID[GUIDSTR_MAX];
  398. TCHAR szBuffer[MAX_PATH];
  399. HINSTANCE hInstanceComCtl32 = NULL;
  400. USES_CONVERSION;
  401. TraceEnter(TRACE_HANDLER, "CDsQuery::Initialize");
  402. // Keep the IQueryFrame interface, we need it for menu negotiation and other
  403. // view -> frame interactions.
  404. _pqf = pQueryFrame;
  405. _pqf->AddRef();
  406. _dwOQWFlags = dwOQWFlags;
  407. // If we have a parameter block then lets take copies of the interesting
  408. // fields from there.
  409. if ( pDsQueryInitParams )
  410. {
  411. _dwFlags = pDsQueryInitParams->dwFlags;
  412. // did the user specify a default scope?
  413. if ( pDsQueryInitParams->pDefaultScope && pDsQueryInitParams->pDefaultScope[0] )
  414. {
  415. Trace(TEXT("Default scope:"), W2T(pDsQueryInitParams->pDefaultScope));
  416. hres = LocalAllocStringW(&_pDefaultScope, pDsQueryInitParams->pDefaultScope);
  417. FailGracefully(hres, "Failed to cope default scope");
  418. }
  419. // default save location?
  420. if ( (_dwFlags & DSQPF_SAVELOCATION) && pDsQueryInitParams->pDefaultSaveLocation )
  421. {
  422. Trace(TEXT("Default save location:"), W2T(pDsQueryInitParams->pDefaultSaveLocation));
  423. hres = LocalAllocStringW(&_pDefaultSaveLocation, pDsQueryInitParams->pDefaultSaveLocation);
  424. FailGracefully(hres, "Failed to copy save location");
  425. }
  426. // do we have credential information?
  427. if ( _dwFlags & DSQPF_HASCREDENTIALS )
  428. {
  429. TraceMsg("Copying credential/server information from init params");
  430. if ( pDsQueryInitParams->pUserName )
  431. {
  432. hres = LocalAllocStringW(&_pUserName, pDsQueryInitParams->pUserName);
  433. FailGracefully(hres, "Failed to copy user name");
  434. }
  435. if ( pDsQueryInitParams->pPassword )
  436. {
  437. hres = LocalAllocStringW(&_pPassword, pDsQueryInitParams->pPassword);
  438. FailGracefully(hres, "Failed to copy password");
  439. }
  440. if ( pDsQueryInitParams->pServer )
  441. {
  442. hres = LocalAllocStringW(&_pServer, pDsQueryInitParams->pServer);
  443. FailGracefully(hres, "Failed to copy server");
  444. }
  445. Trace(TEXT("_pUserName : %s"), _pUserName ? W2T(_pUserName):TEXT("<not specified>"));
  446. Trace(TEXT("_pPassword : %s"), _pPassword ? W2T(_pPassword):TEXT("<not specified>"));
  447. Trace(TEXT("_pServer : %s"), _pServer ? W2T(_pServer):TEXT("<not specified>"));
  448. }
  449. }
  450. // Finally load the must structures that we are going to use, then modify them
  451. // based on the flags that the caller gave us.
  452. //
  453. // NB: removes the last two items from the file menu assumed to be the
  454. // "save" and its seperator
  455. _hFileMenu = LoadMenu(GLOBAL_HINSTANCE, MAKEINTRESOURCE(IDR_MENU_FILE));
  456. _hEditMenu = LoadMenu(GLOBAL_HINSTANCE, MAKEINTRESOURCE(IDR_MENU_EDIT));
  457. _hViewMenu = LoadMenu(GLOBAL_HINSTANCE, MAKEINTRESOURCE(IDR_MENU_VIEW));
  458. _hHelpMenu = LoadMenu(GLOBAL_HINSTANCE, MAKEINTRESOURCE(IDR_MENU_HELP));
  459. if ( !_hFileMenu || !_hEditMenu || !_hViewMenu || !_hHelpMenu )
  460. ExitGracefully(hres, E_FAIL, "Failed to load resources for menus");
  461. if ( _dwFlags & DSQPF_NOSAVE )
  462. {
  463. HMENU hFileMenu = GetSubMenu(_hFileMenu, 0);
  464. INT i = GetMenuItemCount(hFileMenu);
  465. DeleteMenu(hFileMenu, i-1, MF_BYPOSITION);
  466. DeleteMenu(hFileMenu, i-2, MF_BYPOSITION);
  467. }
  468. // Init ComCtl32, including checking to see if we can use the filter control or not,
  469. // the filter control was added to the WC_HEADER32 in IE5, so check the DLL version
  470. // to see which we are using.
  471. InitCommonControls();
  472. hInstanceComCtl32 = GetModuleHandle(TEXT("comctl32"));
  473. TraceAssert(hInstanceComCtl32);
  474. if ( hInstanceComCtl32 )
  475. {
  476. DLLVERSIONINFO dllVersionInfo = { 0 };
  477. DLLGETVERSIONPROC pfnDllGetVersion = (DLLGETVERSIONPROC)GetProcAddress(hInstanceComCtl32, "DllGetVersion");
  478. TraceAssert(pfnDllGetVersion);
  479. dllVersionInfo.cbSize = SIZEOF(dllVersionInfo);
  480. if ( pfnDllGetVersion && SUCCEEDED(pfnDllGetVersion(&dllVersionInfo)) )
  481. {
  482. Trace(TEXT("DllGetVersion succeeded on ComCtl32, dwMajorVersion %08x"), dllVersionInfo.dwMajorVersion);
  483. _fFilterSupported = dllVersionInfo.dwMajorVersion >= 5;
  484. }
  485. }
  486. Trace(TEXT("_fFilterSupported is %d"), _fFilterSupported);
  487. hres = S_OK; // success
  488. exit_gracefully:
  489. TraceLeaveResult(hres);
  490. }
  491. /*---------------------------------------------------------------------------*/
  492. STDMETHODIMP CDsQuery::GetViewInfo(LPCQVIEWINFO pViewInfo)
  493. {
  494. HICON hIcon;
  495. TraceEnter(TRACE_HANDLER, "CDsQuery::GetViewInfo");
  496. pViewInfo->dwFlags = 0;
  497. pViewInfo->hInstance = GLOBAL_HINSTANCE;
  498. pViewInfo->idLargeIcon = IDI_FINDDS;
  499. pViewInfo->idSmallIcon = IDI_FINDDS;
  500. pViewInfo->idTitle = IDS_WINDOWTITLE;
  501. pViewInfo->idAnimation = IDR_DSFINDANIMATION;
  502. TraceLeaveResult(S_OK);
  503. }
  504. /*---------------------------------------------------------------------------*/
  505. STDMETHODIMP CDsQuery::AddScopes()
  506. {
  507. HRESULT hres;
  508. DWORD dwThreadId;
  509. HANDLE hThread;
  510. LPSCOPETHREADDATA pstd = NULL;
  511. USES_CONVERSION;
  512. TraceEnter(TRACE_HANDLER, "CDsQuery::AddScopes");
  513. // Enumerate the rest of the scopes on a seperate thread to gather the
  514. // scopes we are interested in.
  515. pstd = (LPSCOPETHREADDATA)LocalAlloc(LPTR, SIZEOF(SCOPETHREADDATA));
  516. TraceAssert(pstd);
  517. if ( !pstd )
  518. ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate scope data structure");
  519. _pqf->GetWindow(&pstd->hwndFrame);
  520. // pstd->pDefaultScope = NULL;
  521. // pstd->pServer = NULL; // no credential stuff currently
  522. // pstd->pUserName = NULL;
  523. // pstd->pPassword = NULL;
  524. if ( _pDefaultScope )
  525. {
  526. hres = LocalAllocStringW(&pstd->pDefaultScope, _pDefaultScope);
  527. FailGracefully(hres, "Failed to copy the default scope");
  528. }
  529. hres = _CopyCredentials(&pstd->pUserName, &pstd->pPassword, &pstd->pServer);
  530. FailGracefully(hres, "Failed to copy credentails");
  531. DllAddRef();
  532. hThread = CreateThread(NULL, 0, AddScopesThread, pstd, 0, &dwThreadId);
  533. TraceAssert(hThread);
  534. if ( !hThread )
  535. {
  536. DllRelease();
  537. ExitGracefully(hres, E_FAIL, "Failed to create background thread to enum scopes - BAD!");
  538. }
  539. CloseHandle(hThread);
  540. hres = S_OK;
  541. exit_gracefully:
  542. if ( FAILED(hres) && pstd )
  543. {
  544. LocalFreeStringW(&pstd->pDefaultScope);
  545. LocalFree((HLOCAL)pstd);
  546. }
  547. TraceLeaveResult(hres);
  548. }
  549. /*---------------------------------------------------------------------------*/
  550. typedef struct
  551. {
  552. IADsPathname *padp;
  553. WCHAR szGcPath[MAX_PATH];
  554. } BROWSEFORSCOPE;
  555. int CALLBACK CDsQuery::s_BrowseForScopeCB(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
  556. {
  557. HRESULT hres;
  558. INT iResult = 0;
  559. BROWSEFORSCOPE *pbfs = (BROWSEFORSCOPE*)lpData;
  560. LPTSTR pDirectoryName = NULL;
  561. USES_CONVERSION;
  562. TraceEnter(TRACE_HANDLER, "CDsQuery::s_BrowseForScopeCB");
  563. switch ( uMsg )
  564. {
  565. case DSBM_QUERYINSERT:
  566. {
  567. PDSBITEM pItem = (PDSBITEM)lParam;
  568. TraceAssert(pItem);
  569. // We are interested in modifying the root item of the tree, therefore
  570. // lets check for that being inserted, if it is then we change the
  571. // display name and the icon being shown.
  572. if ( pItem->dwState & DSBS_ROOT )
  573. {
  574. GetModuleFileName(GLOBAL_HINSTANCE, pItem->szIconLocation, ARRAYSIZE(pItem->szIconLocation));
  575. pItem->iIconResID = -IDI_GLOBALCATALOG;
  576. if ( SUCCEEDED(FormatDirectoryName(&pDirectoryName, GLOBAL_HINSTANCE, IDS_GLOBALCATALOG)) )
  577. {
  578. StrCpyN(pItem->szDisplayName, pDirectoryName, DSB_MAX_DISPLAYNAME_CHARS);
  579. LocalFreeString(&pDirectoryName);
  580. }
  581. pItem->dwMask |= DSBF_DISPLAYNAME|DSBF_ICONLOCATION;
  582. iResult = TRUE;
  583. }
  584. break;
  585. }
  586. case BFFM_SELCHANGED:
  587. {
  588. BOOL fEnableOK = TRUE;
  589. LPWSTR pszPath = (LPWSTR)lParam;
  590. LONG nElements = 0;
  591. // The user changes the selection in the browse dialog, therefore
  592. // lets see if we should be enabling the OK button. If the user
  593. // selects GC, but we don't have a GC then we disable it.
  594. if ( SUCCEEDED(pbfs->padp->Set(pszPath, ADS_SETTYPE_FULL)) )
  595. {
  596. pbfs->padp->GetNumElements(&nElements);
  597. Trace(TEXT("nElements on exit from GetNumElements %d"), nElements);
  598. }
  599. if ( !nElements && !pbfs->szGcPath[0] )
  600. {
  601. TraceMsg("'entire directory' selected with NO GC!");
  602. fEnableOK = FALSE;
  603. }
  604. SendMessage(hwnd, BFFM_ENABLEOK, (WPARAM)fEnableOK, 0L);
  605. break;
  606. }
  607. case DSBM_HELP:
  608. {
  609. WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle,
  610. DSQUERY_HELPFILE,
  611. HELP_WM_HELP,
  612. (DWORD_PTR)aBrowseHelpIDs);
  613. break;
  614. }
  615. case DSBM_CONTEXTMENU:
  616. {
  617. WinHelp((HWND)lParam,
  618. DSQUERY_HELPFILE,
  619. HELP_CONTEXTMENU,
  620. (DWORD_PTR)aBrowseHelpIDs);
  621. break;
  622. }
  623. }
  624. TraceLeaveValue(iResult);
  625. }
  626. STDMETHODIMP CDsQuery::BrowseForScope(HWND hwndParent, LPCQSCOPE pCurrentScope, LPCQSCOPE* ppScope)
  627. {
  628. HRESULT hres;
  629. LPDSQUERYSCOPE pDsQueryScope = (LPDSQUERYSCOPE)pCurrentScope;
  630. BROWSEFORSCOPE bfs = { 0 };
  631. DSBROWSEINFO dsbi = { 0 };
  632. INT iResult;
  633. WCHAR szPath[2048];
  634. WCHAR szRoot[MAX_PATH+10]; // LDAP://
  635. WCHAR szObjectClass[64];
  636. LONG nElements;
  637. USES_CONVERSION;
  638. TraceEnter(TRACE_HANDLER, "CDsQuery::BrowseForScope");
  639. Trace(TEXT("hwndParent %08x, pCurrentScope %08x, ppScope %08x"), hwndParent, pCurrentScope, ppScope);
  640. *ppScope = NULL; // nothing yet!
  641. if ( SUCCEEDED(GetGlobalCatalogPath(_pServer, bfs.szGcPath, ARRAYSIZE(bfs.szGcPath))) )
  642. Trace(TEXT("GC path is: %s"), W2T(bfs.szGcPath));
  643. hres = CoCreateInstance(CLSID_Pathname, NULL, CLSCTX_INPROC_SERVER, IID_IADsPathname, (void **)&bfs.padp);
  644. FailGracefully(hres, "Failed to get the IADsPathname interface");
  645. // Fill out the browse info structure to display the object picker, if we have
  646. // enabled admin features then lets make all objects visible, otherwise
  647. // just the standard features.
  648. dsbi.cbStruct = SIZEOF(dsbi);
  649. dsbi.hwndOwner = hwndParent;
  650. dsbi.pszRoot = szRoot;
  651. dsbi.pszPath = szPath;
  652. dsbi.cchPath = ARRAYSIZE(szPath);
  653. dsbi.dwFlags = (DSBI_RETURNOBJECTCLASS|DSBI_EXPANDONOPEN|DSBI_ENTIREDIRECTORY) & ~DSBI_NOROOT;
  654. dsbi.pfnCallback = s_BrowseForScopeCB;
  655. dsbi.lParam = (LPARAM)&bfs;
  656. dsbi.pszObjectClass = szObjectClass;
  657. dsbi.cchObjectClass = ARRAYSIZE(szObjectClass);
  658. if ( _dwFlags & DSQPF_SHOWHIDDENOBJECTS )
  659. dsbi.dwFlags |= DSBI_INCLUDEHIDDEN;
  660. FormatMsgResource((LPTSTR*)&dsbi.pszTitle, GLOBAL_HINSTANCE, IDS_BROWSEPROMPT);
  661. StrCpyW(szRoot, c_szLDAP);
  662. if ( _pServer )
  663. {
  664. if ( lstrlenW(_pServer) > MAX_PATH )
  665. ExitGracefully(hres, E_INVALIDARG, "_pServer is too big");
  666. StrCatW(szRoot, L"//");
  667. StrCatW(szRoot, _pServer);
  668. }
  669. if ( pDsQueryScope )
  670. {
  671. StrCpyNW(szPath, OBJECT_NAME_FROM_SCOPE(pDsQueryScope), ARRAYSIZE(szPath));
  672. Trace(TEXT("pDsQueryScope: %s"), W2T(szPath));
  673. }
  674. // copy the credential information if needed
  675. if ( _dwFlags & DSQPF_HASCREDENTIALS )
  676. {
  677. TraceMsg("Setting credentails information");
  678. dsbi.pUserName = _pUserName;
  679. dsbi.pPassword = _pPassword;
  680. dsbi.dwFlags |= DSBI_HASCREDENTIALS;
  681. }
  682. iResult = DsBrowseForContainer(&dsbi);
  683. Trace(TEXT("DsBrowseForContainer returns %d"), iResult);
  684. // iResult == IDOK if something was selected (szPath),
  685. // if it is -VE if the call failed and we should error
  686. if ( iResult == IDOK )
  687. {
  688. LPWSTR pszScope = szPath;
  689. LPWSTR pszObjectClass = szObjectClass;
  690. LONG nElements = 0;
  691. Trace(TEXT("Path on exit from DsBrowseForContainer: %s"), W2T(szPath));
  692. // does this look like the GC? If so then default to it, as DsBrowseForContainer
  693. // will return us iffy looking information
  694. if ( SUCCEEDED(bfs.padp->Set(szPath, ADS_SETTYPE_FULL)) )
  695. {
  696. bfs.padp->GetNumElements(&nElements);
  697. Trace(TEXT("nElements on exit from GetNumElements %d"), nElements);
  698. }
  699. if ( !nElements )
  700. {
  701. TraceMsg("nElements = 0, so defaulting to GC");
  702. pszScope = bfs.szGcPath;
  703. pszObjectClass = GC_OBJECTCLASS;
  704. }
  705. Trace(TEXT("Scope selected is: %s, Object class: %s"), W2T(pszScope), W2T(pszObjectClass));
  706. hres = AllocScope(ppScope, 0, pszScope, pszObjectClass);
  707. FailGracefully(hres, "Failed converting the DS path to a scope");
  708. }
  709. else if ( iResult == IDCANCEL )
  710. {
  711. hres = S_FALSE; // nothing selected, returning S_FALSE;
  712. }
  713. else if ( iResult < 0 )
  714. {
  715. ExitGracefully(hres, E_FAIL, "DsBrowseForContainer failed");
  716. }
  717. exit_gracefully:
  718. LocalFreeString((LPTSTR*)&dsbi.pszTitle);
  719. Trace(TEXT("*ppScope == %08x"), *ppScope);
  720. DoRelease(bfs.padp);
  721. TraceLeaveResult(hres);
  722. }
  723. /*---------------------------------------------------------------------------*/
  724. //
  725. // WndProc for the banner window
  726. //
  727. LRESULT CALLBACK CDsQuery::s_BannerWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  728. {
  729. LRESULT lResult = 0;
  730. switch ( uMsg )
  731. {
  732. case WM_SIZE:
  733. InvalidateRect(hwnd, NULL, FALSE);
  734. break;
  735. case WM_ERASEBKGND:
  736. break;
  737. case WM_PAINT:
  738. {
  739. TCHAR szBuffer[MAX_PATH];
  740. HFONT hFont, hOldFont;
  741. SIZE szText;
  742. RECT rcClient;
  743. INT len;
  744. PAINTSTRUCT paint;
  745. COLORREF oldFgColor, oldBkColor;
  746. BeginPaint(hwnd, &paint);
  747. hFont = (HFONT)SendMessage(GetParent(hwnd), WM_GETFONT, 0, 0L);
  748. hOldFont = (HFONT)SelectObject(paint.hdc, hFont);
  749. if ( hOldFont )
  750. {
  751. oldFgColor = SetTextColor(paint.hdc, GetSysColor(COLOR_WINDOWTEXT));
  752. oldBkColor = SetBkColor(paint.hdc, ListView_GetBkColor(GetParent(hwnd)));
  753. len = GetWindowText(hwnd, szBuffer, ARRAYSIZE(szBuffer));
  754. GetTextExtentPoint32(paint.hdc, szBuffer, len, &szText);
  755. GetClientRect(GetParent(hwnd), &rcClient);
  756. ExtTextOut(paint.hdc,
  757. (rcClient.right - szText.cx) / 2,
  758. GetSystemMetrics(SM_CYBORDER)*4,
  759. ETO_CLIPPED|ETO_OPAQUE, &rcClient,
  760. szBuffer, len,
  761. NULL);
  762. SetTextColor(paint.hdc, oldFgColor);
  763. SetBkColor(paint.hdc, oldBkColor);
  764. SelectObject(paint.hdc, hOldFont);
  765. }
  766. EndPaint(hwnd, &paint);
  767. break;
  768. }
  769. case WM_SETTEXT:
  770. {
  771. InvalidateRect(hwnd, NULL, FALSE);
  772. //break; // deliberate drop through..
  773. }
  774. default:
  775. lResult = DefWindowProc(hwnd, uMsg, wParam, lParam);
  776. break;
  777. }
  778. return lResult;
  779. }
  780. //
  781. // WndProc for the bg window (lives behind list view, used by rest of the world)
  782. //
  783. LRESULT CALLBACK CDsQuery::s_ResultViewWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  784. {
  785. LRESULT lResult = 0;
  786. CDsQuery* pDsQuery = NULL;
  787. if ( uMsg == WM_CREATE )
  788. {
  789. pDsQuery = (CDsQuery*)((LPCREATESTRUCT)lParam)->lpCreateParams;
  790. SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pDsQuery);
  791. }
  792. else
  793. {
  794. pDsQuery = (CDsQuery*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
  795. switch ( uMsg )
  796. {
  797. case WM_SIZE:
  798. pDsQuery->OnSize(LOWORD(lParam), HIWORD(lParam));
  799. return(0);
  800. case WM_DESTROY:
  801. pDsQuery->_hwndView = NULL; // view is gone!
  802. break;
  803. case WM_NOTIFY:
  804. return(pDsQuery->OnNotify(hwnd, wParam, lParam));
  805. case WM_SETFOCUS:
  806. SetFocus(pDsQuery->_hwndView);
  807. break;
  808. case WM_GETDLGCODE:
  809. return ((LRESULT)(DLGC_WANTARROWS | DLGC_WANTCHARS));
  810. case WM_CONTEXTMENU:
  811. pDsQuery->OnContextMenu(NULL, lParam);
  812. return TRUE;
  813. case DSQVM_ADDRESULTS:
  814. return SUCCEEDED(pDsQuery->OnAddResults((DWORD)wParam, (HDPA)lParam));
  815. case DSQVM_FINISHED:
  816. if ( (DWORD)wParam == pDsQuery->_dwQueryReference )
  817. {
  818. // the references match so lets finish the query, and display
  819. // the "too many results" prompt if the user did a really
  820. // big query and we chopped them off
  821. pDsQuery->StopQuery();
  822. if ( lParam ) // == 0 then we are OK!
  823. {
  824. HWND hwndFrame;
  825. pDsQuery->_pqf->GetWindow(&hwndFrame);
  826. FormatMsgBox(GetParent(hwndFrame),
  827. GLOBAL_HINSTANCE, IDS_WINDOWTITLE, IDS_ERR_MAXRESULT,
  828. MB_OK|MB_ICONERROR);
  829. }
  830. }
  831. SetFocus(pDsQuery->_hwndView);
  832. return(1);
  833. }
  834. }
  835. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  836. }
  837. STDMETHODIMP CDsQuery::CreateResultView(HWND hwndParent, HWND* phWndView)
  838. {
  839. HRESULT hres;
  840. WNDCLASS wc;
  841. HWND hwndFilter, hwndFilterOld;
  842. HIMAGELIST himlSmall, himlLarge;
  843. DWORD dwLVStyle = LVS_AUTOARRANGE|LVS_SHAREIMAGELISTS|LVS_SHOWSELALWAYS|LVS_REPORT;
  844. RECT rc;
  845. TraceEnter(TRACE_HANDLER, "CDsQuery::CreateResultView");
  846. if ( IsWindow(_hwnd) )
  847. ExitGracefully(hres, E_FAIL, "Can only create one view at a time");
  848. // Create our result viewer, this is the parent window to the ListView
  849. // that we attach when we issue the query.
  850. ZeroMemory(&wc, SIZEOF(wc));
  851. wc.lpfnWndProc = s_ResultViewWndProc;
  852. wc.hInstance = GLOBAL_HINSTANCE;
  853. wc.lpszClassName = VIEW_CLASS;
  854. RegisterClass(&wc);
  855. _hwnd = CreateWindow(VIEW_CLASS,
  856. NULL,
  857. WS_TABSTOP|WS_CLIPCHILDREN|WS_CHILD|WS_VISIBLE,
  858. 0, 0, 0, 0,
  859. hwndParent,
  860. NULL,
  861. GLOBAL_HINSTANCE,
  862. this);
  863. if ( !_hwnd )
  864. ExitGracefully(hres, E_FAIL, "Failed to create view parent window");
  865. // Now register the window classes we are using.
  866. ZeroMemory(&wc, SIZEOF(wc));
  867. wc.lpfnWndProc = s_BannerWndProc;
  868. wc.hInstance = GLOBAL_HINSTANCE;
  869. wc.lpszClassName = BANNER_CLASS;
  870. RegisterClass(&wc);
  871. if ( _dwOQWFlags & OQWF_SINGLESELECT )
  872. dwLVStyle |= LVS_SINGLESEL;
  873. GetClientRect(_hwnd, &rc);
  874. _hwndView = CreateWindowEx(WS_EX_CLIENTEDGE,
  875. WC_LISTVIEW,
  876. NULL,
  877. WS_TABSTOP|WS_CLIPCHILDREN|WS_CHILD|WS_VISIBLE|dwLVStyle,
  878. 0, 0,
  879. rc.right, rc.bottom,
  880. _hwnd,
  881. (HMENU)IDC_RESULTS,
  882. GLOBAL_HINSTANCE,
  883. NULL);
  884. if ( !_hwndView )
  885. ExitGracefully(hres, E_FAIL, "Failed to create the view window");
  886. ListView_SetExtendedListViewStyle(_hwndView, LVS_EX_FULLROWSELECT|LVS_EX_LABELTIP);
  887. Shell_GetImageLists(&himlLarge, &himlSmall);
  888. ListView_SetImageList(_hwndView, himlLarge, LVSIL_NORMAL);
  889. ListView_SetImageList(_hwndView, himlSmall, LVSIL_SMALL);
  890. // Create the banner window, this is a child of the ListView, it is used to display
  891. // information about the query being issued
  892. _hwndBanner = CreateWindow(BANNER_CLASS, NULL,
  893. WS_CHILD,
  894. 0, 0, 0, 0, // nb: size fixed later
  895. _hwndView,
  896. (HMENU)IDC_STATUS,
  897. GLOBAL_HINSTANCE,
  898. NULL);
  899. if ( !_hwndBanner )
  900. ExitGracefully(hres, E_FAIL, "Failed to create the static banner window");
  901. _SetFilter(_fFilter);
  902. _SetViewMode(_idViewMode);
  903. _ShowBanner(SWP_SHOWWINDOW, IDS_INITALIZING);
  904. hres = S_OK; // success
  905. exit_gracefully:
  906. if ( SUCCEEDED(hres) )
  907. *phWndView = _hwnd;
  908. TraceLeaveResult(hres);
  909. }
  910. /*---------------------------------------------------------------------------*/
  911. #define MGW_EDIT 2
  912. STDMETHODIMP CDsQuery::ActivateView(UINT uState, WPARAM wParam, LPARAM lParam)
  913. {
  914. HRESULT hres;
  915. HWND hwnd;
  916. INT i;
  917. TraceEnter(TRACE_HANDLER, "CDsQuery::ActivateView");
  918. switch ( uState )
  919. {
  920. case CQRVA_ACTIVATE:
  921. {
  922. HMENU hMenu;
  923. OLEMENUGROUPWIDTHS omgw = { 0, 0, 0, 0, 0, 0 };
  924. // Allow the cframe to merge its menus into our menu bar before we
  925. // add ours to it.
  926. if ( !(hMenu = CreateMenu()) )
  927. ExitGracefully(hres, E_FAIL, "Failed to create a base menu bar to be used");
  928. hres = _pqf->InsertMenus(hMenu, &omgw);
  929. FailGracefully(hres, "Failed when calling CQueryFrame::InsertMenus");
  930. Shell_MergeMenus(GetSubMenu(hMenu, 0), GetSubMenu(_hFileMenu, 0), 0x0, 0x0, 0x7fff, 0);
  931. MergeMenu(hMenu, _hEditMenu, omgw.width[0]);
  932. MergeMenu(hMenu, _hViewMenu, omgw.width[0]+1);
  933. MergeMenu(hMenu, _hHelpMenu, omgw.width[0]+MGW_EDIT+omgw.width[2]+omgw.width[4]);
  934. if ( _dwOQWFlags & OQWF_SINGLESELECT )
  935. {
  936. ENABLE_MENU_ITEM(hMenu, DSQH_EDIT_SELECTALL, FALSE);
  937. ENABLE_MENU_ITEM(hMenu, DSQH_EDIT_INVERTSELECTION, FALSE);
  938. }
  939. hres = _pqf->SetMenu(hMenu, NULL); // set the frames menu bar
  940. FailGracefully(hres, "Failed when calling CQueryFrame::SetMenu");
  941. break;
  942. }
  943. case CQRVA_INITMENUBAR:
  944. {
  945. // we recieve a CQRVA_INITMENUBAR before the popup so that we can store the
  946. // menu bar information, and invalidate an interface pointers we maybe holding
  947. // onto.
  948. Trace(TEXT("Received an CQRVA_INITMENUBAR, hMenu %08x"), wParam);
  949. _hFrameMenuBar = (HMENU)wParam;
  950. DoRelease(_pcm);
  951. break;
  952. }
  953. case CQRVA_INITMENUBARPOPUP:
  954. {
  955. HMENU hFileMenu;
  956. BOOL fDeleteItems = FALSE;
  957. TraceMsg("Received an CQRVA_INITMENUBARPOPUP");
  958. hFileMenu = GetSubMenu(_hFrameMenuBar, 0);
  959. // if we have a view then lets try and collect the selection from it,
  960. // having done that we can merge the verbs for that selection into the
  961. // views "File" menu.
  962. if ( (hFileMenu == (HMENU)wParam) && !_pcm )
  963. {
  964. _fNoSelection = TRUE; // no selection currenlty
  965. if ( IsWindow(_hwndView) )
  966. {
  967. for ( i = GetMenuItemCount(hFileMenu) - 1; i >= 0 ; i-- )
  968. {
  969. if ( !fDeleteItems && (GetMenuItemID(hFileMenu, i) == DSQH_FILE_PROPERTIES) )
  970. {
  971. Trace(TEXT("Setting fDeleteItems true on index %d"), i);
  972. fDeleteItems = TRUE;
  973. }
  974. else
  975. {
  976. if ( fDeleteItems )
  977. DeleteMenu(hFileMenu, i, MF_BYPOSITION);
  978. }
  979. }
  980. // Collect the selection, and using that construct an IContextMenu interface, if that works
  981. // then we can merge in the verbs that relate to this object.
  982. hres = _GetContextMenu();
  983. FailGracefully(hres, "Failed when calling _GetAndViewObject");
  984. if ( ListView_GetSelectedCount(_hwndView) > 0 )
  985. {
  986. _GetContextMenuVerbs(hFileMenu, CMF_VERBSONLY);
  987. _fNoSelection = FALSE;
  988. }
  989. }
  990. ENABLE_MENU_ITEM(hFileMenu, DSQH_FILE_PROPERTIES, !_fNoSelection);
  991. }
  992. // 211991 11/6/00 JonN and DavidDv
  993. // choose columns is disabled if there are no columns to show/the user has marked for disabled
  994. ENABLE_MENU_ITEM(_hFrameMenuBar, DSQH_VIEW_PICKCOLUMNS,
  995. IsWindow(_hwndView) && _hdsaColumns && (!(_dwFlags & DSQPF_NOCHOOSECOLUMNS)));
  996. ENABLE_MENU_ITEM(_hFrameMenuBar, DSQH_VIEW_REFRESH, IsWindow(_hwndView) && _dwThreadId);
  997. _InitViewMenuItems(_hFrameMenuBar);
  998. break;
  999. }
  1000. case CQRVA_FORMCHANGED:
  1001. {
  1002. // we receieve a form change, we store the form name as we will use it
  1003. // as the default name for saved queries authored by the user.
  1004. Trace(TEXT("Form '%s' selected"), (LPTSTR)lParam);
  1005. LocalFreeString(&_pDefaultSaveName);
  1006. hres = LocalAllocString(&_pDefaultSaveName, (LPCTSTR)lParam);
  1007. FailGracefully(hres, "Failed to set the default save name");
  1008. break;
  1009. }
  1010. case CQRVA_STARTQUERY:
  1011. {
  1012. Trace(TEXT("Query is: %s"), wParam ? TEXT("starting"):TEXT("stopping"));
  1013. break;
  1014. }
  1015. case CQRVA_HELP:
  1016. {
  1017. LPHELPINFO pHelpInfo = (LPHELPINFO)lParam;
  1018. TraceAssert(pHelpInfo)
  1019. TraceMsg("Invoking help on the objects in the windows");
  1020. WinHelp((HWND)pHelpInfo->hItemHandle, DSQUERY_HELPFILE, HELP_WM_HELP, (DWORD_PTR)aHelpIDs);
  1021. break;
  1022. }
  1023. case CQRVA_CONTEXTMENU:
  1024. {
  1025. HWND hwndForHelp = (HWND)wParam;
  1026. Trace(TEXT("CQRVA_CONTEXTMENU recieved on the bg of the frame %d"), GetDlgCtrlID(hwndForHelp));
  1027. WinHelp(hwndForHelp, DSQUERY_HELPFILE, HELP_CONTEXTMENU, (DWORD_PTR)aHelpIDs);
  1028. break;
  1029. }
  1030. }
  1031. hres = S_OK;
  1032. exit_gracefully:
  1033. TraceLeaveResult(hres);
  1034. }
  1035. /*---------------------------------------------------------------------------*/
  1036. STDMETHODIMP CDsQuery::InvokeCommand(HWND hwndParent, UINT uID)
  1037. {
  1038. HRESULT hres = S_OK;
  1039. HWND hwndFrame;
  1040. DECLAREWAITCURSOR;
  1041. TraceEnter(TRACE_HANDLER, "CDsQuery::InvokeCommand");
  1042. Trace(TEXT("hwndParent %08x, uID %d"), hwndParent, uID);
  1043. SetWaitCursor();
  1044. switch ( uID )
  1045. {
  1046. case DSQH_BG_SELECT:
  1047. SendMessage(hwndParent, WM_COMMAND, IDOK, 0);
  1048. break;
  1049. case DSQH_FILE_PROPERTIES:
  1050. hres = OnFileProperties();
  1051. break;
  1052. case DSQH_FILE_SAVEQUERY:
  1053. hres = OnFileSaveQuery();
  1054. break;
  1055. case DSQH_EDIT_SELECTALL:
  1056. hres = OnEditSelectAll();
  1057. break;
  1058. case DSQH_EDIT_INVERTSELECTION:
  1059. hres = OnEditInvertSelection();
  1060. break;
  1061. case DSQH_VIEW_FILTER:
  1062. _SetFilter(!_fFilter);
  1063. break;
  1064. case DSQH_VIEW_LARGEICONS:
  1065. case DSQH_VIEW_SMALLICONS:
  1066. case DSQH_VIEW_LIST:
  1067. case DSQH_VIEW_DETAILS:
  1068. _SetViewMode(uID);
  1069. break;
  1070. case DSQH_VIEW_REFRESH:
  1071. {
  1072. if ( IsWindow(_hwndView) && _dwThreadId )
  1073. {
  1074. _InitNewQuery(NULL, FALSE);
  1075. PostThreadMessage(_dwThreadId, RVTM_REFRESH, _dwQueryReference, 0L);
  1076. }
  1077. break;
  1078. }
  1079. case DSQH_VIEW_PICKCOLUMNS:
  1080. {
  1081. TraceAssert(_hdsaColumns);
  1082. OnPickColumns(hwndParent);
  1083. break;
  1084. }
  1085. case DSQH_HELP_CONTENTS:
  1086. {
  1087. TraceMsg("Calling for to display help topics");
  1088. _pqf->GetWindow(&hwndFrame);
  1089. _pqf->CallForm(NULL, DSQPM_HELPTOPICS, 0, (LPARAM)hwndFrame);
  1090. break;
  1091. }
  1092. case DSQH_HELP_WHATISTHIS:
  1093. _pqf->GetWindow(&hwndFrame);
  1094. SendMessage(hwndFrame, WM_SYSCOMMAND, SC_CONTEXTHELP, MAKELPARAM(0,0));
  1095. break;
  1096. default:
  1097. {
  1098. // if it looks like a sort request then lets handle it, otherwise attempt
  1099. // to send to the context menu handler we may have at htis poiunt.
  1100. if ( (uID >= DSQH_VIEW_ARRANGEFIRST) && (uID < DSQH_VIEW_ARRANGELAST) )
  1101. {
  1102. TraceAssert(_hdsaColumns);
  1103. if ( _hdsaColumns )
  1104. {
  1105. Trace(TEXT("Calling _SortResults for column %d"), uID - DSQH_VIEW_ARRANGEFIRST);
  1106. _SortResults(uID - DSQH_VIEW_ARRANGEFIRST);
  1107. }
  1108. }
  1109. else if ( _pcm )
  1110. {
  1111. CMINVOKECOMMANDINFO ici;
  1112. ici.cbSize = SIZEOF(ici);
  1113. ici.fMask = 0;
  1114. _pqf->GetWindow(&ici.hwnd);
  1115. ici.lpVerb = (LPCSTR)IntToPtr(uID - DSQH_FILE_CONTEXT_FIRST);
  1116. ici.lpParameters = NULL;
  1117. ici.lpDirectory = NULL;
  1118. ici.nShow = SW_NORMAL;
  1119. ici.dwHotKey = 0;
  1120. ici.hIcon = NULL;
  1121. hres = _pcm->InvokeCommand(&ici);
  1122. FailGracefully(hres, "Failed when calling IContextMenu::InvokeCommand");
  1123. DoRelease(_pcm); // no longer needed
  1124. }
  1125. break;
  1126. }
  1127. }
  1128. exit_gracefully:
  1129. ResetWaitCursor();
  1130. TraceLeaveResult(hres);
  1131. }
  1132. /*---------------------------------------------------------------------------*/
  1133. STDMETHODIMP CDsQuery::GetCommandString(UINT uID, DWORD dwFlags, LPTSTR pBuffer, INT cchBuffer)
  1134. {
  1135. HRESULT hres;
  1136. TCHAR szBuffer[MAX_PATH];
  1137. TraceEnter(TRACE_HANDLER, "CDsQuery::GetCommandString");
  1138. Trace(TEXT("uID %08x, dwFlags %08x, pBuffer %08x, cchBuffer %d"), uID, dwFlags, pBuffer, cchBuffer);
  1139. if ( (uID >= DSQH_FILE_CONTEXT_FIRST) && (uID < DSQH_FILE_CONTEXT_LAST) )
  1140. {
  1141. if ( _pcm )
  1142. {
  1143. TraceMsg("Trying the IContextMenu::GetCommandString");
  1144. hres = _pcm->GetCommandString((uID - DSQH_FILE_CONTEXT_FIRST), GCS_HELPTEXT, NULL, (LPSTR)pBuffer, cchBuffer);
  1145. #if UNICODE
  1146. // we build UNICODE, therefore if we failed then try and pick up the ANSI string from
  1147. // the handler, if that works then we convert the multi-byte string to UNICODE
  1148. // and hand that back to the caller.
  1149. if ( FAILED(hres) )
  1150. {
  1151. CHAR szBuffer[MAX_PATH];
  1152. hres = _pcm->GetCommandString((uID - DSQH_FILE_CONTEXT_FIRST), GCS_HELPTEXTA, NULL, szBuffer, ARRAYSIZE(szBuffer));
  1153. if ( SUCCEEDED(hres) )
  1154. {
  1155. TraceMsg("Handler provided an ANSI string");
  1156. MultiByteToWideChar(CP_ACP, 0, szBuffer, -1, pBuffer, cchBuffer);
  1157. }
  1158. }
  1159. #endif
  1160. FailGracefully(hres, "Failed when asking for help text from IContextMenu iface");
  1161. }
  1162. }
  1163. else
  1164. {
  1165. if ( (uID >= DSQH_VIEW_ARRANGEFIRST) && (uID < DSQH_VIEW_ARRANGELAST) )
  1166. {
  1167. INT iColumn = uID-DSQH_VIEW_ARRANGEFIRST;
  1168. TCHAR szFmt[MAX_PATH];
  1169. Trace(TEXT("Get command text for column %d"), iColumn);
  1170. if ( _hdsaColumns && (iColumn < DSA_GetItemCount(_hdsaColumns)) )
  1171. {
  1172. LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(_hdsaColumns, iColumn);
  1173. TraceAssert(pColumn);
  1174. LoadString(GLOBAL_HINSTANCE, IDS_ARRANGEBY_HELP, szFmt, ARRAYSIZE(szFmt));
  1175. wsprintf(pBuffer, szFmt, pColumn->pHeading);
  1176. Trace(TEXT("Resulting string is: %s"), pBuffer);
  1177. }
  1178. }
  1179. else
  1180. {
  1181. if ( !LoadString(GLOBAL_HINSTANCE, uID, pBuffer, cchBuffer) )
  1182. ExitGracefully(hres, E_FAIL, "Failed to load the command text for this verb");
  1183. }
  1184. }
  1185. hres = S_OK;
  1186. exit_gracefully:
  1187. TraceLeaveResult(hres);
  1188. }
  1189. /*---------------------------------------------------------------------------*/
  1190. STDMETHODIMP CDsQuery::IssueQuery(LPCQPARAMS pQueryParams)
  1191. {
  1192. HRESULT hres;
  1193. LPTHREADINITDATA ptid = NULL;
  1194. LPDSQUERYSCOPE pDsQueryScope = (LPDSQUERYSCOPE)pQueryParams->pQueryScope;
  1195. LPDSQUERYPARAMS pDsQueryParams = (LPDSQUERYPARAMS)pQueryParams->pQueryParameters;
  1196. LPTSTR pBuffer = NULL;
  1197. MSG msg;
  1198. TraceEnter(TRACE_HANDLER, "CDsQuery::IssueQuery");
  1199. Trace(TEXT("pQueryParams %08x, pDsQueryScope %08x, pDsQueryParams %08x"), pQueryParams, pDsQueryScope, pDsQueryParams);
  1200. // Persist the existing column information if there was some, then
  1201. // get the new column table initialized and the columns added to the
  1202. // view
  1203. if ( _hdsaColumns )
  1204. {
  1205. if ( _fColumnsModified )
  1206. {
  1207. _SaveColumnTable(_clsidForm, _hdsaColumns);
  1208. _fColumnsModified = FALSE;
  1209. }
  1210. _SaveColumnTable();
  1211. }
  1212. // Initialize the view with items
  1213. _clsidForm = pQueryParams->clsidForm; // keep the form ID (for persistance)
  1214. hres = _InitNewQuery(pDsQueryParams, TRUE);
  1215. FailGracefully(hres, "Failed to initialize the new query");
  1216. // Now build the thread information needed to get the thread
  1217. // up and running.
  1218. ptid = (LPTHREADINITDATA)LocalAlloc(LPTR, SIZEOF(THREADINITDATA));
  1219. TraceAssert(ptid);
  1220. if ( !ptid )
  1221. ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate THREADINITDATA");
  1222. ptid->dwReference = _dwQueryReference;
  1223. //ptid->hwndView = NULL;
  1224. //ptid->pQuery = NULL;
  1225. //ptid->pScope = NULL;
  1226. //ptid->hdsaColumns = NULL;
  1227. //ptid->fShowHidden = FALSE;
  1228. //ptid->pServer = NULL;
  1229. //ptid->pUserName = NULL;
  1230. //ptid->pPassword = NULL;
  1231. Trace(TEXT("_dwFlags %08x (& DSQPF_SHOWHIDDENOBJECTS)"), _dwFlags, _dwFlags & DSQPF_SHOWHIDDENOBJECTS);
  1232. ptid->fShowHidden = (_dwFlags & DSQPF_SHOWHIDDENOBJECTS) ? 1:0;
  1233. ptid->hwndView = _hwndView;
  1234. hres = _GetColumnTable(_clsidForm, pDsQueryParams, &ptid->hdsaColumns, FALSE);
  1235. FailGracefully(hres, "Failed to create column DSA");
  1236. hres = LocalAllocStringW(&ptid->pQuery, (LPWSTR)ByteOffset(pDsQueryParams, pDsQueryParams->offsetQuery));
  1237. FailGracefully(hres, "Failed to copy query filter string");
  1238. hres = LocalAllocStringW(&ptid->pScope, OBJECT_NAME_FROM_SCOPE(pDsQueryScope));
  1239. FailGracefully(hres, "Failed to copy scope to thread init data");
  1240. hres = _CopyCredentials(&ptid->pUserName, &ptid->pPassword, &ptid->pServer);
  1241. FailGracefully(hres, "Failed to copy credentails");
  1242. // now create the thread that is going to perform the query, this includes
  1243. // telling the previous one that it needs to close down
  1244. if ( _hThread && _dwThreadId )
  1245. {
  1246. Trace(TEXT("Killing old query thread %08x, ID %d"), _hThread, _dwThreadId);
  1247. PostThreadMessage(_dwThreadId, RVTM_STOPQUERY, 0, 0);
  1248. PostThreadMessage(_dwThreadId, WM_QUIT, 0, 0);
  1249. CloseHandle(_hThread);
  1250. _hThread = NULL;
  1251. _dwThreadId = 0;
  1252. }
  1253. DllAddRef();
  1254. _hThread = CreateThread(NULL, 0, QueryThread, ptid, 0, &_dwThreadId);
  1255. TraceAssert(_hThread);
  1256. if ( !_hThread )
  1257. {
  1258. DllRelease();
  1259. ExitGracefully(hres, E_FAIL, "Failed to create background thread - BAD!");
  1260. }
  1261. hres = S_OK; // success
  1262. exit_gracefully:
  1263. if ( SUCCEEDED(hres) && IsWindow(_hwndView) )
  1264. SetFocus(_hwndView);
  1265. if ( FAILED(hres) )
  1266. {
  1267. QueryThread_FreeThreadInitData(&ptid);
  1268. _pqf->StartQuery(FALSE);
  1269. }
  1270. TraceLeaveResult(hres);
  1271. }
  1272. /*---------------------------------------------------------------------------*/
  1273. STDMETHODIMP CDsQuery::StopQuery()
  1274. {
  1275. HRESULT hres;
  1276. INT cResults = _hdpaResults ? DPA_GetPtrCount(_hdpaResults):0;
  1277. LPTSTR pBuffer;
  1278. TraceEnter(TRACE_HANDLER, "CDsQuery::StopQuery");
  1279. if ( !IsWindow(_hwndView) )
  1280. ExitGracefully(hres, E_FAIL, "View not initalized yet");
  1281. // we are stopping the query, we are going to tidy up the UI now
  1282. // and we just want the thread to closedown cleanly, therefore lets
  1283. // do so, increasing our query reference
  1284. _pqf->StartQuery(FALSE);
  1285. _dwQueryReference++;
  1286. _PopulateView(-1, -1); // update status bar etc
  1287. if ( _dwThreadId )
  1288. PostThreadMessage(_dwThreadId, RVTM_STOPQUERY, 0, 0);
  1289. hres = S_OK; // success
  1290. exit_gracefully:
  1291. TraceLeaveResult(hres);
  1292. }
  1293. /*---------------------------------------------------------------------------*/
  1294. HRESULT CDsQuery::_SetDataObjectData(IDataObject* pDataObject, UINT cf, LPVOID pData, DWORD cbSize)
  1295. {
  1296. FORMATETC fmte = {(CLIPFORMAT)cf, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  1297. STGMEDIUM medium = { TYMED_NULL, NULL, NULL };
  1298. LPVOID pAlloc;
  1299. HRESULT hr = AllocStorageMedium(&fmte, &medium, cbSize, &pAlloc);
  1300. if (SUCCEEDED(hr))
  1301. {
  1302. CopyMemory(pAlloc, pData, cbSize);
  1303. hr = pDataObject->SetData(&fmte, &medium, TRUE);
  1304. ReleaseStgMedium(&medium); // were done, so release the storage medium
  1305. }
  1306. return hr;
  1307. }
  1308. HRESULT CDsQuery::_SetDispSpecOptions(IDataObject *pdo)
  1309. {
  1310. CLIPFORMAT cfDsDispSpecOptions = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DSDISPLAYSPECOPTIONS);
  1311. FORMATETC fmte = {cfDsDispSpecOptions, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  1312. STGMEDIUM medium = { TYMED_NULL, NULL, NULL };
  1313. DSDISPLAYSPECOPTIONS *pddso;
  1314. LPWSTR pAttribPrefix = DS_PROP_SHELL_PREFIX;
  1315. // are we in admin mode? if so then lets fix the property prefix for reading things
  1316. // from the display specifiers
  1317. if (_dwFlags & DSQPF_ENABLEADMINFEATURES)
  1318. pAttribPrefix = DS_PROP_ADMIN_PREFIX;
  1319. // include all the string data into the allocation size (these are stored after the
  1320. // structure)
  1321. DWORD cbStruct = SIZEOF(DSDISPLAYSPECOPTIONS);
  1322. cbStruct += StringByteSizeW(pAttribPrefix);
  1323. cbStruct += StringByteSizeW(_pUserName);
  1324. cbStruct += StringByteSizeW(_pPassword);
  1325. cbStruct += StringByteSizeW(_pServer);
  1326. // allocate and fill...
  1327. HRESULT hr = AllocStorageMedium(&fmte, &medium, cbStruct, (void **)&pddso);
  1328. if (SUCCEEDED(hr))
  1329. {
  1330. DWORD offsetStrings = SIZEOF(DSDISPLAYSPECOPTIONS);
  1331. pddso->dwSize = SIZEOF(DSDISPLAYSPECOPTIONS);
  1332. pddso->dwFlags = DSDSOF_HASUSERANDSERVERINFO|DSDSOF_DSAVAILABLE;
  1333. //pddso->offsetAttribPrefix = 0x0;
  1334. //pddso->offsetUserName = 0x0;
  1335. //pddso->offsetPassword = 0x0;
  1336. //pddso->offsetServer = 0x0;
  1337. //pddso->offsetServerConfigPath = 0x0;
  1338. pddso->offsetAttribPrefix = offsetStrings;
  1339. StringByteCopyW(pddso, offsetStrings, pAttribPrefix);
  1340. offsetStrings += StringByteSizeW(pAttribPrefix);
  1341. if ( _pUserName )
  1342. {
  1343. pddso->offsetUserName = offsetStrings;
  1344. StringByteCopyW(pddso, offsetStrings, _pUserName);
  1345. offsetStrings += StringByteSizeW(_pUserName);
  1346. }
  1347. if ( _pPassword )
  1348. {
  1349. pddso->offsetPassword = offsetStrings;
  1350. StringByteCopyW(pddso, offsetStrings, _pPassword);
  1351. offsetStrings += StringByteSizeW(_pPassword);
  1352. }
  1353. if ( _pServer )
  1354. {
  1355. pddso->offsetServer = offsetStrings;
  1356. StringByteCopyW(pddso, offsetStrings, _pServer);
  1357. offsetStrings += StringByteSizeW(_pServer);
  1358. }
  1359. // lets set into the IDataObject
  1360. hr = pdo->SetData(&fmte, &medium, TRUE);
  1361. ReleaseStgMedium(&medium);
  1362. }
  1363. return hr;
  1364. }
  1365. STDMETHODIMP CDsQuery::GetViewObject(UINT uScope, REFIID riid, void **ppvOut)
  1366. {
  1367. HRESULT hres;
  1368. IDataObject* pDataObject = NULL;
  1369. LPDSQUERYPARAMS pDsQueryParams = NULL;
  1370. LPDSQUERYSCOPE pDsQueryScope = NULL;
  1371. UINT cfDsQueryParams = RegisterClipboardFormat(CFSTR_DSQUERYPARAMS);
  1372. UINT cfDsQueryScope = RegisterClipboardFormat(CFSTR_DSQUERYSCOPE);
  1373. BOOL fJustSelection = !(_dwFlags & DSQPF_RETURNALLRESULTS);
  1374. TraceEnter(TRACE_HANDLER, "CDsQuery::GetViewObject");
  1375. // We only support returning the selection as an IDataObject
  1376. DECLAREWAITCURSOR;
  1377. SetWaitCursor();
  1378. if ( !ppvOut && ((uScope & CQRVS_MASK) != CQRVS_SELECTION) )
  1379. ExitGracefully(hres, E_INVALIDARG, "Bad arguments to GetViewObject");
  1380. if ( !IsEqualIID(riid, IID_IDataObject) )
  1381. ExitGracefully(hres, E_NOINTERFACE, "Object IID supported");
  1382. //
  1383. // write the extra data we have into the IDataObject:
  1384. //
  1385. // - query parameters (filter)
  1386. // - scope
  1387. // - attribute prefix information
  1388. //
  1389. hres = _GetDataObjectFromSelection(fJustSelection, &pDataObject);
  1390. FailGracefully(hres, "Failed to get the IDataObject from the namespace");
  1391. if ( SUCCEEDED(_pqf->CallForm(NULL, CQPM_GETPARAMETERS, 0, (LPARAM)&pDsQueryParams)) )
  1392. {
  1393. if ( pDsQueryParams )
  1394. {
  1395. hres = _SetDataObjectData(pDataObject, cfDsQueryParams, pDsQueryParams, pDsQueryParams->cbStruct);
  1396. FailGracefully(hres, "Failed set the DSQUERYPARAMS into the data object");
  1397. }
  1398. }
  1399. if ( SUCCEEDED(_pqf->GetScope((LPCQSCOPE*)&pDsQueryScope)) )
  1400. {
  1401. if ( pDsQueryScope )
  1402. {
  1403. LPWSTR pScope = OBJECT_NAME_FROM_SCOPE(pDsQueryScope);
  1404. TraceAssert(pScope);
  1405. hres = _SetDataObjectData(pDataObject, cfDsQueryScope, pScope, StringByteSizeW(pScope));
  1406. FailGracefully(hres, "Failed set the DSQUERYSCOPE into the data object");
  1407. }
  1408. }
  1409. // success, so lets pass out the IDataObject.
  1410. pDataObject->AddRef();
  1411. *ppvOut = (LPVOID)pDataObject;
  1412. hres = S_OK;
  1413. exit_gracefully:
  1414. DoRelease(pDataObject);
  1415. if ( pDsQueryParams )
  1416. CoTaskMemFree(pDsQueryParams);
  1417. if ( pDsQueryScope )
  1418. CoTaskMemFree(pDsQueryScope);
  1419. ResetWaitCursor();
  1420. TraceLeaveResult(hres);
  1421. }
  1422. /*---------------------------------------------------------------------------*/
  1423. STDMETHODIMP CDsQuery::LoadQuery(IPersistQuery* pPersistQuery)
  1424. {
  1425. HRESULT hres;
  1426. WCHAR szBuffer[MAX_PATH];
  1427. IADs *pDsObject = NULL;
  1428. BSTR bstrObjectClass = NULL;
  1429. INT iFilter;
  1430. LPCQSCOPE pScope = NULL;
  1431. INT cbScope;
  1432. USES_CONVERSION;
  1433. TraceEnter(TRACE_HANDLER, "CDsQuery::LoadQuery");
  1434. if ( !pPersistQuery )
  1435. ExitGracefully(hres, E_INVALIDARG, "No IPersistQuery object");
  1436. if ( SUCCEEDED(pPersistQuery->ReadInt(c_szDsQuery, c_szScopeSize, &cbScope)) &&
  1437. (cbScope < SIZEOF(szBuffer)) &&
  1438. SUCCEEDED(pPersistQuery->ReadStruct(c_szDsQuery, c_szScope, szBuffer, cbScope)) )
  1439. {
  1440. Trace(TEXT("Selected scope from file is %s"), W2T(szBuffer));
  1441. // get the object class from the file - this should be written to the file
  1442. hres = ADsOpenObject(szBuffer, _pUserName, _pPassword, ADS_SECURE_AUTHENTICATION, IID_IADs, (void **)&pDsObject);
  1443. FailGracefully(hres, "Failed to bind to the specified object");
  1444. hres = pDsObject->get_Class(&bstrObjectClass);
  1445. FailGracefully(hres, "Failed to get the object class");
  1446. // allocate a new scope
  1447. if ( SUCCEEDED(AllocScope(&pScope, 0, szBuffer, bstrObjectClass)) )
  1448. {
  1449. hres = _pqf->AddScope(pScope, 0x0, TRUE);
  1450. FailGracefully(hres, "Failed to add scope to list");
  1451. }
  1452. }
  1453. // Read the remainder of the view state
  1454. if ( SUCCEEDED(pPersistQuery->ReadInt(c_szDsQuery, c_szViewMode, &_idViewMode)) )
  1455. {
  1456. Trace(TEXT("View mode is: %0x8"), _idViewMode);
  1457. _SetViewMode(_idViewMode);
  1458. }
  1459. if ( SUCCEEDED(pPersistQuery->ReadInt(c_szDsQuery, c_szEnableFilter, &iFilter)) )
  1460. {
  1461. Trace(TEXT("Filter mode set to %d"), _fFilter);
  1462. _SetFilter(iFilter);
  1463. }
  1464. hres = S_OK;
  1465. exit_gracefully:
  1466. if ( pScope )
  1467. CoTaskMemFree(pScope);
  1468. DoRelease(pDsObject);
  1469. SysFreeString(bstrObjectClass);
  1470. TraceLeaveResult(hres);
  1471. }
  1472. /*---------------------------------------------------------------------------*/
  1473. STDMETHODIMP CDsQuery::SaveQuery(IPersistQuery* pPersistQuery, LPCQSCOPE pScope)
  1474. {
  1475. HRESULT hres;
  1476. LPDSQUERYSCOPE pDsQueryScope = (LPDSQUERYSCOPE)pScope;
  1477. LPWSTR pScopePath = OBJECT_NAME_FROM_SCOPE(pDsQueryScope);
  1478. WCHAR szGcPath[MAX_PATH];
  1479. TraceEnter(TRACE_HANDLER, "CDsQuery::SaveQuery");
  1480. if ( !pPersistQuery || !pScope )
  1481. ExitGracefully(hres, E_INVALIDARG, "No IPersistQuery/pScope object");
  1482. if ( SUCCEEDED(GetGlobalCatalogPath(_pServer, szGcPath, ARRAYSIZE(szGcPath))) && StrCmpW(pScopePath, szGcPath) )
  1483. {
  1484. // if this is not the GC then persist
  1485. TraceMsg("GC path differs from scope, so persisting");
  1486. hres = pPersistQuery->WriteInt(c_szDsQuery, c_szScopeSize, StringByteSizeW(pScopePath));
  1487. FailGracefully(hres, "Failed to write the scope size");
  1488. hres = pPersistQuery->WriteStruct(c_szDsQuery, c_szScope, pScopePath, StringByteSizeW(pScopePath));
  1489. FailGracefully(hres, "Failed to write scope");
  1490. }
  1491. hres = pPersistQuery->WriteInt(c_szDsQuery, c_szViewMode, _idViewMode);
  1492. FailGracefully(hres, "Failed to write view mode");
  1493. hres = pPersistQuery->WriteInt(c_szDsQuery, c_szEnableFilter, _fFilter);
  1494. FailGracefully(hres, "Failed to write filter state");
  1495. hres = S_OK;
  1496. exit_gracefully:
  1497. TraceLeaveResult(hres);
  1498. }
  1499. /*----------------------------------------------------------------------------
  1500. / IObjectWithSite
  1501. /----------------------------------------------------------------------------*/
  1502. STDMETHODIMP CDsQuery::SetSite(IUnknown* punk)
  1503. {
  1504. HRESULT hres = S_OK;
  1505. TraceEnter(TRACE_HANDLER, "CDsQuery::SetSite");
  1506. DoRelease(_punkSite);
  1507. if ( punk )
  1508. {
  1509. TraceMsg("QIing for IUnknown from the site object");
  1510. hres = punk->QueryInterface(IID_IUnknown, (void **)&_punkSite);
  1511. FailGracefully(hres, "Failed to get IUnknown from the site object");
  1512. }
  1513. exit_gracefully:
  1514. TraceLeaveResult(hres);
  1515. }
  1516. STDMETHODIMP CDsQuery::GetSite(REFIID riid, void **ppv)
  1517. {
  1518. HRESULT hres;
  1519. TraceEnter(TRACE_HANDLER, "CDsQuery::GetSite");
  1520. if ( !_punkSite )
  1521. ExitGracefully(hres, E_NOINTERFACE, "No site to QI from");
  1522. hres = _punkSite->QueryInterface(riid, ppv);
  1523. FailGracefully(hres, "QI failed on the site unknown object");
  1524. exit_gracefully:
  1525. TraceLeaveResult(hres);
  1526. }
  1527. /*----------------------------------------------------------------------------
  1528. / IDsQueryHandler
  1529. /----------------------------------------------------------------------------*/
  1530. VOID CDsQuery::_DeleteViewItems(LPDSOBJECTNAMES pdon)
  1531. {
  1532. INT iResult;
  1533. DWORD iItem;
  1534. USES_CONVERSION;
  1535. TraceEnter(TRACE_HANDLER, "CDsQuery::_DeleteObjectNames");
  1536. if ( pdon->cItems )
  1537. {
  1538. // walk through all the items in the view deleting as required.
  1539. for ( iItem = 0 ; iItem != pdon->cItems ; iItem++ )
  1540. {
  1541. // do we have an item to delete?
  1542. if ( pdon->aObjects[iItem].offsetName )
  1543. {
  1544. LPCWSTR pwszName = (LPCWSTR)ByteOffset(pdon, pdon->aObjects[iItem].offsetName);
  1545. Trace(TEXT("pwszName to delete: %s"), W2CT(pwszName));
  1546. // walk all the results in the view deleting them as we go.
  1547. for ( iResult = 0 ; iResult < DPA_GetPtrCount(_hdpaResults); iResult++ )
  1548. {
  1549. LPQUERYRESULT pResult = (LPQUERYRESULT)DPA_GetPtr(_hdpaResults, iResult);
  1550. TraceAssert(pResult);
  1551. // if we match the item we want to delete then remove it, if the view
  1552. // is not filtered then remove ite from the list, otherwise leave the
  1553. // view update until we have finished deleting
  1554. if ( !StrCmpW(pwszName, pResult->pPath) )
  1555. {
  1556. Trace(TEXT("Item maps to result %d in the list"), iResult);
  1557. FreeQueryResult(pResult, DSA_GetItemCount(_hdsaColumns));
  1558. DPA_DeletePtr(_hdpaResults, iResult);
  1559. if ( !_fFilter )
  1560. {
  1561. TraceMsg("Deleting the item from the view");
  1562. ListView_DeleteItem(_hwndView, iResult);
  1563. }
  1564. }
  1565. }
  1566. }
  1567. }
  1568. // the view was filtered, so lets repopulate with the items
  1569. if ( _fFilter )
  1570. {
  1571. TraceMsg("View is filter, therefore just forcing a refresh");
  1572. _FilterView(FALSE);
  1573. }
  1574. }
  1575. TraceLeave();
  1576. }
  1577. STDMETHODIMP CDsQuery::UpdateView(DWORD dwType, LPDSOBJECTNAMES pdon)
  1578. {
  1579. HRESULT hres;
  1580. TraceEnter(TRACE_HANDLER, "CDsQuery::UpdateView");
  1581. switch ( dwType & DSQRVF_OPMASK )
  1582. {
  1583. case DSQRVF_ITEMSDELETED:
  1584. {
  1585. if ( !pdon )
  1586. ExitGracefully(hres, E_INVALIDARG, "Invlaidate pdon specified for refresh");
  1587. _DeleteViewItems(pdon);
  1588. break;
  1589. }
  1590. default:
  1591. ExitGracefully(hres, E_INVALIDARG, "Invalidate refresh type speciifed");
  1592. }
  1593. hres = S_OK;
  1594. exit_gracefully:
  1595. TraceLeaveResult(hres);
  1596. }
  1597. /*-----------------------------------------------------------------------------
  1598. / Message/Command Handlers
  1599. /----------------------------------------------------------------------------*/
  1600. /*-----------------------------------------------------------------------------
  1601. / CDsQuery::OnSize
  1602. / ----------------
  1603. / Result viewer is being sized, so ensure that our children have their
  1604. / sizes correctly addjusted.
  1605. /
  1606. / In:
  1607. / cx, cy = new size of the parent window
  1608. /
  1609. / Out:
  1610. / -
  1611. /----------------------------------------------------------------------------*/
  1612. LRESULT CDsQuery::OnSize(INT cx, INT cy)
  1613. {
  1614. TraceEnter(TRACE_VIEW, "CDsQuery::OnSize");
  1615. SetWindowPos(_hwndView, NULL, 0, 0, cx, cy, SWP_NOZORDER|SWP_NOMOVE);
  1616. _ShowBanner(0, 0);
  1617. TraceLeaveValue(0);
  1618. }
  1619. /*-----------------------------------------------------------------------------
  1620. / CDsQuery::OnNotify
  1621. / ------------------
  1622. / Notify message being recieved by the view, so try and handle it as best
  1623. / we can.
  1624. /
  1625. / In:
  1626. / hWnd = window handle of the notify
  1627. / wParam, lParam = parameters for the notify event
  1628. /
  1629. / Out:
  1630. / LRESULT
  1631. /----------------------------------------------------------------------------*/
  1632. LRESULT CDsQuery::OnNotify(HWND hWnd, WPARAM wParam, LPARAM lParam)
  1633. {
  1634. HRESULT hres;
  1635. LRESULT lr = 0;
  1636. DECLAREWAITCURSOR = GetCursor();
  1637. USES_CONVERSION;
  1638. TraceEnter(TRACE_VIEW, "CDsQuery::OnNotify");
  1639. switch ( ((LPNMHDR)lParam)->code )
  1640. {
  1641. case HDN_FILTERCHANGE:
  1642. _FilterView(TRUE);
  1643. break;
  1644. case HDN_FILTERBTNCLICK:
  1645. {
  1646. NMHDFILTERBTNCLICK* pNotify = (NMHDFILTERBTNCLICK*)lParam;
  1647. HMENU hMenu;
  1648. POINT pt;
  1649. HD_ITEM hdi;
  1650. UINT uID;
  1651. if ( _hdsaColumns && (pNotify->iItem < DSA_GetItemCount(_hdsaColumns)) )
  1652. {
  1653. LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(_hdsaColumns, pNotify->iItem);
  1654. if (pColumn)
  1655. {
  1656. hMenu = LoadMenu(GLOBAL_HINSTANCE, property_type_table[pColumn->iPropertyType].pMenuName);
  1657. TraceAssert(hMenu);
  1658. if ( hMenu )
  1659. {
  1660. pt.x = pNotify->rc.right;
  1661. pt.y = pNotify->rc.bottom;
  1662. MapWindowPoints(pNotify->hdr.hwndFrom, NULL, &pt, 1);
  1663. CheckMenuRadioItem(GetSubMenu(hMenu, 0),
  1664. FILTER_FIRST, FILTER_LAST, pColumn->idOperator,
  1665. MF_BYCOMMAND);
  1666. uID = TrackPopupMenu(GetSubMenu(hMenu, 0),
  1667. TPM_RIGHTALIGN|TPM_RETURNCMD,
  1668. pt.x, pt.y,
  1669. 0, pNotify->hdr.hwndFrom, NULL);
  1670. switch ( uID )
  1671. {
  1672. case DSQH_CLEARFILTER:
  1673. Header_ClearFilter(ListView_GetHeader(_hwndView), pNotify->iItem);
  1674. break;
  1675. case DSQH_CLEARALLFILTERS:
  1676. Header_ClearAllFilters(ListView_GetHeader(_hwndView));
  1677. break;
  1678. default:
  1679. {
  1680. if ( uID && (uID != pColumn->idOperator) )
  1681. {
  1682. // update the filter string based on the new operator
  1683. pColumn->idOperator = uID;
  1684. _GetFilterValue(pNotify->iItem, NULL);
  1685. lr = TRUE;
  1686. }
  1687. break;
  1688. }
  1689. }
  1690. DestroyMenu(hMenu);
  1691. }
  1692. }
  1693. }
  1694. break;
  1695. }
  1696. case HDN_ITEMCHANGED:
  1697. {
  1698. HD_NOTIFY* pNotify = (HD_NOTIFY*)lParam;
  1699. HD_ITEM* pitem = (HD_ITEM*)pNotify->pitem;
  1700. if ( _hdsaColumns && (pNotify->iItem < DSA_GetItemCount(_hdsaColumns)) )
  1701. {
  1702. LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(_hdsaColumns, pNotify->iItem);
  1703. TraceAssert(pColumn);
  1704. // store the new column width information in the column structure and
  1705. // mark the column table as dirty
  1706. if ( pitem->mask & HDI_WIDTH )
  1707. {
  1708. Trace(TEXT("Column %d, cx %d (marking state as dirty)"), pNotify->iItem, pitem->cxy);
  1709. pColumn->cx = pitem->cxy;
  1710. _fColumnsModified = TRUE;
  1711. }
  1712. if ( pitem->mask & HDI_FILTER )
  1713. {
  1714. Trace(TEXT("Filter for column %d has been changed"), pNotify->iItem);
  1715. _GetFilterValue(pNotify->iItem, pitem);
  1716. }
  1717. }
  1718. break;
  1719. }
  1720. case LVN_GETDISPINFO:
  1721. {
  1722. LV_DISPINFO* pNotify = (LV_DISPINFO*)lParam;
  1723. TraceAssert(pNotify);
  1724. if ( pNotify && (pNotify->item.mask & LVIF_TEXT) && pNotify->item.lParam )
  1725. {
  1726. LPQUERYRESULT pResult = (LPQUERYRESULT)pNotify->item.lParam;
  1727. INT iColumn = pNotify->item.iSubItem;
  1728. pNotify->item.pszText[0] = TEXT('\0'); // nothing to display yet
  1729. switch ( pResult->aColumn[iColumn].iPropertyType )
  1730. {
  1731. case PROPERTY_ISUNDEFINED:
  1732. break;
  1733. case PROPERTY_ISUNKNOWN:
  1734. case PROPERTY_ISSTRING:
  1735. {
  1736. if ( pResult->aColumn[iColumn].pszText )
  1737. StrCpyN(pNotify->item.pszText, pResult->aColumn[iColumn].pszText, pNotify->item.cchTextMax);
  1738. break;
  1739. }
  1740. case PROPERTY_ISNUMBER:
  1741. case PROPERTY_ISBOOL:
  1742. wsprintf(pNotify->item.pszText, TEXT("%d"), pResult->aColumn[iColumn].iValue);
  1743. break;
  1744. }
  1745. lr = TRUE; // we formatted a value
  1746. }
  1747. break;
  1748. }
  1749. case LVN_ITEMACTIVATE:
  1750. {
  1751. LPNMHDR pNotify = (LPNMHDR)lParam;
  1752. DWORD dwFlags = CMF_NORMAL;
  1753. HWND hwndFrame;
  1754. HMENU hMenu;
  1755. UINT uID;
  1756. // convert the current selection to IDLITs and an IContextMenu interface
  1757. // that we can then get the default verb from.
  1758. SetWaitCursor();
  1759. hres = _GetContextMenu();
  1760. FailGracefully(hres, "Failed when calling _GetContextMenu");
  1761. _fNoSelection = !ListView_GetSelectedCount(_hwndView);
  1762. if ( !_fNoSelection )
  1763. {
  1764. // create a popup menu pickup the context menu for the current selection
  1765. // and then pass it down to the invoke command handler.
  1766. hMenu = CreatePopupMenu();
  1767. TraceAssert(hMenu);
  1768. if ( hMenu )
  1769. {
  1770. if ( GetKeyState(VK_SHIFT) < 0 )
  1771. dwFlags |= CMF_EXPLORE; // SHIFT + dblclick does a Explore by default
  1772. _GetContextMenuVerbs(hMenu, dwFlags);
  1773. uID = GetMenuDefaultItem(hMenu, MF_BYCOMMAND, 0);
  1774. Trace(TEXT("Default uID after double click %08x"), uID);
  1775. if ( uID != -1 )
  1776. {
  1777. _pqf->GetWindow(&hwndFrame);
  1778. InvokeCommand(hwndFrame, uID);
  1779. }
  1780. DoRelease(_pcm); // no longer needed
  1781. DestroyMenu(hMenu);
  1782. }
  1783. }
  1784. break;
  1785. }
  1786. case LVN_COLUMNCLICK:
  1787. {
  1788. NM_LISTVIEW* pNotify = (NM_LISTVIEW*)lParam;
  1789. TraceAssert(pNotify);
  1790. _SortResults(pNotify->iSubItem);
  1791. break;
  1792. }
  1793. default:
  1794. lr = DefWindowProc(hWnd, WM_NOTIFY, wParam, lParam);
  1795. break;
  1796. }
  1797. exit_gracefully:
  1798. ResetWaitCursor();
  1799. TraceLeaveValue(lr);
  1800. }
  1801. /*-----------------------------------------------------------------------------
  1802. / CDsQuery::OnAddResults
  1803. / ----------------------
  1804. / The background thread has sent us some results, so lets add them to
  1805. / the DPA of results, discarding the ones we don't add because we cannot
  1806. / grow the DPA.
  1807. /
  1808. / dwQueryReference conatins the reference ID for this query, only add
  1809. / results where these match.
  1810. /
  1811. / In:
  1812. / dwQueryReference = reference that this block is for
  1813. / hdpaResults = DPA containing the results to add
  1814. /
  1815. / Out:
  1816. / HRESULT
  1817. /----------------------------------------------------------------------------*/
  1818. HRESULT CDsQuery::OnAddResults(DWORD dwQueryReference, HDPA hdpaResults)
  1819. {
  1820. HRESULT hres;
  1821. INT i, iPopulateFrom;
  1822. TraceEnter(TRACE_VIEW, "CDsQuery::OnAddResults");
  1823. if ( (dwQueryReference != _dwQueryReference) || !hdpaResults )
  1824. ExitGracefully(hres, E_FAIL, "Failed to add results, bad DPA/reference ID");
  1825. // the caller gives us a DPA then we add them to our result DPA, we then
  1826. // update the view populating from the first item we added.
  1827. iPopulateFrom = DPA_GetPtrCount(_hdpaResults);
  1828. for ( i = DPA_GetPtrCount(hdpaResults); --i >= 0 ; )
  1829. {
  1830. LPQUERYRESULT pResult = (LPQUERYRESULT)DPA_GetPtr(hdpaResults, i);
  1831. TraceAssert(pResult);
  1832. // add the result to the main DPA, if that fails then ensure we nuke
  1833. // this result blob!
  1834. if ( -1 == DPA_AppendPtr(_hdpaResults, pResult) )
  1835. FreeQueryResult(pResult, DSA_GetItemCount(_hdsaColumns));
  1836. DPA_DeletePtr(hdpaResults, i); // remove from result DPA
  1837. }
  1838. _PopulateView(iPopulateFrom, DPA_GetPtrCount(_hdpaResults));
  1839. TraceAssert(DPA_GetPtrCount(hdpaResults) == 0);
  1840. DPA_Destroy(hdpaResults);
  1841. hres = S_OK;
  1842. exit_gracefully:
  1843. TraceLeaveResult(hres);
  1844. }
  1845. /*-----------------------------------------------------------------------------
  1846. / CDsQuery::OnContextMenu
  1847. / -----------------------
  1848. / The user has right clicked in the result view, therefore we must attempt
  1849. / to display the context menu for those objects
  1850. /
  1851. / In:
  1852. / hwndMenu = window that that the user menued over
  1853. / pt = point to show the context menu
  1854. /
  1855. / Out:
  1856. / -
  1857. /----------------------------------------------------------------------------*/
  1858. LRESULT CDsQuery::OnContextMenu(HWND hwndMenu, LPARAM lParam)
  1859. {
  1860. HRESULT hres;
  1861. HMENU hMenu = NULL;
  1862. POINT pt = { 0, 0 };
  1863. INT i;
  1864. RECT rc;
  1865. HWND hwndFrame;
  1866. TraceEnter(TRACE_VIEW, "CDsQuery::OnContextMenu");
  1867. // Collect the selection, obtaining a IContextMenu interface pointer, or a HR == S_FALSE
  1868. // if there is no selection for us to be using.
  1869. hres = _GetContextMenu();
  1870. FailGracefully(hres, "Failed when calling _GetContextMenu()");
  1871. _fNoSelection = !ListView_GetSelectedCount(_hwndView);
  1872. if ( !(hMenu = CreatePopupMenu()) )
  1873. ExitGracefully(hres, E_FAIL, "Failed to create the popup menu");
  1874. if ( !_fNoSelection )
  1875. {
  1876. // pick up the context menu that maps tot he current selection, including fixing
  1877. // the "select" verb if we need one.
  1878. _GetContextMenuVerbs(hMenu, CMF_NORMAL);
  1879. }
  1880. else
  1881. {
  1882. // There is no selection so lets pick up the view bg menu, this contains
  1883. // some useful helpers for modifying the view state.
  1884. HMENU hBgMenu = LoadMenu(GLOBAL_HINSTANCE, MAKEINTRESOURCE(IDR_VIEWBACKGROUND));
  1885. if ( !hBgMenu )
  1886. ExitGracefully(hres, E_FAIL, "Failed to load pop-up menu for the background");
  1887. Shell_MergeMenus(hMenu, GetSubMenu(hBgMenu, 0), 0, 0, CQID_MAXHANDLERMENUID, 0x0);
  1888. DestroyMenu(hBgMenu);
  1889. _InitViewMenuItems(hMenu);
  1890. }
  1891. // if lParam == -1 then we know that the user hit the "context menu" key
  1892. // so lets set the co-ordinates of the item.
  1893. if ( lParam == (DWORD)-1 )
  1894. {
  1895. i = ListView_GetNextItem(_hwndView, -1, LVNI_FOCUSED|LVNI_SELECTED);
  1896. Trace(TEXT("Item with focus + selection: %d"), i);
  1897. if ( i == -1 )
  1898. {
  1899. i = ListView_GetNextItem(_hwndView, -1, LVNI_SELECTED);
  1900. Trace(TEXT("1st selected item: %D"), i);
  1901. }
  1902. if ( i != -1 )
  1903. {
  1904. TraceMsg("We have an item, so getting bounds of the icon for position");
  1905. ListView_GetItemRect(_hwndView, i, &rc, LVIR_ICON);
  1906. pt.x = (rc.left+rc.right)/2;
  1907. pt.y = (rc.top+rc.bottom)/2;
  1908. }
  1909. MapWindowPoints(_hwndView, HWND_DESKTOP, &pt, 1); // they are in client co-ordinates
  1910. }
  1911. else
  1912. {
  1913. pt.x = GET_X_LPARAM(lParam);
  1914. pt.y = GET_Y_LPARAM(lParam);
  1915. }
  1916. // we have the position so lets use it
  1917. _pqf->GetWindow(&hwndFrame);
  1918. TrackPopupMenu(hMenu, TPM_LEFTALIGN, pt.x, pt.y, 0, hwndFrame, NULL);
  1919. exit_gracefully:
  1920. if ( hMenu )
  1921. DestroyMenu(hMenu);
  1922. TraceLeaveValue(0);
  1923. }
  1924. /*-----------------------------------------------------------------------------
  1925. / CDsQuery::OnFileProperties
  1926. / --------------------------
  1927. / Show properties for the given selection. To do this we CoCreate
  1928. / IDsFolderProperties on the IDsFolder implementation and that
  1929. / we can invoke properties using.
  1930. /
  1931. / In:
  1932. / Out:
  1933. / HRESULT
  1934. /----------------------------------------------------------------------------*/
  1935. HRESULT CDsQuery::OnFileProperties(VOID)
  1936. {
  1937. HRESULT hres;
  1938. IDataObject* pDataObject = NULL;
  1939. IDsFolderProperties* pDsFolderProperties = NULL;
  1940. TraceEnter(TRACE_VIEW, "CDsQuery::OnFileProperties");
  1941. hres = GetViewObject(CQRVS_SELECTION, IID_IDataObject, (void **)&pDataObject);
  1942. FailGracefully(hres, "Failed to get IDataObject for shortcut creation");
  1943. hres = CoCreateInstance(CLSID_DsFolderProperties, NULL, CLSCTX_INPROC_SERVER, IID_IDsFolderProperties, (void **)&pDsFolderProperties);
  1944. FailGracefully(hres, "Failed to get IDsFolderProperties for the desktop object");
  1945. hres = pDsFolderProperties->ShowProperties(_hwnd, pDataObject);
  1946. FailGracefully(hres, "Failed to invoke property UI for the given selection");
  1947. // hres = S_OK; // success
  1948. exit_gracefully:
  1949. DoRelease(pDataObject);
  1950. DoRelease(pDsFolderProperties);
  1951. TraceLeaveResult(hres);
  1952. }
  1953. /*-----------------------------------------------------------------------------
  1954. / CDsQuery::OnFileSaveQuery
  1955. / -------------------------
  1956. / Allow the user to choose a location to save the query (initial directory
  1957. / is nethood). Having done that we then start the save process by passing
  1958. / the frame object a IQueryIO object that allows them to persist the
  1959. / query into.
  1960. /
  1961. / In:
  1962. / Out:
  1963. / HRESULT
  1964. /----------------------------------------------------------------------------*/
  1965. HRESULT CDsQuery::OnFileSaveQuery(VOID)
  1966. {
  1967. HRESULT hres;
  1968. OPENFILENAME ofn;
  1969. TCHAR szFilename[MAX_PATH];
  1970. TCHAR szDirectory[MAX_PATH];
  1971. TCHAR szFilter[64];
  1972. TCHAR szTitle[64];
  1973. LPTSTR pFilter;
  1974. IPersistQuery *ppq = NULL;
  1975. USES_CONVERSION;
  1976. TraceEnter(TRACE_VIEW, "CDsQuery::OnFileSaveQuery");
  1977. // Load the default strings and fix up the filter string as it needs
  1978. // NULL's seperating the various resource sections.
  1979. LoadString(GLOBAL_HINSTANCE, IDS_SAVETITLE, szTitle, ARRAYSIZE(szTitle));
  1980. StrCpy(szFilename, _pDefaultSaveName);
  1981. LoadString(GLOBAL_HINSTANCE, IDS_SAVEFILTER, szFilter, ARRAYSIZE(szFilter));
  1982. for ( pFilter = szFilter ; *pFilter ; pFilter++ )
  1983. {
  1984. if ( *pFilter == TEXT('\n') )
  1985. *pFilter = TEXT('\0');
  1986. }
  1987. // fix the open filename structure ready to do our save....
  1988. ZeroMemory(&ofn, SIZEOF(ofn));
  1989. ofn.lStructSize = SIZEOF(ofn);
  1990. _pqf->GetWindow(&ofn.hwndOwner);
  1991. ofn.hInstance = GLOBAL_HINSTANCE;
  1992. ofn.lpstrFilter = szFilter;
  1993. ofn.lpstrFile = szFilename;
  1994. ofn.nMaxFile = ARRAYSIZE(szFilename);
  1995. if ( _pDefaultSaveLocation )
  1996. {
  1997. Trace(TEXT("Saving into: %s"), W2T(_pDefaultSaveLocation));
  1998. StrCpy(szDirectory, W2T(_pDefaultSaveLocation));
  1999. ofn.lpstrInitialDir = szDirectory;
  2000. }
  2001. ofn.lpstrTitle = szTitle;
  2002. ofn.Flags = OFN_EXPLORER|OFN_NOCHANGEDIR|OFN_OVERWRITEPROMPT|OFN_PATHMUSTEXIST|OFN_HIDEREADONLY;
  2003. ofn.lpstrDefExt = TEXT("dsq");
  2004. // If we get a save filename then lets ensure that we delete the previous
  2005. // query saved there (if there is one) and then we can create an IPersistQuery
  2006. // object that will save to that location.
  2007. if ( GetSaveFileName(&ofn) )
  2008. {
  2009. Trace(TEXT("Saving query as: %s"), szFilename);
  2010. if ( !DeleteFile(szFilename) && (GetLastError() != ERROR_FILE_NOT_FOUND) )
  2011. ExitGracefully(hres, E_FAIL, "Failed to delete previous query");
  2012. hres = CPersistQuery_CreateInstance(szFilename, &ppq);
  2013. FailGracefully(hres, "Failed to create the peristance object");
  2014. hres = _pqf->SaveQuery(ppq);
  2015. FailGracefully(hres, "Failed when calling IQueryFrame::SaveSearch");
  2016. }
  2017. hres = S_OK;
  2018. exit_gracefully:
  2019. DoRelease(ppq);
  2020. TraceLeaveResult(hres);
  2021. }
  2022. /*-----------------------------------------------------------------------------
  2023. / CDsQuery::OnEditSelectAll
  2024. / -------------------------
  2025. / Walk all the items in the view setting their selected state.
  2026. /
  2027. / In:
  2028. / Out:
  2029. / HRESULT
  2030. /----------------------------------------------------------------------------*/
  2031. HRESULT CDsQuery::OnEditSelectAll(VOID)
  2032. {
  2033. TraceEnter(TRACE_VIEW, "CDsQuery::OnEditSelectAll");
  2034. for ( INT i = ListView_GetItemCount(_hwndView) ; --i >= 0 ; )
  2035. {
  2036. ListView_SetItemState(_hwndView, i, LVIS_SELECTED, LVIS_SELECTED);
  2037. }
  2038. TraceLeaveResult(S_OK);
  2039. }
  2040. /*-----------------------------------------------------------------------------
  2041. / CDsQuery::OnEditInvertSelection
  2042. / -------------------------------
  2043. / Walk all the items in the view and invert their selected state.
  2044. /
  2045. / In:
  2046. / Out:
  2047. / HRESULT
  2048. /----------------------------------------------------------------------------*/
  2049. HRESULT CDsQuery::OnEditInvertSelection(VOID)
  2050. {
  2051. TraceEnter(TRACE_VIEW, "CDsQuery::OnEditInvertSelection");
  2052. for ( INT i = ListView_GetItemCount(_hwndView) ; --i >= 0 ; )
  2053. {
  2054. DWORD dwState = ListView_GetItemState(_hwndView, i, LVIS_SELECTED);
  2055. ListView_SetItemState(_hwndView, i, dwState ^ LVIS_SELECTED, LVIS_SELECTED);
  2056. }
  2057. TraceLeaveResult(S_OK);
  2058. }
  2059. /*-----------------------------------------------------------------------------
  2060. / CDsQuery::_InitNewQuery
  2061. / ----------------------
  2062. / Initialize the view ready for a new query, including setting the frame
  2063. / into a query running state.
  2064. /
  2065. / In:
  2066. / pDsQueryParams = DSQUERYPARAMs structure used to issue the query.
  2067. / fRefreshColumnTable = re-read the column table from the params/registry etc
  2068. /
  2069. / Out:
  2070. / HRESULT
  2071. /----------------------------------------------------------------------------*/
  2072. HRESULT CDsQuery::_InitNewQuery(LPDSQUERYPARAMS pDsQueryParams, BOOL fRefreshColumnTable)
  2073. {
  2074. HRESULT hres;
  2075. LPTSTR pBuffer;
  2076. TraceEnter(TRACE_VIEW, "CDsQuery::_InitNewQuery");
  2077. hres = _pqf->StartQuery(TRUE);
  2078. TraceAssert(SUCCEEDED(hres));
  2079. // Claim the cached DS object for this window
  2080. // if refreshing the column table then _GetColumnTable handles this all for us,
  2081. // otherwise lets just nuke the result set ourselves.
  2082. if ( fRefreshColumnTable )
  2083. {
  2084. _SaveColumnTable();
  2085. hres = _GetColumnTable(_clsidForm, pDsQueryParams, &_hdsaColumns, TRUE);
  2086. FailGracefully(hres, "Failed to create column DSA");
  2087. }
  2088. else
  2089. {
  2090. _FreeResults();
  2091. }
  2092. // initialize the view to start the query running, display the prompt banner and
  2093. // initialize the result DPA.
  2094. _ShowBanner(SWP_SHOWWINDOW, IDS_SEARCHING); // we are now searching
  2095. if ( SUCCEEDED(FormatMsgResource(&pBuffer, GLOBAL_HINSTANCE, IDS_SEARCHING)) )
  2096. {
  2097. _pqf->SetStatusText(pBuffer);
  2098. LocalFreeString(&pBuffer);
  2099. }
  2100. TraceAssert(_hdpaResults==NULL); // should never catch
  2101. _hdpaResults = DPA_Create(16);
  2102. TraceAssert(_hdpaResults);
  2103. if ( !_hdpaResults )
  2104. ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate result DPA");
  2105. _dwQueryReference++;
  2106. exit_gracefully:
  2107. TraceLeaveResult(hres);
  2108. }
  2109. /*-----------------------------------------------------------------------------
  2110. / CDsQuery::_GetFilterValue
  2111. / ------------------------
  2112. / Given a column index collect the filter value from it, note that
  2113. / when doing this
  2114. /
  2115. / In:
  2116. / i = column to retrieve
  2117. / pitem -> HD_ITEM structure for the current filter / == NULL then read from header
  2118. /
  2119. / Out:
  2120. / HRESULT
  2121. /----------------------------------------------------------------------------*/
  2122. HRESULT CDsQuery::_GetFilterValue(INT i, HD_ITEM* pitem)
  2123. {
  2124. HRESULT hres;
  2125. HD_ITEM hdi;
  2126. HD_TEXTFILTER textFilter;
  2127. TCHAR szBuffer[MAX_PATH];
  2128. INT iValue;
  2129. UINT cchFilter = 0;
  2130. LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(_hdsaColumns, i);
  2131. if (!pColumn)
  2132. ExitGracefully(hres, E_FAIL, "Failed to get the column");
  2133. TraceEnter(TRACE_VIEW, "CDsQuery::_GetFilterValue");
  2134. // if pitem == NULL then lets pick up the filter value from the
  2135. // header control, using the stored property type (the filter one
  2136. // has already been nuked) to defined which filter we want.
  2137. if ( !pitem )
  2138. {
  2139. hdi.mask = HDI_FILTER;
  2140. switch ( pColumn->iPropertyType )
  2141. {
  2142. case PROPERTY_ISUNKNOWN:
  2143. case PROPERTY_ISSTRING:
  2144. {
  2145. hdi.type = HDFT_ISSTRING;
  2146. hdi.pvFilter = &textFilter;
  2147. textFilter.pszText = szBuffer;
  2148. textFilter.cchTextMax = ARRAYSIZE(szBuffer);
  2149. break;
  2150. }
  2151. case PROPERTY_ISNUMBER:
  2152. case PROPERTY_ISBOOL:
  2153. {
  2154. hdi.type = HDFT_ISNUMBER;
  2155. hdi.pvFilter = &iValue;
  2156. break;
  2157. }
  2158. }
  2159. if ( !Header_GetItem(ListView_GetHeader(_hwndView), i, &hdi) )
  2160. ExitGracefully(hres, E_FAIL, "Failed to get the filter string");
  2161. pitem = &hdi;
  2162. }
  2163. // discard the previous filter value and lets read from the
  2164. // structure the information we need to cache our filter information
  2165. FreeColumnValue(&pColumn->filter);
  2166. if ( !(pitem->type & HDFT_HASNOVALUE) && pitem->pvFilter )
  2167. {
  2168. switch ( pitem->type & HDFT_ISMASK )
  2169. {
  2170. case HDFT_ISSTRING:
  2171. {
  2172. LPHD_TEXTFILTER ptextFilter = (LPHD_TEXTFILTER)pitem->pvFilter;
  2173. TraceAssert(ptextFilter);
  2174. pColumn->filter.iPropertyType = PROPERTY_ISSTRING;
  2175. // text filters are stored in their wildcarded state, therefore
  2176. // filtering doesn't require converting from the text form
  2177. // to something more elobrate each pass through. the down
  2178. // side is when the operator changes we must rebuild the
  2179. // filter string for that column (small price)
  2180. GetPatternString(NULL, &cchFilter, pColumn->idOperator, ptextFilter->pszText);
  2181. TraceAssert(cchFilter != 0);
  2182. if ( cchFilter )
  2183. {
  2184. hres = LocalAllocStringLen(&pColumn->filter.pszText, cchFilter);
  2185. FailGracefully(hres, "Failed to allocate buffer to read string into");
  2186. GetPatternString(pColumn->filter.pszText, &cchFilter, pColumn->idOperator, ptextFilter->pszText);
  2187. Trace(TEXT("Filter (with pattern info): %s"), pColumn->filter.pszText);
  2188. LCMapString(0x0, LCMAP_UPPERCASE, pColumn->filter.pszText, -1, pColumn->filter.pszText, cchFilter+1);
  2189. Trace(TEXT("After converting to uppercase (LCMapString): %s"), pColumn->filter.pszText);
  2190. }
  2191. break;
  2192. }
  2193. case HDFT_ISNUMBER:
  2194. {
  2195. INT* piFilter = (INT*)pitem->pvFilter;
  2196. TraceAssert(piFilter);
  2197. pColumn->filter.iPropertyType = PROPERTY_ISNUMBER;
  2198. pColumn->filter.iValue = *piFilter;
  2199. Trace(TEXT("Filter: %d"), pColumn->filter.iValue);
  2200. break;
  2201. }
  2202. }
  2203. }
  2204. hres = S_OK;
  2205. exit_gracefully:
  2206. TraceLeaveResult(hres);
  2207. }
  2208. /*-----------------------------------------------------------------------------
  2209. / CDsQuery::_FilterView
  2210. / --------------------
  2211. / Filter the result set populating the view again with the changes
  2212. /
  2213. / In:
  2214. / fCheck
  2215. /
  2216. / Out:
  2217. / HRESULT
  2218. /----------------------------------------------------------------------------*/
  2219. UINT _GetFilter(HDSA hdsaColumns, LPTSTR pBuffer, UINT* pcchBuffer)
  2220. {
  2221. INT i;
  2222. TCHAR szBuffer[MAX_PATH];
  2223. TraceEnter(TRACE_VIEW, "_GetFilter");
  2224. TraceAssert(hdsaColumns && pcchBuffer);
  2225. *pcchBuffer = 0;
  2226. // form the string containin [operatorID]value pairs for each of the
  2227. // filter columns that is defined.
  2228. for ( i = 0 ; i < DSA_GetItemCount(hdsaColumns); i++ )
  2229. {
  2230. LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(hdsaColumns, i);
  2231. TraceAssert(pColumn);
  2232. if ( pColumn->filter.iPropertyType != PROPERTY_ISUNDEFINED )
  2233. {
  2234. wsprintf(szBuffer, TEXT("[%d]"), pColumn->idOperator);
  2235. PutStringElement(pBuffer, pcchBuffer, szBuffer);
  2236. switch ( pColumn->filter.iPropertyType )
  2237. {
  2238. case PROPERTY_ISUNDEFINED:
  2239. break;
  2240. case PROPERTY_ISUNKNOWN:
  2241. case PROPERTY_ISSTRING:
  2242. PutStringElement(pBuffer, pcchBuffer, pColumn->filter.pszText);
  2243. break;
  2244. case PROPERTY_ISNUMBER:
  2245. case PROPERTY_ISBOOL:
  2246. wsprintf(szBuffer, TEXT("%d"), pColumn->filter.iValue);
  2247. PutStringElement(pBuffer, pcchBuffer, szBuffer);
  2248. break;
  2249. }
  2250. }
  2251. }
  2252. Trace(TEXT("pBuffer contains: %s (%d)"), pBuffer ? pBuffer:TEXT("<NULL>"), *pcchBuffer);
  2253. TraceLeaveValue(*pcchBuffer);
  2254. }
  2255. HRESULT CDsQuery::_FilterView(BOOL fCheck)
  2256. {
  2257. HRESULT hres = S_OK;
  2258. LPTSTR pFilter = NULL;
  2259. UINT cchFilter;
  2260. BOOL fSetCursor = FALSE;
  2261. TraceEnter(TRACE_VIEW, "CDsQuery::_FilterView");
  2262. if ( !_hdpaResults )
  2263. ExitGracefully(hres, S_OK, "FitlerView bailing, no results");
  2264. DECLAREWAITCURSOR;
  2265. SetWaitCursor(); // this could take some time
  2266. fSetCursor = TRUE;
  2267. // get the current filter string, this consists of the filter
  2268. // information from all the columns
  2269. if ( _GetFilter(_hdsaColumns, NULL, &cchFilter) )
  2270. {
  2271. hres = LocalAllocStringLen(&pFilter, cchFilter);
  2272. FailGracefully(hres, "Failed to allocate filter string");
  2273. _GetFilter(_hdsaColumns, pFilter, &cchFilter);
  2274. }
  2275. // if the filters don't match then re-populate the view,
  2276. // as the criteria for the results has changed
  2277. if ( !fCheck ||
  2278. (!pFilter || !_pFilter) ||
  2279. (pFilter && _pFilter && StrCmpI(pFilter, _pFilter)) )
  2280. {
  2281. LPTSTR pBuffer;
  2282. TraceMsg("Filtering the view, filters differ");
  2283. ListView_DeleteAllItems(_hwndView);
  2284. _ShowBanner(SWP_SHOWWINDOW, IDS_FILTERING);
  2285. if ( SUCCEEDED(FormatMsgResource(&pBuffer, GLOBAL_HINSTANCE, IDS_FILTERING)) )
  2286. {
  2287. _pqf->SetStatusText(pBuffer);
  2288. LocalFreeString(&pBuffer);
  2289. }
  2290. _PopulateView(0, DPA_GetPtrCount(_hdpaResults));
  2291. }
  2292. // ensure we hang onto the new filter, discarding the previous one
  2293. LocalFreeString(&_pFilter);
  2294. _pFilter = pFilter;
  2295. exit_gracefully:
  2296. if (fSetCursor)
  2297. ResetWaitCursor();
  2298. TraceLeaveResult(hres);
  2299. }
  2300. /*-----------------------------------------------------------------------------
  2301. / CDsQuery::_PopulateView
  2302. / ----------------------
  2303. / Add items from the result DPA to the view filtering as required. The
  2304. / caller gives us the start index (0 if all) and we walk the results
  2305. / adding them to the view.
  2306. /
  2307. / In:
  2308. / iItem = first item to add / == 0 first / == -1 then add none, just update status
  2309. / iLast = last item to be updated
  2310. /
  2311. / Out:
  2312. / HRESULT
  2313. /----------------------------------------------------------------------------*/
  2314. HRESULT CDsQuery::_PopulateView(INT iItem, INT iLast)
  2315. {
  2316. HRESULT hres;
  2317. BOOL fBannerShown = IsWindowVisible(_hwndBanner);
  2318. LPTSTR pBuffer = NULL;
  2319. LV_ITEM lvi;
  2320. INT iColumn, i;
  2321. INT iVisible = 0;
  2322. INT iHidden = 0;
  2323. BOOL fIncludeItem;
  2324. MSG msg;
  2325. USES_CONVERSION;
  2326. TraceEnter(TRACE_VIEW, "CDsQuery::_PopulateView");
  2327. Trace(TEXT("Range %d to %d"), iItem, iLast);
  2328. if ( iItem > -1 )
  2329. {
  2330. Trace(TEXT("Adding items %d to %d"), iItem, DPA_GetPtrCount(_hdpaResults));
  2331. lvi.mask = LVIF_TEXT|LVIF_PARAM|LVIF_IMAGE;
  2332. lvi.iItem = 0x7fffffff;
  2333. lvi.iSubItem = 0;
  2334. lvi.pszText = LPSTR_TEXTCALLBACK;
  2335. // Walk the results in the range we need to add and add them to the view
  2336. // applying the filter to remove items we are not interested in.
  2337. for ( i = 0; iItem < iLast ; i++, iItem++ )
  2338. {
  2339. LPQUERYRESULT pResult = (LPQUERYRESULT)DPA_GetPtr(_hdpaResults, iItem);
  2340. if (!pResult)
  2341. continue;
  2342. fIncludeItem = TRUE; // new items always get included
  2343. // if the filter is visilbe then lets walk it removing items from the list
  2344. // of results. fIncludeItem starts as TRUE and after the filter
  2345. // loop should become either TRUE/FALSE. All columns are ANDed together
  2346. // therefore the logic is quite simple.
  2347. if ( _fFilter )
  2348. {
  2349. for ( iColumn = 0 ; fIncludeItem && (iColumn < DSA_GetItemCount(_hdsaColumns)); iColumn++ )
  2350. {
  2351. LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(_hdsaColumns, iColumn);
  2352. TraceAssert(pColumn);
  2353. // if the column has a filter defined (!PROPERTY_ISUNDEFINED) then
  2354. // check that the properties match, if they don't then skip the
  2355. // itmer otherwise lets try applying the filter based on
  2356. // the type.
  2357. if ( pColumn->filter.iPropertyType == PROPERTY_ISUNDEFINED )
  2358. continue;
  2359. if ( pResult->aColumn[iColumn].iPropertyType == PROPERTY_ISUNDEFINED )
  2360. {
  2361. // column is undefined therefore lets ignore it, it won't
  2362. // match the criteria
  2363. fIncludeItem = FALSE;
  2364. }
  2365. else
  2366. {
  2367. switch ( pColumn->filter.iPropertyType )
  2368. {
  2369. case PROPERTY_ISUNDEFINED:
  2370. break;
  2371. case PROPERTY_ISUNKNOWN:
  2372. case PROPERTY_ISSTRING:
  2373. {
  2374. TCHAR szBuffer[MAX_PATH];
  2375. LPTSTR pszBuffer = NULL;
  2376. LPTSTR pszValue = pResult->aColumn[iColumn].pszText;
  2377. INT cchValue = lstrlen(pszValue);
  2378. LPTSTR pszValueUC = szBuffer;
  2379. // the filter value is stored in uppercase, so to ensure we are case insensitive
  2380. // we must convert the string to uppercase. We have a buffer we will use,
  2381. // however, if the value is too large we will allocate a buffer we can use.
  2382. if ( cchValue > ARRAYSIZE(szBuffer) )
  2383. {
  2384. TraceMsg("Value too big for our static buffer, so allocating");
  2385. if ( FAILED(LocalAllocStringLen(&pszBuffer, cchValue)) )
  2386. {
  2387. TraceMsg("Failed to allocate a buffer for the string, so ignoring it!");
  2388. fIncludeItem = FALSE;
  2389. break;
  2390. }
  2391. pszValueUC = pszBuffer; // fix the pointer to the new string
  2392. }
  2393. LCMapString(0x0, LCMAP_UPPERCASE, pszValue, -1, pszValueUC, cchValue+1);
  2394. Trace(TEXT("After converting to uppercase (LCMapString): %s"), pszValueUC);
  2395. // string properties need to be compared using the match filter
  2396. // function, this code is given the filter and the result
  2397. // and we must compare, in return we get TRUE/FALSE, therefore
  2398. // catch the NOT cases specificly.
  2399. switch ( pColumn->idOperator )
  2400. {
  2401. case FILTER_CONTAINS:
  2402. case FILTER_STARTSWITH:
  2403. case FILTER_ENDSWITH:
  2404. case FILTER_IS:
  2405. fIncludeItem = MatchPattern(pszValueUC, pColumn->filter.pszText);
  2406. break;
  2407. case FILTER_NOTCONTAINS:
  2408. case FILTER_ISNOT:
  2409. fIncludeItem = !MatchPattern(pszValueUC, pColumn->filter.pszText);
  2410. break;
  2411. }
  2412. LocalFreeString(&pszBuffer); // ensure we don't leak, in thise case it would be costly!
  2413. break;
  2414. }
  2415. case PROPERTY_ISBOOL:
  2416. case PROPERTY_ISNUMBER:
  2417. {
  2418. // numeric properties are handled only as ints, therefore
  2419. // lets compare the numeric value we have
  2420. switch ( pColumn->idOperator )
  2421. {
  2422. case FILTER_IS:
  2423. fIncludeItem = (pColumn->filter.iValue == pResult->aColumn[iColumn].iValue);
  2424. break;
  2425. case FILTER_ISNOT:
  2426. fIncludeItem = (pColumn->filter.iValue != pResult->aColumn[iColumn].iValue);
  2427. break;
  2428. case FILTER_GREATEREQUAL:
  2429. fIncludeItem = (pColumn->filter.iValue <= pResult->aColumn[iColumn].iValue);
  2430. break;
  2431. case FILTER_LESSEQUAL:
  2432. fIncludeItem = (pColumn->filter.iValue >= pResult->aColumn[iColumn].iValue);
  2433. break;
  2434. }
  2435. break;
  2436. }
  2437. }
  2438. }
  2439. }
  2440. }
  2441. Trace(TEXT("fInclude item is %d"), fIncludeItem);
  2442. if ( fIncludeItem )
  2443. {
  2444. // we are going to add the item to the view, so lets hide the banner
  2445. // if it is shown, then add a list view item. The list view
  2446. // item has text-callback and the lParam -> pResult structure
  2447. // we are using.
  2448. //
  2449. // also, if the view hasn't had the image list set then lets
  2450. // take care of that now
  2451. if ( fBannerShown )
  2452. {
  2453. TraceMsg("Adding an item and banner visible, therefore hiding");
  2454. _ShowBanner(SWP_HIDEWINDOW, 0); // hide the banner
  2455. fBannerShown = FALSE;
  2456. }
  2457. lvi.lParam = (LPARAM)pResult;
  2458. lvi.iImage = pResult->iImage;
  2459. ListView_InsertItem(_hwndView, &lvi);
  2460. if ( i % FILTER_UPDATE_COUNT )
  2461. UpdateWindow(_hwndView);
  2462. }
  2463. }
  2464. }
  2465. // lets update the status bar to reflect the world around us
  2466. TraceAssert(_hdpaResults);
  2467. if ( _hdpaResults )
  2468. {
  2469. iVisible = ListView_GetItemCount(_hwndView);
  2470. iHidden = DPA_GetPtrCount(_hdpaResults)-iVisible;
  2471. }
  2472. if ( iVisible <= 0 )
  2473. {
  2474. _ShowBanner(SWP_SHOWWINDOW, IDS_NOTHINGFOUND);
  2475. }
  2476. else
  2477. {
  2478. // ensure that at least one item in the view has focus
  2479. if ( -1 == ListView_GetNextItem(_hwndView, -1, LVNI_FOCUSED) )
  2480. ListView_SetItemState(_hwndView, 0, LVIS_FOCUSED, LVIS_FOCUSED);
  2481. }
  2482. if ( SUCCEEDED(FormatMsgResource(&pBuffer,
  2483. GLOBAL_HINSTANCE, iHidden ? IDS_FOUNDITEMSHIDDEN:IDS_FOUNDITEMS,
  2484. iVisible, iHidden)) )
  2485. {
  2486. Trace(TEXT("Setting status text to: %s"), pBuffer);
  2487. _pqf->SetStatusText(pBuffer);
  2488. LocalFreeString(&pBuffer);
  2489. }
  2490. _iSortColumn = -1; // sort is no longer valid!
  2491. TraceLeaveResult(S_OK);
  2492. }
  2493. /*-----------------------------------------------------------------------------
  2494. / CDsQuery::_FreeResults
  2495. / ---------------------
  2496. / Do the tidy up to release the results from the view. This includes
  2497. / destroying the DPA and removing the items from the list view.
  2498. /
  2499. / In:
  2500. / Out:
  2501. / -
  2502. /----------------------------------------------------------------------------*/
  2503. VOID CDsQuery::_FreeResults(VOID)
  2504. {
  2505. TraceEnter(TRACE_VIEW, "CDsQuery::_FreeResults");
  2506. if ( IsWindow(_hwndView) )
  2507. ListView_DeleteAllItems(_hwndView);
  2508. if ( _hdpaResults )
  2509. {
  2510. DPA_DestroyCallback(_hdpaResults, FreeQueryResultCB, IntToPtr(DSA_GetItemCount(_hdsaColumns)));
  2511. _hdpaResults = NULL;
  2512. }
  2513. LocalFreeString(&_pFilter);
  2514. TraceLeave();
  2515. }
  2516. /*-----------------------------------------------------------------------------
  2517. / CDsQuery::_SetViewMode
  2518. / ---------------------
  2519. / Convert the command ID of the view mode into a LVS_ style bit that can
  2520. / then we applied to the view.
  2521. /
  2522. / In:
  2523. / uID = view mode to be selected
  2524. /
  2525. / Out:
  2526. / DWORD = LVS_ style for this view mode
  2527. /----------------------------------------------------------------------------*/
  2528. DWORD CDsQuery::_SetViewMode(INT uID)
  2529. {
  2530. const DWORD dwIdToStyle[] = { LVS_ICON, LVS_SMALLICON, LVS_LIST, LVS_REPORT };
  2531. DWORD dwResult = 0;
  2532. DWORD dwStyle;
  2533. TraceEnter(TRACE_HANDLER|TRACE_VIEW, "CDsQuery::_SetViewMode");
  2534. Trace(TEXT("Setting view mode to %08x"), uID);
  2535. _idViewMode = uID;
  2536. uID -= DSQH_VIEW_LARGEICONS;
  2537. if ( uID < ARRAYSIZE(dwIdToStyle ) )
  2538. {
  2539. dwResult = dwIdToStyle[uID];
  2540. if ( IsWindow(_hwndView) )
  2541. {
  2542. dwStyle = GetWindowLong(_hwndView, GWL_STYLE);
  2543. if ( ( dwStyle & LVS_TYPEMASK ) != dwResult )
  2544. {
  2545. TraceMsg("Changing view style to reflect new mode");
  2546. SetWindowLong(_hwndView, GWL_STYLE, (dwStyle & ~LVS_TYPEMASK)|dwResult);
  2547. }
  2548. }
  2549. }
  2550. _ShowBanner(0, 0);
  2551. TraceLeaveValue(dwResult);
  2552. }
  2553. /*-----------------------------------------------------------------------------
  2554. / CDsQuery::_SetFilter
  2555. / -------------------
  2556. / Sets the visible state of the filter, refreshing the view as required.
  2557. /
  2558. / NB: To ensure that the ListView correctly refrehes its contents we first
  2559. / remove the header from the view, then toggle the filter state
  2560. / and re-enable the banner.
  2561. / In:
  2562. / fFilter = flag indicating the filter state
  2563. /
  2564. / Out:
  2565. / VOID
  2566. /----------------------------------------------------------------------------*/
  2567. VOID CDsQuery::_SetFilter(BOOL fFilter)
  2568. {
  2569. TraceEnter(TRACE_HANDLER|TRACE_VIEW, "CDsQuery::_SetFilter");
  2570. _fFilter = fFilter; // store the new filter value
  2571. if ( IsWindow(_hwndView) )
  2572. {
  2573. HWND hwndHeader = ListView_GetHeader(_hwndView);
  2574. DWORD dwStyle = GetWindowLong(hwndHeader, GWL_STYLE) & ~(HDS_FILTERBAR|WS_TABSTOP);
  2575. SetWindowLong(_hwndView, GWL_STYLE, GetWindowLong(_hwndView, GWL_STYLE) | LVS_NOCOLUMNHEADER);
  2576. if ( _fFilter )
  2577. dwStyle |= HDS_FILTERBAR|WS_TABSTOP;
  2578. SetWindowLong(hwndHeader, GWL_STYLE, dwStyle);
  2579. SetWindowLong(_hwndView, GWL_STYLE, GetWindowLong(_hwndView, GWL_STYLE) & ~LVS_NOCOLUMNHEADER);
  2580. _ShowBanner(0, 0);
  2581. if ( _hdpaResults )
  2582. {
  2583. if ( (_fFilter && _pFilter) ||
  2584. (!_fFilter && (DPA_GetPtrCount(_hdpaResults) != ListView_GetItemCount(_hwndView))) )
  2585. {
  2586. _FilterView(FALSE);
  2587. }
  2588. }
  2589. }
  2590. TraceLeave();
  2591. }
  2592. /*-----------------------------------------------------------------------------
  2593. / CDsQuery::_SortResults
  2594. / ---------------------
  2595. / Sort the view given a column, handles either being clicked or
  2596. / invoked on from an API.
  2597. /
  2598. / In:
  2599. / iColumn = column to sort on
  2600. /
  2601. / Out:
  2602. / -
  2603. /----------------------------------------------------------------------------*/
  2604. INT _ResultSortCB(LPARAM lParam1, LPARAM lParam2, LPARAM lParam)
  2605. {
  2606. LPQUERYRESULT pResult1, pResult2;
  2607. INT iColumn = LOWORD(lParam);
  2608. // if lParam != 0 then we are reverse sorting, therefore swap
  2609. // over the object pointers
  2610. if ( !HIWORD(lParam) )
  2611. {
  2612. pResult1 = (LPQUERYRESULT)lParam1;
  2613. pResult2 = (LPQUERYRESULT)lParam2;
  2614. }
  2615. else
  2616. {
  2617. pResult2 = (LPQUERYRESULT)lParam1;
  2618. pResult1 = (LPQUERYRESULT)lParam2;
  2619. }
  2620. if ( pResult1 && pResult2 )
  2621. {
  2622. LPCOLUMNVALUE pColumn1 = (LPCOLUMNVALUE)&pResult1->aColumn[iColumn];
  2623. LPCOLUMNVALUE pColumn2 = (LPCOLUMNVALUE)&pResult2->aColumn[iColumn];
  2624. BOOL fHasColumn1 = pColumn1->iPropertyType != PROPERTY_ISUNDEFINED;
  2625. BOOL fHasColumn2 = pColumn2->iPropertyType != PROPERTY_ISUNDEFINED;
  2626. // check that both properties are defined, if they are not then return the
  2627. // comparison based on that field. then we check that the properties
  2628. // are the same type, if that matches then lets compare based on the
  2629. // type.
  2630. if ( !fHasColumn1 || !fHasColumn2 )
  2631. {
  2632. return fHasColumn1 ? -1:+1;
  2633. }
  2634. else
  2635. {
  2636. TraceAssert(pColumn1->iPropertyType == pColumn2->iPropertyType);
  2637. switch ( pColumn1->iPropertyType )
  2638. {
  2639. case PROPERTY_ISUNDEFINED:
  2640. break;
  2641. case PROPERTY_ISUNKNOWN:
  2642. case PROPERTY_ISSTRING:
  2643. return StrCmpI(pColumn1->pszText, pColumn2->pszText);
  2644. case PROPERTY_ISBOOL:
  2645. case PROPERTY_ISNUMBER:
  2646. return pColumn1->iValue - pColumn2->iValue;
  2647. }
  2648. }
  2649. }
  2650. return 0;
  2651. }
  2652. VOID CDsQuery::_SortResults(INT iColumn)
  2653. {
  2654. DECLAREWAITCURSOR;
  2655. TraceEnter(TRACE_VIEW, "CDsQuery::_SortResults");
  2656. Trace(TEXT("iColumn %d"), iColumn);
  2657. if ( (iColumn >= 0) && (iColumn < DSA_GetItemCount(_hdsaColumns)) )
  2658. {
  2659. // if we have already hit the column then lets invert the sort order,
  2660. // there is no indicator to worry about so this should just work out
  2661. // fine. if we haven't used this column before then default oto
  2662. // ascending, then do the sort!
  2663. //
  2664. // ensure that the focused item is visible when the sort has completed
  2665. if ( _iSortColumn == iColumn )
  2666. _fSortDescending = !_fSortDescending;
  2667. else
  2668. _fSortDescending = FALSE;
  2669. _iSortColumn = iColumn;
  2670. Trace(TEXT("Sorting on column %d, %s"),
  2671. _iSortColumn, _fSortDescending ? TEXT("(descending)"):TEXT("(ascending)"));
  2672. SetWaitCursor();
  2673. ListView_SortItems(_hwndView, _ResultSortCB, MAKELPARAM(_iSortColumn, _fSortDescending));
  2674. ResetWaitCursor();
  2675. }
  2676. TraceLeave();
  2677. }
  2678. /*-----------------------------------------------------------------------------
  2679. / CDsQuery::_ShowBanner
  2680. / --------------------
  2681. / Show the views banner, including sizing it to obscure only the top section
  2682. / of the window.
  2683. /
  2684. / In:
  2685. / uFlags = flags to combine when calling SetWindowPos
  2686. / idPrompt = resource ID of prompt text ot be displayed
  2687. /
  2688. / Out:
  2689. / -
  2690. /----------------------------------------------------------------------------*/
  2691. VOID CDsQuery::_ShowBanner(UINT uFlags, UINT idPrompt)
  2692. {
  2693. HRESULT hres;
  2694. WINDOWPOS wpos;
  2695. RECT rcClient;
  2696. HD_LAYOUT hdl;
  2697. TCHAR szBuffer[MAX_PATH];
  2698. TraceEnter(TRACE_VIEW, "CDsQuery::_ShowBanner");
  2699. // if we have a resource id then lets load the string and
  2700. // set the window text to have it
  2701. if ( idPrompt )
  2702. {
  2703. LoadString(GLOBAL_HINSTANCE, idPrompt, szBuffer, ARRAYSIZE(szBuffer));
  2704. SetWindowText(_hwndBanner, szBuffer);
  2705. }
  2706. // now position the window back to real location, this we need to
  2707. // talk to the listview/header control to work out exactly where it
  2708. // should be living
  2709. GetClientRect(_hwndView, &rcClient);
  2710. if ( (GetWindowLong(_hwndView, GWL_STYLE) & LVS_TYPEMASK) == LVS_REPORT )
  2711. {
  2712. TraceMsg("Calling header for layout information");
  2713. wpos.hwnd = ListView_GetHeader(_hwndView);
  2714. wpos.hwndInsertAfter = NULL;
  2715. wpos.x = 0;
  2716. wpos.y = 0;
  2717. wpos.cx = rcClient.right;
  2718. wpos.cy = rcClient.bottom;
  2719. wpos.flags = SWP_NOZORDER;
  2720. hdl.prc = &rcClient;
  2721. hdl.pwpos = &wpos;
  2722. if ( !Header_Layout(wpos.hwnd, &hdl) )
  2723. ExitGracefully(hres, E_FAIL, "Failed to get the layout information (HDM_LAYOUT)");
  2724. }
  2725. SetWindowPos(_hwndBanner,
  2726. HWND_TOP,
  2727. rcClient.left, rcClient.top,
  2728. rcClient.right - rcClient.left, 100,
  2729. uFlags);
  2730. exit_gracefully:
  2731. TraceLeave();
  2732. }
  2733. /*-----------------------------------------------------------------------------
  2734. / CDsQuery::_InitViewMenuItems
  2735. / ---------------------------
  2736. / Setup the view menu based on the given view mode and the filter state, enabled
  2737. / disable the items as required.
  2738. /
  2739. / In:
  2740. / hMenu = menu to set the menu items on
  2741. /
  2742. / Out:
  2743. / -
  2744. /----------------------------------------------------------------------------*/
  2745. VOID CDsQuery::_InitViewMenuItems(HMENU hMenu)
  2746. {
  2747. MENUITEMINFO mii;
  2748. HMENU hArrangeMenu;
  2749. BOOL fHaveView = IsWindow(_hwndView);
  2750. INT i;
  2751. TraceEnter(TRACE_HANDLER|TRACE_VIEW, "CDsQuery::_InitViewMenuItems");
  2752. CheckMenuItem(hMenu, DSQH_VIEW_FILTER, MF_BYCOMMAND| (_fFilter ? MF_CHECKED:0) );
  2753. ENABLE_MENU_ITEM(hMenu, DSQH_VIEW_FILTER, fHaveView && _fFilterSupported && (_idViewMode == DSQH_VIEW_DETAILS));
  2754. CheckMenuRadioItem(hMenu, DSQH_VIEW_LARGEICONS, DSQH_VIEW_DETAILS, _idViewMode, MF_BYCOMMAND);
  2755. // construct the arrange menu, add it to the view menu that we have been given.
  2756. hArrangeMenu = CreatePopupMenu();
  2757. TraceAssert(hArrangeMenu);
  2758. if ( _hdsaColumns && DSA_GetItemCount(_hdsaColumns) )
  2759. {
  2760. TCHAR szFmt[32];
  2761. TCHAR szBuffer[MAX_PATH];
  2762. hArrangeMenu = CreatePopupMenu();
  2763. TraceAssert(hArrangeMenu);
  2764. LoadString(GLOBAL_HINSTANCE, IDS_ARRANGEBY, szFmt, ARRAYSIZE(szFmt));
  2765. if ( hArrangeMenu )
  2766. {
  2767. for ( i = 0 ; i < DSA_GetItemCount(_hdsaColumns); i++ )
  2768. {
  2769. LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(_hdsaColumns, i);
  2770. TraceAssert(pColumn);
  2771. wsprintf(szBuffer, szFmt, pColumn->pHeading);
  2772. InsertMenu(hArrangeMenu, i, MF_STRING|MF_BYPOSITION, DSQH_VIEW_ARRANGEFIRST+i, szBuffer);
  2773. }
  2774. }
  2775. }
  2776. // now place the arrange menu into the view and ungrey as required.
  2777. ZeroMemory(&mii, SIZEOF(mii));
  2778. mii.cbSize = SIZEOF(mii);
  2779. mii.fMask = MIIM_SUBMENU|MIIM_ID;
  2780. mii.hSubMenu = hArrangeMenu;
  2781. mii.wID = DSQH_VIEW_ARRANGEICONS;
  2782. if ( SetMenuItemInfo(hMenu, DSQH_VIEW_ARRANGEICONS, FALSE, &mii) )
  2783. {
  2784. ENABLE_MENU_ITEM(hMenu, DSQH_VIEW_ARRANGEICONS, fHaveView && GetMenuItemCount(hArrangeMenu));
  2785. hArrangeMenu = NULL;
  2786. }
  2787. if ( hArrangeMenu )
  2788. DestroyMenu(hArrangeMenu);
  2789. TraceLeave();
  2790. }
  2791. /*-----------------------------------------------------------------------------
  2792. / CDsQuery::_GetQueryFormKey
  2793. / --------------------------
  2794. / Given the CLSID for the query form we are interested in, get the
  2795. / forms key for it, note that these settings are stored per-user.
  2796. /
  2797. / In:
  2798. / clsidForm = CLSID of form to pick up
  2799. / phKey -> recevies the HKEY of the form we are intersted in.
  2800. /
  2801. / Out:
  2802. / HRESULT
  2803. /----------------------------------------------------------------------------*/
  2804. HRESULT CDsQuery::_GetQueryFormKey(REFCLSID clsidForm, HKEY* phKey)
  2805. {
  2806. HRESULT hres;
  2807. TCHAR szGUID[GUIDSTR_MAX];
  2808. TCHAR szBuffer[MAX_PATH];
  2809. TraceEnter(TRACE_VIEW, "CDsQuery::_GetQueryFormKey");
  2810. GetStringFromGUID(clsidForm, szGUID, ARRAYSIZE(szGUID));
  2811. wsprintf(szBuffer, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Directory UI\\QueryForms\\%s"), szGUID);
  2812. Trace(TEXT("Settings key is: %s"), szBuffer);
  2813. if ( ERROR_SUCCESS != RegCreateKey(HKEY_CURRENT_USER, szBuffer, phKey) )
  2814. ExitGracefully(hres, E_FAIL, "Failed to open settings key");
  2815. hres = S_OK;
  2816. exit_gracefully:
  2817. TraceLeaveResult(hres);
  2818. }
  2819. /*-----------------------------------------------------------------------------
  2820. / CDsQuery::_GetColumnTable
  2821. / ------------------------
  2822. / Build the column table for the view we are about to display. The column
  2823. / table is constructed from either the query parameters or the persisted
  2824. / column settings stored in the registry.
  2825. /
  2826. / In:
  2827. / clsidForm = clisd of the form to be used
  2828. / pDsQueryParams -> query parameter structure to be used
  2829. / pHDSA -> DSA to recieve the column table (sorted by visible order)
  2830. / fSetInView = Set the columns into the view
  2831. /
  2832. / Out:
  2833. / HRESULT
  2834. /----------------------------------------------------------------------------*/
  2835. HRESULT CDsQuery::_GetColumnTable(REFCLSID clsidForm, LPDSQUERYPARAMS pDsQueryParams, HDSA* pHDSA, BOOL fSetInView)
  2836. {
  2837. HRESULT hres;
  2838. HKEY hKey = NULL;
  2839. BOOL fDefaultSettings = TRUE;
  2840. LPSAVEDCOLUMN aSavedColumn = NULL;
  2841. LPTSTR pSettingsValue = VIEW_SETTINGS_VALUE;
  2842. BOOL fHaveSizeInfo = FALSE;
  2843. LPWSTR pProperty;
  2844. DWORD dwType, cbSize;
  2845. LV_COLUMN lvc;
  2846. SIZE sz;
  2847. INT i, iNewColumn;
  2848. HD_ITEM hdi;
  2849. IDsDisplaySpecifier *pdds = NULL;
  2850. USES_CONVERSION;
  2851. TraceEnter(TRACE_VIEW, "CDsQuery::_GetColumnTable");
  2852. TraceGUID("clsidForm ", clsidForm);
  2853. Trace(TEXT("pDsQueryParams %08x, pHDSA %08x"), pDsQueryParams, pHDSA);
  2854. DECLAREWAITCURSOR;
  2855. SetWaitCursor();
  2856. if ( !pHDSA )
  2857. ExitGracefully(hres, E_INVALIDARG, "Bad pDsQueryParams / pHDSA");
  2858. // construct the column DSA then attempt to look up the view settings stored in
  2859. // the registry for the current form.
  2860. *pHDSA = DSA_Create(SIZEOF(COLUMN), 16);
  2861. TraceAssert(*pHDSA);
  2862. if ( !*pHDSA )
  2863. ExitGracefully(hres, E_OUTOFMEMORY, "Failed to construct the column DSA");
  2864. // If invoked for admin UI then we look at the admin view settings, this way
  2865. // the admin can have one set of columns, and the user can have another.
  2866. if ( _dwFlags & DSQPF_ENABLEADMINFEATURES )
  2867. pSettingsValue = ADMIN_VIEW_SETTINGS_VALUE;
  2868. Trace(TEXT("View settings value: %s"), pSettingsValue);
  2869. if ( SUCCEEDED(_GetQueryFormKey(clsidForm, &hKey)) )
  2870. {
  2871. // we have the handle to the forms sub-key table, now lets check and
  2872. // see what size the view settings stream is.
  2873. if ( (ERROR_SUCCESS == RegQueryValueEx(hKey, pSettingsValue, NULL, &dwType, NULL, &cbSize)) &&
  2874. (dwType == REG_BINARY) &&
  2875. (cbSize > SIZEOF(SAVEDCOLUMN)) )
  2876. {
  2877. Trace(TEXT("Reading view settings from registry (size %d)"), cbSize);
  2878. aSavedColumn = (LPSAVEDCOLUMN)LocalAlloc(LPTR, cbSize);
  2879. TraceAssert(aSavedColumn);
  2880. if ( aSavedColumn &&
  2881. (ERROR_SUCCESS == RegQueryValueEx(hKey, pSettingsValue, NULL, NULL, (LPBYTE)aSavedColumn, &cbSize)) )
  2882. {
  2883. // compute the size of the table from the values that we have
  2884. // read from the registry and now lets allocate a table for it
  2885. for ( i = 0; aSavedColumn[i].cbSize; i++ )
  2886. {
  2887. COLUMN column = { 0 };
  2888. LPCWSTR pProperty = (LPCWSTR)ByteOffset(aSavedColumn, aSavedColumn[i].offsetProperty);
  2889. LPCTSTR pHeading = (LPCTSTR)ByteOffset(aSavedColumn, aSavedColumn[i].offsetHeading);
  2890. hres = LocalAllocStringW(&column.pProperty, pProperty);
  2891. FailGracefully(hres, "Failed to allocate property name");
  2892. hres = LocalAllocString(&column.pHeading, pHeading);
  2893. FailGracefully(hres, "Failed to allocate column heading");
  2894. //column.fHasColumnProvider = FALSE;
  2895. column.cx = aSavedColumn[i].cx;
  2896. column.fmt = aSavedColumn[i].fmt;
  2897. column.iPropertyType = PROPERTY_ISUNKNOWN;
  2898. //column.idOperator = 0;
  2899. //column.clsidColumnHandler = { 0 };
  2900. //column.pColumnHandler = NULL;
  2901. ZeroMemory(&column.filter, SIZEOF(column.filter));
  2902. column.filter.iPropertyType = PROPERTY_ISUNDEFINED;
  2903. Trace(TEXT("pProperty: '%s', pHeading: '%s', cx %d, fmt %08x"),
  2904. W2T(column.pProperty), column.pHeading, column.cx, column.fmt);
  2905. if ( -1 == DSA_AppendItem(*pHDSA, &column) )
  2906. ExitGracefully(hres, E_OUTOFMEMORY, "Failed to add column to the DSA");
  2907. }
  2908. fDefaultSettings = FALSE; // success we have a table
  2909. }
  2910. }
  2911. }
  2912. if ( fDefaultSettings )
  2913. {
  2914. // unable to read the settings from the registy, therefore defaulting to
  2915. // those defined in the query parameters block.
  2916. if ( !pDsQueryParams )
  2917. ExitGracefully(hres, E_INVALIDARG, "No DSQUERYPARAMs to default using");
  2918. for ( i = 0 ; i < pDsQueryParams->iColumns; i++ )
  2919. {
  2920. COLUMN column = { 0 };
  2921. switch ( pDsQueryParams->aColumns[i].offsetProperty )
  2922. {
  2923. case DSCOLUMNPROP_ADSPATH:
  2924. pProperty = c_szADsPathCH;
  2925. break;
  2926. case DSCOLUMNPROP_OBJECTCLASS:
  2927. pProperty = c_szObjectClassCH;
  2928. break;
  2929. default:
  2930. pProperty = (LPWSTR)ByteOffset(pDsQueryParams, pDsQueryParams->aColumns[i].offsetProperty);
  2931. break;
  2932. }
  2933. hres = LocalAllocStringW(&column.pProperty, pProperty);
  2934. FailGracefully(hres, "Failed to allocate property name");
  2935. hres = FormatMsgResource(&column.pHeading, pDsQueryParams->hInstance, pDsQueryParams->aColumns[i].idsName);
  2936. FailGracefully(hres, "Failed to allocate column heading");
  2937. //column.fHasColumnProvider = FALSE;
  2938. column.cx = pDsQueryParams->aColumns[i].cx;
  2939. column.fmt = pDsQueryParams->aColumns[i].fmt;
  2940. column.iPropertyType = PROPERTY_ISUNKNOWN;
  2941. //column.idOperator = 0;
  2942. //column.clsidColumnHandler = { 0 };
  2943. //column.pColumnHandler = NULL;
  2944. ZeroMemory(&column.filter, SIZEOF(column.filter));
  2945. column.filter.iPropertyType = PROPERTY_ISUNDEFINED;
  2946. // Now fix the width of the column we are about to specify, this
  2947. // value has the following meaning:
  2948. //
  2949. // == 0 => use default width
  2950. // > 0 => user 'n' magic characters
  2951. // < 0 => pixel width
  2952. // FEATURE: -ve should be % of the view
  2953. if ( column.cx < 0 )
  2954. {
  2955. TraceMsg("Column width specified in pixels");
  2956. column.cx = -column.cx;
  2957. }
  2958. else
  2959. {
  2960. // Default the size if it is == 0, then having done this
  2961. // lets grab the font we want to use before moving on
  2962. // to create a DC and measure the character we need.
  2963. if ( !column.cx )
  2964. column.cx = DEFAULT_WIDTH;
  2965. sz.cx = 10; // random default value.
  2966. if ( !fHaveSizeInfo )
  2967. {
  2968. HDC hDC;
  2969. LOGFONT lf;
  2970. HFONT hFont, hOldFont;
  2971. SystemParametersInfo(SPI_GETICONTITLELOGFONT, SIZEOF(lf), &lf, FALSE);
  2972. hFont = CreateFontIndirect(&lf); // icon title font
  2973. if (hFont)
  2974. {
  2975. hDC = CreateCompatibleDC(NULL); // screen compatible DC
  2976. if (hDC)
  2977. {
  2978. hOldFont = (HFONT)SelectObject(hDC, hFont);
  2979. GetTextExtentPoint(hDC, TEXT("0"), 1, &sz);
  2980. SelectObject(hDC, hOldFont);
  2981. DeleteDC(hDC);
  2982. }
  2983. DeleteFont(hFont);
  2984. }
  2985. fHaveSizeInfo = TRUE;
  2986. }
  2987. column.cx = column.cx*sz.cx; // n chars width
  2988. }
  2989. Trace(TEXT("pProperty: '%s', pHeading: '%s', cx %d, fmt %08x"),
  2990. W2T(column.pProperty), column.pHeading, column.cx, column.fmt);
  2991. if ( -1 == DSA_AppendItem(*pHDSA, &column) )
  2992. ExitGracefully(hres, E_OUTOFMEMORY, "Failed to add column to the DSA");
  2993. }
  2994. }
  2995. // Scan the column list getting both the property name and the CLSID for the
  2996. // column handler (if there is one).
  2997. hres = CoCreateInstance(CLSID_DsDisplaySpecifier, NULL, CLSCTX_INPROC_SERVER, IID_IDsDisplaySpecifier, (void **)&pdds);
  2998. FailGracefully(hres, "Failed to get the IDsDisplaySpecifier interface");
  2999. if ( _dwFlags & DSQPF_HASCREDENTIALS )
  3000. {
  3001. hres = pdds->SetServer(_pServer, _pUserName, _pPassword, DSSSF_DSAVAILABLE);
  3002. FailGracefully(hres, "Failed to server information");
  3003. }
  3004. for ( i = 0 ; i < DSA_GetItemCount(*pHDSA) ; i++ )
  3005. {
  3006. LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(*pHDSA, i);
  3007. TraceAssert(pColumn);
  3008. Trace(TEXT("Property for column %d, %s"), i, W2T(pColumn->pProperty));
  3009. // lets get the property type, column handler and the default operator for it.
  3010. hres = GetColumnHandlerFromProperty(pColumn, NULL);
  3011. FailGracefully(hres, "Failed to get the column handler from property string");
  3012. if ( pColumn->fHasColumnHandler )
  3013. {
  3014. TraceMsg("Has a column handler, therefore property is now a string");
  3015. pColumn->iPropertyType = PROPERTY_ISSTRING;
  3016. }
  3017. else
  3018. {
  3019. pColumn->iPropertyType = PropertyIsFromAttribute(pColumn->pProperty, pdds);
  3020. }
  3021. pColumn->idOperator = property_type_table[pColumn->iPropertyType].idOperator;
  3022. }
  3023. // Set the columns up in the view (remove all items first) to allow us
  3024. // to add/remove columns as required.
  3025. if ( fSetInView )
  3026. {
  3027. for ( i = Header_GetItemCount(ListView_GetHeader(_hwndView)); --i >= 0 ; )
  3028. ListView_DeleteColumn(_hwndView, i);
  3029. // add the columns to the view, then having done that set
  3030. // the type of the filter to reflect the property being
  3031. // shown.
  3032. for ( i = 0 ; i < DSA_GetItemCount(_hdsaColumns); i++ )
  3033. {
  3034. LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(_hdsaColumns, i);
  3035. TraceAssert(pColumn);
  3036. lvc.mask = LVCF_TEXT|LVCF_WIDTH|LVCF_FMT;
  3037. lvc.fmt = pColumn->fmt;
  3038. lvc.cx = pColumn->cx;
  3039. lvc.pszText = pColumn->pHeading;
  3040. iNewColumn = ListView_InsertColumn(_hwndView, i, &lvc);
  3041. TraceAssert(iNewColumn != -1);
  3042. if ( iNewColumn != i )
  3043. ExitGracefully(hres, E_FAIL, "Failed to add the column to the view");
  3044. hdi.mask = HDI_FILTER;
  3045. hdi.type = property_type_table[pColumn->iPropertyType].hdft|HDFT_HASNOVALUE;
  3046. hdi.pvFilter = NULL;
  3047. Trace(TEXT("iPropertyType %d, hdi.type %08x"), pColumn->iPropertyType, hdi.type);
  3048. if ( !Header_SetItem(ListView_GetHeader(_hwndView), iNewColumn, &hdi) )
  3049. ExitGracefully(hres, E_FAIL, "Failed to set the filter type into the view");
  3050. }
  3051. }
  3052. hres = S_OK; // success
  3053. exit_gracefully:
  3054. if ( hKey )
  3055. RegCloseKey(hKey);
  3056. if ( aSavedColumn )
  3057. LocalFree((HLOCAL)aSavedColumn);
  3058. DoRelease(pdds);
  3059. ResetWaitCursor();
  3060. TraceLeaveResult(hres);
  3061. }
  3062. /*-----------------------------------------------------------------------------
  3063. / CDsQuery::_SaveColumnTable
  3064. / -------------------------
  3065. / Free the column table stored in a DSA. This code released all the
  3066. / allocated memory stored with the table.
  3067. /
  3068. / In:
  3069. / Out:
  3070. / -
  3071. /----------------------------------------------------------------------------*/
  3072. VOID CDsQuery::_SaveColumnTable(VOID)
  3073. {
  3074. TraceEnter(TRACE_VIEW, "CDsQuery::_SaveColumnTable");
  3075. _FreeResults();
  3076. if ( _hdsaColumns )
  3077. {
  3078. DSA_DestroyCallback(_hdsaColumns, FreeColumnCB, NULL);
  3079. _hdsaColumns = NULL;
  3080. }
  3081. _iSortColumn = -1;
  3082. _fSortDescending = FALSE;
  3083. TraceLeave();
  3084. }
  3085. /*-----------------------------------------------------------------------------
  3086. / CDsQuery::_SaveColumnTable
  3087. / -------------------------
  3088. / Save the current column table from the DPA into the registry so
  3089. / we can restore it the next time the user uses this query form.
  3090. /
  3091. / In:
  3092. / clsidForm = form ID to store it under
  3093. / hdsaColumns -> DSA to destory
  3094. /
  3095. / Out:
  3096. / HRESULT
  3097. /----------------------------------------------------------------------------*/
  3098. HRESULT CDsQuery::_SaveColumnTable(REFCLSID clsidForm, HDSA hdsaColumns)
  3099. {
  3100. HRESULT hres;
  3101. LPWSTR pProperty;
  3102. LPSAVEDCOLUMN aSavedColumn = NULL;
  3103. DWORD cbData, offset;
  3104. HKEY hKey = NULL;
  3105. LPTSTR pSettingsValue = VIEW_SETTINGS_VALUE;
  3106. INT i;
  3107. USES_CONVERSION;
  3108. TraceEnter(TRACE_VIEW, "CDsQuery::_SaveColumnTable");
  3109. TraceGUID("clsidForm ", clsidForm);
  3110. if ( !hdsaColumns )
  3111. ExitGracefully(hres, E_FAIL, "No column data to save");
  3112. // first compute the size of the blob we are going to store into
  3113. // the registry, whilst doing this compute the offset to
  3114. // the start of the string data.
  3115. offset = SIZEOF(SAVEDCOLUMN);
  3116. cbData = SIZEOF(SAVEDCOLUMN);
  3117. for ( i = 0 ; i < DSA_GetItemCount(hdsaColumns); i++ )
  3118. {
  3119. LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(hdsaColumns, i);
  3120. TraceAssert(pColumn);
  3121. offset += SIZEOF(SAVEDCOLUMN);
  3122. cbData += SIZEOF(SAVEDCOLUMN);
  3123. cbData += StringByteSizeW(pColumn->pProperty);
  3124. // this is a potential problem, must be kept in sync with GetPropertyFromColumn
  3125. if ( pColumn->fHasColumnHandler )
  3126. cbData += SIZEOF(WCHAR)*(GUIDSTR_MAX + 1); // nb+1 for seperator (,} + string is UNICODE
  3127. cbData += StringByteSize(pColumn->pHeading);
  3128. }
  3129. Trace(TEXT("offset %d, cbData %d"), offset, cbData);
  3130. // Allocate the structure and lets place all the data into it,
  3131. // again running over the data blocks, append the string data
  3132. // to the end of the blob in a single go.
  3133. aSavedColumn = (LPSAVEDCOLUMN)LocalAlloc(LPTR, cbData);
  3134. TraceAssert(aSavedColumn);
  3135. if ( !aSavedColumn )
  3136. ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate settings data");
  3137. Trace(TEXT("Building data blob at %08x"), aSavedColumn);
  3138. for ( i = 0 ; i < DSA_GetItemCount(hdsaColumns); i++ )
  3139. {
  3140. LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(hdsaColumns, i);
  3141. TraceAssert(pColumn);
  3142. hres = GetPropertyFromColumn(&pProperty, pColumn);
  3143. FailGracefully(hres, "Failed to allocate property from column");
  3144. aSavedColumn[i].cbSize = SIZEOF(SAVEDCOLUMN);
  3145. aSavedColumn[i].dwFlags = 0;
  3146. aSavedColumn[i].offsetProperty = offset;
  3147. aSavedColumn[i].offsetHeading = offset + StringByteSizeW(pProperty);
  3148. aSavedColumn[i].cx = pColumn->cx;
  3149. aSavedColumn[i].fmt = pColumn->fmt;
  3150. StringByteCopyW(aSavedColumn, aSavedColumn[i].offsetProperty, pProperty);
  3151. offset += StringByteSizeW(pProperty);
  3152. StringByteCopy(aSavedColumn, aSavedColumn[i].offsetHeading, pColumn->pHeading);
  3153. offset += StringByteSize(pColumn->pHeading);
  3154. LocalFreeStringW(&pProperty);
  3155. }
  3156. aSavedColumn[i].cbSize = 0; // terminate the list of columns with a NULL
  3157. Trace(TEXT("offset %d, cbData %d"), offset, cbData);
  3158. TraceAssert(offset == cbData);
  3159. // now put the data into the registry filed under the key for the query for that
  3160. // it maps to.
  3161. hres = _GetQueryFormKey(clsidForm, &hKey);
  3162. FailGracefully(hres, "Failed to get settings sub-key");
  3163. // If invoked for admin UI then we look at the admin view settings, this way
  3164. // the admin can have one set of columns, and the user can have another.
  3165. if ( _dwFlags & DSQPF_ENABLEADMINFEATURES )
  3166. pSettingsValue = ADMIN_VIEW_SETTINGS_VALUE;
  3167. Trace(TEXT("View settings value: %s"), pSettingsValue);
  3168. if ( ERROR_SUCCESS != RegSetValueEx(hKey, pSettingsValue, 0, REG_BINARY, (LPBYTE)aSavedColumn, cbData) )
  3169. ExitGracefully(hres, E_FAIL, "Failed to write setting into the view");
  3170. hres = S_OK;
  3171. exit_gracefully:
  3172. if ( aSavedColumn )
  3173. LocalFree((HLOCAL)aSavedColumn);
  3174. if ( hKey )
  3175. RegCloseKey(hKey);
  3176. TraceLeaveResult(hres);
  3177. }
  3178. // retrieve the context menu object
  3179. HRESULT _FolderCFMCallback(LPSHELLFOLDER psf, HWND hwndView, LPDATAOBJECT pDataObject, UINT uMsg, WPARAM wParam, LPARAM lParam)
  3180. {
  3181. Trace(TEXT("uMsg %d, wParam %08x, lParam %08x"), uMsg, wParam, lParam);
  3182. switch ( uMsg )
  3183. {
  3184. case DFM_MERGECONTEXTMENU:
  3185. return S_OK;
  3186. case DFM_INVOKECOMMAND:
  3187. switch (wParam)
  3188. {
  3189. case (WPARAM)DFM_CMD_PROPERTIES:
  3190. TraceMsg("ShowProperties");
  3191. return ShowObjectProperties(hwndView, pDataObject);
  3192. default:
  3193. return S_FALSE;
  3194. }
  3195. break;
  3196. case DFM_GETDEFSTATICID:
  3197. *((WPARAM*)lParam) = (WPARAM)DFM_CMD_PROPERTIES;
  3198. return NOERROR;
  3199. }
  3200. return E_NOTIMPL;
  3201. }
  3202. HRESULT CDsQuery::_GetContextMenu()
  3203. {
  3204. ITEMIDLIST idl = {0};
  3205. LPITEMIDLIST *aidl = NULL;
  3206. HKEY aKeys[UIKEY_MAX] = { 0 };
  3207. int cItems = ListView_GetSelectedCount(_hwndView);
  3208. if (cItems != 0)
  3209. {
  3210. // try to get the selected item (there is one, so first we try the focused, then the selected)
  3211. int i = ListView_GetNextItem(_hwndView, -1, LVNI_FOCUSED|LVNI_SELECTED);
  3212. if (i == -1)
  3213. i = ListView_GetNextItem(_hwndView, -1, LVNI_SELECTED);
  3214. // given the item lets get the result object so that we can create the menu based on
  3215. // the right class information
  3216. LV_ITEM lvi = { 0 };
  3217. lvi.mask = LVIF_PARAM;
  3218. lvi.iItem = i;
  3219. if (ListView_GetItem(_hwndView, &lvi))
  3220. {
  3221. LPQUERYRESULT pResult = (LPQUERYRESULT)lvi.lParam;
  3222. GetKeysForClass(pResult->pObjectClass, pResult->fIsContainer, ARRAYSIZE(aKeys), aKeys);
  3223. }
  3224. aidl = (LPITEMIDLIST*)LocalAlloc(LPTR, sizeof(LPITEMIDLIST)*cItems);
  3225. }
  3226. // given that we probably have the keys we are interested in,
  3227. // lets now create the context menu.
  3228. DoRelease(_pcm); // release the previous context menu
  3229. HRESULT hr = E_OUTOFMEMORY;
  3230. if (!cItems|| (cItems && aidl))
  3231. {
  3232. // fake up the array of IDLISTs so that they can be cloned correctly from
  3233. // the view.
  3234. for (int i= 0 ; i < cItems ; i++)
  3235. aidl[i] = &idl;
  3236. hr = CDefFolderMenu_Create2(&idl, _hwnd,
  3237. cItems, (LPCITEMIDLIST*)aidl,
  3238. this,
  3239. _FolderCFMCallback,
  3240. ARRAYSIZE(aKeys), aKeys,
  3241. &_pcm);
  3242. }
  3243. if (aidl)
  3244. LocalFree(aidl);
  3245. if (FAILED(hr))
  3246. TidyKeys(ARRAYSIZE(aKeys), aKeys);
  3247. return hr;
  3248. }
  3249. /*-----------------------------------------------------------------------------
  3250. / CDsQuery::_GetContextMenuVerbs
  3251. / -----------------------------
  3252. / Do the query context menu handling the case where we have been invoked
  3253. / modally.
  3254. /
  3255. / In:
  3256. / hMenu = menu handle to merge into
  3257. / dwFlags = flags for QueryContextMenu
  3258. /
  3259. / Out:
  3260. / VOID
  3261. /----------------------------------------------------------------------------*/
  3262. VOID CDsQuery::_GetContextMenuVerbs(HMENU hMenu, DWORD dwFlags)
  3263. {
  3264. TCHAR szBuffer[MAX_PATH];
  3265. TraceEnter(TRACE_VIEW, "CDsQuery::_GetContextMenuVerbs");
  3266. _pcm->QueryContextMenu(hMenu, 0, DSQH_FILE_CONTEXT_FIRST, DSQH_FILE_CONTEXT_LAST, dwFlags);
  3267. Trace(TEXT("Menu item count after QueryContextMenu %d (%08x)"), GetMenuItemCount(hMenu), hMenu);
  3268. if ( (_dwOQWFlags & OQWF_OKCANCEL) && LoadString(GLOBAL_HINSTANCE, IDS_SELECT, szBuffer, ARRAYSIZE(szBuffer)) )
  3269. {
  3270. InsertMenu(hMenu, 0, MF_BYPOSITION|MF_STRING, DSQH_BG_SELECT, szBuffer);
  3271. InsertMenu(hMenu, 1, MF_BYPOSITION|MF_SEPARATOR, 0, NULL);
  3272. SetMenuDefaultItem(hMenu, DSQH_BG_SELECT, FALSE);
  3273. }
  3274. TraceLeave();
  3275. }
  3276. // get the items in the view into a IDataObject, the caller can request either the
  3277. // selection or all the items
  3278. HRESULT CDsQuery::_AddResultToDataObject(HDSA hdsa, INT i)
  3279. {
  3280. LV_ITEM lvi = { 0 };
  3281. lvi.mask = LVIF_PARAM;
  3282. lvi.iItem = i;
  3283. HRESULT hr = E_FAIL;
  3284. if (ListView_GetItem(_hwndView, &lvi))
  3285. {
  3286. QUERYRESULT *pResult = (QUERYRESULT*)lvi.lParam;
  3287. DATAOBJECTITEM doi = {0};
  3288. // copy the strings for the item into the structure
  3289. hr = LocalAllocStringW(&doi.pszPath, pResult->pPath);
  3290. if (SUCCEEDED(hr) && pResult->pObjectClass)
  3291. hr = LocalAllocStringW(&doi.pszObjectClass, pResult->pObjectClass);
  3292. // copy the rest of the state
  3293. doi.fIsContainer = pResult->fIsContainer;
  3294. // append to the DSA.
  3295. if (SUCCEEDED(hr))
  3296. {
  3297. if (-1 == DSA_AppendItem(hdsa, &doi))
  3298. {
  3299. LocalFreeStringW(&doi.pszPath);
  3300. LocalFreeStringW(&doi.pszObjectClass);
  3301. hr = E_OUTOFMEMORY;
  3302. }
  3303. else
  3304. {
  3305. hr = S_OK;
  3306. }
  3307. }
  3308. }
  3309. return hr;
  3310. }
  3311. HRESULT CDsQuery::_GetDataObjectFromSelection(BOOL fGetAll, IDataObject **ppdo)
  3312. {
  3313. HRESULT hr = S_OK;
  3314. HDSA hdsa = DSA_Create(SIZEOF(DATAOBJECTITEM), 16);
  3315. if (hdsa)
  3316. {
  3317. // focused item always come first (primary selection rulz).
  3318. int iFocused = ListView_GetNextItem(_hwndView, -1, LVNI_ALL|LVNI_SELECTED|LVNI_FOCUSED);
  3319. if (iFocused > -1)
  3320. {
  3321. hr = _AddResultToDataObject(hdsa, iFocused);
  3322. }
  3323. // now walk all the items in the view collecting the selection and adding
  3324. // those items to the DPA - we must skip the item which is focused as we
  3325. // have already added it.
  3326. if (SUCCEEDED(hr))
  3327. {
  3328. int i = -1;
  3329. do
  3330. {
  3331. i = ListView_GetNextItem(_hwndView, i, LVNI_ALL|LVNI_SELECTED);
  3332. if ( (i >= 0) && (i != iFocused))
  3333. {
  3334. hr = _AddResultToDataObject(hdsa, i);
  3335. }
  3336. }
  3337. while (SUCCEEDED(hr) && (i != -1));
  3338. }
  3339. // given the DPA lets build an IDataObject using it.
  3340. hr = CDataObject_CreateInstance(hdsa,
  3341. (_dwFlags & DSQPF_ENABLEADVANCEDFEATURES),
  3342. IID_IDataObject, (void**)ppdo);
  3343. if (SUCCEEDED(hr))
  3344. hr = _SetDispSpecOptions(*ppdo);
  3345. }
  3346. else
  3347. {
  3348. hr = E_OUTOFMEMORY;
  3349. }
  3350. if (FAILED(hr) && hdsa)
  3351. FreeDataObjectDSA(hdsa); // data object failed to construct, lets kill the DSA
  3352. return hr;
  3353. }
  3354. /*-----------------------------------------------------------------------------
  3355. / CDsQuery::_CopyCredentials
  3356. / --------------------------
  3357. / Copy the user name, password and server as required.
  3358. /
  3359. / In:
  3360. / ppszUserName, psszPassword & ppszServer => destinations
  3361. /
  3362. / Out:
  3363. / HRESULT
  3364. /----------------------------------------------------------------------------*/
  3365. HRESULT CDsQuery::_CopyCredentials(LPWSTR *ppszUserName, LPWSTR *ppszPassword, LPWSTR *ppszServer)
  3366. {
  3367. HRESULT hres;
  3368. TraceEnter(TRACE_VIEW, "CDsQuery::_CopyCredentials");
  3369. hres = LocalAllocStringW(ppszUserName, _pUserName);
  3370. FailGracefully(hres, "Failed to copy the user name");
  3371. hres = LocalAllocStringW(ppszPassword, _pPassword);
  3372. FailGracefully(hres, "Failed to copy the password");
  3373. hres = LocalAllocStringW(ppszServer, _pServer);
  3374. FailGracefully(hres, "Failed to copy the server name");
  3375. exit_gracefully:
  3376. TraceLeaveResult(hres);
  3377. }
  3378. /*-----------------------------------------------------------------------------
  3379. / CDsQuery::OnPickColumns
  3380. /----------------------------------------------------------------------------*/
  3381. typedef struct
  3382. {
  3383. LPWSTR pProperty;
  3384. LPTSTR pHeading;
  3385. BOOL fIsColumn; // item is column and added to column list box on init
  3386. INT cx; // pixel width of column
  3387. INT fmt; // formatting information of column
  3388. } PICKERITEM, * LPPICKERITEM;
  3389. typedef struct
  3390. {
  3391. HDPA hdpaItems; // all the items in the view
  3392. HDSA hdsaColumns; // column table generated by this dialog
  3393. HWND hwndProperties; // hwnd's for the columns/property tables
  3394. HWND hwndColumns;
  3395. } PICKERSTATE, * LPPICKERSTATE;
  3396. VOID _PickerMoveColumn(HWND hwndDest, HWND hwndSrc, BOOL fInsert);
  3397. HRESULT _Picker_GetColumnTable(HWND hwndColumns, HDSA* pHDSA);
  3398. INT_PTR _PickerDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  3399. INT _PickerItemFreeCB(LPVOID pData, LPVOID lParam);
  3400. INT _PickerItemCmpCB(LPVOID p1, LPVOID p2, LPARAM lParam);
  3401. //
  3402. // Help ID mappings
  3403. //
  3404. static DWORD const aPickColumnsHelpIDs[] =
  3405. {
  3406. IDC_LBPROPERTIES, IDH_COLUMNS_AVAILABLE,
  3407. IDC_LBCOLUMNS, IDH_COLUMNS_SHOWN,
  3408. IDC_ADD, IDH_ADD_COLUMNS,
  3409. IDC_REMOVE, IDH_REMOVE_COLUMNS,
  3410. 0, 0
  3411. };
  3412. /*-----------------------------------------------------------------------------
  3413. / _PickerMoveColumn
  3414. / -----------------
  3415. / Move the selected item in one list box to another, we assume that the
  3416. / item data points to a PICKERITEM structure, we transfer the selection
  3417. / and
  3418. /
  3419. / In:
  3420. / hwndSrc, hwndState = windows to move selection within
  3421. / fInsert = insert at selection point in destionation, or add the string (sorted)
  3422. /
  3423. / Out:
  3424. / VOID
  3425. /----------------------------------------------------------------------------*/
  3426. VOID _PickerMoveColumn(HWND hwndDest, HWND hwndSrc, BOOL fInsert)
  3427. {
  3428. INT iSelection, i;
  3429. LPPICKERITEM pItem;
  3430. USES_CONVERSION;
  3431. TraceEnter(TRACE_FIELDCHOOSER, "_PickerMoveColumn");
  3432. iSelection = ListBox_GetCurSel(hwndSrc);
  3433. TraceAssert(iSelection >= 0 );
  3434. if ( iSelection >= 0 )
  3435. {
  3436. LPPICKERITEM pItem = (LPPICKERITEM)ListBox_GetItemData(hwndSrc, iSelection);
  3437. TraceAssert(pItem);
  3438. TraceAssert(pItem->pHeading);
  3439. // Add the new item to the view (if this it the properties)
  3440. // then this will result in the list being sorted and select
  3441. // it to allow the user to remove/add another
  3442. if ( fInsert )
  3443. {
  3444. Trace(TEXT("Inserting the item at index %d"), ListBox_GetCurSel(hwndDest)+1);
  3445. i = ListBox_InsertString(hwndDest, ListBox_GetCurSel(hwndDest)+1, pItem->pHeading);
  3446. }
  3447. else
  3448. {
  3449. TraceMsg("Adding string to listbox");
  3450. i = ListBox_AddString(hwndDest, pItem->pHeading);
  3451. }
  3452. TraceAssert(i != -1);
  3453. ListBox_SetItemData(hwndDest, i, (LPARAM)pItem);
  3454. ListBox_SetCurSel(hwndDest, i);
  3455. // remove the item from the source, ensuring that the
  3456. // selection stays visually at the same place (nb
  3457. // cope with removing the last item).
  3458. ListBox_DeleteString(hwndSrc, iSelection);
  3459. if ( iSelection >= ListBox_GetCount(hwndSrc) )
  3460. iSelection = ListBox_GetCount(hwndSrc)-1;
  3461. if ( iSelection >= 0 )
  3462. ListBox_SetCurSel(hwndSrc, iSelection);
  3463. }
  3464. TraceLeave();
  3465. }
  3466. /*-----------------------------------------------------------------------------
  3467. / _Picker_GetColumnTable
  3468. / ---------------------
  3469. / The user has hit OK, therefore we must build a new column table, this
  3470. / code walks the items in the columns ListBox and generates the
  3471. / column DSA that we should be using.
  3472. /
  3473. / In:
  3474. / hwndColumns -> ListBox containing the columns
  3475. / pHDSA -> DSA to receive the columnt able
  3476. /
  3477. / Out:
  3478. / HRESULT
  3479. /----------------------------------------------------------------------------*/
  3480. HRESULT _Picker_GetColumnTable(HWND hwndColumns, HDSA* pHDSA)
  3481. {
  3482. HRESULT hres;
  3483. HDSA hdsaColumns = NULL;
  3484. INT i;
  3485. INT cxColumn = 0;
  3486. TraceEnter(TRACE_FIELDCHOOSER, "_Picker_GetColumnTable");
  3487. // Construct a DSA to store the column table in
  3488. hdsaColumns = DSA_Create(SIZEOF(COLUMN), 4);
  3489. TraceAssert(hdsaColumns);
  3490. if ( !hdsaColumns )
  3491. ExitGracefully(hres, E_OUTOFMEMORY, "Failed to create the column DSA");
  3492. // For each entry in the ListBox add an item to the DSA that contains
  3493. // the relevant column information
  3494. for ( i = 0 ; i < ListBox_GetCount(hwndColumns) ; i++ )
  3495. {
  3496. COLUMN column = { 0 };
  3497. LPPICKERITEM pItem = (LPPICKERITEM)ListBox_GetItemData(hwndColumns, i);
  3498. TraceAssert(pItem);
  3499. // column.fHasColumnHandler = FALSE;
  3500. column.pProperty = NULL;
  3501. column.pHeading = NULL;
  3502. //column.cx = 0;
  3503. //column.fmt = 0;
  3504. column.iPropertyType = PROPERTY_ISUNKNOWN;
  3505. column.idOperator = 0;
  3506. // column.clsidColumnHandler = { 0 };
  3507. // column.pColumnHandler = NULL;
  3508. // fIsColumn indicates that the entry was originally a column therefore
  3509. // has extra state information, ensure that we copy this over, otherwise
  3510. // just pick sensible defaults. Having done that we can allocate the
  3511. // strings, add the item and continue...
  3512. if ( pItem->fIsColumn )
  3513. {
  3514. column.cx = pItem->cx;
  3515. column.fmt = pItem->fmt;
  3516. }
  3517. else
  3518. {
  3519. // Have we cached the column width yet? If not then lets do
  3520. // so and apply it to all the columns which are using the
  3521. // default width (as they have not yet been sized)
  3522. if ( !cxColumn )
  3523. {
  3524. HDC hDC;
  3525. LOGFONT lf;
  3526. HFONT hFont, hOldFont;
  3527. SIZE sz;
  3528. sz.cx = 10; // random default value.
  3529. SystemParametersInfo(SPI_GETICONTITLELOGFONT, SIZEOF(lf), &lf, FALSE);
  3530. hFont = CreateFontIndirect(&lf); // icon title font
  3531. if (hFont)
  3532. {
  3533. hDC = CreateCompatibleDC(NULL); // screen compatible DC
  3534. if (hDC)
  3535. {
  3536. hOldFont = (HFONT)SelectObject(hDC, hFont);
  3537. GetTextExtentPoint(hDC, TEXT("0"), 1, &sz);
  3538. SelectObject(hDC, hOldFont);
  3539. DeleteDC(hDC);
  3540. }
  3541. DeleteObject(hFont);
  3542. }
  3543. cxColumn = DEFAULT_WIDTH * sz.cx;
  3544. }
  3545. column.cx = cxColumn;
  3546. column.fmt = 0;
  3547. }
  3548. if ( FAILED(GetColumnHandlerFromProperty(&column, pItem->pProperty)) ||
  3549. FAILED(LocalAllocString(&column.pHeading, pItem->pHeading)) ||
  3550. (-1 == DSA_AppendItem(hdsaColumns, &column)) )
  3551. {
  3552. LocalFreeStringW(&column.pProperty);
  3553. LocalFreeString(&column.pHeading);
  3554. ExitGracefully(hres, E_OUTOFMEMORY, "Failed to construct the column entry");
  3555. }
  3556. }
  3557. Trace(TEXT("New column table contains %d items"), DSA_GetItemCount(hdsaColumns));
  3558. hres = S_OK;
  3559. exit_gracefully:
  3560. if ( FAILED(hres) && hdsaColumns )
  3561. {
  3562. DSA_DestroyCallback(hdsaColumns, FreeColumnCB, NULL);
  3563. hdsaColumns = NULL;
  3564. }
  3565. *pHDSA = hdsaColumns;
  3566. TraceLeaveResult(hres);
  3567. }
  3568. /*-----------------------------------------------------------------------------
  3569. / _PickerDlgProc
  3570. / --------------
  3571. / Dialog proc for handling the dialog messages (there is a suprise)
  3572. /
  3573. / In:
  3574. / hwnd, uMsg, wParam, lParam = message information
  3575. / DWL_USER => LPPICKERSTATE structure
  3576. /
  3577. / Out:
  3578. / INT_PTR
  3579. /----------------------------------------------------------------------------*/
  3580. #define SET_BTN_STYLE(hwnd, idc, style) \
  3581. SendDlgItemMessage(hwnd, idc, BM_SETSTYLE, MAKEWPARAM(style, 0), MAKELPARAM(TRUE, 0));
  3582. INT_PTR _PickerDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  3583. {
  3584. USES_CONVERSION;
  3585. INT_PTR fResult = FALSE;
  3586. LPPICKERSTATE pState = NULL;
  3587. BOOL fUpdateButtonState = TRUE;
  3588. PICKERITEM item;
  3589. INT i, j;
  3590. if ( uMsg == WM_INITDIALOG )
  3591. {
  3592. pState = (LPPICKERSTATE)lParam;
  3593. TraceAssert(pState);
  3594. SetWindowLongPtr(hwnd, DWLP_USER, lParam);
  3595. pState->hwndProperties = GetDlgItem(hwnd, IDC_LBPROPERTIES);
  3596. pState->hwndColumns = GetDlgItem(hwnd, IDC_LBCOLUMNS);
  3597. // pState->hdsaColumns contains the currently visible column table, this is
  3598. // the table being used by the current view, therefore we must not modify
  3599. // it, just treat it as read only. Add the columns to the ListBox
  3600. // marking the visible items in the properties DPA, then add those
  3601. // items not already shown to the properties list box.
  3602. for ( i = 0 ; i < DSA_GetItemCount(pState->hdsaColumns) ; i++ )
  3603. {
  3604. LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(pState->hdsaColumns, i);
  3605. TraceAssert(pColumn);
  3606. if ( SUCCEEDED(GetPropertyFromColumn(&item.pProperty, pColumn)) )
  3607. {
  3608. j = DPA_Search(pState->hdpaItems, &item, 0, _PickerItemCmpCB, NULL, DPAS_SORTED);
  3609. Trace(TEXT("Searching for %s yielded %d"), W2T(item.pProperty), j);
  3610. if ( j >= 0 )
  3611. {
  3612. LPPICKERITEM pItem = (LPPICKERITEM)DPA_GetPtr(pState->hdpaItems, j);
  3613. TraceAssert(pItem);
  3614. ListBox_SetItemData(pState->hwndColumns,
  3615. ListBox_AddString(pState->hwndColumns, pItem->pHeading),
  3616. (LPARAM)pItem);
  3617. }
  3618. LocalFreeStringW(&item.pProperty);
  3619. }
  3620. }
  3621. for ( i = 0 ; i < DPA_GetPtrCount(pState->hdpaItems) ; i++ )
  3622. {
  3623. LPPICKERITEM pItem = (LPPICKERITEM)DPA_GetPtr(pState->hdpaItems, i);
  3624. TraceAssert(pItem && pItem->pHeading);
  3625. if ( !pItem->fIsColumn )
  3626. {
  3627. ListBox_SetItemData(pState->hwndProperties,
  3628. ListBox_AddString(pState->hwndProperties, pItem->pHeading),
  3629. (LPARAM)pItem);
  3630. }
  3631. }
  3632. // Ensure we default select the top items of each list
  3633. ListBox_SetCurSel(pState->hwndProperties, 0);
  3634. ListBox_SetCurSel(pState->hwndColumns, 0);
  3635. }
  3636. else
  3637. {
  3638. pState = (LPPICKERSTATE)GetWindowLongPtr(hwnd, DWLP_USER);
  3639. TraceAssert(pState);
  3640. switch ( uMsg )
  3641. {
  3642. case WM_HELP:
  3643. {
  3644. LPHELPINFO pHelpInfo = (LPHELPINFO)lParam;
  3645. WinHelp((HWND)pHelpInfo->hItemHandle,
  3646. DSQUERY_HELPFILE,
  3647. HELP_WM_HELP,
  3648. (DWORD_PTR)aPickColumnsHelpIDs);
  3649. break;
  3650. }
  3651. case WM_CONTEXTMENU:
  3652. WinHelp((HWND)wParam, DSQUERY_HELPFILE, HELP_CONTEXTMENU, (DWORD_PTR)aPickColumnsHelpIDs);
  3653. fResult = TRUE;
  3654. break;
  3655. case WM_COMMAND:
  3656. {
  3657. switch ( LOWORD(wParam) )
  3658. {
  3659. case IDOK:
  3660. {
  3661. _Picker_GetColumnTable(pState->hwndColumns, &pState->hdsaColumns);
  3662. EndDialog(hwnd, IDOK);
  3663. break;
  3664. }
  3665. case IDCANCEL:
  3666. EndDialog(hwnd, IDCANCEL);
  3667. break;
  3668. case IDC_ADD:
  3669. _PickerMoveColumn(pState->hwndColumns, pState->hwndProperties, TRUE);
  3670. break;
  3671. case IDC_REMOVE:
  3672. _PickerMoveColumn(pState->hwndProperties, pState->hwndColumns, FALSE);
  3673. break;
  3674. case IDC_LBPROPERTIES:
  3675. {
  3676. if ( ListBox_GetCount(pState->hwndProperties) > 0 )
  3677. {
  3678. if ( HIWORD(wParam) == LBN_DBLCLK )
  3679. _PickerMoveColumn(pState->hwndColumns, pState->hwndProperties, TRUE);
  3680. }
  3681. break;
  3682. }
  3683. case IDC_LBCOLUMNS:
  3684. {
  3685. if ( ListBox_GetCount(pState->hwndColumns) > 1 )
  3686. {
  3687. if ( HIWORD(wParam) == LBN_DBLCLK )
  3688. _PickerMoveColumn(pState->hwndProperties, pState->hwndColumns, FALSE);
  3689. }
  3690. break;
  3691. }
  3692. default:
  3693. fUpdateButtonState = FALSE;
  3694. break;
  3695. }
  3696. break;
  3697. }
  3698. default:
  3699. fUpdateButtonState = FALSE;
  3700. break;
  3701. }
  3702. }
  3703. // if the selections state change, or something which would cause
  3704. // us to refresh the add/remove buttons state then lets do it.
  3705. // each button is enabled only if there is a selection and the
  3706. // number of items is > the min value.
  3707. if ( pState && fUpdateButtonState )
  3708. {
  3709. BOOL fEnableAdd = FALSE;
  3710. BOOL fEnableRemove = FALSE;
  3711. DWORD dwButtonStyle;
  3712. if ( (ListBox_GetCount(pState->hwndProperties) > 0) )
  3713. {
  3714. fEnableAdd = TRUE;
  3715. }
  3716. if ( (ListBox_GetCount(pState->hwndColumns) > 1) )
  3717. {
  3718. fEnableRemove = TRUE;
  3719. }
  3720. // Make sure the DefButton is an enabled button
  3721. // FEATURE: Need to add an SHSetDefID() export to shlwapi
  3722. // which is simply a SetDefID that "does the right thing"
  3723. // wrt disabled buttons.
  3724. if ( (!fEnableRemove) && (!fEnableAdd) )
  3725. {
  3726. SET_BTN_STYLE(hwnd, IDC_ADD, BS_PUSHBUTTON);
  3727. SET_BTN_STYLE(hwnd, IDC_REMOVE, BS_PUSHBUTTON);
  3728. SendMessage(hwnd, DM_SETDEFID, IDOK, 0);
  3729. SET_BTN_STYLE(hwnd, IDOK, BS_DEFPUSHBUTTON);
  3730. SetFocus(GetDlgItem(hwnd, IDOK));
  3731. }
  3732. else if ( !fEnableAdd )
  3733. {
  3734. dwButtonStyle = (DWORD)GetWindowLong(GetDlgItem(hwnd, IDC_ADD), GWL_STYLE);
  3735. if ( dwButtonStyle & BS_DEFPUSHBUTTON )
  3736. {
  3737. SET_BTN_STYLE(hwnd, IDC_ADD, BS_PUSHBUTTON);
  3738. SendMessage(hwnd, DM_SETDEFID, IDC_REMOVE, 0);
  3739. SET_BTN_STYLE(hwnd, IDC_REMOVE, BS_DEFPUSHBUTTON);
  3740. SetFocus(GetDlgItem(hwnd, IDC_REMOVE));
  3741. }
  3742. }
  3743. else if ( !fEnableRemove )
  3744. {
  3745. dwButtonStyle = (DWORD) GetWindowLong(GetDlgItem(hwnd, IDC_REMOVE), GWL_STYLE);
  3746. if ( dwButtonStyle & BS_DEFPUSHBUTTON )
  3747. {
  3748. SET_BTN_STYLE(hwnd, IDC_REMOVE, BS_PUSHBUTTON);
  3749. SendMessage(hwnd, DM_SETDEFID, IDC_ADD, 0);
  3750. SET_BTN_STYLE(hwnd, IDC_ADD, BS_DEFPUSHBUTTON);
  3751. SetFocus(GetDlgItem(hwnd, IDC_ADD));
  3752. }
  3753. }
  3754. Button_Enable(GetDlgItem(hwnd, IDC_ADD), fEnableAdd);
  3755. Button_Enable(GetDlgItem(hwnd, IDC_REMOVE), fEnableRemove);
  3756. }
  3757. return fResult;
  3758. }
  3759. /*-----------------------------------------------------------------------------
  3760. / CDsQuery::OnPickColumns
  3761. / -----------------------
  3762. / Handle picking the columns that should be displayed in the result view,
  3763. / if the user selects new columns and hits OK then we refresh the
  3764. / view and the internal table of visible columns.
  3765. /
  3766. / In:
  3767. / hwndParent = parent for the dialog
  3768. /
  3769. / Out:
  3770. / HRESULT
  3771. /----------------------------------------------------------------------------*/
  3772. INT _PickerItemFreeCB(LPVOID pData, LPVOID lParam)
  3773. {
  3774. LPPICKERITEM pItem = (LPPICKERITEM)pData;
  3775. LocalFreeStringW(&pItem->pProperty);
  3776. LocalFreeString(&pItem->pHeading);
  3777. LocalFree(pItem);
  3778. return 1;
  3779. }
  3780. INT _PickerItemCmpCB(LPVOID p1, LPVOID p2, LPARAM lParam)
  3781. {
  3782. USES_CONVERSION;
  3783. LPPICKERITEM pEntry1 = (LPPICKERITEM)p1;
  3784. LPPICKERITEM pEntry2 = (LPPICKERITEM)p2;
  3785. INT nResult = -1;
  3786. if ( pEntry1 && pEntry2 )
  3787. nResult = StrCmpW(pEntry1->pProperty, pEntry2->pProperty);
  3788. return nResult;
  3789. }
  3790. typedef struct
  3791. {
  3792. PICKERSTATE *pps;
  3793. HDPA hdpaProperties; // attributes to be appeneded.
  3794. } PICKERENUMATTRIB;
  3795. HRESULT CALLBACK _PickerEnumAttribCB(LPARAM lParam, LPCWSTR pAttributeName, LPCWSTR pDisplayName, DWORD dwFlags)
  3796. {
  3797. HRESULT hres;
  3798. PICKERENUMATTRIB *ppea = (PICKERENUMATTRIB*)lParam;
  3799. PICKERITEM item;
  3800. INT j;
  3801. USES_CONVERSION;
  3802. TraceEnter(TRACE_FIELDCHOOSER, "_PickerEnumAttribCB");
  3803. // fix casting
  3804. item.pProperty = (LPWSTR)pAttributeName;
  3805. j = DPA_Search(ppea->pps->hdpaItems, &item, 0, _PickerItemCmpCB, NULL, DPAS_SORTED);
  3806. if ( j == -1 )
  3807. {
  3808. Trace(TEXT("Property not already in list: %s"), W2CT(pAttributeName));
  3809. hres = StringDPA_AppendStringW(ppea->hdpaProperties, pAttributeName, NULL);
  3810. FailGracefully(hres, "Failed to add unique property to DPA");
  3811. }
  3812. hres = S_OK;
  3813. exit_gracefully:
  3814. TraceLeaveResult(hres);
  3815. }
  3816. HRESULT CDsQuery::OnPickColumns(HWND hwndParent)
  3817. {
  3818. USES_CONVERSION;
  3819. HRESULT hres;
  3820. HDPA hdpaProperties = NULL;
  3821. PICKERSTATE state;
  3822. PICKERENUMATTRIB pea = { 0 };
  3823. INT i, j, iProperty, iColumn;
  3824. LPDSQUERYCLASSLIST pDsQueryClassList = NULL;
  3825. IDsDisplaySpecifier* pdds = NULL;
  3826. TraceEnter(TRACE_FIELDCHOOSER, "CDsQuery::OnPickColumns");
  3827. Trace(TEXT("Column count %d"), DSA_GetItemCount(_hdsaColumns));
  3828. state.hdpaItems = NULL;
  3829. state.hdsaColumns = _hdsaColumns;
  3830. state.hwndProperties = NULL;
  3831. state.hwndColumns = NULL;
  3832. // Build a list of unique properties sorted and remove the ones
  3833. // which match the currently displayed property set.
  3834. state.hdpaItems = DPA_Create(16);
  3835. TraceAssert(state.hdpaItems);
  3836. if ( !state.hdpaItems )
  3837. ExitGracefully(hres, E_OUTOFMEMORY, "Failed to create property DPA");
  3838. //
  3839. // attempt to get the IDsDisplaySpecifier object
  3840. //
  3841. hres = CoCreateInstance(CLSID_DsDisplaySpecifier, NULL, CLSCTX_INPROC_SERVER, IID_IDsDisplaySpecifier, (void **)&pdds);
  3842. FailGracefully(hres, "Failed to get the IDsDisplaySpecifier interface");
  3843. hres = pdds->SetServer(_pServer, _pUserName, _pPassword, DSSSF_DSAVAILABLE);
  3844. FailGracefully(hres, "Failed to server information");
  3845. // add the columns properties to the the list, marking them as active columns
  3846. // storing their size and other information.
  3847. for ( i = 0 ; i < DPA_GetPtrCount(_hdsaColumns); i++ )
  3848. {
  3849. LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(_hdsaColumns, i);
  3850. TraceAssert(pColumn);
  3851. LPPICKERITEM pItem = (LPPICKERITEM)LocalAlloc(LPTR, SIZEOF(PICKERITEM));
  3852. TraceAssert(pItem);
  3853. if ( pItem )
  3854. {
  3855. pItem->pProperty = NULL;
  3856. pItem->pHeading = NULL;
  3857. pItem->fIsColumn = TRUE;
  3858. pItem->cx = pColumn->cx;
  3859. pItem->fmt = pColumn->fmt;
  3860. hres = GetPropertyFromColumn(&pItem->pProperty, pColumn);
  3861. TraceAssert(SUCCEEDED(hres));
  3862. if ( SUCCEEDED(hres) )
  3863. {
  3864. hres = LocalAllocString(&pItem->pHeading, pColumn->pHeading);
  3865. TraceAssert(SUCCEEDED(hres));
  3866. }
  3867. Trace(TEXT("Adding column %d, with property %s"), i, W2T(pItem->pProperty));
  3868. if ( FAILED(hres) || (-1 == DPA_AppendPtr(state.hdpaItems, pItem)) )
  3869. {
  3870. TraceMsg("Failed to add property to the DPA");
  3871. hres = E_FAIL;
  3872. }
  3873. if ( FAILED(hres) )
  3874. _PickerItemFreeCB(pItem, NULL);
  3875. }
  3876. }
  3877. DPA_Sort(state.hdpaItems, _PickerItemCmpCB, NULL); // sort the DPA now we have all the elements
  3878. // for all the classes we have loop through and build the unique
  3879. // list, sorted as we go.
  3880. hres = _pqf->CallForm(&_clsidForm, DSQPM_GETCLASSLIST, 0, (LPARAM)&pDsQueryClassList);
  3881. FailGracefully(hres, "Failed to get the class list");
  3882. if ( !pDsQueryClassList )
  3883. ExitGracefully(hres, E_FAIL, "Failed to get the class list");
  3884. Trace(TEXT("Classes returned from DSQPM_GETCLASSLIST %d"), pDsQueryClassList->cClasses);
  3885. for ( i = 0 ; i < pDsQueryClassList->cClasses ; i++ )
  3886. {
  3887. LPWSTR pObjectClass = (LPWSTR)ByteOffset(pDsQueryClassList, pDsQueryClassList->offsetClass[i]);
  3888. TraceAssert(pObjectClass);
  3889. Trace(TEXT("Adding class '%s' to the property DPA"), W2T(pObjectClass));
  3890. // allocate a DPA to be filed with items
  3891. StringDPA_Destroy(&pea.hdpaProperties);
  3892. pea.pps = &state;
  3893. pea.hdpaProperties = DPA_Create(16);
  3894. if ( !pea.hdpaProperties )
  3895. ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate string DPA");
  3896. if ( SUCCEEDED(EnumClassAttributes(pdds, pObjectClass, _PickerEnumAttribCB, (LPARAM)&pea)) )
  3897. {
  3898. Trace(TEXT("Unique property list has %d entries"), DPA_GetPtrCount(pea.hdpaProperties));
  3899. // Having constructed the unique list of properties for this class lets now
  3900. // add them to the item data list and allocate real structures for them.
  3901. for ( iProperty = 0 ; iProperty < DPA_GetPtrCount(pea.hdpaProperties); iProperty++ )
  3902. {
  3903. LPWSTR pProperty = StringDPA_GetStringW(pea.hdpaProperties, iProperty);
  3904. TraceAssert(pProperty != NULL);
  3905. LPPICKERITEM pItem = (LPPICKERITEM)LocalAlloc(LPTR, SIZEOF(PICKERITEM));
  3906. TraceAssert(pItem);
  3907. if ( pItem )
  3908. {
  3909. WCHAR szBuffer[MAX_PATH];
  3910. GetFriendlyAttributeName(pdds, pObjectClass, pProperty, szBuffer, ARRAYSIZE(szBuffer));
  3911. pItem->pProperty = NULL;
  3912. pItem->pHeading = NULL;
  3913. pItem->fIsColumn = FALSE;
  3914. pItem->cx = 0;
  3915. pItem->fmt = 0;
  3916. hres = LocalAllocStringW(&pItem->pProperty, pProperty);
  3917. TraceAssert(SUCCEEDED(hres));
  3918. if ( SUCCEEDED(hres) )
  3919. {
  3920. hres = LocalAllocStringW2T(&pItem->pHeading, szBuffer);
  3921. TraceAssert(SUCCEEDED(hres));
  3922. }
  3923. if ( FAILED(hres) || (-1 == DPA_AppendPtr(state.hdpaItems, pItem)) )
  3924. {
  3925. TraceMsg("Failed to add property to the DPA");
  3926. hres = E_FAIL;
  3927. }
  3928. if ( FAILED(hres) )
  3929. _PickerItemFreeCB(pItem, NULL);
  3930. }
  3931. DPA_Sort(state.hdpaItems, _PickerItemCmpCB, NULL); // sort the DPA now we have all the elements
  3932. }
  3933. }
  3934. }
  3935. Trace(TEXT("Property table is %d items in size"), DPA_GetPtrCount(state.hdpaItems));
  3936. // If the user selects OK then the DlgProc will have generated a new column
  3937. // table stored in the PICKERSTATE structure, we should persist this, then
  3938. // load it ready to refresh the result viewer.
  3939. i = (int)DialogBoxParam(GLOBAL_HINSTANCE, MAKEINTRESOURCE(IDD_PICKCOLUMNS),
  3940. hwndParent,
  3941. _PickerDlgProc, (LPARAM)&state);
  3942. if ( i == IDOK )
  3943. {
  3944. hres = _SaveColumnTable(_clsidForm, state.hdsaColumns);
  3945. FailGracefully(hres, "Failed to write column table");
  3946. hres = _InitNewQuery(NULL, TRUE); // initialize the view
  3947. FailGracefully(hres, "Failed when starting new query");
  3948. TraceAssert(_dwThreadId);
  3949. PostThreadMessage(_dwThreadId, RVTM_SETCOLUMNTABLE, _dwQueryReference, (LPARAM)state.hdsaColumns);
  3950. _fColumnsModified = FALSE;
  3951. }
  3952. hres = S_OK;
  3953. exit_gracefully:
  3954. StringDPA_Destroy(&pea.hdpaProperties);
  3955. if ( state.hdpaItems )
  3956. DPA_DestroyCallback(state.hdpaItems, _PickerItemFreeCB, NULL);
  3957. if ( pDsQueryClassList )
  3958. CoTaskMemFree(pDsQueryClassList);
  3959. DoRelease(pdds);
  3960. TraceLeaveValue(hres);
  3961. }
  3962. // persistance object
  3963. class CDsPersistQuery : public IPersistQuery
  3964. {
  3965. private:
  3966. LONG _cRef;
  3967. TCHAR m_szFilename[MAX_PATH];
  3968. public:
  3969. CDsPersistQuery(LPCTSTR pFilename);;
  3970. ~CDsPersistQuery();
  3971. // IUnknown
  3972. STDMETHOD(QueryInterface)(REFIID riid, LPVOID* ppvObject);
  3973. STDMETHOD_(ULONG, AddRef)();
  3974. STDMETHOD_(ULONG, Release)();
  3975. // IPersist
  3976. STDMETHOD(GetClassID)(CLSID* pClassID)
  3977. { return E_NOTIMPL; }
  3978. // IPersistQuery
  3979. STDMETHOD(WriteString)(LPCTSTR pSection, LPCTSTR pValueName, LPCTSTR pValue);
  3980. STDMETHOD(ReadString)(LPCTSTR pSection, LPCTSTR pValueName, LPTSTR pBuffer, INT cchBuffer);
  3981. STDMETHOD(WriteInt)(LPCTSTR pSection, LPCTSTR pValueName, INT value);
  3982. STDMETHOD(ReadInt)(LPCTSTR pSection, LPCTSTR pValueName, LPINT pValue);
  3983. STDMETHOD(WriteStruct)(LPCTSTR pSection, LPCTSTR pValueName, LPVOID pStruct, DWORD cbStruct);
  3984. STDMETHOD(ReadStruct)(LPCTSTR pSection, LPCTSTR pValueName, LPVOID pStruct, DWORD cbStruct);
  3985. STDMETHOD(Clear)()
  3986. { return S_OK; }
  3987. };
  3988. #define STRING_SIZE TEXT("%sLength")
  3989. #define STRING_VALUE TEXT("%sValue")
  3990. CDsPersistQuery::CDsPersistQuery(LPCTSTR pFilename) :
  3991. _cRef(1)
  3992. {
  3993. StrCpy(m_szFilename, pFilename); // copy the filename
  3994. DllAddRef();
  3995. }
  3996. CDsPersistQuery::~CDsPersistQuery()
  3997. {
  3998. DllRelease();
  3999. }
  4000. // IUnknown
  4001. ULONG CDsPersistQuery::AddRef()
  4002. {
  4003. return InterlockedIncrement(&_cRef);
  4004. }
  4005. ULONG CDsPersistQuery::Release()
  4006. {
  4007. if (InterlockedDecrement(&_cRef))
  4008. return _cRef;
  4009. delete this;
  4010. return 0;
  4011. }
  4012. HRESULT CDsPersistQuery::QueryInterface(REFIID riid, void **ppv)
  4013. {
  4014. static const QITAB qit[] =
  4015. {
  4016. QITABENTMULTI(CDsPersistQuery, IPersist, IPersistQuery), // IID_IPersist
  4017. QITABENT(CDsPersistQuery, IPersistQuery), // IID_IPersistQuery
  4018. {0, 0 },
  4019. };
  4020. return QISearch(this, qit, riid, ppv);
  4021. }
  4022. STDAPI CPersistQuery_CreateInstance(LPTSTR pszPath, IPersistQuery **pppq)
  4023. {
  4024. CDsPersistQuery *ppq = new CDsPersistQuery(pszPath);
  4025. if (!ppq)
  4026. return E_OUTOFMEMORY;
  4027. HRESULT hr = ppq->QueryInterface(IID_IPersistQuery, (void **)pppq);
  4028. ppq->Release();
  4029. return hr;
  4030. }
  4031. // IPersistQuery
  4032. STDMETHODIMP CDsPersistQuery::WriteString(LPCTSTR pSection, LPCTSTR pKey, LPCTSTR pValue)
  4033. {
  4034. HRESULT hr = S_OK;
  4035. TCHAR szBuffer[MAX_PATH];
  4036. if ( !pSection || !pKey || !pValue )
  4037. return E_INVALIDARG;
  4038. // Write the string into the stream as a UNICODE string. If we are built for UNICODE
  4039. // then we can simply WriteProfileStruct, otherwise we must attempt to convert it
  4040. // from a multi-byte string.
  4041. // Write the string size
  4042. wsprintf(szBuffer, STRING_SIZE, pKey);
  4043. INT cchValue = 1+lstrlen(pValue);
  4044. if ( !WritePrivateProfileStruct(pSection, szBuffer, &cchValue, SIZEOF(cchValue), m_szFilename) )
  4045. ExitGracefully(hr, E_FAIL, "Failed to write string size to stream");
  4046. wsprintf(szBuffer, STRING_VALUE, pKey);
  4047. if ( !WritePrivateProfileStruct(pSection, szBuffer, (LPVOID)pValue, SIZEOF(WCHAR)*cchValue, m_szFilename) )
  4048. ExitGracefully(hr, E_FAIL, "Failed to write string to stream");
  4049. hr = S_OK;
  4050. exit_gracefully:
  4051. TraceLeaveResult(hr);
  4052. }
  4053. STDMETHODIMP CDsPersistQuery::ReadString(LPCTSTR pSection, LPCTSTR pKey, LPTSTR pBuffer, INT cchBuffer)
  4054. {
  4055. HRESULT hr;
  4056. TCHAR szBuffer[MAX_PATH];
  4057. INT cchValue;
  4058. TraceEnter(TRACE_IO, "CDsPersistQuery::ReadString");
  4059. if ( !pSection || !pKey || !pBuffer )
  4060. ExitGracefully(hr, E_INVALIDARG, "Nothing to read (or into)");
  4061. pBuffer[0] = TEXT('\0'); // terminate the buffer
  4062. Trace(TEXT("pSection: %s, pKey: %s"), pSection, pKey);
  4063. // Read the length of the string, checking to see if its fits in the the buffer
  4064. // we have, if it does then read and convert as requried.
  4065. wsprintf(szBuffer, STRING_SIZE, pKey); // <key name>Length
  4066. Trace(TEXT("Opening key: %s"), szBuffer);
  4067. if ( !GetPrivateProfileStruct(pSection, szBuffer, &cchValue, SIZEOF(cchValue), m_szFilename) )
  4068. ExitGracefully(hr, E_FAIL, "Failed to read string size");
  4069. Trace(TEXT("cchValue %d"), cchValue);
  4070. if ( cchValue > cchBuffer )
  4071. ExitGracefully(hr, E_FAIL, "Buffer too small for string in stream");
  4072. if ( cchValue > 0 )
  4073. {
  4074. wsprintf(szBuffer, STRING_VALUE, pKey);
  4075. if ( !GetPrivateProfileStruct(pSection, szBuffer, pBuffer, SIZEOF(WCHAR)*cchValue, m_szFilename) )
  4076. ExitGracefully(hr, E_FAIL, "Failed to read string data");
  4077. }
  4078. Trace(TEXT("Value is: %s"), pBuffer);
  4079. hr = S_OK;
  4080. exit_gracefully:
  4081. TraceLeaveResult(hr);
  4082. }
  4083. STDMETHODIMP CDsPersistQuery::WriteInt(LPCTSTR pSection, LPCTSTR pKey, INT value)
  4084. {
  4085. HRESULT hr;
  4086. TraceEnter(TRACE_IO, "CDsPersistQuery::WriteInt");
  4087. if ( !pSection || !pKey )
  4088. ExitGracefully(hr, E_INVALIDARG, "Nothing to write");
  4089. Trace(TEXT("pSection: %s, pKey: %s, value: %d"), pSection, pKey, value);
  4090. if ( !WritePrivateProfileStruct(pSection, pKey, &value, SIZEOF(value), m_szFilename) )
  4091. ExitGracefully(hr, E_FAIL, "Failed to write value");
  4092. hr = S_OK;
  4093. exit_gracefully:
  4094. TraceLeaveResult(hr);
  4095. }
  4096. STDMETHODIMP CDsPersistQuery::ReadInt(LPCTSTR pSection, LPCTSTR pKey, LPINT pValue)
  4097. {
  4098. HRESULT hr;
  4099. TraceEnter(TRACE_IO, "CDsPersistQuery::ReadInt");
  4100. if ( !pSection || !pKey || !pValue )
  4101. ExitGracefully(hr, E_INVALIDARG, "Nothing to read");
  4102. Trace(TEXT("pSection: %s, pKey: %s, pValue: %08x"), pSection, pKey, pValue);
  4103. if ( !GetPrivateProfileStruct(pSection, pKey, pValue, SIZEOF(*pValue), m_szFilename) )
  4104. ExitGracefully(hr, E_FAIL, "Failed to read value");
  4105. hr = S_OK;
  4106. exit_gracefully:
  4107. TraceLeaveResult(hr);
  4108. }
  4109. STDMETHODIMP CDsPersistQuery::WriteStruct(LPCTSTR pSection, LPCTSTR pKey, LPVOID pStruct, DWORD cbStruct)
  4110. {
  4111. HRESULT hr;
  4112. TraceEnter(TRACE_IO, "CDsPersistQuery::WriteStruct");
  4113. if ( !pSection || !pKey || !pStruct )
  4114. ExitGracefully(hr, E_INVALIDARG, "Nothing to write");
  4115. Trace(TEXT("pSection: %s, pKey: %s, pStruct: %08x, cbStruct: %d"), pSection, pKey, pStruct, cbStruct);
  4116. if ( !WritePrivateProfileStruct(pSection, pKey, pStruct, cbStruct, m_szFilename) )
  4117. ExitGracefully(hr, E_FAIL, "Failed to write struct");
  4118. hr = S_OK;
  4119. exit_gracefully:
  4120. TraceLeaveResult(hr);
  4121. }
  4122. STDMETHODIMP CDsPersistQuery::ReadStruct(LPCTSTR pSection, LPCTSTR pKey, LPVOID pStruct, DWORD cbStruct)
  4123. {
  4124. HRESULT hr;
  4125. TraceEnter(TRACE_IO, "CDsPersistQuery::ReadStruct");
  4126. if ( !pSection || !pKey || !pStruct )
  4127. ExitGracefully(hr, E_INVALIDARG, "Nothing to read");
  4128. Trace(TEXT("pSection: %s, pKey: %s, pStruct: %08x, cbStruct: %d"), pSection, pKey, pStruct, cbStruct);
  4129. if ( !GetPrivateProfileStruct(pSection, pKey, pStruct, cbStruct, m_szFilename) )
  4130. ExitGracefully(hr, E_FAIL, "Failed to read struct");
  4131. hr = S_OK;
  4132. exit_gracefully:
  4133. TraceLeaveResult(hr);
  4134. }