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

6479 lines
187 KiB

  1. #include "cabinet.h"
  2. #include "taskband.h"
  3. #include <shguidp.h>
  4. #include "bandsite.h"
  5. #include "util.h"
  6. #include "tray.h"
  7. #include "rcids.h"
  8. #include "bandsite.h"
  9. #include "startmnu.h"
  10. #include "mixer.h"
  11. #include <regstr.h>
  12. #include "uemapp.h"
  13. #include "strsafe.h"
  14. #define TIF_RENDERFLASHED 0x000000001
  15. #define TIF_SHOULDTIP 0x000000002
  16. #define TIF_ACTIVATEALT 0x000000004
  17. #define TIF_EVERACTIVEALT 0x000000008
  18. #define TIF_FLASHING 0x000000010
  19. #define TIF_TRANSPARENT 0x000000020
  20. #define TIF_CHECKED 0x000000040
  21. #define TIF_ISGLOMMING 0x000000080
  22. #define TIF_NEEDSREDRAW 0x000000100
  23. #define IDT_SYSMENU 2
  24. #define IDT_ASYNCANIMATION 3
  25. #define IDT_REDRAW 4
  26. #define IDT_RECHECKRUDEAPP1 5
  27. #define IDT_RECHECKRUDEAPP2 6
  28. #define IDT_RECHECKRUDEAPP3 7
  29. #define IDT_RECHECKRUDEAPP4 8
  30. #define IDT_RECHECKRUDEAPP5 9
  31. #define TIMEOUT_SYSMENU 2000
  32. #define TIMEOUT_SYSMENU_HUNG 125
  33. #define GLOM_OLDEST 0
  34. #define GLOM_BIGGEST 1
  35. #define GLOM_SIZE 2
  36. #define ANIMATE_INSERT 0
  37. #define ANIMATE_DELETE 1
  38. #define ANIMATE_GLOM 2
  39. #define IL_NORMAL 0
  40. #define IL_SHIL 1
  41. #define MAX_WNDTEXT 80 // arbitrary, matches NMTTDISPINFO.szText
  42. #define INVALID_PRIORITY (THREAD_PRIORITY_LOWEST - 1)
  43. const TCHAR c_szTaskSwClass[] = TEXT("MSTaskSwWClass");
  44. const TCHAR c_wzTaskBandTheme[] = TEXT("TaskBand");
  45. const TCHAR c_wzTaskBandThemeVert[] = TEXT("TaskBandVert");
  46. const TCHAR c_wzTaskBandGroupMenuTheme[] = TEXT("TaskBandGroupMenu");
  47. typedef struct
  48. {
  49. WCHAR szExeName[MAX_PATH];
  50. } EXCLUDELIST;
  51. static const EXCLUDELIST g_rgNoGlom[] =
  52. {
  53. { L"rundll32.exe" }
  54. // Add any future apps that shouldn't be glommed
  55. };
  56. void _RestoreWindow(HWND hwnd, DWORD dwFlags);
  57. HMENU _GetSystemMenu(HWND hwnd);
  58. BOOL _IsRudeWindowActive(HWND hwnd);
  59. ////////////////////////////////////////////////////////////////////////////
  60. //
  61. // BEGIN CTaskBandSMC
  62. //
  63. // CTaskBand can't implement IShellMenuCallback itself because menuband
  64. // sets itself as the callback's site. Hence this class.
  65. //
  66. //
  67. ////////////////////////////////////////////////////////////////////////////
  68. class CTaskBandSMC : public IShellMenuCallback
  69. , public IContextMenu
  70. , public IObjectWithSite
  71. {
  72. public:
  73. // *** IUnknown methods ***
  74. STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj)
  75. {
  76. static const QITAB qit[] =
  77. {
  78. QITABENT(CTaskBandSMC, IShellMenuCallback),
  79. QITABENT(CTaskBandSMC, IContextMenu),
  80. QITABENT(CTaskBandSMC, IObjectWithSite),
  81. { 0 },
  82. };
  83. return QISearch(this, qit, riid, ppvObj);
  84. }
  85. STDMETHODIMP_(ULONG) AddRef() { return ++_cRef; }
  86. STDMETHODIMP_(ULONG) Release()
  87. {
  88. ASSERT(_cRef > 0);
  89. if (--_cRef > 0)
  90. {
  91. return _cRef;
  92. }
  93. delete this;
  94. return 0;
  95. }
  96. // *** IShellMenuCallback methods ***
  97. STDMETHODIMP CallbackSM(LPSMDATA smd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  98. // *** IContextMenu methods ***
  99. STDMETHODIMP QueryContextMenu(HMENU hmenu, UINT iIndexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
  100. STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO lpici);
  101. STDMETHODIMP GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pRes, LPSTR pszName, UINT cchMax) { return E_NOTIMPL; }
  102. // *** IObjectWithSite methods ***
  103. STDMETHODIMP SetSite(IUnknown* punkSite)
  104. {
  105. ATOMICRELEASE(_punkSite);
  106. if (punkSite != NULL)
  107. {
  108. _punkSite = punkSite;
  109. _punkSite->AddRef();
  110. }
  111. return S_OK;
  112. }
  113. STDMETHODIMP GetSite(REFIID riid, void** ppvSite) { return E_NOTIMPL; };
  114. CTaskBandSMC(CTaskBand* ptb) : _cRef(1)
  115. {
  116. _ptb = ptb;
  117. _ptb->AddRef();
  118. }
  119. private:
  120. virtual ~CTaskBandSMC() { ATOMICRELEASE(_ptb); }
  121. ULONG _cRef;
  122. CTaskBand* _ptb;
  123. IUnknown* _punkSite;
  124. HWND _hwndSelected;
  125. };
  126. STDMETHODIMP CTaskBandSMC::CallbackSM(LPSMDATA psmd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  127. {
  128. ASSERT(_ptb);
  129. HRESULT hres = S_FALSE;
  130. if (!_ptb->_IsButtonChecked(_ptb->_iIndexPopup) && (SMC_EXITMENU != uMsg))
  131. {
  132. _ptb->_SetCurSel(_ptb->_iIndexPopup, TRUE);
  133. }
  134. switch (uMsg)
  135. {
  136. case SMC_EXEC:
  137. {
  138. PTASKITEM pti = _ptb->_GetItem(psmd->uId);
  139. if (pti)
  140. {
  141. _ptb->_SetCurSel(psmd->uId, FALSE);
  142. _ptb->_OnButtonPressed(psmd->uId, pti, lParam);
  143. hres = S_OK;
  144. }
  145. }
  146. break;
  147. case SMC_GETINFO:
  148. {
  149. SMINFO* psminfo = (SMINFO*)lParam;
  150. hres = S_OK;
  151. if (psminfo->dwMask & SMIM_TYPE)
  152. {
  153. psminfo->dwType = SMIT_STRING;
  154. }
  155. if (psminfo->dwMask & SMIM_FLAGS)
  156. {
  157. psminfo->dwFlags = SMIF_ICON | SMIF_DRAGNDROP;
  158. }
  159. if (psminfo->dwMask & SMIM_ICON)
  160. {
  161. TBBUTTONINFO tbbi;
  162. tbbi.iImage = I_IMAGENONE;
  163. PTASKITEM pti = _ptb->_GetItem(psmd->uId, &tbbi);
  164. if (pti && tbbi.iImage == I_IMAGECALLBACK)
  165. {
  166. _ptb->_UpdateItemIcon(psmd->uId);
  167. _ptb->_GetItem(psmd->uId, &tbbi);
  168. }
  169. psminfo->iIcon = tbbi.iImage;
  170. }
  171. }
  172. break;
  173. case SMC_CUSTOMDRAW:
  174. {
  175. PTASKITEM pti = _ptb->_GetItem(psmd->uId);
  176. if (pti)
  177. {
  178. *(LRESULT*)wParam = _ptb->_HandleCustomDraw((NMTBCUSTOMDRAW*)lParam, pti);
  179. hres = S_OK;
  180. }
  181. }
  182. break;
  183. case SMC_SELECTITEM:
  184. {
  185. PTASKITEM pti = _ptb->_GetItem(psmd->uId);
  186. _hwndSelected = pti ? pti->hwnd : NULL;
  187. }
  188. break;
  189. case SMC_GETOBJECT:
  190. {
  191. GUID *pguid = (GUID*)wParam;
  192. if (IsEqualIID(*pguid, IID_IContextMenu) && !SHRestricted(REST_NOTRAYCONTEXTMENU))
  193. {
  194. hres = QueryInterface(*pguid, (void **)lParam);
  195. }
  196. else
  197. {
  198. hres = E_FAIL;
  199. }
  200. }
  201. break;
  202. case SMC_GETINFOTIP:
  203. {
  204. PTASKITEM pti = _ptb->_GetItem(psmd->uId);
  205. if (pti)
  206. {
  207. _ptb->_GetItemTitle(psmd->uId, (TCHAR*)wParam, (int)lParam, TRUE);
  208. hres = S_OK;
  209. }
  210. }
  211. break;
  212. case SMC_GETIMAGELISTS:
  213. {
  214. HIMAGELIST himl = (HIMAGELIST)_ptb->_tb.SendMessage(TB_GETIMAGELIST, psmd->uId, 0);
  215. if (himl)
  216. {
  217. *((HIMAGELIST*)lParam) = *((HIMAGELIST*)wParam) = himl;
  218. hres = S_OK;
  219. }
  220. }
  221. break;
  222. case SMC_EXITMENU:
  223. {
  224. _hwndSelected = NULL;
  225. CToolTipCtrl ttc = _ptb->_tb.GetToolTips();
  226. ttc.Activate(TRUE);
  227. _ptb->_iIndexPopup = -1;
  228. }
  229. break;
  230. }
  231. return hres;
  232. }
  233. // *** IContextMenu methods ***
  234. STDMETHODIMP CTaskBandSMC::QueryContextMenu(HMENU hmenu, UINT iIndexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
  235. {
  236. ASSERT(_ptb);
  237. HRESULT hr = ResultFromShort(0);
  238. if (_hwndSelected != NULL)
  239. {
  240. HMENU hmenuTemp = _GetSystemMenu(_hwndSelected);
  241. if (hmenuTemp)
  242. {
  243. if (Shell_MergeMenus(hmenu, hmenuTemp, 0, iIndexMenu, idCmdLast, uFlags))
  244. {
  245. SetMenuDefaultItem(hmenu, 0, MF_BYPOSITION);
  246. hr = ResultFromShort(GetMenuItemCount(hmenuTemp));
  247. }
  248. DestroyMenu(hmenuTemp);
  249. }
  250. }
  251. return hr;
  252. }
  253. STDMETHODIMP CTaskBandSMC::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
  254. {
  255. ASSERT(_ptb);
  256. PTASKITEM pti = _ptb->_FindItemByHwnd(_hwndSelected);
  257. if (pti)
  258. {
  259. int iCommand = LOWORD(lpici->lpVerb);
  260. if (iCommand)
  261. {
  262. _RestoreWindow(pti->hwnd, pti->dwFlags);
  263. _ptb->_ExecuteMenuOption(pti->hwnd, iCommand);
  264. }
  265. }
  266. return S_OK;
  267. }
  268. ////////////////////////////////////////////////////////////////////////////
  269. //
  270. // END CTaskBandSMC
  271. //
  272. ////////////////////////////////////////////////////////////////////////////
  273. ULONG CTaskBand::AddRef()
  274. {
  275. _cRef++;
  276. return _cRef;
  277. }
  278. ULONG CTaskBand::Release()
  279. {
  280. ASSERT(_cRef > 0);
  281. _cRef--;
  282. if (_cRef > 0)
  283. return _cRef;
  284. delete this;
  285. return 0;
  286. }
  287. HRESULT CTaskBand::GetWindow(HWND * lphwnd)
  288. {
  289. *lphwnd = _hwnd;
  290. if (_hwnd)
  291. return S_OK;
  292. return E_FAIL;
  293. }
  294. CTaskBand::CTaskBand() : _dwBandID((DWORD)-1), _iDropItem(-2), _iIndexActiveAtLDown(-1), _cRef(1), _iOldPriority(INVALID_PRIORITY)
  295. {
  296. }
  297. CTaskBand::~CTaskBand()
  298. {
  299. ATOMICRELEASE(_punkSite);
  300. ATOMICRELEASE(_pimlSHIL);
  301. if (_dsaAII)
  302. _dsaAII.Destroy();
  303. if (_hfontCapNormal)
  304. DeleteFont(_hfontCapNormal);
  305. if (_hfontCapBold)
  306. DeleteFont(_hfontCapBold);
  307. }
  308. HRESULT CTaskBand::QueryInterface(REFIID riid, LPVOID* ppvObj)
  309. {
  310. static const QITAB qit[] =
  311. {
  312. QITABENTMULTI(CTaskBand, IDockingWindow, IDeskBand),
  313. QITABENTMULTI(CTaskBand, IOleWindow, IDeskBand),
  314. QITABENT(CTaskBand, IDeskBand),
  315. QITABENT(CTaskBand, IObjectWithSite),
  316. QITABENT(CTaskBand, IDropTarget),
  317. QITABENT(CTaskBand, IInputObject),
  318. QITABENTMULTI(CTaskBand, IPersist, IPersistStream),
  319. QITABENT(CTaskBand, IPersistStream),
  320. QITABENT(CTaskBand, IWinEventHandler),
  321. QITABENT(CTaskBand, IOleCommandTarget),
  322. { 0 },
  323. };
  324. return QISearch(this, qit, riid, ppvObj);
  325. }
  326. HRESULT CTaskBand::Init(CTray* ptray)
  327. {
  328. HRESULT hr = E_OUTOFMEMORY;
  329. if (_dsaAII.Create(2))
  330. {
  331. _ptray = ptray;
  332. hr = S_OK;
  333. }
  334. return hr;
  335. }
  336. // *** IPersistStream methods ***
  337. HRESULT CTaskBand::GetClassID(LPCLSID pClassID)
  338. {
  339. *pClassID = CLSID_TaskBand;
  340. return S_OK;
  341. }
  342. HRESULT CTaskBand::_BandInfoChanged()
  343. {
  344. if (_dwBandID != (DWORD)-1)
  345. {
  346. VARIANTARG var = {0};
  347. var.vt = VT_I4;
  348. var.lVal = _dwBandID;
  349. return IUnknown_Exec(_punkSite, &CGID_DeskBand, DBID_BANDINFOCHANGED, 0, &var, NULL);
  350. }
  351. else
  352. return S_OK;
  353. }
  354. HRESULT CTaskBand::Load(IStream *ps)
  355. {
  356. return S_OK;
  357. }
  358. // *** IOleCommandTarget ***
  359. STDMETHODIMP CTaskBand::Exec(const GUID *pguidCmdGroup,DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
  360. {
  361. HRESULT hr = OLECMDERR_E_NOTSUPPORTED;
  362. return hr;
  363. }
  364. STDMETHODIMP CTaskBand::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext)
  365. {
  366. if (pguidCmdGroup)
  367. {
  368. if (IsEqualIID(*pguidCmdGroup, IID_IDockingWindow))
  369. {
  370. for (UINT i = 0; i < cCmds; i++)
  371. {
  372. switch (rgCmds[i].cmdID)
  373. {
  374. case DBID_PERMITAUTOHIDE:
  375. rgCmds[i].cmdf = OLECMDF_SUPPORTED;
  376. if (!_fFlashing)
  377. {
  378. rgCmds[i].cmdf |= OLECMDF_ENABLED;
  379. }
  380. break;
  381. }
  382. }
  383. return S_OK;
  384. }
  385. }
  386. return OLECMDERR_E_UNKNOWNGROUP;
  387. }
  388. //*** IInputObject methods ***
  389. HRESULT CTaskBand::HasFocusIO()
  390. {
  391. BOOL f;
  392. HWND hwndFocus = GetFocus();
  393. f = IsChildOrHWND(_hwnd, hwndFocus);
  394. ASSERT(hwndFocus != NULL || !f);
  395. ASSERT(_hwnd != NULL || !f);
  396. return f ? S_OK : S_FALSE;
  397. }
  398. HRESULT CTaskBand::UIActivateIO(BOOL fActivate, LPMSG lpMsg)
  399. {
  400. ASSERT(NULL == lpMsg || IS_VALID_WRITE_PTR(lpMsg, MSG));
  401. if (fActivate)
  402. {
  403. // don't show a hot item if we weren't properly tabbed
  404. // into/clicked on, in which case we have a NULL lpMsg,
  405. // e.g. if the tray just decided to activate us for lack of
  406. // anyone better.
  407. _fDenyHotItemChange = !lpMsg;
  408. IUnknown_OnFocusChangeIS(_punkSite, SAFECAST(this, IInputObject*), TRUE);
  409. ::SetFocus(_hwnd);
  410. _fDenyHotItemChange = FALSE;
  411. }
  412. else
  413. {
  414. // if we don't have focus, we're fine;
  415. // if we do have focus, there's nothing we can do about it...
  416. }
  417. return S_OK;
  418. }
  419. HRESULT CTaskBand::SetSite(IUnknown* punk)
  420. {
  421. if (punk && !_hwnd)
  422. {
  423. _LoadSettings();
  424. _RegisterWindowClass();
  425. HWND hwndParent;
  426. IUnknown_GetWindow(punk, &hwndParent);
  427. HWND hwnd = CreateWindowEx(0, c_szTaskSwClass, NULL,
  428. WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
  429. 0, 0, 0, 0, hwndParent, NULL, hinstCabinet, (void*)(CImpWndProc*)this);
  430. SetWindowTheme(hwnd, c_wzTaskBandTheme, NULL);
  431. }
  432. ATOMICRELEASE(_punkSite);
  433. if (punk)
  434. {
  435. _punkSite = punk;
  436. punk->AddRef();
  437. }
  438. return S_OK;
  439. }
  440. HRESULT CTaskBand::GetBandInfo(DWORD dwBandID, DWORD fViewMode,
  441. DESKBANDINFO* pdbi)
  442. {
  443. _dwBandID = dwBandID;
  444. pdbi->ptMaxSize.y = -1;
  445. pdbi->ptActual.y = g_cySize + 2*g_cyEdge;
  446. LONG lButHeight = _GetCurButtonHeight();
  447. if (fViewMode & DBIF_VIEWMODE_VERTICAL)
  448. {
  449. pdbi->ptMinSize.x = lButHeight;
  450. // The 1.2 gives us enough space for the dropdown arrow
  451. pdbi->ptMinSize.y = lButHeight * (_fGlom ? 1.2 : 1);
  452. pdbi->ptIntegral.y = 1;
  453. }
  454. else
  455. {
  456. TBMETRICS tbm;
  457. _GetToolbarMetrics(&tbm);
  458. pdbi->ptMinSize.x = lButHeight * 3;
  459. pdbi->ptMinSize.y = lButHeight;
  460. pdbi->ptIntegral.y = lButHeight + tbm.cyButtonSpacing;
  461. }
  462. pdbi->dwModeFlags = DBIMF_VARIABLEHEIGHT | DBIMF_UNDELETEABLE | DBIMF_TOPALIGN;
  463. pdbi->dwMask &= ~DBIM_TITLE; // no title for us (ever)
  464. DWORD dwOldViewMode = _dwViewMode;
  465. _dwViewMode = fViewMode;
  466. if (_tb && (_dwViewMode != dwOldViewMode))
  467. {
  468. SendMessage(_tb, TB_SETWINDOWTHEME, 0, (LPARAM)(_IsHorizontal() ? c_wzTaskBandTheme : c_wzTaskBandThemeVert));
  469. _CheckSize();
  470. }
  471. return S_OK;
  472. }
  473. void _RaiseDesktop(BOOL fRaise)
  474. {
  475. SendMessage(v_hwndTray, TM_RAISEDESKTOP, fRaise, 0);
  476. }
  477. // *** IDropTarget methods ***
  478. STDMETHODIMP CTaskBand::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
  479. {
  480. _DragEnter(_hwnd, ptl, pdtobj);
  481. IUnknown_DragEnter(_punkSite, pdtobj, grfKeyState, ptl, pdwEffect);
  482. _iDropItem = -2; // reset to no target
  483. *pdwEffect = DROPEFFECT_LINK;
  484. return S_OK;
  485. }
  486. STDMETHODIMP CTaskBand::DragLeave()
  487. {
  488. IUnknown_DragLeave(_punkSite);
  489. DAD_DragLeave();
  490. return S_OK;
  491. }
  492. STDMETHODIMP CTaskBand::DragOver(DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
  493. {
  494. int iHitNew = _HitTest(ptl);
  495. if (iHitNew == -1)
  496. {
  497. DWORD dwEffect = *pdwEffect;
  498. IUnknown_DragOver(_punkSite, grfKeyState, ptl, &dwEffect);
  499. }
  500. *pdwEffect = DROPEFFECT_LINK;
  501. _DragMove(_hwnd, ptl);
  502. if (_iDropItem != iHitNew)
  503. {
  504. _iDropItem = iHitNew;
  505. _dwTriggerStart = GetTickCount();
  506. _dwTriggerDelay = 250;
  507. if (iHitNew == -1)
  508. {
  509. _dwTriggerDelay += 250; // make a little longer for minimize all
  510. }
  511. }
  512. else if (GetTickCount() - _dwTriggerStart > _dwTriggerDelay)
  513. {
  514. DAD_ShowDragImage(FALSE); // unlock the drag sink if we are dragging.
  515. if (_iDropItem == -1)
  516. {
  517. _RaiseDesktop(TRUE);
  518. }
  519. else if (_iDropItem >= 0 && _iDropItem < _tb.GetButtonCount())
  520. {
  521. _iIndexLastPopup = -1;
  522. _SwitchToItem(_iDropItem, _GetItem(_iDropItem)->hwnd, TRUE);
  523. UpdateWindow(v_hwndTray);
  524. }
  525. DAD_ShowDragImage(TRUE); // restore the lock state.
  526. _dwTriggerDelay += 10000; // don't let this happen again for 10 seconds
  527. // simulate a single shot event
  528. }
  529. if (_iDropItem != -1)
  530. *pdwEffect = DROPEFFECT_MOVE; // try to get the move cursor
  531. else
  532. *pdwEffect = DROPEFFECT_NONE;
  533. return S_OK;
  534. }
  535. STDMETHODIMP CTaskBand::Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
  536. {
  537. IUnknown_DragLeave(_punkSite);
  538. DAD_DragLeave();
  539. //
  540. // post ourselves a message to put up a message box to explain that you
  541. // can't drag to the taskbar. we need to return from the Drop method
  542. // now so the DragSource isn't hung while our box is up
  543. //
  544. PostMessage(_hwnd, TBC_WARNNODROP, 0, 0L);
  545. // be sure to clear DROPEFFECT_MOVE so apps don't delete their data
  546. *pdwEffect = DROPEFFECT_NONE;
  547. return S_OK;
  548. }
  549. // *** IWinEventHandler methods ***
  550. HRESULT CTaskBand::OnWinEvent(HWND hwnd, UINT dwMsg, WPARAM wParam, LPARAM lParam, LRESULT* plres)
  551. {
  552. *plres = 0;
  553. switch (dwMsg)
  554. {
  555. case WM_WININICHANGE:
  556. _HandleWinIniChange(wParam, lParam, FALSE);
  557. break;
  558. case WM_NOTIFY:
  559. if (lParam)
  560. {
  561. switch (((LPNMHDR)lParam)->code)
  562. {
  563. case NM_SETFOCUS:
  564. IUnknown_OnFocusChangeIS(_punkSite, SAFECAST(this, IInputObject*), TRUE);
  565. break;
  566. }
  567. }
  568. break;
  569. }
  570. return S_OK;
  571. }
  572. HRESULT CTaskBand::IsWindowOwner(HWND hwnd)
  573. {
  574. BOOL bRet = IsChildOrHWND(_hwnd, hwnd);
  575. ASSERT (_hwnd || !bRet);
  576. ASSERT (hwnd || !bRet);
  577. return bRet ? S_OK : S_FALSE;
  578. }
  579. //-----------------------------------------------------------------------------
  580. // DESCRIPTION: Returns whether or not the button is hidden
  581. //
  582. // PARAMETERS: 1. hwndToolBar - handle to the toolbar window
  583. // 2. iIndex - item index
  584. //
  585. // RETURN: TRUE = Item is visible, FALSE = Item is hidden.
  586. //-----------------------------------------------------------------------------
  587. BOOL ToolBar_IsVisible(HWND hwndToolBar, int iIndex)
  588. {
  589. TBBUTTONINFO tbbi;
  590. tbbi.cbSize = sizeof(tbbi);
  591. tbbi.dwMask = TBIF_STATE | TBIF_BYINDEX;
  592. SendMessage(hwndToolBar, TB_GETBUTTONINFO, iIndex, (LPARAM) &tbbi);
  593. return !(tbbi.fsState & TBSTATE_HIDDEN);
  594. }
  595. //*****************************************************************************
  596. //
  597. // ITEM ANIMATION FUNCTIONS
  598. //
  599. //*****************************************************************************
  600. //-----------------------------------------------------------------------------
  601. // DESCRIPTION: Inserts item(s) into the animation list
  602. //
  603. // PARAMETERS: 1. iIndex - index for item, group index for a group
  604. // 2. fExpand - TRUE = Insert or Unglom, FALSE = Delete or Glom
  605. // 3. fGlomAnimation - TRUE = this is a glom or unglom animation
  606. //-----------------------------------------------------------------------------
  607. BOOL CTaskBand::_AnimateItems(int iIndex, BOOL fExpand, BOOL fGlomAnimation)
  608. {
  609. ANIMATIONITEMINFO aii;
  610. _SetAnimationState(&aii, fExpand, fGlomAnimation);
  611. // Is item being inserted into glomming group?
  612. if (aii.fState == ANIMATE_INSERT)
  613. {
  614. int iIndexGroup = _GetGroupIndex(iIndex);
  615. if (_GetItem(iIndexGroup)->dwFlags & TIF_ISGLOMMING)
  616. {
  617. aii.fState = ANIMATE_GLOM;
  618. }
  619. }
  620. else if (aii.fState == ANIMATE_GLOM)
  621. {
  622. _GetItem(iIndex)->dwFlags |= TIF_ISGLOMMING;
  623. }
  624. // Number of items to animate
  625. int cItems = 1;
  626. if (fGlomAnimation)
  627. {
  628. // insert the group
  629. cItems = _GetGroupSize(iIndex);
  630. iIndex++;
  631. }
  632. // Insert items into animation list
  633. while(cItems)
  634. {
  635. aii.iIndex = iIndex;
  636. aii.pti = _GetItem(iIndex);
  637. if (aii.fState == ANIMATE_DELETE)
  638. {
  639. // NOTE: HWND_TOPMOST is used here to indicate that the deleted
  640. // button is being animated. This allows the button to stay
  641. // around after its hwnd becomes invalid
  642. aii.pti->hwnd = HWND_TOPMOST;
  643. aii.pti->dwFlags |= TIF_TRANSPARENT;
  644. }
  645. //sorts left to right && removes redundant items
  646. int iAnimationPos = _GetAnimationInsertPos(iIndex);
  647. _dsaAII.InsertItem(iAnimationPos++, &aii);
  648. cItems--;
  649. iIndex++;
  650. }
  651. SetTimer(_hwnd, IDT_ASYNCANIMATION, 100, NULL);
  652. return TRUE;
  653. }
  654. //-----------------------------------------------------------------------------
  655. // DESCRIPTION: Animates the items in the animtation list by one step.
  656. //-----------------------------------------------------------------------------
  657. void CTaskBand::_AsyncAnimateItems()
  658. {
  659. BOOL fRedraw = (BOOL)SendMessage(_tb, WM_SETREDRAW, FALSE, 0);
  660. // Glomming is turned off here because in the middle of the animation we
  661. // may call _DeleteItem which could cause an unglom\glom.
  662. // This is bad because it would modify the contents of animation list that
  663. // we are in the middle of processing.
  664. BOOL fGlom = _fGlom;
  665. _fGlom = FALSE;
  666. _UpdateAnimationIndices();
  667. _ResizeAnimationItems();
  668. int iDistanceLeft = _CheckAnimationSize();
  669. _fGlom = fGlom;
  670. _CheckSize();
  671. SendMessage(_tb, WM_SETREDRAW, fRedraw, 0);
  672. UpdateWindow(_tb);
  673. if (_dsaAII.GetItemCount())
  674. {
  675. SetTimer(_hwnd, IDT_ASYNCANIMATION, _GetStepTime(iDistanceLeft), NULL);
  676. }
  677. else
  678. {
  679. KillTimer(_hwnd, IDT_ASYNCANIMATION);
  680. if (_ptray->_hwndLastActive)
  681. {
  682. int iIndex = _FindIndexByHwnd(_ptray->_hwndLastActive);
  683. if ((iIndex != -1) && (_IsButtonChecked(iIndex)))
  684. {
  685. _ScrollIntoView(iIndex);
  686. }
  687. }
  688. _RestoreThreadPriority();
  689. // Make sure no one was glommed into a group of one
  690. // there are certain race conditions where this can happen
  691. for (int i = _tb.GetButtonCount() - 1; i >= 0; i--)
  692. {
  693. PTASKITEM pti = _GetItem(i);
  694. if (!pti->hwnd)
  695. {
  696. int iSize = _GetGroupSize(i);
  697. if ((iSize < 2) && (!_IsHidden(i)))
  698. {
  699. _Glom(i, FALSE);
  700. }
  701. }
  702. }
  703. }
  704. }
  705. //-----------------------------------------------------------------------------
  706. // DESCRIPTION: Adjusts the widths of the animating items by the animation
  707. // step.
  708. //
  709. // RETURN: The Total width of all animating items.
  710. //-----------------------------------------------------------------------------
  711. void CTaskBand::_ResizeAnimationItems()
  712. {
  713. int cxStep = _GetAnimationStep();
  714. for (int i = _dsaAII.GetItemCount() - 1; i >= 0; i--)
  715. {
  716. PANIMATIONITEMINFO paii = _dsaAII.GetItemPtr(i);
  717. _SetAnimationItemWidth(paii, cxStep);
  718. }
  719. }
  720. //-----------------------------------------------------------------------------
  721. // DESCRIPTION: Checks if animation items have reached their target animation
  722. // width
  723. //
  724. // RETURN: The total distance left to animate
  725. //-----------------------------------------------------------------------------
  726. int CTaskBand::_CheckAnimationSize()
  727. {
  728. PANIMATIONITEMINFO paii;
  729. int iTotDistLeft = 0;
  730. int iRemainder = 0;
  731. int iNormalWidth = _GetIdealWidth(&iRemainder);
  732. int cAnimatingItems = _dsaAII.GetItemCount();
  733. for (int i = cAnimatingItems - 1; i >= 0; i--)
  734. {
  735. paii = _dsaAII.GetItemPtr(i);
  736. if (paii)
  737. {
  738. int iDistLeft = _GetAnimationDistLeft(paii, iNormalWidth);
  739. if (!iDistLeft)
  740. {
  741. ANIMATIONITEMINFO aiiTemp = *paii;
  742. _dsaAII.DeleteItem(i);
  743. _FinishAnimation(&aiiTemp);
  744. }
  745. else
  746. {
  747. iTotDistLeft += iDistLeft;
  748. }
  749. }
  750. #ifdef DEBUG
  751. else
  752. {
  753. int nCurrentCount = _dsaAII.GetItemCount();
  754. if (i >= nCurrentCount)
  755. TraceMsg(TF_ERROR, "Invalid counter %x in the loop, size = %x", i, nCurrentCount);
  756. else
  757. TraceMsg(TF_ERROR, "NULL paii for %x.", i);
  758. }
  759. #endif
  760. }
  761. return iTotDistLeft;
  762. }
  763. //-----------------------------------------------------------------------------
  764. // DESCRIPTION: Sets the animation state for an ANIMATIONITEMINFO struct.
  765. //
  766. // PARAMETERS: 1. paii - PANIMATIONITEMINFO for the animation item
  767. // 2. fExpand - TRUE = Insert or Unglom, FALSE = Delete or Glom
  768. // 3. fGlomAnimation - TRUE = this is a glom or unglom animation
  769. //-----------------------------------------------------------------------------
  770. void CTaskBand::_SetAnimationState(PANIMATIONITEMINFO paii, BOOL fExpand,
  771. BOOL fGlomAnimation)
  772. {
  773. if (fExpand)
  774. {
  775. paii->fState = ANIMATE_INSERT;
  776. }
  777. else
  778. {
  779. if (fGlomAnimation)
  780. {
  781. paii->fState = ANIMATE_GLOM;
  782. }
  783. else
  784. {
  785. paii->fState = ANIMATE_DELETE;
  786. }
  787. }
  788. }
  789. //-----------------------------------------------------------------------------
  790. // DESCRIPTION: Determines the animation list index that keeps the list in the
  791. // same order as the toolbar indexes.
  792. // (Duplicate toolbar items are removed from the animation list.)
  793. //
  794. // PARAMETERS: 1. iIndex - item's index in the toolbar
  795. //
  796. // RETURN: The position the item should be inserted into the animation list
  797. //-----------------------------------------------------------------------------
  798. int CTaskBand::_GetAnimationInsertPos(int iIndex)
  799. {
  800. int iPos = 0;
  801. if (_dsaAII.GetItemCount())
  802. {
  803. _UpdateAnimationIndices();
  804. for (int i = _dsaAII.GetItemCount() - 1; i >= 0; i--)
  805. {
  806. PANIMATIONITEMINFO paii = _dsaAII.GetItemPtr(i);
  807. if (paii->iIndex == iIndex)
  808. {
  809. // remove duplicate
  810. _dsaAII.DeleteItem(i);
  811. iPos = i;
  812. break;
  813. }
  814. else if (paii->iIndex < iIndex)
  815. {
  816. iPos = i + 1;
  817. break;
  818. }
  819. }
  820. }
  821. return iPos;
  822. }
  823. void CTaskBand::_RemoveItemFromAnimationList(PTASKITEM ptiRemove)
  824. {
  825. for (int i = _dsaAII.GetItemCount() - 1; i >= 0; i--)
  826. {
  827. PANIMATIONITEMINFO paii = _dsaAII.GetItemPtr(i);
  828. if (paii->pti == ptiRemove)
  829. {
  830. _dsaAII.DeleteItem(i);
  831. break;
  832. }
  833. }
  834. }
  835. //-----------------------------------------------------------------------------
  836. // DESCRIPTION: Adjusts the width of the animating item by the animation step.
  837. //
  838. // PARAMETERS: 1. paii - PANIMATIONITEMINFO for the animation item
  839. // 2. cxStep - animation step used to adjust the item's width
  840. //
  841. // RETURN: the new width
  842. //-----------------------------------------------------------------------------
  843. #define ANIM_SLOWSTEPS 3
  844. #define ANIM_SLOWZONE 15
  845. void CTaskBand::_SetAnimationItemWidth(PANIMATIONITEMINFO paii, int cxStep)
  846. {
  847. int iWidth = _GetItemWidth(paii->iIndex);
  848. switch (paii->fState)
  849. {
  850. case ANIMATE_INSERT:
  851. iWidth += cxStep;
  852. break;
  853. case ANIMATE_DELETE:
  854. //slow animation towards end
  855. if (((iWidth / cxStep) <= ANIM_SLOWSTEPS) &&
  856. ((iWidth - cxStep) < ANIM_SLOWZONE - _GetVisibleItemCount()))
  857. {
  858. // The last step takes 3 times as long
  859. cxStep = cxStep / 3;
  860. }
  861. iWidth -= cxStep;
  862. iWidth = max(iWidth, 0);
  863. break;
  864. case ANIMATE_GLOM:
  865. iWidth -= cxStep;
  866. iWidth = max(iWidth, 1); //toolbar sizes 0 width to full size
  867. break;
  868. }
  869. _SetItemWidth(paii->iIndex, iWidth);
  870. }
  871. //-----------------------------------------------------------------------------
  872. // DESCRIPTION: Returns the distance the items must travel to end the
  873. // animation
  874. //
  875. // PARAMETERS: 1. paii - pointer to the ANIMATIONITEMINFO for the item
  876. // 2. iNormalWidth - width of a non-animation item
  877. //
  878. // RETURN: the distance the items must travel to end the animation
  879. //-----------------------------------------------------------------------------
  880. int CTaskBand::_GetAnimationDistLeft(PANIMATIONITEMINFO paii, int iNormalWidth)
  881. {
  882. int cxDistLeft = 0;
  883. int iWidth = _GetItemWidth(paii->iIndex);
  884. switch (paii->fState)
  885. {
  886. case ANIMATE_INSERT:
  887. cxDistLeft = max(0, iNormalWidth - iWidth);
  888. break;
  889. case ANIMATE_DELETE:
  890. if ((paii->iIndex == _GetLastVisibleItem()) && (iNormalWidth == g_cxMinimized))
  891. {
  892. cxDistLeft = 0;
  893. }
  894. else
  895. {
  896. cxDistLeft = max(0, iWidth);
  897. }
  898. break;
  899. case ANIMATE_GLOM:
  900. {
  901. int iGroupIndex = _GetGroupIndex(paii->iIndex);
  902. if (!ToolBar_IsVisible(_tb, iGroupIndex))
  903. {
  904. int cGroupSize = _GetGroupSize(iGroupIndex);
  905. if (cGroupSize)
  906. {
  907. int iGroupWidth = _GetGroupWidth(iGroupIndex);
  908. cxDistLeft = max(0, iGroupWidth - iNormalWidth);
  909. if (iGroupWidth == cGroupSize)
  910. {
  911. cxDistLeft = 0;
  912. }
  913. cxDistLeft = cxDistLeft/cGroupSize;
  914. }
  915. }
  916. }
  917. break;
  918. }
  919. return cxDistLeft;
  920. }
  921. //-----------------------------------------------------------------------------
  922. // DESCRIPTION: Completes tasks to finish an animation
  923. //
  924. // PARAMETERS: 1. paii - pointer to the ANIMATIONITEMINFO for the item
  925. //
  926. // RETURN: the distance the items must travel to end the animation
  927. //-----------------------------------------------------------------------------
  928. void CTaskBand::_FinishAnimation(PANIMATIONITEMINFO paii)
  929. {
  930. switch (paii->fState)
  931. {
  932. case ANIMATE_DELETE:
  933. _DeleteItem(NULL, paii->iIndex);
  934. break;
  935. case ANIMATE_GLOM:
  936. {
  937. int iGroupIndex = _GetGroupIndex(paii->iIndex);
  938. if (!ToolBar_IsVisible(_tb, iGroupIndex))
  939. {
  940. // Turn off glomming flag
  941. _GetItem(iGroupIndex)->dwFlags &= ~TIF_ISGLOMMING;
  942. _HideGroup(iGroupIndex, TRUE);
  943. }
  944. // NOTE: HWND_TOPMOST is used to indicate that the deleted button
  945. // is being animated. This allows the button to stay around after
  946. // its real hwnd becomes invalid
  947. if (paii->pti->hwnd == HWND_TOPMOST)
  948. {
  949. // The button was deleting before it was glommed
  950. // Now that the glomming is done, delete it.
  951. _DeleteItem(NULL, paii->iIndex);
  952. }
  953. }
  954. break;
  955. }
  956. }
  957. //-----------------------------------------------------------------------------
  958. // DESCRIPTION: Returns the width of all the animating buttons
  959. //
  960. // RETURN: The total animation width
  961. //-----------------------------------------------------------------------------
  962. int CTaskBand::_GetAnimationWidth()
  963. {
  964. int iTotAnimationWidth = 0;
  965. _UpdateAnimationIndices();
  966. for (int i = _dsaAII.GetItemCount() - 1; i >= 0; i--)
  967. {
  968. PANIMATIONITEMINFO paii = _dsaAII.GetItemPtr(i);
  969. iTotAnimationWidth += _GetItemWidth(paii->iIndex);
  970. }
  971. return iTotAnimationWidth;
  972. }
  973. //-----------------------------------------------------------------------------
  974. // DESCRIPTION: Synchronizes the indexes held by the animating items to the
  975. // true toolbar indexes.
  976. // Note: This function may cause the number of animating items to
  977. // change.
  978. //-----------------------------------------------------------------------------
  979. void CTaskBand::_UpdateAnimationIndices()
  980. {
  981. int cAnimatingItems = _dsaAII.GetItemCount();
  982. if (cAnimatingItems)
  983. {
  984. // NOTE: items in the animation list are in the same order as the
  985. // toolbar
  986. int iCurrAnimationItem = cAnimatingItems - 1;
  987. PANIMATIONITEMINFO paii = _dsaAII.GetItemPtr(iCurrAnimationItem);
  988. for (int i = _tb.GetButtonCount() - 1; i >=0 ; i--)
  989. {
  990. if (_GetItem(i) == paii->pti)
  991. {
  992. paii->iIndex = i;
  993. iCurrAnimationItem--;
  994. if (iCurrAnimationItem < 0)
  995. {
  996. break;
  997. }
  998. paii = _dsaAII.GetItemPtr(iCurrAnimationItem);
  999. }
  1000. }
  1001. // If animation items are not in the same order as the items in the
  1002. // toolbar then iCurrAnimationItem not be -1
  1003. //ASSERT(iCurrAnimationItem == -1);
  1004. if (iCurrAnimationItem != -1)
  1005. {
  1006. _UpdateAnimationIndicesSlow();
  1007. }
  1008. }
  1009. }
  1010. void CTaskBand::_UpdateAnimationIndicesSlow()
  1011. {
  1012. #ifdef DEBUG
  1013. int cAnimatingItems = _dsaAII.GetItemCount();
  1014. TraceMsg(TF_WARNING, "CTaskBand::_UpdateAnimationIndicesSlow: enter");
  1015. #endif
  1016. for (int i = _dsaAII.GetItemCount() - 1; i >= 0; i--)
  1017. {
  1018. PANIMATIONITEMINFO paii = _dsaAII.GetItemPtr(i);
  1019. int iIndex = _FindItem(paii->pti);
  1020. if (iIndex == -1)
  1021. {
  1022. _dsaAII.DeleteItem(i);
  1023. }
  1024. else
  1025. {
  1026. paii->iIndex = i;
  1027. }
  1028. }
  1029. #ifdef DEBUG
  1030. // Being in this function means that either an animating item is no longer in the
  1031. // toolbar, or that the animating items are in a different order than the toolbar.
  1032. // If the animating items are only in a different order (bad), the number of animating
  1033. // items will remain the same.
  1034. if (cAnimatingItems == _dsaAII.GetItemCount())
  1035. {
  1036. TraceMsg(TF_WARNING, "CTaskBand::_UpdateAnimationIndicesSlow: Animating items are in diff order than toolbar");
  1037. }
  1038. #endif
  1039. }
  1040. int CTaskBand::_FindItem(PTASKITEM pti)
  1041. {
  1042. int iIndex = -1;
  1043. if (pti)
  1044. {
  1045. for (int i = _tb.GetButtonCount() - 1; i >= 0; i--)
  1046. {
  1047. if (pti == _GetItem(i))
  1048. {
  1049. iIndex = i;
  1050. break;
  1051. }
  1052. }
  1053. }
  1054. return iIndex;
  1055. }
  1056. //-----------------------------------------------------------------------------
  1057. // DESCRIPTION: Animation Step Constants
  1058. //-----------------------------------------------------------------------------
  1059. #define ANIM_STEPFACTOR 9
  1060. #define ANIM_STEPMAX 40 // max size of an animation step
  1061. #define ANIM_STEPMIN 11 // min size of an animation step
  1062. //-----------------------------------------------------------------------------
  1063. // DESCRIPTION: Determines an animation step based on the number of items
  1064. // visible in the toolbar.
  1065. //
  1066. // PARAMETERS: 1. iTotalItems - number of visible items in toolbar
  1067. //
  1068. // RETURN: The animation step
  1069. //-----------------------------------------------------------------------------
  1070. int CTaskBand::_GetAnimationStep()
  1071. {
  1072. DWORD dwStep;
  1073. int iVisibleItems = _GetVisibleItemCount();
  1074. int iRows;
  1075. _GetNumberOfRowsCols(&iRows, NULL, TRUE); // _GetNumberOfRows will never return < 1
  1076. int iTotalItems = iVisibleItems - _dsaAII.GetItemCount();
  1077. // The step must be large when there are many items, but can be very small
  1078. // when there are few items. This is achieved by cubing the total items.
  1079. dwStep = (DWORD)(iTotalItems * iTotalItems * iTotalItems) / ANIM_STEPFACTOR;
  1080. dwStep = min(dwStep, ANIM_STEPMAX);
  1081. dwStep = max(dwStep, ANIM_STEPMIN);
  1082. return dwStep;
  1083. }
  1084. //-----------------------------------------------------------------------------
  1085. // DESCRIPTION: Animation Sleep Constants
  1086. //-----------------------------------------------------------------------------
  1087. #define ANIM_PAUSE 1000
  1088. #define ANIM_MAXPAUSE 30
  1089. //-----------------------------------------------------------------------------
  1090. // DESCRIPTION: Returns the amount of time to sleep
  1091. //
  1092. // PARAMETERS: 1. iStep - current animation step
  1093. // 2. cSteps - total animation steps
  1094. // 3. iStepSize - step size for the animation
  1095. //
  1096. // RETURN: time to sleep
  1097. //-----------------------------------------------------------------------------
  1098. DWORD CTaskBand::_GetStepTime(int cx)
  1099. {
  1100. // NOTE: The cx is decrementing to ZERO.
  1101. // As the cx gets smaller we want to
  1102. // increment the sleep time.
  1103. // don't let cx be zero
  1104. cx = max(1, cx);
  1105. cx = min(32767, cx);
  1106. // x^2 curve gives a larger pause at the end.
  1107. int iDenominator = cx * cx;
  1108. return min(ANIM_MAXPAUSE, ANIM_PAUSE / iDenominator);
  1109. }
  1110. //*****************************************************************************
  1111. // END OF ANIMATION FUNCTIONS
  1112. //*****************************************************************************
  1113. void CTaskBand::_SetItemWidth(int iItem, int iWidth)
  1114. {
  1115. TBBUTTONINFO tbbi;
  1116. tbbi.cbSize = sizeof(tbbi);
  1117. tbbi.dwMask = TBIF_SIZE | TBIF_BYINDEX;
  1118. tbbi.cx = (WORD)iWidth;
  1119. _tb.SetButtonInfo(iItem, &tbbi);
  1120. }
  1121. int CTaskBand::_GetItemWidth(int iItem)
  1122. {
  1123. TBBUTTONINFO tbbi;
  1124. tbbi.cbSize = sizeof(tbbi);
  1125. tbbi.dwMask = TBIF_SIZE | TBIF_BYINDEX;
  1126. _tb.GetButtonInfo(iItem, &tbbi);
  1127. return tbbi.cx;
  1128. }
  1129. //-----------------------------------------------------------------------------
  1130. // DESCRIPTION: Retrives the index of the last visible button on the toolbar
  1131. //
  1132. // RETURN: Index of the last visible item on the toolbar.
  1133. //-----------------------------------------------------------------------------
  1134. int CTaskBand::_GetLastVisibleItem()
  1135. {
  1136. int iLastIndex = -1;
  1137. for (int i = _tb.GetButtonCount() - 1; i >=0 ; i--)
  1138. {
  1139. if (ToolBar_IsVisible(_tb, i))
  1140. {
  1141. iLastIndex = i;
  1142. break;
  1143. }
  1144. }
  1145. return iLastIndex;
  1146. }
  1147. //-----------------------------------------------------------------------------
  1148. // DESCRIPTION: Retrives the total width of all buttons in the group
  1149. //
  1150. // PARAMETERS: 1. iIndexGroup - the index of the group
  1151. //
  1152. // RETURN: the total width of all buttons in the group
  1153. //-----------------------------------------------------------------------------
  1154. int CTaskBand::_GetGroupWidth(int iIndexGroup)
  1155. {
  1156. int iGroupWidth = 0;
  1157. int cButtons = _tb.GetButtonCount();
  1158. for (int i = iIndexGroup + 1; i < cButtons; i++)
  1159. {
  1160. PTASKITEM pti = _GetItem(i);
  1161. if (!pti->hwnd)
  1162. {
  1163. break;
  1164. }
  1165. iGroupWidth += _GetItemWidth(i);
  1166. }
  1167. return iGroupWidth;
  1168. }
  1169. //-----------------------------------------------------------------------------
  1170. // DESCRIPTION: Retrives the number of visible buttons on the toolbar
  1171. // RETURN: the number of visible buttons on the toolbar
  1172. //-----------------------------------------------------------------------------
  1173. int CTaskBand::_GetVisibleItemCount()
  1174. {
  1175. int cItems = 0;
  1176. // Count the number of visible buttons before the animated item(s)
  1177. for (int i = _tb.GetButtonCount() - 1; i >=0 ; i--)
  1178. {
  1179. if (ToolBar_IsVisible(_tb, i))
  1180. {
  1181. cItems++;
  1182. }
  1183. }
  1184. return cItems;
  1185. }
  1186. //-----------------------------------------------------------------------------
  1187. // DESCRIPTION: Retrives the ideal width of a non-animating button
  1188. //
  1189. // PARAMETERS: 1. iRemainder[OUT] - width needed for the total item width
  1190. // to equal the window width. (set to zero unless the ideal
  1191. // width is less than the maximum button width.
  1192. //
  1193. // RETURN: the total width of all buttons in the group
  1194. //-----------------------------------------------------------------------------
  1195. int CTaskBand::_GetIdealWidth(int *iRemainder)
  1196. {
  1197. int iIdeal = 0;
  1198. *iRemainder = 0;
  1199. RECT rcWin;
  1200. GetWindowRect(_hwnd, &rcWin);
  1201. int iWinWidth = RECTWIDTH(rcWin);
  1202. int iRows;
  1203. _GetNumberOfRowsCols(&iRows, NULL, TRUE);
  1204. int cItems = _GetVisibleItemCount();
  1205. // button spacing
  1206. TBMETRICS tbm;
  1207. _GetToolbarMetrics(&tbm);
  1208. if (iRows == 1)
  1209. {
  1210. // window width that can be used for non-animating items
  1211. iWinWidth -= (_GetAnimationWidth() + (_dsaAII.GetItemCount() * tbm.cxButtonSpacing));
  1212. iWinWidth = max(0, iWinWidth);
  1213. // find number of non-animating items
  1214. cItems -= _dsaAII.GetItemCount();
  1215. cItems = max(1, cItems);
  1216. }
  1217. // We need to round up so that iCols is the smallest number such that
  1218. // iCols*iRows >= cItems
  1219. int iCols = (cItems + iRows - 1) / iRows;
  1220. iCols = max(1, iCols);
  1221. // calculate the ideal width
  1222. iIdeal = (iWinWidth / iCols);
  1223. if (iCols > 1)
  1224. {
  1225. iIdeal -= tbm.cxButtonSpacing;
  1226. }
  1227. // adjust ideal width
  1228. int iMax = _IsHorizontal() ? g_cxMinimized : iWinWidth;
  1229. int iMin = g_cySize + 2*g_cxEdge;
  1230. if (_IsHorizontal())
  1231. {
  1232. iMin *= 1.8;
  1233. }
  1234. iMin += _GetTextSpace();
  1235. iIdeal = min(iMax, iIdeal);
  1236. // calculate the remainder
  1237. if (_IsHorizontal() && (iIdeal != iMax) && (iRows == 1) && (iIdeal >= iMin))
  1238. {
  1239. *iRemainder = iWinWidth - (iCols * (iIdeal + tbm.cxButtonSpacing));
  1240. *iRemainder = max(0, *iRemainder);
  1241. }
  1242. return iIdeal;
  1243. }
  1244. void CTaskBand::_GetNumberOfRowsCols(int* piRows, int* piCols, BOOL fCurrentSize)
  1245. {
  1246. RECT rcWin;
  1247. RECT rcItem;
  1248. RECT rcTB;
  1249. int iIndexVisible = _GetLastVisibleItem();
  1250. GetWindowRect(_hwnd, &rcWin);
  1251. int cxTB = RECTWIDTH(rcWin);
  1252. int cyTB = RECTHEIGHT(rcWin);
  1253. if (fCurrentSize)
  1254. {
  1255. GetWindowRect(_tb, &rcTB);
  1256. DWORD dwStyle = GetWindowLong(_hwnd, GWL_STYLE);
  1257. if (dwStyle & WS_HSCROLL)
  1258. {
  1259. cyTB = RECTHEIGHT(rcTB);
  1260. }
  1261. else if (dwStyle & WS_VSCROLL)
  1262. {
  1263. cxTB = RECTWIDTH(rcTB);
  1264. }
  1265. }
  1266. _tb.GetItemRect(iIndexVisible, &rcItem);
  1267. TBMETRICS tbm;
  1268. _GetToolbarMetrics(&tbm);
  1269. if (piRows)
  1270. {
  1271. int cyRow = RECTHEIGHT(rcItem) + tbm.cyButtonSpacing;
  1272. *piRows = (cyTB + tbm.cyButtonSpacing) / cyRow;
  1273. *piRows = max(*piRows, 1);
  1274. }
  1275. if (piCols && RECTWIDTH(rcItem))
  1276. {
  1277. int cxCol = RECTWIDTH(rcItem) + tbm.cxButtonSpacing;
  1278. *piCols = (cxTB + tbm.cxButtonSpacing) / cxCol;
  1279. *piCols = max(*piCols, 1);
  1280. }
  1281. }
  1282. //-----------------------------------------------------------------------------
  1283. // DESCRIPTION: Retrives the minimum text width for a button. (used only to
  1284. // determine when task items should be glommed.)
  1285. //
  1286. // RETURN: the minimum text width for a button
  1287. //-----------------------------------------------------------------------------
  1288. int CTaskBand::_GetTextSpace()
  1289. {
  1290. int iTextSpace = 0;
  1291. if (_fGlom && _IsHorizontal() && (_iGroupSize < GLOM_SIZE))
  1292. {
  1293. if (!_iTextSpace)
  1294. {
  1295. HFONT hfont = (HFONT)SendMessage(_tb, WM_GETFONT, 0, 0);
  1296. if (hfont)
  1297. {
  1298. HDC hdc = GetDC(_tb);
  1299. TEXTMETRIC tm;
  1300. GetTextMetrics(hdc, &tm);
  1301. _iTextSpace = tm.tmAveCharWidth * 8;
  1302. ReleaseDC(_tb, hdc);
  1303. }
  1304. }
  1305. iTextSpace = _iTextSpace;
  1306. }
  1307. return iTextSpace;
  1308. }
  1309. //-----------------------------------------------------------------------------
  1310. // DESCRIPTION: Retrieves the toolbar metrics requested by the mask
  1311. //
  1312. // RETURN: toolbar metrics
  1313. //-----------------------------------------------------------------------------
  1314. void CTaskBand::_GetToolbarMetrics(TBMETRICS *ptbm)
  1315. {
  1316. ptbm->cbSize = sizeof(*ptbm);
  1317. ptbm->dwMask = TBMF_PAD | TBMF_BARPAD | TBMF_BUTTONSPACING;
  1318. _tb.SendMessage(TB_GETMETRICS, 0, (LPARAM)ptbm);
  1319. }
  1320. //-----------------------------------------------------------------------------
  1321. // DESCRIPTION: Sizes the non-animating buttons to the taskbar. Shrinks
  1322. // and/or gloms items so that all visible items fit on window.
  1323. //-----------------------------------------------------------------------------
  1324. void CTaskBand::_CheckSize()
  1325. {
  1326. if (_dsaAII)
  1327. {
  1328. int cItems = _GetVisibleItemCount();
  1329. // Check for non-animating buttons to size
  1330. if (cItems > _dsaAII.GetItemCount())
  1331. {
  1332. // Handle grouping by size
  1333. if (_fGlom && (_iGroupSize >= GLOM_SIZE))
  1334. {
  1335. _AutoGlomGroup(TRUE, 0);
  1336. }
  1337. RECT rc;
  1338. GetWindowRect(_hwnd, &rc);
  1339. if (!IsRectEmpty(&rc) && (_tb.GetWindowLong(GWL_STYLE) & WS_VISIBLE))
  1340. {
  1341. int iRemainder = 0;
  1342. int iIdeal = _GetIdealWidth(&iRemainder);
  1343. BOOL fHoriz = _IsHorizontal();
  1344. int iMin = g_cySize + 2*g_cxEdge;
  1345. if (fHoriz)
  1346. {
  1347. iMin *= 1.8;
  1348. }
  1349. iMin += _GetTextSpace();
  1350. iIdeal = max(iIdeal, iMin);
  1351. _SizeItems(iIdeal, iRemainder);
  1352. _tb.SetButtonWidth(iIdeal, iIdeal);
  1353. int iRows;
  1354. int iCols;
  1355. _GetNumberOfRowsCols(&iRows, &iCols, FALSE);
  1356. BOOL fAllowUnGlom = TRUE;
  1357. if (_fGlom && fHoriz && (iIdeal == iMin))
  1358. {
  1359. _AutoGlomGroup(TRUE, 0);
  1360. iMin = (g_cySize + 2*g_cxEdge) * 1.8;
  1361. iIdeal = _GetIdealWidth(&iRemainder);
  1362. iIdeal = max(iIdeal, iMin);
  1363. _SizeItems(iIdeal, iRemainder);
  1364. _tb.SetButtonWidth(iIdeal, iIdeal);
  1365. fAllowUnGlom = FALSE;
  1366. }
  1367. // if we're forced to the minimum size, then we may need some scrollbars
  1368. if ((fHoriz && (iIdeal == iMin)) || (!fHoriz && (cItems > (iRows * iCols))))
  1369. {
  1370. if (!(_fGlom && _AutoGlomGroup(TRUE, 0)))
  1371. {
  1372. TBMETRICS tbm;
  1373. _GetToolbarMetrics(&tbm);
  1374. RECT rcItem;
  1375. _tb.GetItemRect(_GetLastVisibleItem(), &rcItem);
  1376. int cyRow = RECTHEIGHT(rcItem) + tbm.cyButtonSpacing;
  1377. int iColsInner = (cItems + iRows - 1) / iRows;
  1378. _CheckNeedScrollbars(cyRow, cItems, iColsInner, iRows, iIdeal + tbm.cxButtonSpacing, &rc);
  1379. }
  1380. }
  1381. else
  1382. {
  1383. if (_fGlom && fHoriz)
  1384. {
  1385. // iMin is reset above to handle the two-stage minimum. The first minimum size forces glomming, and the second minimum size
  1386. // enforces hard limit for minimum button size.
  1387. // In the case, of unglomming we need unglom base on the first minimum size
  1388. iMin = (g_cySize + 2*g_cxEdge) * 1.8 + _GetTextSpace();
  1389. }
  1390. int cOpenSlots = fHoriz ? ((RECTWIDTH(rc) - _GetAnimationWidth()) -
  1391. (iMin * (cItems - _dsaAII.GetItemCount()))) / iMin : iRows - cItems;
  1392. if (!(_fGlom && (cOpenSlots >= 2) && fAllowUnGlom && _AutoGlomGroup(FALSE, cOpenSlots)))
  1393. {
  1394. _NukeScrollbar(SB_HORZ);
  1395. _NukeScrollbar(SB_VERT);
  1396. _tb.SetWindowPos(0, 0, 0, RECTWIDTH(rc), RECTHEIGHT(rc), SWP_NOACTIVATE | SWP_NOZORDER);
  1397. }
  1398. }
  1399. // force wrap recalc
  1400. _tb.AutoSize();
  1401. }
  1402. else
  1403. {
  1404. _SizeItems(g_cxMinimized);
  1405. _tb.SetButtonWidth(g_cxMinimized, g_cxMinimized);
  1406. }
  1407. }
  1408. }
  1409. }
  1410. //-----------------------------------------------------------------------------
  1411. // DESCRIPTION: Set the sizes of non-animating buttons
  1412. //
  1413. // PARAMETERS: 1. iButtonWidth - width to assign each non-animating item
  1414. // 2. IRemainder - extra width to keep total width constant.
  1415. //
  1416. //-----------------------------------------------------------------------------
  1417. void CTaskBand::_SizeItems(int iButtonWidth, int iRemainder)
  1418. {
  1419. TBBUTTONINFO tbbi;
  1420. tbbi.cbSize = sizeof(tbbi);
  1421. tbbi.dwMask = TBIF_SIZE | TBIF_BYINDEX;
  1422. int iAnimCount = _dsaAII.GetItemCount();
  1423. for (int i = _tb.GetButtonCount() - 1; i >=0 ; i--)
  1424. {
  1425. if (ToolBar_IsVisible(_tb, i))
  1426. {
  1427. BOOL fResize = TRUE;
  1428. if (iAnimCount)
  1429. {
  1430. for (int j = 0; (j < iAnimCount) && fResize; j++)
  1431. {
  1432. PANIMATIONITEMINFO paii = _dsaAII.GetItemPtr(j);
  1433. if (paii->iIndex == i)
  1434. {
  1435. fResize = FALSE;
  1436. }
  1437. }
  1438. }
  1439. if (fResize)
  1440. {
  1441. tbbi.cx = (WORD) iButtonWidth;
  1442. if (iRemainder)
  1443. {
  1444. tbbi.cx++;
  1445. iRemainder--;
  1446. }
  1447. _tb.SetButtonInfo(i, &tbbi);
  1448. }
  1449. }
  1450. }
  1451. }
  1452. //---------------------------------------------------------------------------
  1453. //
  1454. // Track which shortcut launched a particular task.
  1455. // Every so often, we tickle the file's entry in the UEM database
  1456. // to indicate that the program has been running for a long time.
  1457. //
  1458. // These structures are used only by the taskbar thread, hence do
  1459. // not need to be thread-safe.
  1460. //
  1461. class TaskShortcut
  1462. {
  1463. public:
  1464. TaskShortcut(LPCTSTR pszExeName, DWORD pid);
  1465. void AddRef() { _cRef++; }
  1466. void Release() { if (--_cRef == 0) delete this; }
  1467. void Tickle();
  1468. void Promote();
  1469. static BOOL _PromotePidl(LPCITEMIDLIST pidl, BOOL fForce);
  1470. inline BOOL MatchesCachedPid(PTASKITEM pti)
  1471. {
  1472. return _pid == s_pidCache;
  1473. }
  1474. static BOOL MatchesCachedExe(PTASKITEM pti)
  1475. {
  1476. return pti->pszExeName &&
  1477. lstrcmpiW(pti->pszExeName, s_szTargetNameCache) == 0;
  1478. }
  1479. inline BOOL MatchesPid(DWORD pid) const { return pid == _pid; }
  1480. void SetInfoFromCache();
  1481. static BOOL _HandleShortcutInvoke(LPSHShortcutInvokeAsIDList psidl);
  1482. //
  1483. // Note that the session time is now hard-coded to 4 hours and is not
  1484. // affected by the browseui session time.
  1485. //
  1486. enum {
  1487. s_msSession = 4 * 3600 * 1000 // 4 hours - per DCR
  1488. };
  1489. private:
  1490. static DWORD s_pidCache;
  1491. static int s_csidlCache;
  1492. static WCHAR s_szShortcutNameCache[MAX_PATH];
  1493. static WCHAR s_szTargetNameCache[MAX_PATH];
  1494. private:
  1495. ~TaskShortcut() { SHFree(_pszShortcutName); }
  1496. ULONG _cRef; // reference count
  1497. DWORD _pid; // process id
  1498. DWORD _tmTickle; // time of last tickle
  1499. int _csidl; // csidl we are a child of
  1500. LPWSTR _pszShortcutName; // Which shortcut launched us? (NULL = don't know)
  1501. };
  1502. //---------------------------------------------------------------------------
  1503. //
  1504. DWORD TaskShortcut::s_pidCache;
  1505. int TaskShortcut::s_csidlCache;
  1506. WCHAR TaskShortcut::s_szShortcutNameCache[MAX_PATH];
  1507. WCHAR TaskShortcut::s_szTargetNameCache[MAX_PATH];
  1508. TaskShortcut::TaskShortcut(LPCTSTR pszExeName, DWORD pid)
  1509. : _cRef(1), _pid(pid), _tmTickle(GetTickCount()), _pszShortcutName(NULL)
  1510. {
  1511. // If this app was recently launched from a shortcut,
  1512. // save the shortcut name.
  1513. if (s_pidCache == pid &&
  1514. pszExeName &&
  1515. pszExeName[0] &&
  1516. lstrcmpi(pszExeName, s_szTargetNameCache) == 0)
  1517. {
  1518. SetInfoFromCache();
  1519. }
  1520. }
  1521. void TaskShortcut::SetInfoFromCache()
  1522. {
  1523. _csidl = s_csidlCache;
  1524. SHStrDup(s_szShortcutNameCache, &_pszShortcutName);
  1525. }
  1526. //---------------------------------------------------------------------------
  1527. void CTaskBand::_AttachTaskShortcut(PTASKITEM pti, LPCTSTR pszExeName)
  1528. {
  1529. DWORD pid = 0;
  1530. GetWindowThreadProcessId(pti->hwnd, &pid);
  1531. int i;
  1532. for (i = _tb.GetButtonCount() - 1; i >= 0; i--)
  1533. {
  1534. PTASKITEM ptiT = _GetItem(i);
  1535. if (ptiT->ptsh && ptiT->ptsh->MatchesPid(pid))
  1536. {
  1537. pti->ptsh = ptiT->ptsh;
  1538. pti->ptsh->AddRef();
  1539. return;
  1540. }
  1541. }
  1542. // Wow, the first window associated with this pid. Need to create
  1543. // a new entry.
  1544. // Make sure nobody tries to do this in a multithreaded way
  1545. // since we're not protecting the cache with a critical section
  1546. ASSERT(GetCurrentThreadId() == GetWindowThreadProcessId(_hwnd, NULL));
  1547. pti->ptsh = new TaskShortcut(pszExeName, pid);
  1548. }
  1549. //
  1550. // There is a race condition between app startup and our receiving the
  1551. // change notification. If the app starts up first, the
  1552. // _AttachTaskShortcut will fail because we haven't received the change
  1553. // notification yet.
  1554. //
  1555. // _ReattachTaskShortcut looks back through the taskbar and checks if
  1556. // the program for which we received the change notification is already
  1557. // on the taskbar, in which case we update his information retroactively.
  1558. //
  1559. void CTaskBand::_ReattachTaskShortcut()
  1560. {
  1561. // Make sure nobody tries to do this in a multithreaded way
  1562. // since we're not protecting the cache with a critical section
  1563. ASSERT(GetCurrentThreadId() == GetWindowThreadProcessId(_hwnd, NULL));
  1564. int i;
  1565. for (i = _tb.GetButtonCount() - 1; i >= 0; i--)
  1566. {
  1567. PTASKITEM ptiT = _GetItem(i);
  1568. if (ptiT->ptsh && ptiT->ptsh->MatchesCachedPid(ptiT))
  1569. {
  1570. int iIndexGroup = _GetGroupIndex(i);
  1571. PTASKITEM ptiGroup = _GetItem(iIndexGroup);
  1572. if (ptiT->ptsh->MatchesCachedExe(ptiGroup))
  1573. {
  1574. ptiT->ptsh->SetInfoFromCache();
  1575. // Stop after finding the first match, since all apps
  1576. // with the same pid share the same TaskShortcut, so
  1577. // updating one entry fixes them all.
  1578. return;
  1579. }
  1580. }
  1581. }
  1582. }
  1583. //---------------------------------------------------------------------------
  1584. void TaskShortcut::Tickle()
  1585. {
  1586. if (_pszShortcutName)
  1587. {
  1588. DWORD tmNow = GetTickCount();
  1589. if (tmNow - _tmTickle > s_msSession)
  1590. {
  1591. _tmTickle = tmNow;
  1592. // Note that we promote only once, even if multiple tickle intervals
  1593. // have elapsed. That way, if you leave Outlook running while you
  1594. // go on a two-week vacation, then click on Outlook when you get
  1595. // back, we treat this as one usage, not dozens.
  1596. //
  1597. Promote();
  1598. }
  1599. }
  1600. }
  1601. //---------------------------------------------------------------------------
  1602. // Returns whether or not we actually promoted anybody
  1603. BOOL TaskShortcut::_PromotePidl(LPCITEMIDLIST pidl, BOOL fForce)
  1604. {
  1605. BOOL fPromoted = FALSE;
  1606. IShellFolder *psf;
  1607. LPCITEMIDLIST pidlChild;
  1608. if (SUCCEEDED(SHBindToFolderIDListParent(NULL, pidl,
  1609. IID_PPV_ARG(IShellFolder, &psf), &pidlChild)))
  1610. {
  1611. if (!fForce)
  1612. {
  1613. // Don't fire the event if somebody else ran the
  1614. // shortcut within the last session. We want to bump
  1615. // the usage count only once per session even if there
  1616. // are multiple apps running that use the shortcut.
  1617. FILETIME ftSession; // start of current session
  1618. GetSystemTimeAsFileTime(&ftSession);
  1619. DecrementFILETIME(&ftSession, (__int64)10000 * s_msSession);
  1620. UEMINFO uei;
  1621. uei.cbSize = sizeof(uei);
  1622. uei.dwMask = UEIM_FILETIME;
  1623. SetFILETIMEfromInt64(&uei.ftExecute, 0);
  1624. // If this query fails, then uei.ftExecute stays 0
  1625. UEMQueryEvent(&UEMIID_SHELL, UEME_RUNPIDL,
  1626. (WPARAM)psf, (LPARAM)pidlChild, &uei);
  1627. fForce = CompareFileTime(&uei.ftExecute, &ftSession) < 0;
  1628. }
  1629. if (fForce)
  1630. {
  1631. UEMFireEvent(&UEMIID_SHELL, UEME_RUNPIDL, UEMF_XEVENT,
  1632. (WPARAM)psf, (LPARAM)pidlChild);
  1633. fPromoted = TRUE;
  1634. }
  1635. psf->Release();
  1636. }
  1637. return fPromoted;
  1638. }
  1639. //---------------------------------------------------------------------------
  1640. void TaskShortcut::Promote()
  1641. {
  1642. // Use SHSimpleIDListFromPath so we don't spin up drives or
  1643. // hang Explorer if the drive is unavailable
  1644. LPITEMIDLIST pidl = SHSimpleIDListFromPath(_pszShortcutName);
  1645. if (pidl)
  1646. {
  1647. if (_PromotePidl(pidl, FALSE))
  1648. {
  1649. // Now we have to walk back up the tree to the root of our
  1650. // csidl, because that's what the Start Menu does.
  1651. // (Promoting a child entails promoting all his parents.
  1652. // Otherwise you can get into a weird state where a child
  1653. // has been promoted but his ancestors haven't.)
  1654. LPITEMIDLIST pidlParent;
  1655. if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, _csidl, &pidlParent)))
  1656. {
  1657. for (ILRemoveLastID(pidl);
  1658. ILIsParent(pidlParent, pidl, FALSE); ILRemoveLastID(pidl))
  1659. {
  1660. _PromotePidl(pidl, TRUE);
  1661. }
  1662. }
  1663. }
  1664. ILFree(pidl);
  1665. }
  1666. }
  1667. //---------------------------------------------------------------------------
  1668. BOOL _IsChildOfCsidl(int csidl, LPCWSTR pwszPath)
  1669. {
  1670. WCHAR wszCsidl[MAX_PATH];
  1671. // Explicitly check S_OK. S_FALSE means directory doesn't exist,
  1672. // so no point in checking for prefix.
  1673. if (S_OK == SHGetFolderPathW(NULL, csidl, NULL, SHGFP_TYPE_CURRENT, wszCsidl))
  1674. {
  1675. return PathIsPrefixW(wszCsidl, pwszPath);
  1676. }
  1677. return FALSE;
  1678. }
  1679. const int c_rgCsidlShortcutInvoke[] = {
  1680. CSIDL_DESKTOPDIRECTORY,
  1681. CSIDL_PROGRAMS,
  1682. CSIDL_COMMON_DESKTOPDIRECTORY,
  1683. CSIDL_COMMON_PROGRAMS,
  1684. };
  1685. BOOL TaskShortcut::_HandleShortcutInvoke(LPSHShortcutInvokeAsIDList psidl)
  1686. {
  1687. // The shortcut must reside in one of the directories that the Start Page
  1688. // cares about
  1689. int i;
  1690. for (i = 0; i < ARRAYSIZE(c_rgCsidlShortcutInvoke); i++)
  1691. {
  1692. if (_IsChildOfCsidl(c_rgCsidlShortcutInvoke[i], psidl->szShortcutName))
  1693. {
  1694. // Yes it is -- cache it
  1695. s_pidCache = psidl->dwPid;
  1696. s_csidlCache = c_rgCsidlShortcutInvoke[i];
  1697. StringCchCopy(s_szShortcutNameCache, ARRAYSIZE(s_szShortcutNameCache), psidl->szShortcutName);
  1698. StringCchCopy(s_szTargetNameCache, ARRAYSIZE(s_szTargetNameCache), psidl->szTargetName);
  1699. return TRUE;
  1700. }
  1701. }
  1702. return FALSE;
  1703. }
  1704. TASKITEM::TASKITEM(TASKITEM* pti)
  1705. {
  1706. hwnd = pti->hwnd;
  1707. dwFlags = pti->dwFlags;
  1708. ptsh = NULL;
  1709. dwTimeLastClicked = pti->dwTimeLastClicked;
  1710. dwTimeFirstOpened = pti->dwTimeFirstOpened;
  1711. if (pti->pszExeName)
  1712. {
  1713. int cchExeName = lstrlen(pti->pszExeName) + 1;
  1714. pszExeName = new WCHAR[cchExeName];
  1715. if (pszExeName)
  1716. {
  1717. StringCchCopy(pszExeName, cchExeName, pti->pszExeName);
  1718. }
  1719. }
  1720. }
  1721. TASKITEM::~TASKITEM()
  1722. {
  1723. if (ptsh) ptsh->Release();
  1724. if (pszExeName)
  1725. {
  1726. delete [] pszExeName;
  1727. }
  1728. }
  1729. BOOL IsSmallerThanScreen(HWND hwnd)
  1730. {
  1731. HMONITOR hMonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY);
  1732. MONITORINFO mi;
  1733. mi.cbSize = sizeof(mi);
  1734. GetMonitorInfo(hMonitor, &mi);
  1735. WINDOWINFO wi;
  1736. wi.cbSize = sizeof(wi);
  1737. GetWindowInfo(hwnd, &wi);
  1738. int dxMax = mi.rcWork.right - mi.rcWork.left;
  1739. int dyMax = mi.rcWork.bottom - mi.rcWork.top;
  1740. return ((wi.rcWindow.right - wi.rcWindow.left < dxMax) ||
  1741. (wi.rcWindow.bottom - wi.rcWindow.top < dyMax));
  1742. }
  1743. HMENU _GetSystemMenu(HWND hwnd)
  1744. {
  1745. // We have to make a copy of the menu because the documentation for
  1746. // GetSystemMenu blatantly lies, it does not give you a copy of the hmenu
  1747. // and you are not at liberty to alter said menu
  1748. HMENU hmenu = CreatePopupMenu();
  1749. Shell_MergeMenus(hmenu, GetSystemMenu(hwnd, FALSE), 0, 0, 0xffff, 0);
  1750. if (hmenu)
  1751. {
  1752. /* Stolen from Core\ntuser\kernel\mnsys.c xxxSetSysMenu */
  1753. UINT wSize;
  1754. UINT wMinimize;
  1755. UINT wMaximize;
  1756. UINT wMove;
  1757. UINT wRestore;
  1758. UINT wDefault;
  1759. LONG lStyle = GetWindowLong(hwnd, GWL_STYLE);
  1760. /*
  1761. * System modal window: no size, icon, zoom, or move.
  1762. */
  1763. wSize = wMaximize = wMinimize = wMove = 0;
  1764. wRestore = MFS_GRAYED;
  1765. //
  1766. // Default menu command is close.
  1767. //
  1768. wDefault = SC_CLOSE;
  1769. /*
  1770. * Minimized exceptions: no minimize, restore.
  1771. */
  1772. // we need to reverse these because VB has a "special" window
  1773. // that is both minimized but without a minbox.
  1774. if (IsIconic(hwnd))
  1775. {
  1776. wRestore = 0;
  1777. wMinimize = MFS_GRAYED;
  1778. wSize = MFS_GRAYED;
  1779. wDefault = SC_RESTORE;
  1780. }
  1781. else if (!(lStyle & WS_MINIMIZEBOX))
  1782. wMinimize = MFS_GRAYED;
  1783. /*
  1784. * Maximized exceptions: no maximize, restore.
  1785. */
  1786. if (!(lStyle & WS_MAXIMIZEBOX))
  1787. wMaximize = MFS_GRAYED;
  1788. else if (IsZoomed(hwnd)) {
  1789. wRestore = 0;
  1790. /*
  1791. * If the window is maximized but it isn't larger than the
  1792. * screen, we allow the user to move the window around the
  1793. * desktop (but we don't allow resizing).
  1794. */
  1795. wMove = MFS_GRAYED;
  1796. if (!(lStyle & WS_CHILD)) {
  1797. if (IsSmallerThanScreen(hwnd)) {
  1798. wMove = 0;
  1799. }
  1800. }
  1801. wSize = MFS_GRAYED;
  1802. wMaximize = MFS_GRAYED;
  1803. }
  1804. if (!(lStyle & WS_SIZEBOX))
  1805. wSize = MFS_GRAYED;
  1806. /*
  1807. * Are we dealing with a framed dialog box with a sys menu?
  1808. * Dialogs with min/max/size boxes get a regular system menu
  1809. * (as opposed to the dialog menu)
  1810. */
  1811. if (!(lStyle & WS_DLGFRAME) || (lStyle & (WS_SIZEBOX | WS_MINIMIZEBOX | WS_MAXIMIZEBOX))) {
  1812. EnableMenuItem(hmenu, (UINT)SC_SIZE, wSize);
  1813. EnableMenuItem(hmenu, (UINT)SC_MINIMIZE, wMinimize);
  1814. EnableMenuItem(hmenu, (UINT)SC_MAXIMIZE, wMaximize);
  1815. EnableMenuItem(hmenu, (UINT)SC_RESTORE, wRestore);
  1816. }
  1817. EnableMenuItem(hmenu, (UINT)SC_MOVE, wMove);
  1818. SetMenuDefaultItem(hmenu, wDefault, MF_BYCOMMAND);
  1819. }
  1820. return hmenu;
  1821. }
  1822. void CTaskBand::_ExecuteMenuOption(HWND hwnd, int iCmd)
  1823. {
  1824. if (iCmd == SC_SIZE || iCmd == SC_MOVE)
  1825. {
  1826. _FreePopupMenu();
  1827. SwitchToThisWindow(hwnd, TRUE);
  1828. }
  1829. PostMessage(hwnd, WM_SYSCOMMAND, iCmd, 0);
  1830. }
  1831. BOOL _IsWindowNormal(HWND hwnd)
  1832. {
  1833. return (hwnd != v_hwndTray) && (hwnd != v_hwndDesktop) && IsWindow(hwnd);
  1834. }
  1835. void _RestoreWindow(HWND hwnd, DWORD dwFlags)
  1836. {
  1837. HWND hwndTask = hwnd;
  1838. HWND hwndProxy = hwndTask;
  1839. if (g_fDesktopRaised)
  1840. {
  1841. _RaiseDesktop(FALSE);
  1842. }
  1843. // set foreground first so that we'll switch to it.
  1844. if (IsIconic(hwndTask) &&
  1845. (dwFlags & TIF_EVERACTIVEALT))
  1846. {
  1847. HWND hwndProxyT = (HWND) GetWindowLongPtr(hwndTask, 0);
  1848. if (hwndProxyT != NULL && IsWindow(hwndProxyT))
  1849. hwndProxy = hwndProxyT;
  1850. }
  1851. SetForegroundWindow(GetLastActivePopup(hwndProxy));
  1852. if (hwndProxy != hwndTask)
  1853. SendMessage(hwndTask, WM_SYSCOMMAND, SC_RESTORE, -2);
  1854. }
  1855. PTASKITEM CTaskBand::_GetItem(int i, TBBUTTONINFO* ptbb /*= NULL*/, BOOL fByIndex /*= TRUE*/)
  1856. {
  1857. if (i >= 0 && i < _tb.GetButtonCount())
  1858. {
  1859. TBBUTTONINFO tbb;
  1860. if (ptbb == NULL)
  1861. {
  1862. ptbb = &tbb;
  1863. ptbb->dwMask = TBIF_LPARAM;
  1864. }
  1865. else
  1866. {
  1867. ptbb->dwMask = TBIF_COMMAND | TBIF_IMAGE | TBIF_LPARAM |
  1868. TBIF_SIZE | TBIF_STATE | TBIF_STYLE;
  1869. }
  1870. if (fByIndex)
  1871. {
  1872. ptbb->dwMask |= TBIF_BYINDEX;
  1873. }
  1874. ptbb->cbSize = sizeof(*ptbb);
  1875. _tb.GetButtonInfo(i, ptbb);
  1876. ASSERT(ptbb->lParam); // we check for NULL before insertion, so shouldn't be NULL here
  1877. return (PTASKITEM)ptbb->lParam;
  1878. }
  1879. return NULL;
  1880. }
  1881. int CTaskBand::_FindIndexByHwnd(HWND hwnd)
  1882. {
  1883. if (hwnd)
  1884. {
  1885. for (int i = _tb.GetButtonCount() - 1; i >= 0; i--)
  1886. {
  1887. PTASKITEM pti = _GetItem(i);
  1888. if (pti && pti->hwnd == hwnd)
  1889. {
  1890. return i;
  1891. }
  1892. }
  1893. }
  1894. return -1;
  1895. }
  1896. void CTaskBand::_CheckNeedScrollbars(int cyRow, int cItems, int iCols, int iRows,
  1897. int iItemWidth, LPRECT prcView)
  1898. {
  1899. int cxRow = iItemWidth;
  1900. int iVisibleColumns = RECTWIDTH(*prcView) / cxRow;
  1901. int iVisibleRows = RECTHEIGHT(*prcView) / cyRow;
  1902. int x,y, cx,cy;
  1903. RECT rcTabs;
  1904. rcTabs = *prcView;
  1905. iVisibleColumns = max(iVisibleColumns, 1);
  1906. iVisibleRows = max(iVisibleRows, 1);
  1907. SCROLLINFO si;
  1908. si.cbSize = sizeof(si);
  1909. si.fMask = SIF_PAGE | SIF_RANGE;
  1910. si.nMin = 0;
  1911. si.nPage = 0;
  1912. si.nPos = 0;
  1913. if (_IsHorizontal())
  1914. {
  1915. // do vertical scrollbar
  1916. // -1 because it's 0 based.
  1917. si.nMax = (cItems + iVisibleColumns - 1) / iVisibleColumns -1 ;
  1918. si.nPage = iVisibleRows;
  1919. // we're actually going to need the scrollbars
  1920. if (si.nPage <= (UINT)si.nMax)
  1921. {
  1922. // this effects the vis columns and therefore nMax and nPage
  1923. rcTabs.right -= g_cxVScroll;
  1924. iVisibleColumns = RECTWIDTH(rcTabs) / cxRow;
  1925. if (!iVisibleColumns)
  1926. iVisibleColumns = 1;
  1927. si.nMax = (cItems + iVisibleColumns - 1) / iVisibleColumns -1 ;
  1928. }
  1929. SetScrollInfo(_hwnd, SB_VERT, &si, TRUE);
  1930. si.fMask = SIF_POS | SIF_PAGE | SIF_RANGE;
  1931. GetScrollInfo(_hwnd, SB_VERT, &si);
  1932. x = 0;
  1933. y = -si.nPos * cyRow;
  1934. if (iRows == 1)
  1935. {
  1936. cx = RECTWIDTH(rcTabs);
  1937. }
  1938. else
  1939. {
  1940. cx = cxRow * iVisibleColumns;
  1941. }
  1942. // +1 because si.nMax is zero based
  1943. cy = cyRow * (si.nMax +1);
  1944. // nuke the other scroll bar
  1945. _NukeScrollbar(SB_HORZ);
  1946. }
  1947. else
  1948. {
  1949. // do horz scrollbar
  1950. si.nMax = iCols -1;
  1951. si.nPage = iVisibleColumns;
  1952. // we're actually going to need the scrollbars
  1953. if (si.nPage <= (UINT)si.nMax)
  1954. {
  1955. // this effects the vis columns and therefore nMax and nPage
  1956. rcTabs.bottom -= g_cyHScroll;
  1957. iVisibleRows = RECTHEIGHT(rcTabs) / cyRow;
  1958. if (!iVisibleRows)
  1959. iVisibleRows = 1;
  1960. si.nMax = (cItems + iVisibleRows - 1) / iVisibleRows -1 ;
  1961. }
  1962. SetScrollInfo(_hwnd, SB_HORZ, &si, TRUE);
  1963. si.fMask = SIF_POS | SIF_PAGE | SIF_RANGE;
  1964. GetScrollInfo(_hwnd, SB_HORZ, &si);
  1965. y = 0;
  1966. x = -si.nPos * cxRow;
  1967. cx = cxRow * (si.nMax + 1);
  1968. cy = cyRow * iVisibleRows;
  1969. // nuke the other scroll bar
  1970. _NukeScrollbar(SB_VERT);
  1971. }
  1972. _tb.SetWindowPos(0, x,y, cx, cy, SWP_NOACTIVATE| SWP_NOZORDER);
  1973. }
  1974. void CTaskBand::_NukeScrollbar(int fnBar)
  1975. {
  1976. SCROLLINFO si;
  1977. si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
  1978. si.cbSize = sizeof(si);
  1979. si.nMin = 0;
  1980. si.nMax = 0;
  1981. si.nPage = 0;
  1982. si.nPos = 0;
  1983. SetScrollInfo(_hwnd, fnBar, &si, TRUE);
  1984. }
  1985. BOOL CTaskBand::_IsHidden(int i)
  1986. {
  1987. TBBUTTONINFO tbbi;
  1988. tbbi.cbSize = sizeof(tbbi);
  1989. tbbi.dwMask = TBIF_STATE | TBIF_BYINDEX;
  1990. _tb.GetButtonInfo(i, &tbbi);
  1991. if (tbbi.fsState & TBSTATE_HIDDEN)
  1992. {
  1993. return TRUE;
  1994. }
  1995. return FALSE;
  1996. }
  1997. int CTaskBand::_GetGroupIndexFromExeName(WCHAR* pszExeName)
  1998. {
  1999. for (int i = _tb.GetButtonCount() - 1; i >=0; i--)
  2000. {
  2001. PTASKITEM pti = _GetItem(i);
  2002. if ((!pti->hwnd) && (lstrcmpi(pti->pszExeName, pszExeName) == 0))
  2003. {
  2004. return i;
  2005. }
  2006. }
  2007. return -1;
  2008. }
  2009. DWORD CTaskBand::_GetGroupAge(int iIndexGroup)
  2010. {
  2011. int iGroupSize = _GetGroupSize(iIndexGroup);
  2012. DWORD dwTimeLastClicked = _GetItem(iIndexGroup + 1)->dwTimeLastClicked;
  2013. for (int i = iIndexGroup + 2; i <= iIndexGroup + iGroupSize; i++)
  2014. {
  2015. PTASKITEM pti = _GetItem(i);
  2016. if (pti->dwTimeLastClicked > dwTimeLastClicked)
  2017. {
  2018. dwTimeLastClicked = pti->dwTimeLastClicked;
  2019. }
  2020. }
  2021. return dwTimeLastClicked;
  2022. }
  2023. //
  2024. // _GetGroupSize: returns size of group *not including* the group button
  2025. //
  2026. int CTaskBand::_GetGroupSize(int iIndexGroup)
  2027. {
  2028. int iGroupSize = 0;
  2029. PTASKITEM ptiGroup = _GetItem(iIndexGroup);
  2030. if (ptiGroup)
  2031. {
  2032. ASSERT(!ptiGroup->hwnd);
  2033. int cButtons = _tb.GetButtonCount();
  2034. for (int i = iIndexGroup + 1; i < cButtons; i++)
  2035. {
  2036. PTASKITEM pti = _GetItem(i);
  2037. if (!pti->hwnd)
  2038. {
  2039. break;
  2040. }
  2041. iGroupSize++;
  2042. }
  2043. }
  2044. return iGroupSize;
  2045. }
  2046. int CTaskBand::_GetGroupIndex(int iIndexApp)
  2047. {
  2048. int i = iIndexApp;
  2049. while ((i > 0) && (_GetItem(i)->hwnd))
  2050. {
  2051. i--;
  2052. }
  2053. return i;
  2054. }
  2055. void CTaskBand::_UpdateFlashingFlag()
  2056. {
  2057. // Loop through the tab items, see if any have TIF_FLASHING
  2058. // set, and update the flashing flag.
  2059. _fFlashing = FALSE;
  2060. int iCount = _tb.GetButtonCount();
  2061. for (int i = 0; i < iCount; i++)
  2062. {
  2063. PTASKITEM pti = _GetItem(i);
  2064. if (!pti->hwnd)
  2065. {
  2066. pti->dwFlags &= ~(TIF_FLASHING | TIF_RENDERFLASHED);
  2067. }
  2068. else
  2069. {
  2070. int iGroupIndex = _GetGroupIndex(i);
  2071. PTASKITEM ptiGroup = _GetItem(iGroupIndex);
  2072. if (pti->dwFlags & TIF_FLASHING)
  2073. {
  2074. ptiGroup->dwFlags |= TIF_FLASHING;
  2075. _fFlashing = TRUE;
  2076. }
  2077. if (pti->dwFlags & TIF_RENDERFLASHED)
  2078. {
  2079. ptiGroup->dwFlags |= TIF_RENDERFLASHED;
  2080. }
  2081. }
  2082. }
  2083. }
  2084. void CTaskBand::_RealityCheck()
  2085. {
  2086. //
  2087. // Delete any buttons corresponding to non-existent windows.
  2088. //
  2089. for (int i = 0; i < _tb.GetButtonCount(); i++)
  2090. {
  2091. PTASKITEM pti = _GetItem(i);
  2092. // NOTE: HWND_TOPMOST is used to indicate that the deleted button
  2093. // is being animated. This allows the button to stay around after
  2094. // its real hwnd becomes invalid
  2095. if (pti->hwnd && !IsWindow(pti->hwnd) &&
  2096. ((pti->hwnd != HWND_TOPMOST) || !_dsaAII.GetItemCount()))
  2097. {
  2098. #ifdef DEBUG
  2099. PTASKITEM ptiGroup = _GetItem(_GetGroupIndex(i));
  2100. TraceMsg(TF_WARNING, "CTaskBand::_RealityCheck: window %x (%s) no longer valid", pti->hwnd, ptiGroup->pszExeName);
  2101. #endif
  2102. _DeleteItem(pti->hwnd, i);
  2103. }
  2104. }
  2105. }
  2106. class ICONDATA
  2107. {
  2108. public:
  2109. ICONDATA(int i, CTaskBand* p) : iPref(i), ptb(p) { ptb->AddRef(); }
  2110. virtual ~ICONDATA() { ptb->Release(); }
  2111. int iPref;
  2112. CTaskBand* ptb;
  2113. };
  2114. typedef ICONDATA* PICONDATA;
  2115. void CALLBACK CTaskBand::IconAsyncProc(HWND hwnd, UINT uMsg, ULONG_PTR dwData, LRESULT lResult)
  2116. {
  2117. PICONDATA pid = (PICONDATA)dwData;
  2118. if (pid)
  2119. {
  2120. pid->ptb->_SetWindowIcon(hwnd, (HICON)lResult, pid->iPref);
  2121. delete pid;
  2122. }
  2123. }
  2124. int CTaskBand::GetIconCB(CTaskBand* ptb, PICONCBPARAM pip, LPARAM lParam, int iPref)
  2125. {
  2126. int iRet = I_IMAGENONE;
  2127. if (IsWindow(pip->hwnd))
  2128. {
  2129. PICONDATA pid = new ICONDATA(iPref, ptb);
  2130. if (pid)
  2131. {
  2132. if (!SendMessageCallback(pip->hwnd, WM_GETICON, lParam, 0, CTaskBand::IconAsyncProc, (ULONG_PTR)pid))
  2133. {
  2134. delete pid;
  2135. }
  2136. }
  2137. }
  2138. return iRet;
  2139. }
  2140. int CTaskBand::GetSHILIconCB(CTaskBand* ptb, PICONCBPARAM pip, LPARAM lParam, int)
  2141. {
  2142. int iRet = I_IMAGENONE;
  2143. TCHAR szIcon[MAX_PATH];
  2144. DWORD cb = sizeof(szIcon);
  2145. HKEY hkeyApp;
  2146. if (SUCCEEDED(AssocQueryKey(ASSOCF_OPEN_BYEXENAME | ASSOCF_VERIFY, ASSOCKEY_APP, pip->pszExeName, NULL, &hkeyApp)))
  2147. {
  2148. if (ERROR_SUCCESS == SHGetValue(hkeyApp, NULL, TEXT("TaskbarGroupIcon"), NULL, szIcon, &cb))
  2149. {
  2150. int iIcon = PathParseIconLocation(szIcon);
  2151. int iIndex = Shell_GetCachedImageIndex(szIcon, iIcon, 0);
  2152. if (iIndex >= 0)
  2153. {
  2154. iRet = MAKELONG(iIndex, IL_SHIL);
  2155. }
  2156. }
  2157. RegCloseKey(hkeyApp);
  2158. }
  2159. if (iRet == I_IMAGENONE)
  2160. {
  2161. int iIndex = Shell_GetCachedImageIndex(pip->pszExeName, 0, 0);
  2162. if (iIndex >= 0)
  2163. {
  2164. iRet = MAKELONG(iIndex, IL_SHIL);
  2165. }
  2166. }
  2167. return iRet;
  2168. }
  2169. int CTaskBand::GetDefaultIconCB(CTaskBand* ptb, PICONCBPARAM pip, LPARAM, int)
  2170. {
  2171. HICON hicon = LoadIcon(NULL, IDI_WINLOGO);
  2172. return ptb->_AddIconToNormalImageList(hicon, pip->iImage);
  2173. }
  2174. int CTaskBand::GetClassIconCB(CTaskBand* ptb, PICONCBPARAM pip, LPARAM lParam, int)
  2175. {
  2176. if (IsWindow(pip->hwnd))
  2177. {
  2178. HICON hicon = (HICON)GetClassLongPtr(pip->hwnd, (int)lParam);
  2179. return ptb->_AddIconToNormalImageList(hicon, pip->iImage);
  2180. }
  2181. return I_IMAGENONE;
  2182. }
  2183. void CTaskBand::_UpdateItemIcon(int iIndex)
  2184. {
  2185. static const struct
  2186. {
  2187. PICONCALLBACK pfnCB;
  2188. LPARAM lParam;
  2189. }
  2190. c_IconCallbacks[] =
  2191. {
  2192. { CTaskBand::GetIconCB, ICON_SMALL2 },
  2193. { CTaskBand::GetIconCB, ICON_SMALL },
  2194. { CTaskBand::GetIconCB, ICON_BIG },
  2195. { CTaskBand::GetClassIconCB, GCLP_HICONSM },
  2196. { CTaskBand::GetClassIconCB, GCLP_HICON },
  2197. { CTaskBand::GetSHILIconCB, 0, },
  2198. { CTaskBand::GetDefaultIconCB, 0, },
  2199. };
  2200. TBBUTTONINFO tbbi;
  2201. PTASKITEM pti = _GetItem(iIndex, &tbbi);
  2202. if (pti)
  2203. {
  2204. int iIndexGroup = _GetGroupIndex(iIndex);
  2205. PTASKITEM ptiGroup = _GetItem(iIndexGroup);
  2206. if (ptiGroup)
  2207. {
  2208. ICONCBPARAM ip;
  2209. ip.hwnd = pti->hwnd;
  2210. ip.pszExeName = ptiGroup->pszExeName;
  2211. ip.iImage = tbbi.iImage;
  2212. for (int i = 0; i < ARRAYSIZE(c_IconCallbacks); i++)
  2213. {
  2214. int iPref = (ARRAYSIZE(c_IconCallbacks) - i) + 1;
  2215. if (iPref >= pti->iIconPref)
  2216. {
  2217. PTASKITEM ptiTemp = _GetItem(iIndex);
  2218. if (ptiTemp == pti)
  2219. {
  2220. int iImage = c_IconCallbacks[i].pfnCB(this, &ip, c_IconCallbacks[i].lParam, iPref);
  2221. if (iImage != I_IMAGENONE)
  2222. {
  2223. _SetItemImage(iIndex, iImage, iPref);
  2224. break;
  2225. }
  2226. }
  2227. }
  2228. }
  2229. }
  2230. }
  2231. }
  2232. BOOL IsValidHICON(HICON hicon)
  2233. {
  2234. BOOL fIsValid = FALSE;
  2235. if (hicon)
  2236. {
  2237. // Check validity of icon returned
  2238. ICONINFO ii = {0};
  2239. fIsValid = GetIconInfo(hicon, &ii);
  2240. if (ii.hbmMask)
  2241. {
  2242. DeleteObject(ii.hbmMask);
  2243. }
  2244. if (ii.hbmColor)
  2245. {
  2246. DeleteObject(ii.hbmColor);
  2247. }
  2248. }
  2249. return fIsValid;
  2250. }
  2251. void CTaskBand::_MoveGroup(HWND hwnd, WCHAR* szNewExeName)
  2252. {
  2253. BOOL fRedraw = (BOOL)_tb.SendMessage(WM_SETREDRAW, FALSE, 0);
  2254. int iIndexNewGroup = _GetGroupIndexFromExeName(szNewExeName);
  2255. int iIndexOld = _FindIndexByHwnd(hwnd);
  2256. int iIndexOldGroup = _GetGroupIndex(iIndexOld);
  2257. if (iIndexNewGroup != iIndexOldGroup)
  2258. {
  2259. if (iIndexOld >= 0)
  2260. {
  2261. PTASKITEM pti = _GetItem(iIndexOld);
  2262. if (iIndexNewGroup < 0)
  2263. {
  2264. PTASKITEM ptiGroup = new TASKITEM;
  2265. if (ptiGroup)
  2266. {
  2267. ptiGroup->hwnd = NULL;
  2268. ptiGroup->dwTimeLastClicked = 0;
  2269. int cchExeName = lstrlen(szNewExeName) + 1;
  2270. ptiGroup->pszExeName = new WCHAR[cchExeName];
  2271. if (ptiGroup->pszExeName)
  2272. {
  2273. StringCchCopy(ptiGroup->pszExeName, cchExeName, szNewExeName);
  2274. iIndexNewGroup = _AddToTaskbar(ptiGroup, -1, FALSE, FALSE);
  2275. if (iIndexNewGroup < 0)
  2276. {
  2277. delete[] ptiGroup->pszExeName;
  2278. delete ptiGroup;
  2279. }
  2280. else if (iIndexNewGroup <= iIndexOldGroup)
  2281. {
  2282. iIndexOld++;
  2283. iIndexOldGroup++;
  2284. }
  2285. }
  2286. else
  2287. {
  2288. delete ptiGroup;
  2289. }
  2290. }
  2291. }
  2292. if (iIndexNewGroup >= 0)
  2293. {
  2294. int iIndexNew = _AddToTaskbar(pti, iIndexNewGroup + _GetGroupSize(iIndexNewGroup) + 1, _IsHidden(iIndexNewGroup), FALSE);
  2295. if (iIndexNew >= 0)
  2296. {
  2297. _CheckButton(iIndexNew, pti->dwFlags & TIF_CHECKED);
  2298. if (iIndexNew <= iIndexOldGroup)
  2299. {
  2300. iIndexOld++;
  2301. iIndexOldGroup++;
  2302. }
  2303. // Copy the old icon to prevent re-getting the icon
  2304. TBBUTTONINFO tbbiOld;
  2305. _GetItem(iIndexOld, &tbbiOld);
  2306. TBBUTTONINFO tbbiNew;
  2307. _GetItem(iIndexNew, &tbbiNew);
  2308. tbbiNew.iImage = tbbiOld.iImage;
  2309. tbbiNew.dwMask = TBIF_BYINDEX | TBIF_IMAGE;
  2310. _tb.SetButtonInfo(iIndexNew, &tbbiNew);
  2311. tbbiOld.iImage = I_IMAGENONE;
  2312. tbbiOld.dwMask = TBIF_BYINDEX | TBIF_IMAGE;
  2313. _tb.SetButtonInfo(iIndexOld, &tbbiOld);
  2314. _DeleteTaskItem(iIndexOld, FALSE);
  2315. int iSize = _GetGroupSize(iIndexOldGroup);
  2316. if (iSize == 0)
  2317. {
  2318. _DeleteTaskItem(iIndexOldGroup, TRUE);
  2319. }
  2320. else if (iSize == 1)
  2321. {
  2322. _Glom(iIndexOldGroup, FALSE);
  2323. }
  2324. }
  2325. }
  2326. }
  2327. }
  2328. _tb.SetRedraw(fRedraw);
  2329. _CheckSize();
  2330. }
  2331. void CTaskBand::_SetWindowIcon(HWND hwnd, HICON hicon, int iPref)
  2332. {
  2333. int iIndex = _FindIndexByHwnd(hwnd);
  2334. if (iIndex >= 0)
  2335. {
  2336. TBBUTTONINFO tbbi;
  2337. PTASKITEM pti = _GetItem(iIndex, &tbbi);
  2338. if (iPref >= pti->iIconPref && IsValidHICON(hicon))
  2339. {
  2340. int iImage = _AddIconToNormalImageList(hicon, tbbi.iImage);
  2341. if (iImage >= 0)
  2342. {
  2343. _SetItemImage(iIndex, iImage, iPref);
  2344. if (pti->hwnd)
  2345. {
  2346. int iIndexGroup = _GetGroupIndex(iIndex);
  2347. PTASKITEM ptiGroup = _GetItem(iIndexGroup);
  2348. HKEY hkeyApp;
  2349. if (SUCCEEDED(AssocQueryKey(ASSOCF_OPEN_BYEXENAME | ASSOCF_VERIFY, ASSOCKEY_APP, ptiGroup->pszExeName, NULL, &hkeyApp)))
  2350. {
  2351. HKEY hkeyIcons;
  2352. if (ERROR_SUCCESS == RegOpenKeyEx(hkeyApp, TEXT("TaskbarExceptionsIcons"), 0, KEY_READ, &hkeyIcons))
  2353. {
  2354. int iKey = 0;
  2355. WCHAR szKeyName[MAX_PATH];
  2356. DWORD cchKeyName = ARRAYSIZE(szKeyName);
  2357. FILETIME ftBogus;
  2358. while (ERROR_SUCCESS == RegEnumKeyEx(hkeyIcons, iKey, szKeyName, &cchKeyName, NULL, NULL, NULL, &ftBogus))
  2359. {
  2360. HKEY hkeyNewGroup;
  2361. if (ERROR_SUCCESS == RegOpenKeyEx(hkeyIcons, szKeyName, 0, KEY_READ, &hkeyNewGroup))
  2362. {
  2363. WCHAR szIconName[MAX_PATH];
  2364. DWORD cbIconName = sizeof(szIconName);
  2365. if (ERROR_SUCCESS == RegQueryValueEx(hkeyNewGroup, L"IconPath", NULL, NULL, (LPBYTE)szIconName, &cbIconName))
  2366. {
  2367. HICON hiconDll = NULL;
  2368. int iIconIndex = PathParseIconLocation(szIconName);
  2369. ExtractIconEx(szIconName, iIconIndex, NULL, &hiconDll, 1);
  2370. if (hiconDll)
  2371. {
  2372. if (SHAreIconsEqual(hiconDll, hicon))
  2373. {
  2374. WCHAR szNewGroup[MAX_PATH];
  2375. DWORD cbNewGroup = sizeof(szNewGroup);
  2376. if (ERROR_SUCCESS == RegQueryValueEx(hkeyNewGroup, L"NewExeName", NULL, NULL, (LPBYTE)szNewGroup, &cbNewGroup))
  2377. {
  2378. for (int i = _tb.GetButtonCount() - 1; i >=0; i--)
  2379. {
  2380. PTASKITEM pti = _GetItem(i);
  2381. if (!pti->hwnd)
  2382. {
  2383. WCHAR* pszGroupExe = PathFindFileName(pti->pszExeName);
  2384. if (pszGroupExe && (lstrcmpi(pszGroupExe, szNewGroup) == 0))
  2385. {
  2386. DWORD dwType;
  2387. // Make it is an exe and that it exists
  2388. if (GetBinaryType(pti->pszExeName, &dwType))
  2389. {
  2390. _MoveGroup(hwnd, pti->pszExeName);
  2391. }
  2392. break;
  2393. }
  2394. }
  2395. }
  2396. }
  2397. }
  2398. DestroyIcon(hiconDll);
  2399. }
  2400. }
  2401. RegCloseKey(hkeyNewGroup);
  2402. }
  2403. cchKeyName = ARRAYSIZE(szKeyName);
  2404. iKey++;
  2405. }
  2406. RegCloseKey(hkeyIcons);
  2407. }
  2408. RegCloseKey(hkeyApp);
  2409. }
  2410. }
  2411. }
  2412. }
  2413. }
  2414. }
  2415. void CTaskBand::_Glom(int iIndexGroup, BOOL fGlom)
  2416. {
  2417. BOOL fRedraw = (BOOL)_tb.SendMessage(WM_SETREDRAW, FALSE, 0);
  2418. if ((!fGlom) && (iIndexGroup == _iIndexPopup))
  2419. {
  2420. _FreePopupMenu();
  2421. }
  2422. if (fGlom == _IsHidden(iIndexGroup))
  2423. {
  2424. if (_fAnimate && _IsHorizontal())
  2425. {
  2426. int iGroupSize = _GetGroupSize(iIndexGroup);
  2427. if (!fGlom)
  2428. {
  2429. _HideGroup(iIndexGroup, FALSE);
  2430. if (iGroupSize)
  2431. {
  2432. int iWidth = _GetItemWidth(iIndexGroup) / iGroupSize;
  2433. iWidth = max(iWidth, 1);
  2434. for(int i = iIndexGroup + iGroupSize; i > iIndexGroup; i--)
  2435. {
  2436. _SetItemWidth(i, iWidth);
  2437. }
  2438. }
  2439. }
  2440. if (!(fGlom && (_GetItem(iIndexGroup)->dwFlags & TIF_ISGLOMMING)))
  2441. {
  2442. _AnimateItems(iIndexGroup, !fGlom, TRUE);
  2443. }
  2444. }
  2445. else
  2446. {
  2447. _HideGroup(iIndexGroup, fGlom);
  2448. _CheckSize();
  2449. }
  2450. }
  2451. _tb.SetRedraw(fRedraw);
  2452. }
  2453. void CTaskBand::_HideGroup(int iIndexGroup, BOOL fHide)
  2454. {
  2455. int iGroupSize = _GetGroupSize(iIndexGroup);
  2456. TBBUTTONINFO tbbi;
  2457. tbbi.cbSize = sizeof(tbbi);
  2458. tbbi.dwMask = TBIF_STATE | TBIF_BYINDEX;
  2459. // Glom button
  2460. _tb.GetButtonInfo(iIndexGroup, &tbbi);
  2461. tbbi.fsState = fHide ? (tbbi.fsState & ~TBSTATE_HIDDEN) : (tbbi.fsState | TBSTATE_HIDDEN);
  2462. _tb.SetButtonInfo(iIndexGroup, &tbbi);
  2463. // Group buttons
  2464. for (int i = iIndexGroup + iGroupSize; i > iIndexGroup; i--)
  2465. {
  2466. _tb.GetButtonInfo(i, &tbbi);
  2467. tbbi.fsState = fHide ? (tbbi.fsState | TBSTATE_HIDDEN) : (tbbi.fsState & ~TBSTATE_HIDDEN);
  2468. _tb.SetButtonInfo(i, &tbbi);
  2469. }
  2470. }
  2471. BOOL CTaskBand::_AutoGlomGroup(BOOL fGlom, int iOpenSlots)
  2472. {
  2473. int iIndex = -1;
  2474. DWORD dwTimeLastClicked = 0;
  2475. int iSize = 0;
  2476. int i = 0;
  2477. while (i < _tb.GetButtonCount())
  2478. {
  2479. PTASKITEM pti = _GetItem(i);
  2480. int iGroupSize = _GetGroupSize(i);
  2481. // Don't mess with the blank group
  2482. if ((pti->pszExeName && (pti->pszExeName[0] != 0)) &&
  2483. (fGlom || (!fGlom && ((_iGroupSize >= GLOM_SIZE) || (iGroupSize < iOpenSlots)))) &&
  2484. ((iGroupSize > 1) && (fGlom == _IsHidden(i))))
  2485. {
  2486. BOOL fMatch;
  2487. DWORD dwGroupTime = 0;
  2488. switch (_iGroupSize)
  2489. {
  2490. case GLOM_OLDEST:
  2491. dwGroupTime = _GetGroupAge(i);
  2492. fMatch = (dwTimeLastClicked == 0) ||
  2493. (fGlom && (dwGroupTime < dwTimeLastClicked)) ||
  2494. (!fGlom && (dwGroupTime > dwTimeLastClicked));
  2495. break;
  2496. case GLOM_BIGGEST:
  2497. fMatch = (fGlom && (iGroupSize > iSize)) ||
  2498. (!fGlom && ((iGroupSize < iSize) || (iSize == 0)));
  2499. break;
  2500. default:
  2501. fMatch = (fGlom && (iGroupSize >= _iGroupSize)) ||
  2502. (!fGlom && (iGroupSize < _iGroupSize));
  2503. break;
  2504. }
  2505. if (fMatch)
  2506. {
  2507. dwTimeLastClicked = dwGroupTime;
  2508. iSize = iGroupSize;
  2509. iIndex = i;
  2510. }
  2511. }
  2512. i += iGroupSize + 1;
  2513. }
  2514. if ((iIndex != -1) &&
  2515. (fGlom || (!fGlom && (iSize < iOpenSlots))))
  2516. {
  2517. _Glom(iIndex, fGlom);
  2518. return TRUE;
  2519. }
  2520. return FALSE;
  2521. }
  2522. void CTaskBand::_GetItemTitle(int iIndex, WCHAR* pszTitle, int cchTitle, BOOL fCustom)
  2523. {
  2524. PTASKITEM pti = _GetItem(iIndex);
  2525. if (pti->hwnd)
  2526. {
  2527. if (InternalGetWindowText(pti->hwnd, pszTitle, cchTitle))
  2528. {
  2529. if (fCustom)
  2530. {
  2531. WCHAR szGrpText[MAX_PATH] = L" - ";
  2532. int iIndexGroup = _GetGroupIndex(iIndex);
  2533. _GetItemTitle(iIndexGroup, &szGrpText[3], MAX_PATH - 3, TRUE);
  2534. int iLenGrp = lstrlen(szGrpText);
  2535. int iLenWnd = lstrlen(pszTitle);
  2536. if (iLenWnd > iLenGrp)
  2537. {
  2538. if (StrCmp(&pszTitle[iLenWnd - iLenGrp], szGrpText) == 0)
  2539. {
  2540. pszTitle[iLenWnd - iLenGrp] = 0;
  2541. }
  2542. }
  2543. }
  2544. }
  2545. }
  2546. else
  2547. {
  2548. if ((pti->pszExeName) && (pti->pszExeName[0] != 0))
  2549. {
  2550. DWORD cchOut = cchTitle;
  2551. AssocQueryString(ASSOCF_INIT_BYEXENAME | ASSOCF_VERIFY, ASSOCSTR_FRIENDLYAPPNAME, pti->pszExeName, NULL, pszTitle, &cchOut);
  2552. }
  2553. else
  2554. {
  2555. pszTitle[0] = 0;
  2556. }
  2557. }
  2558. }
  2559. int CTaskBand::_AddToTaskbar(PTASKITEM pti, int iIndexTaskbar, BOOL fVisible, BOOL fForceGetIcon)
  2560. {
  2561. ASSERT(IS_VALID_WRITE_PTR(pti, TASKITEM));
  2562. int iIndex = -1;
  2563. TBBUTTON tbb = {0};
  2564. BOOL fRedraw = (BOOL)_tb.SendMessage(WM_SETREDRAW, FALSE, 0);
  2565. if (fForceGetIcon)
  2566. {
  2567. tbb.iBitmap = I_IMAGENONE;
  2568. }
  2569. else
  2570. {
  2571. tbb.iBitmap = I_IMAGECALLBACK;
  2572. }
  2573. tbb.fsState = TBSTATE_ENABLED;
  2574. if (!fVisible)
  2575. tbb.fsState |= TBSTATE_HIDDEN;
  2576. tbb.fsStyle = BTNS_CHECK | BTNS_NOPREFIX;
  2577. if (!pti->hwnd)
  2578. tbb.fsStyle |= BTNS_DROPDOWN | BTNS_WHOLEDROPDOWN;
  2579. tbb.dwData = (DWORD_PTR)pti;
  2580. tbb.idCommand = Toolbar_GetUniqueID(_tb);
  2581. if (_tb.InsertButton(iIndexTaskbar, &tbb))
  2582. {
  2583. iIndex = iIndexTaskbar;
  2584. if (iIndex == -1)
  2585. {
  2586. iIndex = _tb.GetButtonCount() - 1;
  2587. }
  2588. if (fForceGetIcon)
  2589. {
  2590. _UpdateItemIcon(iIndex);
  2591. }
  2592. _UpdateItemText(iIndex);
  2593. }
  2594. _tb.SetRedraw(fRedraw);
  2595. return (iIndex);
  2596. }
  2597. void CTaskBand::_DeleteTaskItem(int iIndex, BOOL fDeletePTI)
  2598. {
  2599. if (iIndex >= 0 && iIndex < _tb.GetButtonCount())
  2600. {
  2601. TBBUTTONINFO tbbi;
  2602. PTASKITEM pti = _GetItem(iIndex, &tbbi);
  2603. _tb.DeleteButton(iIndex);
  2604. _RemoveItemFromAnimationList(pti);
  2605. if (fDeletePTI)
  2606. {
  2607. delete pti;
  2608. }
  2609. _RemoveImage(tbbi.iImage);
  2610. }
  2611. }
  2612. void CTaskBand::_SetThreadPriority(int iPriority, DWORD dwWakeupTime)
  2613. {
  2614. if (_iOldPriority == INVALID_PRIORITY)
  2615. {
  2616. HANDLE hThread = GetCurrentThread();
  2617. int iCurPriority = GetThreadPriority(hThread);
  2618. // Make sure we are actually changed the thread priority
  2619. if (iCurPriority != iPriority)
  2620. {
  2621. _iOldPriority = iCurPriority;
  2622. _iNewPriority = iPriority;
  2623. if (dwWakeupTime)
  2624. {
  2625. // Make sure that we are guaranteed to wakeup, by having the desktop thread up our thread priority
  2626. SendMessage(GetShellWindow(), CWM_TASKBARWAKEUP, GetCurrentThreadId(), MAKELONG(dwWakeupTime, _iOldPriority));
  2627. }
  2628. SetThreadPriority(hThread, _iNewPriority);
  2629. TraceMsg(TF_WARNING, "CTaskBand:: Thread Priority was changed from %d to %d", _iOldPriority, _iNewPriority);
  2630. }
  2631. }
  2632. }
  2633. void CTaskBand::_RestoreThreadPriority()
  2634. {
  2635. if (_iOldPriority != INVALID_PRIORITY)
  2636. {
  2637. HANDLE hThread = GetCurrentThread();
  2638. int iCurPriority = GetThreadPriority(hThread);
  2639. // Make sure no one has changed our priority since that last time we did
  2640. if (iCurPriority == _iNewPriority)
  2641. {
  2642. SetThreadPriority(hThread, _iOldPriority);
  2643. SendMessage(GetShellWindow(), CWM_TASKBARWAKEUP, 0, 0);
  2644. TraceMsg(TF_WARNING, "CTaskBand:: Thread Priority was restored from %d to %d", _iNewPriority, _iOldPriority);
  2645. }
  2646. _iOldPriority = INVALID_PRIORITY;
  2647. _iNewPriority = INVALID_PRIORITY;
  2648. }
  2649. }
  2650. void CTaskBand::_UpdateProgramCount()
  2651. {
  2652. DWORD dwDisposition;
  2653. HKEY hKey;
  2654. if (ERROR_SUCCESS == RegCreateKeyEx(HKEY_CURRENT_USER, TEXT("SessionInformation"),
  2655. 0, NULL, REG_OPTION_VOLATILE, KEY_SET_VALUE,
  2656. NULL, &hKey, &dwDisposition))
  2657. {
  2658. DWORD dwProgramCount = _ptray->CountOfRunningPrograms();
  2659. RegSetValueEx(hKey, TEXT("ProgramCount"),
  2660. 0, REG_DWORD, reinterpret_cast<LPBYTE>(&dwProgramCount),
  2661. sizeof(dwProgramCount));
  2662. RegCloseKey(hKey);
  2663. }
  2664. }
  2665. int CTaskBand::_InsertItem(HWND hwndTask, PTASKITEM pti, BOOL fForceGetIcon)
  2666. {
  2667. _SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL, 5000);
  2668. BOOL fRestoreThreadPriority = TRUE;
  2669. PTASKITEM ptiGroup = NULL;
  2670. WCHAR szExeName[MAX_PATH];
  2671. int iRet = _FindIndexByHwnd(hwndTask);
  2672. int iIndexGroup = -1;
  2673. if (iRet != -1)
  2674. return iRet;
  2675. SHExeNameFromHWND(hwndTask, szExeName, ARRAYSIZE(szExeName));
  2676. WCHAR* pszNoPath = PathFindFileName(szExeName);
  2677. if (pszNoPath)
  2678. {
  2679. for (int i = 0; i < ARRAYSIZE(g_rgNoGlom); i++)
  2680. {
  2681. if (lstrcmpi(pszNoPath, g_rgNoGlom[i].szExeName) == 0)
  2682. {
  2683. StringCchPrintf(szExeName, ARRAYSIZE(szExeName), L"HWND%x", hwndTask);
  2684. }
  2685. }
  2686. }
  2687. // Initialize Taskbar entry, this entry will go into a group on the taskbar or onto the taskbar
  2688. if (!pti)
  2689. {
  2690. pti = new TASKITEM;
  2691. if (!pti)
  2692. goto Failure;
  2693. pti->hwnd = hwndTask;
  2694. pti->dwTimeFirstOpened = pti->dwTimeLastClicked = GetTickCount();
  2695. }
  2696. _AttachTaskShortcut(pti, szExeName);
  2697. // Find the last taskbar entry with a given Exe Name
  2698. if (_fGlom)
  2699. {
  2700. iIndexGroup = _GetGroupIndexFromExeName(szExeName);
  2701. }
  2702. if (iIndexGroup == -1)
  2703. {
  2704. ptiGroup = new TASKITEM;
  2705. if (!ptiGroup)
  2706. goto Failure;
  2707. ptiGroup->hwnd = NULL;
  2708. ptiGroup->dwTimeLastClicked = 0;
  2709. int cchExeName = lstrlen(szExeName) + 1;
  2710. ptiGroup->pszExeName = new WCHAR[cchExeName];
  2711. if (!ptiGroup->pszExeName)
  2712. goto Failure;
  2713. StringCchCopy(ptiGroup->pszExeName, cchExeName, szExeName);
  2714. iRet = _AddToTaskbar(ptiGroup, -1, FALSE, fForceGetIcon);
  2715. if (iRet == -1)
  2716. goto Failure;
  2717. int iRetLast = iRet;
  2718. iRet = _AddToTaskbar(pti, -1, TRUE, fForceGetIcon);
  2719. if (iRet == -1)
  2720. {
  2721. _DeleteTaskItem(iRetLast, TRUE);
  2722. ptiGroup = NULL;
  2723. }
  2724. }
  2725. else
  2726. {
  2727. iRet = _AddToTaskbar(pti, iIndexGroup + _GetGroupSize(iIndexGroup) + 1, _IsHidden(iIndexGroup), fForceGetIcon);
  2728. }
  2729. // If _AddToTaskbar fails (iRet == -1) don't try to add this item anywhere else
  2730. if ((iIndexGroup == _iIndexPopup) && (iRet != -1))
  2731. {
  2732. _AddItemToDropDown(iRet);
  2733. }
  2734. Failure:
  2735. if (iRet == -1)
  2736. {
  2737. if (ptiGroup)
  2738. {
  2739. delete ptiGroup;
  2740. }
  2741. if (pti)
  2742. {
  2743. delete pti;
  2744. }
  2745. }
  2746. else
  2747. {
  2748. if (_fAnimate && _IsHorizontal() &&
  2749. ToolBar_IsVisible(_tb, iRet) && !c_tray.IsTaskbarFading())
  2750. {
  2751. _SetItemWidth(iRet, 1); // cannot be zero or toolbar will resize it.
  2752. // If this operation is successful then _AsyncAnimateItems will raise thread priority
  2753. // after the animation is complete
  2754. fRestoreThreadPriority = !_AnimateItems(iRet, TRUE, FALSE);
  2755. }
  2756. }
  2757. _UpdateProgramCount();
  2758. _CheckSize();
  2759. if (fRestoreThreadPriority)
  2760. {
  2761. _RestoreThreadPriority();
  2762. }
  2763. return iRet;
  2764. }
  2765. //---------------------------------------------------------------------------
  2766. // Delete an item from the listbox but resize the buttons if needed.
  2767. void CTaskBand::_DeleteItem(HWND hwnd, int iIndex)
  2768. {
  2769. if (iIndex == -1)
  2770. iIndex = _FindIndexByHwnd(hwnd);
  2771. if (iIndex != -1)
  2772. {
  2773. int iIndexGroup = _GetGroupIndex(iIndex);
  2774. int iGroupSize = _GetGroupSize(iIndexGroup) - 1;
  2775. if (iGroupSize == 0)
  2776. {
  2777. _FreePopupMenu();
  2778. _DeleteTaskItem(iIndex, TRUE);
  2779. _DeleteTaskItem(iIndexGroup, TRUE);
  2780. }
  2781. else if ((iGroupSize == 1) || (_fGlom && (_iGroupSize >= GLOM_SIZE) && (iGroupSize < _iGroupSize)))
  2782. {
  2783. _FreePopupMenu();
  2784. _DeleteTaskItem(iIndex, TRUE);
  2785. _Glom(iIndexGroup, FALSE);
  2786. }
  2787. else
  2788. {
  2789. if (iIndexGroup == _iIndexPopup)
  2790. _RemoveItemFromDropDown(iIndex);
  2791. _DeleteTaskItem(iIndex, TRUE);
  2792. }
  2793. _CheckSize();
  2794. // Update the flag that says, "There is an item flashing."
  2795. _UpdateFlashingFlag();
  2796. _UpdateProgramCount();
  2797. }
  2798. }
  2799. //---------------------------------------------------------------------------
  2800. // Adds the given window to the task list.
  2801. // Returns TRUE/FALSE depending on whether the window was actually added.
  2802. // NB No check is made to see if it's already in the list.
  2803. BOOL CTaskBand::_AddWindow(HWND hwnd)
  2804. {
  2805. if (_IsWindowNormal(hwnd))
  2806. {
  2807. return _InsertItem(hwnd);
  2808. }
  2809. return FALSE;
  2810. }
  2811. BOOL CTaskBand::_CheckButton(int iIndex, BOOL fCheck)
  2812. {
  2813. TBBUTTONINFO tbbi;
  2814. tbbi.cbSize = sizeof(tbbi);
  2815. tbbi.dwMask = TBIF_STATE | TBIF_BYINDEX;
  2816. _tb.GetButtonInfo(iIndex, &tbbi);
  2817. if (fCheck)
  2818. tbbi.fsState |= TBSTATE_CHECKED;
  2819. else
  2820. tbbi.fsState &= ~TBSTATE_CHECKED;
  2821. return _tb.SetButtonInfo(iIndex, &tbbi);
  2822. }
  2823. BOOL CTaskBand::_IsButtonChecked(int iIndex)
  2824. {
  2825. TBBUTTONINFO tbbi;
  2826. tbbi.cbSize = sizeof(tbbi);
  2827. tbbi.dwMask = TBIF_STATE | TBIF_BYINDEX;
  2828. _tb.GetButtonInfo(iIndex, &tbbi);
  2829. return BOOLIFY(tbbi.fsState & TBSTATE_CHECKED);
  2830. }
  2831. int CTaskBand::_GetCurSel()
  2832. {
  2833. for (int i = _tb.GetButtonCount() - 1; i >= 0; i--)
  2834. {
  2835. if (_IsButtonChecked(i))
  2836. {
  2837. return i;
  2838. }
  2839. }
  2840. return -1;
  2841. }
  2842. void CTaskBand::_SetCurSel(int iIndex, BOOL fIgnoreCtrlKey)
  2843. {
  2844. // Under certain very rare circumstances someone will call us with an invalid index
  2845. // Case #1: CallbackSM is called with a no longer valid uID with maps to a bogus index
  2846. // Case #2: _SelectWindow creates a new button, but before calling this function another button is removed causing
  2847. // the index of the new button to be invalid
  2848. if (iIndex == -1 || (iIndex >= 0 && iIndex < _tb.GetButtonCount()))
  2849. {
  2850. int iIndexGroup = (iIndex == -1) ? -1 : _GetGroupIndex(iIndex);
  2851. BOOL fControlKey = (GetKeyState(VK_CONTROL) < 0) && (!fIgnoreCtrlKey);
  2852. if (fControlKey)
  2853. {
  2854. if (GetForegroundWindow() != (HWND)_tb)
  2855. {
  2856. _fIgnoreTaskbarActivate = TRUE;
  2857. _tb.SetFocus();
  2858. }
  2859. }
  2860. for (int i = _tb.GetButtonCount() - 1; i >= 0; i--)
  2861. {
  2862. PTASKITEM pti = _GetItem(i);
  2863. if (fControlKey)
  2864. {
  2865. if ((i == iIndex) || (i == iIndexGroup))
  2866. {
  2867. pti->dwFlags = (pti->dwFlags & TIF_CHECKED) ? (pti->dwFlags & (~TIF_CHECKED)) : pti->dwFlags | TIF_CHECKED;
  2868. }
  2869. }
  2870. else
  2871. {
  2872. pti->dwFlags = ((i == iIndex) || (i == iIndexGroup)) ? pti->dwFlags | TIF_CHECKED : (pti->dwFlags & (~TIF_CHECKED));
  2873. }
  2874. _CheckButton(i, pti->dwFlags & TIF_CHECKED);
  2875. }
  2876. }
  2877. }
  2878. //---------------------------------------------------------------------------
  2879. // If the given window is in the task list then it is selected.
  2880. // If it's not in the list then it is added.
  2881. int CTaskBand::_SelectWindow(HWND hwnd)
  2882. {
  2883. int i; // Initialize to zero for the empty case
  2884. int iCurSel;
  2885. // Are there any items?
  2886. // Some item has the focus, is it selected?
  2887. iCurSel = _GetCurSel();
  2888. i = -1;
  2889. // We aren't highlighting the correct task. Find it.
  2890. if (IsWindow(hwnd))
  2891. {
  2892. i = _FindIndexByHwnd(hwnd);
  2893. if ( i == -1 )
  2894. {
  2895. // Didn't find it - better add it now.
  2896. i = _InsertItem(hwnd);
  2897. }
  2898. else if (i == iCurSel)
  2899. {
  2900. return i; // the current one is already selected
  2901. }
  2902. }
  2903. // passing -1 is ok
  2904. _SetCurSel(i, TRUE);
  2905. if (i != -1)
  2906. {
  2907. _ScrollIntoView(i);
  2908. }
  2909. return i;
  2910. }
  2911. //---------------------------------------------------------------------------
  2912. // Set the focus to the given window
  2913. // If fAutomin is set the old task will be re-minimising if it was restored
  2914. // during the last switch_to.
  2915. void CTaskBand::_SwitchToWindow(HWND hwnd)
  2916. {
  2917. // use GetLastActivePopup (if it's a visible window) so we don't change
  2918. // what child had focus all the time
  2919. HWND hwndLastActive = GetLastActivePopup(hwnd);
  2920. if ((hwndLastActive) && (IsWindowVisible(hwndLastActive)))
  2921. hwnd = hwndLastActive;
  2922. int iIndex = _FindIndexByHwnd(hwnd);
  2923. if (iIndex != -1)
  2924. {
  2925. PTASKITEM pti = _GetItem(iIndex);
  2926. if (pti)
  2927. {
  2928. pti->dwTimeLastClicked = GetTickCount();
  2929. }
  2930. }
  2931. SwitchToThisWindow(hwnd, TRUE);
  2932. }
  2933. int CTaskBand::_GetSelectedItems(CDSA<PTASKITEM>* pdsa)
  2934. {
  2935. int cSelected = 0;
  2936. for (int i = _tb.GetButtonCount() - 1; i >= 0; i--)
  2937. {
  2938. TBBUTTONINFO tbbi;
  2939. PTASKITEM pti = _GetItem(i, &tbbi);
  2940. if ((tbbi.fsState & TBSTATE_CHECKED) && !(tbbi.fsState & TBSTATE_HIDDEN))
  2941. {
  2942. if (pti->hwnd)
  2943. {
  2944. cSelected++;
  2945. if (pdsa)
  2946. pdsa->AppendItem(&pti);
  2947. }
  2948. else
  2949. {
  2950. cSelected += _GetGroupItems(i, pdsa);
  2951. }
  2952. }
  2953. }
  2954. return cSelected;
  2955. }
  2956. void CTaskBand::_OnGroupCommand(int iRet, CDSA<PTASKITEM>* pdsa)
  2957. {
  2958. // turn off animations during this
  2959. ANIMATIONINFO ami;
  2960. ami.cbSize = sizeof(ami);
  2961. SystemParametersInfo(SPI_GETANIMATION, sizeof(ami), &ami, FALSE);
  2962. LONG iAnimate = ami.iMinAnimate;
  2963. ami.iMinAnimate = FALSE;
  2964. SystemParametersInfo(SPI_SETANIMATION, sizeof(ami), &ami, FALSE);
  2965. switch (iRet)
  2966. {
  2967. case IDM_CASCADE:
  2968. case IDM_VERTTILE:
  2969. case IDM_HORIZTILE:
  2970. {
  2971. int cbHWND = pdsa->GetItemCount();
  2972. HWND* prgHWND = new HWND[cbHWND];
  2973. if (prgHWND)
  2974. {
  2975. for (int i = 0; i < cbHWND; i++)
  2976. {
  2977. PTASKITEM pti;
  2978. pdsa->GetItem(i, &pti);
  2979. prgHWND[i] = pti->hwnd;
  2980. if (IsIconic(pti->hwnd))
  2981. {
  2982. // this needs to by synchronous with the arrange
  2983. ShowWindow(prgHWND[i], SW_RESTORE);
  2984. }
  2985. BringWindowToTop(pti->hwnd);
  2986. }
  2987. if (iRet == IDM_CASCADE)
  2988. {
  2989. CascadeWindows(GetDesktopWindow(), MDITILE_ZORDER, NULL, cbHWND, prgHWND);
  2990. }
  2991. else
  2992. {
  2993. UINT wHow = (iRet == IDM_VERTTILE ? MDITILE_VERTICAL : MDITILE_HORIZONTAL);
  2994. TileWindows(GetDesktopWindow(), wHow, NULL, cbHWND, prgHWND);
  2995. }
  2996. SetForegroundWindow(prgHWND[cbHWND - 1]);
  2997. delete[] prgHWND;
  2998. }
  2999. }
  3000. break;
  3001. case IDM_CLOSE:
  3002. case IDM_MINIMIZE:
  3003. {
  3004. int idCmd;
  3005. switch (iRet)
  3006. {
  3007. case IDM_MINIMIZE: idCmd = SC_MINIMIZE; break;
  3008. case IDM_CLOSE: idCmd = SC_CLOSE; break;
  3009. }
  3010. for (int i = pdsa->GetItemCount() - 1; i >= 0; i--)
  3011. {
  3012. PTASKITEM pti;
  3013. pdsa->GetItem(i, &pti);
  3014. PostMessage(pti->hwnd, WM_SYSCOMMAND, idCmd, 0L);
  3015. }
  3016. _SetCurSel(-1, TRUE);
  3017. }
  3018. break;
  3019. }
  3020. // restore animations state
  3021. ami.iMinAnimate = iAnimate;
  3022. SystemParametersInfo(SPI_SETANIMATION, sizeof(ami), &ami, FALSE);
  3023. }
  3024. int CTaskBand::_GetGroupItems(int iIndexGroup, CDSA<PTASKITEM>* pdsa)
  3025. {
  3026. int iGroupSize = _GetGroupSize(iIndexGroup);
  3027. if (pdsa)
  3028. {
  3029. for (int i = iIndexGroup + 1; i < iIndexGroup + iGroupSize + 1; i++)
  3030. {
  3031. PTASKITEM ptiTemp = _GetItem(i);
  3032. pdsa->AppendItem(&ptiTemp);
  3033. }
  3034. }
  3035. return iGroupSize;
  3036. }
  3037. void CTaskBand::_SysMenuForItem(int i, int x, int y)
  3038. {
  3039. _iSysMenuCount++;
  3040. CDSA<PTASKITEM> dsa;
  3041. dsa.Create(4);
  3042. PTASKITEM pti = _GetItem(i);
  3043. int cSelectedItems = _GetSelectedItems(&dsa);
  3044. if (((cSelectedItems > 1) && _IsButtonChecked(i)) || !pti->hwnd)
  3045. {
  3046. HMENU hmenu = LoadMenuPopup(MAKEINTRESOURCE(MENU_GROUPCONTEXT));
  3047. if (cSelectedItems <= 1)
  3048. {
  3049. dsa.Destroy();
  3050. dsa.Create(4);
  3051. _GetGroupItems(i, &dsa);
  3052. }
  3053. // OFFICESDI: Is this an office app doing its taskbar fakery
  3054. BOOL fMinimize = FALSE;
  3055. BOOL fOfficeApp = FALSE;
  3056. for (int iIndex = (int)(dsa.GetItemCount()) - 1; iIndex >= 0; iIndex--)
  3057. {
  3058. PTASKITEM pti;
  3059. dsa.GetItem(iIndex, &pti);
  3060. if (pti->dwFlags & TIF_EVERACTIVEALT)
  3061. {
  3062. fOfficeApp = TRUE;
  3063. }
  3064. if (_ShouldMinimize(pti->hwnd))
  3065. fMinimize = TRUE;
  3066. }
  3067. // OFFICESDI: If it is an office app disable pretty much everything
  3068. if (fOfficeApp)
  3069. {
  3070. EnableMenuItem(hmenu, IDM_CLOSE, MF_DISABLED | MF_GRAYED | MF_BYCOMMAND);
  3071. EnableMenuItem(hmenu, IDM_CASCADE, MF_DISABLED | MF_GRAYED | MF_BYCOMMAND);
  3072. EnableMenuItem(hmenu, IDM_HORIZTILE, MF_DISABLED | MF_GRAYED | MF_BYCOMMAND);
  3073. EnableMenuItem(hmenu, IDM_VERTTILE, MF_DISABLED | MF_GRAYED | MF_BYCOMMAND);
  3074. EnableMenuItem(hmenu, IDM_MINIMIZE, MF_DISABLED | MF_GRAYED | MF_BYCOMMAND);
  3075. }
  3076. else if (!fMinimize)
  3077. {
  3078. EnableMenuItem(hmenu, IDM_MINIMIZE, MF_DISABLED | MF_GRAYED | MF_BYCOMMAND);
  3079. }
  3080. CToolTipCtrl ttc = _tb.GetToolTips();
  3081. ttc.Activate(FALSE);
  3082. int iRet = TrackPopupMenuEx(hmenu, TPM_RETURNCMD | TPM_RIGHTBUTTON,
  3083. x, y, _tb, NULL);
  3084. ttc.Activate(TRUE);
  3085. _OnGroupCommand(iRet, &dsa);
  3086. }
  3087. else
  3088. {
  3089. LPARAM lParam = MAKELPARAM(x, y);
  3090. _RestoreWindow(pti->hwnd, pti->dwFlags);
  3091. _SelectWindow(pti->hwnd);
  3092. PostMessage(_hwnd, TBC_POSTEDRCLICK, (WPARAM)pti->hwnd, (LPARAM)lParam);
  3093. }
  3094. dsa.Destroy();
  3095. _iSysMenuCount--;
  3096. }
  3097. void CALLBACK CTaskBand::FakeSystemMenuCB(HWND hwnd, UINT uMsg, ULONG_PTR dwData, LRESULT lres)
  3098. {
  3099. CTaskBand* ptasks = (CTaskBand*)dwData;
  3100. KillTimer(ptasks->_hwnd, IDT_SYSMENU);
  3101. if (uMsg == WM_GETICON)
  3102. {
  3103. SendMessageCallback(hwnd, WM_SYSMENU, 0, ptasks->_dwPos, (SENDASYNCPROC)CTaskBand::FakeSystemMenuCB, (ULONG_PTR)ptasks);
  3104. }
  3105. else
  3106. {
  3107. //
  3108. // Since we fake system menu's sometimes, we can come through here
  3109. // 1 or 2 times per system menu request (once for the real one and
  3110. // once for the fake one). Only decrement it down to 0. Don't go neg.
  3111. //
  3112. if (ptasks->_iSysMenuCount) // Decrement it if any outstanding...
  3113. ptasks->_iSysMenuCount--;
  3114. ptasks->_dwPos = 0; // Indicates that we aren't doing a menu now
  3115. if (ptasks->_iSysMenuCount <= 0)
  3116. {
  3117. CToolTipCtrl ttc = ptasks->_tb.GetToolTips();
  3118. ttc.Activate(TRUE);
  3119. }
  3120. }
  3121. }
  3122. HWND CTaskBand::_CreateFakeWindow(HWND hwndOwner)
  3123. {
  3124. WNDCLASSEX wc;
  3125. if (!GetClassInfoEx(hinstCabinet, TEXT("_ExplorerFakeWindow"), &wc))
  3126. {
  3127. ZeroMemory(&wc, sizeof(wc));
  3128. wc.cbSize = sizeof(wc);
  3129. wc.lpfnWndProc = DefWindowProc;
  3130. wc.hInstance = hinstCabinet;
  3131. wc.lpszClassName = TEXT("_ExplorerFakeWindow");
  3132. RegisterClassEx(&wc);
  3133. }
  3134. return CreateWindow(TEXT("_ExplorerFakeWindow"), NULL, WS_POPUP | WS_SYSMENU,
  3135. 0, 0, 0, 0, hwndOwner, NULL, hinstCabinet, NULL);
  3136. }
  3137. void CTaskBand::_HandleSysMenuTimeout()
  3138. {
  3139. HWND hwndTask = _hwndSysMenu;
  3140. DWORD dwPos = _dwPos;
  3141. HWND hwndFake = NULL;
  3142. KillTimer(_hwnd, IDT_SYSMENU);
  3143. HMENU hPopup = GetSystemMenu(hwndTask, FALSE);
  3144. // This window doesn't have the system menu. Since this window
  3145. // is hung, let's fake one so the user can still close it.
  3146. if (hPopup == NULL)
  3147. {
  3148. if ((hwndFake = _CreateFakeWindow(_hwnd)) != NULL)
  3149. {
  3150. hPopup = GetSystemMenu(hwndFake, FALSE);
  3151. }
  3152. }
  3153. if (hPopup)
  3154. {
  3155. // Disable everything on the popup menu _except_ close
  3156. int cItems = GetMenuItemCount(hPopup);
  3157. BOOL fMinimize = _ShouldMinimize(hwndTask);
  3158. for (int iItem = 0; iItem < cItems; iItem++)
  3159. {
  3160. UINT ID = GetMenuItemID(hPopup, iItem);
  3161. // Leave the minimize item as is. NT allows
  3162. // hung-window minimization.
  3163. if (ID == SC_MINIMIZE && fMinimize)
  3164. {
  3165. continue;
  3166. }
  3167. if (ID != SC_CLOSE)
  3168. {
  3169. EnableMenuItem(hPopup, iItem, MF_BYPOSITION | MF_GRAYED);
  3170. }
  3171. }
  3172. // workaround for user bug, we must be the foreground window
  3173. SetForegroundWindow(_hwnd);
  3174. ::SetFocus(_hwnd);
  3175. if (SC_CLOSE == TrackPopupMenu(hPopup,
  3176. TPM_RIGHTBUTTON | TPM_RETURNCMD,
  3177. GET_X_LPARAM(dwPos), GET_Y_LPARAM(dwPos),
  3178. 0,
  3179. _hwnd,
  3180. NULL))
  3181. {
  3182. EndTask(hwndTask, NULL, NULL);
  3183. }
  3184. }
  3185. // Destroy the fake window
  3186. if (hwndFake != NULL)
  3187. {
  3188. DestroyWindow(hwndFake);
  3189. }
  3190. // Turn back on tooltips
  3191. FakeSystemMenuCB(hwndTask, WM_SYSMENU, (ULONG_PTR)this, 0);
  3192. }
  3193. void CTaskBand::_HandleSysMenu(HWND hwnd)
  3194. {
  3195. //
  3196. // At this point, USER32 just told us that the app is now about to bring
  3197. // up its own system menu. We can therefore put away our fake system
  3198. // menu.
  3199. //
  3200. DefWindowProc(_hwnd, WM_CANCELMODE, 0, 0); // Close menu
  3201. KillTimer(_hwnd, IDT_SYSMENU);
  3202. }
  3203. void CTaskBand::_FakeSystemMenu(HWND hwndTask, DWORD dwPos)
  3204. {
  3205. if (_iSysMenuCount <= 0)
  3206. {
  3207. CToolTipCtrl ttc = _tb.GetToolTips();
  3208. ttc.Activate(FALSE);
  3209. }
  3210. // HACKHACK: sleep to give time to switch to them. (user needs this... )
  3211. Sleep(20);
  3212. DWORD dwTimeout = TIMEOUT_SYSMENU;
  3213. //
  3214. // ** Advanced System Menu functionality **
  3215. //
  3216. // If the app doesn't put up its system menu within a reasonable timeout,
  3217. // then we popup a fake menu for it anyway. Suppport for this is required
  3218. // in USER32 (basically it needs to tell us when to turn off our timeout
  3219. // timer).
  3220. //
  3221. // If the user-double right-clicks on the task bar, they get a really
  3222. // short timeout. If the app is already hung, then they get a really
  3223. // short timeout. Otherwise, they get the relatively long timeout.
  3224. //
  3225. if (_dwPos != 0) // 2nd right-click (on a double-right click)
  3226. dwTimeout = TIMEOUT_SYSMENU_HUNG;
  3227. //
  3228. // We check to see if the app in question is hung, and if so, simulate
  3229. // speed up the timeout process. It will happen soon enough.
  3230. //
  3231. _hwndSysMenu = hwndTask;
  3232. _dwPos = dwPos;
  3233. _iSysMenuCount++;
  3234. PTASKITEM pti = NULL;
  3235. int iIndex = _FindIndexByHwnd(hwndTask);
  3236. if (iIndex != -1)
  3237. {
  3238. pti = _GetItem(iIndex);
  3239. }
  3240. if (IsHungAppWindow(hwndTask) || (pti && pti->fHungApp))
  3241. {
  3242. _HandleSysMenuTimeout();
  3243. }
  3244. else
  3245. {
  3246. SetTimer(_hwnd, IDT_SYSMENU, dwTimeout, NULL);
  3247. if (!SendMessageCallback(hwndTask, WM_GETICON, 0, ICON_SMALL2, (SENDASYNCPROC)FakeSystemMenuCB, (ULONG_PTR)this))
  3248. {
  3249. _HandleSysMenuTimeout();
  3250. }
  3251. }
  3252. }
  3253. BOOL CTaskBand::_ContextMenu(DWORD dwPos)
  3254. {
  3255. int i, x, y;
  3256. if (dwPos != (DWORD)-1)
  3257. {
  3258. x = GET_X_LPARAM(dwPos);
  3259. y = GET_Y_LPARAM(dwPos);
  3260. POINT pt = {x, y};
  3261. _tb.ScreenToClient(&pt);
  3262. i = _tb.HitTest(&pt);
  3263. }
  3264. else
  3265. {
  3266. RECT rc;
  3267. i = _tb.GetHotItem();
  3268. _tb.GetItemRect(i, &rc);
  3269. _tb.ClientToScreen((POINT*)&rc);
  3270. x = rc.left;
  3271. y = rc.top;
  3272. }
  3273. if ((i >= 0) && (i < _tb.GetButtonCount()))
  3274. {
  3275. if (!_IsButtonChecked(i))
  3276. {
  3277. _SetCurSel(i, FALSE);
  3278. }
  3279. _SysMenuForItem(i, x, y);
  3280. }
  3281. return (i >= 0);
  3282. }
  3283. void CTaskBand::_HandleCommand(WORD wCmd, WORD wID, HWND hwnd)
  3284. {
  3285. if (hwnd != _tb)
  3286. {
  3287. switch (wCmd)
  3288. {
  3289. case SC_CLOSE:
  3290. {
  3291. BOOL fForce = (GetKeyState(VK_CONTROL) < 0) ? TRUE : FALSE;
  3292. EndTask(_hwndSysMenu, FALSE , fForce);
  3293. }
  3294. break;
  3295. case SC_MINIMIZE:
  3296. ShowWindow(_hwndSysMenu, SW_FORCEMINIMIZE);
  3297. break;
  3298. }
  3299. }
  3300. else if (wCmd == BN_CLICKED)
  3301. {
  3302. int iIndex = _tb.CommandToIndex(wID);
  3303. if (GetKeyState(VK_CONTROL) < 0)
  3304. {
  3305. _SetCurSel(iIndex, FALSE);
  3306. }
  3307. else
  3308. {
  3309. PTASKITEM pti = _GetItem(iIndex);
  3310. if (pti->hwnd)
  3311. {
  3312. _OnButtonPressed(iIndex, pti, FALSE);
  3313. }
  3314. else
  3315. {
  3316. if (_iIndexPopup == -1)
  3317. {
  3318. _SetCurSel(iIndex, FALSE);
  3319. _HandleDropDown(iIndex);
  3320. }
  3321. }
  3322. }
  3323. }
  3324. }
  3325. BOOL _IsChineseLanguage()
  3326. {
  3327. WORD wLang = GetUserDefaultLangID();
  3328. return (PRIMARYLANGID(wLang) == LANG_CHINESE &&
  3329. ((SUBLANGID(wLang) == SUBLANG_CHINESE_TRADITIONAL) ||
  3330. (SUBLANGID(wLang) == SUBLANG_CHINESE_SIMPLIFIED)));
  3331. }
  3332. void CTaskBand::_DrawNumber(HDC hdc, int iValue, BOOL fCalcRect, LPRECT prc)
  3333. {
  3334. DWORD uiStyle = DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX | DT_CENTER;
  3335. WCHAR szCount[14];
  3336. _itow(iValue, szCount, 10);
  3337. if (fCalcRect)
  3338. {
  3339. StringCchCat(szCount, ARRAYSIZE(szCount), L"0");
  3340. }
  3341. uiStyle |= fCalcRect ? DT_CALCRECT : 0;
  3342. if (_hTheme)
  3343. {
  3344. if (fCalcRect)
  3345. {
  3346. GetThemeTextExtent(_hTheme, hdc, TDP_GROUPCOUNT, 0, szCount, -1, uiStyle, NULL, prc);
  3347. }
  3348. else
  3349. {
  3350. DrawThemeText(_hTheme, hdc, TDP_GROUPCOUNT, 0, szCount, -1, uiStyle, 0, prc);
  3351. }
  3352. }
  3353. else
  3354. {
  3355. HFONT hfont = SelectFont(hdc, _hfontCapBold);
  3356. SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
  3357. SetBkMode(hdc, TRANSPARENT);
  3358. DrawText(hdc, (LPTSTR)szCount, -1, prc, uiStyle);
  3359. SelectFont(hdc, hfont);
  3360. }
  3361. }
  3362. LRESULT CTaskBand::_HandleCustomDraw(LPNMTBCUSTOMDRAW ptbcd, PTASKITEM pti)
  3363. {
  3364. if (!pti)
  3365. {
  3366. pti = (PTASKITEM)ptbcd->nmcd.lItemlParam;
  3367. }
  3368. LRESULT lres = CDRF_DODEFAULT;
  3369. switch (ptbcd->nmcd.dwDrawStage)
  3370. {
  3371. case CDDS_PREPAINT:
  3372. lres = CDRF_NOTIFYITEMDRAW;
  3373. break;
  3374. case CDDS_ITEMPREPAINT:
  3375. {
  3376. if (ptbcd->nmcd.uItemState & CDIS_CHECKED)
  3377. {
  3378. // set bold text, unless on chinese language system (where bold text is illegible)
  3379. if (!_IsChineseLanguage())
  3380. {
  3381. _hfontSave = SelectFont(ptbcd->nmcd.hdc, _hfontCapBold);
  3382. lres |= CDRF_NOTIFYPOSTPAINT | CDRF_NEWFONT;
  3383. }
  3384. }
  3385. if (pti->dwFlags & TIF_RENDERFLASHED)
  3386. {
  3387. if (_hTheme)
  3388. {
  3389. DrawThemeBackground(_hTheme, ptbcd->nmcd.hdc, (ptbcd->nmcd.hdr.hwndFrom == _tb) ? TDP_FLASHBUTTON : TDP_FLASHBUTTONGROUPMENU, 0, &(ptbcd->nmcd.rc), 0);
  3390. lres |= TBCDRF_NOBACKGROUND;
  3391. }
  3392. else
  3393. {
  3394. // set blue background
  3395. ptbcd->clrHighlightHotTrack = GetSysColor(COLOR_HIGHLIGHT);
  3396. ptbcd->clrBtnFace = GetSysColor(COLOR_HIGHLIGHT);
  3397. ptbcd->clrText = GetSysColor(COLOR_HIGHLIGHTTEXT);
  3398. if (!(ptbcd->nmcd.uItemState & CDIS_HOT))
  3399. {
  3400. ptbcd->nmcd.uItemState |= CDIS_HOT;
  3401. lres |= TBCDRF_NOEDGES;
  3402. }
  3403. lres |= TBCDRF_HILITEHOTTRACK;
  3404. }
  3405. }
  3406. if (pti->dwFlags & TIF_TRANSPARENT)
  3407. {
  3408. lres = CDRF_SKIPDEFAULT;
  3409. }
  3410. if (!pti->hwnd)
  3411. {
  3412. lres |= CDRF_NOTIFYPOSTPAINT;
  3413. RECT rc;
  3414. int iIndex = _tb.CommandToIndex((int)ptbcd->nmcd.dwItemSpec);
  3415. _DrawNumber(ptbcd->nmcd.hdc, _GetGroupSize(iIndex), TRUE, &rc);
  3416. ptbcd->iListGap = RECTWIDTH(rc);
  3417. }
  3418. }
  3419. break;
  3420. case CDDS_ITEMPOSTPAINT:
  3421. {
  3422. if (!pti->hwnd)
  3423. {
  3424. int iIndex = _tb.CommandToIndex((int)ptbcd->nmcd.dwItemSpec);
  3425. if (ptbcd->nmcd.rc.right >= ptbcd->rcText.left)
  3426. {
  3427. RECT rc = ptbcd->rcText;
  3428. rc.right = rc.left;
  3429. rc.left -= ptbcd->iListGap;
  3430. _DrawNumber(ptbcd->nmcd.hdc, _GetGroupSize(iIndex), FALSE, &rc);
  3431. }
  3432. }
  3433. if (ptbcd->nmcd.uItemState & CDIS_CHECKED)
  3434. {
  3435. // restore font
  3436. ASSERT(!_IsChineseLanguage());
  3437. SelectFont(ptbcd->nmcd.hdc, _hfontSave);
  3438. }
  3439. }
  3440. break;
  3441. }
  3442. return lres;
  3443. }
  3444. void CTaskBand::_RemoveImage(int iImage)
  3445. {
  3446. if (iImage >= 0 && HIWORD(iImage) == IL_NORMAL)
  3447. {
  3448. CImageList il = CImageList(_tb.GetImageList());
  3449. if (il)
  3450. {
  3451. BOOL fRedraw = (BOOL)_tb.SendMessage(WM_SETREDRAW, FALSE, 0);
  3452. il.Remove(iImage);
  3453. // Removing image bumps all subsequent indices down by 1. Iterate
  3454. // through the buttons and patch up their indices as necessary.
  3455. TBBUTTONINFO tbbi;
  3456. tbbi.cbSize = sizeof(tbbi);
  3457. tbbi.dwMask = TBIF_BYINDEX | TBIF_IMAGE;
  3458. for (int i = _tb.GetButtonCount() - 1; i >= 0; i--)
  3459. {
  3460. _tb.GetButtonInfo(i, &tbbi);
  3461. if (tbbi.iImage > iImage && HIWORD(tbbi.iImage) == IL_NORMAL)
  3462. {
  3463. --tbbi.iImage;
  3464. _tb.SetButtonInfo(i, &tbbi);
  3465. }
  3466. }
  3467. _tb.SetRedraw(fRedraw);
  3468. }
  3469. }
  3470. }
  3471. void CTaskBand::_OnButtonPressed(int iIndex, PTASKITEM pti, BOOL fForceRestore)
  3472. {
  3473. ASSERT(pti);
  3474. if (iIndex == _iIndexActiveAtLDown)
  3475. {
  3476. if (pti->dwFlags & TIF_EVERACTIVEALT)
  3477. {
  3478. PostMessage(pti->hwnd, WM_SYSCOMMAND, SC_RESTORE, -1);
  3479. _SetCurSel(-1, FALSE);
  3480. }
  3481. else if (IsIconic(pti->hwnd) || fForceRestore)
  3482. {
  3483. if (pti->hwnd == GetForegroundWindow())
  3484. {
  3485. ShowWindowAsync(pti->hwnd, SW_RESTORE);
  3486. }
  3487. else
  3488. {
  3489. _SwitchToItem(iIndex, pti->hwnd, TRUE);
  3490. }
  3491. }
  3492. else if (_ShouldMinimize(pti->hwnd))
  3493. {
  3494. SHAllowSetForegroundWindow(pti->hwnd);
  3495. PostMessage(pti->hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0);
  3496. _SetCurSel(-1, FALSE);
  3497. }
  3498. }
  3499. else
  3500. {
  3501. _SwitchToItem(iIndex, pti->hwnd, TRUE);
  3502. }
  3503. }
  3504. void CTaskBand::_GetDispInfo(LPNMTBDISPINFO lptbdi)
  3505. {
  3506. if (lptbdi->dwMask & TBNF_IMAGE)
  3507. {
  3508. int iIndex = _tb.CommandToIndex(lptbdi->idCommand);
  3509. _UpdateItemIcon(iIndex);
  3510. TBBUTTONINFO tbbi;
  3511. tbbi.cbSize = sizeof(tbbi);
  3512. tbbi.dwMask = TBIF_BYINDEX | TBIF_IMAGE;
  3513. _tb.GetButtonInfo(iIndex, &tbbi);
  3514. lptbdi->iImage = tbbi.iImage;
  3515. lptbdi->dwMask |= TBNF_DI_SETITEM;
  3516. }
  3517. }
  3518. LRESULT CTaskBand::_HandleNotify(LPNMHDR lpnm)
  3519. {
  3520. switch (lpnm->code)
  3521. {
  3522. case NM_LDOWN:
  3523. {
  3524. int iIndex = _tb.CommandToIndex(((LPNMTOOLBAR)lpnm)->iItem);
  3525. PTASKITEM pti = _GetItem(iIndex);
  3526. if (pti && pti->hwnd)
  3527. {
  3528. _iIndexActiveAtLDown = _GetCurSel();
  3529. }
  3530. }
  3531. break;
  3532. case NM_KEYDOWN:
  3533. {
  3534. LPNMKEY pnmk = (LPNMKEY)lpnm;
  3535. switch (pnmk->nVKey)
  3536. {
  3537. case VK_SPACE:
  3538. case VK_RETURN:
  3539. // need to toggle checked state, toolbar doesn't do it for us
  3540. {
  3541. int iItem = _tb.GetHotItem();
  3542. if (iItem >= 0)
  3543. {
  3544. TBBUTTONINFO tbbi;
  3545. tbbi.cbSize = sizeof(tbbi);
  3546. tbbi.dwMask = TBIF_BYINDEX | TBIF_STATE;
  3547. _tb.GetButtonInfo(iItem, &tbbi);
  3548. tbbi.fsState ^= TBSTATE_CHECKED;
  3549. _tb.SetButtonInfo(iItem, &tbbi);
  3550. PTASKITEM pti = _GetItem(iItem);
  3551. _OnButtonPressed(iItem, pti, FALSE);
  3552. }
  3553. }
  3554. return TRUE;
  3555. }
  3556. }
  3557. break;
  3558. case TBN_DELETINGBUTTON:
  3559. break;
  3560. case TBN_HOTITEMCHANGE:
  3561. if (_fDenyHotItemChange)
  3562. {
  3563. return 1;
  3564. }
  3565. else
  3566. {
  3567. LPNMTBHOTITEM pnmhot = (LPNMTBHOTITEM)lpnm;
  3568. if (pnmhot->dwFlags & HICF_ARROWKEYS)
  3569. {
  3570. // If this change came from a mouse then the hot item is already in view
  3571. _ScrollIntoView(_tb.CommandToIndex(pnmhot->idNew));
  3572. }
  3573. }
  3574. break;
  3575. case TBN_DROPDOWN:
  3576. {
  3577. int iIndex = _tb.CommandToIndex(((LPNMTOOLBAR)lpnm)->iItem);
  3578. int iCurIndex = _GetCurSel();
  3579. _iIndexActiveAtLDown = iCurIndex;
  3580. if ((iCurIndex == -1) || (_GetGroupIndex(iCurIndex) != iIndex) || (GetKeyState(VK_CONTROL) < 0))
  3581. {
  3582. _SetCurSel(iIndex, FALSE);
  3583. }
  3584. if (!(GetKeyState(VK_CONTROL) < 0))
  3585. {
  3586. _SetCurSel(iIndex, FALSE);
  3587. _HandleDropDown(iIndex);
  3588. }
  3589. }
  3590. break;
  3591. case TBN_GETDISPINFO:
  3592. {
  3593. LPNMTBDISPINFO lptbdi = (LPNMTBDISPINFO)lpnm;
  3594. _GetDispInfo(lptbdi);
  3595. }
  3596. break;
  3597. case NM_CUSTOMDRAW:
  3598. return _HandleCustomDraw((LPNMTBCUSTOMDRAW)lpnm);
  3599. case TTN_NEEDTEXT:
  3600. {
  3601. int iIndex = _tb.CommandToIndex((int)lpnm->idFrom);
  3602. LPTOOLTIPTEXT pttt = (LPTOOLTIPTEXT)lpnm;
  3603. int cchLen = 0;
  3604. PTASKITEM pti = _GetItem(iIndex);
  3605. if (pti && !pti->hwnd)
  3606. {
  3607. StringCchPrintf(pttt->szText, ARRAYSIZE(pttt->szText), L"(%d) ", _GetGroupSize(iIndex));
  3608. cchLen = lstrlen(pttt->szText);
  3609. }
  3610. _GetItemTitle(iIndex, &(pttt->szText[cchLen]), ARRAYSIZE(pttt->szText) - cchLen, FALSE);
  3611. }
  3612. break;
  3613. case NM_THEMECHANGED:
  3614. {
  3615. _VerifyButtonHeight();
  3616. }
  3617. break;
  3618. }
  3619. return 0;
  3620. }
  3621. void CTaskBand::_SwitchToItem(int iItem, HWND hwnd, BOOL fIgnoreCtrlKey)
  3622. {
  3623. if (_IsWindowNormal(hwnd))
  3624. {
  3625. _RaiseDesktop(FALSE);
  3626. if (_pmpPopup)
  3627. _pmpPopup->OnSelect(MPOS_FULLCANCEL);
  3628. _SetCurSel(iItem, fIgnoreCtrlKey);
  3629. if (!(GetKeyState(VK_CONTROL) < 0) || fIgnoreCtrlKey)
  3630. {
  3631. _SwitchToWindow(hwnd);
  3632. }
  3633. }
  3634. else if (!hwnd)
  3635. {
  3636. // I know what you are thinking, why would we ever get a NM_CLICK message for a dropdown button.
  3637. // Ok, sit back and enjoy
  3638. // 1) Click on a group button
  3639. // 2) All window messages are funnelled through the menuband currently being used for the group menu
  3640. // 3) User clicks on another group button
  3641. // 4) The WM_LBUTTONDOWN message is captured and eaten by menuband, then menuband dismisses itself causing a TBC_FREEPOPUPMENU
  3642. // 5) Then the toolbar button for the other group button gets an WM_LBUTTONUP message
  3643. // 6) Guess what, dropdown button notifications are sent during WM_LBUTTONDOWN not UP
  3644. // 7) Thus we don't get an TBN_DROPDOWN we get an NM_CLICK
  3645. // 8) We need to make sure the user didn't click on the same group button as before
  3646. // 9) However, the previous group menu has been dismissed, so I create _iIndexLastPopup which persists after a group menu is dismissed
  3647. if (iItem != _iIndexLastPopup)
  3648. {
  3649. _SetCurSel(iItem, fIgnoreCtrlKey);
  3650. if (!(GetKeyState(VK_CONTROL) < 0) || fIgnoreCtrlKey)
  3651. {
  3652. _HandleDropDown(iItem);
  3653. }
  3654. }
  3655. }
  3656. // NOTE: HWND_TOPMOST is used to indicate that the deleted button
  3657. // is being animated. This allows the button to stay around after
  3658. // its real hwnd becomes invalid
  3659. else if (hwnd != HWND_TOPMOST)
  3660. {
  3661. // Window went away?
  3662. _DeleteItem(hwnd);
  3663. _SetCurSel(-1, fIgnoreCtrlKey);
  3664. }
  3665. }
  3666. BOOL WINAPI CTaskBand::BuildEnumProc(HWND hwnd, LPARAM lParam)
  3667. {
  3668. CTaskBand* ptasks = (CTaskBand*)lParam;
  3669. if (IsWindow(hwnd) && IsWindowVisible(hwnd) && !::GetWindow(hwnd, GW_OWNER) &&
  3670. (!(GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_TOOLWINDOW)))
  3671. {
  3672. ptasks->_AddWindow(hwnd);
  3673. }
  3674. return TRUE;
  3675. }
  3676. //---------------------------------------------------------------------------
  3677. // Work around a toolbar bug where it goes wacko if you press both mouse
  3678. // buttons. The reason is that the second mouse button doing down tries
  3679. // to reassert capture. This causes the toolbar to receive WM_CAPTURECHANGED
  3680. // with its own hwnd as lParam. Toolbar doesn't realize that it's being told
  3681. // that it is stealing capture from itself and thinks somebody else is
  3682. // trying to steal capture, so it posts a message to itself to clean up.
  3683. // The posted message arrives, and toolbar cleans up the capture, thinking
  3684. // it's cleaning up the old capture that it lost, but in fact it's cleaning
  3685. // up the NEW capture it just finished setting!
  3686. //
  3687. // So filter out WM_CAPTURECHANGED messages that are effectively NOPs.
  3688. //
  3689. LRESULT CALLBACK s_FilterCaptureSubclassProc(
  3690. HWND hwnd,
  3691. UINT uMsg,
  3692. WPARAM wParam,
  3693. LPARAM lParam,
  3694. UINT_PTR uIdSubclass,
  3695. DWORD_PTR dwRefData)
  3696. {
  3697. switch (uMsg)
  3698. {
  3699. case WM_CAPTURECHANGED:
  3700. if (hwnd == (HWND)lParam)
  3701. {
  3702. // Don't let toolbar be fooled into cleaning up capture
  3703. // when it shouldn't.
  3704. return 0;
  3705. }
  3706. break;
  3707. case WM_NCDESTROY:
  3708. RemoveWindowSubclass(hwnd, s_FilterCaptureSubclassProc, uIdSubclass);
  3709. break;
  3710. }
  3711. return DefSubclassProc(hwnd, uMsg, wParam, lParam);
  3712. }
  3713. //---------------------------------------------------------------------------
  3714. LRESULT CTaskBand::_HandleCreate()
  3715. {
  3716. ASSERT(_hwnd);
  3717. _uCDHardError = RegisterWindowMessage( TEXT(COPYDATA_HARDERROR) );
  3718. RegisterDragDrop(_hwnd, this);
  3719. _tb.Create(_hwnd, NULL, NULL, WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE | CCS_NODIVIDER |
  3720. TBSTYLE_LIST | TBSTYLE_TOOLTIPS | TBSTYLE_WRAPABLE | CCS_NORESIZE | TBSTYLE_TRANSPARENT);
  3721. if (_tb)
  3722. {
  3723. SendMessage(_tb, TB_ADDSTRING, (WPARAM)hinstCabinet, (LPARAM)IDS_BOGUSLABELS);
  3724. _OpenTheme();
  3725. SendMessage(_tb, TB_SETWINDOWTHEME, 0, (LPARAM)(_IsHorizontal() ? c_wzTaskBandTheme : c_wzTaskBandThemeVert));
  3726. SetWindowSubclass(_tb, s_FilterCaptureSubclassProc, 0, 0);
  3727. _tb.SetButtonStructSize();
  3728. // initial size
  3729. SIZE size = {0, 0};
  3730. _tb.SetButtonSize(size);
  3731. _tb.SetExtendedStyle( TBSTYLE_EX_TRANSPARENTDEADAREA |
  3732. TBSTYLE_EX_FIXEDDROPDOWN |
  3733. TBSTYLE_EX_DOUBLEBUFFER |
  3734. TBSTYLE_EX_TOOLTIPSEXCLUDETOOLBAR);
  3735. // version info
  3736. _tb.SendMessage(CCM_SETVERSION, COMCTL32_VERSION, 0);
  3737. _CreateTBImageLists();
  3738. HWND hwndTT = _tb.GetToolTips();
  3739. if (hwndTT)
  3740. {
  3741. SHSetWindowBits(hwndTT, GWL_STYLE, TTS_ALWAYSTIP | TTS_NOPREFIX,
  3742. TTS_ALWAYSTIP | TTS_NOPREFIX);
  3743. }
  3744. // set shell hook
  3745. WM_ShellHook = RegisterWindowMessage(TEXT("SHELLHOOK"));
  3746. RegisterShellHook(_hwnd, 3); // 3 = magic flag
  3747. // force getting of font, calc of metrics
  3748. _HandleWinIniChange(0, 0, TRUE);
  3749. // populate the toolbar
  3750. EnumWindows(BuildEnumProc, (LPARAM)this);
  3751. SHChangeNotifyEntry fsne;
  3752. fsne.fRecursive = FALSE;
  3753. fsne.pidl = NULL;
  3754. _uShortcutInvokeNotify = SHChangeNotifyRegister(_hwnd,
  3755. SHCNRF_NewDelivery | SHCNRF_ShellLevel,
  3756. SHCNE_ASSOCCHANGED |
  3757. SHCNE_EXTENDED_EVENT | SHCNE_UPDATEIMAGE,
  3758. TBC_CHANGENOTIFY,
  3759. 1, &fsne);
  3760. // set window text to give accessibility apps something to read
  3761. TCHAR szTitle[80];
  3762. LoadString(hinstCabinet, IDS_TASKBANDTITLE, szTitle, ARRAYSIZE(szTitle));
  3763. SetWindowText(_hwnd, szTitle);
  3764. SetWindowText(_tb, szTitle);
  3765. return 0; // success
  3766. }
  3767. // Failure.
  3768. return -1;
  3769. }
  3770. void CTaskBand::_FreePopupMenu()
  3771. {
  3772. _iIndexPopup = -1;
  3773. ATOMICRELEASE(_psmPopup);
  3774. if (_pmpPopup)
  3775. {
  3776. IUnknown_SetSite(_pmpPopup, NULL);
  3777. _pmpPopup->OnSelect(MPOS_FULLCANCEL);
  3778. }
  3779. ATOMICRELEASE(_pmpPopup);
  3780. ATOMICRELEASE(_pmbPopup);
  3781. SendMessage(v_hwndTray, TM_SETPUMPHOOK, NULL, NULL);
  3782. _menuPopup.Detach();
  3783. }
  3784. HRESULT CTaskBand::_CreatePopupMenu(POINTL* ppt, RECTL* prcl)
  3785. {
  3786. HRESULT hr = E_FAIL;
  3787. CToolTipCtrl ttc = _tb.GetToolTips();
  3788. ttc.Activate(FALSE);
  3789. SetActiveWindow(v_hwndTray);
  3790. CTaskBandSMC* ptbc = new CTaskBandSMC(this);
  3791. if (ptbc)
  3792. {
  3793. if (SUCCEEDED(CoCreateInstance(CLSID_MenuBand, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellMenu2, &_psmPopup))) &&
  3794. SUCCEEDED(_psmPopup->Initialize(ptbc, 0, 0, SMINIT_CUSTOMDRAW | SMINIT_VERTICAL | SMINIT_TOPLEVEL | SMINIT_USEMESSAGEFILTER)) &&
  3795. SUCCEEDED(CoCreateInstance(CLSID_MenuDeskBar, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IMenuPopup, &_pmpPopup))) &&
  3796. SUCCEEDED(_psmPopup->SetMenu(_menuPopup, _hwnd, SMSET_USEPAGER | SMSET_NOPREFIX)) &&
  3797. SUCCEEDED(_psmPopup->QueryInterface(IID_PPV_ARG(IMenuBand, &_pmbPopup))))
  3798. {
  3799. _psmPopup->SetMinWidth(RECTWIDTH(*prcl));
  3800. IBandSite* pbs;
  3801. if (SUCCEEDED(CoCreateInstance(CLSID_MenuBandSite, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IBandSite, &pbs))))
  3802. {
  3803. if (SUCCEEDED(_pmpPopup->SetClient(pbs)))
  3804. {
  3805. IDeskBand* pdb;
  3806. if (SUCCEEDED(_psmPopup->QueryInterface(IID_PPV_ARG(IDeskBand, &pdb))))
  3807. {
  3808. pbs->AddBand(pdb);
  3809. pdb->Release();
  3810. SendMessage(v_hwndTray, TM_SETPUMPHOOK, (WPARAM)_pmbPopup, (LPARAM)_pmpPopup);
  3811. if (_hTheme)
  3812. {
  3813. HWND hwndTB;
  3814. IUnknown_GetWindow(_psmPopup, &hwndTB);
  3815. if (hwndTB)
  3816. {
  3817. SendMessage(hwndTB, TB_SETWINDOWTHEME, 0, (LPARAM)c_wzTaskBandGroupMenuTheme);
  3818. }
  3819. _psmPopup->SetNoBorder(TRUE);
  3820. }
  3821. hr = _pmpPopup->Popup(ppt, prcl, MPPF_BOTTOM);
  3822. }
  3823. }
  3824. pbs->Release();
  3825. }
  3826. }
  3827. ptbc->Release();
  3828. }
  3829. if (FAILED(hr))
  3830. {
  3831. ttc.Activate(TRUE);
  3832. _FreePopupMenu();
  3833. }
  3834. return hr;
  3835. }
  3836. void CTaskBand::_AddItemToDropDown(int iIndex)
  3837. {
  3838. PTASKITEM pti = _GetItem(iIndex);
  3839. WCHAR szWndText[MAX_WNDTEXT];
  3840. _GetItemTitle(iIndex, szWndText, ARRAYSIZE(szWndText), TRUE);
  3841. if ((HMENU)_menuPopup)
  3842. {
  3843. _menuPopup.InsertMenu(0, MF_BYCOMMAND, iIndex, szWndText);
  3844. }
  3845. if (_psmPopup)
  3846. {
  3847. _psmPopup->InvalidateItem(NULL, SMINV_REFRESH);
  3848. }
  3849. }
  3850. void CTaskBand::_RemoveItemFromDropDown(int iIndex)
  3851. {
  3852. _menuPopup.DeleteMenu(iIndex, MF_BYCOMMAND);
  3853. int iGroupSize = _GetGroupSize(_iIndexPopup);
  3854. for (int i = iIndex + 1; i <= _iIndexPopup + iGroupSize + 1; i++)
  3855. {
  3856. _RefreshItemFromDropDown(i, i - 1, FALSE);
  3857. }
  3858. if (_psmPopup)
  3859. {
  3860. _psmPopup->InvalidateItem(NULL, SMINV_REFRESH);
  3861. }
  3862. }
  3863. void CTaskBand::_RefreshItemFromDropDown(int iIndex, int iNewIndex, BOOL fRefresh)
  3864. {
  3865. PTASKITEM pti = _GetItem(iNewIndex);
  3866. WCHAR szWndText[MAX_WNDTEXT];
  3867. _GetItemTitle(iNewIndex, szWndText, ARRAYSIZE(szWndText), TRUE);
  3868. _menuPopup.ModifyMenu(iIndex, MF_BYCOMMAND, iNewIndex, szWndText);
  3869. if (fRefresh && _psmPopup)
  3870. {
  3871. if (iIndex == iNewIndex)
  3872. {
  3873. SMDATA smd;
  3874. smd.uId = iIndex;
  3875. _psmPopup->InvalidateItem(&smd, SMINV_REFRESH | SMINV_POSITION);
  3876. }
  3877. else
  3878. _psmPopup->InvalidateItem(NULL, SMINV_REFRESH);
  3879. }
  3880. }
  3881. void CTaskBand::_ClosePopupMenus()
  3882. {
  3883. SendMessage(v_hwndTray, SBM_CANCELMENU, 0, 0);
  3884. _FreePopupMenu();
  3885. }
  3886. void CTaskBand::_HandleDropDown(int iIndex)
  3887. {
  3888. _ClosePopupMenus();
  3889. PTASKITEM pti = _GetItem(iIndex);
  3890. if (pti)
  3891. {
  3892. _iIndexLastPopup = _iIndexPopup = iIndex;
  3893. _menuPopup.CreatePopupMenu();
  3894. for (int i = _GetGroupSize(iIndex) + iIndex; i > iIndex; i--)
  3895. {
  3896. _AddItemToDropDown(i);
  3897. }
  3898. RECT rc;
  3899. _tb.GetItemRect(iIndex, &rc);
  3900. MapWindowPoints(_tb, HWND_DESKTOP, (LPPOINT)&rc, 2);
  3901. POINTL pt = {rc.left, rc.top};
  3902. RECTL rcl;
  3903. RECTtoRECTL(&rc, &rcl);
  3904. CToolTipCtrl ttc = _tb.GetToolTips();
  3905. ttc.Activate(FALSE);
  3906. _CreatePopupMenu(&pt, &rcl);
  3907. }
  3908. }
  3909. LRESULT CTaskBand::_HandleDestroy()
  3910. {
  3911. _UnregisterNotify(_uShortcutInvokeNotify);
  3912. RevokeDragDrop(_hwnd);
  3913. RegisterShellHook(_hwnd, FALSE);
  3914. _hwnd = NULL;
  3915. if (_hTheme)
  3916. {
  3917. CloseThemeData(_hTheme);
  3918. _hTheme = NULL;
  3919. }
  3920. if (_tb)
  3921. {
  3922. ASSERT(_tb.IsWindow());
  3923. for (int i = _tb.GetButtonCount() - 1; i >= 0; i--)
  3924. {
  3925. PTASKITEM pti = _GetItem(i);
  3926. if (pti)
  3927. {
  3928. delete pti;
  3929. }
  3930. }
  3931. CImageList il = CImageList(_tb.GetImageList());
  3932. if (il)
  3933. {
  3934. il.Destroy();
  3935. }
  3936. }
  3937. return 1;
  3938. }
  3939. LRESULT CTaskBand::_HandleScroll(BOOL fHoriz, UINT code, int nPos)
  3940. {
  3941. TBMETRICS tbm;
  3942. _GetToolbarMetrics(&tbm);
  3943. SCROLLINFO si;
  3944. si.cbSize = sizeof(si);
  3945. si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
  3946. GetScrollInfo(_hwnd, fHoriz ? SB_HORZ : SB_VERT, &si);
  3947. si.nMax -= (si.nPage -1);
  3948. switch (code)
  3949. {
  3950. case SB_BOTTOM: nPos = si.nMax; break;
  3951. case SB_TOP: nPos = 0; break;
  3952. case SB_ENDSCROLL: nPos = si.nPos; break;
  3953. case SB_LINEDOWN: nPos = si.nPos + 1; break;
  3954. case SB_LINEUP: nPos = si.nPos - 1; break;
  3955. case SB_PAGEDOWN: nPos = si.nPos + si.nPage; break;
  3956. case SB_PAGEUP: nPos = si.nPos - si.nPage; break;
  3957. case SB_THUMBPOSITION:
  3958. case SB_THUMBTRACK: break;
  3959. }
  3960. if (nPos > (int)(si.nMax))
  3961. nPos = si.nMax;
  3962. if (nPos < 0 )
  3963. nPos = 0;
  3964. SetScrollPos(_hwnd, fHoriz ? SB_HORZ : SB_VERT, nPos, TRUE);
  3965. DWORD dwSize = _tb.GetButtonSize();
  3966. if (fHoriz)
  3967. {
  3968. int cxRow = LOWORD(dwSize) + tbm.cxButtonSpacing;
  3969. _tb.SetWindowPos(0, -nPos * cxRow, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE |SWP_NOZORDER);
  3970. }
  3971. else
  3972. {
  3973. int cyRow = HIWORD(dwSize) + tbm.cyButtonSpacing;
  3974. _tb.SetWindowPos(0, 0, -nPos * cyRow , 0, 0, SWP_NOACTIVATE | SWP_NOSIZE |SWP_NOZORDER);
  3975. }
  3976. return 0;
  3977. }
  3978. // after a selection is made, scroll it into view
  3979. void CTaskBand::_ScrollIntoView(int iItem)
  3980. {
  3981. DWORD dwStyle = GetWindowLong(_hwnd, GWL_STYLE);
  3982. if (dwStyle & (WS_HSCROLL | WS_VSCROLL))
  3983. {
  3984. int cVisible = 0;
  3985. for (int i = 0; i < iItem; i++)
  3986. {
  3987. if (!_IsHidden(i))
  3988. cVisible++;
  3989. }
  3990. if (_IsHidden(i))
  3991. {
  3992. PTASKITEM pti = _GetItem(iItem);
  3993. if (pti->hwnd)
  3994. {
  3995. cVisible--;
  3996. }
  3997. }
  3998. int iRows, iCols;
  3999. _GetNumberOfRowsCols(&iRows, &iCols, TRUE);
  4000. _HandleScroll((dwStyle & WS_HSCROLL), SB_THUMBPOSITION, (dwStyle & WS_HSCROLL) ? cVisible / iRows : cVisible / iCols);
  4001. }
  4002. }
  4003. //---------------------------------------------------------------------------
  4004. LRESULT CTaskBand::_HandleSize(WPARAM fwSizeType)
  4005. {
  4006. // Make the listbox fill the parent;
  4007. if (fwSizeType != SIZE_MINIMIZED)
  4008. {
  4009. _CheckSize();
  4010. }
  4011. return 0;
  4012. }
  4013. //---------------------------------------------------------------------------
  4014. // Have the task list show the given window.
  4015. // NB Ignore taskman itself.
  4016. LRESULT CTaskBand::_HandleActivate(HWND hwndActive)
  4017. {
  4018. //
  4019. // App-window activation change is a good time to do a reality
  4020. // check (make sure there are no ghost buttons, etc).
  4021. //
  4022. _RealityCheck();
  4023. if (hwndActive && _IsWindowNormal(hwndActive))
  4024. {
  4025. _RaiseDesktop(FALSE);
  4026. int i = _SelectWindow(hwndActive);
  4027. if (i != -1)
  4028. {
  4029. PTASKITEM pti = _GetItem(i);
  4030. if (pti)
  4031. {
  4032. // Strip off TIF_FLASHING
  4033. pti->dwFlags &= ~TIF_FLASHING;
  4034. // Update the flag that says, "There is an item flashing."
  4035. _UpdateFlashingFlag();
  4036. // if it's flashed blue, turn it off.
  4037. if (pti->dwFlags & TIF_RENDERFLASHED)
  4038. _RedrawItem(hwndActive, HSHELL_REDRAW);
  4039. // Switching to an application counts as "usage"
  4040. // similar to launching it. This solves the "long-running
  4041. // app treated as if it is rarely run" problem
  4042. if (pti->ptsh)
  4043. {
  4044. pti->ptsh->Tickle();
  4045. }
  4046. }
  4047. }
  4048. }
  4049. else
  4050. {
  4051. // Activate taskbar
  4052. if (!(_fIgnoreTaskbarActivate && GetForegroundWindow() == v_hwndTray) && (_iIndexPopup == -1))
  4053. {
  4054. _SetCurSel(-1, TRUE);
  4055. }
  4056. else
  4057. {
  4058. _fIgnoreTaskbarActivate = FALSE;
  4059. }
  4060. }
  4061. if (hwndActive)
  4062. _ptray->_hwndLastActive = hwndActive;
  4063. return TRUE;
  4064. }
  4065. //---------------------------------------------------------------------------
  4066. void CTaskBand::_HandleOtherWindowDestroyed(HWND hwndDestroyed)
  4067. {
  4068. int i;
  4069. // Look for the destoyed window.
  4070. int iItemIndex = _FindIndexByHwnd(hwndDestroyed);
  4071. if (iItemIndex >= 0)
  4072. {
  4073. if (_fAnimate && _IsHorizontal() &&
  4074. ToolBar_IsVisible(_tb, iItemIndex))
  4075. {
  4076. _AnimateItems(iItemIndex, FALSE, FALSE);
  4077. }
  4078. else
  4079. {
  4080. _DeleteItem(hwndDestroyed, iItemIndex);
  4081. }
  4082. }
  4083. else
  4084. {
  4085. // If the item doesn't exist in the task list, make sure it isn't part
  4086. // of somebody's fake SDI implementation. Otherwise Minimize All will
  4087. // break.
  4088. for (i = _tb.GetButtonCount() - 1; i >= 0; i--)
  4089. {
  4090. PTASKITEM pti = _GetItem(i);
  4091. if ((pti->dwFlags & TIF_EVERACTIVEALT) &&
  4092. (HWND) GetWindowLongPtr(pti->hwnd, 0) ==
  4093. hwndDestroyed)
  4094. {
  4095. goto NoDestroy;
  4096. }
  4097. }
  4098. }
  4099. _ptray->HandleWindowDestroyed(hwndDestroyed);
  4100. NoDestroy:
  4101. // This might have been a rude app. Figure out if we've
  4102. // got one now and have the tray sync up.
  4103. HWND hwndRudeApp = _FindRudeApp(NULL);
  4104. _ptray->HandleFullScreenApp(hwndRudeApp);
  4105. if (hwndRudeApp)
  4106. {
  4107. DWORD dwStyleEx = GetWindowLongPtr(hwndRudeApp, GWL_EXSTYLE);
  4108. if (!(dwStyleEx & WS_EX_TOPMOST) && !_IsRudeWindowActive(hwndRudeApp))
  4109. {
  4110. SwitchToThisWindow(hwndRudeApp, TRUE);
  4111. }
  4112. }
  4113. if (_ptray->_hwndLastActive == hwndDestroyed)
  4114. {
  4115. if (_ptray->_hwndLastActive == hwndDestroyed)
  4116. _ptray->_hwndLastActive = NULL;
  4117. }
  4118. }
  4119. void CTaskBand::_HandleGetMinRect(HWND hwndShell, POINTS * prc)
  4120. {
  4121. RECT rc;
  4122. RECT rcTask;
  4123. int i = _FindIndexByHwnd(hwndShell);
  4124. if (i == -1)
  4125. return;
  4126. // Is this button grouped
  4127. if (_IsHidden(i))
  4128. {
  4129. // Yes, get the index for the group button and use its size
  4130. i = _GetGroupIndex(i);
  4131. }
  4132. // Found it in our list.
  4133. _tb.GetItemRect(i, &rc);
  4134. //
  4135. // If the Tab is mirrored then let's retreive the screen coordinates
  4136. // by calculating from the left edge of the screen since screen coordinates
  4137. // are not mirrored so that minRect will prserve its location. [samera]
  4138. //
  4139. if (IS_WINDOW_RTL_MIRRORED(GetDesktopWindow()))
  4140. {
  4141. RECT rcTab;
  4142. _tb.GetWindowRect(&rcTab);
  4143. rc.left += rcTab.left;
  4144. rc.right += rcTab.left;
  4145. rc.top += rcTab.top;
  4146. rc.bottom += rcTab.top;
  4147. }
  4148. else
  4149. {
  4150. _tb.MapWindowPoints(HWND_DESKTOP, (LPPOINT)&rc, 2);
  4151. }
  4152. prc[0].x = (short)rc.left;
  4153. prc[0].y = (short)rc.top;
  4154. prc[1].x = (short)rc.right;
  4155. prc[1].y = (short)rc.bottom;
  4156. // make sure the rect is within out client area
  4157. GetClientRect(_hwnd, &rcTask);
  4158. MapWindowPoints(_hwnd, HWND_DESKTOP, (LPPOINT)&rcTask, 2);
  4159. if (prc[0].x < rcTask.left)
  4160. {
  4161. prc[1].x = prc[0].x = (short)rcTask.left;
  4162. prc[1].x++;
  4163. }
  4164. if (prc[0].x > rcTask.right)
  4165. {
  4166. prc[1].x = prc[0].x = (short)rcTask.right;
  4167. prc[1].x++;
  4168. }
  4169. if (prc[0].y < rcTask.top)
  4170. {
  4171. prc[1].y = prc[0].y = (short)rcTask.top;
  4172. prc[1].y++;
  4173. }
  4174. if (prc[0].y > rcTask.bottom)
  4175. {
  4176. prc[1].y = prc[0].y = (short)rcTask.bottom;
  4177. prc[1].y++;
  4178. }
  4179. }
  4180. BOOL CTaskBand::_IsItemActive(HWND hwndItem)
  4181. {
  4182. HWND hwnd = GetForegroundWindow();
  4183. return (hwnd && hwnd == hwndItem);
  4184. }
  4185. void CTaskBand::_CreateTBImageLists()
  4186. {
  4187. CImageList il = CImageList(_tb.GetImageList());
  4188. ATOMICRELEASE(_pimlSHIL);
  4189. SHGetImageList(SHIL_SYSSMALL, IID_PPV_ARG(IImageList, &_pimlSHIL));
  4190. il.Destroy();
  4191. int cx = GetSystemMetrics(SM_CXSMICON);
  4192. int cy = GetSystemMetrics(SM_CYSMICON);
  4193. il.Create(cx, cy, SHGetImageListFlags(_tb), 4, 4);
  4194. _tb.SendMessage(TB_SETIMAGELIST, IL_NORMAL, (LPARAM)(HIMAGELIST)il);
  4195. _tb.SendMessage(TB_SETIMAGELIST, IL_SHIL, (LPARAM)IImageListToHIMAGELIST(_pimlSHIL));
  4196. }
  4197. int CTaskBand::_AddIconToNormalImageList(HICON hicon, int iImage)
  4198. {
  4199. if (hicon)
  4200. {
  4201. CImageList il = CImageList(_tb.GetImageList());
  4202. if (il)
  4203. {
  4204. int iRet;
  4205. if (iImage < 0 || HIWORD(iImage) != IL_NORMAL)
  4206. iRet = il.ReplaceIcon(-1, hicon);
  4207. else
  4208. iRet = il.ReplaceIcon(iImage, hicon);
  4209. if (iRet == -1)
  4210. {
  4211. TraceMsg(TF_WARNING, "ReplaceIcon failed for iImage %x hicon %x", iImage, hicon);
  4212. iRet = iImage;
  4213. }
  4214. return MAKELONG(iRet, IL_NORMAL);
  4215. }
  4216. }
  4217. return I_IMAGENONE;
  4218. }
  4219. void CTaskBand::_UpdateItemText(int iItem)
  4220. {
  4221. TBBUTTONINFO tbbi;
  4222. tbbi.cbSize = sizeof(tbbi);
  4223. tbbi.dwMask = TBIF_BYINDEX | TBIF_TEXT;
  4224. // get current button text
  4225. TCHAR szWndText[MAX_WNDTEXT];
  4226. *szWndText = 0;
  4227. _GetItemTitle(iItem, szWndText, ARRAYSIZE(szWndText), FALSE);
  4228. tbbi.pszText = szWndText;
  4229. _tb.SetButtonInfo(iItem, &tbbi);
  4230. }
  4231. void CTaskBand::_DoRedrawWhereNeeded()
  4232. {
  4233. int i;
  4234. for (i = _tb.GetButtonCount() - 1; i >= 0; i--)
  4235. {
  4236. PTASKITEM pti = _GetItem(i);
  4237. if (pti->dwFlags & TIF_NEEDSREDRAW)
  4238. {
  4239. pti->dwFlags &= ~TIF_NEEDSREDRAW;
  4240. _RedrawItem(pti->hwnd, HSHELL_REDRAW, i);
  4241. }
  4242. }
  4243. }
  4244. void CTaskBand::_RedrawItem(HWND hwndShell, WPARAM code, int i)
  4245. {
  4246. if (i == -1)
  4247. {
  4248. i = _FindIndexByHwnd(hwndShell);
  4249. }
  4250. if (i != -1)
  4251. {
  4252. TOOLINFO ti;
  4253. ti.cbSize = sizeof(ti);
  4254. PTASKITEM pti = _GetItem(i);
  4255. // set the bit saying whether we should flash or not
  4256. if ((code == HSHELL_FLASH) != BOOLIFY(pti->dwFlags & TIF_RENDERFLASHED))
  4257. {
  4258. // only do the set if this bit changed.
  4259. if (code == HSHELL_FLASH)
  4260. {
  4261. // TIF_RENDERFLASHED means, "Paint the background blue."
  4262. // TIF_FLASHING means, "This item is flashing."
  4263. pti->dwFlags |= TIF_RENDERFLASHED;
  4264. // Only set TIF_FLASHING and unhide the tray if the app is inactive.
  4265. // Some apps (e.g., freecell) flash themselves while active just for
  4266. // fun. It's annoying for the autohid tray to pop out in that case.
  4267. if (!_IsItemActive(pti->hwnd))
  4268. {
  4269. pti->dwFlags |= TIF_FLASHING;
  4270. // unhide the tray whenever we get a flashing app.
  4271. _ptray->Unhide();
  4272. }
  4273. }
  4274. else
  4275. {
  4276. // Don't clear TIF_FLASHING. We clear that only when the app
  4277. // is activated.
  4278. pti->dwFlags &= ~TIF_RENDERFLASHED;
  4279. }
  4280. // Update the flag that says, "There is an item flashing."
  4281. _UpdateFlashingFlag();
  4282. }
  4283. // Don't change the name of a group button
  4284. if (pti->hwnd)
  4285. {
  4286. // update text and icon
  4287. _UpdateItemText(i);
  4288. _UpdateItemIcon(i);
  4289. }
  4290. int iGroupIndex = _GetGroupIndex(i);
  4291. if ((iGroupIndex == _iIndexPopup) && hwndShell)
  4292. {
  4293. _RefreshItemFromDropDown(i, i, TRUE);
  4294. }
  4295. RECT rc;
  4296. if (_tb.GetItemRect(i, &rc))
  4297. {
  4298. InvalidateRect(_tb, &rc, TRUE);
  4299. }
  4300. if (_tb.GetItemRect(iGroupIndex, &rc))
  4301. {
  4302. InvalidateRect(_tb, &rc, TRUE);
  4303. }
  4304. ti.hwnd = _tb;
  4305. ti.uId = i;
  4306. ti.lpszText = LPSTR_TEXTCALLBACK;
  4307. SendMessage(_ptray->GetTrayTips(), TTM_UPDATETIPTEXT, 0, (LPARAM)&ti);
  4308. }
  4309. }
  4310. void CTaskBand::_SetActiveAlt(HWND hwndAlt)
  4311. {
  4312. int iMax;
  4313. int i;
  4314. iMax = _tb.GetButtonCount();
  4315. for ( i = 0; i < iMax; i++)
  4316. {
  4317. PTASKITEM pti = _GetItem(i);
  4318. if (pti->hwnd == hwndAlt)
  4319. pti->dwFlags |= TIF_ACTIVATEALT | TIF_EVERACTIVEALT;
  4320. else
  4321. pti->dwFlags &= ~TIF_ACTIVATEALT;
  4322. }
  4323. }
  4324. BOOL _IsRudeWindowActive(HWND hwnd)
  4325. {
  4326. // A rude window is considered "active" if it is:
  4327. // - in the same thread as the foreground window, or
  4328. // - in the same window hierarchy as the foreground window
  4329. //
  4330. HWND hwndFore = GetForegroundWindow();
  4331. DWORD dwID = GetWindowThreadProcessId(hwnd, NULL);
  4332. DWORD dwIDFore = GetWindowThreadProcessId(hwndFore, NULL);
  4333. if (dwID == dwIDFore)
  4334. return TRUE;
  4335. else if (SHIsParentOwnerOrSelf(hwnd, hwndFore) == S_OK)
  4336. return TRUE;
  4337. return FALSE;
  4338. }
  4339. // _IsRudeWindow -- is given HWND 'rude' (fullscreen) on given monitor
  4340. //
  4341. BOOL _IsRudeWindow(HMONITOR hmon, HWND hwnd, HMONITOR hmonTask, BOOL fSkipActiveCheck)
  4342. {
  4343. ASSERT(hmon);
  4344. ASSERT(hwnd);
  4345. //
  4346. // Don't count the desktop as rude
  4347. // also filter out hidden windows (such as the desktop browser's raised window)
  4348. //
  4349. if (IsWindowVisible(hwnd) && hwnd != v_hwndDesktop)
  4350. {
  4351. RECT rcMon, rcApp, rcTmp;
  4352. DWORD dwStyle;
  4353. //
  4354. // NB: User32 will sometimes send us spurious HSHELL_RUDEAPPACTIVATED
  4355. // messages. When this happens, and we happen to have a maximized
  4356. // app up, the old version of this code would think there was a rude app
  4357. // up. This mistake would break tray always-on-top and autohide.
  4358. //
  4359. //
  4360. // The old logic was:
  4361. //
  4362. // If the app's window rect takes up the whole monitor, then it's rude.
  4363. // (This check could mistake normal maximized apps for rude apps.)
  4364. //
  4365. //
  4366. // The new logic is:
  4367. //
  4368. // If the app window does not have WS_DLGFRAME and WS_THICKFRAME,
  4369. // then do the old check. Rude apps typically lack one of these bits
  4370. // (while normal apps usually have them), so do the old check in
  4371. // this case to avoid potential compat issues with rude apps that
  4372. // have non-fullscreen client areas.
  4373. //
  4374. // Otherwise, get the client rect rather than the window rect
  4375. // and compare that rect against the monitor rect.
  4376. //
  4377. // If (mon U app) == app, then app is filling up entire monitor
  4378. GetMonitorRect(hmon, &rcMon);
  4379. dwStyle = GetWindowLong(hwnd, GWL_STYLE);
  4380. if ((dwStyle & (WS_CAPTION | WS_THICKFRAME)) == (WS_CAPTION | WS_THICKFRAME))
  4381. {
  4382. // Doesn't match rude app profile; use client rect
  4383. GetClientRect(hwnd, &rcApp);
  4384. MapWindowPoints(hwnd, HWND_DESKTOP, (LPPOINT)&rcApp, 2);
  4385. }
  4386. else
  4387. {
  4388. // Matches rude app profile; use window rect
  4389. GetWindowRect(hwnd, &rcApp);
  4390. }
  4391. UnionRect(&rcTmp, &rcApp, &rcMon);
  4392. if (EqualRect(&rcTmp, &rcApp))
  4393. {
  4394. // Looks like a rude app. Is it active?
  4395. if ((hmonTask == hmon) && (fSkipActiveCheck || _IsRudeWindowActive(hwnd)))
  4396. {
  4397. return TRUE;
  4398. }
  4399. }
  4400. }
  4401. // No, not rude
  4402. return FALSE;
  4403. }
  4404. struct iradata
  4405. {
  4406. HMONITOR hmon; // IN hmon we're checking against
  4407. HWND hwnd; // INOUT hwnd of 1st rude app found
  4408. HMONITOR hmonTask;
  4409. HWND hwndSelected;
  4410. };
  4411. BOOL WINAPI CTaskBand::IsRudeEnumProc(HWND hwnd, LPARAM lParam)
  4412. {
  4413. struct iradata *pira = (struct iradata *)lParam;
  4414. HMONITOR hmon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
  4415. if (hmon && (pira->hmon == NULL || pira->hmon == hmon))
  4416. {
  4417. if (_IsRudeWindow(hmon, hwnd, pira->hmonTask, (hwnd == pira->hwndSelected)))
  4418. {
  4419. // We're done
  4420. pira->hwnd = hwnd;
  4421. return FALSE;
  4422. }
  4423. }
  4424. // Keep going
  4425. return TRUE;
  4426. }
  4427. HWND CTaskBand::_EnumForRudeWindow(HWND hwndSelected)
  4428. {
  4429. struct iradata irad = { NULL, 0, MonitorFromWindow(_hwnd, MONITOR_DEFAULTTONEAREST), hwndSelected };
  4430. // First try our cache
  4431. if (IsWindow(_hwndLastRude))
  4432. {
  4433. if (!IsRudeEnumProc(_hwndLastRude, (LPARAM)&irad))
  4434. {
  4435. // Cache hit
  4436. return irad.hwnd;
  4437. }
  4438. }
  4439. // No luck, gotta do it the hard way
  4440. EnumWindows(IsRudeEnumProc, (LPARAM)&irad);
  4441. // Cache it for next time
  4442. _hwndLastRude = irad.hwnd;
  4443. return irad.hwnd;
  4444. }
  4445. HWND CTaskBand::_FindRudeApp(HWND hwndPossible)
  4446. {
  4447. //
  4448. // Search through:
  4449. //
  4450. // (a) the toplevel windows for an "active" one that "looks" fullscreen, and
  4451. // (b) the task items for one that is "active" and is marked fullscreen
  4452. //
  4453. HWND hwndSelected = hwndPossible;
  4454. if (!hwndSelected)
  4455. {
  4456. int iCurSel = _GetCurSel();
  4457. if (iCurSel != -1)
  4458. {
  4459. PTASKITEM pti = _GetItem(iCurSel);
  4460. hwndSelected = pti->hwnd;
  4461. }
  4462. }
  4463. HWND hwnd = _EnumForRudeWindow(hwndSelected);
  4464. for (int i = _tb.GetButtonCount() - 1; hwnd == NULL && i >= 0; i--)
  4465. {
  4466. PTASKITEM pti = _GetItem(i);
  4467. if (pti->fMarkedFullscreen && ((pti->hwnd == hwndSelected) || _IsRudeWindowActive(pti->hwnd)))
  4468. {
  4469. hwnd = pti->hwnd;
  4470. }
  4471. }
  4472. return hwnd;
  4473. }
  4474. // handle WM_APPCOMMAND, special case off those that we know are global
  4475. // to the system, these really are not "App" commands ;-)
  4476. LRESULT CTaskBand::_OnAppCommand(int cmd)
  4477. {
  4478. BOOL bHandled = FALSE;
  4479. switch (cmd)
  4480. {
  4481. // skip all of these, they are either handled by the system volume control
  4482. // or by the media player, don't let these fall through to the registry
  4483. // based app command handling
  4484. case APPCOMMAND_MEDIA_NEXTTRACK:
  4485. case APPCOMMAND_MEDIA_PREVIOUSTRACK:
  4486. case APPCOMMAND_MEDIA_STOP:
  4487. case APPCOMMAND_MEDIA_PLAY_PAUSE:
  4488. break;
  4489. case APPCOMMAND_VOLUME_MUTE:
  4490. Mixer_ToggleMute();
  4491. return 0;
  4492. case APPCOMMAND_VOLUME_DOWN:
  4493. Mixer_SetVolume(-MIXER_DEFAULT_STEP);
  4494. return 0;
  4495. case APPCOMMAND_VOLUME_UP:
  4496. Mixer_SetVolume(MIXER_DEFAULT_STEP);
  4497. return 0;
  4498. case APPCOMMAND_BASS_BOOST:
  4499. Mixer_ToggleBassBoost();
  4500. return 0;
  4501. case APPCOMMAND_BASS_DOWN:
  4502. Mixer_SetBass(-MIXER_DEFAULT_STEP);
  4503. return 0;
  4504. case APPCOMMAND_BASS_UP:
  4505. Mixer_SetBass(MIXER_DEFAULT_STEP);
  4506. return 0;
  4507. case APPCOMMAND_TREBLE_DOWN:
  4508. Mixer_SetTreble(-MIXER_DEFAULT_STEP);
  4509. return 0;
  4510. case APPCOMMAND_TREBLE_UP:
  4511. Mixer_SetTreble(MIXER_DEFAULT_STEP);
  4512. return 0;
  4513. default:
  4514. bHandled = AppCommandTryRegistry(cmd);
  4515. if (!bHandled)
  4516. {
  4517. switch (cmd)
  4518. {
  4519. case APPCOMMAND_BROWSER_SEARCH:
  4520. SHFindFiles(NULL, NULL);
  4521. bHandled = TRUE;
  4522. break;
  4523. }
  4524. }
  4525. }
  4526. return bHandled;
  4527. }
  4528. PTASKITEM CTaskBand::_FindItemByHwnd(HWND hwnd)
  4529. {
  4530. int iIndex = _FindIndexByHwnd(hwnd);
  4531. return _GetItem(iIndex);
  4532. }
  4533. void CTaskBand::_OnWindowActivated(HWND hwnd, BOOL fSuspectFullscreen)
  4534. {
  4535. //
  4536. // First see if we consider this window fullscreen
  4537. //
  4538. HWND hwndRude;
  4539. PTASKITEM pti = _FindItemByHwnd(hwnd);
  4540. if (pti && pti->fMarkedFullscreen)
  4541. {
  4542. //
  4543. // Yes, marked by the app as fullscreen
  4544. //
  4545. hwndRude = hwnd;
  4546. }
  4547. else if (fSuspectFullscreen)
  4548. {
  4549. //
  4550. // Possibly, but we need to double-check for ourselves
  4551. //
  4552. //
  4553. // We shouldn't need to do this but we're getting rude-app activation
  4554. // msgs when there aren't any.
  4555. //
  4556. // Also, the hwnd that user tells us about is just the foreground window --
  4557. // _FindRudeApp will return the window that's actually sized fullscreen.
  4558. //
  4559. hwndRude = _FindRudeApp(hwnd);
  4560. }
  4561. else
  4562. {
  4563. //
  4564. // No, not fullscreen
  4565. //
  4566. hwndRude = NULL;
  4567. }
  4568. SetTimer(_hwnd, IDT_RECHECKRUDEAPP1, 1000, NULL);
  4569. //
  4570. // Okay, now do that weird hwnd futzing for ACTIVEALT apps
  4571. //
  4572. if (pti == NULL)
  4573. {
  4574. BOOL fFoundBackup = FALSE;
  4575. BOOL fDone = FALSE;
  4576. int iMax = _tb.GetButtonCount();
  4577. for (int i = 0; (i < iMax) && (!fDone); i++)
  4578. {
  4579. PTASKITEM ptiT = _GetItem(i);
  4580. if (ptiT->hwnd)
  4581. {
  4582. DWORD dwFlags = ptiT->dwFlags;
  4583. if ((dwFlags & TIF_ACTIVATEALT) ||
  4584. (!fFoundBackup && (dwFlags & TIF_EVERACTIVEALT)))
  4585. {
  4586. DWORD dwpid1, dwpid2;
  4587. GetWindowThreadProcessId(hwnd, &dwpid1);
  4588. GetWindowThreadProcessId(ptiT->hwnd, &dwpid2);
  4589. // Only change if they're in the same process
  4590. if (dwpid1 == dwpid2)
  4591. {
  4592. hwnd = ptiT->hwnd;
  4593. if (dwFlags & TIF_ACTIVATEALT)
  4594. {
  4595. fDone = TRUE;
  4596. break;
  4597. }
  4598. else
  4599. fFoundBackup = TRUE;
  4600. }
  4601. }
  4602. }
  4603. }
  4604. }
  4605. //
  4606. // Now do the actual check/uncheck the button stuff
  4607. //
  4608. _HandleActivate(hwnd);
  4609. //
  4610. // Finally, let the tray know about any fullscreen windowage
  4611. //
  4612. _ptray->HandleFullScreenApp(hwndRude);
  4613. }
  4614. // We get notification about activation etc here. This saves having
  4615. // a fine-grained timer.
  4616. LRESULT CTaskBand::_HandleShellHook(int iCode, LPARAM lParam)
  4617. {
  4618. HWND hwnd = (HWND)lParam;
  4619. switch (iCode)
  4620. {
  4621. case HSHELL_GETMINRECT:
  4622. {
  4623. SHELLHOOKINFO * pshi = (SHELLHOOKINFO *)lParam;
  4624. _HandleGetMinRect(pshi->hwnd, (POINTS *)&pshi->rc);
  4625. }
  4626. return TRUE;
  4627. case HSHELL_RUDEAPPACTIVATED:
  4628. case HSHELL_WINDOWACTIVATED:
  4629. _OnWindowActivated(hwnd, TRUE);
  4630. break;
  4631. case HSHELL_WINDOWREPLACING:
  4632. _hwndReplacing = hwnd;
  4633. break;
  4634. case HSHELL_WINDOWREPLACED:
  4635. if (_hwndReplacing)
  4636. {
  4637. // If we already created a button for this dude, remove it now.
  4638. // We might have one if user sent an HSHELL_WINDOWACTIVATED before
  4639. // the HSHELL_WINDOWREPLACING/HSHELL_WINDOWREPLACED pair.
  4640. _DeleteItem(_hwndReplacing, -1);
  4641. // Swap in _hwndReplacing for hwnd in hwnd's button
  4642. int iItem = _FindIndexByHwnd(hwnd);
  4643. if (iItem != -1)
  4644. {
  4645. PTASKITEM pti = _GetItem(iItem);
  4646. pti->hwnd = _hwndReplacing;
  4647. WCHAR szExeName[MAX_PATH];
  4648. SHExeNameFromHWND(_hwndReplacing, szExeName, ARRAYSIZE(szExeName));
  4649. int iIndexGroup = _GetGroupIndex(iItem);
  4650. PTASKITEM ptiGroup = _GetItem(iIndexGroup);
  4651. pti->fHungApp = (lstrcmpi(ptiGroup->pszExeName, szExeName) != 0);
  4652. }
  4653. _hwndReplacing = NULL;
  4654. }
  4655. break;
  4656. case HSHELL_WINDOWCREATED:
  4657. _AddWindow(hwnd);
  4658. break;
  4659. case HSHELL_WINDOWDESTROYED:
  4660. _HandleOtherWindowDestroyed(hwnd);
  4661. break;
  4662. case HSHELL_ACTIVATESHELLWINDOW:
  4663. SwitchToThisWindow(v_hwndTray, TRUE);
  4664. SetForegroundWindow(v_hwndTray);
  4665. break;
  4666. case HSHELL_TASKMAN:
  4667. // winlogon/user send a -1 lParam to indicate that the
  4668. // task list should be displayed (normally the lParam is the hwnd)
  4669. if (-1 == lParam)
  4670. {
  4671. RunSystemMonitor();
  4672. }
  4673. else
  4674. {
  4675. // if it wasn't invoked via control escape, then it was the win key
  4676. if (!_ptray->_fStuckRudeApp && GetAsyncKeyState(VK_CONTROL) >= 0)
  4677. {
  4678. HWND hwndForeground = GetForegroundWindow();
  4679. BOOL fIsTrayForeground = hwndForeground == v_hwndTray;
  4680. if (v_hwndStartPane && hwndForeground == v_hwndStartPane)
  4681. {
  4682. fIsTrayForeground = TRUE;
  4683. }
  4684. if (!_hwndPrevFocus)
  4685. {
  4686. if (!fIsTrayForeground)
  4687. {
  4688. _hwndPrevFocus = hwndForeground;
  4689. }
  4690. }
  4691. else if (fIsTrayForeground)
  4692. {
  4693. // _hwndPrevFocus will be wiped out by the MPOS_FULLCANCEL
  4694. // so save it before we lose it
  4695. HWND hwndPrevFocus = _hwndPrevFocus;
  4696. _ClosePopupMenus();
  4697. // otherwise they're just hitting the key again.
  4698. // set focus away
  4699. SHAllowSetForegroundWindow(hwndPrevFocus);
  4700. SetForegroundWindow(hwndPrevFocus);
  4701. _hwndPrevFocus = NULL;
  4702. return TRUE;
  4703. }
  4704. }
  4705. PostMessage(v_hwndTray, TM_ACTASTASKSW, 0, 0L);
  4706. }
  4707. return TRUE;
  4708. case HSHELL_REDRAW:
  4709. {
  4710. int i = _FindIndexByHwnd(hwnd);
  4711. if (i != -1)
  4712. {
  4713. PTASKITEM pti = _GetItem(i);
  4714. pti->dwFlags |= TIF_NEEDSREDRAW;
  4715. SetTimer(_hwnd, IDT_REDRAW, 100, 0);
  4716. }
  4717. }
  4718. break;
  4719. case HSHELL_FLASH:
  4720. _RedrawItem(hwnd, iCode);
  4721. break;
  4722. case HSHELL_ENDTASK:
  4723. EndTask(hwnd, FALSE, FALSE);
  4724. break;
  4725. case HSHELL_APPCOMMAND:
  4726. // shell gets last shot at WM_APPCOMMAND messages via our shell hook
  4727. // RegisterShellHookWindow() is called in shell32/.RegisterShellHook()
  4728. return _OnAppCommand(GET_APPCOMMAND_LPARAM(lParam));
  4729. }
  4730. return 0;
  4731. }
  4732. void CTaskBand::_InitFonts()
  4733. {
  4734. HFONT hfont;
  4735. NONCLIENTMETRICS ncm;
  4736. ncm.cbSize = sizeof(ncm);
  4737. if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0))
  4738. {
  4739. // Create the bold font
  4740. ncm.lfCaptionFont.lfWeight = FW_BOLD;
  4741. hfont = CreateFontIndirect(&ncm.lfCaptionFont);
  4742. if (hfont)
  4743. {
  4744. if (_hfontCapBold)
  4745. DeleteFont(_hfontCapBold);
  4746. _hfontCapBold = hfont;
  4747. }
  4748. // Create the normal font
  4749. ncm.lfCaptionFont.lfWeight = FW_NORMAL;
  4750. hfont = CreateFontIndirect(&ncm.lfCaptionFont);
  4751. if (hfont)
  4752. {
  4753. if (_hfontCapNormal)
  4754. DeleteFont(_hfontCapNormal);
  4755. _hfontCapNormal = hfont;
  4756. }
  4757. }
  4758. }
  4759. void CTaskBand::_SetItemImage(int iItem, int iImage, int iPref)
  4760. {
  4761. TBBUTTONINFO tbbi;
  4762. tbbi.cbSize = sizeof(tbbi);
  4763. tbbi.dwMask = TBIF_BYINDEX | TBIF_IMAGE;
  4764. tbbi.iImage = iImage;
  4765. _tb.SetButtonInfo(iItem, &tbbi);
  4766. PTASKITEM pti = _GetItem(iItem);
  4767. pti->iIconPref = iPref;
  4768. }
  4769. void CTaskBand::_UpdateAllIcons()
  4770. {
  4771. BOOL fRedraw = (BOOL)_tb.SendMessage(WM_SETREDRAW, FALSE, 0);
  4772. // Set all of icon indices in the toolbar to image none
  4773. for (int i = _tb.GetButtonCount() - 1; i >= 0; i--)
  4774. {
  4775. _SetItemImage(i, I_IMAGENONE, 0);
  4776. }
  4777. // Create a new image list
  4778. _CreateTBImageLists();
  4779. for (i = _tb.GetButtonCount() - 1; i >= 0; i--)
  4780. {
  4781. _UpdateItemIcon(i);
  4782. }
  4783. _tb.SetRedraw(fRedraw);
  4784. }
  4785. //---------------------------------------------------------------------------
  4786. LRESULT CTaskBand::_HandleWinIniChange(WPARAM wParam, LPARAM lParam, BOOL fOnCreate)
  4787. {
  4788. _tb.SendMessage(WM_WININICHANGE, wParam, lParam);
  4789. if (wParam == SPI_SETNONCLIENTMETRICS ||
  4790. ((!wParam) && (!lParam || (lstrcmpi((LPTSTR)lParam, TEXT("WindowMetrics")) == 0))))
  4791. {
  4792. //
  4793. // On creation, don't bother creating the fonts if someone else
  4794. // (such as the clock control) has already done it for us.
  4795. //
  4796. if (!fOnCreate || !_hfontCapNormal)
  4797. _InitFonts();
  4798. if (_tb)
  4799. {
  4800. _tb.SetFont(_hfontCapNormal);
  4801. }
  4802. // force _TextSpace to be recalculated
  4803. _iTextSpace = 0;
  4804. if (fOnCreate)
  4805. {
  4806. //
  4807. // On creation, we haven't been inserted into bandsite yet,
  4808. // so we need to defer size validation.
  4809. //
  4810. PostMessage(_hwnd, TBC_VERIFYBUTTONHEIGHT, 0, 0);
  4811. }
  4812. else
  4813. {
  4814. _VerifyButtonHeight();
  4815. }
  4816. }
  4817. if (lParam == SPI_SETMENUANIMATION || lParam == SPI_SETUIEFFECTS || (!wParam &&
  4818. (!lParam || (lstrcmpi((LPTSTR)lParam, TEXT("Windows")) == 0) ||
  4819. (lstrcmpi((LPTSTR)lParam, TEXT("VisualEffects")) == 0))))
  4820. {
  4821. _fAnimate = ShouldTaskbarAnimate();
  4822. }
  4823. if (!wParam && (!lParam || (0 == lstrcmpi((LPCTSTR)lParam, TEXT("TraySettings")))))
  4824. {
  4825. _RefreshSettings();
  4826. }
  4827. return 0;
  4828. }
  4829. void CTaskBand::_VerifyButtonHeight()
  4830. {
  4831. // force toolbar to get new sizes
  4832. SIZE size = {0, 0};
  4833. _tb.SetButtonSize(size);
  4834. _BandInfoChanged();
  4835. }
  4836. int CTaskBand::_GetCurButtonHeight()
  4837. {
  4838. TBMETRICS tbm;
  4839. _GetToolbarMetrics(&tbm);
  4840. int cyButtonHeight = HIWORD(_tb.GetButtonSize());
  4841. if (!cyButtonHeight)
  4842. cyButtonHeight = tbm.cyPad + g_cySize;
  4843. return cyButtonHeight;
  4844. }
  4845. void CTaskBand::_HandleChangeNotify(WPARAM wParam, LPARAM lParam)
  4846. {
  4847. LPITEMIDLIST *ppidl;
  4848. LONG lEvent;
  4849. LPSHChangeNotificationLock pshcnl;
  4850. pshcnl = SHChangeNotification_Lock((HANDLE)wParam, (DWORD)lParam, &ppidl, &lEvent);
  4851. if (pshcnl)
  4852. {
  4853. switch (lEvent)
  4854. {
  4855. case SHCNE_EXTENDED_EVENT:
  4856. {
  4857. LPSHShortcutInvokeAsIDList psidl = (LPSHShortcutInvokeAsIDList)ppidl[0];
  4858. if (psidl && psidl->dwItem1 == SHCNEE_SHORTCUTINVOKE)
  4859. {
  4860. // Make sure nobody tries to do this in a multithreaded way
  4861. // since we're not protecting the cache with a critical section
  4862. ASSERT(GetCurrentThreadId() == GetWindowThreadProcessId(_hwnd, NULL));
  4863. if (TaskShortcut::_HandleShortcutInvoke(psidl))
  4864. {
  4865. _ReattachTaskShortcut();
  4866. }
  4867. }
  4868. }
  4869. break;
  4870. case SHCNE_UPDATEIMAGE:
  4871. {
  4872. int iImage = ppidl[0] ? *(int UNALIGNED *)((BYTE *)ppidl[0] + 2) : -1;
  4873. if (iImage == -1)
  4874. {
  4875. _UpdateAllIcons();
  4876. }
  4877. }
  4878. break;
  4879. // The tray doesn't have a changenotify registered so we piggyback
  4880. // off this one. If associations change, icons may have changed,
  4881. // so we have to go rebuild. (Also if the user changes between
  4882. // small and large system icons we will get an AssocChanged.)
  4883. case SHCNE_ASSOCCHANGED:
  4884. PostMessage(v_hwndTray, SBM_REBUILDMENU, 0, 0);
  4885. break;
  4886. }
  4887. SHChangeNotification_Unlock(pshcnl);
  4888. }
  4889. }
  4890. DWORD WINAPI HardErrorBalloonThread(PVOID pv)
  4891. {
  4892. HARDERRORDATA *phed = (HARDERRORDATA *)pv;
  4893. DWORD dwError;
  4894. WCHAR *pwszTitle = NULL;
  4895. WCHAR *pwszText = NULL;
  4896. ASSERT(NULL != phed);
  4897. dwError = phed->dwError;
  4898. if (phed->uOffsetTitleW != 0)
  4899. {
  4900. pwszTitle = (WCHAR *)((BYTE *)phed + phed->uOffsetTitleW);
  4901. }
  4902. if (phed->uOffsetTextW != 0)
  4903. {
  4904. pwszText = (WCHAR *)((BYTE *)phed + phed->uOffsetTextW);
  4905. }
  4906. TCHAR szMutexName[32];
  4907. HANDLE hMutex;
  4908. StringCchPrintf(szMutexName, ARRAYSIZE(szMutexName), TEXT("HardError_%08lX"), dwError);
  4909. hMutex = CreateMutex(NULL, FALSE, szMutexName);
  4910. if (NULL != hMutex)
  4911. {
  4912. DWORD dwWaitResult = WaitForSingleObject(hMutex, 0); // Just test it
  4913. if (dwWaitResult == WAIT_OBJECT_0)
  4914. {
  4915. IUserNotification *pun;
  4916. HRESULT hr;
  4917. hr = CoCreateInstance(CLSID_UserNotification, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IUserNotification, &pun));
  4918. if (SUCCEEDED(hr))
  4919. {
  4920. pun->SetBalloonRetry(120 * 1000, 0, 0);
  4921. pun->SetBalloonInfo(pwszTitle, pwszText, NIIF_WARNING);
  4922. pun->SetIconInfo(NULL, pwszTitle);
  4923. hr = pun->Show(NULL, 0);
  4924. pun->Release();
  4925. }
  4926. ReleaseMutex(hMutex);
  4927. }
  4928. CloseHandle(hMutex);
  4929. }
  4930. LocalFree(pv);
  4931. return 0;
  4932. }
  4933. LRESULT CTaskBand::_HandleHardError(HARDERRORDATA *phed, DWORD cbData)
  4934. {
  4935. DWORD dwError;
  4936. BOOL fHandled;
  4937. BOOL fBalloon;
  4938. dwError = phed->dwError;
  4939. fHandled = FALSE;
  4940. fBalloon = TRUE;
  4941. // Check if we're on the right desktop
  4942. HDESK hdeskInput = OpenInputDesktop(0, FALSE, STANDARD_RIGHTS_REQUIRED | DESKTOP_READOBJECTS);
  4943. if (NULL == hdeskInput)
  4944. {
  4945. // Couldn't open desktop, we must not be getting the hard error while on
  4946. // the default desktop. Lets not handle that case. Its silly to have
  4947. // balloons on the wrong desktop, or not where the user can see them.
  4948. fBalloon = FALSE;
  4949. }
  4950. else
  4951. {
  4952. CloseDesktop(hdeskInput);
  4953. }
  4954. if (fBalloon)
  4955. {
  4956. HARDERRORDATA *phedCopy;
  4957. phedCopy = (HARDERRORDATA *)LocalAlloc(LPTR, cbData);
  4958. if (NULL != phedCopy)
  4959. {
  4960. CopyMemory(phedCopy,phed,cbData);
  4961. if (SHCreateThread(HardErrorBalloonThread,phedCopy,CTF_COINIT,NULL))
  4962. {
  4963. fHandled = TRUE;
  4964. }
  4965. else
  4966. {
  4967. LocalFree(phedCopy);
  4968. }
  4969. }
  4970. }
  4971. return fHandled;
  4972. }
  4973. void CTaskBand::_OnSetFocus()
  4974. {
  4975. NMHDR nmhdr;
  4976. _tb.SetFocus();
  4977. nmhdr.hwndFrom = _hwnd;
  4978. nmhdr.code = NM_SETFOCUS;
  4979. SendMessage(GetParent(_hwnd), WM_NOTIFY, (WPARAM)NULL, (LPARAM)&nmhdr);
  4980. }
  4981. void CTaskBand::_OpenTheme()
  4982. {
  4983. if (_hTheme)
  4984. {
  4985. CloseThemeData(_hTheme);
  4986. _hTheme = NULL;
  4987. }
  4988. _hTheme = OpenThemeData(_hwnd, c_wzTaskBandTheme);
  4989. TBMETRICS tbm;
  4990. _GetToolbarMetrics(&tbm);
  4991. tbm.cxPad = _hTheme ? 20 : 8;
  4992. tbm.cyBarPad = 0;
  4993. tbm.cxButtonSpacing = _hTheme ? 0 : 3;
  4994. tbm.cyButtonSpacing = _hTheme ? 0 : 3;
  4995. _tb.SendMessage(TB_SETMETRICS, 0, (LPARAM)&tbm);
  4996. _CheckSize();
  4997. }
  4998. LRESULT CTaskBand::v_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  4999. {
  5000. LRESULT lres;
  5001. INSTRUMENT_WNDPROC(SHCNFI_MAIN_WNDPROC, hwnd, uMsg, wParam, lParam);
  5002. switch (uMsg)
  5003. {
  5004. case WM_CREATE:
  5005. return _HandleCreate();
  5006. case WM_DESTROY:
  5007. return _HandleDestroy();
  5008. case WM_WINDOWPOSCHANGED:
  5009. {
  5010. LRESULT lres = _HandleSize(wParam);
  5011. SetTimer(_hwnd, IDT_RECHECKRUDEAPP1, 1000, NULL);
  5012. return lres;
  5013. }
  5014. case WM_PAINT:
  5015. case WM_PRINTCLIENT:
  5016. {
  5017. PAINTSTRUCT ps;
  5018. LPRECT prc = NULL;
  5019. HDC hdc = (HDC)wParam;
  5020. if (uMsg == WM_PAINT)
  5021. {
  5022. BeginPaint(hwnd, &ps);
  5023. prc = &ps.rcPaint;
  5024. hdc = ps.hdc;
  5025. }
  5026. if (_hTheme)
  5027. {
  5028. DrawThemeParentBackground(hwnd, hdc, prc);
  5029. }
  5030. else
  5031. {
  5032. RECT rc;
  5033. GetClientRect(hwnd, &rc);
  5034. FillRect(hdc, &rc, (HBRUSH)(COLOR_3DFACE + 1));
  5035. }
  5036. if (uMsg == WM_PAINT)
  5037. {
  5038. EndPaint(hwnd, &ps);
  5039. }
  5040. }
  5041. break;
  5042. case WM_ERASEBKGND:
  5043. {
  5044. if (_hTheme)
  5045. {
  5046. return 1;
  5047. }
  5048. else
  5049. {
  5050. RECT rc;
  5051. GetClientRect(hwnd, &rc);
  5052. FillRect((HDC)wParam, &rc, (HBRUSH)(COLOR_3DFACE + 1));
  5053. }
  5054. }
  5055. // this keeps our window from comming to the front on button down
  5056. // instead, we activate the window on the up click
  5057. // we only want this for the tree and the view window
  5058. // (the view window does this itself)
  5059. case WM_MOUSEACTIVATE:
  5060. {
  5061. POINT pt;
  5062. RECT rc;
  5063. GetCursorPos(&pt);
  5064. GetWindowRect(_hwnd, &rc);
  5065. if ((LOWORD(lParam) == HTCLIENT) && PtInRect(&rc, pt))
  5066. return MA_NOACTIVATE;
  5067. else
  5068. goto DoDefault;
  5069. }
  5070. case WM_SETFOCUS:
  5071. _OnSetFocus();
  5072. break;
  5073. case WM_VSCROLL:
  5074. return _HandleScroll(FALSE, LOWORD(wParam), HIWORD(wParam));
  5075. case WM_HSCROLL:
  5076. return _HandleScroll(TRUE, LOWORD(wParam), HIWORD(wParam));
  5077. case WM_NOTIFY:
  5078. return _HandleNotify((LPNMHDR)lParam);
  5079. case WM_NCHITTEST:
  5080. lres = DefWindowProc(hwnd, uMsg, wParam, lParam);
  5081. if (lres == HTVSCROLL || lres == HTHSCROLL)
  5082. return lres;
  5083. else
  5084. return HTTRANSPARENT;
  5085. case WM_TIMER:
  5086. switch (wParam)
  5087. {
  5088. case IDT_RECHECKRUDEAPP1:
  5089. case IDT_RECHECKRUDEAPP2:
  5090. case IDT_RECHECKRUDEAPP3:
  5091. case IDT_RECHECKRUDEAPP4:
  5092. case IDT_RECHECKRUDEAPP5:
  5093. {
  5094. HWND hwnd = _FindRudeApp(NULL);
  5095. _ptray->HandleFullScreenApp(hwnd);
  5096. if (hwnd)
  5097. {
  5098. DWORD dwStyleEx = GetWindowLongPtr(hwnd, GWL_EXSTYLE);
  5099. if (!(dwStyleEx & WS_EX_TOPMOST) && !_IsRudeWindowActive(hwnd))
  5100. {
  5101. SwitchToThisWindow(hwnd, TRUE);
  5102. }
  5103. }
  5104. KillTimer(_hwnd, wParam);
  5105. if (!hwnd && (wParam < IDT_RECHECKRUDEAPP5))
  5106. {
  5107. SetTimer(_hwnd, wParam + 1, 1000, NULL);
  5108. }
  5109. }
  5110. break;
  5111. case IDT_ASYNCANIMATION:
  5112. _AsyncAnimateItems();
  5113. break;
  5114. case IDT_REDRAW:
  5115. _DoRedrawWhereNeeded();
  5116. KillTimer(hwnd, IDT_REDRAW);
  5117. break;
  5118. case IDT_SYSMENU:
  5119. KillTimer(_hwnd, IDT_SYSMENU);
  5120. _HandleSysMenuTimeout();
  5121. break;
  5122. }
  5123. break;
  5124. case WM_COMMAND:
  5125. _HandleCommand(GET_WM_COMMAND_CMD(wParam, lParam), GET_WM_COMMAND_ID(wParam, lParam), GET_WM_COMMAND_HWND(wParam, lParam));
  5126. break;
  5127. case WM_THEMECHANGED:
  5128. _OpenTheme();
  5129. break;
  5130. case TBC_POSTEDRCLICK:
  5131. _FakeSystemMenu((HWND)wParam, (DWORD)lParam);
  5132. break;
  5133. case TBC_BUTTONHEIGHT:
  5134. return _GetCurButtonHeight();
  5135. case WM_CONTEXTMENU:
  5136. if (SHRestricted(REST_NOTRAYCONTEXTMENU))
  5137. {
  5138. break;
  5139. }
  5140. // if we didn't find an item to put the sys menu up for, then
  5141. // pass on the WM_CONTExTMENU message
  5142. if (!_ContextMenu((DWORD)lParam))
  5143. goto DoDefault;
  5144. break;
  5145. case TBC_SYSMENUCOUNT:
  5146. return _iSysMenuCount;
  5147. case TBC_CHANGENOTIFY:
  5148. _HandleChangeNotify(wParam, lParam);
  5149. break;
  5150. case TBC_VERIFYBUTTONHEIGHT:
  5151. _VerifyButtonHeight();
  5152. break;
  5153. case TBC_SETACTIVEALT:
  5154. _SetActiveAlt((HWND) lParam);
  5155. break;
  5156. case TBC_CANMINIMIZEALL:
  5157. return _CanMinimizeAll();
  5158. case TBC_MINIMIZEALL:
  5159. return _MinimizeAll((HWND) wParam, (BOOL) lParam);
  5160. break;
  5161. case TBC_WARNNODROP:
  5162. //
  5163. // tell the user they can't drop objects on the taskbar
  5164. //
  5165. ShellMessageBox(hinstCabinet, _hwnd,
  5166. MAKEINTRESOURCE(IDS_TASKDROP_ERROR), MAKEINTRESOURCE(IDS_TASKBAR),
  5167. MB_ICONHAND | MB_OK);
  5168. break;
  5169. case TBC_SETPREVFOCUS:
  5170. _hwndPrevFocus = (HWND)lParam;
  5171. break;
  5172. case TBC_FREEPOPUPMENUS:
  5173. DAD_ShowDragImage(FALSE);
  5174. _FreePopupMenu();
  5175. _SetCurSel(-1, TRUE);
  5176. DAD_ShowDragImage(TRUE);
  5177. break;
  5178. case TBC_MARKFULLSCREEN:
  5179. {
  5180. HWND hwndFS = (HWND)lParam;
  5181. if (IsWindow(hwndFS))
  5182. {
  5183. //
  5184. // look for the item they're talking about
  5185. //
  5186. PTASKITEM pti = _FindItemByHwnd(hwndFS);
  5187. if (pti == NULL)
  5188. {
  5189. //
  5190. // we didn't find it, so insert it now
  5191. //
  5192. pti = _GetItem(_InsertItem(hwndFS));
  5193. }
  5194. if (pti)
  5195. {
  5196. //
  5197. // mark it fullscreen/not fullscreen
  5198. //
  5199. pti->fMarkedFullscreen = BOOLIFY(wParam);
  5200. if (_IsRudeWindowActive(hwndFS))
  5201. {
  5202. //
  5203. // it's active, so tell the tray to hide/show
  5204. //
  5205. HWND hwndRude = pti->fMarkedFullscreen ? hwndFS : NULL;
  5206. _ptray->HandleFullScreenApp(hwndRude);
  5207. }
  5208. }
  5209. }
  5210. }
  5211. break;
  5212. case TBC_TASKTAB:
  5213. {
  5214. _tb.SetFocus();
  5215. int iNewIndex = 0;
  5216. int iCurIndex = max(_tb.GetHotItem(), 0);
  5217. int iCount = _tb.GetButtonCount();
  5218. if (iCount >= 2)
  5219. {
  5220. iNewIndex = iCurIndex;
  5221. do
  5222. {
  5223. iNewIndex += (int)wParam;
  5224. if (iNewIndex >= iCount)
  5225. {
  5226. iNewIndex = 0;
  5227. }
  5228. if (iNewIndex < 0)
  5229. {
  5230. iNewIndex = iCount - 1;
  5231. }
  5232. } while (_IsHidden(iNewIndex));
  5233. }
  5234. _tb.SetHotItem(iNewIndex);
  5235. }
  5236. break;
  5237. case WM_COPYDATA:
  5238. {
  5239. COPYDATASTRUCT *pcd;
  5240. pcd = (PCOPYDATASTRUCT)lParam;
  5241. if (pcd && pcd->dwData == _uCDHardError)
  5242. {
  5243. HARDERRORDATA *phed = (HARDERRORDATA *)pcd->lpData;;
  5244. if (phed)
  5245. {
  5246. return _HandleHardError(phed, pcd->cbData);
  5247. }
  5248. return 0; // 0 = not handled
  5249. }
  5250. }
  5251. //
  5252. // If its not our hard error data, then just
  5253. // fall through to default processing
  5254. //
  5255. default:
  5256. DoDefault:
  5257. if (uMsg == WM_ShellHook)
  5258. return _HandleShellHook((int)wParam, lParam);
  5259. else
  5260. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  5261. }
  5262. return 0;
  5263. }
  5264. BOOL CTaskBand::_RegisterWindowClass()
  5265. {
  5266. WNDCLASSEX wc = {0};
  5267. wc.cbSize = sizeof(wc);
  5268. if (GetClassInfoEx(hinstCabinet, c_szTaskSwClass, &wc))
  5269. return TRUE;
  5270. wc.lpszClassName = c_szTaskSwClass;
  5271. wc.lpfnWndProc = s_WndProc;
  5272. wc.cbWndExtra = sizeof(LONG_PTR);
  5273. wc.hInstance = hinstCabinet;
  5274. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  5275. wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
  5276. return RegisterClassEx(&wc);
  5277. }
  5278. int TimeSortCB(PTASKITEM p1, PTASKITEM p2, LPARAM lParam)
  5279. {
  5280. if (p1->dwTimeFirstOpened > p2->dwTimeFirstOpened)
  5281. return -1;
  5282. else
  5283. return 1;
  5284. }
  5285. int DestroyCB(PTASKITEM pti, LPVOID pData)
  5286. {
  5287. if (pti)
  5288. delete pti;
  5289. return 0;
  5290. }
  5291. void CTaskBand::_RefreshSettings()
  5292. {
  5293. BOOL fOldGlom = _fGlom;
  5294. int iOldGroupSize = _iGroupSize;
  5295. _LoadSettings();
  5296. if ((fOldGlom != _fGlom) || (iOldGroupSize != _iGroupSize))
  5297. {
  5298. CDPA<TASKITEM> dpa;
  5299. _BuildTaskList(&dpa);
  5300. if (dpa)
  5301. {
  5302. int i;
  5303. dpa.Sort(TimeSortCB, 0);
  5304. BOOL fRedraw = (BOOL)_tb.SendMessage(WM_SETREDRAW, FALSE, 0);
  5305. BOOL fAnimate = _fAnimate;
  5306. _fAnimate = FALSE;
  5307. for (i = _tb.GetButtonCount() - 1; i >= 0; i--)
  5308. {
  5309. _DeleteTaskItem(i, TRUE);
  5310. }
  5311. for (i = dpa.GetPtrCount() - 1; i >= 0 ; i--)
  5312. {
  5313. PTASKITEM pti = dpa.FastGetPtr(i);
  5314. // NOTE: HWND_TOPMOST is used to indicate that the deleted button
  5315. // is being animated. This allows the button to stay around after
  5316. // its real hwnd becomes invalid.
  5317. // Don't re-insert a button that was deleting.
  5318. if (pti->hwnd != HWND_TOPMOST)
  5319. {
  5320. _InsertItem(pti->hwnd, pti, TRUE);
  5321. }
  5322. }
  5323. dpa.Destroy();
  5324. _tb.SendMessage(WM_SETREDRAW, fRedraw, 0);
  5325. _fAnimate = fAnimate;
  5326. }
  5327. _BandInfoChanged();
  5328. }
  5329. }
  5330. void CTaskBand::_LoadSettings()
  5331. {
  5332. if (SHRestricted(REST_NOTASKGROUPING) == 0)
  5333. {
  5334. _fGlom = SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, TEXT("TaskbarGlomming"),
  5335. FALSE, TRUE);
  5336. if (_fGlom)
  5337. {
  5338. DWORD cbSize = sizeof(_fGlom);
  5339. DWORD dwDefault = GLOM_OLDEST;
  5340. SHRegGetUSValue(REGSTR_EXPLORER_ADVANCED, TEXT("TaskbarGroupSize"),
  5341. NULL, &_iGroupSize, &cbSize, FALSE, (LPBYTE)&dwDefault, sizeof(dwDefault));
  5342. }
  5343. }
  5344. else
  5345. {
  5346. _fGlom = FALSE;
  5347. }
  5348. }
  5349. BOOL CTaskBand::_ShouldMinimize(HWND hwnd)
  5350. {
  5351. BOOL fRet = FALSE;
  5352. DWORD dwStyle = GetWindowLong(hwnd, GWL_STYLE);
  5353. if (IsWindowVisible(hwnd) &&
  5354. !IsMinimized(hwnd) && IsWindowEnabled(hwnd))
  5355. {
  5356. if (dwStyle & WS_MINIMIZEBOX)
  5357. {
  5358. if ((dwStyle & (WS_CAPTION | WS_SYSMENU)) == (WS_CAPTION | WS_SYSMENU))
  5359. {
  5360. HMENU hmenu = GetSystemMenu(hwnd, FALSE);
  5361. if (hmenu)
  5362. {
  5363. // is there a sys menu and is the sc_min/maximize part enabled?
  5364. fRet = !(GetMenuState(hmenu, SC_MINIMIZE, MF_BYCOMMAND) & MF_DISABLED);
  5365. }
  5366. }
  5367. else
  5368. {
  5369. fRet = TRUE;
  5370. }
  5371. }
  5372. }
  5373. return fRet;
  5374. }
  5375. BOOL CTaskBand::_CanMinimizeAll()
  5376. {
  5377. int i;
  5378. for ( i = _tb.GetButtonCount() - 1; i >= 0; i--)
  5379. {
  5380. PTASKITEM pti = _GetItem(i);
  5381. if (_ShouldMinimize(pti->hwnd) || (pti->dwFlags & TIF_EVERACTIVEALT))
  5382. return TRUE;
  5383. }
  5384. return FALSE;
  5385. }
  5386. typedef struct MINALLDATAtag
  5387. {
  5388. CDPA<TASKITEM> dpa;
  5389. CTray* pTray;
  5390. HWND hwndDesktop;
  5391. HWND hwndTray;
  5392. BOOL fPostRaiseDesktop;
  5393. } MINALLDATA;
  5394. DWORD WINAPI CTaskBand::MinimizeAllThreadProc(void* pv)
  5395. {
  5396. LONG iAnimate;
  5397. ANIMATIONINFO ami;
  5398. MINALLDATA* pminData = (MINALLDATA*)pv;
  5399. if (pminData)
  5400. {
  5401. // turn off animiations during this
  5402. ami.cbSize = sizeof(ami);
  5403. SystemParametersInfo(SPI_GETANIMATION, sizeof(ami), &ami, FALSE);
  5404. iAnimate = ami.iMinAnimate;
  5405. ami.iMinAnimate = FALSE;
  5406. SystemParametersInfo(SPI_SETANIMATION, sizeof(ami), &ami, FALSE);
  5407. //
  5408. //EnumWindows(MinimizeEnumProc, 0);
  5409. // go through the tab control and minimize them.
  5410. // don't do enumwindows because we only want to minimize windows
  5411. // that are restorable via the tray
  5412. for (int i = pminData->dpa.GetPtrCount() - 1; i >= 0 ; i--)
  5413. {
  5414. PTASKITEM pti = pminData->dpa.FastGetPtr(i);
  5415. if (pti)
  5416. {
  5417. // we do the whole minimize on its own thread, so we don't do the showwindow
  5418. // async. this allows animation to be off for the full minimize.
  5419. if (_ShouldMinimize(pti->hwnd))
  5420. {
  5421. ShowWindow(pti->hwnd, SW_SHOWMINNOACTIVE);
  5422. }
  5423. else if (pti->dwFlags & TIF_EVERACTIVEALT)
  5424. {
  5425. SHAllowSetForegroundWindow(pti->hwnd);
  5426. SendMessage(pti->hwnd, WM_SYSCOMMAND, SC_MINIMIZE, -1);
  5427. }
  5428. }
  5429. }
  5430. pminData->pTray->CheckWindowPositions();
  5431. pminData->dpa.DestroyCallback(DestroyCB, NULL);
  5432. if (pminData->fPostRaiseDesktop)
  5433. {
  5434. PostMessage(pminData->hwndDesktop, DTM_RAISE, (WPARAM)pminData->hwndTray, DTRF_RAISE);
  5435. }
  5436. delete pminData;
  5437. // restore animations state
  5438. ami.iMinAnimate = iAnimate;
  5439. SystemParametersInfo(SPI_SETANIMATION, sizeof(ami), &ami, FALSE);
  5440. }
  5441. return 0;
  5442. }
  5443. void CTaskBand::_BuildTaskList(CDPA<TASKITEM>* pdpa )
  5444. {
  5445. if (pdpa && _tb)
  5446. {
  5447. if (pdpa->Create(5))
  5448. {
  5449. for (int i = _tb.GetButtonCount() - 1; (i >= 0) && ((HDPA)pdpa); i--)
  5450. {
  5451. PTASKITEM pti = _GetItem(i);
  5452. if (pti->hwnd)
  5453. {
  5454. PTASKITEM ptiNew = new TASKITEM(pti);
  5455. if (ptiNew)
  5456. {
  5457. pdpa->AppendPtr(ptiNew);
  5458. }
  5459. else
  5460. {
  5461. pdpa->DestroyCallback(DestroyCB, NULL);
  5462. }
  5463. }
  5464. }
  5465. }
  5466. else
  5467. {
  5468. pdpa->Destroy();
  5469. }
  5470. }
  5471. }
  5472. BOOL CTaskBand::_MinimizeAll(HWND hwndTray, BOOL fPostRaiseDesktop)
  5473. {
  5474. BOOL fFreeMem = TRUE;
  5475. // might want to move this into MinimizeAllThreadProc (to match
  5476. // _ptray->CheckWindowPositions). but what if CreateThread fails?
  5477. _ptray->SaveWindowPositions(IDS_MINIMIZEALL);
  5478. MINALLDATA* pminData = new MINALLDATA;
  5479. if (pminData)
  5480. {
  5481. _BuildTaskList(&(pminData->dpa));
  5482. if (pminData->dpa)
  5483. {
  5484. pminData->pTray = _ptray;
  5485. pminData->fPostRaiseDesktop = fPostRaiseDesktop;
  5486. pminData->hwndDesktop = v_hwndDesktop;
  5487. pminData->hwndTray = hwndTray;
  5488. // MinimizeAllThreadProc is responsible for freeing this data
  5489. fFreeMem = !SHCreateThread(MinimizeAllThreadProc, (void*)pminData, CTF_INSIST, NULL);
  5490. }
  5491. }
  5492. if (fFreeMem)
  5493. {
  5494. if (pminData)
  5495. {
  5496. pminData->dpa.DestroyCallback(DestroyCB, NULL);
  5497. delete pminData;
  5498. }
  5499. }
  5500. return !fFreeMem;
  5501. }
  5502. int CTaskBand::_HitTest(POINTL ptl)
  5503. {
  5504. POINT pt = {ptl.x,ptl.y};
  5505. _tb.ScreenToClient(&pt);
  5506. int iIndex = _tb.HitTest(&pt);
  5507. if ((iIndex >= _tb.GetButtonCount()) || (iIndex < 0))
  5508. iIndex = -1;
  5509. return iIndex;
  5510. }
  5511. HRESULT CTaskBand_CreateInstance(IUnknown* punkOuter, IUnknown** ppunk)
  5512. {
  5513. HRESULT hr = E_OUTOFMEMORY;
  5514. if (punkOuter)
  5515. return CLASS_E_NOAGGREGATION;
  5516. CTaskBand* ptb = new CTaskBand();
  5517. if (ptb)
  5518. {
  5519. hr = ptb->Init(&c_tray);
  5520. if (SUCCEEDED(hr))
  5521. {
  5522. *ppunk = static_cast<IDeskBand*>(ptb);
  5523. hr = S_OK;
  5524. }
  5525. }
  5526. return hr;
  5527. }