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

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