xref: /reactos/base/shell/explorer/taskswnd.cpp (revision 92551f54)
1 /*
2  * ReactOS Explorer
3  *
4  * Copyright 2006 - 2007 Thomas Weidenmueller <w3seek@reactos.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20 
21 #include "precomp.h"
22 #include <commoncontrols.h>
23 
24 /* Set DUMP_TASKS to 1 to enable a dump of the tasks and task groups every
25    5 seconds */
26 #define DUMP_TASKS  0
27 #define DEBUG_SHELL_HOOK 0
28 
29 #define MAX_TASKS_COUNT (0x7FFF)
30 #define TASK_ITEM_ARRAY_ALLOC   64
31 
32 const WCHAR szTaskSwitchWndClass[] = L"MSTaskSwWClass";
33 const WCHAR szRunningApps[] = L"Running Applications";
34 
35 #if DEBUG_SHELL_HOOK
36 const struct {
37     INT msg;
38     LPCWSTR msg_name;
39 } hshell_msg [] = {
40         { HSHELL_WINDOWCREATED, L"HSHELL_WINDOWCREATED" },
41         { HSHELL_WINDOWDESTROYED, L"HSHELL_WINDOWDESTROYED" },
42         { HSHELL_ACTIVATESHELLWINDOW, L"HSHELL_ACTIVATESHELLWINDOW" },
43         { HSHELL_WINDOWACTIVATED, L"HSHELL_WINDOWACTIVATED" },
44         { HSHELL_GETMINRECT, L"HSHELL_GETMINRECT" },
45         { HSHELL_REDRAW, L"HSHELL_REDRAW" },
46         { HSHELL_TASKMAN, L"HSHELL_TASKMAN" },
47         { HSHELL_LANGUAGE, L"HSHELL_LANGUAGE" },
48         { HSHELL_SYSMENU, L"HSHELL_SYSMENU" },
49         { HSHELL_ENDTASK, L"HSHELL_ENDTASK" },
50         { HSHELL_ACCESSIBILITYSTATE, L"HSHELL_ACCESSIBILITYSTATE" },
51         { HSHELL_APPCOMMAND, L"HSHELL_APPCOMMAND" },
52         { HSHELL_WINDOWREPLACED, L"HSHELL_WINDOWREPLACED" },
53         { HSHELL_WINDOWREPLACING, L"HSHELL_WINDOWREPLACING" },
54         { HSHELL_RUDEAPPACTIVATED, L"HSHELL_RUDEAPPACTIVATED" },
55 };
56 #endif
57 
58 typedef struct _TASK_GROUP
59 {
60     /* We have to use a linked list instead of an array so we don't have to
61        update all pointers to groups in the task item array when removing
62        groups. */
63     struct _TASK_GROUP *Next;
64 
65     DWORD dwTaskCount;
66     DWORD dwProcessId;
67     INT Index;
68     union
69     {
70         DWORD dwFlags;
71         struct
72         {
73 
74             DWORD IsCollapsed : 1;
75         };
76     };
77 } TASK_GROUP, *PTASK_GROUP;
78 
79 typedef struct _TASK_ITEM
80 {
81     HWND hWnd;
82     PTASK_GROUP Group;
83     INT Index;
84     INT IconIndex;
85 
86     union
87     {
88         DWORD dwFlags;
89         struct
90         {
91 
92             /* IsFlashing is TRUE when the task bar item should be flashing. */
93             DWORD IsFlashing : 1;
94 
95             /* RenderFlashed is only TRUE if the task bar item should be
96                drawn with a flash. */
97             DWORD RenderFlashed : 1;
98         };
99     };
100 } TASK_ITEM, *PTASK_ITEM;
101 
102 
103 class CHardErrorThread
104 {
105     DWORD m_ThreadId;
106     HANDLE m_hThread;
107     LONG m_bThreadRunning;
108     DWORD m_Status;
109     DWORD m_dwType;
110     CStringW m_Title;
111     CStringW m_Text;
112 public:
113 
CHardErrorThread()114     CHardErrorThread():
115         m_ThreadId(0),
116         m_hThread(NULL),
117         m_bThreadRunning(FALSE),
118         m_Status(NULL),
119         m_dwType(NULL)
120     {
121     }
122 
~CHardErrorThread()123     ~CHardErrorThread()
124     {
125         if (m_bThreadRunning)
126         {
127             /* Try to unstuck Show */
128             PostThreadMessage(m_ThreadId, WM_QUIT, 0, 0);
129             DWORD ret = WaitForSingleObject(m_hThread, 3*1000);
130             if (ret == WAIT_TIMEOUT)
131                 TerminateThread(m_hThread, 0);
132             CloseHandle(m_hThread);
133         }
134     }
135 
ThreadProc()136     HRESULT ThreadProc()
137     {
138         HRESULT hr;
139         CComPtr<IUserNotification> pnotification;
140 
141         hr = OleInitialize(NULL);
142         if (FAILED_UNEXPECTEDLY(hr))
143             return hr;
144 
145         hr = CoCreateInstance(CLSID_UserNotification,
146                               NULL,
147                               CLSCTX_INPROC_SERVER,
148                               IID_PPV_ARG(IUserNotification, &pnotification));
149         if (FAILED_UNEXPECTEDLY(hr))
150             return hr;
151 
152         hr = pnotification->SetBalloonInfo(m_Title, m_Text, NIIF_WARNING);
153         if (FAILED_UNEXPECTEDLY(hr))
154             return hr;
155 
156         hr = pnotification->SetIconInfo(NULL, NULL);
157         if (FAILED_UNEXPECTEDLY(hr))
158             return hr;
159 
160         /* Show will block until the balloon closes */
161         hr = pnotification->Show(NULL, 0);
162         if (FAILED_UNEXPECTEDLY(hr))
163             return hr;
164 
165         return S_OK;
166     }
167 
s_HardErrorThreadProc(IN OUT LPVOID lpParameter)168     static DWORD CALLBACK s_HardErrorThreadProc(IN OUT LPVOID lpParameter)
169     {
170         CHardErrorThread* pThis = reinterpret_cast<CHardErrorThread*>(lpParameter);
171         pThis->ThreadProc();
172         CloseHandle(pThis->m_hThread);
173         OleUninitialize();
174         InterlockedExchange(&pThis->m_bThreadRunning, FALSE);
175         return 0;
176     }
177 
StartThread(PBALLOON_HARD_ERROR_DATA pData)178     void StartThread(PBALLOON_HARD_ERROR_DATA pData)
179     {
180         BOOL bIsRunning = InterlockedExchange(&m_bThreadRunning, TRUE);
181 
182         /* Ignore the new message if we are already showing one */
183         if (bIsRunning)
184             return;
185 
186         m_Status = pData->Status;
187         m_dwType = pData->dwType;
188         m_Title = (PWCHAR)((ULONG_PTR)pData + pData->TitleOffset);
189         m_Text = (PWCHAR)((ULONG_PTR)pData + pData->MessageOffset);
190         m_hThread = CreateThread(NULL, 0, s_HardErrorThreadProc, this, 0, &m_ThreadId);
191         if (!m_hThread)
192         {
193             m_bThreadRunning = FALSE;
194             CloseHandle(m_hThread);
195         }
196     }
197 };
198 
199 class CTaskToolbar :
200     public CWindowImplBaseT< CToolbar<TASK_ITEM>, CControlWinTraits >
201 {
202 public:
UpdateTbButtonSpacing(IN BOOL bHorizontal,IN BOOL bThemed,IN UINT uiRows=0,IN UINT uiBtnsPerLine=0)203     INT UpdateTbButtonSpacing(IN BOOL bHorizontal, IN BOOL bThemed, IN UINT uiRows = 0, IN UINT uiBtnsPerLine = 0)
204     {
205         TBMETRICS tbm;
206 
207         tbm.cbSize = sizeof(tbm);
208         tbm.dwMask = TBMF_BARPAD | TBMF_BUTTONSPACING;
209 
210         tbm.cxBarPad = tbm.cyBarPad = 0;
211 
212         if (bThemed)
213         {
214             tbm.cxButtonSpacing = 0;
215             tbm.cyButtonSpacing = 0;
216         }
217         else
218         {
219             if (bHorizontal || uiBtnsPerLine > 1)
220                 tbm.cxButtonSpacing = (3 * GetSystemMetrics(SM_CXEDGE) / 2);
221             else
222                 tbm.cxButtonSpacing = 0;
223 
224             if (!bHorizontal || uiRows > 1)
225                 tbm.cyButtonSpacing = (3 * GetSystemMetrics(SM_CYEDGE) / 2);
226             else
227                 tbm.cyButtonSpacing = 0;
228         }
229 
230         SetMetrics(&tbm);
231 
232         return tbm.cxButtonSpacing;
233     }
234 
BeginUpdate()235     VOID BeginUpdate()
236     {
237         SetRedraw(FALSE);
238     }
239 
EndUpdate()240     VOID EndUpdate()
241     {
242         SendMessageW(WM_SETREDRAW, TRUE);
243         InvalidateRect(NULL, TRUE);
244     }
245 
SetButtonCommandId(IN INT iButtonIndex,IN INT iCommandId)246     BOOL SetButtonCommandId(IN INT iButtonIndex, IN INT iCommandId)
247     {
248         TBBUTTONINFO tbbi;
249 
250         tbbi.cbSize = sizeof(tbbi);
251         tbbi.dwMask = TBIF_BYINDEX | TBIF_COMMAND;
252         tbbi.idCommand = iCommandId;
253 
254         return SetButtonInfo(iButtonIndex, &tbbi) != 0;
255     }
256 
OnNcHitTestToolbar(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)257     LRESULT OnNcHitTestToolbar(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
258     {
259         POINT pt;
260 
261         /* See if the mouse is on a button */
262         pt.x = GET_X_LPARAM(lParam);
263         pt.y = GET_Y_LPARAM(lParam);
264         ScreenToClient(&pt);
265 
266         INT index = HitTest(&pt);
267         if (index < 0)
268         {
269             /* Make the control appear to be transparent outside of any buttons */
270             return HTTRANSPARENT;
271         }
272 
273         bHandled = FALSE;
274         return 0;
275     }
276 
277 public:
278     BEGIN_MSG_MAP(CNotifyToolbar)
MESSAGE_HANDLER(WM_NCHITTEST,OnNcHitTestToolbar)279         MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTestToolbar)
280     END_MSG_MAP()
281 
282     BOOL Initialize(HWND hWndParent)
283     {
284         DWORD styles = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN |
285             TBSTYLE_TOOLTIPS | TBSTYLE_WRAPABLE | TBSTYLE_LIST | TBSTYLE_TRANSPARENT |
286             CCS_TOP | CCS_NORESIZE | CCS_NODIVIDER;
287 
288         // HACK & FIXME: CORE-18016
289         HWND toolbar = CToolbar::Create(hWndParent, styles);
290         m_hWnd = NULL;
291         return SubclassWindow(toolbar);
292     }
293 };
294 
295 class CTaskSwitchWnd :
296     public CComCoClass<CTaskSwitchWnd>,
297     public CComObjectRootEx<CComMultiThreadModelNoCS>,
298     public CWindowImpl < CTaskSwitchWnd, CWindow, CControlWinTraits >,
299     public IOleWindow
300 {
301     CTaskToolbar m_TaskBar;
302 
303     CComPtr<ITrayWindow> m_Tray;
304 
305     UINT m_ShellHookMsg;
306 
307     WORD m_TaskItemCount;
308     WORD m_AllocatedTaskItems;
309 
310     PTASK_GROUP m_TaskGroups;
311     PTASK_ITEM m_TaskItems;
312     PTASK_ITEM m_ActiveTaskItem;
313 
314     HTHEME m_Theme;
315     UINT m_ButtonsPerLine;
316     WORD m_ButtonCount;
317 
318     HIMAGELIST m_ImageList;
319 
320     BOOL m_IsGroupingEnabled;
321     BOOL m_IsDestroying;
322 
323     SIZE m_ButtonSize;
324 
325     UINT m_uHardErrorMsg;
326     CHardErrorThread m_HardErrorThread;
327 
328 public:
CTaskSwitchWnd()329     CTaskSwitchWnd() :
330         m_ShellHookMsg(NULL),
331         m_TaskItemCount(0),
332         m_AllocatedTaskItems(0),
333         m_TaskGroups(NULL),
334         m_TaskItems(NULL),
335         m_ActiveTaskItem(NULL),
336         m_Theme(NULL),
337         m_ButtonsPerLine(0),
338         m_ButtonCount(0),
339         m_ImageList(NULL),
340         m_IsGroupingEnabled(FALSE),
341         m_IsDestroying(FALSE)
342     {
343         ZeroMemory(&m_ButtonSize, sizeof(m_ButtonSize));
344         m_uHardErrorMsg = RegisterWindowMessageW(L"HardError");
345     }
~CTaskSwitchWnd()346     virtual ~CTaskSwitchWnd() { }
347 
GetWndTextFromTaskItem(IN PTASK_ITEM TaskItem,LPWSTR szBuf,DWORD cchBuf)348     INT GetWndTextFromTaskItem(IN PTASK_ITEM TaskItem, LPWSTR szBuf, DWORD cchBuf)
349     {
350         /* Get the window text without sending a message so we don't hang if an
351            application isn't responding! */
352         return InternalGetWindowText(TaskItem->hWnd, szBuf, cchBuf);
353     }
354 
355 
356 #if DUMP_TASKS != 0
DumpTasks()357     VOID DumpTasks()
358     {
359         PTASK_GROUP CurrentGroup;
360         PTASK_ITEM CurrentTaskItem, LastTaskItem;
361 
362         TRACE("Tasks dump:\n");
363         if (m_IsGroupingEnabled)
364         {
365             CurrentGroup = m_TaskGroups;
366             while (CurrentGroup != NULL)
367             {
368                 TRACE("- Group PID: 0x%p Tasks: %d Index: %d\n", CurrentGroup->dwProcessId, CurrentGroup->dwTaskCount, CurrentGroup->Index);
369 
370                 CurrentTaskItem = m_TaskItems;
371                 LastTaskItem = CurrentTaskItem + m_TaskItemCount;
372                 while (CurrentTaskItem != LastTaskItem)
373                 {
374                     if (CurrentTaskItem->Group == CurrentGroup)
375                     {
376                         TRACE("  + Task hwnd: 0x%p Index: %d\n", CurrentTaskItem->hWnd, CurrentTaskItem->Index);
377                     }
378                     CurrentTaskItem++;
379                 }
380 
381                 CurrentGroup = CurrentGroup->Next;
382             }
383 
384             CurrentTaskItem = m_TaskItems;
385             LastTaskItem = CurrentTaskItem + m_TaskItemCount;
386             while (CurrentTaskItem != LastTaskItem)
387             {
388                 if (CurrentTaskItem->Group == NULL)
389                 {
390                     TRACE("- Task hwnd: 0x%p Index: %d\n", CurrentTaskItem->hWnd, CurrentTaskItem->Index);
391                 }
392                 CurrentTaskItem++;
393             }
394         }
395         else
396         {
397             CurrentTaskItem = m_TaskItems;
398             LastTaskItem = CurrentTaskItem + m_TaskItemCount;
399             while (CurrentTaskItem != LastTaskItem)
400             {
401                 TRACE("- Task hwnd: 0x%p Index: %d\n", CurrentTaskItem->hWnd, CurrentTaskItem->Index);
402                 CurrentTaskItem++;
403             }
404         }
405     }
406 #endif
407 
UpdateIndexesAfter(IN INT iIndex,BOOL bInserted)408     VOID UpdateIndexesAfter(IN INT iIndex, BOOL bInserted)
409     {
410         PTASK_GROUP CurrentGroup;
411         PTASK_ITEM CurrentTaskItem, LastTaskItem;
412         INT NewIndex;
413 
414         int offset = bInserted ? +1 : -1;
415 
416         if (m_IsGroupingEnabled)
417         {
418             /* Update all affected groups */
419             CurrentGroup = m_TaskGroups;
420             while (CurrentGroup != NULL)
421             {
422                 if (CurrentGroup->IsCollapsed &&
423                     CurrentGroup->Index >= iIndex)
424                 {
425                     /* Update the toolbar buttons */
426                     NewIndex = CurrentGroup->Index + offset;
427                     if (m_TaskBar.SetButtonCommandId(CurrentGroup->Index + offset, NewIndex))
428                     {
429                         CurrentGroup->Index = NewIndex;
430                     }
431                     else
432                         CurrentGroup->Index = -1;
433                 }
434 
435                 CurrentGroup = CurrentGroup->Next;
436             }
437         }
438 
439         /* Update all affected task items */
440         CurrentTaskItem = m_TaskItems;
441         LastTaskItem = CurrentTaskItem + m_TaskItemCount;
442         while (CurrentTaskItem != LastTaskItem)
443         {
444             CurrentGroup = CurrentTaskItem->Group;
445             if (CurrentGroup != NULL)
446             {
447                 if (!CurrentGroup->IsCollapsed &&
448                     CurrentTaskItem->Index >= iIndex)
449                 {
450                     goto UpdateTaskItemBtn;
451                 }
452             }
453             else if (CurrentTaskItem->Index >= iIndex)
454             {
455             UpdateTaskItemBtn:
456                 /* Update the toolbar buttons */
457                 NewIndex = CurrentTaskItem->Index + offset;
458                 if (m_TaskBar.SetButtonCommandId(CurrentTaskItem->Index + offset, NewIndex))
459                 {
460                     CurrentTaskItem->Index = NewIndex;
461                 }
462                 else
463                     CurrentTaskItem->Index = -1;
464             }
465 
466             CurrentTaskItem++;
467         }
468     }
469 
470 
UpdateTaskGroupButton(IN PTASK_GROUP TaskGroup)471     INT UpdateTaskGroupButton(IN PTASK_GROUP TaskGroup)
472     {
473         ASSERT(TaskGroup->Index >= 0);
474 
475         /* FIXME: Implement */
476 
477         return TaskGroup->Index;
478     }
479 
ExpandTaskGroup(IN PTASK_GROUP TaskGroup)480     VOID ExpandTaskGroup(IN PTASK_GROUP TaskGroup)
481     {
482         ASSERT(TaskGroup->dwTaskCount > 0);
483         ASSERT(TaskGroup->IsCollapsed);
484         ASSERT(TaskGroup->Index >= 0);
485 
486         /* FIXME: Implement */
487     }
488 
GetWndIcon(HWND hwnd)489     HICON GetWndIcon(HWND hwnd)
490     {
491         HICON hIcon = NULL;
492 
493         /* Retrieve icon by sending a message */
494 #define GET_ICON(type) \
495     SendMessageTimeout(hwnd, WM_GETICON, (type), 0, SMTO_NOTIMEOUTIFNOTHUNG, 100, (PDWORD_PTR)&hIcon)
496 
497         LRESULT bAlive = GET_ICON(g_TaskbarSettings.bSmallIcons ? ICON_SMALL2 : ICON_BIG);
498         if (hIcon)
499             return hIcon;
500 
501         if (bAlive)
502         {
503             bAlive = GET_ICON(ICON_SMALL);
504             if (hIcon)
505                 return hIcon;
506         }
507 
508         if (bAlive)
509         {
510             GET_ICON(g_TaskbarSettings.bSmallIcons ? ICON_BIG : ICON_SMALL2);
511             if (hIcon)
512                 return hIcon;
513         }
514 #undef GET_ICON
515 
516         /* If we failed, retrieve icon from the window class */
517         hIcon = (HICON)GetClassLongPtr(hwnd, g_TaskbarSettings.bSmallIcons ? GCLP_HICONSM : GCLP_HICON);
518         if (hIcon)
519             return hIcon;
520 
521         return (HICON)GetClassLongPtr(hwnd, g_TaskbarSettings.bSmallIcons ? GCLP_HICON : GCLP_HICONSM);
522     }
523 
UpdateTaskItemButton(IN PTASK_ITEM TaskItem)524     INT UpdateTaskItemButton(IN PTASK_ITEM TaskItem)
525     {
526         TBBUTTONINFO tbbi = { 0 };
527         HICON icon;
528         WCHAR windowText[255];
529 
530         ASSERT(TaskItem->Index >= 0);
531 
532         tbbi.cbSize = sizeof(tbbi);
533         tbbi.dwMask = TBIF_BYINDEX | TBIF_STATE | TBIF_TEXT | TBIF_IMAGE;
534         tbbi.fsState = TBSTATE_ENABLED;
535         if (m_ActiveTaskItem == TaskItem)
536             tbbi.fsState |= TBSTATE_CHECKED;
537 
538         if (TaskItem->RenderFlashed)
539             tbbi.fsState |= TBSTATE_MARKED;
540 
541         /* Check if we're updating a button that is the last one in the
542            line. If so, we need to set the TBSTATE_WRAP flag! */
543         if (!m_Tray->IsHorizontal() || (m_ButtonsPerLine != 0 &&
544             (TaskItem->Index + 1) % m_ButtonsPerLine == 0))
545         {
546             tbbi.fsState |= TBSTATE_WRAP;
547         }
548 
549         if (GetWndTextFromTaskItem(TaskItem, windowText, _countof(windowText)) > 0)
550         {
551             tbbi.pszText = windowText;
552         }
553 
554         icon = GetWndIcon(TaskItem->hWnd);
555         if (!icon)
556             icon = static_cast<HICON>(LoadImageW(NULL, MAKEINTRESOURCEW(OIC_SAMPLE), IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE));
557         TaskItem->IconIndex = ImageList_ReplaceIcon(m_ImageList, TaskItem->IconIndex, icon);
558         tbbi.iImage = TaskItem->IconIndex;
559 
560         if (!m_TaskBar.SetButtonInfo(TaskItem->Index, &tbbi))
561         {
562             TaskItem->Index = -1;
563             return -1;
564         }
565 
566         TRACE("Updated button %d for hwnd 0x%p\n", TaskItem->Index, TaskItem->hWnd);
567         return TaskItem->Index;
568     }
569 
RemoveIcon(IN PTASK_ITEM TaskItem)570     VOID RemoveIcon(IN PTASK_ITEM TaskItem)
571     {
572         TBBUTTONINFO tbbi;
573         PTASK_ITEM currentTaskItem, LastItem;
574 
575         if (TaskItem->IconIndex == -1)
576             return;
577 
578         tbbi.cbSize = sizeof(tbbi);
579         tbbi.dwMask = TBIF_IMAGE;
580 
581         currentTaskItem = m_TaskItems;
582         LastItem = currentTaskItem + m_TaskItemCount;
583         while (currentTaskItem != LastItem)
584         {
585             if (currentTaskItem->IconIndex > TaskItem->IconIndex)
586             {
587                 currentTaskItem->IconIndex--;
588                 tbbi.iImage = currentTaskItem->IconIndex;
589 
590                 m_TaskBar.SetButtonInfo(currentTaskItem->Index, &tbbi);
591             }
592             currentTaskItem++;
593         }
594 
595         ImageList_Remove(m_ImageList, TaskItem->IconIndex);
596     }
597 
FindLastTaskItemOfGroup(IN PTASK_GROUP TaskGroup OPTIONAL,IN PTASK_ITEM NewTaskItem OPTIONAL)598     PTASK_ITEM FindLastTaskItemOfGroup(
599         IN PTASK_GROUP TaskGroup  OPTIONAL,
600         IN PTASK_ITEM NewTaskItem  OPTIONAL)
601     {
602         PTASK_ITEM TaskItem, LastTaskItem, FoundTaskItem = NULL;
603         DWORD dwTaskCount;
604 
605         ASSERT(m_IsGroupingEnabled);
606 
607         TaskItem = m_TaskItems;
608         LastTaskItem = TaskItem + m_TaskItemCount;
609 
610         dwTaskCount = (TaskGroup != NULL ? TaskGroup->dwTaskCount : MAX_TASKS_COUNT);
611 
612         ASSERT(dwTaskCount > 0);
613 
614         while (TaskItem != LastTaskItem)
615         {
616             if (TaskItem->Group == TaskGroup)
617             {
618                 if ((NewTaskItem != NULL && TaskItem != NewTaskItem) || NewTaskItem == NULL)
619                 {
620                     FoundTaskItem = TaskItem;
621                 }
622 
623                 if (--dwTaskCount == 0)
624                 {
625                     /* We found the last task item in the group! */
626                     break;
627                 }
628             }
629 
630             TaskItem++;
631         }
632 
633         return FoundTaskItem;
634     }
635 
CalculateTaskItemNewButtonIndex(IN PTASK_ITEM TaskItem)636     INT CalculateTaskItemNewButtonIndex(IN PTASK_ITEM TaskItem)
637     {
638         PTASK_GROUP TaskGroup;
639         PTASK_ITEM LastTaskItem;
640 
641         /* NOTE: This routine assumes that the group is *not* collapsed! */
642 
643         TaskGroup = TaskItem->Group;
644         if (m_IsGroupingEnabled)
645         {
646             if (TaskGroup != NULL)
647             {
648                 ASSERT(TaskGroup->Index < 0);
649                 ASSERT(!TaskGroup->IsCollapsed);
650 
651                 if (TaskGroup->dwTaskCount > 1)
652                 {
653                     LastTaskItem = FindLastTaskItemOfGroup(TaskGroup, TaskItem);
654                     if (LastTaskItem != NULL)
655                     {
656                         /* Since the group is expanded the task items must have an index */
657                         ASSERT(LastTaskItem->Index >= 0);
658 
659                         return LastTaskItem->Index + 1;
660                     }
661                 }
662             }
663             else
664             {
665                 /* Find the last NULL group button. NULL groups are added at the end of the
666                    task item list when grouping is enabled */
667                 LastTaskItem = FindLastTaskItemOfGroup(NULL, TaskItem);
668                 if (LastTaskItem != NULL)
669                 {
670                     ASSERT(LastTaskItem->Index >= 0);
671 
672                     return LastTaskItem->Index + 1;
673                 }
674             }
675         }
676 
677         return m_ButtonCount;
678     }
679 
AddTaskItemButton(IN OUT PTASK_ITEM TaskItem)680     INT AddTaskItemButton(IN OUT PTASK_ITEM TaskItem)
681     {
682         WCHAR windowText[255];
683         TBBUTTON tbBtn = { 0 };
684         INT iIndex;
685         HICON icon;
686 
687         if (TaskItem->Index >= 0)
688         {
689             return UpdateTaskItemButton(TaskItem);
690         }
691 
692         if (TaskItem->Group != NULL &&
693             TaskItem->Group->IsCollapsed)
694         {
695             /* The task group is collapsed, we only need to update the group button */
696             return UpdateTaskGroupButton(TaskItem->Group);
697         }
698 
699         icon = GetWndIcon(TaskItem->hWnd);
700         if (!icon)
701             icon = static_cast<HICON>(LoadImageW(NULL, MAKEINTRESOURCEW(OIC_SAMPLE), IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE));
702         TaskItem->IconIndex = ImageList_ReplaceIcon(m_ImageList, -1, icon);
703 
704         tbBtn.iBitmap = TaskItem->IconIndex;
705         tbBtn.fsState = TBSTATE_ENABLED | TBSTATE_ELLIPSES;
706         tbBtn.fsStyle = BTNS_CHECK | BTNS_NOPREFIX | BTNS_SHOWTEXT;
707         tbBtn.dwData = TaskItem->Index;
708 
709         if (GetWndTextFromTaskItem(TaskItem, windowText, _countof(windowText)) > 0)
710         {
711             tbBtn.iString = (DWORD_PTR) windowText;
712         }
713 
714         /* Find out where to insert the new button */
715         iIndex = CalculateTaskItemNewButtonIndex(TaskItem);
716         ASSERT(iIndex >= 0);
717         tbBtn.idCommand = iIndex;
718 
719         m_TaskBar.BeginUpdate();
720 
721         if (m_TaskBar.InsertButton(iIndex, &tbBtn))
722         {
723             UpdateIndexesAfter(iIndex, TRUE);
724 
725             TRACE("Added button %d for hwnd 0x%p\n", iIndex, TaskItem->hWnd);
726 
727             TaskItem->Index = iIndex;
728             m_ButtonCount++;
729 
730             /* Update button sizes and fix the button wrapping */
731             UpdateButtonsSize(TRUE);
732             return iIndex;
733         }
734 
735         m_TaskBar.EndUpdate();
736 
737         return -1;
738     }
739 
DeleteTaskItemButton(IN OUT PTASK_ITEM TaskItem)740     BOOL DeleteTaskItemButton(IN OUT PTASK_ITEM TaskItem)
741     {
742         PTASK_GROUP TaskGroup;
743         INT iIndex;
744 
745         TaskGroup = TaskItem->Group;
746 
747         if (TaskItem->Index >= 0)
748         {
749             if ((TaskGroup != NULL && !TaskGroup->IsCollapsed) ||
750                 TaskGroup == NULL)
751             {
752                 m_TaskBar.BeginUpdate();
753 
754                 RemoveIcon(TaskItem);
755                 iIndex = TaskItem->Index;
756                 if (m_TaskBar.DeleteButton(iIndex))
757                 {
758                     TaskItem->Index = -1;
759                     m_ButtonCount--;
760 
761                     UpdateIndexesAfter(iIndex, FALSE);
762 
763                     /* Update button sizes and fix the button wrapping */
764                     UpdateButtonsSize(TRUE);
765                     return TRUE;
766                 }
767 
768                 m_TaskBar.EndUpdate();
769             }
770         }
771 
772         return FALSE;
773     }
774 
AddToTaskGroup(IN HWND hWnd)775     PTASK_GROUP AddToTaskGroup(IN HWND hWnd)
776     {
777         DWORD dwProcessId;
778         PTASK_GROUP TaskGroup, *PrevLink;
779 
780         if (!GetWindowThreadProcessId(hWnd,
781             &dwProcessId))
782         {
783             TRACE("Cannot get process id of hwnd 0x%p\n", hWnd);
784             return NULL;
785         }
786 
787         /* Try to find an existing task group */
788         TaskGroup = m_TaskGroups;
789         PrevLink = &m_TaskGroups;
790         while (TaskGroup != NULL)
791         {
792             if (TaskGroup->dwProcessId == dwProcessId)
793             {
794                 TaskGroup->dwTaskCount++;
795                 return TaskGroup;
796             }
797 
798             PrevLink = &TaskGroup->Next;
799             TaskGroup = TaskGroup->Next;
800         }
801 
802         /* Allocate a new task group */
803         TaskGroup = (PTASK_GROUP) HeapAlloc(hProcessHeap,
804             HEAP_ZERO_MEMORY,
805             sizeof(*TaskGroup));
806         if (TaskGroup != NULL)
807         {
808             TaskGroup->dwTaskCount = 1;
809             TaskGroup->dwProcessId = dwProcessId;
810             TaskGroup->Index = -1;
811 
812             /* Add the task group to the list */
813             *PrevLink = TaskGroup;
814         }
815 
816         return TaskGroup;
817     }
818 
RemoveTaskFromTaskGroup(IN OUT PTASK_ITEM TaskItem)819     VOID RemoveTaskFromTaskGroup(IN OUT PTASK_ITEM TaskItem)
820     {
821         PTASK_GROUP TaskGroup, CurrentGroup, *PrevLink;
822 
823         TaskGroup = TaskItem->Group;
824         if (TaskGroup != NULL)
825         {
826             DWORD dwNewTaskCount = --TaskGroup->dwTaskCount;
827             if (dwNewTaskCount == 0)
828             {
829                 /* Find the previous pointer in the chain */
830                 CurrentGroup = m_TaskGroups;
831                 PrevLink = &m_TaskGroups;
832                 while (CurrentGroup != TaskGroup)
833                 {
834                     PrevLink = &CurrentGroup->Next;
835                     CurrentGroup = CurrentGroup->Next;
836                 }
837 
838                 /* Remove the group from the list */
839                 ASSERT(TaskGroup == CurrentGroup);
840                 *PrevLink = TaskGroup->Next;
841 
842                 /* Free the task group */
843                 HeapFree(hProcessHeap,
844                     0,
845                     TaskGroup);
846             }
847             else if (TaskGroup->IsCollapsed &&
848                 TaskGroup->Index >= 0)
849             {
850                 if (dwNewTaskCount > 1)
851                 {
852                     /* FIXME: Check if we should expand the group */
853                     /* Update the task group button */
854                     UpdateTaskGroupButton(TaskGroup);
855                 }
856                 else
857                 {
858                     /* Expand the group of one task button to a task button */
859                     ExpandTaskGroup(TaskGroup);
860                 }
861             }
862         }
863     }
864 
FindTaskItem(IN HWND hWnd)865     PTASK_ITEM FindTaskItem(IN HWND hWnd)
866     {
867         PTASK_ITEM TaskItem, LastItem;
868 
869         TaskItem = m_TaskItems;
870         LastItem = TaskItem + m_TaskItemCount;
871         while (TaskItem != LastItem)
872         {
873             if (TaskItem->hWnd == hWnd)
874                 return TaskItem;
875 
876             TaskItem++;
877         }
878 
879         return NULL;
880     }
881 
FindOtherTaskItem(IN HWND hWnd)882     PTASK_ITEM FindOtherTaskItem(IN HWND hWnd)
883     {
884         PTASK_ITEM LastItem, TaskItem;
885         PTASK_GROUP TaskGroup;
886         DWORD dwProcessId;
887 
888         if (!GetWindowThreadProcessId(hWnd, &dwProcessId))
889         {
890             return NULL;
891         }
892 
893         /* Try to find another task that belongs to the same
894            process as the given window */
895         TaskItem = m_TaskItems;
896         LastItem = TaskItem + m_TaskItemCount;
897         while (TaskItem != LastItem)
898         {
899             TaskGroup = TaskItem->Group;
900             if (TaskGroup != NULL)
901             {
902                 if (TaskGroup->dwProcessId == dwProcessId)
903                     return TaskItem;
904             }
905             else
906             {
907                 DWORD dwProcessIdTask;
908 
909                 if (GetWindowThreadProcessId(TaskItem->hWnd,
910                     &dwProcessIdTask) &&
911                     dwProcessIdTask == dwProcessId)
912                 {
913                     return TaskItem;
914                 }
915             }
916 
917             TaskItem++;
918         }
919 
920         return NULL;
921     }
922 
AllocTaskItem()923     PTASK_ITEM AllocTaskItem()
924     {
925         if (m_TaskItemCount >= MAX_TASKS_COUNT)
926         {
927             /* We need the most significant bit in 16 bit command IDs to indicate whether it
928                is a task group or task item. WM_COMMAND limits command IDs to 16 bits! */
929             return NULL;
930         }
931 
932         ASSERT(m_AllocatedTaskItems >= m_TaskItemCount);
933 
934         if (m_TaskItemCount == 0)
935         {
936             m_TaskItems = (PTASK_ITEM) HeapAlloc(hProcessHeap,
937                 0,
938                 TASK_ITEM_ARRAY_ALLOC * sizeof(*m_TaskItems));
939             if (m_TaskItems != NULL)
940             {
941                 m_AllocatedTaskItems = TASK_ITEM_ARRAY_ALLOC;
942             }
943             else
944                 return NULL;
945         }
946         else if (m_TaskItemCount >= m_AllocatedTaskItems)
947         {
948             PTASK_ITEM NewArray;
949             SIZE_T NewArrayLength, ActiveTaskItemIndex;
950 
951             NewArrayLength = m_AllocatedTaskItems + TASK_ITEM_ARRAY_ALLOC;
952 
953             NewArray = (PTASK_ITEM) HeapReAlloc(hProcessHeap,
954                 0,
955                 m_TaskItems,
956                 NewArrayLength * sizeof(*m_TaskItems));
957             if (NewArray != NULL)
958             {
959                 if (m_ActiveTaskItem != NULL)
960                 {
961                     /* Fixup the ActiveTaskItem pointer */
962                     ActiveTaskItemIndex = m_ActiveTaskItem - m_TaskItems;
963                     m_ActiveTaskItem = NewArray + ActiveTaskItemIndex;
964                 }
965                 m_AllocatedTaskItems = (WORD) NewArrayLength;
966                 m_TaskItems = NewArray;
967             }
968             else
969                 return NULL;
970         }
971 
972         return m_TaskItems + m_TaskItemCount++;
973     }
974 
FreeTaskItem(IN OUT PTASK_ITEM TaskItem)975     VOID FreeTaskItem(IN OUT PTASK_ITEM TaskItem)
976     {
977         WORD wIndex;
978 
979         if (TaskItem == m_ActiveTaskItem)
980             m_ActiveTaskItem = NULL;
981 
982         wIndex = (WORD) (TaskItem - m_TaskItems);
983         if (wIndex + 1 < m_TaskItemCount)
984         {
985             MoveMemory(TaskItem,
986                 TaskItem + 1,
987                 (m_TaskItemCount - wIndex - 1) * sizeof(*TaskItem));
988         }
989 
990         m_TaskItemCount--;
991     }
992 
DeleteTaskItem(IN OUT PTASK_ITEM TaskItem)993     VOID DeleteTaskItem(IN OUT PTASK_ITEM TaskItem)
994     {
995         if (!m_IsDestroying)
996         {
997             /* Delete the task button from the toolbar */
998             DeleteTaskItemButton(TaskItem);
999         }
1000 
1001         /* Remove the task from it's group */
1002         RemoveTaskFromTaskGroup(TaskItem);
1003 
1004         /* Free the task item */
1005         FreeTaskItem(TaskItem);
1006     }
1007 
CheckActivateTaskItem(IN OUT PTASK_ITEM TaskItem)1008     VOID CheckActivateTaskItem(IN OUT PTASK_ITEM TaskItem)
1009     {
1010         PTASK_ITEM CurrentTaskItem;
1011         PTASK_GROUP TaskGroup = NULL;
1012 
1013         CurrentTaskItem = m_ActiveTaskItem;
1014 
1015         if (TaskItem != NULL)
1016             TaskGroup = TaskItem->Group;
1017 
1018         if (m_IsGroupingEnabled &&
1019             TaskGroup != NULL &&
1020             TaskGroup->IsCollapsed)
1021         {
1022             /* FIXME */
1023             return;
1024         }
1025 
1026         if (CurrentTaskItem != NULL)
1027         {
1028             PTASK_GROUP CurrentTaskGroup;
1029 
1030             if (CurrentTaskItem == TaskItem)
1031                 return;
1032 
1033             CurrentTaskGroup = CurrentTaskItem->Group;
1034 
1035             if (m_IsGroupingEnabled &&
1036                 CurrentTaskGroup != NULL &&
1037                 CurrentTaskGroup->IsCollapsed)
1038             {
1039                 if (CurrentTaskGroup == TaskGroup)
1040                     return;
1041 
1042                 /* FIXME */
1043             }
1044             else
1045             {
1046                 m_ActiveTaskItem = NULL;
1047                 if (CurrentTaskItem->Index >= 0)
1048                 {
1049                     UpdateTaskItemButton(CurrentTaskItem);
1050                 }
1051             }
1052         }
1053 
1054         m_ActiveTaskItem = TaskItem;
1055 
1056         if (TaskItem != NULL && TaskItem->Index >= 0)
1057         {
1058             UpdateTaskItemButton(TaskItem);
1059         }
1060         else if (TaskItem == NULL)
1061         {
1062             TRACE("Active TaskItem now NULL\n");
1063         }
1064     }
1065 
FindTaskItemByIndex(IN INT Index)1066     PTASK_ITEM FindTaskItemByIndex(IN INT Index)
1067     {
1068         PTASK_ITEM TaskItem, LastItem;
1069 
1070         TaskItem = m_TaskItems;
1071         LastItem = TaskItem + m_TaskItemCount;
1072         while (TaskItem != LastItem)
1073         {
1074             if (TaskItem->Index == Index)
1075                 return TaskItem;
1076 
1077             TaskItem++;
1078         }
1079 
1080         return NULL;
1081     }
1082 
FindTaskGroupByIndex(IN INT Index)1083     PTASK_GROUP FindTaskGroupByIndex(IN INT Index)
1084     {
1085         PTASK_GROUP CurrentGroup;
1086 
1087         CurrentGroup = m_TaskGroups;
1088         while (CurrentGroup != NULL)
1089         {
1090             if (CurrentGroup->Index == Index)
1091                 break;
1092 
1093             CurrentGroup = CurrentGroup->Next;
1094         }
1095 
1096         return CurrentGroup;
1097     }
1098 
AddTask(IN HWND hWnd)1099     BOOL AddTask(IN HWND hWnd)
1100     {
1101         PTASK_ITEM TaskItem;
1102 
1103         if (!::IsWindow(hWnd) || m_Tray->IsSpecialHWND(hWnd))
1104             return FALSE;
1105 
1106         TaskItem = FindTaskItem(hWnd);
1107         if (TaskItem == NULL)
1108         {
1109             TRACE("Add window 0x%p\n", hWnd);
1110             TaskItem = AllocTaskItem();
1111             if (TaskItem != NULL)
1112             {
1113                 ZeroMemory(TaskItem, sizeof(*TaskItem));
1114                 TaskItem->hWnd = hWnd;
1115                 TaskItem->Index = -1;
1116                 TaskItem->Group = AddToTaskGroup(hWnd);
1117 
1118                 if (!m_IsDestroying)
1119                 {
1120                     AddTaskItemButton(TaskItem);
1121                 }
1122             }
1123         }
1124 
1125         return TaskItem != NULL;
1126     }
1127 
ActivateTaskItem(IN OUT PTASK_ITEM TaskItem OPTIONAL)1128     BOOL ActivateTaskItem(IN OUT PTASK_ITEM TaskItem  OPTIONAL)
1129     {
1130         if (TaskItem != NULL)
1131         {
1132             TRACE("Activate window 0x%p on button %d\n", TaskItem->hWnd, TaskItem->Index);
1133         }
1134 
1135         CheckActivateTaskItem(TaskItem);
1136         return FALSE;
1137     }
1138 
ActivateTask(IN HWND hWnd)1139     BOOL ActivateTask(IN HWND hWnd)
1140     {
1141         PTASK_ITEM TaskItem;
1142 
1143         if (!hWnd)
1144         {
1145             return ActivateTaskItem(NULL);
1146         }
1147 
1148         TaskItem = FindTaskItem(hWnd);
1149         if (TaskItem == NULL)
1150         {
1151             TaskItem = FindOtherTaskItem(hWnd);
1152         }
1153 
1154         if (TaskItem == NULL)
1155         {
1156             WARN("Activate window 0x%p, could not find task\n", hWnd);
1157             RefreshWindowList();
1158         }
1159 
1160         return ActivateTaskItem(TaskItem);
1161     }
1162 
DeleteTask(IN HWND hWnd)1163     BOOL DeleteTask(IN HWND hWnd)
1164     {
1165         PTASK_ITEM TaskItem;
1166 
1167         TaskItem = FindTaskItem(hWnd);
1168         if (TaskItem != NULL)
1169         {
1170             TRACE("Delete window 0x%p on button %d\n", hWnd, TaskItem->Index);
1171             DeleteTaskItem(TaskItem);
1172             return TRUE;
1173         }
1174         //else
1175         //TRACE("Failed to delete window 0x%p\n", hWnd);
1176 
1177         return FALSE;
1178     }
1179 
DeleteAllTasks()1180     VOID DeleteAllTasks()
1181     {
1182         PTASK_ITEM CurrentTask;
1183 
1184         if (m_TaskItemCount > 0)
1185         {
1186             CurrentTask = m_TaskItems + m_TaskItemCount;
1187             do
1188             {
1189                 DeleteTaskItem(--CurrentTask);
1190             } while (CurrentTask != m_TaskItems);
1191         }
1192     }
1193 
FlashTaskItem(IN OUT PTASK_ITEM TaskItem)1194     VOID FlashTaskItem(IN OUT PTASK_ITEM TaskItem)
1195     {
1196         TaskItem->RenderFlashed = 1;
1197         UpdateTaskItemButton(TaskItem);
1198     }
1199 
FlashTask(IN HWND hWnd)1200     BOOL FlashTask(IN HWND hWnd)
1201     {
1202         PTASK_ITEM TaskItem;
1203 
1204         TaskItem = FindTaskItem(hWnd);
1205         if (TaskItem != NULL)
1206         {
1207             TRACE("Flashing window 0x%p on button %d\n", hWnd, TaskItem->Index);
1208             FlashTaskItem(TaskItem);
1209             return TRUE;
1210         }
1211 
1212         return FALSE;
1213     }
1214 
RedrawTaskItem(IN OUT PTASK_ITEM TaskItem)1215     VOID RedrawTaskItem(IN OUT PTASK_ITEM TaskItem)
1216     {
1217         PTASK_GROUP TaskGroup;
1218 
1219         TaskGroup = TaskItem->Group;
1220         if (m_IsGroupingEnabled && TaskGroup != NULL)
1221         {
1222             if (TaskGroup->IsCollapsed && TaskGroup->Index >= 0)
1223             {
1224                 UpdateTaskGroupButton(TaskGroup);
1225             }
1226             else if (TaskItem->Index >= 0)
1227             {
1228                 goto UpdateTaskItem;
1229             }
1230         }
1231         else if (TaskItem->Index >= 0)
1232         {
1233         UpdateTaskItem:
1234             TaskItem->RenderFlashed = 0;
1235             UpdateTaskItemButton(TaskItem);
1236         }
1237     }
1238 
1239 
RedrawTask(IN HWND hWnd)1240     BOOL RedrawTask(IN HWND hWnd)
1241     {
1242         PTASK_ITEM TaskItem;
1243 
1244         TaskItem = FindTaskItem(hWnd);
1245         if (TaskItem != NULL)
1246         {
1247             RedrawTaskItem(TaskItem);
1248             return TRUE;
1249         }
1250 
1251         return FALSE;
1252     }
1253 
UpdateButtonsSize(IN BOOL bRedrawDisabled)1254     VOID UpdateButtonsSize(IN BOOL bRedrawDisabled)
1255     {
1256         RECT rcClient;
1257         UINT uiRows, uiMax, uiMin, uiBtnsPerLine, ui;
1258         LONG NewBtnSize;
1259         BOOL Horizontal;
1260 
1261         /* Update the size of the image list if needed */
1262         int cx, cy;
1263         ImageList_GetIconSize(m_ImageList, &cx, &cy);
1264         if (cx != GetSystemMetrics(g_TaskbarSettings.bSmallIcons ? SM_CXSMICON : SM_CXICON) ||
1265             cy != GetSystemMetrics(g_TaskbarSettings.bSmallIcons ? SM_CYSMICON : SM_CYICON))
1266         {
1267             ImageList_SetIconSize(m_ImageList,
1268                                   GetSystemMetrics(g_TaskbarSettings.bSmallIcons ? SM_CXSMICON : SM_CXICON),
1269                                   GetSystemMetrics(g_TaskbarSettings.bSmallIcons ? SM_CYSMICON : SM_CYICON));
1270 
1271             /* SetIconSize removes all icons so we have to reinsert them */
1272             PTASK_ITEM TaskItem = m_TaskItems;
1273             PTASK_ITEM LastTaskItem = m_TaskItems + m_TaskItemCount;
1274             while (TaskItem != LastTaskItem)
1275             {
1276                 TaskItem->IconIndex = -1;
1277                 UpdateTaskItemButton(TaskItem);
1278 
1279                 TaskItem++;
1280             }
1281             m_TaskBar.SetImageList(m_ImageList);
1282         }
1283 
1284         if (GetClientRect(&rcClient) && !IsRectEmpty(&rcClient))
1285         {
1286             if (m_ButtonCount > 0)
1287             {
1288                 Horizontal = m_Tray->IsHorizontal();
1289 
1290                 if (Horizontal)
1291                 {
1292                     TBMETRICS tbm = { 0 };
1293                     tbm.cbSize = sizeof(tbm);
1294                     tbm.dwMask = TBMF_BUTTONSPACING;
1295                     m_TaskBar.GetMetrics(&tbm);
1296 
1297                     if (m_ButtonSize.cy + tbm.cyButtonSpacing != 0)
1298                         uiRows = (rcClient.bottom + tbm.cyButtonSpacing) / (m_ButtonSize.cy + tbm.cyButtonSpacing);
1299                     else
1300                         uiRows = 1;
1301 
1302                     if (uiRows == 0)
1303                         uiRows = 1;
1304 
1305                     uiBtnsPerLine = (m_ButtonCount + uiRows - 1) / uiRows;
1306                 }
1307                 else
1308                 {
1309                     uiBtnsPerLine = 1;
1310                     uiRows = m_ButtonCount;
1311                 }
1312 
1313                 if (!bRedrawDisabled)
1314                     m_TaskBar.BeginUpdate();
1315 
1316                 /* We might need to update the button spacing */
1317                 int cxButtonSpacing = m_TaskBar.UpdateTbButtonSpacing(
1318                     Horizontal, m_Theme != NULL,
1319                     uiRows, uiBtnsPerLine);
1320 
1321                 /* Determine the minimum and maximum width of a button */
1322                 uiMin = GetSystemMetrics(SM_CXSIZE) + (2 * GetSystemMetrics(SM_CXEDGE));
1323                 if (Horizontal)
1324                 {
1325                     uiMax = GetSystemMetrics(SM_CXMINIMIZED);
1326 
1327                     /* Calculate the ideal width and make sure it's within the allowed range */
1328                     NewBtnSize = (rcClient.right - (uiBtnsPerLine * cxButtonSpacing)) / uiBtnsPerLine;
1329 
1330                     if (NewBtnSize < (LONG) uiMin)
1331                         NewBtnSize = uiMin;
1332                     if (NewBtnSize >(LONG)uiMax)
1333                         NewBtnSize = uiMax;
1334 
1335                     /* Recalculate how many buttons actually fit into one line */
1336                     uiBtnsPerLine = rcClient.right / (NewBtnSize + cxButtonSpacing);
1337                     if (uiBtnsPerLine == 0)
1338                         uiBtnsPerLine++;
1339                 }
1340                 else
1341                 {
1342                     NewBtnSize = uiMax = rcClient.right;
1343                 }
1344 
1345                 m_ButtonSize.cx = NewBtnSize;
1346 
1347                 m_ButtonsPerLine = uiBtnsPerLine;
1348 
1349                 for (ui = 0; ui != m_ButtonCount; ui++)
1350                 {
1351                     TBBUTTONINFOW tbbi = { 0 };
1352                     tbbi.cbSize = sizeof(tbbi);
1353                     tbbi.dwMask = TBIF_BYINDEX | TBIF_SIZE | TBIF_STATE;
1354                     tbbi.cx = (INT) NewBtnSize;
1355                     tbbi.fsState = TBSTATE_ENABLED;
1356 
1357                     /* Check if we're updating a button that is the last one in the
1358                        line. If so, we need to set the TBSTATE_WRAP flag! */
1359                     if (Horizontal)
1360                     {
1361                         if ((ui + 1) % uiBtnsPerLine == 0)
1362                             tbbi.fsState |= TBSTATE_WRAP;
1363                     }
1364                     else
1365                     {
1366                         tbbi.fsState |= TBSTATE_WRAP;
1367                     }
1368 
1369                     if (m_ActiveTaskItem != NULL &&
1370                         m_ActiveTaskItem->Index == (INT)ui)
1371                     {
1372                         tbbi.fsState |= TBSTATE_CHECKED;
1373                     }
1374 
1375                     m_TaskBar.SetButtonInfo(ui, &tbbi);
1376                 }
1377             }
1378             else
1379             {
1380                 m_ButtonsPerLine = 0;
1381                 m_ButtonSize.cx = 0;
1382             }
1383         }
1384 
1385         // FIXME: This seems to be enabling redraws prematurely, but moving it to its right place doesn't work!
1386         m_TaskBar.EndUpdate();
1387     }
1388 
EnumWindowsProc(IN HWND hWnd)1389     BOOL CALLBACK EnumWindowsProc(IN HWND hWnd)
1390     {
1391         if (m_Tray->IsTaskWnd(hWnd))
1392         {
1393             TRACE("Adding task for %p...\n", hWnd);
1394             AddTask(hWnd);
1395         }
1396         return TRUE;
1397     }
1398 
s_EnumWindowsProc(IN HWND hWnd,IN LPARAM lParam)1399     static BOOL CALLBACK s_EnumWindowsProc(IN HWND hWnd, IN LPARAM lParam)
1400     {
1401         CTaskSwitchWnd * This = (CTaskSwitchWnd *) lParam;
1402 
1403         return This->EnumWindowsProc(hWnd);
1404     }
1405 
RefreshWindowList()1406     BOOL RefreshWindowList()
1407     {
1408         TRACE("Refreshing window list...\n");
1409         /* Add all windows to the toolbar */
1410         return EnumWindows(s_EnumWindowsProc, (LPARAM)this);
1411     }
1412 
OnThemeChanged(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)1413     LRESULT OnThemeChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1414     {
1415         TRACE("OmThemeChanged\n");
1416 
1417         if (m_Theme)
1418             CloseThemeData(m_Theme);
1419 
1420         if (IsThemeActive())
1421             m_Theme = OpenThemeData(m_hWnd, L"TaskBand");
1422         else
1423             m_Theme = NULL;
1424 
1425         return TRUE;
1426     }
1427 
OnCreate(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)1428     LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1429     {
1430         if (!m_TaskBar.Initialize(m_hWnd))
1431             return FALSE;
1432 
1433         SetWindowTheme(m_TaskBar.m_hWnd, L"TaskBand", NULL);
1434 
1435         m_ImageList = ImageList_Create(GetSystemMetrics(g_TaskbarSettings.bSmallIcons ? SM_CXSMICON : SM_CXICON),
1436                                        GetSystemMetrics(g_TaskbarSettings.bSmallIcons ? SM_CYSMICON : SM_CYICON),
1437                                        ILC_COLOR32 | ILC_MASK, 0, 1000);
1438         m_TaskBar.SetImageList(m_ImageList);
1439 
1440         /* Set proper spacing between buttons */
1441         m_TaskBar.UpdateTbButtonSpacing(m_Tray->IsHorizontal(), m_Theme != NULL);
1442 
1443         /* Register the shell hook */
1444         m_ShellHookMsg = RegisterWindowMessageW(L"SHELLHOOK");
1445 
1446         TRACE("ShellHookMsg got assigned number %d\n", m_ShellHookMsg);
1447 
1448         RegisterShellHook(m_hWnd, 3); /* 1 if no NT! We're targeting NT so we don't care! */
1449 
1450         RefreshWindowList();
1451 
1452         /* Recalculate the button size */
1453         UpdateButtonsSize(FALSE);
1454 
1455 #if DUMP_TASKS != 0
1456         SetTimer(hwnd, 1, 5000, NULL);
1457 #endif
1458         return TRUE;
1459     }
1460 
OnDestroy(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)1461     LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1462     {
1463         m_IsDestroying = TRUE;
1464 
1465         /* Unregister the shell hook */
1466         RegisterShellHook(m_hWnd, FALSE);
1467 
1468         CloseThemeData(m_Theme);
1469         DeleteAllTasks();
1470         return TRUE;
1471     }
1472 
SendPulseToTray(BOOL bDelete,HWND hwndActive)1473     VOID SendPulseToTray(BOOL bDelete, HWND hwndActive)
1474     {
1475         HWND hwndTray = m_Tray->GetHWND();
1476         ::SendMessage(hwndTray, TWM_PULSE, bDelete, (LPARAM)hwndActive);
1477     }
1478 
HandleAppCommand(IN WPARAM wParam,IN LPARAM lParam)1479     BOOL HandleAppCommand(IN WPARAM wParam, IN LPARAM lParam)
1480     {
1481         BOOL Ret = FALSE;
1482 
1483         switch (GET_APPCOMMAND_LPARAM(lParam))
1484         {
1485         case APPCOMMAND_BROWSER_SEARCH:
1486             Ret = SHFindFiles(NULL,
1487                 NULL);
1488             break;
1489 
1490         case APPCOMMAND_BROWSER_HOME:
1491         case APPCOMMAND_LAUNCH_MAIL:
1492         default:
1493             TRACE("Shell app command %d unhandled!\n", (INT) GET_APPCOMMAND_LPARAM(lParam));
1494             break;
1495         }
1496 
1497         return Ret;
1498     }
1499 
OnShellHook(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)1500     LRESULT OnShellHook(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1501     {
1502         BOOL Ret = FALSE;
1503 
1504         /* In case the shell hook wasn't registered properly, ignore WM_NULLs*/
1505         if (uMsg == 0)
1506         {
1507             bHandled = FALSE;
1508             return 0;
1509         }
1510 
1511         TRACE("Received shell hook message: wParam=%08lx, lParam=%08lx\n", wParam, lParam);
1512 
1513         switch ((INT) wParam)
1514         {
1515         case HSHELL_APPCOMMAND:
1516             Ret = HandleAppCommand(wParam, lParam);
1517             break;
1518 
1519         case HSHELL_WINDOWCREATED:
1520             SendPulseToTray(FALSE, (HWND)lParam);
1521             AddTask((HWND) lParam);
1522             break;
1523 
1524         case HSHELL_WINDOWDESTROYED:
1525             /* The window still exists! Delay destroying it a bit */
1526             SendPulseToTray(TRUE, (HWND)lParam);
1527             DeleteTask((HWND)lParam);
1528             break;
1529 
1530         case HSHELL_RUDEAPPACTIVATED:
1531         case HSHELL_WINDOWACTIVATED:
1532             SendPulseToTray(FALSE, (HWND)lParam);
1533             ActivateTask((HWND)lParam);
1534             break;
1535 
1536         case HSHELL_FLASH:
1537             FlashTask((HWND) lParam);
1538             break;
1539 
1540         case HSHELL_REDRAW:
1541             RedrawTask((HWND) lParam);
1542             break;
1543 
1544         case HSHELL_TASKMAN:
1545             ::PostMessage(m_Tray->GetHWND(), TWM_OPENSTARTMENU, 0, 0);
1546             break;
1547 
1548         case HSHELL_ACTIVATESHELLWINDOW:
1549             ::SwitchToThisWindow(m_Tray->GetHWND(), TRUE);
1550             ::SetForegroundWindow(m_Tray->GetHWND());
1551             break;
1552 
1553         case HSHELL_LANGUAGE:
1554         case HSHELL_SYSMENU:
1555         case HSHELL_ENDTASK:
1556         case HSHELL_ACCESSIBILITYSTATE:
1557         case HSHELL_WINDOWREPLACED:
1558         case HSHELL_WINDOWREPLACING:
1559 
1560         case HSHELL_GETMINRECT:
1561         default:
1562         {
1563 #if DEBUG_SHELL_HOOK
1564             int i, found;
1565             for (i = 0, found = 0; i != _countof(hshell_msg); i++)
1566             {
1567                 if (hshell_msg[i].msg == (INT) wParam)
1568                 {
1569                     TRACE("Shell message %ws unhandled (lParam = 0x%p)!\n", hshell_msg[i].msg_name, lParam);
1570                     found = 1;
1571                     break;
1572                 }
1573             }
1574             if (found)
1575                 break;
1576 #endif
1577             TRACE("Shell message %d unhandled (lParam = 0x%p)!\n", (INT) wParam, lParam);
1578             break;
1579         }
1580         }
1581 
1582         return Ret;
1583     }
1584 
HandleTaskItemClick(IN OUT PTASK_ITEM TaskItem)1585     VOID HandleTaskItemClick(IN OUT PTASK_ITEM TaskItem)
1586     {
1587         BOOL bIsMinimized;
1588         BOOL bIsActive;
1589 
1590         if (::IsWindow(TaskItem->hWnd))
1591         {
1592             bIsMinimized = ::IsIconic(TaskItem->hWnd);
1593             bIsActive = (TaskItem == m_ActiveTaskItem);
1594 
1595             TRACE("Active TaskItem %p, selected TaskItem %p\n", m_ActiveTaskItem, TaskItem);
1596             if (m_ActiveTaskItem)
1597                 TRACE("Active TaskItem hWnd=%p, TaskItem hWnd %p\n", m_ActiveTaskItem->hWnd, TaskItem->hWnd);
1598 
1599             TRACE("Valid button clicked. HWND=%p, IsMinimized=%s, IsActive=%s...\n",
1600                 TaskItem->hWnd, bIsMinimized ? "Yes" : "No", bIsActive ? "Yes" : "No");
1601 
1602             if (!bIsMinimized && bIsActive)
1603             {
1604                 if (!::IsHungAppWindow(TaskItem->hWnd))
1605                     ::ShowWindowAsync(TaskItem->hWnd, SW_MINIMIZE);
1606                 TRACE("Valid button clicked. App window Minimized.\n");
1607             }
1608             else
1609             {
1610                 ::SwitchToThisWindow(TaskItem->hWnd, TRUE);
1611 
1612                 TRACE("Valid button clicked. App window Restored.\n");
1613             }
1614         }
1615     }
1616 
HandleTaskGroupClick(IN OUT PTASK_GROUP TaskGroup)1617     VOID HandleTaskGroupClick(IN OUT PTASK_GROUP TaskGroup)
1618     {
1619         /* TODO: Show task group menu */
1620     }
1621 
HandleButtonClick(IN WORD wIndex)1622     BOOL HandleButtonClick(IN WORD wIndex)
1623     {
1624         PTASK_ITEM TaskItem;
1625         PTASK_GROUP TaskGroup;
1626 
1627         if (m_IsGroupingEnabled)
1628         {
1629             TaskGroup = FindTaskGroupByIndex((INT) wIndex);
1630             if (TaskGroup != NULL && TaskGroup->IsCollapsed)
1631             {
1632                 HandleTaskGroupClick(TaskGroup);
1633                 return TRUE;
1634             }
1635         }
1636 
1637         TaskItem = FindTaskItemByIndex((INT) wIndex);
1638         if (TaskItem != NULL)
1639         {
1640             SendPulseToTray(FALSE, TaskItem->hWnd);
1641             HandleTaskItemClick(TaskItem);
1642             return TRUE;
1643         }
1644 
1645         return FALSE;
1646     }
1647 
1648     static VOID CALLBACK
SendAsyncProc(HWND hwnd,UINT uMsg,DWORD_PTR dwData,LRESULT lResult)1649     SendAsyncProc(HWND hwnd, UINT uMsg, DWORD_PTR dwData, LRESULT lResult)
1650     {
1651         ::PostMessageW(hwnd, WM_NULL, 0, 0);
1652     }
1653 
HandleTaskItemRightClick(IN OUT PTASK_ITEM TaskItem)1654     VOID HandleTaskItemRightClick(IN OUT PTASK_ITEM TaskItem)
1655     {
1656         POINT pt;
1657         GetCursorPos(&pt);
1658 
1659         SetForegroundWindow(TaskItem->hWnd);
1660 
1661         ActivateTask(TaskItem->hWnd);
1662 
1663         if (GetForegroundWindow() != TaskItem->hWnd)
1664             ERR("HandleTaskItemRightClick detected the window did not become foreground\n");
1665 
1666         ::SendMessageCallbackW(TaskItem->hWnd, WM_POPUPSYSTEMMENU, 0, MAKELPARAM(pt.x, pt.y),
1667                                SendAsyncProc, (ULONG_PTR)TaskItem);
1668     }
1669 
HandleTaskGroupRightClick(IN OUT PTASK_GROUP TaskGroup)1670     VOID HandleTaskGroupRightClick(IN OUT PTASK_GROUP TaskGroup)
1671     {
1672         /* TODO: Show task group right click menu */
1673     }
1674 
HandleButtonRightClick(IN WORD wIndex)1675     BOOL HandleButtonRightClick(IN WORD wIndex)
1676     {
1677         PTASK_ITEM TaskItem;
1678         PTASK_GROUP TaskGroup;
1679         if (m_IsGroupingEnabled)
1680         {
1681             TaskGroup = FindTaskGroupByIndex((INT) wIndex);
1682             if (TaskGroup != NULL && TaskGroup->IsCollapsed)
1683             {
1684                 HandleTaskGroupRightClick(TaskGroup);
1685                 return TRUE;
1686             }
1687         }
1688 
1689         TaskItem = FindTaskItemByIndex((INT) wIndex);
1690 
1691         if (TaskItem != NULL)
1692         {
1693             HandleTaskItemRightClick(TaskItem);
1694             return TRUE;
1695         }
1696 
1697         return FALSE;
1698     }
1699 
1700 
HandleItemPaint(IN OUT NMTBCUSTOMDRAW * nmtbcd)1701     LRESULT HandleItemPaint(IN OUT NMTBCUSTOMDRAW *nmtbcd)
1702     {
1703         LRESULT Ret = CDRF_DODEFAULT;
1704         PTASK_GROUP TaskGroup;
1705         PTASK_ITEM TaskItem;
1706 
1707         TaskItem = FindTaskItemByIndex((INT) nmtbcd->nmcd.dwItemSpec);
1708         TaskGroup = FindTaskGroupByIndex((INT) nmtbcd->nmcd.dwItemSpec);
1709         if (TaskGroup == NULL && TaskItem != NULL)
1710         {
1711             ASSERT(TaskItem != NULL);
1712 
1713             if (TaskItem != NULL && ::IsWindow(TaskItem->hWnd))
1714             {
1715                 /* Make the entire button flashing if necessary */
1716                 if (nmtbcd->nmcd.uItemState & CDIS_MARKED)
1717                 {
1718                     Ret = TBCDRF_NOBACKGROUND;
1719                     if (!m_Theme)
1720                     {
1721                         SelectObject(nmtbcd->nmcd.hdc, GetSysColorBrush(COLOR_HIGHLIGHT));
1722                         Rectangle(nmtbcd->nmcd.hdc,
1723                             nmtbcd->nmcd.rc.left,
1724                             nmtbcd->nmcd.rc.top,
1725                             nmtbcd->nmcd.rc.right,
1726                             nmtbcd->nmcd.rc.bottom);
1727                     }
1728                     else
1729                     {
1730                         DrawThemeBackground(m_Theme, nmtbcd->nmcd.hdc, TDP_FLASHBUTTON, 0, &nmtbcd->nmcd.rc, 0);
1731                     }
1732                     nmtbcd->clrText = GetSysColor(COLOR_HIGHLIGHTTEXT);
1733                     return Ret;
1734                 }
1735             }
1736         }
1737         else if (TaskGroup != NULL)
1738         {
1739             /* FIXME: Implement painting for task groups */
1740         }
1741         return Ret;
1742     }
1743 
HandleToolbarNotification(IN const NMHDR * nmh)1744     LRESULT HandleToolbarNotification(IN const NMHDR *nmh)
1745     {
1746         LRESULT Ret = 0;
1747 
1748         switch (nmh->code)
1749         {
1750         case NM_CUSTOMDRAW:
1751         {
1752             LPNMTBCUSTOMDRAW nmtbcd = (LPNMTBCUSTOMDRAW) nmh;
1753 
1754             switch (nmtbcd->nmcd.dwDrawStage)
1755             {
1756 
1757             case CDDS_ITEMPREPAINT:
1758                 Ret = HandleItemPaint(nmtbcd);
1759                 break;
1760 
1761             case CDDS_PREPAINT:
1762                 Ret = CDRF_NOTIFYITEMDRAW;
1763                 break;
1764 
1765             default:
1766                 Ret = CDRF_DODEFAULT;
1767                 break;
1768             }
1769             break;
1770         }
1771         }
1772 
1773         return Ret;
1774     }
1775 
OnEraseBackground(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)1776     LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1777     {
1778         HDC hdc = (HDC) wParam;
1779 
1780         if (!IsAppThemed())
1781         {
1782             bHandled = FALSE;
1783             return 0;
1784         }
1785 
1786         RECT rect;
1787         GetClientRect(&rect);
1788         DrawThemeParentBackground(m_hWnd, hdc, &rect);
1789 
1790         return TRUE;
1791     }
1792 
OnSize(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)1793     LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1794     {
1795         SIZE szClient;
1796 
1797         szClient.cx = LOWORD(lParam);
1798         szClient.cy = HIWORD(lParam);
1799         if (m_TaskBar.m_hWnd != NULL)
1800         {
1801             m_TaskBar.SetWindowPos(NULL, 0, 0, szClient.cx, szClient.cy, SWP_NOZORDER);
1802 
1803             UpdateButtonsSize(FALSE);
1804         }
1805         return TRUE;
1806     }
1807 
OnNcHitTest(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)1808     LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1809     {
1810         LRESULT Ret = TRUE;
1811         /* We want the tray window to be draggable everywhere, so make the control
1812         appear transparent */
1813         Ret = DefWindowProc(uMsg, wParam, lParam);
1814         if (Ret != HTVSCROLL && Ret != HTHSCROLL)
1815             Ret = HTTRANSPARENT;
1816         return Ret;
1817     }
1818 
OnCommand(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)1819     LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1820     {
1821         LRESULT Ret = TRUE;
1822         if (lParam != 0 && (HWND) lParam == m_TaskBar.m_hWnd)
1823         {
1824             HandleButtonClick(LOWORD(wParam));
1825         }
1826         return Ret;
1827     }
1828 
OnNotify(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)1829     LRESULT OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1830     {
1831         LRESULT Ret = TRUE;
1832         const NMHDR *nmh = (const NMHDR *) lParam;
1833 
1834         if (nmh->hwndFrom == m_TaskBar.m_hWnd)
1835         {
1836             Ret = HandleToolbarNotification(nmh);
1837         }
1838         return Ret;
1839     }
1840 
OnUpdateTaskbarPos(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)1841     LRESULT OnUpdateTaskbarPos(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1842     {
1843         /* Update the button spacing */
1844         m_TaskBar.UpdateTbButtonSpacing(m_Tray->IsHorizontal(), m_Theme != NULL);
1845         return TRUE;
1846     }
1847 
OnTaskbarSettingsChanged(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)1848     LRESULT OnTaskbarSettingsChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1849     {
1850         BOOL bSettingsChanged = FALSE;
1851         TaskbarSettings* newSettings = (TaskbarSettings*)lParam;
1852 
1853         if (newSettings->bGroupButtons != g_TaskbarSettings.bGroupButtons)
1854         {
1855             bSettingsChanged = TRUE;
1856             g_TaskbarSettings.bGroupButtons = newSettings->bGroupButtons;
1857             m_IsGroupingEnabled = g_TaskbarSettings.bGroupButtons;
1858         }
1859 
1860         if (newSettings->bSmallIcons != g_TaskbarSettings.bSmallIcons)
1861         {
1862             bSettingsChanged = TRUE;
1863             g_TaskbarSettings.bSmallIcons = newSettings->bSmallIcons;
1864         }
1865 
1866         if (bSettingsChanged)
1867         {
1868             /* Refresh each task item view */
1869             RefreshWindowList();
1870             UpdateButtonsSize(FALSE);
1871         }
1872 
1873         return 0;
1874     }
1875 
OnContextMenu(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)1876     LRESULT OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1877     {
1878         LRESULT Ret = 0;
1879         INT_PTR iBtn = -1;
1880 
1881         if (m_TaskBar.m_hWnd != NULL)
1882         {
1883             POINT pt;
1884 
1885             pt.x = GET_X_LPARAM(lParam);
1886             pt.y = GET_Y_LPARAM(lParam);
1887 
1888             ::ScreenToClient(m_TaskBar.m_hWnd, &pt);
1889 
1890             iBtn = m_TaskBar.HitTest(&pt);
1891             if (iBtn >= 0)
1892             {
1893                 HandleButtonRightClick(iBtn);
1894             }
1895         }
1896         if (iBtn < 0)
1897         {
1898             /* Not on a taskbar button, so forward message to tray */
1899             Ret = SendMessage(m_Tray->GetHWND(), uMsg, wParam, lParam);
1900         }
1901         return Ret;
1902     }
1903 
OnKludgeItemRect(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)1904     LRESULT OnKludgeItemRect(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1905     {
1906         PTASK_ITEM TaskItem = FindTaskItem((HWND) wParam);
1907         if (TaskItem)
1908         {
1909             RECT* prcMinRect = (RECT*) lParam;
1910             RECT rcItem, rcToolbar;
1911             m_TaskBar.GetItemRect(TaskItem->Index, &rcItem);
1912             m_TaskBar.GetWindowRect(&rcToolbar);
1913 
1914             OffsetRect(&rcItem, rcToolbar.left, rcToolbar.top);
1915 
1916             *prcMinRect = rcItem;
1917             return TRUE;
1918         }
1919         return FALSE;
1920     }
1921 
OnMouseActivate(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)1922     LRESULT OnMouseActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1923     {
1924         return MA_NOACTIVATE;
1925     }
1926 
OnTimer(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)1927     LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1928     {
1929 #if DUMP_TASKS != 0
1930         switch (wParam)
1931         {
1932         case 1:
1933             DumpTasks();
1934             break;
1935         }
1936 #endif
1937         return TRUE;
1938     }
1939 
OnSetFont(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)1940     LRESULT OnSetFont(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1941     {
1942         return m_TaskBar.SendMessageW(uMsg, wParam, lParam);
1943     }
1944 
OnSettingChanged(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)1945     LRESULT OnSettingChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1946     {
1947         if (wParam == SPI_SETNONCLIENTMETRICS)
1948         {
1949             /*  Don't update the font, this will be done when we get a WM_SETFONT from our parent */
1950             UpdateButtonsSize(FALSE);
1951         }
1952 
1953         return 0;
1954     }
1955 
OnCopyData(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)1956     LRESULT OnCopyData(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1957     {
1958         PCOPYDATASTRUCT cpData = (PCOPYDATASTRUCT)lParam;
1959         if (cpData->dwData == m_uHardErrorMsg)
1960         {
1961             /* A hard error balloon message */
1962             PBALLOON_HARD_ERROR_DATA pData = (PBALLOON_HARD_ERROR_DATA)cpData->lpData;
1963             ERR("Got balloon data 0x%x, 0x%x, '%S', '%S'\n", pData->Status, pData->dwType, (WCHAR*)((ULONG_PTR)pData + pData->TitleOffset), (WCHAR*)((ULONG_PTR)pData + pData->MessageOffset));
1964             if (pData->cbHeaderSize == sizeof(BALLOON_HARD_ERROR_DATA))
1965                 m_HardErrorThread.StartThread(pData);
1966             return TRUE;
1967         }
1968 
1969         return FALSE;
1970     }
1971 
Initialize(IN HWND hWndParent,IN OUT ITrayWindow * tray)1972     HRESULT Initialize(IN HWND hWndParent, IN OUT ITrayWindow *tray)
1973     {
1974         m_Tray = tray;
1975         m_IsGroupingEnabled = g_TaskbarSettings.bGroupButtons;
1976         Create(hWndParent, 0, szRunningApps, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP);
1977         if (!m_hWnd)
1978             return E_FAIL;
1979         return S_OK;
1980     }
1981 
GetWindow(HWND * phwnd)1982     HRESULT WINAPI GetWindow(HWND* phwnd)
1983     {
1984         if (!phwnd)
1985             return E_INVALIDARG;
1986         *phwnd = m_hWnd;
1987         return S_OK;
1988     }
1989 
ContextSensitiveHelp(BOOL fEnterMode)1990     HRESULT WINAPI ContextSensitiveHelp(BOOL fEnterMode)
1991     {
1992         return E_NOTIMPL;
1993     }
1994 
1995     DECLARE_WND_CLASS_EX(szTaskSwitchWndClass, CS_DBLCLKS, COLOR_3DFACE)
1996 
1997     BEGIN_MSG_MAP(CTaskSwitchWnd)
1998         MESSAGE_HANDLER(WM_THEMECHANGED, OnThemeChanged)
1999         MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
2000         MESSAGE_HANDLER(WM_SIZE, OnSize)
2001         MESSAGE_HANDLER(WM_CREATE, OnCreate)
2002         MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
2003         MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest)
2004         MESSAGE_HANDLER(WM_COMMAND, OnCommand)
2005         MESSAGE_HANDLER(WM_NOTIFY, OnNotify)
2006         MESSAGE_HANDLER(TSWM_UPDATETASKBARPOS, OnUpdateTaskbarPos)
2007         MESSAGE_HANDLER(TWM_SETTINGSCHANGED, OnTaskbarSettingsChanged)
2008         MESSAGE_HANDLER(WM_CONTEXTMENU, OnContextMenu)
2009         MESSAGE_HANDLER(WM_TIMER, OnTimer)
2010         MESSAGE_HANDLER(WM_SETFONT, OnSetFont)
2011         MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChanged)
2012         MESSAGE_HANDLER(m_ShellHookMsg, OnShellHook)
2013         MESSAGE_HANDLER(WM_MOUSEACTIVATE, OnMouseActivate)
2014         MESSAGE_HANDLER(WM_KLUDGEMINRECT, OnKludgeItemRect)
2015         MESSAGE_HANDLER(WM_COPYDATA, OnCopyData)
2016     END_MSG_MAP()
2017 
2018     DECLARE_NOT_AGGREGATABLE(CTaskSwitchWnd)
2019 
2020     DECLARE_PROTECT_FINAL_CONSTRUCT()
2021     BEGIN_COM_MAP(CTaskSwitchWnd)
2022         COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow)
2023     END_COM_MAP()
2024 };
2025 
CTaskSwitchWnd_CreateInstance(IN HWND hWndParent,IN OUT ITrayWindow * Tray,REFIID riid,void ** ppv)2026 HRESULT CTaskSwitchWnd_CreateInstance(IN HWND hWndParent, IN OUT ITrayWindow *Tray, REFIID riid, void **ppv)
2027 {
2028     return ShellObjectCreatorInit<CTaskSwitchWnd>(hWndParent, Tray, riid, ppv);
2029 }
2030