Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4061 lines
118 KiB

  1. #include "stdafx.h"
  2. #include <browseui.h>
  3. #include "sfthost.h"
  4. #include <shellp.h>
  5. #include "startmnu.h"
  6. #define TF_HOST 0x00000010
  7. #define TF_HOSTDD 0x00000040 // drag/drop
  8. #define TF_HOSTPIN 0x00000080 // pin
  9. #define ANIWND_WIDTH 80
  10. #define ANIWND_HEIGHT 50
  11. //---------BEGIN HACKS OF DEATH -------------
  12. // HACKHACK - desktopp.h and browseui.h both define SHCreateFromDesktop
  13. // What's worse, browseui.h includes desktopp.h! So you have to sneak it
  14. // out in this totally wacky way.
  15. #include <desktopp.h>
  16. #define SHCreateFromDesktop _SHCreateFromDesktop
  17. #include <browseui.h>
  18. //---------END HACKS OF DEATH -------------
  19. //****************************************************************************
  20. //
  21. // Dummy IContextMenu
  22. //
  23. // We use this when we can't get the real IContextMenu for an item.
  24. // If the user pins an object and then deletes the underlying
  25. // file, attempting to get the IContextMenu from the shell will fail,
  26. // but we need something there so we can add the "Remove from this list"
  27. // menu item.
  28. //
  29. // Since this dummy context menu has no state, we can make it a static
  30. // singleton object.
  31. class CEmptyContextMenu
  32. : public IContextMenu
  33. {
  34. public:
  35. // *** IUnknown ***
  36. STDMETHODIMP QueryInterface(REFIID riid, void** ppvObj)
  37. {
  38. static const QITAB qit[] = {
  39. QITABENT(CEmptyContextMenu, IContextMenu),
  40. { 0 },
  41. };
  42. return QISearch(this, qit, riid, ppvObj);
  43. }
  44. STDMETHODIMP_(ULONG) AddRef(void) { return 3; }
  45. STDMETHODIMP_(ULONG) Release(void) { return 2; }
  46. // *** IContextMenu ***
  47. STDMETHODIMP QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
  48. {
  49. return ResultFromShort(0); // No items added
  50. }
  51. STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO pici)
  52. {
  53. ASSERT(FALSE);
  54. return E_FAIL;
  55. }
  56. STDMETHODIMP GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pwRes, LPSTR pszName, UINT cchMax)
  57. {
  58. return E_INVALIDARG; // no commands; therefore, no command strings!
  59. }
  60. public:
  61. IContextMenu *GetContextMenu()
  62. {
  63. // Don't need to AddRef since we are a static object
  64. return this;
  65. }
  66. };
  67. static CEmptyContextMenu s_EmptyContextMenu;
  68. //****************************************************************************
  69. #define WC_SFTBARHOST TEXT("DesktopSFTBarHost")
  70. BOOL GetFileCreationTime(LPCTSTR pszFile, FILETIME *pftCreate)
  71. {
  72. WIN32_FILE_ATTRIBUTE_DATA wfad;
  73. BOOL fRc = GetFileAttributesEx(pszFile, GetFileExInfoStandard, &wfad);
  74. if (fRc)
  75. {
  76. *pftCreate = wfad.ftCreationTime;
  77. }
  78. return fRc;
  79. }
  80. // {2A1339D7-523C-4E21-80D3-30C97B0698D2}
  81. const CLSID TOID_SFTBarHostBackgroundEnum = {
  82. 0x2A1339D7, 0x523C, 0x4E21,
  83. { 0x80, 0xD3, 0x30, 0xC9, 0x7B, 0x06, 0x98, 0xD2} };
  84. BOOL SFTBarHost::Register()
  85. {
  86. WNDCLASS wc;
  87. wc.style = 0;
  88. wc.lpfnWndProc = _WndProc;
  89. wc.cbClsExtra = 0;
  90. wc.cbWndExtra = sizeof(void *);
  91. wc.hInstance = _Module.GetModuleInstance();
  92. wc.hIcon = 0;
  93. // We specify a cursor so the OOBE window gets something
  94. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  95. wc.hbrBackground = NULL;
  96. wc.lpszMenuName = 0;
  97. wc.lpszClassName = WC_SFTBARHOST;
  98. return ::SHRegisterClass(&wc);
  99. }
  100. BOOL SFTBarHost::Unregister()
  101. {
  102. return ::UnregisterClass(WC_SFTBARHOST, _Module.GetModuleInstance());
  103. }
  104. LRESULT CALLBACK SFTBarHost::_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  105. {
  106. SFTBarHost *self = reinterpret_cast<SFTBarHost *>(GetWindowPtr0(hwnd));
  107. if (uMsg == WM_NCCREATE)
  108. {
  109. return _OnNcCreate(hwnd, uMsg, wParam, lParam);
  110. }
  111. else if (self)
  112. {
  113. #define HANDLE_SFT_MESSAGE(wm, fn) case wm: return self->fn(hwnd, uMsg, wParam, lParam)
  114. switch (uMsg)
  115. {
  116. HANDLE_SFT_MESSAGE(WM_CREATE, _OnCreate);
  117. HANDLE_SFT_MESSAGE(WM_DESTROY, _OnDestroy);
  118. HANDLE_SFT_MESSAGE(WM_NCDESTROY, _OnNcDestroy);
  119. HANDLE_SFT_MESSAGE(WM_NOTIFY, _OnNotify);
  120. HANDLE_SFT_MESSAGE(WM_SIZE, _OnSize);
  121. HANDLE_SFT_MESSAGE(WM_ERASEBKGND, _OnEraseBackground);
  122. HANDLE_SFT_MESSAGE(WM_CONTEXTMENU, _OnContextMenu);
  123. HANDLE_SFT_MESSAGE(WM_CTLCOLORSTATIC,_OnCtlColorStatic);
  124. HANDLE_SFT_MESSAGE(WM_TIMER, _OnTimer);
  125. HANDLE_SFT_MESSAGE(WM_SETFOCUS, _OnSetFocus);
  126. HANDLE_SFT_MESSAGE(WM_INITMENUPOPUP,_OnMenuMessage);
  127. HANDLE_SFT_MESSAGE(WM_DRAWITEM, _OnMenuMessage);
  128. HANDLE_SFT_MESSAGE(WM_MENUCHAR, _OnMenuMessage);
  129. HANDLE_SFT_MESSAGE(WM_MEASUREITEM, _OnMenuMessage);
  130. HANDLE_SFT_MESSAGE(WM_SYSCOLORCHANGE, _OnSysColorChange);
  131. HANDLE_SFT_MESSAGE(WM_DISPLAYCHANGE, _OnForwardMessage);
  132. HANDLE_SFT_MESSAGE(WM_SETTINGCHANGE, _OnForwardMessage);
  133. HANDLE_SFT_MESSAGE(WM_UPDATEUISTATE, _OnUpdateUIState);
  134. HANDLE_SFT_MESSAGE(SFTBM_REPOPULATE,_OnRepopulate);
  135. HANDLE_SFT_MESSAGE(SFTBM_CHANGENOTIFY+0,_OnChangeNotify);
  136. HANDLE_SFT_MESSAGE(SFTBM_CHANGENOTIFY+1,_OnChangeNotify);
  137. HANDLE_SFT_MESSAGE(SFTBM_CHANGENOTIFY+2,_OnChangeNotify);
  138. HANDLE_SFT_MESSAGE(SFTBM_CHANGENOTIFY+3,_OnChangeNotify);
  139. HANDLE_SFT_MESSAGE(SFTBM_CHANGENOTIFY+4,_OnChangeNotify);
  140. HANDLE_SFT_MESSAGE(SFTBM_CHANGENOTIFY+5,_OnChangeNotify);
  141. HANDLE_SFT_MESSAGE(SFTBM_CHANGENOTIFY+6,_OnChangeNotify);
  142. HANDLE_SFT_MESSAGE(SFTBM_CHANGENOTIFY+7,_OnChangeNotify);
  143. HANDLE_SFT_MESSAGE(SFTBM_REFRESH, _OnRefresh);
  144. HANDLE_SFT_MESSAGE(SFTBM_CASCADE, _OnCascade);
  145. HANDLE_SFT_MESSAGE(SFTBM_ICONUPDATE, _OnIconUpdate);
  146. }
  147. // If this assert fires, you need to add more
  148. // HANDLE_SFT_MESSAGE(SFTBM_CHANGENOTIFY+... entries.
  149. COMPILETIME_ASSERT(SFTHOST_MAXNOTIFY == 8);
  150. #undef HANDLE_SFT_MESSAGE
  151. return self->OnWndProc(hwnd, uMsg, wParam, lParam);
  152. }
  153. return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
  154. }
  155. LRESULT SFTBarHost::_OnNcCreate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  156. {
  157. SMPANEDATA *pspld = PaneDataFromCreateStruct(lParam);
  158. SFTBarHost *self = NULL;
  159. switch (pspld->iPartId)
  160. {
  161. case SPP_PROGLIST:
  162. self = ByUsage_CreateInstance();
  163. break;
  164. case SPP_PLACESLIST:
  165. self = SpecList_CreateInstance();
  166. break;
  167. default:
  168. TraceMsg(TF_ERROR, "Unknown panetype %d", pspld->iPartId);
  169. }
  170. if (self)
  171. {
  172. SetWindowPtr0(hwnd, self);
  173. self->_hwnd = hwnd;
  174. self->_hTheme = pspld->hTheme;
  175. if (FAILED(self->Initialize()))
  176. {
  177. TraceMsg(TF_ERROR, "SFTBarHost::NcCreate Initialize call failed");
  178. return FALSE;
  179. }
  180. return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
  181. }
  182. return FALSE;
  183. }
  184. //
  185. // The tile height is max(imagelist height, text height) + some margin
  186. // The margin is "scientifically computed" to be the value that looks
  187. // reasonably close to the bitmaps the designers gave us.
  188. //
  189. void SFTBarHost::_ComputeTileMetrics()
  190. {
  191. int cyTile = _cyIcon;
  192. HDC hdc = GetDC(_hwndList);
  193. if (hdc)
  194. {
  195. // SOMEDAY - get this to play friendly with themes
  196. HFONT hf = GetWindowFont(_hwndList);
  197. HFONT hfPrev = SelectFont(hdc, hf);
  198. SIZE siz;
  199. if (GetTextExtentPoint(hdc, TEXT("0"), 1, &siz))
  200. {
  201. if (_CanHaveSubtitles())
  202. {
  203. // Reserve space for the subtitle too
  204. siz.cy *= 2;
  205. }
  206. if (cyTile < siz.cy)
  207. cyTile = siz.cy;
  208. }
  209. SelectFont(hdc, hfPrev);
  210. ReleaseDC(_hwndList, hdc);
  211. }
  212. // Listview draws text at left margin + icon + edge
  213. _cxIndent = _cxMargin + _cxIcon + GetSystemMetrics(SM_CXEDGE);
  214. _cyTile = cyTile + (4 * _cyMargin) + _cyTilePadding;
  215. }
  216. void SFTBarHost::_SetTileWidth(int cxTile)
  217. {
  218. LVTILEVIEWINFO tvi;
  219. tvi.cbSize = sizeof(tvi);
  220. tvi.dwMask = LVTVIM_TILESIZE | LVTVIM_COLUMNS;
  221. tvi.dwFlags = LVTVIF_FIXEDSIZE;
  222. // If we support cascading, then reserve space for the cascade arrows
  223. if (_dwFlags & HOSTF_CASCADEMENU)
  224. {
  225. // WARNING! _OnLVItemPostPaint uses these margins
  226. tvi.dwMask |= LVTVIM_LABELMARGIN;
  227. tvi.rcLabelMargin.left = 0;
  228. tvi.rcLabelMargin.top = 0;
  229. tvi.rcLabelMargin.right = _cxMarlett;
  230. tvi.rcLabelMargin.bottom = 0;
  231. }
  232. // Reserve space for subtitles if necessary
  233. tvi.cLines = _CanHaveSubtitles() ? 1 : 0;
  234. // _cyTile has the padding into account, but we want each item to be the height without padding
  235. tvi.sizeTile.cy = _cyTile - _cyTilePadding;
  236. tvi.sizeTile.cx = cxTile;
  237. ListView_SetTileViewInfo(_hwndList, &tvi);
  238. _cxTile = cxTile;
  239. }
  240. LRESULT SFTBarHost::_OnSize(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  241. {
  242. if (_hwndList)
  243. {
  244. SIZE sizeClient = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
  245. sizeClient.cx -= (_margins.cxLeftWidth + _margins.cxRightWidth);
  246. sizeClient.cy -= (_margins.cyTopHeight + _margins.cyBottomHeight);
  247. SetWindowPos(_hwndList, NULL, _margins.cxLeftWidth, _margins.cyTopHeight,
  248. sizeClient.cx, sizeClient.cy,
  249. SWP_NOZORDER | SWP_NOOWNERZORDER);
  250. _SetTileWidth(sizeClient.cx);
  251. if (HasDynamicContent())
  252. {
  253. _InternalRepopulateList();
  254. }
  255. }
  256. return 0;
  257. }
  258. LRESULT SFTBarHost::_OnSysColorChange(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  259. {
  260. // if we're in unthemed mode, then we need to update our colors
  261. if (!_hTheme)
  262. {
  263. ListView_SetTextColor(_hwndList, GetSysColor(COLOR_MENUTEXT));
  264. _clrHot = GetSysColor(COLOR_MENUTEXT);
  265. _clrBG = GetSysColor(COLOR_MENU);
  266. _clrSubtitle = CLR_NONE;
  267. ListView_SetBkColor(_hwndList, _clrBG);
  268. ListView_SetTextBkColor(_hwndList, _clrBG);
  269. }
  270. return _OnForwardMessage(hwnd, uMsg, wParam, lParam);
  271. }
  272. LRESULT SFTBarHost::_OnCtlColorStatic(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  273. {
  274. // Use the same colors as the listview itself.
  275. HDC hdc = GET_WM_CTLCOLOR_HDC(wParam, lParam, uMsg);
  276. SetTextColor(hdc, ListView_GetTextColor(_hwndList));
  277. COLORREF clrBk = ListView_GetTextBkColor(_hwndList);
  278. if (clrBk == CLR_NONE)
  279. {
  280. // The animate control really wants to get a text background color.
  281. // It doesn't support transparency.
  282. if (GET_WM_CTLCOLOR_HWND(wParam, lParam, uMsg) == _hwndAni)
  283. {
  284. if (_hTheme)
  285. {
  286. if (!_hBrushAni)
  287. {
  288. // We need to paint the theme background in a bitmap and use that
  289. // to create a brush for the background of the flashlight animation
  290. RECT rcClient;
  291. GetClientRect(hwnd, &rcClient);
  292. int x = (RECTWIDTH(rcClient) - ANIWND_WIDTH)/2; // IDA_SEARCH is ANIWND_WIDTH pix wide
  293. int y = (RECTHEIGHT(rcClient) - ANIWND_HEIGHT)/2; // IDA_SEARCH is ANIWND_HEIGHT pix tall
  294. RECT rc;
  295. rc.top = y;
  296. rc.bottom = y + ANIWND_HEIGHT;
  297. rc.left = x;
  298. rc.right = x + ANIWND_WIDTH;
  299. HDC hdcBMP = CreateCompatibleDC(hdc);
  300. HBITMAP hbmp = CreateCompatibleBitmap(hdc, ANIWND_WIDTH, ANIWND_HEIGHT);
  301. POINT pt = {0, 0};
  302. // Offset the viewport so that DrawThemeBackground draws the part that we care about
  303. // at the right place
  304. OffsetViewportOrgEx(hdcBMP, -x, -y, &pt);
  305. SelectObject(hdcBMP, hbmp);
  306. DrawThemeBackground(_hTheme, hdcBMP, _iThemePart, 0, &rcClient, 0);
  307. // Our bitmap is now ready!
  308. _hBrushAni = CreatePatternBrush(hbmp);
  309. // Cleanup
  310. SelectObject(hdcBMP, NULL);
  311. DeleteObject(hbmp);
  312. DeleteObject(hdcBMP);
  313. }
  314. return (LRESULT)_hBrushAni;
  315. }
  316. else
  317. {
  318. return (LRESULT)GetSysColorBrush(COLOR_MENU);
  319. }
  320. }
  321. SetBkMode(hdc, TRANSPARENT);
  322. return (LRESULT)GetStockBrush(HOLLOW_BRUSH);
  323. }
  324. else
  325. {
  326. return (LRESULT)GetSysColorBrush(COLOR_MENU);
  327. }
  328. }
  329. //
  330. // Appends the PaneItem to _dpaEnum, or deletes it (and nulls it out)
  331. // if unable to append.
  332. //
  333. int SFTBarHost::_AppendEnumPaneItem(PaneItem *pitem)
  334. {
  335. int iItem = _dpaEnumNew.AppendPtr(pitem);
  336. if (iItem < 0)
  337. {
  338. delete pitem;
  339. iItem = -1;
  340. }
  341. return iItem;
  342. }
  343. BOOL SFTBarHost::AddItem(PaneItem *pitem, IShellFolder *psf, LPCITEMIDLIST pidlChild)
  344. {
  345. BOOL fSuccess = FALSE;
  346. ASSERT(_fEnumerating);
  347. if (_AppendEnumPaneItem(pitem) >= 0)
  348. {
  349. fSuccess = TRUE;
  350. }
  351. return fSuccess;
  352. }
  353. void SFTBarHost::_RepositionItems()
  354. {
  355. DEBUG_CODE(_fListUnstable++);
  356. int iItem;
  357. for (iItem = ListView_GetItemCount(_hwndList) - 1; iItem >= 0; iItem--)
  358. {
  359. PaneItem *pitem = _GetItemFromLV(iItem);
  360. if (pitem)
  361. {
  362. POINT pt;
  363. _ComputeListViewItemPosition(pitem->_iPos, &pt);
  364. ListView_SetItemPosition(_hwndList, iItem, pt.x, pt.y);
  365. }
  366. }
  367. DEBUG_CODE(_fListUnstable--);
  368. }
  369. int SFTBarHost::AddImage(HICON hIcon)
  370. {
  371. int iIcon = -1;
  372. if (_IsPrivateImageList())
  373. {
  374. iIcon = ImageList_AddIcon(_himl, hIcon);
  375. }
  376. return iIcon;
  377. }
  378. //
  379. // pvData = the window to receive the icon
  380. // pvHint = pitem whose icon we just extracted
  381. // iIconIndex = the icon we got
  382. //
  383. void SFTBarHost::SetIconAsync(LPCITEMIDLIST pidl, LPVOID pvData, LPVOID pvHint, INT iIconIndex, INT iOpenIconIndex)
  384. {
  385. HWND hwnd = (HWND)pvData;
  386. if (IsWindow(hwnd))
  387. {
  388. PostMessage(hwnd, SFTBM_ICONUPDATE, iIconIndex, (LPARAM)pvHint);
  389. }
  390. }
  391. //
  392. // wParam = icon index
  393. // lParam = pitem to update
  394. //
  395. LRESULT SFTBarHost::_OnIconUpdate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  396. {
  397. //
  398. // Do not dereference lParam (pitem) until we are sure it is valid.
  399. //
  400. LVFINDINFO fi;
  401. LVITEM lvi;
  402. fi.flags = LVFI_PARAM;
  403. fi.lParam = lParam;
  404. lvi.iItem = ListView_FindItem(_hwndList, -1, &fi);
  405. if (lvi.iItem >= 0)
  406. {
  407. lvi.mask = LVIF_IMAGE;
  408. lvi.iSubItem = 0;
  409. lvi.iImage = (int)wParam;
  410. ListView_SetItem(_hwndList, &lvi);
  411. // Now, we need to go update our cached bitmap version of the start menu.
  412. _SendNotify(_hwnd, SMN_NEEDREPAINT, NULL);
  413. }
  414. return 0;
  415. }
  416. // An over-ridable method to let client direct an item at a particular image
  417. int SFTBarHost::AddImageForItem(PaneItem *pitem, IShellFolder *psf, LPCITEMIDLIST pidl, int iPos)
  418. {
  419. if (_IsPrivateImageList())
  420. {
  421. return _ExtractImageForItem(pitem, psf, pidl);
  422. }
  423. else
  424. {
  425. // system image list: Make the shell do the work.
  426. int iIndex;
  427. SHMapIDListToImageListIndexAsync(_psched, psf, pidl, 0, SetIconAsync, _hwnd, pitem, &iIndex, NULL);
  428. return iIndex;
  429. }
  430. }
  431. HICON _IconOf(IShellFolder *psf, LPCITEMIDLIST pidl, int cxIcon)
  432. {
  433. HRESULT hr;
  434. HICON hicoLarge = NULL, hicoSmall = NULL;
  435. IExtractIcon *pxi;
  436. hr = psf->GetUIObjectOf(NULL, 1, &pidl, IID_PPV_ARG_NULL(IExtractIcon, &pxi));
  437. if (SUCCEEDED(hr))
  438. {
  439. TCHAR szPath[MAX_PATH];
  440. int iIndex;
  441. UINT uiFlags;
  442. hr = pxi->GetIconLocation(0, szPath, ARRAYSIZE(szPath), &iIndex, &uiFlags);
  443. // S_FALSE means "Please use the generic document icon"
  444. if (hr == S_FALSE)
  445. {
  446. StrCpyN(szPath, TEXT("shell32.dll"), ARRAYSIZE(szPath));
  447. iIndex = II_DOCNOASSOC;
  448. hr = S_OK;
  449. }
  450. if (SUCCEEDED(hr))
  451. {
  452. // Even though we don't care about the small icon, we have to
  453. // ask for it anyway because some people fault on NULL.
  454. hr = pxi->Extract(szPath, iIndex, &hicoLarge, &hicoSmall, cxIcon);
  455. // S_FALSE means "I am too lazy to extract the icon myself.
  456. // You do it for me."
  457. if (hr == S_FALSE)
  458. {
  459. hr = SHDefExtractIcon(szPath, iIndex, uiFlags, &hicoLarge, &hicoSmall, cxIcon);
  460. }
  461. }
  462. pxi->Release();
  463. }
  464. // If we can't get an icon (e.g., object is on a slow link),
  465. // then use a generic folder or generic document, as appropriate.
  466. if (FAILED(hr))
  467. {
  468. SFGAOF attr = SFGAO_FOLDER;
  469. int iIndex;
  470. if (SUCCEEDED(psf->GetAttributesOf(1, &pidl, &attr)) &&
  471. (attr & SFGAO_FOLDER))
  472. {
  473. iIndex = II_FOLDER;
  474. }
  475. else
  476. {
  477. iIndex = II_DOCNOASSOC;
  478. }
  479. hr = SHDefExtractIcon(TEXT("shell32.dll"), iIndex, 0, &hicoLarge, &hicoSmall, cxIcon);
  480. }
  481. // Finally! we have an icon or have exhausted all attempts at getting
  482. // one. If we got one, go add it and clean up.
  483. if (hicoSmall)
  484. DestroyIcon(hicoSmall);
  485. return hicoLarge;
  486. }
  487. int SFTBarHost::_ExtractImageForItem(PaneItem *pitem, IShellFolder *psf, LPCITEMIDLIST pidl)
  488. {
  489. int iIcon = -1; // assume no icon
  490. HICON hIcon = _IconOf(psf, pidl, _cxIcon);
  491. if (hIcon)
  492. {
  493. iIcon = AddImage(hIcon);
  494. DestroyIcon(hIcon);
  495. }
  496. return iIcon;
  497. }
  498. //
  499. // There are two sets of numbers that keep track of items. Sorry.
  500. // (I tried to reduce it to one, but things got hairy.)
  501. //
  502. // 1. Position numbers. Separators occupy a position number.
  503. // 2. Item numbers (listview). Separators do not consume an item number.
  504. //
  505. // Example:
  506. //
  507. // iPos iItem
  508. //
  509. // A 0 0
  510. // B 1 1
  511. // ---- 2 N/A
  512. // C 3 2
  513. // ---- 4 N/A
  514. // D 5 3
  515. //
  516. // _rgiSep[] = { 2, 4 };
  517. //
  518. // _PosToItemNo and _ItemNoToPos do the conversion.
  519. int SFTBarHost::_PosToItemNo(int iPos)
  520. {
  521. // Subtract out the slots occupied by separators.
  522. int iItem = iPos;
  523. for (int i = 0; i < _cSep && _rgiSep[i] < iPos; i++)
  524. {
  525. iItem--;
  526. }
  527. return iItem;
  528. }
  529. int SFTBarHost::_ItemNoToPos(int iItem)
  530. {
  531. // Add in the slots occupied by separators.
  532. int iPos = iItem;
  533. for (int i = 0; i < _cSep && _rgiSep[i] <= iPos; i++)
  534. {
  535. iPos++;
  536. }
  537. return iPos;
  538. }
  539. void SFTBarHost::_ComputeListViewItemPosition(int iItem, POINT *pptOut)
  540. {
  541. // WARNING! _InternalRepopulateList uses an incremental version of this
  542. // algorithm. Keep the two in sync!
  543. ASSERT(_cyTilePadding >= 0);
  544. int y = iItem * _cyTile;
  545. // Adjust for all the separators in the list
  546. for (int i = 0; i < _cSep; i++)
  547. {
  548. if (_rgiSep[i] < iItem)
  549. {
  550. y = y - _cyTile + _cySepTile;
  551. }
  552. }
  553. pptOut->x = _cxMargin;
  554. pptOut->y = y;
  555. }
  556. int SFTBarHost::_InsertListViewItem(int iPos, PaneItem *pitem)
  557. {
  558. ASSERT(pitem);
  559. int iItem = -1;
  560. IShellFolder *psf = NULL;
  561. LPCITEMIDLIST pidl = NULL;
  562. LVITEM lvi;
  563. lvi.pszText = NULL;
  564. lvi.mask = 0;
  565. // If necessary, tell listview that we want to use column 1
  566. // as the subtitle.
  567. if (_iconsize == ICONSIZE_LARGE && pitem->HasSubtitle())
  568. {
  569. const static UINT One = 1;
  570. lvi.mask = LVIF_COLUMNS;
  571. lvi.cColumns = 1;
  572. lvi.puColumns = const_cast<UINT*>(&One);
  573. }
  574. ASSERT(!pitem->IsSeparator());
  575. lvi.mask |= LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
  576. if (FAILED(GetFolderAndPidl(pitem, &psf, &pidl)))
  577. {
  578. goto exit;
  579. }
  580. if (lvi.mask & LVIF_IMAGE)
  581. {
  582. lvi.iImage = AddImageForItem(pitem, psf, pidl, iPos);
  583. }
  584. if (lvi.mask & LVIF_TEXT)
  585. {
  586. if (_iconsize == ICONSIZE_SMALL && pitem->HasSubtitle())
  587. {
  588. lvi.pszText = SubtitleOfItem(pitem, psf, pidl);
  589. }
  590. else
  591. {
  592. lvi.pszText = DisplayNameOfItem(pitem, psf, pidl, SHGDN_NORMAL);
  593. }
  594. if (!lvi.pszText)
  595. {
  596. goto exit;
  597. }
  598. }
  599. lvi.iItem = iPos;
  600. lvi.iSubItem = 0;
  601. lvi.lParam = reinterpret_cast<LPARAM>(pitem);
  602. iItem = ListView_InsertItem(_hwndList, &lvi);
  603. // If the item has a subtitle, add it.
  604. // If this fails, don't worry. The subtitle is just a fluffy bonus thing.
  605. if (iItem >= 0 && (lvi.mask & LVIF_COLUMNS))
  606. {
  607. lvi.iItem = iItem;
  608. lvi.iSubItem = 1;
  609. lvi.mask = LVIF_TEXT;
  610. SHFree(lvi.pszText);
  611. lvi.pszText = SubtitleOfItem(pitem, psf, pidl);
  612. if (lvi.pszText)
  613. {
  614. ListView_SetItem(_hwndList, &lvi);
  615. }
  616. }
  617. exit:
  618. ATOMICRELEASE(psf);
  619. SHFree(lvi.pszText);
  620. return iItem;
  621. }
  622. // Add items to our view, or at least as many as will fit
  623. void SFTBarHost::_RepopulateList()
  624. {
  625. //
  626. // Kill the async enum animation now that we're ready
  627. //
  628. if (_idtAni)
  629. {
  630. KillTimer(_hwnd, _idtAni);
  631. _idtAni = 0;
  632. }
  633. if (_hwndAni)
  634. {
  635. if (_hBrushAni)
  636. {
  637. DeleteObject(_hBrushAni);
  638. _hBrushAni = NULL;
  639. }
  640. DestroyWindow(_hwndAni);
  641. _hwndAni = NULL;
  642. }
  643. // Let's see if anything changed
  644. BOOL fChanged = FALSE;
  645. if (_fForceChange)
  646. {
  647. _fForceChange = FALSE;
  648. fChanged = TRUE;
  649. }
  650. else if (_dpaEnum.GetPtrCount() == _dpaEnumNew.GetPtrCount())
  651. {
  652. int iMax = _dpaEnum.GetPtrCount();
  653. int i;
  654. for (i=0; i<iMax; i++)
  655. {
  656. if (!_dpaEnum.FastGetPtr(i)->IsEqual(_dpaEnumNew.FastGetPtr(i)))
  657. {
  658. fChanged = TRUE;
  659. break;
  660. }
  661. }
  662. }
  663. else
  664. {
  665. fChanged = TRUE;
  666. }
  667. // No need to do any real work if nothing changed.
  668. if (fChanged)
  669. {
  670. // Now move the _dpaEnumNew to _dpaEnum
  671. // Clear out the old DPA, we don't need it anymore
  672. _dpaEnum.EnumCallbackEx(PaneItem::DPAEnumCallback, (void *)NULL);
  673. _dpaEnum.DeleteAllPtrs();
  674. // switch DPAs now
  675. CDPA<PaneItem> dpaTemp = _dpaEnum;
  676. _dpaEnum = _dpaEnumNew;
  677. _dpaEnumNew = dpaTemp;
  678. _InternalRepopulateList();
  679. }
  680. else
  681. {
  682. // Clear out the new DPA, we don't need it anymore
  683. _dpaEnumNew.EnumCallbackEx(PaneItem::DPAEnumCallback, (void *)NULL);
  684. _dpaEnumNew.DeleteAllPtrs();
  685. }
  686. _fNeedsRepopulate = FALSE;
  687. }
  688. // The internal version is when we decide to repopulate on our own,
  689. // not at the prompting of the background thread. (Therefore, we
  690. // don't nuke the animation.)
  691. void SFTBarHost::_InternalRepopulateList()
  692. {
  693. //
  694. // Start with a clean slate.
  695. //
  696. ListView_DeleteAllItems(_hwndList);
  697. if (_IsPrivateImageList())
  698. {
  699. ImageList_RemoveAll(_himl);
  700. }
  701. int cPinned = 0;
  702. int cNormal = 0;
  703. _DebugConsistencyCheck();
  704. SetWindowRedraw(_hwndList, FALSE);
  705. DEBUG_CODE(_fPopulating++);
  706. //
  707. // To populate the list, we toss the pinned items at the top,
  708. // then let the enumerated items flow beneath them.
  709. //
  710. // Separator "items" don't get added to the listview. They
  711. // are added to the special "separators list".
  712. //
  713. // WARNING! We are computing incrementally the same values as
  714. // _ComputeListViewItemPosition. Keep the two in sync.
  715. //
  716. int iPos; // the slot we are trying to fill
  717. int iEnum; // the item index we will fill it from
  718. int y = 0; // where the next item should be placed
  719. BOOL fSepSeen = FALSE; // have we seen a separator yet?
  720. PaneItem *pitem; // the item that will fill it
  721. _cSep = 0; // no separators (yet)
  722. RECT rc;
  723. GetClientRect(_hwndList, &rc);
  724. //
  725. // Subtract out the bonus separator used by SPP_PROGLIST
  726. //
  727. if (_iThemePart == SPP_PROGLIST)
  728. {
  729. rc.bottom -= _cySep;
  730. }
  731. // Note that the loop control must be a _dpaEnum.GetPtr(), not a
  732. // _dpaEnum.FastGetPtr(), because iEnum can go past the end of the
  733. // array if we do't have enough items to fill the view.
  734. //
  735. //
  736. // The "while" condition is "there is room for another non-separator
  737. // item and there are items remaining in the enumeration".
  738. BOOL fCheckMaxLength = HasDynamicContent();
  739. for (iPos = iEnum = 0;
  740. (pitem = _dpaEnum.GetPtr(iEnum)) != NULL;
  741. iEnum++)
  742. {
  743. if (fCheckMaxLength)
  744. {
  745. if (y + _cyTile > rc.bottom)
  746. {
  747. break;
  748. }
  749. // Once we hit a separator, check if we satisfied the number
  750. // of normal items. We have to wait until a separator is
  751. // hit, because _cNormalDesired can be zero; otherwise we
  752. // would end up stopping before adding even the pinned items!
  753. if (fSepSeen && cNormal >= _cNormalDesired)
  754. {
  755. break;
  756. }
  757. }
  758. #ifdef DEBUG
  759. // Make sure that we are in sync with _ComputeListViewItemPosition
  760. POINT pt;
  761. _ComputeListViewItemPosition(iPos, &pt);
  762. ASSERT(pt.x == _cxMargin);
  763. ASSERT(pt.y == y);
  764. #endif
  765. if (pitem->IsSeparator())
  766. {
  767. fSepSeen = TRUE;
  768. // Add the separator, but only if it actually separate something.
  769. // If this EVAL fires, it means somebody added a separator
  770. // and MAX_SEPARATORS needs to be increased.
  771. if (iPos > 0 && EVAL(_cSep < ARRAYSIZE(_rgiSep)))
  772. {
  773. _rgiSep[_cSep++] = iPos++;
  774. y += _cySepTile;
  775. }
  776. }
  777. else
  778. {
  779. if (_InsertListViewItem(iPos, pitem) >= 0)
  780. {
  781. pitem->_iPos = iPos++;
  782. y += _cyTile;
  783. if (pitem->IsPinned())
  784. {
  785. cPinned++;
  786. }
  787. else
  788. {
  789. cNormal++;
  790. }
  791. }
  792. }
  793. }
  794. //
  795. // If the last item was a separator, then delete it
  796. // since it's not actually separating anything.
  797. //
  798. if (_cSep && _rgiSep[_cSep-1] == iPos - 1)
  799. {
  800. _cSep--;
  801. }
  802. _cPinned = cPinned;
  803. //
  804. // Now put the items where they belong.
  805. //
  806. _RepositionItems();
  807. DEBUG_CODE(_fPopulating--);
  808. SetWindowRedraw(_hwndList, TRUE);
  809. // Now, we need to go update our cached bitmap version of the start menu.
  810. _SendNotify(_hwnd, SMN_NEEDREPAINT, NULL);
  811. _DebugConsistencyCheck();
  812. }
  813. LRESULT SFTBarHost::_OnCreate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  814. {
  815. RECT rc;
  816. GetClientRect(_hwnd, &rc);
  817. if (_hTheme)
  818. {
  819. GetThemeMargins(_hTheme, NULL, _iThemePart, 0, TMT_CONTENTMARGINS, &rc, &_margins);
  820. }
  821. else
  822. {
  823. _margins.cyTopHeight = 2*GetSystemMetrics(SM_CXEDGE);
  824. _margins.cxLeftWidth = 2*GetSystemMetrics(SM_CXEDGE);
  825. _margins.cxRightWidth = 2*GetSystemMetrics(SM_CXEDGE);
  826. }
  827. //
  828. // Now to create the listview.
  829. //
  830. DWORD dwStyle = WS_CHILD | WS_VISIBLE |
  831. WS_CLIPCHILDREN | WS_CLIPSIBLINGS |
  832. // Do not set WS_TABSTOP; SFTBarHost handles tabbing
  833. LVS_LIST |
  834. LVS_SINGLESEL |
  835. LVS_NOSCROLL |
  836. LVS_SHAREIMAGELISTS;
  837. if (_dwFlags & HOSTF_CANRENAME)
  838. {
  839. dwStyle |= LVS_EDITLABELS;
  840. }
  841. DWORD dwExStyle = 0;
  842. _hwndList = CreateWindowEx(dwExStyle, WC_LISTVIEW, NULL, dwStyle,
  843. _margins.cxLeftWidth, _margins.cyTopHeight, rc.right, rc.bottom, // no point in being too exact, we'll be resized later
  844. _hwnd, NULL,
  845. _Module.GetModuleInstance(), NULL);
  846. if (!_hwndList)
  847. return -1;
  848. //
  849. // Don't freak out if this fails. It just means that the accessibility
  850. // stuff won't be perfect.
  851. //
  852. SetAccessibleSubclassWindow(_hwndList);
  853. //
  854. // Create two dummy columns. We will never display them, but they
  855. // are necessary so that we have someplace to put our subtitle.
  856. //
  857. LVCOLUMN lvc;
  858. lvc.mask = LVCF_WIDTH;
  859. lvc.cx = 1;
  860. ListView_InsertColumn(_hwndList, 0, &lvc);
  861. ListView_InsertColumn(_hwndList, 1, &lvc);
  862. //
  863. // If we are topmost, then force the tooltip topmost, too.
  864. // Otherwise we end up covering our own tooltip!
  865. //
  866. if (GetWindowExStyle(GetAncestor(_hwnd, GA_ROOT)) & WS_EX_TOPMOST)
  867. {
  868. HWND hwndTT = ListView_GetToolTips(_hwndList);
  869. if (hwndTT)
  870. {
  871. SetWindowPos(hwndTT, HWND_TOPMOST, 0, 0, 0, 0,
  872. SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
  873. }
  874. }
  875. // Must do Marlett after doing the listview font, because we base the
  876. // Marlett font metrics on the listview font metrics (so they match)
  877. if (_dwFlags & HOSTF_CASCADEMENU)
  878. {
  879. if (!_CreateMarlett())
  880. return -1;
  881. }
  882. // We can survive if these objects fail to be created
  883. CoCreateInstance(CLSID_DragDropHelper, NULL, CLSCTX_INPROC_SERVER,
  884. IID_PPV_ARG(IDropTargetHelper, &_pdth));
  885. CoCreateInstance(CLSID_DragDropHelper, NULL, CLSCTX_INPROC_SERVER,
  886. IID_PPV_ARG(IDragSourceHelper, &_pdsh));
  887. //
  888. // If this fails, no big whoop - you just don't get
  889. // drag/drop, boo hoo.
  890. //
  891. RegisterDragDrop(_hwndList, this);
  892. // If this fails, then disable "fancy droptarget" since we won't be
  893. // able to manage it properly.
  894. if (!SetWindowSubclass(_hwndList, s_DropTargetSubclassProc, 0,
  895. reinterpret_cast<DWORD_PTR>(this)))
  896. {
  897. ATOMICRELEASE(_pdth);
  898. }
  899. if (!_dpaEnum.Create(4))
  900. return -1;
  901. if (!_dpaEnumNew.Create(4))
  902. return -1;
  903. //-------------------------
  904. // Imagelist goo
  905. int iIconSize = ReadIconSize();
  906. Shell_GetImageLists(iIconSize ? &_himl : NULL, iIconSize ? NULL : &_himl);
  907. if (!_himl)
  908. return -1;
  909. // Preload values in case GetIconSize barfs
  910. _cxIcon = GetSystemMetrics(iIconSize ? SM_CXICON : SM_CXSMICON);
  911. _cyIcon = GetSystemMetrics(iIconSize ? SM_CYICON : SM_CYSMICON);
  912. ImageList_GetIconSize(_himl, &_cxIcon, &_cyIcon);
  913. //
  914. // If we asked for the MEDIUM-sized icons, then create the real
  915. // image list based on the system image list.
  916. //
  917. _iconsize = (ICONSIZE)iIconSize;
  918. if (_iconsize == ICONSIZE_MEDIUM)
  919. {
  920. // These upcoming computations rely on the fact that ICONSIZE_LARGE
  921. // and ICONSIZE_MEDIUM are both nonzero so when we fetched the icon
  922. // sizes for ICONSIZE_MEDIUM, we got SM_CXICON (large).
  923. COMPILETIME_ASSERT(ICONSIZE_LARGE && ICONSIZE_MEDIUM);
  924. // SM_CXICON is the size of shell large icons. SM_CXSMICON is *not*
  925. // the size of shell small icons! It is the size of caption small
  926. // icons. Shell small icons are always 50% of shell large icons.
  927. // We want to be halfway between shell small (50%) and shell
  928. // large (100%); i.e., we want 75%.
  929. _cxIcon = _cxIcon * 3 / 4;
  930. _cyIcon = _cyIcon * 3 / 4;
  931. //
  932. // When the user is in Large Icon mode, we end up choosing 36x36
  933. // (halfway between 24x24 and 48x48), but there is no 36x36 icon
  934. // in the icon resource. But we do have a 32, which is close
  935. // enough. (If we didn't do this, then the 36x36 icon would be
  936. // the 32x32 icon stretched, which looks ugly.)
  937. //
  938. // So any square icon in the range 28..36 we round to 32.
  939. //
  940. if (_cxIcon == _cyIcon && _cxIcon >= 28 && _cxIcon <= 36)
  941. {
  942. _cxIcon = _cyIcon = 32;
  943. }
  944. // It is critical that we overwrite _himl even on failure, so our
  945. // destructor doesn't try to destroy a system image list!
  946. _himl = ImageList_Create(_cxIcon, _cyIcon, ImageList_GetFlags(_himl), 8, 2);
  947. if (!_himl)
  948. {
  949. return -1;
  950. }
  951. }
  952. ListView_SetImageList(_hwndList, _himl, LVSIL_NORMAL);
  953. // Register for SHCNE_UPDATEIMAGE so we know when to reload our icons
  954. _RegisterNotify(SFTHOST_HOSTNOTIFY_UPDATEIMAGE, SHCNE_UPDATEIMAGE, NULL, FALSE);
  955. //-------------------------
  956. _cxMargin = GetSystemMetrics(SM_CXEDGE);
  957. _cyMargin = GetSystemMetrics(SM_CYEDGE);
  958. _cyTilePadding = 0;
  959. _ComputeTileMetrics();
  960. //
  961. // In the themed case, the designers want a narrow separator.
  962. // In the nonthemed case, we need a fat separator because we need
  963. // to draw an etch (which requires two pixels).
  964. //
  965. if (_hTheme)
  966. {
  967. SIZE siz={0};
  968. GetThemePartSize(_hTheme, NULL, _iThemePartSep, 0, NULL, TS_TRUE, &siz);
  969. _cySep = siz.cy;
  970. }
  971. else
  972. {
  973. _cySep = GetSystemMetrics(SM_CYEDGE);
  974. }
  975. _cySepTile = 4 * _cySep;
  976. ASSERT(rc.left == 0 && rc.top == 0); // Should still be a client rectangle
  977. _SetTileWidth(rc.right); // so rc.right = RCWIDTH and rc.bottom = RCHEIGHT
  978. // In tile view, full-row-select really means full-tile-select
  979. DWORD dwLvExStyle = LVS_EX_INFOTIP |
  980. LVS_EX_FULLROWSELECT;
  981. if (!GetSystemMetrics(SM_REMOTESESSION) && !GetSystemMetrics(SM_REMOTECONTROL))
  982. {
  983. dwLvExStyle |= LVS_EX_DOUBLEBUFFER;
  984. }
  985. ListView_SetExtendedListViewStyleEx(_hwndList, dwLvExStyle,
  986. dwLvExStyle);
  987. if (!_hTheme)
  988. {
  989. ListView_SetTextColor(_hwndList, GetSysColor(COLOR_MENUTEXT));
  990. _clrHot = GetSysColor(COLOR_MENUTEXT);
  991. _clrBG = GetSysColor(COLOR_MENU); // default color for no theme case
  992. _clrSubtitle = CLR_NONE;
  993. }
  994. else
  995. {
  996. COLORREF clrText;
  997. GetThemeColor(_hTheme, _iThemePart, 0, TMT_HOTTRACKING, &_clrHot); // todo - use state
  998. GetThemeColor(_hTheme, _iThemePart, 0, TMT_CAPTIONTEXT, &_clrSubtitle);
  999. _clrBG = CLR_NONE;
  1000. GetThemeColor(_hTheme, _iThemePart, 0, TMT_TEXTCOLOR, &clrText);
  1001. ListView_SetTextColor(_hwndList, clrText);
  1002. ListView_SetOutlineColor(_hwndList, _clrHot);
  1003. }
  1004. ListView_SetBkColor(_hwndList, _clrBG);
  1005. ListView_SetTextBkColor(_hwndList, _clrBG);
  1006. ListView_SetView(_hwndList, LV_VIEW_TILE);
  1007. // USER will send us a WM_SIZE after the WM_CREATE, which will cause
  1008. // the listview to repopulate, if we chose to repopulate in the
  1009. // foreground.
  1010. return 0;
  1011. }
  1012. BOOL SFTBarHost::_CreateMarlett()
  1013. {
  1014. HDC hdc = GetDC(_hwndList);
  1015. if (hdc)
  1016. {
  1017. HFONT hfPrev = SelectFont(hdc, GetWindowFont(_hwndList));
  1018. if (hfPrev)
  1019. {
  1020. TEXTMETRIC tm;
  1021. if (GetTextMetrics(hdc, &tm))
  1022. {
  1023. LOGFONT lf;
  1024. ZeroMemory(&lf, sizeof(lf));
  1025. lf.lfHeight = tm.tmAscent;
  1026. lf.lfWeight = FW_NORMAL;
  1027. lf.lfCharSet = SYMBOL_CHARSET;
  1028. StrCpyN(lf.lfFaceName, TEXT("Marlett"), ARRAYSIZE(lf.lfFaceName));
  1029. _hfMarlett = CreateFontIndirect(&lf);
  1030. if (_hfMarlett)
  1031. {
  1032. SelectFont(hdc, _hfMarlett);
  1033. if (GetTextMetrics(hdc, &tm))
  1034. {
  1035. _tmAscentMarlett = tm.tmAscent;
  1036. SIZE siz;
  1037. if (GetTextExtentPoint(hdc, TEXT("8"), 1, &siz))
  1038. {
  1039. _cxMarlett = siz.cx;
  1040. }
  1041. }
  1042. }
  1043. }
  1044. SelectFont(hdc, hfPrev);
  1045. }
  1046. ReleaseDC(_hwndList, hdc);
  1047. }
  1048. return _cxMarlett;
  1049. }
  1050. void SFTBarHost::_CreateBoldFont()
  1051. {
  1052. if (!_hfBold)
  1053. {
  1054. HFONT hf = GetWindowFont(_hwndList);
  1055. if (hf)
  1056. {
  1057. LOGFONT lf;
  1058. if (GetObject(hf, sizeof(lf), &lf))
  1059. {
  1060. lf.lfWeight = FW_BOLD;
  1061. SHAdjustLOGFONT(&lf); // locale-specific adjustments
  1062. _hfBold = CreateFontIndirect(&lf);
  1063. }
  1064. }
  1065. }
  1066. }
  1067. void SFTBarHost::_ReloadText()
  1068. {
  1069. int iItem;
  1070. for (iItem = ListView_GetItemCount(_hwndList) - 1; iItem >= 0; iItem--)
  1071. {
  1072. TCHAR szText[MAX_PATH];
  1073. LVITEM lvi;
  1074. lvi.iItem = iItem;
  1075. lvi.iSubItem = 0;
  1076. lvi.mask = LVIF_PARAM | LVIF_TEXT;
  1077. lvi.pszText = szText;
  1078. lvi.cchTextMax = ARRAYSIZE(szText);
  1079. if (ListView_GetItem(_hwndList, &lvi))
  1080. {
  1081. PaneItem *pitem = _GetItemFromLVLParam(lvi.lParam);
  1082. if (!pitem)
  1083. {
  1084. break;
  1085. }
  1086. // Update the display name in case it changed behind our back.
  1087. // Note that this is not redundant with the creation of the items
  1088. // in _InsertListViewItem because this is done only on the second
  1089. // and subsequent enumeration. (We assume the first enumeration
  1090. // is just peachy.)
  1091. lvi.iItem = iItem;
  1092. lvi.iSubItem = 0;
  1093. lvi.mask = LVIF_TEXT;
  1094. lvi.pszText = _DisplayNameOfItem(pitem, SHGDN_NORMAL);
  1095. if (lvi.pszText)
  1096. {
  1097. if (StrCmpN(szText, lvi.pszText, ARRAYSIZE(szText)) != 0)
  1098. {
  1099. ListView_SetItem(_hwndList, &lvi);
  1100. _SendNotify(_hwnd, SMN_NEEDREPAINT, NULL);
  1101. }
  1102. SHFree(lvi.pszText);
  1103. }
  1104. }
  1105. }
  1106. }
  1107. void SFTBarHost::_RevalidateItems()
  1108. {
  1109. // If client does not require revalidation, then assume still valid
  1110. if (!(_dwFlags & HOSTF_REVALIDATE))
  1111. {
  1112. return;
  1113. }
  1114. int iItem;
  1115. for (iItem = ListView_GetItemCount(_hwndList) - 1; iItem >= 0; iItem--)
  1116. {
  1117. PaneItem *pitem = _GetItemFromLV(iItem);
  1118. if (!pitem || !IsItemStillValid(pitem))
  1119. {
  1120. _fEnumValid = FALSE;
  1121. break;
  1122. }
  1123. }
  1124. }
  1125. void SFTBarHost::_RevalidatePostPopup()
  1126. {
  1127. _RevalidateItems();
  1128. if (_dwFlags & HOSTF_RELOADTEXT)
  1129. {
  1130. SetTimer(_hwnd, IDT_RELOADTEXT, 250, NULL);
  1131. }
  1132. // If the list is still good, then don't bother redoing it
  1133. if (!_fEnumValid)
  1134. {
  1135. _EnumerateContents(FALSE);
  1136. }
  1137. }
  1138. void SFTBarHost::_EnumerateContents(BOOL fUrgent)
  1139. {
  1140. // If we have deferred refreshes until the window closes, then
  1141. // leave it alone.
  1142. if (!fUrgent && _fNeedsRepopulate)
  1143. {
  1144. return;
  1145. }
  1146. // If we're already enumerating, then just remember to do it again
  1147. if (_fBGTask)
  1148. {
  1149. // accumulate urgency so a low-priority request + an urgent request
  1150. // is treated as urgent
  1151. _fRestartUrgent |= fUrgent;
  1152. _fRestartEnum = TRUE;
  1153. return;
  1154. }
  1155. _fRestartEnum = FALSE;
  1156. _fRestartUrgent = FALSE;
  1157. // If the list is still good, then don't bother redoing it
  1158. if (_fEnumValid && !fUrgent)
  1159. {
  1160. return;
  1161. }
  1162. // This re-enumeration will make everything valid.
  1163. _fEnumValid = TRUE;
  1164. // Clear out all the leftover stuff from the previous enumeration
  1165. _dpaEnumNew.EnumCallbackEx(PaneItem::DPAEnumCallback, (void *)NULL);
  1166. _dpaEnumNew.DeleteAllPtrs();
  1167. // Let client do some work on the foreground thread
  1168. PrePopulate();
  1169. // Finish the enumeration either on the background thread (if requested)
  1170. // or on the foreground thread (if can't enumerate in the background).
  1171. HRESULT hr;
  1172. if (NeedBackgroundEnum())
  1173. {
  1174. if (_psched)
  1175. {
  1176. hr = S_OK;
  1177. }
  1178. else
  1179. {
  1180. // We need a separate task scheduler for each instance
  1181. hr = CoCreateInstance(CLSID_ShellTaskScheduler, NULL, CLSCTX_INPROC_SERVER,
  1182. IID_PPV_ARG(IShellTaskScheduler, &_psched));
  1183. }
  1184. if (SUCCEEDED(hr))
  1185. {
  1186. CBGEnum *penum = new CBGEnum(this, fUrgent);
  1187. if (penum)
  1188. {
  1189. // We want to run at a priority slightly above normal
  1190. // because the user is sitting there waiting for the
  1191. // enumeration to complete.
  1192. #define ITSAT_BGENUM_PRIORITY (ITSAT_DEFAULT_PRIORITY + 0x1000)
  1193. hr = _psched->AddTask(penum, TOID_SFTBarHostBackgroundEnum, (DWORD_PTR)this, ITSAT_BGENUM_PRIORITY);
  1194. if (SUCCEEDED(hr))
  1195. {
  1196. _fBGTask = TRUE;
  1197. if (ListView_GetItemCount(_hwndList) == 0)
  1198. {
  1199. //
  1200. // Set a timer that will create the "please wait"
  1201. // animation if the enumeration takes too long.
  1202. //
  1203. _idtAni = IDT_ASYNCENUM;
  1204. SetTimer(_hwnd, _idtAni, 1000, NULL);
  1205. }
  1206. }
  1207. penum->Release();
  1208. }
  1209. }
  1210. }
  1211. if (!_fBGTask)
  1212. {
  1213. // Fallback: Do it on the foreground thread
  1214. _EnumerateContentsBackground();
  1215. _RepopulateList();
  1216. }
  1217. }
  1218. void SFTBarHost::_EnumerateContentsBackground()
  1219. {
  1220. // Start over
  1221. DEBUG_CODE(_fEnumerating = TRUE);
  1222. EnumItems();
  1223. DEBUG_CODE(_fEnumerating = FALSE);
  1224. #ifdef _ALPHA_
  1225. // Alpha compiler is lame
  1226. _dpaEnumNew.Sort((CDPA<PaneItem>::_PFNDPACOMPARE)_SortItemsAfterEnum, (LPARAM)this);
  1227. #else
  1228. _dpaEnumNew.SortEx(_SortItemsAfterEnum, this);
  1229. #endif
  1230. }
  1231. int CALLBACK SFTBarHost::_SortItemsAfterEnum(PaneItem *p1, PaneItem *p2, SFTBarHost *self)
  1232. {
  1233. //
  1234. // Put all pinned items (sorted by pin position) ahead of unpinned items.
  1235. //
  1236. if (p1->IsPinned())
  1237. {
  1238. if (p2->IsPinned())
  1239. {
  1240. return p1->GetPinPos() - p2->GetPinPos();
  1241. }
  1242. return -1;
  1243. }
  1244. else if (p2->IsPinned())
  1245. {
  1246. return +1;
  1247. }
  1248. //
  1249. // Both unpinned - let the client decide.
  1250. //
  1251. return self->CompareItems(p1, p2);
  1252. }
  1253. SFTBarHost::~SFTBarHost()
  1254. {
  1255. // We shouldn't be destroyed while in these temporary states.
  1256. // If this fires, it's possible that somebody incremented
  1257. // _fListUnstable/_fPopulating and forgot to decrement it.
  1258. ASSERT(!_fListUnstable);
  1259. ASSERT(!_fPopulating);
  1260. ATOMICRELEASE(_pdth);
  1261. ATOMICRELEASE(_pdsh);
  1262. ATOMICRELEASE(_psched);
  1263. ASSERT(_pdtoDragOut == NULL);
  1264. if (_dpaEnum)
  1265. {
  1266. _dpaEnum.DestroyCallbackEx(PaneItem::DPAEnumCallback, (void *)NULL);
  1267. }
  1268. if (_dpaEnumNew)
  1269. {
  1270. _dpaEnumNew.DestroyCallbackEx(PaneItem::DPAEnumCallback, (void *)NULL);
  1271. }
  1272. if (_IsPrivateImageList() && _himl)
  1273. {
  1274. ImageList_Destroy(_himl);
  1275. }
  1276. if (_hfList)
  1277. {
  1278. DeleteObject(_hfList);
  1279. }
  1280. if (_hfBold)
  1281. {
  1282. DeleteObject(_hfBold);
  1283. }
  1284. if (_hfMarlett)
  1285. {
  1286. DeleteObject(_hfMarlett);
  1287. }
  1288. if (_hBrushAni)
  1289. {
  1290. DeleteObject(_hBrushAni);
  1291. }
  1292. }
  1293. LRESULT SFTBarHost::_OnDestroy(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1294. {
  1295. UINT id;
  1296. for (id = 0; id < SFTHOST_MAXNOTIFY; id++)
  1297. {
  1298. UnregisterNotify(id);
  1299. }
  1300. if (_hwndList)
  1301. {
  1302. RevokeDragDrop(_hwndList);
  1303. }
  1304. return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
  1305. }
  1306. LRESULT SFTBarHost::_OnNcDestroy(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1307. {
  1308. // WARNING! "this" might be NULL (if WM_NCCREATE failed).
  1309. LRESULT lres = DefWindowProc(hwnd, uMsg, wParam, lParam);
  1310. SetWindowPtr0(hwnd, 0);
  1311. if (this) {
  1312. _hwndList = NULL;
  1313. _hwnd = NULL;
  1314. if (_psched)
  1315. {
  1316. // Remove all tasks now, and wait for them to finish
  1317. _psched->RemoveTasks(TOID_NULL, ITSAT_DEFAULT_LPARAM, TRUE);
  1318. ATOMICRELEASE(_psched);
  1319. }
  1320. Release();
  1321. }
  1322. return lres;
  1323. }
  1324. LRESULT SFTBarHost::_OnNotify(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1325. {
  1326. LPNMHDR pnm = reinterpret_cast<LPNMHDR>(lParam);
  1327. if (pnm->hwndFrom == _hwndList)
  1328. {
  1329. switch (pnm->code)
  1330. {
  1331. case NM_CUSTOMDRAW:
  1332. return _OnLVCustomDraw(CONTAINING_RECORD(
  1333. CONTAINING_RECORD(pnm, NMCUSTOMDRAW, hdr),
  1334. NMLVCUSTOMDRAW, nmcd));
  1335. case NM_CLICK:
  1336. return _OnLVNItemActivate(CONTAINING_RECORD(pnm, NMITEMACTIVATE, hdr));
  1337. case NM_RETURN:
  1338. return _ActivateItem(_GetLVCurSel(), AIF_KEYBOARD);
  1339. case NM_KILLFOCUS:
  1340. // On loss of focus, deselect all items so they all draw
  1341. // in the plain state.
  1342. ListView_SetItemState(_hwndList, -1, 0, LVIS_SELECTED | LVIS_FOCUSED);
  1343. break;
  1344. case LVN_GETINFOTIP:
  1345. return _OnLVNGetInfoTip(CONTAINING_RECORD(pnm, NMLVGETINFOTIP, hdr));
  1346. case LVN_BEGINDRAG:
  1347. case LVN_BEGINRDRAG:
  1348. return _OnLVNBeginDrag(CONTAINING_RECORD(pnm, NMLISTVIEW, hdr));
  1349. case LVN_BEGINLABELEDIT:
  1350. return _OnLVNBeginLabelEdit(CONTAINING_RECORD(pnm, NMLVDISPINFO, hdr));
  1351. case LVN_ENDLABELEDIT:
  1352. return _OnLVNEndLabelEdit(CONTAINING_RECORD(pnm, NMLVDISPINFO, hdr));
  1353. case LVN_KEYDOWN:
  1354. return _OnLVNKeyDown(CONTAINING_RECORD(pnm, NMLVKEYDOWN, hdr));
  1355. }
  1356. }
  1357. else
  1358. {
  1359. switch (pnm->code)
  1360. {
  1361. case SMN_INITIALUPDATE:
  1362. _EnumerateContents(FALSE);
  1363. break;
  1364. case SMN_POSTPOPUP:
  1365. _RevalidatePostPopup();
  1366. break;
  1367. case SMN_GETMINSIZE:
  1368. return _OnSMNGetMinSize(CONTAINING_RECORD(pnm, SMNGETMINSIZE, hdr));
  1369. break;
  1370. case SMN_FINDITEM:
  1371. return _OnSMNFindItem(CONTAINING_RECORD(pnm, SMNDIALOGMESSAGE, hdr));
  1372. case SMN_DISMISS:
  1373. return _OnSMNDismiss();
  1374. case SMN_APPLYREGION:
  1375. return HandleApplyRegion(_hwnd, _hTheme, (SMNMAPPLYREGION *)lParam, _iThemePart, 0);
  1376. case SMN_SHELLMENUDISMISSED:
  1377. _iCascading = -1;
  1378. return 0;
  1379. }
  1380. }
  1381. // Give derived class a chance to respond
  1382. return OnWndProc(hwnd, uMsg, wParam, lParam);
  1383. }
  1384. LRESULT SFTBarHost::_OnTimer(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1385. {
  1386. switch (wParam)
  1387. {
  1388. case IDT_ASYNCENUM:
  1389. KillTimer(hwnd, wParam);
  1390. // For some reason, we sometimes get spurious WM_TIMER messages,
  1391. // so ignore them if we aren't expecting them.
  1392. if (_idtAni)
  1393. {
  1394. _idtAni = 0;
  1395. if (_hwndList && !_hwndAni)
  1396. {
  1397. DWORD dwStyle = WS_CHILD | WS_VISIBLE |
  1398. WS_CLIPCHILDREN | WS_CLIPSIBLINGS |
  1399. ACS_AUTOPLAY | ACS_TIMER | ACS_TRANSPARENT;
  1400. RECT rcClient;
  1401. GetClientRect(_hwnd, &rcClient);
  1402. int x = (RECTWIDTH(rcClient) - ANIWND_WIDTH)/2; // IDA_SEARCH is ANIWND_WIDTH pix wide
  1403. int y = (RECTHEIGHT(rcClient) - ANIWND_HEIGHT)/2; // IDA_SEARCH is ANIWND_HEIGHT pix tall
  1404. _hwndAni = CreateWindow(ANIMATE_CLASS, NULL, dwStyle,
  1405. x, y, 0, 0,
  1406. _hwnd, NULL,
  1407. _Module.GetModuleInstance(), NULL);
  1408. if (_hwndAni)
  1409. {
  1410. SetWindowPos(_hwndAni, HWND_TOP, 0, 0, 0, 0,
  1411. SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
  1412. #define IDA_SEARCH 150 // from shell32
  1413. Animate_OpenEx(_hwndAni, GetModuleHandle(TEXT("SHELL32")), MAKEINTRESOURCE(IDA_SEARCH));
  1414. }
  1415. }
  1416. }
  1417. return 0;
  1418. case IDT_RELOADTEXT:
  1419. KillTimer(hwnd, wParam);
  1420. _ReloadText();
  1421. break;
  1422. case IDT_REFRESH:
  1423. KillTimer(hwnd, wParam);
  1424. PostMessage(hwnd, SFTBM_REFRESH, FALSE, 0);
  1425. break;
  1426. }
  1427. // Give derived class a chance to respond
  1428. return OnWndProc(hwnd, uMsg, wParam, lParam);
  1429. }
  1430. LRESULT SFTBarHost::_OnSetFocus(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1431. {
  1432. if (_hwndList)
  1433. {
  1434. SetFocus(_hwndList);
  1435. }
  1436. return 0;
  1437. }
  1438. LRESULT SFTBarHost::_OnEraseBackground(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1439. {
  1440. RECT rc;
  1441. GetClientRect(hwnd, &rc);
  1442. if (_hTheme)
  1443. DrawThemeBackground(_hTheme, (HDC)wParam, _iThemePart, 0, &rc, 0);
  1444. else
  1445. {
  1446. SHFillRectClr((HDC)wParam, &rc, _clrBG);
  1447. if (_iThemePart == SPP_PLACESLIST) // we set this even in non-theme case, its how we tell them apart
  1448. DrawEdge((HDC)wParam, &rc, EDGE_ETCHED, BF_LEFT);
  1449. }
  1450. return TRUE;
  1451. }
  1452. LRESULT SFTBarHost::_OnLVCustomDraw(LPNMLVCUSTOMDRAW plvcd)
  1453. {
  1454. _DebugConsistencyCheck();
  1455. switch (plvcd->nmcd.dwDrawStage)
  1456. {
  1457. case CDDS_PREPAINT:
  1458. return _OnLVPrePaint(plvcd);
  1459. case CDDS_ITEMPREPAINT:
  1460. return _OnLVItemPrePaint(plvcd);
  1461. case CDDS_ITEMPREPAINT | CDDS_SUBITEM:
  1462. return _OnLVSubItemPrePaint(plvcd);
  1463. case CDDS_ITEMPOSTPAINT:
  1464. return _OnLVItemPostPaint(plvcd);
  1465. case CDDS_POSTPAINT:
  1466. return _OnLVPostPaint(plvcd);
  1467. }
  1468. return CDRF_DODEFAULT;
  1469. }
  1470. //
  1471. // Catch WM_PAINT messages headed to ListView and hide any drop effect
  1472. // so it doesn't interfere with painting. WM_PAINT messages might nest
  1473. // under extreme conditions, so do this only at outer level.
  1474. //
  1475. LRESULT CALLBACK SFTBarHost::s_DropTargetSubclassProc(
  1476. HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
  1477. UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
  1478. {
  1479. SFTBarHost *self = reinterpret_cast<SFTBarHost *>(dwRefData);
  1480. LRESULT lres;
  1481. switch (uMsg)
  1482. {
  1483. case WM_PAINT:
  1484. // If entering outermost paint cycle, hide the drop feedback
  1485. ++self->_cPaint;
  1486. if (self->_cPaint == 1 && self->_pdth)
  1487. {
  1488. self->_pdth->Show(FALSE);
  1489. }
  1490. lres = DefSubclassProc(hwnd, uMsg, wParam, lParam);
  1491. // If exiting outermost paint cycle, restore the drop feedback
  1492. // Don't decrement _cPaint until really finished because
  1493. // Show() will call UpdateWindow and trigger a nested paint cycle.
  1494. if (self->_cPaint == 1 && self->_pdth)
  1495. {
  1496. self->_pdth->Show(TRUE);
  1497. }
  1498. --self->_cPaint;
  1499. return lres;
  1500. case WM_NCDESTROY:
  1501. RemoveWindowSubclass(hwnd, s_DropTargetSubclassProc, uIdSubclass);
  1502. break;
  1503. }
  1504. return DefSubclassProc(hwnd, uMsg, wParam, lParam);
  1505. }
  1506. //
  1507. // Listview makes it hard to detect whether you are in a real customdraw
  1508. // or a fake customdraw, since it frequently "gets confused" and gives
  1509. // you a 0x0 rectangle even though it really wants you to draw something.
  1510. //
  1511. // Even worse, within a single paint cycle, Listview uses multiple
  1512. // NMLVCUSTOMDRAW structures so you can't stash state inside the customdraw
  1513. // structure. You have to save it externally.
  1514. //
  1515. // The only trustworthy guy is CDDS_PREPAINT. Use his rectangle to
  1516. // determine whether this is a real or fake customdraw...
  1517. //
  1518. // What's even weirder is that inside a regular paint cycle, you
  1519. // can get re-entered with a sub-paint cycle, so we have to maintain
  1520. // a stack of "is the current customdraw cycle real or fake?" bits.
  1521. void SFTBarHost::_CustomDrawPush(BOOL fReal)
  1522. {
  1523. _dwCustomDrawState = (_dwCustomDrawState << 1) | fReal;
  1524. }
  1525. BOOL SFTBarHost::_IsRealCustomDraw()
  1526. {
  1527. return _dwCustomDrawState & 1;
  1528. }
  1529. void SFTBarHost::_CustomDrawPop()
  1530. {
  1531. _dwCustomDrawState >>= 1;
  1532. }
  1533. LRESULT SFTBarHost::_OnLVPrePaint(LPNMLVCUSTOMDRAW plvcd)
  1534. {
  1535. LRESULT lResult;
  1536. // Always ask for postpaint so we can maintain our customdraw stack
  1537. lResult = CDRF_NOTIFYITEMDRAW | CDRF_NOTIFYPOSTPAINT;
  1538. BOOL fReal = !IsRectEmpty(&plvcd->nmcd.rc);
  1539. _CustomDrawPush(fReal);
  1540. return lResult;
  1541. }
  1542. //
  1543. // Hack! We want to know in _OnLvSubItemPrePaint whether the item
  1544. // is selected or not, We borrow the CDIS_CHECKED bit, which is
  1545. // otherwise used only by toolbar controls.
  1546. //
  1547. #define CDIS_WASSELECTED CDIS_CHECKED
  1548. LRESULT SFTBarHost::_OnLVItemPrePaint(LPNMLVCUSTOMDRAW plvcd)
  1549. {
  1550. LRESULT lResult = CDRF_DODEFAULT;
  1551. plvcd->nmcd.uItemState &= ~CDIS_WASSELECTED;
  1552. if (GetFocus() == _hwndList &&
  1553. (plvcd->nmcd.uItemState & CDIS_SELECTED))
  1554. {
  1555. plvcd->nmcd.uItemState |= CDIS_WASSELECTED;
  1556. // menu-highlighted tiles are always opaque
  1557. if (_hTheme)
  1558. {
  1559. plvcd->clrText = GetSysColor(COLOR_HIGHLIGHTTEXT);
  1560. plvcd->clrFace = plvcd->clrTextBk = GetSysColor(COLOR_MENUHILIGHT);
  1561. }
  1562. else
  1563. {
  1564. plvcd->clrText = GetSysColor(COLOR_HIGHLIGHTTEXT);
  1565. plvcd->clrFace = plvcd->clrTextBk = GetSysColor(COLOR_HIGHLIGHT);
  1566. }
  1567. }
  1568. // Turn off CDIS_SELECTED because it causes the icon to get alphablended
  1569. // and we don't want that. Turn off CDIS_FOCUS because that draws a
  1570. // focus rectangle and we don't want that either.
  1571. plvcd->nmcd.uItemState &= ~(CDIS_SELECTED | CDIS_FOCUS);
  1572. //
  1573. if (plvcd->nmcd.uItemState & CDIS_HOT && _clrHot != CLR_NONE)
  1574. plvcd->clrText = _clrHot;
  1575. // Turn off selection highlighting for everyone except
  1576. // the drop target highlight
  1577. if ((int)plvcd->nmcd.dwItemSpec != _iDragOver || !_pdtDragOver)
  1578. {
  1579. lResult |= LVCDRF_NOSELECT;
  1580. }
  1581. PaneItem *pitem = _GetItemFromLVLParam(plvcd->nmcd.lItemlParam);
  1582. if (!pitem)
  1583. {
  1584. // Sometimes ListView doesn't give us an lParam so we have to
  1585. // get it ourselves
  1586. pitem = _GetItemFromLV((int)plvcd->nmcd.dwItemSpec);
  1587. }
  1588. if (pitem)
  1589. {
  1590. if (IsBold(pitem))
  1591. {
  1592. _CreateBoldFont();
  1593. SelectFont(plvcd->nmcd.hdc, _hfBold);
  1594. lResult |= CDRF_NEWFONT;
  1595. }
  1596. if (pitem->IsCascade())
  1597. {
  1598. // Need subitem notification because that's what sets the colors
  1599. lResult |= CDRF_NOTIFYPOSTPAINT | CDRF_NOTIFYSUBITEMDRAW;
  1600. }
  1601. if (pitem->HasAccelerator())
  1602. {
  1603. // Need subitem notification because that's what sets the colors
  1604. lResult |= CDRF_NOTIFYPOSTPAINT | CDRF_NOTIFYSUBITEMDRAW;
  1605. }
  1606. if (pitem->HasSubtitle())
  1607. {
  1608. lResult |= CDRF_NOTIFYSUBITEMDRAW;
  1609. }
  1610. }
  1611. return lResult;
  1612. }
  1613. LRESULT SFTBarHost::_OnLVSubItemPrePaint(LPNMLVCUSTOMDRAW plvcd)
  1614. {
  1615. LRESULT lResult = CDRF_DODEFAULT;
  1616. if (plvcd->iSubItem == 1)
  1617. {
  1618. // Second line uses the regular font (first line was bold)
  1619. SelectFont(plvcd->nmcd.hdc, GetWindowFont(_hwndList));
  1620. lResult |= CDRF_NEWFONT;
  1621. if (GetFocus() == _hwndList &&
  1622. (plvcd->nmcd.uItemState & CDIS_WASSELECTED))
  1623. {
  1624. plvcd->clrText = GetSysColor(COLOR_HIGHLIGHTTEXT);
  1625. }
  1626. else
  1627. // Maybe there's a custom subtitle color
  1628. if (_clrSubtitle != CLR_NONE)
  1629. {
  1630. plvcd->clrText = _clrSubtitle;
  1631. }
  1632. else
  1633. {
  1634. plvcd->clrText = GetSysColor(COLOR_MENUTEXT);
  1635. }
  1636. }
  1637. return lResult;
  1638. }
  1639. // QUIRK! Listview often sends item postpaint messages even though we
  1640. // didn't ask for one. It does this because we set NOTIFYPOSTPAINT on
  1641. // the CDDS_PREPAINT notification ("please notify me when the entire
  1642. // listview is finished painting") and it thinks that that flag also
  1643. // turns on postpaint notifications for each item...
  1644. LRESULT SFTBarHost::_OnLVItemPostPaint(LPNMLVCUSTOMDRAW plvcd)
  1645. {
  1646. PaneItem *pitem = _GetItemFromLVLParam(plvcd->nmcd.lItemlParam);
  1647. if (_IsRealCustomDraw() && pitem)
  1648. {
  1649. RECT rc;
  1650. if (ListView_GetItemRect(_hwndList, plvcd->nmcd.dwItemSpec, &rc, LVIR_LABEL))
  1651. {
  1652. COLORREF clrBkPrev = SetBkColor(plvcd->nmcd.hdc, plvcd->clrFace);
  1653. COLORREF clrTextPrev = SetTextColor(plvcd->nmcd.hdc, plvcd->clrText);
  1654. int iModePrev = SetBkMode(plvcd->nmcd.hdc, TRANSPARENT);
  1655. BOOL fRTL = GetLayout(plvcd->nmcd.hdc) & LAYOUT_RTL;
  1656. if (pitem->IsCascade())
  1657. {
  1658. {
  1659. HFONT hfPrev = SelectFont(plvcd->nmcd.hdc, _hfMarlett);
  1660. if (hfPrev)
  1661. {
  1662. TCHAR chOut = fRTL ? TEXT('w') : TEXT('8');
  1663. UINT fuOptions = 0;
  1664. if (fRTL)
  1665. {
  1666. fuOptions |= ETO_RTLREADING;
  1667. }
  1668. ExtTextOut(plvcd->nmcd.hdc, rc.right - _cxMarlett,
  1669. rc.top + (rc.bottom - rc.top - _tmAscentMarlett)/2,
  1670. fuOptions, &rc, &chOut, 1, NULL);
  1671. SelectFont(plvcd->nmcd.hdc, hfPrev);
  1672. }
  1673. }
  1674. }
  1675. if (pitem->HasAccelerator() &&
  1676. (plvcd->nmcd.uItemState & CDIS_SHOWKEYBOARDCUES))
  1677. {
  1678. // Subtitles mess up our computations...
  1679. ASSERT(!pitem->HasSubtitle());
  1680. rc.right -= _cxMarlett; // Subtract out our margin
  1681. UINT uFormat = DT_VCENTER | DT_SINGLELINE | DT_PREFIXONLY |
  1682. DT_WORDBREAK | DT_EDITCONTROL | DT_WORD_ELLIPSIS;
  1683. if (fRTL)
  1684. {
  1685. uFormat |= DT_RTLREADING;
  1686. }
  1687. DrawText(plvcd->nmcd.hdc, pitem->_pszAccelerator, -1, &rc, uFormat);
  1688. rc.right += _cxMarlett; // restore it
  1689. }
  1690. SetBkMode(plvcd->nmcd.hdc, iModePrev);
  1691. SetTextColor(plvcd->nmcd.hdc, clrTextPrev);
  1692. SetBkColor(plvcd->nmcd.hdc, clrBkPrev);
  1693. }
  1694. }
  1695. return CDRF_DODEFAULT;
  1696. }
  1697. LRESULT SFTBarHost::_OnLVPostPaint(LPNMLVCUSTOMDRAW plvcd)
  1698. {
  1699. if (_IsRealCustomDraw())
  1700. {
  1701. _DrawInsertionMark(plvcd);
  1702. _DrawSeparators(plvcd);
  1703. }
  1704. _CustomDrawPop();
  1705. return CDRF_DODEFAULT;
  1706. }
  1707. LRESULT SFTBarHost::_OnUpdateUIState(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1708. {
  1709. // Only need to do this when the Start Menu is visible; if not visible, then
  1710. // don't waste your time invalidating useless rectangles (and paging them in!)
  1711. if (IsWindowVisible(GetAncestor(_hwnd, GA_ROOT)))
  1712. {
  1713. // All UIS_SETs should happen when the Start Menu is hidden;
  1714. // we assume that the only thing we will be asked to do is to
  1715. // start showing the underlines
  1716. ASSERT(LOWORD(wParam) != UIS_SET);
  1717. DWORD dwLvExStyle = 0;
  1718. if (!GetSystemMetrics(SM_REMOTESESSION) && !GetSystemMetrics(SM_REMOTECONTROL))
  1719. {
  1720. dwLvExStyle |= LVS_EX_DOUBLEBUFFER;
  1721. }
  1722. if ((ListView_GetExtendedListViewStyle(_hwndList) & LVS_EX_DOUBLEBUFFER) != dwLvExStyle)
  1723. {
  1724. ListView_SetExtendedListViewStyleEx(_hwndList, LVS_EX_DOUBLEBUFFER, dwLvExStyle);
  1725. }
  1726. int iItem;
  1727. for (iItem = ListView_GetItemCount(_hwndList) - 1; iItem >= 0; iItem--)
  1728. {
  1729. PaneItem *pitem = _GetItemFromLV(iItem);
  1730. if (pitem && pitem->HasAccelerator())
  1731. {
  1732. RECT rc;
  1733. if (ListView_GetItemRect(_hwndList, iItem, &rc, LVIR_LABEL))
  1734. {
  1735. // We need to repaint background because of cleartype double print issues
  1736. InvalidateRect(_hwndList, &rc, TRUE);
  1737. }
  1738. }
  1739. }
  1740. }
  1741. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  1742. }
  1743. PaneItem *SFTBarHost::_GetItemFromLV(int iItem)
  1744. {
  1745. LVITEM lvi;
  1746. lvi.iItem = iItem;
  1747. lvi.iSubItem = 0;
  1748. lvi.mask = LVIF_PARAM;
  1749. if (iItem >= 0 && ListView_GetItem(_hwndList, &lvi))
  1750. {
  1751. PaneItem *pitem = _GetItemFromLVLParam(lvi.lParam);
  1752. return pitem;
  1753. }
  1754. return NULL;
  1755. }
  1756. LRESULT SFTBarHost::_OnMenuMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1757. {
  1758. LRESULT lres;
  1759. if (_pcm3Pop && SUCCEEDED(_pcm3Pop->HandleMenuMsg2(uMsg, wParam, lParam, &lres)))
  1760. {
  1761. return lres;
  1762. }
  1763. if (_pcm2Pop && SUCCEEDED(_pcm2Pop->HandleMenuMsg(uMsg, wParam, lParam)))
  1764. {
  1765. return 0;
  1766. }
  1767. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  1768. }
  1769. LRESULT SFTBarHost::_OnForwardMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1770. {
  1771. SHPropagateMessage(hwnd, uMsg, wParam, lParam, SPM_SEND | SPM_ONELEVEL);
  1772. // Give derived class a chance to get the message, too
  1773. return OnWndProc(hwnd, uMsg, wParam, lParam);
  1774. }
  1775. BOOL SFTBarHost::UnregisterNotify(UINT id)
  1776. {
  1777. ASSERT(id < SFTHOST_MAXNOTIFY);
  1778. if (id < SFTHOST_MAXNOTIFY && _rguChangeNotify[id])
  1779. {
  1780. UINT uChangeNotify = _rguChangeNotify[id];
  1781. _rguChangeNotify[id] = 0;
  1782. return SHChangeNotifyDeregister(uChangeNotify);
  1783. }
  1784. return FALSE;
  1785. }
  1786. BOOL SFTBarHost::_RegisterNotify(UINT id, LONG lEvents, LPCITEMIDLIST pidl, BOOL fRecursive)
  1787. {
  1788. ASSERT(id < SFTHOST_MAXNOTIFY);
  1789. if (id < SFTHOST_MAXNOTIFY)
  1790. {
  1791. UnregisterNotify(id);
  1792. SHChangeNotifyEntry fsne;
  1793. fsne.fRecursive = fRecursive;
  1794. fsne.pidl = pidl;
  1795. int fSources = SHCNRF_NewDelivery | SHCNRF_ShellLevel | SHCNRF_InterruptLevel;
  1796. if (fRecursive)
  1797. {
  1798. // SHCNRF_RecursiveInterrupt means "Please use a recursive FindFirstChangeNotify"
  1799. fSources |= SHCNRF_RecursiveInterrupt;
  1800. }
  1801. _rguChangeNotify[id] = SHChangeNotifyRegister(_hwnd, fSources, lEvents,
  1802. SFTBM_CHANGENOTIFY + id, 1, &fsne);
  1803. return _rguChangeNotify[id];
  1804. }
  1805. return FALSE;
  1806. }
  1807. //
  1808. // wParam = 0 if this is not an urgent refresh (can be postponed)
  1809. // wParam = 1 if this is urgent (must refresh even if menu is open)
  1810. //
  1811. LRESULT SFTBarHost::_OnRepopulate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1812. {
  1813. // Don't update the list now if we are visible, except if the list was empty
  1814. _fBGTask = FALSE;
  1815. if (wParam || !IsWindowVisible(_hwnd) || ListView_GetItemCount(_hwndList) == 0)
  1816. {
  1817. _RepopulateList();
  1818. }
  1819. else
  1820. {
  1821. _fNeedsRepopulate = TRUE;
  1822. }
  1823. if (_fRestartEnum)
  1824. {
  1825. _EnumerateContents(_fRestartUrgent);
  1826. }
  1827. return 0;
  1828. }
  1829. LRESULT SFTBarHost::_OnChangeNotify(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1830. {
  1831. LPITEMIDLIST *ppidl;
  1832. LONG lEvent;
  1833. LPSHChangeNotificationLock pshcnl;
  1834. pshcnl = SHChangeNotification_Lock((HANDLE)wParam, (DWORD)lParam, &ppidl, &lEvent);
  1835. if (pshcnl)
  1836. {
  1837. UINT id = uMsg - SFTBM_CHANGENOTIFY;
  1838. if (id < SFTHOST_MAXCLIENTNOTIFY)
  1839. {
  1840. OnChangeNotify(id, lEvent, ppidl[0], ppidl[1]);
  1841. }
  1842. else if (id == SFTHOST_HOSTNOTIFY_UPDATEIMAGE)
  1843. {
  1844. _OnUpdateImage(ppidl[0], ppidl[1]);
  1845. }
  1846. else
  1847. {
  1848. // Our wndproc shouldn't have dispatched to us
  1849. ASSERT(0);
  1850. }
  1851. SHChangeNotification_Unlock(pshcnl);
  1852. }
  1853. return 0;
  1854. }
  1855. void SFTBarHost::_OnUpdateImage(LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra)
  1856. {
  1857. // Must use pidl and not pidlExtra because pidlExtra is sometimes NULL
  1858. SHChangeDWORDAsIDList *pdwidl = (SHChangeDWORDAsIDList *)pidl;
  1859. if (pdwidl->dwItem1 == 0xFFFFFFFF)
  1860. {
  1861. // Wholesale icon rebuild; just pitch everything and start over
  1862. ::PostMessage(v_hwndTray, SBM_REBUILDMENU, 0, 0);
  1863. }
  1864. else
  1865. {
  1866. int iImage = SHHandleUpdateImage(pidlExtra);
  1867. if (iImage >= 0)
  1868. {
  1869. UpdateImage(iImage);
  1870. }
  1871. }
  1872. }
  1873. //
  1874. // See if anybody is using this image; if so, invalidate the cached bitmap.
  1875. //
  1876. void SFTBarHost::UpdateImage(int iImage)
  1877. {
  1878. ASSERT(!_IsPrivateImageList());
  1879. int iItem;
  1880. for (iItem = ListView_GetItemCount(_hwndList) - 1; iItem >= 0; iItem--)
  1881. {
  1882. LVITEM lvi;
  1883. lvi.iItem = iItem;
  1884. lvi.iSubItem = 0;
  1885. lvi.mask = LVIF_IMAGE;
  1886. if (ListView_GetItem(_hwndList, &lvi) && lvi.iImage == iImage)
  1887. {
  1888. // The cached bitmap is no good; an icon changed
  1889. _SendNotify(_hwnd, SMN_NEEDREPAINT, NULL);
  1890. break;
  1891. }
  1892. }
  1893. }
  1894. //
  1895. // wParam = 0 if this is not an urgent refresh (can be postponed)
  1896. // wParam = 1 if this is urgen (must refresh even if menu is open)
  1897. //
  1898. LRESULT SFTBarHost::_OnRefresh(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1899. {
  1900. _EnumerateContents((BOOL)wParam);
  1901. return 0;
  1902. }
  1903. LPTSTR _DisplayNameOf(IShellFolder *psf, LPCITEMIDLIST pidl, UINT shgno)
  1904. {
  1905. LPTSTR pszOut;
  1906. DisplayNameOfAsOLESTR(psf, pidl, shgno, &pszOut);
  1907. return pszOut;
  1908. }
  1909. LPTSTR SFTBarHost::_DisplayNameOfItem(PaneItem *pitem, UINT shgno)
  1910. {
  1911. IShellFolder *psf;
  1912. LPCITEMIDLIST pidl;
  1913. LPTSTR pszOut = NULL;
  1914. if (SUCCEEDED(_GetFolderAndPidl(pitem, &psf, &pidl)))
  1915. {
  1916. pszOut = DisplayNameOfItem(pitem, psf, pidl, (SHGNO)shgno);
  1917. psf->Release();
  1918. }
  1919. return pszOut;
  1920. }
  1921. HRESULT SFTBarHost::_GetUIObjectOfItem(PaneItem *pitem, REFIID riid, void * *ppv)
  1922. {
  1923. *ppv = NULL;
  1924. IShellFolder *psf;
  1925. LPCITEMIDLIST pidlItem;
  1926. HRESULT hr = _GetFolderAndPidl(pitem, &psf, &pidlItem);
  1927. if (SUCCEEDED(hr))
  1928. {
  1929. hr = psf->GetUIObjectOf(_hwnd, 1, &pidlItem, riid, NULL, ppv);
  1930. psf->Release();
  1931. }
  1932. return hr;
  1933. }
  1934. HRESULT SFTBarHost::_GetUIObjectOfItem(int iItem, REFIID riid, void * *ppv)
  1935. {
  1936. PaneItem *pitem = _GetItemFromLV(iItem);
  1937. if (pitem)
  1938. {
  1939. HRESULT hr = _GetUIObjectOfItem(pitem, riid, ppv);
  1940. return hr;
  1941. }
  1942. return E_FAIL;
  1943. }
  1944. HRESULT SFTBarHost::_GetFolderAndPidl(PaneItem *pitem, IShellFolder **ppsfOut, LPCITEMIDLIST *ppidlOut)
  1945. {
  1946. *ppsfOut = NULL;
  1947. *ppidlOut = NULL;
  1948. return pitem->IsSeparator() ? E_FAIL : GetFolderAndPidl(pitem, ppsfOut, ppidlOut);
  1949. }
  1950. //
  1951. // Given the coordinates of a context menu (lParam from WM_CONTEXTMENU),
  1952. // determine which item's context menu should be activated, or -1 if the
  1953. // context menu is not for us.
  1954. //
  1955. // Also, returns on success in *ppt the coordinates at which the
  1956. // context menu should be displayed.
  1957. //
  1958. int SFTBarHost::_ContextMenuCoordsToItem(LPARAM lParam, POINT *ppt)
  1959. {
  1960. int iItem;
  1961. ppt->x = GET_X_LPARAM(lParam);
  1962. ppt->y = GET_Y_LPARAM(lParam);
  1963. // If initiated from keyboard, act like they clicked on the center
  1964. // of the focused icon.
  1965. if (IS_WM_CONTEXTMENU_KEYBOARD(lParam))
  1966. {
  1967. iItem = _GetLVCurSel();
  1968. if (iItem >= 0)
  1969. {
  1970. RECT rc;
  1971. if (ListView_GetItemRect(_hwndList, iItem, &rc, LVIR_ICON))
  1972. {
  1973. MapWindowRect(_hwndList, NULL, &rc);
  1974. ppt->x = (rc.left+rc.right)/2;
  1975. ppt->y = (rc.top+rc.bottom)/2;
  1976. }
  1977. else
  1978. {
  1979. iItem = -1;
  1980. }
  1981. }
  1982. }
  1983. else
  1984. {
  1985. // Initiated from mouse; find the item they clicked on
  1986. LVHITTESTINFO hti;
  1987. hti.pt = *ppt;
  1988. MapWindowPoints(NULL, _hwndList, &hti.pt, 1);
  1989. iItem = ListView_HitTest(_hwndList, &hti);
  1990. }
  1991. return iItem;
  1992. }
  1993. LRESULT SFTBarHost::_OnContextMenu(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1994. {
  1995. if(_AreChangesRestricted())
  1996. {
  1997. return 0;
  1998. }
  1999. TCHAR szBuf[MAX_PATH];
  2000. _DebugConsistencyCheck();
  2001. BOOL fSuccess = FALSE;
  2002. POINT pt;
  2003. int iItem = _ContextMenuCoordsToItem(lParam, &pt);
  2004. if (iItem >= 0)
  2005. {
  2006. PaneItem *pitem = _GetItemFromLV(iItem);
  2007. if (pitem)
  2008. {
  2009. // If we can't get the official shell context menu,
  2010. // then use a dummy one.
  2011. IContextMenu *pcm;
  2012. if (FAILED(_GetUIObjectOfItem(pitem, IID_PPV_ARG(IContextMenu, &pcm))))
  2013. {
  2014. pcm = s_EmptyContextMenu.GetContextMenu();
  2015. }
  2016. HMENU hmenu = ::CreatePopupMenu();
  2017. if (hmenu)
  2018. {
  2019. UINT uFlags = CMF_NORMAL;
  2020. if (GetKeyState(VK_SHIFT) < 0)
  2021. {
  2022. uFlags |= CMF_EXTENDEDVERBS;
  2023. }
  2024. if (_dwFlags & HOSTF_CANRENAME)
  2025. {
  2026. uFlags |= CMF_CANRENAME;
  2027. }
  2028. pcm->QueryContextMenu(hmenu, 0, IDM_QCM_MIN, IDM_QCM_MAX, uFlags);
  2029. // Remove "Create shortcut" from context menu because it creates
  2030. // the shortcut on the desktop, which the user can't see...
  2031. ContextMenu_DeleteCommandByName(pcm, hmenu, IDM_QCM_MIN, TEXT("link"));
  2032. // Remove "Cut" from context menu because we don't want objects
  2033. // to be deleted.
  2034. ContextMenu_DeleteCommandByName(pcm, hmenu, IDM_QCM_MIN, TEXT("cut"));
  2035. // Let clients override the "delete" option.
  2036. // Change "Delete" to "Remove from this list".
  2037. // If client doesn't support "delete" then nuke it outright.
  2038. // If client supports "delete" but the IContextMenu didn't create one,
  2039. // then create a fake one so we cn add the "Remove from list" option.
  2040. UINT uPosDelete = GetMenuIndexForCanonicalVerb(hmenu, pcm, IDM_QCM_MIN, TEXT("delete"));
  2041. UINT uiFlags = 0;
  2042. UINT idsDelete = AdjustDeleteMenuItem(pitem, &uiFlags);
  2043. if (idsDelete)
  2044. {
  2045. if (LoadString(_Module.GetResourceInstance(), idsDelete, szBuf, ARRAYSIZE(szBuf)))
  2046. {
  2047. if (uPosDelete != -1)
  2048. {
  2049. ModifyMenu(hmenu, uPosDelete, uiFlags | MF_BYPOSITION | MF_STRING, IDM_REMOVEFROMLIST, szBuf);
  2050. }
  2051. else
  2052. {
  2053. AppendMenu(hmenu, MF_SEPARATOR, -1, NULL);
  2054. AppendMenu(hmenu, uiFlags | MF_STRING, IDM_REMOVEFROMLIST, szBuf);
  2055. }
  2056. }
  2057. }
  2058. else
  2059. {
  2060. DeleteMenu(hmenu, uPosDelete, MF_BYPOSITION);
  2061. }
  2062. _SHPrettyMenu(hmenu);
  2063. ASSERT(_pcm2Pop == NULL); // Shouldn't be recursing
  2064. pcm->QueryInterface(IID_PPV_ARG(IContextMenu2, &_pcm2Pop));
  2065. ASSERT(_pcm3Pop == NULL); // Shouldn't be recursing
  2066. pcm->QueryInterface(IID_PPV_ARG(IContextMenu3, &_pcm3Pop));
  2067. int idCmd = TrackPopupMenuEx(hmenu,
  2068. TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN,
  2069. pt.x, pt.y, hwnd, NULL);
  2070. ATOMICRELEASE(_pcm2Pop);
  2071. ATOMICRELEASE(_pcm3Pop);
  2072. if (idCmd)
  2073. {
  2074. switch (idCmd)
  2075. {
  2076. case IDM_REMOVEFROMLIST:
  2077. StrCpyN(szBuf, TEXT("delete"), ARRAYSIZE(szBuf));
  2078. break;
  2079. default:
  2080. ContextMenu_GetCommandStringVerb(pcm, idCmd - IDM_QCM_MIN, szBuf, ARRAYSIZE(szBuf));
  2081. break;
  2082. }
  2083. idCmd -= IDM_QCM_MIN;
  2084. CMINVOKECOMMANDINFOEX ici = {
  2085. sizeof(ici), // cbSize
  2086. CMIC_MASK_FLAG_LOG_USAGE | // this was an explicit user action
  2087. CMIC_MASK_ASYNCOK, // fMask
  2088. hwnd, // hwnd
  2089. (LPCSTR)IntToPtr(idCmd),// lpVerb
  2090. NULL, // lpParameters
  2091. NULL, // lpDirectory
  2092. SW_SHOWDEFAULT, // nShow
  2093. 0, // dwHotKey
  2094. 0, // hIcon
  2095. NULL, // lpTitle
  2096. (LPCWSTR)IntToPtr(idCmd),// lpVerbW
  2097. NULL, // lpParametersW
  2098. NULL, // lpDirectoryW
  2099. NULL, // lpTitleW
  2100. { pt.x, pt.y }, // ptInvoke
  2101. };
  2102. if ((_dwFlags & HOSTF_CANRENAME) &&
  2103. StrCmpI(szBuf, TEXT("rename")) == 0)
  2104. {
  2105. _EditLabel(iItem);
  2106. }
  2107. else
  2108. {
  2109. ContextMenuInvokeItem(pitem, pcm, &ici, szBuf);
  2110. }
  2111. }
  2112. DestroyMenu(hmenu);
  2113. fSuccess = TRUE;
  2114. }
  2115. pcm->Release();
  2116. }
  2117. }
  2118. _DebugConsistencyCheck();
  2119. return fSuccess ? 0 : DefWindowProc(hwnd, uMsg, wParam, lParam);
  2120. }
  2121. void SFTBarHost::_EditLabel(int iItem)
  2122. {
  2123. _fAllowEditLabel = TRUE;
  2124. ListView_EditLabel(_hwndList, iItem);
  2125. _fAllowEditLabel = FALSE;
  2126. }
  2127. HRESULT SFTBarHost::ContextMenuInvokeItem(PaneItem *pitem, IContextMenu *pcm, CMINVOKECOMMANDINFOEX *pici, LPCTSTR pszVerb)
  2128. {
  2129. // Make sure none of our private menu items leaked through
  2130. ASSERT(PtrToLong(pici->lpVerb) >= 0);
  2131. // FUSION: When we call out to 3rd party code we want it to use
  2132. // the process default context. This means that the 3rd party code will get
  2133. // v5 in the explorer process. However, if shell32 is hosted in a v6 process,
  2134. // then the 3rd party code will still get v6.
  2135. ULONG_PTR cookie = 0;
  2136. ActivateActCtx(NULL, &cookie);
  2137. HRESULT hr = pcm->InvokeCommand(reinterpret_cast<LPCMINVOKECOMMANDINFO>(pici));
  2138. if (cookie != 0)
  2139. {
  2140. DeactivateActCtx(0, cookie);
  2141. }
  2142. return hr;
  2143. }
  2144. LRESULT SFTBarHost::_OnLVNItemActivate(LPNMITEMACTIVATE pnmia)
  2145. {
  2146. return _ActivateItem(pnmia->iItem, 0);
  2147. }
  2148. LRESULT SFTBarHost::_ActivateItem(int iItem, DWORD dwFlags)
  2149. {
  2150. PaneItem *pitem;
  2151. IShellFolder *psf;
  2152. LPCITEMIDLIST pidl;
  2153. DWORD dwCascadeFlags = 0;
  2154. if (dwFlags & AIF_KEYBOARD)
  2155. {
  2156. dwCascadeFlags = MPPF_KEYBOARD | MPPF_INITIALSELECT;
  2157. }
  2158. if (_OnCascade(iItem, dwCascadeFlags))
  2159. {
  2160. // We did the cascade thing; all finished!
  2161. }
  2162. else
  2163. if ((pitem = _GetItemFromLV(iItem)) &&
  2164. SUCCEEDED(_GetFolderAndPidl(pitem, &psf, &pidl)))
  2165. {
  2166. // See if the item is still valid.
  2167. // Do this only for SFGAO_FILESYSTEM objects because
  2168. // we can't be sure that other folders support SFGAO_VALIDATE,
  2169. // and besides, you can't resolve any other types of objects
  2170. // anyway...
  2171. DWORD dwAttr = SFGAO_FILESYSTEM | SFGAO_VALIDATE;
  2172. if (FAILED(psf->GetAttributesOf(1, &pidl, &dwAttr)) ||
  2173. (dwAttr & SFGAO_FILESYSTEM | SFGAO_VALIDATE) == SFGAO_FILESYSTEM ||
  2174. FAILED(_InvokeDefaultCommand(iItem, psf, pidl)))
  2175. {
  2176. // Object is bogus - offer to delete it
  2177. if ((_dwFlags & HOSTF_CANDELETE) && pitem->IsPinned())
  2178. {
  2179. _OfferDeleteBrokenItem(pitem, psf, pidl);
  2180. }
  2181. }
  2182. psf->Release();
  2183. }
  2184. return 0;
  2185. }
  2186. HRESULT SFTBarHost::_InvokeDefaultCommand(int iItem, IShellFolder *psf, LPCITEMIDLIST pidl)
  2187. {
  2188. HRESULT hr = SHInvokeDefaultCommand(GetShellWindow(), psf, pidl);
  2189. if (SUCCEEDED(hr))
  2190. {
  2191. if (_dwFlags & HOSTF_FIREUEMEVENTS)
  2192. {
  2193. _FireUEMPidlEvent(psf, pidl);
  2194. }
  2195. SMNMCOMMANDINVOKED ci;
  2196. ListView_GetItemRect(_hwndList, iItem, &ci.rcItem, LVIR_BOUNDS);
  2197. MapWindowRect(_hwndList, NULL, &ci.rcItem);
  2198. _SendNotify(_hwnd, SMN_COMMANDINVOKED, &ci.hdr);
  2199. }
  2200. return hr;
  2201. }
  2202. class OfferDelete
  2203. {
  2204. public:
  2205. LPTSTR _pszName;
  2206. LPITEMIDLIST _pidlFolder;
  2207. LPITEMIDLIST _pidlFull;
  2208. IStartMenuPin * _psmpin;
  2209. HWND _hwnd;
  2210. ~OfferDelete()
  2211. {
  2212. SHFree(_pszName);
  2213. ILFree(_pidlFolder);
  2214. ILFree(_pidlFull);
  2215. }
  2216. BOOL _RepairBrokenItem();
  2217. void _ThreadProc();
  2218. static DWORD s_ThreadProc(LPVOID lpParameter)
  2219. {
  2220. OfferDelete *poffer = (OfferDelete *)lpParameter;
  2221. poffer->_ThreadProc();
  2222. delete poffer;
  2223. return 0;
  2224. }
  2225. };
  2226. BOOL OfferDelete::_RepairBrokenItem()
  2227. {
  2228. BOOL fSuccess = FALSE;
  2229. LPITEMIDLIST pidlNew;
  2230. HRESULT hr = _psmpin->Resolve(_hwnd, 0, _pidlFull, &pidlNew);
  2231. if (pidlNew)
  2232. {
  2233. ASSERT(hr == S_OK); // only the S_OK case should alloc a new pidl
  2234. // Update to reflect the new pidl
  2235. ILFree(_pidlFull);
  2236. _pidlFull = pidlNew;
  2237. // Re-invoke the default command; if it fails the second time,
  2238. // then I guess the Resolve didn't work after all.
  2239. IShellFolder *psf;
  2240. LPCITEMIDLIST pidlChild;
  2241. if (SUCCEEDED(SHBindToIDListParent(_pidlFull, IID_PPV_ARG(IShellFolder, &psf), &pidlChild)))
  2242. {
  2243. if (SUCCEEDED(SHInvokeDefaultCommand(_hwnd, psf, pidlChild)))
  2244. {
  2245. fSuccess = TRUE;
  2246. }
  2247. psf->Release();
  2248. }
  2249. }
  2250. return fSuccess;
  2251. }
  2252. void OfferDelete::_ThreadProc()
  2253. {
  2254. _hwnd = SHCreateWorkerWindow(NULL, NULL, 0, 0, NULL, NULL);
  2255. if (_hwnd)
  2256. {
  2257. if (SUCCEEDED(CoCreateInstance(CLSID_StartMenuPin, NULL, CLSCTX_INPROC_SERVER,
  2258. IID_PPV_ARG(IStartMenuPin, &_psmpin))))
  2259. {
  2260. //
  2261. // First try to repair it by invoking the shortcut tracking code.
  2262. // If that fails, then offer to delete.
  2263. if (!_RepairBrokenItem() &&
  2264. ShellMessageBox(_Module.GetResourceInstance(), NULL,
  2265. MAKEINTRESOURCE(IDS_SFTHOST_OFFERREMOVEITEM),
  2266. _pszName, MB_YESNO) == IDYES)
  2267. {
  2268. _psmpin->Modify(_pidlFull, NULL);
  2269. }
  2270. ATOMICRELEASE(_psmpin);
  2271. }
  2272. DestroyWindow(_hwnd);
  2273. }
  2274. }
  2275. void SFTBarHost::_OfferDeleteBrokenItem(PaneItem *pitem, IShellFolder *psf, LPCITEMIDLIST pidlChild)
  2276. {
  2277. //
  2278. // The offer is done on a separate thread because putting up modal
  2279. // UI while the Start Menu is open creates all sorts of weirdness.
  2280. // (The user might decide to switch to Classic Start Menu
  2281. // while the dialog is still up, and we get our infrastructure
  2282. // ripped out from underneath us and then USER faults inside
  2283. // MessageBox... Not good.)
  2284. //
  2285. OfferDelete *poffer = new OfferDelete;
  2286. if (poffer)
  2287. {
  2288. if ((poffer->_pszName = DisplayNameOfItem(pitem, psf, pidlChild, SHGDN_NORMAL)) != NULL &&
  2289. SUCCEEDED(SHGetIDListFromUnk(psf, &poffer->_pidlFolder)) &&
  2290. (poffer->_pidlFull = ILCombine(poffer->_pidlFolder, pidlChild)) != NULL &&
  2291. SHCreateThread(OfferDelete::s_ThreadProc, poffer, CTF_COINIT, NULL))
  2292. {
  2293. poffer = NULL; // thread took ownership
  2294. }
  2295. delete poffer;
  2296. }
  2297. }
  2298. BOOL ShowInfoTip()
  2299. {
  2300. // find out if infotips are on or off, from the registry settings
  2301. SHELLSTATE ss;
  2302. // force a refresh
  2303. SHGetSetSettings(&ss, 0, TRUE);
  2304. SHGetSetSettings(&ss, SSF_SHOWINFOTIP, FALSE);
  2305. return ss.fShowInfoTip;
  2306. }
  2307. // over-ridable method for getting the infotip on an item
  2308. void SFTBarHost::GetItemInfoTip(PaneItem *pitem, LPTSTR pszText, DWORD cch)
  2309. {
  2310. IShellFolder *psf;
  2311. LPCITEMIDLIST pidl;
  2312. if (pszText && cch)
  2313. {
  2314. *pszText = 0;
  2315. if (SUCCEEDED(_GetFolderAndPidl(pitem, &psf, &pidl)))
  2316. {
  2317. GetInfoTip(psf, pidl, pszText, cch);
  2318. psf->Release();
  2319. }
  2320. }
  2321. }
  2322. LRESULT SFTBarHost::_OnLVNGetInfoTip(LPNMLVGETINFOTIP plvn)
  2323. {
  2324. _DebugConsistencyCheck();
  2325. PaneItem *pitem;
  2326. if (ShowInfoTip() &&
  2327. (pitem = _GetItemFromLV(plvn->iItem)) &&
  2328. !pitem->IsCascade())
  2329. {
  2330. int cchName = (plvn->dwFlags & LVGIT_UNFOLDED) ? 0 : lstrlen(plvn->pszText);
  2331. if (cchName)
  2332. {
  2333. StrCatBuff(plvn->pszText, TEXT("\r\n"), plvn->cchTextMax);
  2334. cchName = lstrlen(plvn->pszText);
  2335. }
  2336. // If there is room in the buffer after we added CRLF, append the
  2337. // infotip text. We succeeded if there was nontrivial infotip text.
  2338. if (cchName < plvn->cchTextMax)
  2339. {
  2340. GetItemInfoTip(pitem, plvn->pszText + cchName, plvn->cchTextMax - cchName);
  2341. }
  2342. }
  2343. return 0;
  2344. }
  2345. LRESULT _SendNotify(HWND hwndFrom, UINT code, OPTIONAL NMHDR *pnm)
  2346. {
  2347. NMHDR nm;
  2348. if (pnm == NULL)
  2349. {
  2350. pnm = &nm;
  2351. }
  2352. pnm->hwndFrom = hwndFrom;
  2353. pnm->idFrom = GetDlgCtrlID(hwndFrom);
  2354. pnm->code = code;
  2355. return SendMessage(GetParent(hwndFrom), WM_NOTIFY, pnm->idFrom, (LPARAM)pnm);
  2356. }
  2357. //****************************************************************************
  2358. //
  2359. // Drag sourcing
  2360. //
  2361. // *** IDropSource::GiveFeedback ***
  2362. HRESULT SFTBarHost::GiveFeedback(DWORD dwEffect)
  2363. {
  2364. if (_fForceArrowCursor)
  2365. {
  2366. SetCursor(LoadCursor(NULL, IDC_ARROW));
  2367. return S_OK;
  2368. }
  2369. return DRAGDROP_S_USEDEFAULTCURSORS;
  2370. }
  2371. // *** IDropSource::QueryContinueDrag ***
  2372. HRESULT SFTBarHost::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState)
  2373. {
  2374. if (fEscapePressed ||
  2375. (grfKeyState & (MK_LBUTTON | MK_RBUTTON)) == (MK_LBUTTON | MK_RBUTTON))
  2376. {
  2377. return DRAGDROP_S_CANCEL;
  2378. }
  2379. if ((grfKeyState & (MK_LBUTTON | MK_RBUTTON)) == 0)
  2380. {
  2381. return DRAGDROP_S_DROP;
  2382. }
  2383. return S_OK;
  2384. }
  2385. LRESULT SFTBarHost::_OnLVNBeginDrag(LPNMLISTVIEW plv)
  2386. {
  2387. //If changes are restricted, don't allow drag and drop!
  2388. if(_AreChangesRestricted())
  2389. return 0;
  2390. _DebugConsistencyCheck();
  2391. ASSERT(_pdtoDragOut == NULL);
  2392. _pdtoDragOut = NULL;
  2393. PaneItem *pitem = _GetItemFromLV(plv->iItem);
  2394. ASSERT(pitem);
  2395. IDataObject *pdto;
  2396. if (pitem && SUCCEEDED(_GetUIObjectOfItem(pitem, IID_PPV_ARG(IDataObject, &pdto))))
  2397. {
  2398. POINT pt;
  2399. pt = plv->ptAction;
  2400. ClientToScreen(_hwndList, &pt);
  2401. if (_pdsh)
  2402. {
  2403. _pdsh->InitializeFromWindow(_hwndList, &pt, pdto);
  2404. }
  2405. CLIPFORMAT cfOFFSETS = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_SHELLIDLISTOFFSET);
  2406. POINT *apts = (POINT*)GlobalAlloc(GPTR, sizeof(POINT)*2);
  2407. if (NULL != apts)
  2408. {
  2409. POINT ptOrigin = {0};
  2410. POINT ptItem = {0};
  2411. ListView_GetOrigin(_hwndList, &ptOrigin);
  2412. apts[0].x = plv->ptAction.x + ptOrigin.x;
  2413. apts[0].y = plv->ptAction.y + ptOrigin.y;
  2414. ListView_GetItemPosition(_hwndList,plv->iItem,&ptItem);
  2415. apts[1].x = ptItem.x - apts[0].x;
  2416. apts[1].y = ptItem.y - apts[0].y;
  2417. HRESULT hr = DataObj_SetGlobal(pdto, cfOFFSETS, apts);
  2418. if (FAILED(hr))
  2419. {
  2420. GlobalFree((HGLOBAL)apts);
  2421. }
  2422. }
  2423. // We don't need to refcount _pdtoDragOut since its lifetime
  2424. // is the same as pdto.
  2425. _pdtoDragOut = pdto;
  2426. _iDragOut = plv->iItem;
  2427. _iPosDragOut = pitem->_iPos;
  2428. // Notice that DROPEFFECT_MOVE is explicitly forbidden.
  2429. // You cannot move things out of the control.
  2430. DWORD dwEffect = DROPEFFECT_LINK | DROPEFFECT_COPY;
  2431. DoDragDrop(pdto, this, dwEffect, &dwEffect);
  2432. _pdtoDragOut = NULL;
  2433. pdto->Release();
  2434. }
  2435. return 0;
  2436. }
  2437. //
  2438. // Must perform validation of SFGAO_CANRENAME when the label edit begins
  2439. // because John Gray somehow can trick the listview into going into edit
  2440. // mode by clicking in the right magic place, so this is the only chance
  2441. // we get to reject things that aren't renamable...
  2442. //
  2443. LRESULT SFTBarHost::_OnLVNBeginLabelEdit(NMLVDISPINFO *plvdi)
  2444. {
  2445. LRESULT lres = 1;
  2446. PaneItem *pitem = _GetItemFromLVLParam(plvdi->item.lParam);
  2447. IShellFolder *psf;
  2448. LPCITEMIDLIST pidl;
  2449. if (_fAllowEditLabel &&
  2450. pitem && SUCCEEDED(_GetFolderAndPidl(pitem, &psf, &pidl)))
  2451. {
  2452. DWORD dwAttr = SFGAO_CANRENAME;
  2453. if (SUCCEEDED(psf->GetAttributesOf(1, &pidl, &dwAttr)) &&
  2454. (dwAttr & SFGAO_CANRENAME))
  2455. {
  2456. LPTSTR ptszName = _DisplayNameOf(psf, pidl,
  2457. SHGDN_INFOLDER | SHGDN_FOREDITING);
  2458. if (ptszName)
  2459. {
  2460. HWND hwndEdit = ListView_GetEditControl(_hwndList);
  2461. if (hwndEdit)
  2462. {
  2463. SetWindowText(hwndEdit, ptszName);
  2464. int cchLimit = MAX_PATH;
  2465. IItemNameLimits *pinl;
  2466. if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IItemNameLimits, &pinl))))
  2467. {
  2468. pinl->GetMaxLength(ptszName, &cchLimit);
  2469. pinl->Release();
  2470. }
  2471. Edit_LimitText(hwndEdit, cchLimit);
  2472. // use way-cool helper which pops up baloon tips if they enter an invalid folder....
  2473. SHLimitInputEdit(hwndEdit, psf);
  2474. // Block menu mode during editing so the user won't
  2475. // accidentally cancel out of rename mode just because
  2476. // they moved the mouse.
  2477. SMNMBOOL nmb;
  2478. nmb.f = TRUE;
  2479. _SendNotify(_hwnd, SMN_BLOCKMENUMODE, &nmb.hdr);
  2480. lres = 0;
  2481. }
  2482. SHFree(ptszName);
  2483. }
  2484. }
  2485. psf->Release();
  2486. }
  2487. return lres;
  2488. }
  2489. LRESULT SFTBarHost::_OnLVNEndLabelEdit(NMLVDISPINFO *plvdi)
  2490. {
  2491. // Unblock menu mode now that editing is over.
  2492. SMNMBOOL nmb;
  2493. nmb.f = FALSE;
  2494. _SendNotify(_hwnd, SMN_BLOCKMENUMODE, &nmb.hdr);
  2495. // If changing to NULL pointer, then user is cancelling
  2496. if (!plvdi->item.pszText)
  2497. return FALSE;
  2498. // Note: We allow the user to type blanks. Regfolder treats a blank
  2499. // name as "restore default name".
  2500. PathRemoveBlanks(plvdi->item.pszText);
  2501. PaneItem *pitem = _GetItemFromLVLParam(plvdi->item.lParam);
  2502. HRESULT hr = ContextMenuRenameItem(pitem, plvdi->item.pszText);
  2503. if (SUCCEEDED(hr))
  2504. {
  2505. LPTSTR ptszName = _DisplayNameOfItem(pitem, SHGDN_NORMAL);
  2506. if (ptszName)
  2507. {
  2508. ListView_SetItemText(_hwndList, plvdi->item.iItem, 0, ptszName);
  2509. _SendNotify(_hwnd, SMN_NEEDREPAINT, NULL);
  2510. }
  2511. }
  2512. else if (hr != HRESULT_FROM_WIN32(ERROR_CANCELLED))
  2513. {
  2514. _EditLabel(plvdi->item.iItem);
  2515. }
  2516. // Always return FALSE to prevent listview from changing the
  2517. // item text to what the user typed. If the rename succeeded,
  2518. // we manually set the name to the new name (which might not be
  2519. // the same as what the user typed).
  2520. return FALSE;
  2521. }
  2522. LRESULT SFTBarHost::_OnLVNKeyDown(LPNMLVKEYDOWN pkd)
  2523. {
  2524. // Plain F2 (no shift, ctrl or alt) = rename
  2525. if (pkd->wVKey == VK_F2 && GetKeyState(VK_SHIFT) >= 0 &&
  2526. GetKeyState(VK_CONTROL) >= 0 && GetKeyState(VK_MENU) >= 0 &&
  2527. (_dwFlags & HOSTF_CANRENAME))
  2528. {
  2529. int iItem = _GetLVCurSel();
  2530. if (iItem >= 0)
  2531. {
  2532. _EditLabel(iItem);
  2533. // cannot return TRUE because listview mistakenly thinks
  2534. // that all WM_KEYDOWNs lead to WM_CHARs (but this one doesn't)
  2535. }
  2536. }
  2537. return 0;
  2538. }
  2539. LRESULT SFTBarHost::_OnSMNGetMinSize(PSMNGETMINSIZE pgms)
  2540. {
  2541. // We need to synchronize here to get the proper size
  2542. if (_fBGTask && !HasDynamicContent())
  2543. {
  2544. // Wait for the enumeration to be done
  2545. while (TRUE)
  2546. {
  2547. MSG msg;
  2548. // Need to peek messages for all queues here or else WaitMessage will say
  2549. // that some messages are ready to be processed and we'll end up with an
  2550. // active loop
  2551. if (PeekMessage(&msg, NULL, NULL, NULL, PM_NOREMOVE))
  2552. {
  2553. if (PeekMessage(&msg, _hwnd, SFTBM_REPOPULATE, SFTBM_REPOPULATE, PM_REMOVE))
  2554. {
  2555. DispatchMessage(&msg);
  2556. break;
  2557. }
  2558. }
  2559. WaitMessage();
  2560. }
  2561. }
  2562. int cItems = _cPinnedDesired + _cNormalDesired;
  2563. int cSep = _cSep;
  2564. // if the repopulate hasn't happened yet, but we've got pinned items, we're going to have a separator
  2565. if (_cSep == 0 && _cPinnedDesired > 0)
  2566. cSep = 1;
  2567. int cy = (_cyTile * cItems) + (_cySepTile * cSep);
  2568. // add in theme margins
  2569. cy += _margins.cyTopHeight + _margins.cyBottomHeight;
  2570. // SPP_PROGLIST gets a bonus separator at the bottom
  2571. if (_iThemePart == SPP_PROGLIST)
  2572. {
  2573. cy += _cySep;
  2574. }
  2575. pgms->siz.cy = cy;
  2576. return 0;
  2577. }
  2578. LRESULT SFTBarHost::_OnSMNFindItem(PSMNDIALOGMESSAGE pdm)
  2579. {
  2580. LRESULT lres = _OnSMNFindItemWorker(pdm);
  2581. if (lres)
  2582. {
  2583. //
  2584. // If caller requested that the item also be selected, then do so.
  2585. //
  2586. if (pdm->flags & SMNDM_SELECT)
  2587. {
  2588. ListView_SetItemState(_hwndList, pdm->itemID,
  2589. LVIS_SELECTED | LVIS_FOCUSED,
  2590. LVIS_SELECTED | LVIS_FOCUSED);
  2591. if ((pdm->flags & SMNDM_FINDMASK) != SMNDM_HITTEST)
  2592. {
  2593. ListView_KeyboardSelected(_hwndList, pdm->itemID);
  2594. }
  2595. }
  2596. }
  2597. else
  2598. {
  2599. //
  2600. // If not found, then tell caller what our orientation is (vertical)
  2601. // and where the currently-selected item is.
  2602. //
  2603. pdm->flags |= SMNDM_VERTICAL;
  2604. int iItem = _GetLVCurSel();
  2605. RECT rc;
  2606. if (iItem >= 0 &&
  2607. ListView_GetItemRect(_hwndList, iItem, &rc, LVIR_BOUNDS))
  2608. {
  2609. pdm->pt.x = (rc.left + rc.right)/2;
  2610. pdm->pt.y = (rc.top + rc.bottom)/2;
  2611. }
  2612. else
  2613. {
  2614. pdm->pt.x = 0;
  2615. pdm->pt.y = 0;
  2616. }
  2617. }
  2618. return lres;
  2619. }
  2620. TCHAR SFTBarHost::GetItemAccelerator(PaneItem *pitem, int iItemStart)
  2621. {
  2622. TCHAR sz[2];
  2623. ListView_GetItemText(_hwndList, iItemStart, 0, sz, ARRAYSIZE(sz));
  2624. return CharUpperChar(sz[0]);
  2625. }
  2626. LRESULT SFTBarHost::_OnSMNFindItemWorker(PSMNDIALOGMESSAGE pdm)
  2627. {
  2628. LVFINDINFO lvfi;
  2629. LVHITTESTINFO lvhti;
  2630. switch (pdm->flags & SMNDM_FINDMASK)
  2631. {
  2632. case SMNDM_FINDFIRST:
  2633. L_SMNDM_FINDFIRST:
  2634. // Note: We can't just return item 0 because drag/drop pinning
  2635. // may have gotten the physical locations out of sync with the
  2636. // item numbers.
  2637. lvfi.vkDirection = VK_HOME;
  2638. lvfi.flags = LVFI_NEARESTXY;
  2639. pdm->itemID = ListView_FindItem(_hwndList, -1, &lvfi);
  2640. return pdm->itemID >= 0;
  2641. case SMNDM_FINDLAST:
  2642. // Note: We can't just return cItems-1 because drag/drop pinning
  2643. // may have gotten the physical locations out of sync with the
  2644. // item numbers.
  2645. lvfi.vkDirection = VK_END;
  2646. lvfi.flags = LVFI_NEARESTXY;
  2647. pdm->itemID = ListView_FindItem(_hwndList, -1, &lvfi);
  2648. return pdm->itemID >= 0;
  2649. case SMNDM_FINDNEAREST:
  2650. lvfi.pt = pdm->pt;
  2651. lvfi.vkDirection = VK_UP;
  2652. lvfi.flags = LVFI_NEARESTXY;
  2653. pdm->itemID = ListView_FindItem(_hwndList, -1, &lvfi);
  2654. return pdm->itemID >= 0;
  2655. case SMNDM_HITTEST:
  2656. lvhti.pt = pdm->pt;
  2657. pdm->itemID = ListView_HitTest(_hwndList, &lvhti);
  2658. return pdm->itemID >= 0;
  2659. case SMNDM_FINDFIRSTMATCH:
  2660. case SMNDM_FINDNEXTMATCH:
  2661. {
  2662. int iItemStart;
  2663. if ((pdm->flags & SMNDM_FINDMASK) == SMNDM_FINDFIRSTMATCH)
  2664. {
  2665. iItemStart = 0;
  2666. }
  2667. else
  2668. {
  2669. iItemStart = _GetLVCurSel() + 1;
  2670. }
  2671. TCHAR tch = CharUpperChar((TCHAR)pdm->pmsg->wParam);
  2672. int iItems = ListView_GetItemCount(_hwndList);
  2673. for (iItemStart; iItemStart < iItems; iItemStart++)
  2674. {
  2675. PaneItem *pitem = _GetItemFromLV(iItemStart);
  2676. if (GetItemAccelerator(pitem, iItemStart) == tch)
  2677. {
  2678. pdm->itemID = iItemStart;
  2679. return TRUE;
  2680. }
  2681. }
  2682. return FALSE;
  2683. }
  2684. break;
  2685. case SMNDM_FINDNEXTARROW:
  2686. if (pdm->pmsg->wParam == VK_UP)
  2687. {
  2688. pdm->itemID = ListView_GetNextItem(_hwndList, _GetLVCurSel(), LVNI_ABOVE);
  2689. return pdm->itemID >= 0;
  2690. }
  2691. if (pdm->pmsg->wParam == VK_DOWN)
  2692. {
  2693. // HACK! ListView_GetNextItem explicitly fails to find a "next item"
  2694. // if you tell it to start at -1 (no current item), so if there is no
  2695. // focus item, we have to change it to a SMNDM_FINDFIRST.
  2696. int iItem = _GetLVCurSel();
  2697. if (iItem == -1)
  2698. {
  2699. goto L_SMNDM_FINDFIRST;
  2700. }
  2701. pdm->itemID = ListView_GetNextItem(_hwndList, iItem, LVNI_BELOW);
  2702. return pdm->itemID >= 0;
  2703. }
  2704. if (pdm->flags & SMNDM_TRYCASCADE)
  2705. {
  2706. pdm->itemID = _GetLVCurSel();
  2707. return _OnCascade((int)pdm->itemID, MPPF_KEYBOARD | MPPF_INITIALSELECT);
  2708. }
  2709. return FALSE;
  2710. case SMNDM_INVOKECURRENTITEM:
  2711. {
  2712. int iItem = _GetLVCurSel();
  2713. if (iItem >= 0)
  2714. {
  2715. DWORD aif = 0;
  2716. if (pdm->flags & SMNDM_KEYBOARD)
  2717. {
  2718. aif |= AIF_KEYBOARD;
  2719. }
  2720. _ActivateItem(iItem, aif);
  2721. return TRUE;
  2722. }
  2723. }
  2724. return FALSE;
  2725. case SMNDM_OPENCASCADE:
  2726. {
  2727. DWORD mppf = 0;
  2728. if (pdm->flags & SMNDM_KEYBOARD)
  2729. {
  2730. mppf |= MPPF_KEYBOARD | MPPF_INITIALSELECT;
  2731. }
  2732. pdm->itemID = _GetLVCurSel();
  2733. return _OnCascade((int)pdm->itemID, mppf);
  2734. }
  2735. case SMNDM_FINDITEMID:
  2736. return TRUE;
  2737. default:
  2738. ASSERT(!"Unknown SMNDM command");
  2739. break;
  2740. }
  2741. return FALSE;
  2742. }
  2743. LRESULT SFTBarHost::_OnSMNDismiss()
  2744. {
  2745. if (_fNeedsRepopulate)
  2746. {
  2747. _RepopulateList();
  2748. }
  2749. return 0;
  2750. }
  2751. LRESULT SFTBarHost::_OnCascade(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  2752. {
  2753. return _OnCascade((int)wParam, (DWORD)lParam);
  2754. }
  2755. BOOL SFTBarHost::_OnCascade(int iItem, DWORD dwFlags)
  2756. {
  2757. BOOL fSuccess = FALSE;
  2758. SMNTRACKSHELLMENU tsm;
  2759. tsm.dwFlags = dwFlags;
  2760. tsm.itemID = iItem;
  2761. if (iItem >= 0 &&
  2762. ListView_GetItemRect(_hwndList, iItem, &tsm.rcExclude, LVIR_BOUNDS))
  2763. {
  2764. PaneItem *pitem = _GetItemFromLV(iItem);
  2765. if (pitem && pitem->IsCascade())
  2766. {
  2767. if (SUCCEEDED(GetCascadeMenu(pitem, &tsm.psm)))
  2768. {
  2769. MapWindowRect(_hwndList, NULL, &tsm.rcExclude);
  2770. HWND hwnd = _hwnd;
  2771. _iCascading = iItem;
  2772. _SendNotify(_hwnd, SMN_TRACKSHELLMENU, &tsm.hdr);
  2773. tsm.psm->Release();
  2774. fSuccess = TRUE;
  2775. }
  2776. }
  2777. }
  2778. return fSuccess;
  2779. }
  2780. HRESULT SFTBarHost::QueryInterface(REFIID riid, void * *ppvOut)
  2781. {
  2782. static const QITAB qit[] = {
  2783. QITABENT(SFTBarHost, IDropTarget),
  2784. QITABENT(SFTBarHost, IDropSource),
  2785. QITABENT(SFTBarHost, IAccessible),
  2786. QITABENT(SFTBarHost, IDispatch), // IAccessible derives from IDispatch
  2787. { 0 },
  2788. };
  2789. return QISearch(this, qit, riid, ppvOut);
  2790. }
  2791. ULONG SFTBarHost::AddRef()
  2792. {
  2793. return InterlockedIncrement(&_lRef);
  2794. }
  2795. ULONG SFTBarHost::Release()
  2796. {
  2797. ASSERT( 0 != _lRef );
  2798. ULONG cRef = InterlockedDecrement(&_lRef);
  2799. if ( 0 == cRef )
  2800. {
  2801. delete this;
  2802. }
  2803. return cRef;
  2804. }
  2805. void SFTBarHost::_SetDragOver(int iItem)
  2806. {
  2807. if (_iDragOver >= 0)
  2808. {
  2809. ListView_SetItemState(_hwndList, _iDragOver, 0, LVIS_DROPHILITED);
  2810. }
  2811. _iDragOver = iItem;
  2812. if (_iDragOver >= 0)
  2813. {
  2814. ListView_SetItemState(_hwndList, _iDragOver, LVIS_DROPHILITED, LVIS_DROPHILITED);
  2815. _tmDragOver = NonzeroGetTickCount();
  2816. }
  2817. else
  2818. {
  2819. _tmDragOver = 0;
  2820. }
  2821. }
  2822. void SFTBarHost::_ClearInnerDropTarget()
  2823. {
  2824. if (_pdtDragOver)
  2825. {
  2826. ASSERT(_iDragState == DRAGSTATE_ENTERED);
  2827. _pdtDragOver->DragLeave();
  2828. _pdtDragOver->Release();
  2829. _pdtDragOver = NULL;
  2830. DEBUG_CODE(_iDragState = DRAGSTATE_UNINITIALIZED);
  2831. }
  2832. _SetDragOver(-1);
  2833. }
  2834. HRESULT SFTBarHost::_TryInnerDropTarget(int iItem, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
  2835. {
  2836. HRESULT hr;
  2837. if (_iDragOver != iItem)
  2838. {
  2839. _ClearInnerDropTarget();
  2840. // Even if it fails, remember that we have this item so we don't
  2841. // query for the drop target again (and have it fail again).
  2842. _SetDragOver(iItem);
  2843. ASSERT(_pdtDragOver == NULL);
  2844. ASSERT(_iDragState == DRAGSTATE_UNINITIALIZED);
  2845. PaneItem *pitem = _GetItemFromLV(iItem);
  2846. if (pitem && pitem->IsDropTarget())
  2847. {
  2848. hr = _GetUIObjectOfItem(pitem, IID_PPV_ARG(IDropTarget, &_pdtDragOver));
  2849. if (SUCCEEDED(hr))
  2850. {
  2851. hr = _pdtDragOver->DragEnter(_pdtoDragIn, grfKeyState, ptl, pdwEffect);
  2852. if (SUCCEEDED(hr) && *pdwEffect)
  2853. {
  2854. DEBUG_CODE(_iDragState = DRAGSTATE_ENTERED);
  2855. }
  2856. else
  2857. {
  2858. DEBUG_CODE(_iDragState = DRAGSTATE_UNINITIALIZED);
  2859. ATOMICRELEASE(_pdtDragOver);
  2860. }
  2861. }
  2862. }
  2863. }
  2864. ASSERT(_iDragOver == iItem);
  2865. if (_pdtDragOver)
  2866. {
  2867. ASSERT(_iDragState == DRAGSTATE_ENTERED);
  2868. hr = _pdtDragOver->DragOver(grfKeyState, ptl, pdwEffect);
  2869. }
  2870. else
  2871. {
  2872. hr = E_FAIL; // No drop target
  2873. }
  2874. return hr;
  2875. }
  2876. void SFTBarHost::_PurgeDragDropData()
  2877. {
  2878. _SetInsertMarkPosition(-1);
  2879. _fForceArrowCursor = FALSE;
  2880. _ClearInnerDropTarget();
  2881. ATOMICRELEASE(_pdtoDragIn);
  2882. }
  2883. // *** IDropTarget::DragEnter ***
  2884. HRESULT SFTBarHost::DragEnter(IDataObject *pdto, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
  2885. {
  2886. if(_AreChangesRestricted())
  2887. {
  2888. *pdwEffect = DROPEFFECT_NONE;
  2889. return S_OK;
  2890. }
  2891. POINT pt = { ptl.x, ptl.y };
  2892. if (_pdth) {
  2893. _pdth->DragEnter(_hwnd, pdto, &pt, *pdwEffect);
  2894. }
  2895. return _DragEnter(pdto, grfKeyState, ptl, pdwEffect);
  2896. }
  2897. HRESULT SFTBarHost::_DragEnter(IDataObject *pdto, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
  2898. {
  2899. _PurgeDragDropData();
  2900. _fDragToSelf = SHIsSameObject(pdto, _pdtoDragOut);
  2901. _fInsertable = IsInsertable(pdto);
  2902. ASSERT(_pdtoDragIn == NULL);
  2903. _pdtoDragIn = pdto;
  2904. _pdtoDragIn->AddRef();
  2905. return DragOver(grfKeyState, ptl, pdwEffect);
  2906. }
  2907. // *** IDropTarget::DragOver ***
  2908. HRESULT SFTBarHost::DragOver(DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
  2909. {
  2910. if(_AreChangesRestricted())
  2911. {
  2912. *pdwEffect = DROPEFFECT_NONE;
  2913. return S_OK;
  2914. }
  2915. _DebugConsistencyCheck();
  2916. ASSERT(_pdtoDragIn);
  2917. POINT pt = { ptl.x, ptl.y };
  2918. if (_pdth) {
  2919. _pdth->DragOver(&pt, *pdwEffect);
  2920. }
  2921. _fForceArrowCursor = FALSE;
  2922. // Need to remember this because at the point of the drop, OLE gives
  2923. // us the keystate after the user releases the button, so we can't
  2924. // tell what kind of a drag operation the user performed!
  2925. _grfKeyStateLast = grfKeyState;
  2926. #ifdef DEBUG
  2927. if (_fDragToSelf)
  2928. {
  2929. ASSERT(_pdtoDragOut);
  2930. ASSERT(_iDragOut >= 0);
  2931. PaneItem *pitem = _GetItemFromLV(_iDragOut);
  2932. ASSERT(pitem && (pitem->_iPos == _iPosDragOut));
  2933. }
  2934. #endif
  2935. // Find the last item above the cursor position. This allows us
  2936. // to treat the entire blank space at the bottom as belonging to the
  2937. // last item, and separators end up belonging to the item immediately
  2938. // above them. Note that we don't bother testing item zero since
  2939. // he is always above everything (since he's the first item).
  2940. ScreenToClient(_hwndList, &pt);
  2941. POINT ptItem;
  2942. int cItems = ListView_GetItemCount(_hwndList);
  2943. int iItem;
  2944. for (iItem = cItems - 1; iItem >= 1; iItem--)
  2945. {
  2946. ListView_GetItemPosition(_hwndList, iItem, &ptItem);
  2947. if (ptItem.y <= pt.y)
  2948. {
  2949. break;
  2950. }
  2951. }
  2952. //
  2953. // We didn't bother checking item 0 because we knew his position
  2954. // (by treating him special, this also causes all negative coordinates
  2955. // to be treated as belonging to item zero also).
  2956. //
  2957. if (iItem <= 0)
  2958. {
  2959. ptItem.y = 0;
  2960. iItem = 0;
  2961. }
  2962. //
  2963. // Decide whether this is a drag-between or a drag-over...
  2964. //
  2965. // For computational purposes, we treat each tile as four
  2966. // equal-sized "units" tall. For each unit, we consider the
  2967. // possible actions in the order listed.
  2968. //
  2969. // +-----
  2970. // | 0 insert above, drop on, reject
  2971. // | ----
  2972. // | 1 drop on, reject
  2973. // | ----
  2974. // | 2 drop on, reject
  2975. // | ----
  2976. // | 3 insert below, drop on, reject
  2977. // +-----
  2978. //
  2979. // If the listview is empty, then treat as an
  2980. // insert before (imaginary) item zero; i.e., pin
  2981. // to top of the list.
  2982. //
  2983. UINT uUnit = 0;
  2984. if (_cyTile && cItems)
  2985. {
  2986. int dy = pt.y - ptItem.y;
  2987. // Peg out-of-bounds values to the nearest edge.
  2988. if (dy < 0) dy = 0;
  2989. if (dy >= _cyTile) dy = _cyTile - 1;
  2990. // Decide which unit we are in.
  2991. uUnit = 4 * dy / _cyTile;
  2992. ASSERT(uUnit < 4);
  2993. }
  2994. //
  2995. // Now determine the appropriate action depending on which unit
  2996. // we are in.
  2997. //
  2998. int iInsert = -1; // Assume not inserting
  2999. if (_fInsertable)
  3000. {
  3001. // Note! Spec says that if you are in the non-pinned part of
  3002. // the list, we draw the insert bar at the very bottom of
  3003. // the pinned area.
  3004. switch (uUnit)
  3005. {
  3006. case 0:
  3007. iInsert = min(iItem, _cPinned);
  3008. break;
  3009. case 3:
  3010. iInsert = min(iItem+1, _cPinned);
  3011. break;
  3012. }
  3013. }
  3014. //
  3015. // If inserting above or below isn't allowed, try dropping on.
  3016. //
  3017. if (iInsert < 0)
  3018. {
  3019. _SetInsertMarkPosition(-1); // Not inserting
  3020. // Up above, we let separators be hit-tested as if they
  3021. // belongs to the item above them. But that doesn't work for
  3022. // drops, so reject them now.
  3023. //
  3024. // Also reject attempts to drop on the nonexistent item zero,
  3025. // and don't let the user drop an item on itself.
  3026. if (InRange(pt.y, ptItem.y, ptItem.y + _cyTile - 1) &&
  3027. cItems &&
  3028. !(_fDragToSelf && _iDragOut == iItem) &&
  3029. SUCCEEDED(_TryInnerDropTarget(iItem, grfKeyState, ptl, pdwEffect)))
  3030. {
  3031. // Woo-hoo, happy joy!
  3032. }
  3033. else
  3034. {
  3035. // Note that we need to convert a failed drop into a DROPEFFECT_NONE
  3036. // rather than returning a flat-out error code, because if we return
  3037. // an error code, OLE will stop sending us drag/drop notifications!
  3038. *pdwEffect = DROPEFFECT_NONE;
  3039. }
  3040. // If the user is hovering over a cascadable item, then open it.
  3041. // First see if the user has hovered long enough...
  3042. if (_tmDragOver && (GetTickCount() - _tmDragOver) >= _GetCascadeHoverTime())
  3043. {
  3044. _tmDragOver = 0;
  3045. // Now see if it's cascadable
  3046. PaneItem *pitem = _GetItemFromLV(_iDragOver);
  3047. if (pitem && pitem->IsCascade())
  3048. {
  3049. // Must post this message because the cascading is modal
  3050. // and we have to return a result to OLE
  3051. PostMessage(_hwnd, SFTBM_CASCADE, _iDragOver, 0);
  3052. }
  3053. }
  3054. }
  3055. else
  3056. {
  3057. _ClearInnerDropTarget(); // Not dropping
  3058. if (_fDragToSelf)
  3059. {
  3060. // Even though we're going to return DROPEFFECT_LINK,
  3061. // tell the drag source (namely, ourselves) that we would
  3062. // much prefer a regular arrow cursor because this is
  3063. // a Move operation from the user's point of view.
  3064. _fForceArrowCursor = TRUE;
  3065. }
  3066. //
  3067. // If user is dropping to a place where nothing would change,
  3068. // then don't draw an insert mark.
  3069. //
  3070. if (IsInsertMarkPointless(iInsert))
  3071. {
  3072. _SetInsertMarkPosition(-1);
  3073. }
  3074. else
  3075. {
  3076. _SetInsertMarkPosition(iInsert);
  3077. }
  3078. // Sigh. MergedFolder (used by the merged Start Menu)
  3079. // won't let you create shortcuts, so we pretend that
  3080. // we're copying if the data object doesn't permit
  3081. // linking.
  3082. if (*pdwEffect & DROPEFFECT_LINK)
  3083. {
  3084. *pdwEffect = DROPEFFECT_LINK;
  3085. }
  3086. else
  3087. {
  3088. *pdwEffect = DROPEFFECT_COPY;
  3089. }
  3090. }
  3091. return S_OK;
  3092. }
  3093. // *** IDropTarget::DragLeave ***
  3094. HRESULT SFTBarHost::DragLeave()
  3095. {
  3096. if(_AreChangesRestricted())
  3097. {
  3098. return S_OK;
  3099. }
  3100. if (_pdth) {
  3101. _pdth->DragLeave();
  3102. }
  3103. _PurgeDragDropData();
  3104. return S_OK;
  3105. }
  3106. // *** IDropTarget::Drop ***
  3107. HRESULT SFTBarHost::Drop(IDataObject *pdto, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
  3108. {
  3109. if(_AreChangesRestricted())
  3110. {
  3111. *pdwEffect = DROPEFFECT_NONE;
  3112. return S_OK;
  3113. }
  3114. _DebugConsistencyCheck();
  3115. // Use the key state from the last DragOver call
  3116. grfKeyState = _grfKeyStateLast;
  3117. // Need to go through the whole _DragEnter thing again because who knows
  3118. // maybe the data object and coordinates of the drop are different from
  3119. // the ones we got in DragEnter/DragOver... We use _DragEnter, which
  3120. // bypasses the IDropTargetHelper::DragEnter.
  3121. //
  3122. _DragEnter(pdto, grfKeyState, ptl, pdwEffect);
  3123. POINT pt = { ptl.x, ptl.y };
  3124. if (_pdth) {
  3125. _pdth->Drop(pdto, &pt, *pdwEffect);
  3126. }
  3127. int iInsert = _iInsert;
  3128. _SetInsertMarkPosition(-1);
  3129. if (*pdwEffect)
  3130. {
  3131. ASSERT(_pdtoDragIn);
  3132. if (iInsert >= 0) // "add to pin" or "move"
  3133. {
  3134. BOOL fTriedMove = FALSE;
  3135. // First see if it was just a move of an existing pinned item
  3136. if (_fDragToSelf)
  3137. {
  3138. PaneItem *pitem = _GetItemFromLV(_iDragOut);
  3139. if (pitem)
  3140. {
  3141. if (pitem->IsPinned())
  3142. {
  3143. // Yup, it was a move - so move it.
  3144. if (SUCCEEDED(MovePinnedItem(pitem, iInsert)))
  3145. {
  3146. // We used to try to update all the item positions
  3147. // incrementally. This was a major pain in the neck.
  3148. //
  3149. // So now we just do a full refresh. Turns out that a
  3150. // full refresh is fast enough anyway.
  3151. //
  3152. PostMessage(_hwnd, SFTBM_REFRESH, TRUE, 0);
  3153. }
  3154. // We tried to move a pinned item (return TRUE even if
  3155. // we actually failed).
  3156. fTriedMove = TRUE;
  3157. }
  3158. }
  3159. }
  3160. if (!fTriedMove)
  3161. {
  3162. if (SUCCEEDED(InsertPinnedItem(_pdtoDragIn, iInsert)))
  3163. {
  3164. PostMessage(_hwnd, SFTBM_REFRESH, TRUE, 0);
  3165. }
  3166. }
  3167. }
  3168. else if (_pdtDragOver) // Not an insert, maybe it was a plain drop
  3169. {
  3170. ASSERT(_iDragState == DRAGSTATE_ENTERED);
  3171. _pdtDragOver->Drop(_pdtoDragIn, grfKeyState, ptl, pdwEffect);
  3172. }
  3173. }
  3174. _PurgeDragDropData();
  3175. _DebugConsistencyCheck();
  3176. return S_OK;
  3177. }
  3178. void SFTBarHost::_SetInsertMarkPosition(int iInsert)
  3179. {
  3180. if (_iInsert != iInsert)
  3181. {
  3182. _InvalidateInsertMark();
  3183. _iInsert = iInsert;
  3184. _InvalidateInsertMark();
  3185. }
  3186. }
  3187. BOOL SFTBarHost::_GetInsertMarkRect(LPRECT prc)
  3188. {
  3189. if (_iInsert >= 0)
  3190. {
  3191. GetClientRect(_hwndList, prc);
  3192. POINT pt;
  3193. _ComputeListViewItemPosition(_iInsert, &pt);
  3194. int iBottom = pt.y;
  3195. int cyEdge = GetSystemMetrics(SM_CYEDGE);
  3196. prc->top = iBottom - cyEdge;
  3197. prc->bottom = iBottom + cyEdge;
  3198. return TRUE;
  3199. }
  3200. return FALSE;
  3201. }
  3202. void SFTBarHost::_InvalidateInsertMark()
  3203. {
  3204. RECT rc;
  3205. if (_GetInsertMarkRect(&rc))
  3206. {
  3207. InvalidateRect(_hwndList, &rc, TRUE);
  3208. }
  3209. }
  3210. void SFTBarHost::_DrawInsertionMark(LPNMLVCUSTOMDRAW plvcd)
  3211. {
  3212. RECT rc;
  3213. if (_GetInsertMarkRect(&rc))
  3214. {
  3215. FillRect(plvcd->nmcd.hdc, &rc, GetSysColorBrush(COLOR_WINDOWTEXT));
  3216. }
  3217. }
  3218. void SFTBarHost::_DrawSeparator(HDC hdc, int x, int y)
  3219. {
  3220. RECT rc;
  3221. rc.left = x;
  3222. rc.top = y;
  3223. rc.right = rc.left + _cxTile;
  3224. rc.bottom = rc.top + _cySep;
  3225. if (!_hTheme)
  3226. {
  3227. DrawEdge(hdc, &rc, EDGE_ETCHED,BF_TOPLEFT);
  3228. }
  3229. else
  3230. {
  3231. DrawThemeBackground(_hTheme, hdc, _iThemePartSep, 0, &rc, 0);
  3232. }
  3233. }
  3234. void SFTBarHost::_DrawSeparators(LPNMLVCUSTOMDRAW plvcd)
  3235. {
  3236. POINT pt;
  3237. RECT rc;
  3238. for (int iSep = 0; iSep < _cSep; iSep++)
  3239. {
  3240. _ComputeListViewItemPosition(_rgiSep[iSep], &pt);
  3241. pt.y = pt.y - _cyTilePadding + (_cySepTile - _cySep + _cyTilePadding)/2;
  3242. _DrawSeparator(plvcd->nmcd.hdc, pt.x, pt.y);
  3243. }
  3244. // Also draw a bonus separator at the bottom of the list to separate
  3245. // the MFU list from the More Programs button.
  3246. if (_iThemePart == SPP_PROGLIST)
  3247. {
  3248. _ComputeListViewItemPosition(0, &pt);
  3249. GetClientRect(_hwndList, &rc);
  3250. rc.bottom -= _cySep;
  3251. _DrawSeparator(plvcd->nmcd.hdc, pt.x, rc.bottom);
  3252. }
  3253. }
  3254. //****************************************************************************
  3255. //
  3256. // Accessibility
  3257. //
  3258. PaneItem *SFTBarHost::_GetItemFromAccessibility(const VARIANT& varChild)
  3259. {
  3260. if (varChild.lVal)
  3261. {
  3262. return _GetItemFromLV(varChild.lVal - 1);
  3263. }
  3264. return NULL;
  3265. }
  3266. //
  3267. // The default accessibility object reports listview items as
  3268. // ROLE_SYSTEM_LISTITEM, but we know that we are really a menu.
  3269. //
  3270. // Our items are either ROLE_SYSTEM_MENUITEM or ROLE_SYSTEM_MENUPOPUP.
  3271. //
  3272. HRESULT SFTBarHost::get_accRole(VARIANT varChild, VARIANT *pvarRole)
  3273. {
  3274. HRESULT hr = _paccInner->get_accRole(varChild, pvarRole);
  3275. if (SUCCEEDED(hr) && V_VT(pvarRole) == VT_I4)
  3276. {
  3277. switch (V_I4(pvarRole))
  3278. {
  3279. case ROLE_SYSTEM_LIST:
  3280. V_I4(pvarRole) = ROLE_SYSTEM_MENUPOPUP;
  3281. break;
  3282. case ROLE_SYSTEM_LISTITEM:
  3283. V_I4(pvarRole) = ROLE_SYSTEM_MENUITEM;
  3284. break;
  3285. }
  3286. }
  3287. return hr;
  3288. }
  3289. HRESULT SFTBarHost::get_accState(VARIANT varChild, VARIANT *pvarState)
  3290. {
  3291. HRESULT hr = _paccInner->get_accState(varChild, pvarState);
  3292. if (SUCCEEDED(hr) && V_VT(pvarState) == VT_I4)
  3293. {
  3294. PaneItem *pitem = _GetItemFromAccessibility(varChild);
  3295. if (pitem && pitem->IsCascade())
  3296. {
  3297. V_I4(pvarState) |= STATE_SYSTEM_HASPOPUP;
  3298. }
  3299. }
  3300. return hr;
  3301. }
  3302. HRESULT SFTBarHost::get_accKeyboardShortcut(VARIANT varChild, BSTR *pszKeyboardShortcut)
  3303. {
  3304. if (varChild.lVal)
  3305. {
  3306. PaneItem *pitem = _GetItemFromAccessibility(varChild);
  3307. if (pitem)
  3308. {
  3309. return CreateAcceleratorBSTR(GetItemAccelerator(pitem, varChild.lVal - 1), pszKeyboardShortcut);
  3310. }
  3311. }
  3312. *pszKeyboardShortcut = NULL;
  3313. return E_NOT_APPLICABLE;
  3314. }
  3315. //
  3316. // Default action for cascading menus is Open/Close (depending on
  3317. // whether the item is already open); for regular items
  3318. // is Execute.
  3319. //
  3320. HRESULT SFTBarHost::get_accDefaultAction(VARIANT varChild, BSTR *pszDefAction)
  3321. {
  3322. *pszDefAction = NULL;
  3323. if (varChild.lVal)
  3324. {
  3325. PaneItem *pitem = _GetItemFromAccessibility(varChild);
  3326. if (pitem && pitem->IsCascade())
  3327. {
  3328. DWORD dwRole = varChild.lVal - 1 == _iCascading ? ACCSTR_CLOSE : ACCSTR_OPEN;
  3329. return GetRoleString(dwRole, pszDefAction);
  3330. }
  3331. return GetRoleString(ACCSTR_EXECUTE, pszDefAction);
  3332. }
  3333. return E_NOT_APPLICABLE;
  3334. }
  3335. HRESULT SFTBarHost::accDoDefaultAction(VARIANT varChild)
  3336. {
  3337. if (varChild.lVal)
  3338. {
  3339. PaneItem *pitem = _GetItemFromAccessibility(varChild);
  3340. if (pitem && pitem->IsCascade())
  3341. {
  3342. if (varChild.lVal - 1 == _iCascading)
  3343. {
  3344. _SendNotify(_hwnd, SMN_CANCELSHELLMENU);
  3345. return S_OK;
  3346. }
  3347. }
  3348. }
  3349. return CAccessible::accDoDefaultAction(varChild);
  3350. }
  3351. //****************************************************************************
  3352. //
  3353. // Debugging helpers
  3354. //
  3355. #ifdef FULL_DEBUG
  3356. void SFTBarHost::_DebugConsistencyCheck()
  3357. {
  3358. int i;
  3359. int citems;
  3360. if (_hwndList && !_fListUnstable)
  3361. {
  3362. //
  3363. // Check that the items in the listview are in their correct positions.
  3364. //
  3365. citems = ListView_GetItemCount(_hwndList);
  3366. for (i = 0; i < citems; i++)
  3367. {
  3368. PaneItem *pitem = _GetItemFromLV(i);
  3369. if (pitem)
  3370. {
  3371. // Make sure the item number and the iPos are in agreement
  3372. ASSERT(pitem->_iPos == _ItemNoToPos(i));
  3373. ASSERT(_PosToItemNo(pitem->_iPos) == i);
  3374. // Make sure the item is where it should be
  3375. POINT pt, ptShould;
  3376. _ComputeListViewItemPosition(pitem->_iPos, &ptShould);
  3377. ListView_GetItemPosition(_hwndList, i, &pt);
  3378. ASSERT(pt.x == ptShould.x);
  3379. ASSERT(pt.y == ptShould.y);
  3380. }
  3381. }
  3382. }
  3383. }
  3384. #endif
  3385. // iFile is the zero-based index of the file being requested
  3386. // or 0xFFFFFFFF if you don't care about any particular file
  3387. //
  3388. // puFiles receives the number of files in the HDROP
  3389. // or NULL if you don't care about the number of files
  3390. //
  3391. STDAPI_(HRESULT)
  3392. IDataObject_DragQueryFile(IDataObject *pdto, UINT iFile, LPTSTR pszBuf, UINT cch, UINT *puFiles)
  3393. {
  3394. static FORMATETC const feHdrop =
  3395. { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
  3396. STGMEDIUM stgm;
  3397. HRESULT hr;
  3398. // Sigh. IDataObject::GetData has a bad prototype and says that
  3399. // the first parameter is a modifiable FORMATETC, even though it
  3400. // isn't.
  3401. hr = pdto->GetData(const_cast<FORMATETC*>(&feHdrop), &stgm);
  3402. if (SUCCEEDED(hr))
  3403. {
  3404. HDROP hdrop = reinterpret_cast<HDROP>(stgm.hGlobal);
  3405. if (puFiles)
  3406. {
  3407. *puFiles = DragQueryFile(hdrop, 0xFFFFFFFF, NULL, 0);
  3408. }
  3409. if (iFile != 0xFFFFFFFF)
  3410. {
  3411. hr = DragQueryFile(hdrop, iFile, pszBuf, cch) ? S_OK : E_FAIL;
  3412. }
  3413. ReleaseStgMedium(&stgm);
  3414. }
  3415. return hr;
  3416. }
  3417. /*
  3418. * If pidl has an alias, free the original pidl and return the alias.
  3419. * Otherwise, just return pidl unchanged.
  3420. *
  3421. * Expected usage is
  3422. *
  3423. * pidlTarget = ConvertToLogIL(pidlTarget);
  3424. *
  3425. */
  3426. STDAPI_(LPITEMIDLIST) ConvertToLogIL(LPITEMIDLIST pidl)
  3427. {
  3428. LPITEMIDLIST pidlAlias = SHLogILFromFSIL(pidl);
  3429. if (pidlAlias)
  3430. {
  3431. ILFree(pidl);
  3432. return pidlAlias;
  3433. }
  3434. return pidl;
  3435. }
  3436. //****************************************************************************
  3437. //
  3438. STDAPI_(HFONT) LoadControlFont(HTHEME hTheme, int iPart, BOOL fUnderline, DWORD dwSizePercentage)
  3439. {
  3440. LOGFONT lf;
  3441. BOOL bSuccess;
  3442. if (hTheme)
  3443. {
  3444. bSuccess = SUCCEEDED(GetThemeFont(hTheme, NULL, iPart, 0, TMT_FONT, &lf));
  3445. }
  3446. else
  3447. {
  3448. bSuccess = SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lf), &lf, FALSE);
  3449. }
  3450. if (bSuccess)
  3451. {
  3452. // only apply size scaling factor in non-theme case, for themes it makes sense to specify the exact font in the theme
  3453. if (!hTheme && dwSizePercentage && dwSizePercentage != 100)
  3454. {
  3455. lf.lfHeight = (lf.lfHeight * (int)dwSizePercentage) / 100;
  3456. lf.lfWidth = 0; // get the closest based on aspect ratio
  3457. }
  3458. if (fUnderline)
  3459. {
  3460. lf.lfUnderline = TRUE;
  3461. }
  3462. return CreateFontIndirect(&lf);
  3463. }
  3464. return NULL;
  3465. }