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

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