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