task.c
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 70k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. #include "cabinet.h"
  2. #include "rcids.h"
  3. #include <trayp.h>
  4. #include "taskband.h"
  5. #include "bandsite.h"
  6. #include "mmhelper.h" // Multimonitor helper functions
  7. #include "apithk.h"
  8. extern TRAYSTUFF g_ts;
  9. void RunSystemMonitor(void);
  10. #define HSHELL_OVERFLOW (WM_USER+42)        // BUGBUG - BobDay move to WINUSER.W
  11. #ifdef WINNT
  12. #define USE_SYSMENU_TIMEOUT
  13. #endif
  14. typedef struct {
  15.     TC_ITEM tcitem;
  16.     DWORD   dwFlags;
  17. } TASKTABITEM, * PTASKTABITEM;
  18. #define RECTWIDTH(rc)   ((rc).right-(rc).left)
  19. #define RECTHEIGHT(rc)  ((rc).bottom-(rc).top)
  20. #define ResizeWindow(hwnd, cWidth, cHeight) 
  21.     SetWindowPos(hwnd, 0, 0, 0, cWidth, cHeight, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER)
  22. #define IDC_FOLDERTABS             1
  23. #define TIF_RENDERFLASHED       0x000000001
  24. #define TIF_SHOULDTIP           0x000000002
  25. #define TIF_ACTIVATEALT         0x000000004
  26. #define TIF_EVERACTIVEALT       0x000000008
  27. #define TIF_FLASHING            0x000000010
  28. HFONT g_hfontCapBold = NULL;
  29. HFONT g_hfontCapNormal = NULL;
  30. void SetWindowStyleBit(HWND hWnd, DWORD dwBit, DWORD dwValue);
  31. CTasks g_tasks = {0};
  32. #define IDT_SYSMENU 2
  33. #define IDT_RECHECKRUDEAPP 3
  34. #define TIMEOUT_SYSMENU         2000
  35. #define TIMEOUT_SYSMENU_HUNG    125
  36. void UpdateButtonSize(PTasks ptasks);
  37. BOOL Task_SwitchToSelection(PTasks ptasks);
  38. LRESULT Task_HandleWinIniChange(PTasks ptasks, WPARAM wParam, LPARAM lParam, BOOL fDeferBandsiteChange);
  39. void Task_VerifyButtonHeight(PTasks ptasks);
  40. void Cabinet_InitGlobalMetrics(WPARAM, LPTSTR);
  41. void SHAllowSetForegroundWindow(HWND hwnd);
  42. DWORD Tray_GetStuckPlace();
  43. void Tray_HandleFullScreenApp(HWND hwnd);
  44. BOOL ShouldMinMax(HWND hwnd, BOOL fMin);
  45. void LowerDesktop();
  46. void Tray_Unhide();
  47. void RedrawItem(PTasks ptasks, HWND hwndShell, WPARAM code );
  48. void Task_ScrollIntoView(PTasks ptasks);
  49. HWND Task_FindRudeApp(PTasks ptasks);
  50. BOOL Window_IsNormal(HWND hwnd)
  51. {
  52.     return (hwnd != v_hwndTray) && (hwnd != v_hwndDesktop) && IsWindow(hwnd);
  53. }
  54. //======================================================================
  55. //
  56. HWND TabCtrl_GetItemHwnd(HWND hwnd, int i)
  57. {
  58.     TASKTABITEM item;
  59.     item.tcitem.lParam = 0;
  60.     item.tcitem.mask = TCIF_PARAM;
  61.     TabCtrl_GetItem(hwnd, i, (TC_ITEM *)&item);
  62.     return (HWND)item.tcitem.lParam;
  63. }
  64. DWORD TabCtrl_GetItemFlags(HWND hwnd, int i)
  65. {
  66.     TASKTABITEM item;
  67.     item.tcitem.mask = TCIF_PARAM;
  68.     TabCtrl_GetItem(hwnd, i, (TC_ITEM *)&item);
  69.     return item.dwFlags;
  70. }
  71. void TabCtrl_SetItemFlags(HWND hwnd, int i, DWORD dwFlags)
  72. {
  73.     TASKTABITEM item;
  74.     item.tcitem.mask = TCIF_PARAM;
  75.     if (TabCtrl_GetItem(hwnd, i, (TC_ITEM *)&item)) {
  76.         item.dwFlags = dwFlags;
  77.         TabCtrl_SetItem(hwnd, i, (TC_ITEM *)&item);
  78.     }
  79. }
  80. int FindItem(PTasks ptasks, HWND hwnd)
  81. {
  82.     int iMax;
  83.     int i;
  84.     iMax = TabCtrl_GetItemCount(ptasks->hwndTab);
  85.     for ( i = 0; i < iMax; i++)
  86.     {
  87.         HWND hwndTask = TabCtrl_GetItemHwnd(ptasks->hwndTab, i);
  88.         if (hwndTask == hwnd) {
  89.             return i;
  90.         }
  91.     }
  92.     return -1;
  93. }
  94. void CheckNeedScrollbars(PTasks ptasks, int cyRow, int cItems, int iCols, int iRows,
  95.                                      int iItemWidth, LPRECT lprcView)
  96. {
  97.     DWORD dwStuck = Tray_GetStuckPlace();
  98.     SCROLLINFO si;
  99.     RECT rcTabs;
  100.     int cxRow = iItemWidth + g_cxTabSpace;
  101.     int iVisibleColumns = ((RECTWIDTH(*lprcView) + g_cxTabSpace) / cxRow);
  102.     int iVisibleRows = ((RECTHEIGHT(*lprcView) + g_cyTabSpace) / cyRow);
  103.     int x,y, cx,cy;
  104.     rcTabs = *lprcView;
  105.     if (!iVisibleColumns)
  106.         iVisibleColumns = 1;
  107.     if (!iVisibleRows)
  108.         iVisibleRows = 1;
  109.     si.cbSize = SIZEOF(SCROLLINFO);
  110.     si.fMask = SIF_PAGE | SIF_RANGE;
  111.     si.nMin = 0;
  112.     si.nPage = 0;
  113.     si.nPos = 0;
  114.     if (STUCK_HORIZONTAL(dwStuck)) {
  115.         // do vertical scrollbar
  116.         // -1 because it's 0 based.
  117.         si.nMax = (cItems + iVisibleColumns - 1) / iVisibleColumns  -1 ;
  118.         si.nPage = iVisibleRows;
  119.         // we're actually going to need the scrollbars
  120.         if (si.nPage <= (UINT)si.nMax) {
  121.             // this effects the vis columns and therefore nMax and nPage
  122.             rcTabs.right -= g_cxVScroll;
  123.             iVisibleColumns = ((RECTWIDTH(rcTabs) + g_cxTabSpace) / cxRow);
  124.             if (!iVisibleColumns)
  125.                 iVisibleColumns = 1;
  126.             si.nMax = (cItems + iVisibleColumns - 1) / iVisibleColumns  -1 ;
  127.         }
  128.         SetScrollInfo(ptasks->hwnd, SB_VERT, &si, TRUE);
  129.         si.fMask = SIF_POS | SIF_PAGE | SIF_RANGE;
  130.         GetScrollInfo(ptasks->hwnd, SB_VERT, &si);
  131.         x = 0;
  132.         y = -si.nPos * cyRow;
  133.         cx = cxRow * iVisibleColumns;
  134.         // +1 because si.nMax is zero based
  135.         cy = cyRow * (si.nMax +1);
  136.         // nuke the other scroll bar
  137.         si.nMax = 0;
  138.         si.nPos = 0;
  139.         si.nMin = 0;
  140.         si.nPage = 0;
  141.         SetScrollInfo(ptasks->hwnd, SB_HORZ, &si, TRUE);
  142.     } else {
  143.         // do horz scrollbar
  144.         si.nMax = iCols -1;
  145.         si.nPage = iVisibleColumns;
  146.         // we're actually going to need the scrollbars
  147.         if (si.nPage <= (UINT)si.nMax) {
  148.             // this effects the vis columns and therefore nMax and nPage
  149.             rcTabs.bottom -= g_cyHScroll;
  150.             iVisibleRows = ((RECTHEIGHT(rcTabs) + g_cyTabSpace) / cyRow);
  151.             if (!iVisibleRows)
  152.                 iVisibleRows = 1;
  153.             si.nMax = (cItems + iVisibleRows - 1) / iVisibleRows  -1 ;
  154.         }
  155.         SetScrollInfo(ptasks->hwnd, SB_HORZ, &si, TRUE);
  156.         si.fMask = SIF_POS | SIF_PAGE | SIF_RANGE;
  157.         GetScrollInfo(ptasks->hwnd, SB_HORZ, &si);
  158.         y = 0;
  159.         x = -si.nPos * cxRow;
  160.         cx = cxRow * (si.nMax + 1);
  161.         cy = cyRow * iVisibleRows;
  162.         // nuke the other scroll bar
  163.         si.nMax = 0;
  164.         si.nPos = 0;
  165.         si.nMin = 0;
  166.         si.nPage = 0;
  167.         SetScrollInfo(ptasks->hwnd, SB_VERT, &si, TRUE);
  168.     }
  169.     SetWindowPos(ptasks->hwndTab, 0, x,y, cx, cy, SWP_NOACTIVATE| SWP_NOZORDER);
  170. }
  171. void NukeScrollbars(PTasks ptasks)
  172. {
  173.     SCROLLINFO si;
  174.     si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
  175.     si.cbSize = SIZEOF(SCROLLINFO);
  176.     si.nMin = 0;
  177.     si.nMax = 0;
  178.     si.nPage = 0;
  179.     si.nPos = 0;
  180.     SetScrollInfo(ptasks->hwnd, SB_VERT, &si, TRUE);
  181.     SetScrollInfo(ptasks->hwnd, SB_HORZ, &si, TRUE);
  182. }
  183. //======================================================================
  184. // this checks the size of the tab's items.  it shrinks them to
  185. // make sure that all are visible on the window.  min size is smallicon size
  186. // plus 3*xedge.  max size is small icons size.
  187. void CheckSize(PTasks ptasks, BOOL fForceResize)
  188. {
  189.     RECT rc;
  190.     int iWinWidth;
  191.     int iWinHeight;
  192.     int cItems;
  193.     int iIdeal;
  194.     int cyRow;
  195.     int iMax, iMin;
  196.     int iRows;
  197.     int iOldWidth;
  198.     RECT rcItem;
  199.     GetWindowRect(ptasks->hwnd, &rc);
  200.     if (IsRectEmpty(&rc) || !(GetWindowLong(ptasks->hwndTab, GWL_STYLE) & WS_VISIBLE))
  201.         return;
  202.     iWinWidth = RECTWIDTH(rc);
  203.     iWinHeight = RECTHEIGHT(rc);
  204.     cItems = TabCtrl_GetItemCount(ptasks->hwndTab);
  205.     TabCtrl_GetItemRect(ptasks->hwndTab, 0, &rcItem);
  206.     iOldWidth = RECTWIDTH(rcItem);
  207.     // we need to add the iButtonSpace on the nominator because there are n-1 spaces.
  208.     // we need to add the iButtonSpace in the denominator because that's the full height
  209.     // of a row
  210.     cyRow = RECTHEIGHT(rcItem) + g_cyTabSpace;
  211.     iRows = (iWinHeight + g_cyTabSpace) / cyRow;
  212.     if (iRows == 0) iRows = 1;
  213.     if (cItems) {
  214.         DWORD dwStuck;
  215.         // interbutton spacing by the tabs
  216.         int iCols;
  217.         // We need to round up so that iCols is the smallest number such that
  218.         // iCols*iRows >= cItems
  219.         iCols = (cItems + iRows - 1) / iRows;
  220.         iIdeal = (iWinWidth / iCols) - g_cxTabSpace;
  221.         dwStuck = Tray_GetStuckPlace();
  222.         // now check if we want to bail..
  223.         // bail if we're increasing the width, but not by very much
  224.         //
  225.         // use the ideal width for this calculation
  226.         if (STUCK_HORIZONTAL(dwStuck) && !fForceResize && (iOldWidth < iIdeal) && ((iOldWidth / (iIdeal - iOldWidth)) >= 3)) {
  227.             return;
  228.         }
  229.         if (STUCK_HORIZONTAL(dwStuck))
  230.             iMax = g_cxMinimized;
  231.         else
  232.             iMax = iWinWidth;
  233.         iMin = g_cySize + 2*g_cxEdge;
  234.         iIdeal = min(iMax, iIdeal);
  235.         iIdeal = max(iMin, iIdeal);
  236.         TabCtrl_SetItemSize(ptasks->hwndTab, iIdeal, g_cySize + 2 * g_cyEdge);
  237.         // if we're forced to the minimum size, then we may need some scrollbars
  238.         if (iIdeal == iMin) {
  239.             CheckNeedScrollbars(ptasks, cyRow, cItems, iCols, iRows, iIdeal, &rc);
  240.         } else {
  241.             NukeScrollbars(ptasks);
  242.             SetWindowPos(ptasks->hwndTab, 0, 0, 0,iWinWidth, iWinHeight, SWP_NOACTIVATE | SWP_NOZORDER);
  243.         }
  244.     } else {
  245.         TabCtrl_SetItemSize(ptasks->hwndTab, g_cxMinimized, g_cySize + 2 * g_cyEdge);
  246.     }
  247. }
  248. void Task_UpdateFlashingFlag(HWND hwnd)
  249. {
  250.     // Loop through the tab items, see if any have TIF_FLASHING
  251.     // set, and update the global traystuff flashing flag.
  252.     int cItems = TabCtrl_GetItemCount(hwnd);
  253.     while (cItems--)
  254.     {
  255.         DWORD dwFlags = TabCtrl_GetItemFlags(hwnd, cItems);
  256.         if (dwFlags & TIF_FLASHING)
  257.         {
  258.             g_ts.fFlashing = TRUE;
  259.             return;
  260.         }
  261.     }
  262.     g_ts.fFlashing = FALSE;
  263. }
  264. //---------------------------------------------------------------------------
  265. // Delete an item from the listbox but resize the buttons if needed.
  266. void Task_DeleteItem(PTasks ptasks, UINT i)
  267. {
  268.     //ImageList... delete icon
  269.     TabCtrl_DeleteItem(ptasks->hwndTab, i); // delete first to avoid refresh
  270.     // Update the global traystuff flag that says, "There is an item flashing."
  271.     Task_UpdateFlashingFlag(ptasks->hwndTab);
  272.     CheckSize(ptasks, FALSE);
  273. }
  274. void Task_RealityCheck(HWND hwndView)
  275. {
  276.     PTasks ptasks = GetWindowPtr(hwndView, 0);
  277.     if (ptasks)
  278.     {
  279.         //
  280.         // Delete any buttons corresponding to non-existent windows.
  281.         //
  282.         int iItem = TabCtrl_GetItemCount(ptasks->hwndTab);
  283.         while (--iItem >= 0)
  284.         {
  285.             HWND hwnd = TabCtrl_GetItemHwnd(ptasks->hwndTab, iItem);
  286.             if (!IsWindow(hwnd))
  287.                 Task_DeleteItem(ptasks, iItem);
  288.         }
  289.     }
  290. }
  291. //---------------------------------------------------------------------------
  292. // Insert an item into the listbox but resize the buttons if required.
  293. int Task_InsertItem(PTasks ptasks, HWND hwndTask)
  294. {
  295.     TASKTABITEM ti;
  296.     ti.tcitem.mask = TCIF_PARAM;
  297.     ti.tcitem.lParam = (LPARAM)hwndTask;
  298.     ti.dwFlags = 0L;
  299.     TabCtrl_InsertItem(ptasks->hwndTab, 0x7FFF, (TC_ITEM*)&ti);
  300.     CheckSize(ptasks, FALSE);
  301.     return TRUE;
  302. }
  303. //---------------------------------------------------------------------------
  304. // Adds the given window to the task list.
  305. // Returns it's position in the list or -1 of there's a problem.
  306. // NB No check is made to see if it's already in the list.
  307. int Task_AddWindow(PTasks ptasks, HWND hwnd)
  308. {
  309.     int iInsert = -1;
  310.     if (Window_IsNormal(hwnd))
  311.     {
  312.         // Button.
  313.         if (FindItem(ptasks, hwnd) != -1)
  314.             return -1;
  315.         if(Task_InsertItem(ptasks, hwnd) == 0)
  316.             return -1;
  317.     }
  318.     return iInsert;
  319. }
  320. //---------------------------------------------------------------------------
  321. // If the given window is in the task list then it is selected.
  322. // If it's not in the list then it is added.
  323. int Task_SelectWindow(PTasks ptasks, HWND hwnd)
  324. {
  325.     int i;      // Initialize to zero for the empty case
  326.     int iCurSel;
  327.     // Are there any items?
  328.     // Some item has the focus, is it selected?
  329.     iCurSel = TabCtrl_GetCurSel(ptasks->hwndTab);
  330.     i = -1;
  331.     // We aren't highlighting the correct task. Find it.
  332.     if (IsWindow(hwnd)) {
  333.         i = FindItem(ptasks, hwnd);
  334.         if (i == -1) {
  335.             // Didn't find it - better add it now.
  336.             i = Task_AddWindow(ptasks, hwnd);
  337.         } else if (i == iCurSel) {
  338.             return i; // the current one is already selected
  339.         }
  340.     }
  341.     // passing -1 is ok
  342.     TabCtrl_SetCurSel(ptasks->hwndTab, i);
  343.     
  344.     return i;
  345. }
  346. //---------------------------------------------------------------------------
  347. // Set the focus to the given window
  348. // If fAutomin is set the old task will be re-minimising if it was restored
  349. // during the last switch_to.
  350. void Task_SwitchToWindow(PTasks ptasks, HWND hwnd)
  351. {
  352.     // use GetLastActivePopup (if it's a visible window) so we don't change
  353.     // what child had focus all the time
  354.     HWND hwndLastActive = GetLastActivePopup(hwnd);
  355.     if (IsWindowVisible(hwndLastActive))
  356.         hwnd = hwndLastActive;
  357.     SwitchToThisWindow(hwnd, TRUE);
  358. }
  359. void CALLBACK _FakeSystemMenuCallback(HWND hwnd, UINT uiMsg,
  360.                                 ULONG_PTR dwData, LRESULT result)
  361. {
  362.     PTasks ptasks = (PTasks)dwData;
  363.     KillTimer(ptasks->hwnd, IDT_SYSMENU);
  364.     //
  365.     // Since we fake system menu's sometimes, we can come through here
  366.     // 1 or 2 times per system menu request (once for the real one and
  367.     // once for the fake one).  Only decrement it down to 0. Don't go neg.
  368.     //
  369.     if (ptasks->iSysMenuCount)      // Decrement it if any outstanding...
  370.         ptasks->iSysMenuCount--;
  371.     ptasks->dwPos = 0;          // Indicates that we aren't doing a menu now
  372.     if (ptasks->iSysMenuCount <= 0) {
  373.         SendMessage(g_ts.hwndTrayTips, TTM_ACTIVATE, TRUE, 0L);
  374.     }
  375. }
  376. #ifdef USE_SYSMENU_TIMEOUT
  377. HWND CreateFakeWindow(HWND hwndOwner)
  378. {
  379.     WNDCLASSEX wc;
  380.     if (!GetClassInfoEx(hinstCabinet, TEXT("_ExplorerFakeWindow"), &wc)) {
  381.         ZeroMemory(&wc, sizeof(wc));
  382.         wc.cbSize = sizeof(wc);
  383.         wc.lpfnWndProc = DefWindowProc;
  384.         wc.hInstance = hinstCabinet;
  385.         wc.lpszClassName = TEXT("_ExplorerFakeWindow");
  386.         RegisterClassEx(&wc);
  387.     }
  388.     return CreateWindow(TEXT("_ExplorerFakeWindow"), NULL, WS_POPUP | WS_SYSMENU, 
  389.             0, 0, 0, 0, hwndOwner, NULL, hinstCabinet, NULL);
  390. }
  391. void _HandleSysMenuTimeout(PTasks ptasks)
  392. {
  393.     HMENU   hPopup;
  394.     HWND    hwndTask = ptasks->hwndSysMenu;
  395.     DWORD   dwPos = ptasks->dwPos;
  396.     HWND    hwndFake = NULL;
  397.     KillTimer(ptasks->hwnd, IDT_SYSMENU);
  398.     hPopup = GetSystemMenu(hwndTask, FALSE);
  399.     // This window doesn't have the system menu. Since this window
  400.     // is hung, let's fake one so the user can still close it.
  401.     if (hPopup == NULL) {
  402.         if ((hwndFake = CreateFakeWindow(ptasks->hwnd)) != NULL) {
  403.             hPopup = GetSystemMenu(hwndFake, FALSE);
  404.         }
  405.     }
  406.     if (hPopup)
  407.     {
  408.         //
  409.         // Disable everything on the popup menu _except_ close
  410.         //
  411.         int cItems = GetMenuItemCount(hPopup);
  412.         int iItem  = 0;
  413. #ifdef WINNT
  414.         BOOL fMinimize = ShouldMinMax(hwndTask, TRUE);
  415. #endif
  416.         for (; iItem < cItems; iItem++)
  417.         {
  418.             UINT ID = GetMenuItemID(hPopup, iItem);
  419. #ifdef WINNT
  420.             // Leave the minimize item as is. NT allows
  421.             // hung-window minimization.
  422.             if (ID == SC_MINIMIZE && fMinimize) {
  423.                 continue;
  424.             }
  425. #endif
  426.             if (ID != SC_CLOSE)
  427.             {
  428.                 EnableMenuItem(hPopup, iItem, MF_BYPOSITION | MF_GRAYED);
  429.             }
  430.         }
  431.         // BUGBUG (RAID 10667) Until this user bug is fixed, we
  432.         // must be the foreground window
  433.         SetForegroundWindow(ptasks->hwnd);
  434.         SetFocus(ptasks->hwnd);
  435.         TrackPopupMenu(hPopup,
  436.                        TPM_RIGHTBUTTON,
  437.                        GET_X_LPARAM(dwPos), GET_Y_LPARAM(dwPos),
  438.                        0,
  439.                        ptasks->hwnd,
  440.                        NULL);
  441.     }
  442.     // Destroy the fake window
  443.     if (hwndFake != NULL) {
  444.         DestroyWindow(hwndFake);
  445.     }
  446.     // Turn back on tooltips
  447.     _FakeSystemMenuCallback(hwndTask, WM_SYSMENU, (ULONG_PTR)ptasks, 0);
  448. }
  449. void _HandleSysMenu( PTasks ptasks, HWND hwnd )
  450. {
  451.     //
  452.     // At this point, USER32 just told us that the app is now about to bring
  453.     // up its own system menu.  We can therefore put away our fake system
  454.     // menu.
  455.     //
  456.     DefWindowProc(ptasks->hwnd, WM_CANCELMODE, 0, 0);   // Close menu
  457.     KillTimer(ptasks->hwnd, IDT_SYSMENU);
  458. }
  459. #endif
  460. void Task_FakeSystemMenu(PTasks ptasks, HWND hwndTask, DWORD dwPos)
  461. {
  462.     DWORD   dwTimeout = TIMEOUT_SYSMENU;
  463.     if (ptasks->iSysMenuCount <= 0) {
  464.         SendMessage(g_ts.hwndTrayTips, TTM_ACTIVATE, FALSE, 0L);
  465.     }
  466.     // HACKHACK: sleep to give time to switch to them.  (user needs this... )
  467.     Sleep(20);
  468. #ifdef USE_SYSMENU_TIMEOUT
  469.     //
  470.     // ** Advanced System Menu functionality **
  471.     //
  472.     // If the app doesn't put up its system menu within a reasonable timeout,
  473.     // then we popup a fake menu for it anyway.  Suppport for this is required
  474.     // in USER32 (basically it needs to tell us when to turn off our timeout
  475.     // timer).
  476.     //
  477.     // If the user-double right-clicks on the task bar, they get a really
  478.     // short timeout.  If the app is already hung, then they get a really
  479.     // short timeout.  Otherwise, they get the relatively long timeout.
  480.     //
  481.     if (ptasks->dwPos != 0)     // 2nd right-click (on a double-right click)
  482.         dwTimeout = TIMEOUT_SYSMENU_HUNG;
  483.     //
  484.     // We check to see if the app in question is hung, and if so, simulate
  485.     // speed up the timeout process.  It will happen soon enough.
  486.     //
  487. #ifdef WINNT
  488.     if (IsHungAppWindow(hwndTask))
  489. #else
  490.     if (IsHungThread(GetWindowThreadProcessId(hwndTask, NULL)))
  491. #endif
  492.         dwTimeout = TIMEOUT_SYSMENU_HUNG;
  493.     ptasks->hwndSysMenu = hwndTask;
  494.     ptasks->dwPos = dwPos;
  495.     SetTimer(ptasks->hwnd, IDT_SYSMENU, dwTimeout, NULL);
  496. #endif
  497.     ptasks->iSysMenuCount++;
  498.     if (!SendMessageCallback(hwndTask, WM_SYSMENU, 0, dwPos, _FakeSystemMenuCallback, (ULONG_PTR)ptasks)) {
  499.         _FakeSystemMenuCallback(hwndTask, WM_SYSMENU, (ULONG_PTR)ptasks, 0);
  500.     }
  501. }
  502. int Task_GetSelectedItems(PTasks ptasks, HDSA hdsa)
  503. {
  504.     int iSelected = 0;
  505.     int i = TabCtrl_GetItemCount(ptasks->hwndTab) - 1;
  506.     TASKTABITEM item;
  507.     item.tcitem.mask = TCIF_PARAM | TCIF_STATE;
  508.     item.tcitem.dwStateMask = TCIS_BUTTONPRESSED;
  509.     for ( ; i >= 0; i--) {
  510.         TabCtrl_GetItem(ptasks->hwndTab, i, (LPTCITEM)&item);
  511.         if (item.tcitem.dwState & TCIS_BUTTONPRESSED) {
  512.             if (hdsa) {
  513.                 DSA_AppendItem(hdsa, &item.tcitem.lParam);
  514.             }
  515.             iSelected++;
  516.         }
  517.     }
  518.     return iSelected;
  519. }
  520. // because we dont' define WIN32_WINNT 0x0500 so that we can run downlevel for now
  521. #ifndef MDITILE_ZORDER
  522. #define MDITILE_ZORDER         0x0004
  523. #endif
  524. void Task_OnCombinedCommand(PTasks ptasks, int iRet)
  525. {
  526.     int i;
  527.     int idCmd;
  528.     HDSA hdsa = DSA_Create(SIZEOF(HWND), 4);
  529.     int iCount;
  530.     ANIMATIONINFO ami;
  531.     LONG iAnimate;
  532.     if (!hdsa) {
  533.         return;
  534.     }
  535.     iCount = Task_GetSelectedItems(ptasks, hdsa);
  536.     // turn off animiations during this
  537.     ami.cbSize = SIZEOF(ANIMATIONINFO);
  538.     SystemParametersInfo(SPI_GETANIMATION, SIZEOF(ami), &ami, FALSE);
  539.     iAnimate = ami.iMinAnimate;
  540.     ami.iMinAnimate = FALSE;
  541.     SystemParametersInfo(SPI_SETANIMATION, SIZEOF(ami), &ami, FALSE);
  542.     switch (iRet) {
  543.     case IDM_CASCADE:
  544.     case IDM_VERTTILE:
  545.     case IDM_HORIZTILE:
  546.         //AppBarNotifyAll(ABN_WINDOWARRANGE, NULL, TRUE);
  547.         for (i = 0; i < iCount; i++) {
  548.             HWND hwnd;
  549.             DSA_GetItem(hdsa, i, &hwnd);
  550.             if (IsIconic(hwnd)) {
  551.                 // this needs to by synchronous with the arrange
  552.                 ShowWindow(hwnd, SW_RESTORE);
  553.             }
  554.         }
  555.         if (iRet == IDM_CASCADE)
  556.             CascadeWindows(GetDesktopWindow(), MDITILE_ZORDER, NULL, iCount, DSA_GetItemPtr(hdsa, 0));
  557.         else {
  558.             TileWindows(GetDesktopWindow(), 
  559.                         (iRet == IDM_VERTTILE ?
  560.                                 MDITILE_VERTICAL : MDITILE_HORIZONTAL), NULL, iCount, DSA_GetItemPtr(hdsa, 0));
  561.         }
  562.         //AppBarNotifyAll(ABN_WINDOWARRANGE, NULL, FALSE);
  563.         break;
  564.     case IDM_MINIMIZE:
  565.         idCmd = SC_MINIMIZE;
  566.         goto DoSysCommand;
  567.     case IDM_RESTORE:
  568.         idCmd = SC_RESTORE;
  569.         goto DoSysCommand;
  570.     case IDM_MAXIMIZE:
  571.         idCmd = SC_MAXIMIZE;
  572.         goto DoSysCommand;
  573.     case IDM_CLOSE:
  574.         idCmd = SC_CLOSE;
  575. DoSysCommand:
  576.         for (iCount--; iCount >= 0; iCount--) {
  577.             HWND hwnd;
  578.             DSA_GetItem(hdsa, iCount, &hwnd);
  579.             if (idCmd == SC_MAXIMIZE)
  580.                 if (!ShouldMinMax(hwnd, FALSE))
  581.                     continue;
  582.             PostMessage(hwnd, WM_SYSCOMMAND, idCmd, 0L);
  583.         }
  584.         break;
  585.     }
  586.     // restore animations  state
  587.     ami.iMinAnimate = iAnimate;
  588.     SystemParametersInfo(SPI_SETANIMATION, SIZEOF(ami), &ami, FALSE);
  589.     DSA_Destroy(hdsa);
  590. }
  591. void Task_CombinedMenu(PTasks ptasks, DWORD dwPos)
  592. {
  593.     HMENU hmenu = LoadMenuPopup(MAKEINTRESOURCE(MENU_COMBINEDTASKS));
  594.     if (hmenu) {
  595.         HWND hwndLastForeground = GetForegroundWindow();
  596.         int i = TabCtrl_GetItemCount(ptasks->hwndTab) - 1;
  597.         int iRet;
  598.         TASKTABITEM item;
  599.         item.tcitem.mask = TCIF_PARAM | TCIF_STATE;
  600.         item.tcitem.dwStateMask = TCIS_BUTTONPRESSED;
  601.         for ( ; i >= 0; i--) {
  602.             TabCtrl_GetItem(ptasks->hwndTab, i, (LPTCITEM)&item);
  603.             if (item.tcitem.dwState & TCIS_BUTTONPRESSED) {
  604.                 HWND hwnd = (HWND)item.tcitem.lParam;
  605.                 HMENU hmenuSys;
  606.                 if (ShouldMinMax(hwnd, TRUE)) {
  607.                     EnableMenuItem(hmenu, IDM_MINIMIZE, MF_BYCOMMAND|MF_ENABLED);
  608.                 }
  609.                 if (IsIconic(hwnd)) {
  610.                     EnableMenuItem(hmenu, IDM_RESTORE, MF_BYCOMMAND|MF_ENABLED);
  611.                 }
  612.                 hmenuSys = GetSystemMenu(hwnd, FALSE);
  613.                 if (hmenuSys) {
  614.                     if (!(GetMenuState(hmenuSys, SC_RESTORE, MF_BYCOMMAND) & MF_DISABLED)) {
  615.                         EnableMenuItem(hmenu, IDM_RESTORE, MF_BYCOMMAND|MF_ENABLED);
  616.                     }
  617.                     if (!(GetMenuState(hmenuSys, SC_MAXIMIZE, MF_BYCOMMAND) & MF_DISABLED)) {
  618.                         EnableMenuItem(hmenu, IDM_MAXIMIZE, MF_BYCOMMAND|MF_ENABLED);
  619.                     }
  620.                     if (!(GetMenuState(hmenuSys, SC_MINIMIZE, MF_BYCOMMAND) & MF_DISABLED)) {
  621.                         EnableMenuItem(hmenu, IDM_MINIMIZE, MF_BYCOMMAND|MF_ENABLED);
  622.                     }
  623.                     if (!(GetMenuState(hmenuSys, SC_CLOSE, MF_BYCOMMAND) & MF_DISABLED)) {
  624.                         EnableMenuItem(hmenu, IDM_CLOSE, MF_BYCOMMAND|MF_ENABLED);
  625.                     }
  626.                 }
  627.             }
  628.         }
  629.         g_ts.fIgnoreTaskbarActivate = TRUE;
  630.         SetForegroundWindow(v_hwndTray);
  631.         
  632.         iRet = TrackPopupMenuEx(hmenu, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN,
  633.             GET_X_LPARAM(dwPos), GET_Y_LPARAM(dwPos),
  634.             ptasks->hwnd, NULL);
  635.         g_ts.fIgnoreTaskbarActivate = FALSE;
  636.         if (iRet) {
  637.             Task_OnCombinedCommand(ptasks, iRet);
  638.         }
  639.         SendMessage(ptasks->hwndTab, TCM_DESELECTALL, FALSE, 0);
  640.         SetForegroundWindow(v_hwndTray);
  641.         DestroyMenu(hmenu);
  642.     }
  643. }
  644. void Task_SysMenuForItem(PTasks ptasks, int i, DWORD dwPos)
  645. {
  646.     if (Task_GetSelectedItems(ptasks, NULL) > 1) {
  647.         // more than one selection... do the combined menu.
  648.         Task_CombinedMenu(ptasks, dwPos);
  649.     } else {
  650.         HWND hwndTask = TabCtrl_GetItemHwnd(ptasks->hwndTab, i);
  651.         HWND hwndProxy = hwndTask;
  652.         if (g_fDesktopRaised) {
  653.             LowerDesktop();
  654.         }
  655.         // set foreground first so that we'll switch to it.
  656.         if (IsIconic(hwndTask) && 
  657.             (TabCtrl_GetItemFlags(ptasks->hwndTab, i) & TIF_EVERACTIVEALT)) {
  658.             HWND hwndProxyT = (HWND) GetWindowLongPtr(hwndTask, 0);
  659.             if (hwndProxyT != NULL && IsWindow(hwndProxyT))
  660.                 hwndProxy = hwndProxyT;
  661.         }
  662.         SetForegroundWindow(GetLastActivePopup(hwndProxy));
  663.         if (hwndProxy != hwndTask)
  664.             SendMessage(hwndTask, WM_SYSCOMMAND, SC_RESTORE, -2);
  665.         Task_SelectWindow(ptasks, hwndTask);
  666.         PostMessage(ptasks->hwnd, TM_POSTEDRCLICK, (WPARAM)hwndTask, (LPARAM)dwPos);
  667.     }
  668. }
  669. BOOL Task_PostFakeSystemMenu(PTasks ptasks, DWORD dwPos)
  670. {
  671.     int i;
  672.     BOOL fRet;
  673.     TC_HITTESTINFO tcht;
  674.     if (dwPos != (DWORD)-1) {
  675.         tcht.pt.x = GET_X_LPARAM(dwPos);
  676.         tcht.pt.y = GET_Y_LPARAM(dwPos);
  677.         ScreenToClient(ptasks->hwndTab, &tcht.pt);
  678.         i = (int)SendMessage(ptasks->hwndTab, TCM_HITTEST, 0, (LPARAM)&tcht);
  679.     } else {
  680.         i = TabCtrl_GetCurFocus(ptasks->hwndTab);
  681.     }
  682.     fRet = (i != -1);
  683.     if (fRet) {
  684.         Task_SysMenuForItem(ptasks, i, dwPos);
  685.     }
  686.     return fRet;
  687. }
  688. LRESULT Task_HandleNotify(PTasks ptasks, LPNMHDR lpnm)
  689. {
  690.     switch (lpnm->code) {
  691.         case NM_CLICK: {
  692.             TC_HITTESTINFO hitinfo;
  693.             int i;
  694.             DWORD dwPos = GetMessagePos();
  695.             hitinfo.pt.x = GET_X_LPARAM(dwPos);
  696.             hitinfo.pt.y = GET_Y_LPARAM(dwPos);
  697.             // did the click happen on the currently selected tab?
  698.             // if so, tab ctrl isn't going to send us a message. so di it ourselves
  699.             i = TabCtrl_GetCurSel(ptasks->hwndTab);
  700.             ScreenToClient(ptasks->hwndTab, &hitinfo.pt);
  701.             if (i == TabCtrl_HitTest(ptasks->hwndTab, &hitinfo)) {
  702.                 HWND hwnd = TabCtrl_GetItemHwnd(ptasks->hwndTab, i);
  703.                 if (TabCtrl_GetItemFlags(ptasks->hwndTab, i) & TIF_EVERACTIVEALT) {
  704.                     PostMessage(hwnd, WM_SYSCOMMAND, SC_RESTORE, -1);
  705.                     break;
  706.                 }
  707.                 if (hwnd == GetForegroundWindow()) {
  708.                     if (IsIconic(hwnd))
  709.                         ShowWindowAsync(hwnd, SW_RESTORE);
  710.                 } else {
  711.                     if (IsIconic(hwnd))
  712.                         Task_SwitchToSelection(ptasks);
  713.                     else if (ShouldMinMax(hwnd, TRUE)) {
  714.                         SHAllowSetForegroundWindow(hwnd);
  715.                         PostMessage(hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0);
  716.                         PostMessage(ptasks->hwndTab, TCM_SETCURSEL, (WPARAM)-1, 0);
  717.                     }
  718.                 }
  719.             }
  720.             break;
  721.         }
  722.         case TCN_SELCHANGE:
  723.             Task_SwitchToSelection(ptasks);
  724.             break;
  725.             
  726.         case TCN_FOCUSCHANGE:
  727.             Task_ScrollIntoView(ptasks);
  728.             break;
  729.         case TTN_SHOW:
  730.             SetWindowPos(g_ts.hwndTrayTips,
  731.                          HWND_TOP,
  732.                          0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
  733.             break;
  734.         case TTN_NEEDTEXT:
  735.         {
  736.             LPTOOLTIPTEXT lpttt = (LPTOOLTIPTEXT)lpnm;
  737.             HWND hwnd;
  738.             TASKTABITEM item;
  739.             item.tcitem.lParam = 0;
  740.             item.tcitem.mask = TCIF_PARAM;
  741.             TabCtrl_GetItem(ptasks->hwndTab, lpttt->hdr.idFrom, (TC_ITEM *)&item);
  742.             hwnd = (HWND)item.tcitem.lParam;
  743.             // do an IsWindow check because user might (does) sometimes
  744.             // send us a redraw item as it's dying, and since we do
  745.             // a postmessage, we don't get it till we're dead
  746.             //DebugMsg(DM_TRACE, "NeedText for hwnd %d, dwflags = %d", hwnd, item.dwFlags);
  747.             if ((item.dwFlags & TIF_SHOULDTIP) && IsWindow(hwnd))
  748.             {
  749. #if defined(WINDOWS_ME)
  750.                 DWORD    exStyle;
  751.                 exStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
  752.                 if (exStyle & WS_EX_RTLREADING)
  753.                         lpttt->uFlags |= TTF_RTLREADING;
  754.                 else
  755.                         lpttt->uFlags &= ~TTF_RTLREADING;
  756. #endif
  757.         // BUGBUG If you're not unicode, you deserve to hang
  758. #ifdef UNICODE
  759.                 InternalGetWindowText(hwnd, lpttt->szText, ARRAYSIZE(lpttt->szText));
  760. #else
  761.                 GetWindowText(hwnd, lpttt->szText, ARRAYSIZE(lpttt->szText));
  762. #endif
  763.             }
  764.             else
  765.                 lpttt->szText[0] = 0;
  766.             break;
  767.         }
  768.     }
  769.     return 0L;
  770. }
  771. //---------------------------------------------------------------------------
  772. // Switch to a single task (Doesn't do anything if more than one item
  773. // is selected.)
  774. // Returns TRUE if a switch took place.
  775. BOOL Task_SwitchToSelection(PTasks ptasks)
  776. {
  777.     int iItem = (int)SendMessage(ptasks->hwndTab,TCM_GETCURSEL, 0, 0);
  778.     if (iItem != -1)
  779.     {
  780.         HWND hwndTask = TabCtrl_GetItemHwnd(ptasks->hwndTab, iItem);
  781.         if (Window_IsNormal(hwndTask))
  782.         {
  783.             LowerDesktop();
  784.             Task_SwitchToWindow(ptasks, hwndTask);
  785.             return TRUE;
  786.         }
  787.         else
  788.         {
  789.             // Window went away?
  790.             Task_DeleteItem(ptasks, iItem);
  791.         }
  792.     }
  793.     return FALSE;
  794. }
  795. BOOL CALLBACK Task_BuildCallback(HWND hwnd, LPARAM lParam)
  796. {
  797.     PTasks ptasks = (PTasks)lParam;
  798.     if (IsWindow(hwnd) && IsWindowVisible(hwnd) && !GetWindow(hwnd, GW_OWNER) &&
  799.         (!(GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_TOOLWINDOW))) {
  800.         Task_AddWindow(ptasks, hwnd);
  801.     }
  802.     return TRUE;
  803. }
  804. //---------------------------------------------------------------------------
  805. LRESULT _HandleCreate(HWND hwnd, LPVOID lpCreateParams)
  806. {
  807.     PTasks ptasks = lpCreateParams;
  808.     DWORD dwStyle = 0;
  809.     
  810.     SetWindowPtr(hwnd, 0, ptasks);
  811.     g_ts.hwndView = hwnd;
  812.     ptasks->hwnd = hwnd;
  813.     // Create a listbox in the client area.
  814.     ptasks->hwndTab = CreateWindow(WC_TABCONTROL, NULL,
  815.                                   WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE |
  816.                                    TCS_FOCUSNEVER | TCS_MULTISELECT | TCS_OWNERDRAWFIXED | TCS_MULTILINE | TCS_BUTTONS | TCS_FIXEDWIDTH | dwStyle,
  817.                                   0,0,0,0, hwnd, (HMENU)IDC_FOLDERTABS, hinstCabinet, NULL);
  818.     if (ptasks->hwndTab)
  819.     {
  820.         TOOLINFO ti;
  821.         TabCtrl_SetItemExtra(ptasks->hwndTab, (SIZEOF(TASKTABITEM) - SIZEOF(TC_ITEM)) + SIZEOF(HWND));
  822.         // initial size
  823.         TabCtrl_SetItemSize(ptasks->hwndTab, g_cxMinimized, g_cySize + 2 * g_cyEdge);
  824.         ti.cbSize = SIZEOF(ti);
  825.         ti.uFlags = TTF_IDISHWND;
  826.         ti.hwnd = ptasks->hwndTab;
  827.         ti.uId = (UINT_PTR)ptasks->hwndTab;
  828.         ti.lpszText = 0;
  829.         if (g_ts.hwndTrayTips) {
  830.             SendMessage(g_ts.hwndTrayTips, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);
  831.             SendMessage(ptasks->hwndTab, TCM_SETTOOLTIPS, (WPARAM)g_ts.hwndTrayTips, 0L);
  832.         }
  833.         ptasks->WM_ShellHook = RegisterWindowMessage(TEXT("SHELLHOOK"));
  834.         RegisterShellHook(hwnd, 3); // 3 = magic flag
  835.         // force getting of font, calc of metrics
  836.         Task_HandleWinIniChange(ptasks, 0, 0, TRUE);
  837.         EnumWindows(Task_BuildCallback, (LPARAM)ptasks);
  838.         return 0;       // success
  839.     }
  840.     // Failure.
  841.     return -1;
  842. }
  843. //---------------------------------------------------------------------------
  844. LRESULT _HandleDestroy(PTasks ptasks)
  845. {
  846.     RegisterShellHook(ptasks->hwnd, FALSE);
  847.     ptasks->hwnd = NULL;
  848.     return 1;
  849. }
  850. int _HandleScroll(PTasks ptasks, UINT code, int nPos, UINT sb)
  851. {
  852.     SCROLLINFO si;
  853.     si.cbSize = SIZEOF(SCROLLINFO);
  854.     si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
  855.     GetScrollInfo(ptasks->hwnd, sb, &si);
  856.     si.nMax -= (si.nPage -1);
  857.     switch (code) {
  858.         case SB_BOTTOM:
  859.             nPos = si.nMax;
  860.             break;
  861.         case SB_TOP:
  862.             nPos = 0;
  863.             break;
  864.         case SB_ENDSCROLL:
  865.             nPos = si.nPos;
  866.             break;
  867.         case SB_LINEDOWN:
  868.             nPos = si.nPos + 1;
  869.             break;
  870.         case SB_LINEUP:
  871.             nPos = si.nPos - 1;
  872.             break;
  873.         case SB_PAGEDOWN:
  874.             nPos = si.nPos + si.nPage;
  875.             break;
  876.         case SB_PAGEUP:
  877.             nPos = si.nPos - si.nPage;
  878.             break;
  879.         case SB_THUMBPOSITION:
  880.         case SB_THUMBTRACK:
  881.             break;
  882.     }
  883.     if (nPos > (int)(si.nMax))
  884.         nPos = si.nMax;
  885.     if (nPos < 0 )
  886.         nPos = 0;
  887.     SetScrollPos(ptasks->hwnd, sb, nPos, TRUE);
  888.     return nPos;
  889. }
  890. //---------------------------------------------------------------------------
  891. LRESULT _HandleVScroll(PTasks ptasks, UINT code, int nPos)
  892. {
  893.     RECT rcItem;
  894.     int cyRow;
  895.     nPos = _HandleScroll(ptasks, code, nPos, SB_VERT);
  896.     TabCtrl_GetItemRect(ptasks->hwndTab, 0, &rcItem);
  897.     cyRow = RECTHEIGHT(rcItem) + g_cyTabSpace;
  898.     SetWindowPos(ptasks->hwndTab, 0, 0, -nPos * cyRow , 0, 0, SWP_NOACTIVATE | SWP_NOSIZE |SWP_NOZORDER);
  899.     return 0;
  900. }
  901. LRESULT _HandleHScroll(PTasks ptasks, UINT code, int nPos)
  902. {
  903.     RECT rcItem;
  904.     int cxRow;
  905.     nPos = _HandleScroll(ptasks, code, nPos, SB_HORZ);
  906.     TabCtrl_GetItemRect(ptasks->hwndTab, 0, &rcItem);
  907.     cxRow = RECTWIDTH(rcItem) + g_cxTabSpace;
  908.     SetWindowPos(ptasks->hwndTab, 0, -nPos * cxRow, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE |SWP_NOZORDER);
  909.     return 0;
  910. }
  911. // after a selection is made, scroll it into view
  912. void Task_ScrollIntoView(PTasks ptasks)
  913. {
  914.     DWORD dwStyle = GetWindowLong(ptasks->hwnd, GWL_STYLE);
  915.     if (dwStyle & (WS_HSCROLL | WS_VSCROLL)) {
  916.         int iItem = (int)SendMessage(ptasks->hwndTab,TCM_GETCURFOCUS, 0, 0);
  917.         // loop until return call below
  918.         while (iItem != -1) {
  919.             RECT rc;
  920.             RECT rcWindow;
  921.             RECT rcIntersect;
  922.             TabCtrl_GetItemRect(ptasks->hwndTab, iItem, &rc);
  923.             MapWindowRect(ptasks->hwndTab, HWND_DESKTOP, &rc);
  924.             GetWindowRect(ptasks->hwnd, &rcWindow);
  925.             
  926.             if (!IntersectRect(&rcIntersect, &rc, &rcWindow)) {
  927.                 // need to scroll.  selected button not in view
  928.                 if (dwStyle & WS_HSCROLL) {
  929.                     if (rc.right < rcWindow.right)
  930.                         _HandleHScroll(ptasks, SB_LINEUP,SB_HORZ);
  931.                     else 
  932.                         _HandleHScroll(ptasks, SB_LINEDOWN,SB_HORZ);
  933.                 } else {
  934.                     if (rc.top < rcWindow.top)
  935.                         _HandleVScroll(ptasks, SB_LINEUP, SB_VERT);
  936.                     else
  937.                         _HandleVScroll(ptasks, SB_LINEDOWN, SB_VERT);
  938.                 }
  939.             } else
  940.                 return;
  941.         }
  942.     }
  943. }
  944. //---------------------------------------------------------------------------
  945. LRESULT _HandleSize(PTasks ptasks, WPARAM fwSizeType)
  946. {
  947.     // Make the listbox fill the parent;
  948.     if (fwSizeType != SIZE_MINIMIZED)
  949.     {
  950.         CheckSize(ptasks, TRUE);
  951.     }
  952.     return 0;
  953. }
  954. extern void Tray_RealityCheck();
  955. //---------------------------------------------------------------------------
  956. // Have the task list show the given window.
  957. // NB Ignore taskman itself.
  958. LRESULT Task_HandleActivate(PTasks ptasks, HWND hwndActive)
  959. {
  960.     //
  961.     // App-window activation change is a good time to do a reality
  962.     // check (make sure "always-on-top" agrees with actual window
  963.     // position, make sure there are no ghost buttons, etc).
  964.     //
  965.     Tray_RealityCheck();
  966.     if (Window_IsNormal(hwndActive))
  967.     {
  968.         int i;
  969.         DWORD dwFlags;
  970.         LowerDesktop();
  971.         i = Task_SelectWindow(ptasks, hwndActive);
  972.         dwFlags = TabCtrl_GetItemFlags(ptasks->hwndTab, i);
  973.         // Strip off TIF_FLASHING
  974.         dwFlags &= ~TIF_FLASHING;
  975.         TabCtrl_SetItemFlags(ptasks->hwndTab, i, dwFlags);
  976.         // Update the global traystuff flag that says, "There is an item flashing."
  977.         Task_UpdateFlashingFlag(ptasks->hwndTab);
  978.         // if it's flashed blue, turn it off.
  979.         if (dwFlags & TIF_RENDERFLASHED)
  980.             RedrawItem(ptasks, hwndActive, HSHELL_REDRAW);
  981.     }
  982.     else
  983.     {
  984.         if (!(g_ts.fIgnoreTaskbarActivate && GetForegroundWindow() == v_hwndTray))
  985.             TabCtrl_SetCurSel(ptasks->hwndTab, -1);
  986.     }
  987.     if (hwndActive)
  988.         g_ts.hwndLastActive = hwndActive;
  989.     return TRUE;
  990. }
  991. //---------------------------------------------------------------------------
  992. void Task_HandleOtherWindowDestroyed(PTasks ptasks, HWND hwndDestroyed)
  993. {
  994.     int i;
  995.     MSG msg;
  996.     
  997.     if (PeekMessage(&msg, NULL, TM_WINDOWDESTROYED, TM_WINDOWDESTROYED,
  998.         PM_NOREMOVE) && msg.hwnd==ptasks->hwnd)
  999.     {
  1000.         // Don's use ShowWindow(SW_HIDE) since we don't want a repaint until
  1001.         // we are all done
  1002.         SetWindowStyleBit(ptasks->hwndTab, WS_VISIBLE, 0);
  1003.     }
  1004.     else
  1005.     {
  1006.         if (!(GetWindowLong(ptasks->hwndTab, GWL_STYLE) & WS_VISIBLE))
  1007.             ShowWindow(ptasks->hwndTab, SW_SHOWNORMAL);
  1008.     }
  1009.     // Look for the destoyed window.
  1010.     i = FindItem(ptasks, hwndDestroyed);
  1011.     if (i != -1)
  1012.     {
  1013.         Task_DeleteItem(ptasks, i);
  1014.     }
  1015.     else
  1016.     {
  1017.         // If the item doesn't exist in the task list, make sure it isn't part
  1018.         // of somebody's fake SDI implementation.  Otherwise Minimize All will
  1019.         // break.
  1020.         for (i = TabCtrl_GetItemCount(ptasks->hwndTab) - 1; i >= 0; i--)
  1021.         {
  1022.             if ((TabCtrl_GetItemFlags(ptasks->hwndTab, i) & TIF_EVERACTIVEALT) &&
  1023.                 (HWND) GetWindowLongPtr(TabCtrl_GetItemHwnd(ptasks->hwndTab, i), 0) ==
  1024.                        hwndDestroyed)
  1025.             {
  1026.                 goto NoDestroy;
  1027.             }
  1028.         }
  1029.     }
  1030.     TrayHandleWindowDestroyed(hwndDestroyed);
  1031. NoDestroy:
  1032.     // This might have been a rude app.  Figure out if we've
  1033.     // got one now and have the tray sync up.
  1034.     Tray_HandleFullScreenApp(Task_FindRudeApp(ptasks));
  1035.     if (g_ts.hwndLastActive == hwndDestroyed)
  1036.     {
  1037.         if (g_ts.hwndLastActive == hwndDestroyed)
  1038.             g_ts.hwndLastActive = NULL;
  1039.     }
  1040. }
  1041. //---------------------------------------------------------------------------
  1042. void Task_HandleOverflow(PTasks ptasks)
  1043. {
  1044.     int i;
  1045.     BOOL fFoundDestroyedItems = FALSE;
  1046.     for (i = TabCtrl_GetItemCount(ptasks->hwndTab) - 1; i >= 0; i--)
  1047.     {
  1048.         HWND hwnd = TabCtrl_GetItemHwnd(ptasks->hwndTab, i);
  1049.         if (!IsWindow(hwnd))
  1050.         {
  1051.             if (!fFoundDestroyedItems)
  1052.             {
  1053.                 // Don's use ShowWindow(SW_HIDE) since we don't want a repaint until
  1054.                 // we are all done
  1055.                 SetWindowStyleBit(ptasks->hwndTab, WS_VISIBLE, 0);
  1056.                 fFoundDestroyedItems = TRUE;
  1057.             }
  1058.             // And delete this item...
  1059.             Task_DeleteItem(ptasks, i);
  1060.         }
  1061.     }
  1062.     // If we deleted items call off to destroy stuff out of save list
  1063.     if (fFoundDestroyedItems)
  1064.     {
  1065.         ShowWindow(ptasks->hwndTab, SW_SHOWNORMAL);
  1066.         TrayHandleWindowDestroyed(NULL);
  1067.     }
  1068. }
  1069. void Task_HandleOtherWindowCreated(PTasks ptasks, HWND hwndCreated)
  1070. {
  1071.     MSG msg;
  1072.     while (PeekMessage(&msg, NULL, TM_WINDOWDESTROYED, TM_WINDOWDESTROYED, PM_REMOVE)) {
  1073.         Task_HandleOtherWindowDestroyed(ptasks, (HWND)msg.lParam);
  1074.     }
  1075.     Task_AddWindow(ptasks, hwndCreated);
  1076. }
  1077. void Task_HandleGetMinRect(PTasks ptasks, HWND hwndShell, LPPOINTS lprc)
  1078. {
  1079.     int i;
  1080.     RECT rc;
  1081.     RECT rcTask;
  1082.     i = FindItem(ptasks, hwndShell);
  1083.     if (i == -1)
  1084.         return;
  1085.     // Found it in our list.
  1086.     TabCtrl_GetItemRect(ptasks->hwndTab, i, &rc);
  1087.     //
  1088.     // If the Tab is mirrored then let's retreive the screen coordinates
  1089.     // by calculating from the left edge of the screen since screen coordinates 
  1090.     // are not mirrored so that minRect will prserve its location. [samera]
  1091.     //
  1092.     if (IS_WINDOW_RTL_MIRRORED(GetDesktopWindow())) {  
  1093.         RECT rcTab;
  1094.         GetWindowRect( ptasks->hwndTab , &rcTab );
  1095.         rc.left   += rcTab.left;
  1096.         rc.right  += rcTab.left;
  1097.         rc.top    += rcTab.top;
  1098.         rc.bottom += rcTab.top;
  1099.     }
  1100.     else
  1101.     {
  1102.         MapWindowPoints(ptasks->hwndTab, HWND_DESKTOP, (LPPOINT)&rc, 2);
  1103.     }
  1104.     lprc[0].x = (short)rc.left;
  1105.     lprc[0].y = (short)rc.top;
  1106.     lprc[1].x = (short)rc.right;
  1107.     lprc[1].y = (short)rc.bottom;
  1108.     // make sure the rect is within out client area
  1109.     GetClientRect(ptasks->hwnd, &rcTask);
  1110.     MapWindowPoints(ptasks->hwnd, HWND_DESKTOP, (LPPOINT)&rcTask, 2);
  1111.     if (lprc[0].x < rcTask.left) {
  1112.         lprc[1].x = lprc[0].x = (short)rcTask.left;
  1113.         lprc[1].x++;
  1114.     }
  1115.     if (lprc[0].x > rcTask.right) {
  1116.         lprc[1].x = lprc[0].x = (short)rcTask.right;
  1117.         lprc[1].x++;
  1118.     }
  1119.     if (lprc[0].y < rcTask.top) {
  1120.         lprc[1].y = lprc[0].y = (short)rcTask.top;
  1121.         lprc[1].y++;
  1122.     }
  1123.     if (lprc[0].y > rcTask.bottom) {
  1124.         lprc[1].y = lprc[0].y = (short)rcTask.bottom;
  1125.         lprc[1].y++;
  1126.     }
  1127. }
  1128. BOOL Task_IsItemActive(PTasks ptasks, int iItem)
  1129. {
  1130.     HWND hwnd = GetForegroundWindow();
  1131.     HWND hwndItem = TabCtrl_GetItemHwnd(ptasks->hwndTab, iItem);
  1132.     return (hwnd && hwnd == hwndItem);
  1133. }
  1134. // bugbug, move this to shellp.h
  1135. #define HSHELL_HIGHBIT 0x8000
  1136. #define HSHELL_FLASH (HSHELL_REDRAW|HSHELL_HIGHBIT)
  1137. #define HSHELL_RUDEAPPACTIVATED (HSHELL_WINDOWACTIVATED|HSHELL_HIGHBIT)
  1138. void RedrawItem(PTasks ptasks, HWND hwndShell, WPARAM code )
  1139. {
  1140.     int i = FindItem(ptasks, hwndShell);
  1141.     if (i != -1)
  1142.     {
  1143.         RECT rc;
  1144.         DWORD dwFlags;
  1145.         TOOLINFO ti;
  1146.         ti.cbSize = SIZEOF(ti);
  1147.         // set the bit saying whether we should flash or not
  1148.         dwFlags = TabCtrl_GetItemFlags(ptasks->hwndTab, i);
  1149.         if ((code == HSHELL_FLASH) != BOOLIFY(dwFlags & TIF_RENDERFLASHED))
  1150.         {
  1151.             // only do the set if this bit changed.
  1152.             if (code == HSHELL_FLASH)
  1153.             {
  1154.                 // TIF_RENDERFLASHED means, "Paint the background blue."
  1155.                 // TIF_FLASHING means, "This item is flashing."
  1156.                 dwFlags |= TIF_RENDERFLASHED;
  1157.                 // Only set TIF_FLASHING and unhide the tray if the app is inactive.
  1158.                 // Some apps (e.g., freecell) flash themselves while active just for
  1159.                 // fun.  It's annoying for the autohid tray to pop out in that case.
  1160.                 if (!Task_IsItemActive(ptasks, i))
  1161.                 {
  1162.                     dwFlags |= TIF_FLASHING;
  1163.                     // On NT5, unhide the tray whenever we get a flashing app.
  1164.                     if (g_bRunOnNT5)
  1165.                         Tray_Unhide();
  1166.                 }
  1167.             }
  1168.             else
  1169.             {
  1170.                 // Don't clear TIF_FLASHING.  We clear that only when the app
  1171.                 // is activated.
  1172.                 dwFlags &= ~TIF_RENDERFLASHED;
  1173.             }
  1174.             TabCtrl_SetItemFlags(ptasks->hwndTab, i, dwFlags);
  1175.             // Update the global traystuff flag that says, "There is an item flashing."
  1176.             Task_UpdateFlashingFlag(ptasks->hwndTab);
  1177.         }
  1178.         if (TabCtrl_GetItemRect(ptasks->hwndTab, i, &rc))
  1179.         {
  1180.             InflateRect(&rc, -g_cxEdge, -g_cyEdge);
  1181.             RedrawWindow(ptasks->hwndTab, &rc, NULL, RDW_INVALIDATE);
  1182.         }
  1183.         ti.hwnd = ptasks->hwndTab;
  1184.         ti.uId = i;
  1185.         ti.lpszText = LPSTR_TEXTCALLBACK;
  1186.         SendMessage(g_ts.hwndTrayTips, TTM_UPDATETIPTEXT, 0, (LPARAM)&ti);
  1187.     }
  1188. }
  1189. void SetActiveAlt(PTasks ptasks, HWND hwndAlt)
  1190. {
  1191.     int iMax;
  1192.     int i;
  1193.     if (!ptasks)
  1194.         return;
  1195.     iMax = TabCtrl_GetItemCount(ptasks->hwndTab);
  1196.     for ( i = 0; i < iMax; i++) {
  1197.         DWORD dwFlags = TabCtrl_GetItemFlags(ptasks->hwndTab, i);
  1198.         HWND hwndTask = TabCtrl_GetItemHwnd(ptasks->hwndTab, i);
  1199.         if (hwndTask == hwndAlt)
  1200.             dwFlags |= TIF_ACTIVATEALT | TIF_EVERACTIVEALT;
  1201.         else
  1202.             dwFlags &= ~TIF_ACTIVATEALT;
  1203.         TabCtrl_SetItemFlags(ptasks->hwndTab, i, dwFlags);
  1204.     }
  1205. }
  1206. HRESULT SHIsParentOwnerOrSelf(HWND hwndParent, HWND hwnd)
  1207. {
  1208.     while (hwnd)
  1209.     {
  1210.         if (hwnd == hwndParent)
  1211.             return S_OK;
  1212.         hwnd = GetParent(hwnd);
  1213.     }
  1214.     return E_FAIL;
  1215. }
  1216. BOOL Task_IsRudeWindowActive(HWND hwnd)
  1217. {
  1218.     // A rude window is considered "active" if it is:
  1219.     // - in the same thread as the foreground window, or
  1220.     // - in the same window hierarchy as the foreground window
  1221.     //
  1222.     HWND hwndFore = GetForegroundWindow();
  1223.     DWORD dwID = GetWindowThreadProcessId(hwnd, NULL);
  1224.     DWORD dwIDFore = GetWindowThreadProcessId(hwndFore, NULL);
  1225.     if (dwID == dwIDFore)
  1226.         return TRUE;
  1227.     else if (SHIsParentOwnerOrSelf(hwnd, hwndFore) == S_OK)
  1228.         return TRUE;
  1229.     return FALSE;
  1230. }
  1231. //***   Task_IsRudeWindow -- is given HWND 'rude' (fullscreen) on given monitor
  1232. //
  1233. BOOL Task_IsRudeWindow(HMONITOR hmon, HWND hwnd)
  1234. {
  1235.     ASSERT(hmon);
  1236.     ASSERT(hwnd);
  1237.     // Don't count the desktop as rude
  1238.     if (hwnd != v_hwndDesktop)
  1239.     {
  1240.         RECT rcMon, rcApp, rcTmp;
  1241.         DWORD dwStyle;
  1242.         //
  1243.         // NB: User32 will sometimes send us spurious HSHELL_RUDEAPPACTIVATED
  1244.         // messages.  When this happens, and we happen to have a maximized
  1245.         // app up, the old version of this code would think there was a rude app
  1246.         // up.  This mistake would break tray always-on-top and autohide.
  1247.         //
  1248.         //
  1249.         // The old logic was:
  1250.         //
  1251.         // If the app's window rect takes up the whole monitor, then it's rude.
  1252.         // (This check could mistake normal maximized apps for rude apps.)
  1253.         //
  1254.         //
  1255.         // The new logic is:
  1256.         //
  1257.         // If the app window does not have WS_DLGFRAME and WS_THICKFRAME,
  1258.         // then do the old check.  Rude apps typically lack one of these bits
  1259.         // (while normal apps usually have them), so do the old check in
  1260.         // this case to avoid potential compat issues with rude apps that
  1261.         // have non-fullscreen client areas.
  1262.         //
  1263.         // Otherwise, get the client rect rather than the window rect
  1264.         // and compare that rect against the monitor rect.
  1265.         //
  1266.         // If (mon U app) == app, then app is filling up entire monitor
  1267.         GetMonitorRect(hmon, &rcMon);
  1268.         dwStyle = GetWindowLong(hwnd, GWL_STYLE);
  1269.         if ((dwStyle & (WS_CAPTION | WS_THICKFRAME)) == (WS_CAPTION | WS_THICKFRAME))
  1270.         {
  1271.             // Doesn't match rude app profile; use client rect
  1272.             GetClientRect(hwnd, &rcApp);
  1273.             MapWindowPoints(hwnd, HWND_DESKTOP, (LPPOINT)&rcApp, 2);
  1274.         }
  1275.         else
  1276.         {
  1277.             // Matches rude app profile; use window rect
  1278.             GetWindowRect(hwnd, &rcApp);
  1279.         }
  1280.         UnionRect(&rcTmp, &rcApp, &rcMon);
  1281.         if (EqualRect(&rcTmp, &rcApp))
  1282.         {
  1283.             // Looks like a rude app.  Is it active?
  1284.             if (Task_IsRudeWindowActive(hwnd))
  1285.                 return TRUE;
  1286.         }
  1287.     }
  1288.     // No, not rude
  1289.     return FALSE;
  1290. }
  1291. struct iradata {
  1292.     HMONITOR    hmon;   // IN hmon we're checking against
  1293.     HWND        hwnd;   // INOUT hwnd of 1st rude app found
  1294. };
  1295. BOOL CALLBACK Task_IsRudeCallback(HWND hwnd, LPARAM lParam)
  1296. {
  1297.     struct iradata *pira = (struct iradata *)lParam;
  1298.     HMONITOR hmon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
  1299.     if ((pira->hmon == NULL || pira->hmon == hmon))
  1300.     {
  1301.         if (Task_IsRudeWindow(hmon, hwnd))
  1302.         {
  1303.             // We're done
  1304.             pira->hwnd = hwnd;
  1305.             return FALSE;
  1306.         }
  1307.     }
  1308.     // Keep going
  1309.     return TRUE;
  1310. }
  1311. HWND Task_FindRudeApp(PTasks ptasks)
  1312. {
  1313.     struct iradata irad = { NULL, 0 };
  1314.     // First try our cache
  1315.     if (IsWindow(ptasks->hwndLastRude))
  1316.     {
  1317.         if (!Task_IsRudeCallback(ptasks->hwndLastRude, (LPARAM)&irad))
  1318.         {
  1319.             // Cache hit
  1320.             return irad.hwnd;
  1321.         }
  1322.     }
  1323.     // No luck, gotta do it the hard way
  1324.     EnumWindows(Task_IsRudeCallback, (LPARAM)&irad);
  1325.     // Cache it for next time
  1326.     ptasks->hwndLastRude = irad.hwnd;
  1327.     return irad.hwnd;
  1328. }
  1329. extern HWND g_hwndPrevFocus;
  1330. //---------------------------------------------------------------------------
  1331. // We get notification about activation etc here. This saves having
  1332. // a fine-grained timer.
  1333. LRESULT Task_HandleShellHook(PTasks ptasks, int iCode, LPARAM lParam)
  1334. {
  1335.     HWND hwndRude = NULL;
  1336.     HWND hwnd = (HWND)lParam;
  1337.     // Tell library function that we have processed this message.
  1338.     RegisterShellHook(ptasks->hwnd, 5);
  1339.     switch (iCode) {
  1340.     case HSHELL_GETMINRECT:
  1341.         {
  1342.             LPSHELLHOOKINFO pshi = (LPSHELLHOOKINFO)lParam;
  1343.             Task_HandleGetMinRect(ptasks, pshi->hwnd, (LPPOINTS)&pshi->rc);
  1344.         }
  1345.         return TRUE;
  1346.     case HSHELL_RUDEAPPACTIVATED:
  1347.         // We shouldn't need to do this but we're getting rude-app activation
  1348.         // msgs when there aren't any.
  1349.         // Also, the hwnd that user tells us about is just the foreground window --
  1350.         // Task_FindRudeApp will return the window that's actually sized fullscreen.
  1351.         hwndRude = Task_FindRudeApp(ptasks);
  1352.         if (!hwndRude)
  1353.         {
  1354.             // If we can't find a rude app, set a timer and check again in a bit
  1355.             // we check this much earlier now than in win95, there are race conditions that apps 
  1356.             // activate smaller than size up and if we check them before they're sized up, we lose.
  1357.             // so set a timer and check again in a bit if we're going to blow off user's notify
  1358.             SetTimer(ptasks->hwnd, IDT_RECHECKRUDEAPP, 1000, NULL);
  1359.         }
  1360.         goto L_HSHELL_WINDOWACTIVATED;
  1361.     case HSHELL_WINDOWACTIVATED:
  1362.     L_HSHELL_WINDOWACTIVATED:
  1363.         {
  1364.             int iItem = FindItem(ptasks, hwnd);
  1365.             if (iItem == -1)
  1366.             {
  1367.                 int iMax;
  1368.                 int i;
  1369.                 BOOL fFoundBackup = FALSE;
  1370.                 iMax = TabCtrl_GetItemCount(ptasks->hwndTab);
  1371.                 for (i = 0; i < iMax; i++)
  1372.                 {
  1373.                     DWORD dwFlags = TabCtrl_GetItemFlags(ptasks->hwndTab, i);
  1374.                     if ((dwFlags & TIF_ACTIVATEALT) ||
  1375.                         (!fFoundBackup && (dwFlags & TIF_EVERACTIVEALT)))
  1376.                     {
  1377.                         DWORD dwpid1, dwpid2;
  1378.                         HWND hwndNew = TabCtrl_GetItemHwnd(ptasks->hwndTab, i);
  1379.                         GetWindowThreadProcessId(hwnd, &dwpid1);
  1380.                         GetWindowThreadProcessId(hwndNew, &dwpid2);
  1381.                         // Only change if they're in the same process
  1382.                         if (dwpid1 == dwpid2)
  1383.                         {
  1384.                             hwnd = TabCtrl_GetItemHwnd(ptasks->hwndTab, i);
  1385.                             if (dwFlags & TIF_ACTIVATEALT)
  1386.                                 break;
  1387.                             else
  1388.                                 fFoundBackup = TRUE;
  1389.                         }
  1390.                     }
  1391.                 }
  1392.             }
  1393.             Task_HandleActivate(ptasks, hwnd);
  1394.             Tray_HandleFullScreenApp(hwndRude);
  1395.         }
  1396.         break;
  1397.     case HSHELL_WINDOWCREATED:
  1398.         Task_HandleOtherWindowCreated(ptasks, hwnd);
  1399.         break;
  1400.     case HSHELL_WINDOWDESTROYED:
  1401.         if (!PostMessage(ptasks->hwnd, TM_WINDOWDESTROYED, 0, lParam))
  1402.         {
  1403.             Task_HandleOtherWindowDestroyed(ptasks, hwnd);
  1404.         }
  1405.         break;
  1406.     case HSHELL_ACTIVATESHELLWINDOW:
  1407.         SwitchToThisWindow(v_hwndTray, TRUE);
  1408.         SetForegroundWindow(v_hwndTray);
  1409.         break;
  1410.     case HSHELL_TASKMAN:
  1411.         //
  1412.         // On NT, we've arranged for winlogon/user to send a -1 lParam to indicate
  1413.         // that the real task list should be displayed (normally the lParam is
  1414.         // the hwnd)
  1415.         //
  1416. #ifdef WINNT
  1417.         if (-1 == lParam)
  1418.         {
  1419.             RunSystemMonitor();
  1420.         }
  1421.         else
  1422.         {
  1423. #endif
  1424.             // if it wasn't invoked via control escape, then it was the win key
  1425.             if (!g_ts.fStuckRudeApp && GetAsyncKeyState(VK_CONTROL) >= 0)
  1426.             {
  1427.                 if (!g_hwndPrevFocus)
  1428.                 {
  1429.                     HWND hwndForeground = GetForegroundWindow();
  1430.                     if (hwndForeground != v_hwndTray)
  1431.                     {
  1432.                         g_hwndPrevFocus = hwndForeground;
  1433.                     }
  1434.                 }
  1435.                 else if (GetForegroundWindow() == v_hwndTray)
  1436.                 {
  1437.                     // g_hwndPrevFocus will be wiped out by the MPOS_FULLCANCEL
  1438.                     // so save it before we lose it
  1439.                     HWND hwndPrevFocus = g_hwndPrevFocus;
  1440.                     if (g_ts._pmpStartMenu)
  1441.                         g_ts._pmpStartMenu->lpVtbl->OnSelect(g_ts._pmpStartMenu, MPOS_FULLCANCEL);
  1442.                     // otherwise they're just hitting the key again.
  1443.                     // set focus away
  1444.                     SHAllowSetForegroundWindow(hwndPrevFocus);
  1445.                     SetForegroundWindow(hwndPrevFocus);
  1446.                     g_hwndPrevFocus = NULL;
  1447.                     return TRUE;
  1448.                 }
  1449.             }
  1450.             PostMessage(v_hwndTray, TM_ACTASTASKSW, 0, 0L);
  1451. #ifdef WINNT
  1452.         }
  1453. #endif
  1454.         return TRUE;
  1455. #ifdef USE_SYSMENU_TIMEOUT
  1456.     case HSHELL_SYSMENU:
  1457.         _HandleSysMenu(ptasks, hwnd);
  1458.         break;
  1459. #endif
  1460.     case HSHELL_REDRAW:
  1461.     case HSHELL_FLASH:
  1462.         RedrawItem(ptasks, hwnd, iCode);
  1463.         break;
  1464.     case HSHELL_OVERFLOW:
  1465.         Task_HandleOverflow(ptasks);
  1466.         break;
  1467. #ifdef WINNT
  1468.     case HSHELL_ENDTASK:
  1469.         EndTask(hwnd, FALSE, FALSE);
  1470.         break;
  1471.     default:
  1472.         return Task_HandleAppCommand((WPARAM)iCode, lParam);
  1473. #endif
  1474.     }
  1475.     return 0;
  1476. }
  1477. void Task_InitGlobalFonts()
  1478. {
  1479.     HFONT hfont;
  1480.     NONCLIENTMETRICS ncm;
  1481.     ncm.cbSize = SIZEOF(ncm);
  1482.     if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, SIZEOF(ncm), &ncm, 0))
  1483.     {
  1484.         // Create the bold font
  1485.         ncm.lfCaptionFont.lfWeight = FW_BOLD;
  1486.         hfont = CreateFontIndirect(&ncm.lfCaptionFont);
  1487.         if (hfont) 
  1488.         {
  1489.             if (g_hfontCapBold)
  1490.                 DeleteObject(g_hfontCapBold);
  1491.             g_hfontCapBold = hfont;
  1492.         }
  1493.         // Create the normal font
  1494.         ncm.lfCaptionFont.lfWeight = FW_NORMAL;
  1495.         hfont = CreateFontIndirect(&ncm.lfCaptionFont);
  1496.         if (hfont) 
  1497.         {
  1498.             if (g_hfontCapNormal)
  1499.                 DeleteObject(g_hfontCapNormal);
  1500.             g_hfontCapNormal = hfont;
  1501.         }
  1502.     }
  1503. }
  1504. //---------------------------------------------------------------------------
  1505. LRESULT Task_HandleWinIniChange(PTasks ptasks, WPARAM wParam, LPARAM lParam, BOOL fOnCreate)
  1506. {
  1507.     if (wParam == SPI_SETNONCLIENTMETRICS ||
  1508.         ((!wParam) && (!lParam || (lstrcmpi((LPTSTR)lParam, TEXT("WindowMetrics")) == 0)))) 
  1509.     {
  1510.         //
  1511.         // On creation, don't bother creating the fonts if someone else
  1512.         // (such as the clock control) has already done it for us.
  1513.         //
  1514.         if (!fOnCreate || !g_hfontCapNormal)
  1515.             Task_InitGlobalFonts();
  1516.         if (fOnCreate)
  1517.         {
  1518.             //
  1519.             // On creation, we haven't been inserted into bandsite yet,
  1520.             // so we need to defer size validation.
  1521.             //
  1522.             PostMessage(ptasks->hwnd, TBC_VERIFYBUTTONHEIGHT, 0, 0);
  1523.         }
  1524.         else
  1525.         {
  1526.             Task_VerifyButtonHeight(ptasks);
  1527.         }
  1528.     }
  1529.     return 0;
  1530. }
  1531. void Task_VerifyButtonHeight(PTasks ptasks)
  1532. {
  1533.     RECT rc;
  1534.     DWORD dwStuck;
  1535.     int cyOld;
  1536.     int cyNew;
  1537.     // verify the size of the buttons.
  1538.     TabCtrl_GetItemRect(ptasks->hwndTab, 0, &rc);
  1539.     cyOld = RECTHEIGHT(rc);
  1540.     CheckSize(ptasks, TRUE);
  1541.     BandSite_Update(g_ts.ptbs);     // _BandInfoChanged
  1542.     TabCtrl_GetItemRect(ptasks->hwndTab, 0, &rc);
  1543.     cyNew = RECTHEIGHT(rc);
  1544.     dwStuck = Tray_GetStuckPlace();
  1545.     if (STUCK_HORIZONTAL(dwStuck) &&  (cyOld != cyNew)) 
  1546.     {
  1547.         // if the size has changed, resize the tray,
  1548.         // make sure it's at least one row height
  1549.         GetWindowRect(v_hwndTray, &rc);
  1550.         if (RECTHEIGHT(rc) < cyNew) 
  1551.         {
  1552.             if (rc.top <= 0)
  1553.                 rc.bottom = rc.top + cyNew;
  1554.             else
  1555.                 rc.top = rc.bottom - cyNew;
  1556.         }
  1557.         SendMessage(v_hwndTray, WM_SIZING, 0, (LPARAM)&rc);
  1558.         SetWindowPos(v_hwndTray, 0, rc.left, rc.top, RECTWIDTH(rc), RECTHEIGHT(rc),
  1559.                      SWP_NOACTIVATE | SWP_NOZORDER);
  1560.     } 
  1561.     else 
  1562.     {
  1563.         // otherwise just redraw it all
  1564.         RedrawWindow(ptasks->hwndTab, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  1565.     }
  1566. }
  1567. LRESULT _HandleSizing(PTasks ptasks, LPRECT lpRect)
  1568. {
  1569.     int iHeight;
  1570.     int iItemHeight;
  1571.     RECT rcItem;
  1572.     lpRect->bottom += g_cyEdge*2;
  1573.     iHeight = RECTHEIGHT(*lpRect);
  1574.     // full height of one row including y inter button spacing
  1575.     TabCtrl_GetItemRect(ptasks->hwndTab, 0, &rcItem);
  1576.     iItemHeight = RECTHEIGHT(rcItem) + g_cyTabSpace;
  1577.     iHeight += iItemHeight/2;
  1578.     iHeight -= (iHeight % iItemHeight);
  1579.     if (iHeight > g_cyTabSpace)
  1580.         iHeight -= g_cyTabSpace;
  1581.     lpRect->bottom = lpRect->top + iHeight;
  1582.     return 0;
  1583. }
  1584. BOOL _HandleDrawItem(PTasks ptasks, LPDRAWITEMSTRUCT lpdis)
  1585. {
  1586.     UINT uDCFlags;
  1587.     struct TaskExtra {
  1588.         HWND hwnd;
  1589.         DWORD dwFlags;
  1590.     } *info =  (struct TaskExtra*)lpdis->itemData;
  1591.     BOOL fTruncated;
  1592.     WORD wLang;
  1593.     if (!Window_IsNormal(info->hwnd)) 
  1594.     {
  1595.         //Task_HandleOtherWindowDestroyed(ptasks, info->hwnd);
  1596.         // our shell hook should have prevented this
  1597.         //ASSERT(0);
  1598.     } 
  1599.     else 
  1600.     {
  1601.         if (info->dwFlags & TIF_RENDERFLASHED) 
  1602.         {
  1603.             uDCFlags = DC_ACTIVE;
  1604.         } 
  1605.         else 
  1606.         {
  1607.             uDCFlags =
  1608.                 DC_INBUTTON  |
  1609.                     ((lpdis->itemState & ODS_SELECTED) ? DC_ACTIVE : 0);
  1610.         }
  1611.         if (lpdis->itemState & ODS_SELECTED) 
  1612.         {
  1613.             lpdis->rcItem.bottom++;
  1614.             lpdis->rcItem.top++;
  1615.         }
  1616.        // _HandleDrawItem(PTasks ptasks, LPDRAWITEMSTRUCT lpdis)
  1617.         wLang = GetUserDefaultLangID();
  1618.         if (PRIMARYLANGID(wLang) == LANG_CHINESE &&
  1619.            ((SUBLANGID(wLang) == SUBLANG_CHINESE_TRADITIONAL) ||
  1620.             (SUBLANGID(wLang) == SUBLANG_CHINESE_SIMPLIFIED)))
  1621.         {
  1622.             // Select normal weight font for chinese language
  1623.             fTruncated = !DrawCaptionTemp(info->hwnd, lpdis->hDC,&lpdis->rcItem,
  1624.                                           g_hfontCapNormal,
  1625.                                           NULL, NULL,
  1626.                                           uDCFlags | DC_TEXT | DC_ICON | DC_NOSENDMSG);
  1627.         }
  1628.         else
  1629.         {
  1630.             fTruncated = !DrawCaptionTemp(info->hwnd, lpdis->hDC, &lpdis->rcItem,
  1631.                         (lpdis->itemState & ODS_SELECTED) ? g_hfontCapBold : g_hfontCapNormal,
  1632.                         NULL, NULL, uDCFlags | DC_TEXT | DC_ICON | DC_NOSENDMSG);
  1633.         }
  1634.         // save away info on whether we should tool tip or not
  1635.         if (fTruncated)
  1636.             info->dwFlags |= TIF_SHOULDTIP;
  1637.         else
  1638.             info->dwFlags &= ~TIF_SHOULDTIP;
  1639.         if (lpdis->itemState & ODS_SELECTED) 
  1640.         {
  1641.             COLORREF clr;
  1642.             HBRUSH hbr;
  1643.             // now draw in that one line
  1644.             if (uDCFlags == DC_ACTIVE) 
  1645.             {
  1646.                 clr = SetBkColor(lpdis->hDC, GetSysColor(COLOR_ACTIVECAPTION));
  1647.             } 
  1648.             else 
  1649.             {
  1650.                 hbr = (HBRUSH)DefWindowProc(ptasks->hwndTab, WM_CTLCOLORSCROLLBAR, (WPARAM)lpdis->hDC, (LPARAM)ptasks->hwndTab);
  1651.                 hbr = SelectObject(lpdis->hDC, hbr);
  1652.             }
  1653.             lpdis->rcItem.top--;
  1654.             lpdis->rcItem.bottom = lpdis->rcItem.top + 1;
  1655.             ExtTextOut(lpdis->hDC, 0, 0, ETO_OPAQUE, &lpdis->rcItem, NULL, 0,NULL);
  1656.             if (uDCFlags == DC_ACTIVE) 
  1657.             {
  1658.                 SetBkColor(lpdis->hDC, clr);
  1659.             } 
  1660.             else 
  1661.             {
  1662.                 SelectObject(lpdis->hDC, hbr);
  1663.             }
  1664.         }
  1665.     }
  1666.     return TRUE;
  1667. }
  1668. void Task_TaskTab(PTasks ptasks, int iIncr)
  1669. {
  1670.     int i;
  1671.     int iCount = TabCtrl_GetItemCount(ptasks->hwndTab);
  1672.     if (iCount) {
  1673.         if (GetFocus() != ptasks->hwndTab)
  1674.             SetFocus(ptasks->hwndTab);
  1675.         // make sure nothing is selected
  1676.         if (TabCtrl_GetCurSel(ptasks->hwndTab) != -1)
  1677.             Task_SelectWindow(ptasks, NULL);
  1678.         i = TabCtrl_GetCurFocus(ptasks->hwndTab);
  1679.         if ((INT_PTR)iIncr < 0 && i == -1)
  1680.             i = 0;
  1681.         i = (i + iIncr + iCount) % iCount;
  1682.         TabCtrl_SetCurFocus(ptasks->hwndTab, i);
  1683.     }
  1684. }
  1685. void Task_OnSetFocus(PTasks ptasks)
  1686. {
  1687.     NMHDR nmhdr;
  1688.     SetFocus(ptasks->hwndTab);
  1689.     nmhdr.hwndFrom = ptasks->hwnd;
  1690.     nmhdr.code = NM_SETFOCUS;
  1691.     SendMessage(GetParent(ptasks->hwnd), WM_NOTIFY, (WPARAM)NULL, (LPARAM)&nmhdr);
  1692. }
  1693. LRESULT CALLBACK Task_WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  1694. {
  1695.     PTasks ptasks = &g_tasks;
  1696.     LRESULT lres;
  1697.     INSTRUMENT_WNDPROC(SHCNFI_MAIN_WNDPROC, hwnd, msg, wParam, lParam);
  1698.     switch (msg) {
  1699.     case WM_CREATE:
  1700.         return _HandleCreate(hwnd, ((LPCREATESTRUCT)lParam)->lpCreateParams);
  1701.     case WM_DESTROY:
  1702.         return _HandleDestroy(ptasks);
  1703.     case WM_SIZE:
  1704.         return _HandleSize(ptasks, wParam);
  1705.     case WM_DRAWITEM:
  1706.         return _HandleDrawItem(ptasks, (LPDRAWITEMSTRUCT)lParam);
  1707.     case WM_WININICHANGE:
  1708.         Cabinet_InitGlobalMetrics(wParam, (LPTSTR)lParam);
  1709.         Task_HandleWinIniChange(ptasks, wParam, lParam, FALSE);
  1710.         return SendMessage(ptasks->hwndTab, WM_WININICHANGE, wParam, lParam);
  1711.     case WM_SIZING:
  1712.         return _HandleSizing(ptasks, (LPRECT)lParam);
  1713.     // this keeps our window from comming to the front on button down
  1714.     // instead, we activate the window on the up click
  1715.     // we only want this for the tree and the view window
  1716.     // (the view window does this itself)
  1717.     case WM_MOUSEACTIVATE: {
  1718.         POINT pt;
  1719.         RECT rc;
  1720.         GetCursorPos(&pt);
  1721.         GetWindowRect(ptasks->hwnd, &rc);
  1722.         if ((LOWORD(lParam) == HTCLIENT) && PtInRect(&rc, pt))
  1723.             return MA_NOACTIVATE;
  1724.         else
  1725.             goto DoDefault;
  1726.     }
  1727.     case WM_SETFOCUS: 
  1728.         Task_OnSetFocus(ptasks);
  1729.         break;
  1730.     case WM_VSCROLL:
  1731.         return _HandleVScroll(ptasks, LOWORD(wParam), HIWORD(wParam));
  1732.     case WM_HSCROLL:
  1733.         return _HandleHScroll(ptasks, LOWORD(wParam), HIWORD(wParam));
  1734.     case WM_NOTIFY:
  1735.         return Task_HandleNotify(ptasks, (LPNMHDR)lParam);
  1736.     case WM_NCHITTEST:
  1737.         lres = DefWindowProc(hwnd, msg, wParam, lParam);
  1738.         if (lres == HTVSCROLL || lres == HTHSCROLL)
  1739.             return lres;
  1740.         else
  1741.             return HTTRANSPARENT;
  1742.     case WM_TIMER:
  1743.         switch (wParam) {
  1744.         case IDT_RECHECKRUDEAPP:
  1745.             Tray_HandleFullScreenApp(Task_FindRudeApp(ptasks));
  1746.             KillTimer(hwnd, IDT_RECHECKRUDEAPP);
  1747.             break;
  1748.             
  1749. #ifdef USE_SYSMENU_TIMEOUT
  1750.         case IDT_SYSMENU:
  1751.             _HandleSysMenuTimeout(ptasks);
  1752.             break;
  1753. #endif
  1754.         }
  1755.         break;
  1756.     case WM_COMMAND:
  1757.         if (LOWORD(wParam) == SC_CLOSE)
  1758.         {
  1759.             BOOL fForce = GetKeyState(VK_CONTROL) & (1 << 16) ? TRUE : FALSE;
  1760. #ifdef WINNT
  1761.             EndTask(ptasks->hwndSysMenu, FALSE , fForce);
  1762. #else
  1763.             EndTask(ptasks->hwndSysMenu, 0, NULL, fForce ? 0 : ET_TRYTOKILLNICELY);
  1764. #endif
  1765.         }
  1766. #ifdef WINNT
  1767.         else if (LOWORD(wParam) == SC_MINIMIZE) {
  1768.             ShowWindow(ptasks->hwndSysMenu, SW_FORCEMINIMIZE);
  1769.         }
  1770. #endif
  1771.         break;
  1772.     case TM_POSTEDRCLICK:
  1773.         // wparam is handle to the apps window
  1774.         Task_FakeSystemMenu(ptasks, (HWND)wParam, (DWORD)lParam);
  1775.         break;
  1776.     case TM_TASKTAB:
  1777.         Task_TaskTab(ptasks, (int)wParam);
  1778.         break;
  1779.     case WM_CONTEXTMENU:
  1780.         if (SHRestricted(REST_NOTRAYCONTEXTMENU)) {
  1781.             break;
  1782.         }
  1783.         // if we didn't find an item to put the sys menu up for, then
  1784.         // pass on the WM_CONTExTMENU message
  1785.         if (!Task_PostFakeSystemMenu(ptasks, (DWORD)lParam))
  1786.             goto DoDefault;
  1787.         DebugMsg(DM_TRACE, TEXT("Task GOT CONTEXT MENU!"));
  1788.         break;
  1789.     case TM_WINDOWDESTROYED:
  1790.         Task_HandleOtherWindowDestroyed(ptasks, (HWND)lParam);
  1791.         break;
  1792.     case TM_SYSMENUCOUNT:
  1793.         return ptasks->iSysMenuCount;
  1794.     case TBC_VERIFYBUTTONHEIGHT:
  1795.         Task_VerifyButtonHeight(ptasks);
  1796.         break;
  1797.         
  1798.     case TBC_SETACTIVEALT:
  1799.         SetActiveAlt(ptasks, (HWND) lParam);
  1800.         break;
  1801.     default:
  1802. DoDefault:
  1803.         if ((ptasks != NULL) && (msg == ptasks->WM_ShellHook))
  1804.             return Task_HandleShellHook(ptasks, (int)wParam, lParam);
  1805.         else
  1806.             return DefWindowProc(hwnd, msg, wParam, lParam);
  1807.     }
  1808.     return 0;
  1809. }
  1810. //---------------------------------------------------------------------------
  1811. const TCHAR c_szTaskSwClass[] = TEXT("MSTaskSwWClass");
  1812. BOOL CTasks_RegisterWindowClass()
  1813. {
  1814.     WNDCLASSEX  wc;
  1815.     ZeroMemory(&wc, SIZEOF(wc));
  1816.     wc.cbSize = SIZEOF(WNDCLASSEX);
  1817.     if (GetClassInfoEx(hinstCabinet, c_szTaskSwClass, &wc))
  1818.         return TRUE;
  1819.     wc.lpszClassName    = c_szTaskSwClass;
  1820.     wc.lpfnWndProc      = Task_WndProc;
  1821.     wc.cbWndExtra       = SIZEOF(PTasks);
  1822.     wc.hInstance        = hinstCabinet;
  1823.     wc.hCursor          = LoadCursor(NULL, IDC_ARROW);
  1824.     wc.hbrBackground    = (HBRUSH)(COLOR_3DFACE + 1);
  1825.     return RegisterClassEx(&wc);
  1826. }
  1827. BOOL Tasks_Create(HWND hwndParent)
  1828. {
  1829.     if (!CTasks_RegisterWindowClass())
  1830.         return FALSE;
  1831.     // this sets this->hwnd
  1832.     return BOOLFROMPTR(CreateWindowEx(0, c_szTaskSwClass, NULL,
  1833.                                 WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
  1834.                                 0, 0, 0, 0, hwndParent, NULL, hinstCabinet, (CTasks *)&g_tasks));
  1835. }
  1836. void SetWindowStyleBit(HWND hWnd, DWORD dwBit, DWORD dwValue)
  1837. {
  1838.     DWORD dwStyle;
  1839.     dwStyle = GetWindowLong(hWnd, GWL_STYLE);
  1840.     if ((dwStyle & dwBit) != dwValue) {
  1841.         dwStyle ^= dwBit;
  1842.         SetWindowLong(hWnd, GWL_STYLE, dwStyle);
  1843.     }
  1844. }
  1845. BOOL ShouldMinMax(HWND hwnd, BOOL fMin)
  1846. {
  1847.     DWORD dwStyle = GetWindowLong(hwnd, GWL_STYLE);
  1848.     if (IsWindowVisible(hwnd) &&
  1849.         (fMin ? !IsMinimized(hwnd) : !IsMaximized(hwnd)) && 
  1850.         IsWindowEnabled(hwnd)) {
  1851.         if (dwStyle & (fMin ? WS_MINIMIZEBOX : WS_MAXIMIZEBOX)) {
  1852.             if ((dwStyle & (WS_CAPTION | WS_SYSMENU)) == (WS_CAPTION | WS_SYSMENU)) {
  1853.                 HMENU hmenu = GetSystemMenu(hwnd, FALSE);
  1854.                 if (hmenu) {
  1855.                     // is there a sys menu and is the sc_min/maximize part enabled?
  1856.                     if (!(GetMenuState(hmenu, (fMin ? SC_MINIMIZE : SC_MAXIMIZE), MF_BYCOMMAND) & MF_DISABLED)) {
  1857.                         return TRUE;
  1858.                     }
  1859.                 }
  1860.             } else {
  1861.                 return TRUE;
  1862.             }
  1863.         }
  1864.     }
  1865.     return FALSE;
  1866. }
  1867. void SaveWindowPositions(UINT idRes);
  1868. BOOL CanMinimizeAll(HWND hwndView)
  1869. {
  1870.     PTasks ptasks;
  1871.     int i;
  1872.     ASSERT(IS_VALID_HANDLE(hwndView, WND));
  1873.     ptasks = GetWindowPtr(hwndView, 0);
  1874.     ASSERT(IS_VALID_READ_PTR(ptasks, CTasks));
  1875.     for ( i = TabCtrl_GetItemCount(ptasks->hwndTab) -1; i >= 0; i--)
  1876.     {
  1877.         HWND hwnd = TabCtrl_GetItemHwnd(ptasks->hwndTab, i);
  1878.         if (ShouldMinMax(hwnd, TRUE) || (TabCtrl_GetItemFlags(ptasks->hwndTab, i) & TIF_EVERACTIVEALT))
  1879.             return TRUE;
  1880.     }
  1881.     return FALSE;
  1882. }
  1883. void CheckWindowPositions();
  1884. DWORD MinimizeAllThread(LPVOID lpv)
  1885. {
  1886.     HWND hwndView = (HWND)lpv;
  1887.     LONG iAnimate;
  1888.     ANIMATIONINFO ami;
  1889.     int i;
  1890.     PTasks ptasks;
  1891.     if (IsWindow(hwndView))
  1892.         ptasks = GetWindowPtr(hwndView, 0);
  1893.     else
  1894.         ptasks = 0;
  1895.     if (!ptasks)
  1896.         return 0;
  1897.     // turn off animiations during this
  1898.     ami.cbSize = SIZEOF(ANIMATIONINFO);
  1899.     SystemParametersInfo(SPI_GETANIMATION, SIZEOF(ami), &ami, FALSE);
  1900.     iAnimate = ami.iMinAnimate;
  1901.     ami.iMinAnimate = FALSE;
  1902.     SystemParametersInfo(SPI_SETANIMATION, SIZEOF(ami), &ami, FALSE);
  1903.     //
  1904.     //EnumWindows(MinimizeEnumProc, 0);
  1905.     // go through the tab control and minimize them.
  1906.     // don't do enumwindows because we only want to minimize windows
  1907.     // that are restorable via the tray
  1908.     for ( i = TabCtrl_GetItemCount(ptasks->hwndTab) -1; i >= 0; i--)
  1909.     {
  1910.         // we do the whole minimize on its own thread, so we don't do the showwindow
  1911.         // async.  this allows animation to be off for the full minimize.
  1912.         HWND hwnd = TabCtrl_GetItemHwnd(ptasks->hwndTab, i);
  1913.         if (ShouldMinMax(hwnd, TRUE)) {
  1914.             TraceMsg(TF_TRAY, "mat: hwnd=0x%x send SW_SHOWMINNOACT", hwnd);
  1915.             ShowWindow(hwnd, SW_SHOWMINNOACTIVE);
  1916.         }
  1917.         else if (TabCtrl_GetItemFlags(ptasks->hwndTab, i) & TIF_EVERACTIVEALT) {
  1918.             TraceMsg(TF_TRAY, "mat: add hwnd=0x%x send SC_MIN", hwnd);
  1919.             SHAllowSetForegroundWindow(hwnd);
  1920.             SendMessage(hwnd, WM_SYSCOMMAND, SC_MINIMIZE, -1);
  1921.         }
  1922.         else {
  1923.             TraceMsg(TF_TRAY, "mat: hwnd=0x%x skip min", hwnd);
  1924.         }
  1925.     }
  1926.     CheckWindowPositions();
  1927.     // restore animations  state
  1928.     ami.iMinAnimate = iAnimate;
  1929.     SystemParametersInfo(SPI_SETANIMATION, SIZEOF(ami), &ami, FALSE);
  1930.     return 0;
  1931. }
  1932. void MinimizeAll(HWND hwndView) 
  1933. {
  1934.     // might want to move this into MinimizeAllThread (to match
  1935.     // CheckWindowPositions).  but what if CreateThread fails?
  1936.     SaveWindowPositions(IDS_MINIMIZEALL);
  1937.     SHCreateThread(MinimizeAllThread, hwndView, 0, NULL);
  1938. }
  1939. int Task_HitTest(HWND hwndTask, POINTL ptl)
  1940. {
  1941.     PTasks ptasks = GetWindowPtr(hwndTask, 0);
  1942.     if (ptasks)
  1943.     {
  1944.         TC_HITTESTINFO hitinfo = { {ptl.x, ptl.y}, TCHT_ONITEM };
  1945.         ScreenToClient(ptasks->hwndTab, &hitinfo.pt);
  1946.         return TabCtrl_HitTest(ptasks->hwndTab, &hitinfo);
  1947.     }
  1948.     return -1;
  1949. }
  1950. void Task_SetCurSel(HWND hwndTask, int i)
  1951. {
  1952.     PTasks ptasks = GetWindowPtr(hwndTask, 0);
  1953.     if (ptasks)
  1954.     {
  1955.         TabCtrl_SetCurSel(ptasks->hwndTab, i);
  1956.         Task_SwitchToSelection(ptasks);
  1957.     }
  1958. }