4 * Copyright 2006 - 2007 Thomas Weidenmueller <w3seek@reactos.org>
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.
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.
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
22 #include <commoncontrols.h>
24 /* Set DUMP_TASKS to 1 to enable a dump of the tasks and task groups every
27 #define DEBUG_SHELL_HOOK 0
29 #define MAX_TASKS_COUNT (0x7FFF)
30 #define TASK_ITEM_ARRAY_ALLOC 64
32 const WCHAR szTaskSwitchWndClass
[] = L
"MSTaskSwWClass";
33 const WCHAR szRunningApps
[] = L
"Running Applications";
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" },
58 typedef struct _TASK_GROUP
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
63 struct _TASK_GROUP
*Next
;
74 DWORD IsCollapsed
: 1;
77 } TASK_GROUP
, *PTASK_GROUP
;
79 typedef struct _TASK_ITEM
92 /* IsFlashing is TRUE when the task bar item should be flashing. */
95 /* RenderFlashed is only TRUE if the task bar item should be
96 drawn with a flash. */
97 DWORD RenderFlashed
: 1;
100 } TASK_ITEM
, *PTASK_ITEM
;
103 class CHardErrorThread
107 LONG m_bThreadRunning
;
117 m_bThreadRunning(FALSE
),
125 if (m_bThreadRunning
)
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
);
139 CComPtr
<IUserNotification
> pnotification
;
141 hr
= OleInitialize(NULL
);
142 if (FAILED_UNEXPECTEDLY(hr
))
145 hr
= CoCreateInstance(CLSID_UserNotification
,
147 CLSCTX_INPROC_SERVER
,
148 IID_PPV_ARG(IUserNotification
, &pnotification
));
149 if (FAILED_UNEXPECTEDLY(hr
))
152 hr
= pnotification
->SetBalloonInfo(m_Title
, m_Text
, NIIF_WARNING
);
153 if (FAILED_UNEXPECTEDLY(hr
))
156 hr
= pnotification
->SetIconInfo(NULL
, NULL
);
157 if (FAILED_UNEXPECTEDLY(hr
))
160 /* Show will block until the balloon closes */
161 hr
= pnotification
->Show(NULL
, 0);
162 if (FAILED_UNEXPECTEDLY(hr
))
168 static DWORD CALLBACK
s_HardErrorThreadProc(IN OUT LPVOID lpParameter
)
170 CHardErrorThread
* pThis
= reinterpret_cast<CHardErrorThread
*>(lpParameter
);
172 CloseHandle(pThis
->m_hThread
);
174 InterlockedExchange(&pThis
->m_bThreadRunning
, FALSE
);
178 void StartThread(PBALLOON_HARD_ERROR_DATA pData
)
180 BOOL bIsRunning
= InterlockedExchange(&m_bThreadRunning
, TRUE
);
182 /* Ignore the new message if we are already showing one */
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
);
193 m_bThreadRunning
= FALSE
;
194 CloseHandle(m_hThread
);
200 public CWindowImplBaseT
< CToolbar
<TASK_ITEM
>, CControlWinTraits
>
203 INT
UpdateTbButtonSpacing(IN BOOL bHorizontal
, IN BOOL bThemed
, IN UINT uiRows
= 0, IN UINT uiBtnsPerLine
= 0)
207 tbm
.cbSize
= sizeof(tbm
);
208 tbm
.dwMask
= TBMF_BARPAD
| TBMF_BUTTONSPACING
;
210 tbm
.cxBarPad
= tbm
.cyBarPad
= 0;
214 tbm
.cxButtonSpacing
= 0;
215 tbm
.cyButtonSpacing
= 0;
219 if (bHorizontal
|| uiBtnsPerLine
> 1)
220 tbm
.cxButtonSpacing
= (3 * GetSystemMetrics(SM_CXEDGE
) / 2);
222 tbm
.cxButtonSpacing
= 0;
224 if (!bHorizontal
|| uiRows
> 1)
225 tbm
.cyButtonSpacing
= (3 * GetSystemMetrics(SM_CYEDGE
) / 2);
227 tbm
.cyButtonSpacing
= 0;
232 return tbm
.cxButtonSpacing
;
242 SendMessageW(WM_SETREDRAW
, TRUE
);
243 InvalidateRect(NULL
, TRUE
);
246 BOOL
SetButtonCommandId(IN INT iButtonIndex
, IN INT iCommandId
)
250 tbbi
.cbSize
= sizeof(tbbi
);
251 tbbi
.dwMask
= TBIF_BYINDEX
| TBIF_COMMAND
;
252 tbbi
.idCommand
= iCommandId
;
254 return SetButtonInfo(iButtonIndex
, &tbbi
) != 0;
257 LRESULT
OnNcHitTestToolbar(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
261 /* See if the mouse is on a button */
262 pt
.x
= GET_X_LPARAM(lParam
);
263 pt
.y
= GET_Y_LPARAM(lParam
);
266 INT index
= HitTest(&pt
);
269 /* Make the control appear to be transparent outside of any buttons */
270 return HTTRANSPARENT
;
278 BEGIN_MSG_MAP(CNotifyToolbar
)
279 MESSAGE_HANDLER(WM_NCHITTEST
, OnNcHitTestToolbar
)
282 BOOL
Initialize(HWND hWndParent
)
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
;
288 return SubclassWindow(CToolbar::Create(hWndParent
, styles
));
292 class CTaskSwitchWnd
:
293 public CComCoClass
<CTaskSwitchWnd
>,
294 public CComObjectRootEx
<CComMultiThreadModelNoCS
>,
295 public CWindowImpl
< CTaskSwitchWnd
, CWindow
, CControlWinTraits
>,
298 CTaskToolbar m_TaskBar
;
300 CComPtr
<ITrayWindow
> m_Tray
;
304 WORD m_TaskItemCount
;
305 WORD m_AllocatedTaskItems
;
307 PTASK_GROUP m_TaskGroups
;
308 PTASK_ITEM m_TaskItems
;
309 PTASK_ITEM m_ActiveTaskItem
;
312 UINT m_ButtonsPerLine
;
315 HIMAGELIST m_ImageList
;
317 BOOL m_IsGroupingEnabled
;
322 UINT m_uHardErrorMsg
;
323 CHardErrorThread m_HardErrorThread
;
327 m_ShellHookMsg(NULL
),
329 m_AllocatedTaskItems(0),
332 m_ActiveTaskItem(NULL
),
337 m_IsGroupingEnabled(FALSE
),
338 m_IsDestroying(FALSE
)
340 ZeroMemory(&m_ButtonSize
, sizeof(m_ButtonSize
));
341 m_uHardErrorMsg
= RegisterWindowMessageW(L
"HardError");
343 virtual ~CTaskSwitchWnd() { }
345 INT
GetWndTextFromTaskItem(IN PTASK_ITEM TaskItem
, LPWSTR szBuf
, DWORD cchBuf
)
347 /* Get the window text without sending a message so we don't hang if an
348 application isn't responding! */
349 return InternalGetWindowText(TaskItem
->hWnd
, szBuf
, cchBuf
);
356 PTASK_GROUP CurrentGroup
;
357 PTASK_ITEM CurrentTaskItem
, LastTaskItem
;
359 TRACE("Tasks dump:\n");
360 if (m_IsGroupingEnabled
)
362 CurrentGroup
= m_TaskGroups
;
363 while (CurrentGroup
!= NULL
)
365 TRACE("- Group PID: 0x%p Tasks: %d Index: %d\n", CurrentGroup
->dwProcessId
, CurrentGroup
->dwTaskCount
, CurrentGroup
->Index
);
367 CurrentTaskItem
= m_TaskItems
;
368 LastTaskItem
= CurrentTaskItem
+ m_TaskItemCount
;
369 while (CurrentTaskItem
!= LastTaskItem
)
371 if (CurrentTaskItem
->Group
== CurrentGroup
)
373 TRACE(" + Task hwnd: 0x%p Index: %d\n", CurrentTaskItem
->hWnd
, CurrentTaskItem
->Index
);
378 CurrentGroup
= CurrentGroup
->Next
;
381 CurrentTaskItem
= m_TaskItems
;
382 LastTaskItem
= CurrentTaskItem
+ m_TaskItemCount
;
383 while (CurrentTaskItem
!= LastTaskItem
)
385 if (CurrentTaskItem
->Group
== NULL
)
387 TRACE("- Task hwnd: 0x%p Index: %d\n", CurrentTaskItem
->hWnd
, CurrentTaskItem
->Index
);
394 CurrentTaskItem
= m_TaskItems
;
395 LastTaskItem
= CurrentTaskItem
+ m_TaskItemCount
;
396 while (CurrentTaskItem
!= LastTaskItem
)
398 TRACE("- Task hwnd: 0x%p Index: %d\n", CurrentTaskItem
->hWnd
, CurrentTaskItem
->Index
);
405 VOID
UpdateIndexesAfter(IN INT iIndex
, BOOL bInserted
)
407 PTASK_GROUP CurrentGroup
;
408 PTASK_ITEM CurrentTaskItem
, LastTaskItem
;
411 int offset
= bInserted
? +1 : -1;
413 if (m_IsGroupingEnabled
)
415 /* Update all affected groups */
416 CurrentGroup
= m_TaskGroups
;
417 while (CurrentGroup
!= NULL
)
419 if (CurrentGroup
->IsCollapsed
&&
420 CurrentGroup
->Index
>= iIndex
)
422 /* Update the toolbar buttons */
423 NewIndex
= CurrentGroup
->Index
+ offset
;
424 if (m_TaskBar
.SetButtonCommandId(CurrentGroup
->Index
+ offset
, NewIndex
))
426 CurrentGroup
->Index
= NewIndex
;
429 CurrentGroup
->Index
= -1;
432 CurrentGroup
= CurrentGroup
->Next
;
436 /* Update all affected task items */
437 CurrentTaskItem
= m_TaskItems
;
438 LastTaskItem
= CurrentTaskItem
+ m_TaskItemCount
;
439 while (CurrentTaskItem
!= LastTaskItem
)
441 CurrentGroup
= CurrentTaskItem
->Group
;
442 if (CurrentGroup
!= NULL
)
444 if (!CurrentGroup
->IsCollapsed
&&
445 CurrentTaskItem
->Index
>= iIndex
)
447 goto UpdateTaskItemBtn
;
450 else if (CurrentTaskItem
->Index
>= iIndex
)
453 /* Update the toolbar buttons */
454 NewIndex
= CurrentTaskItem
->Index
+ offset
;
455 if (m_TaskBar
.SetButtonCommandId(CurrentTaskItem
->Index
+ offset
, NewIndex
))
457 CurrentTaskItem
->Index
= NewIndex
;
460 CurrentTaskItem
->Index
= -1;
468 INT
UpdateTaskGroupButton(IN PTASK_GROUP TaskGroup
)
470 ASSERT(TaskGroup
->Index
>= 0);
472 /* FIXME: Implement */
474 return TaskGroup
->Index
;
477 VOID
ExpandTaskGroup(IN PTASK_GROUP TaskGroup
)
479 ASSERT(TaskGroup
->dwTaskCount
> 0);
480 ASSERT(TaskGroup
->IsCollapsed
);
481 ASSERT(TaskGroup
->Index
>= 0);
483 /* FIXME: Implement */
486 HICON
GetWndIcon(HWND hwnd
)
490 SendMessageTimeout(hwnd
, WM_GETICON
, ICON_SMALL2
, 0, SMTO_ABORTIFHUNG
, 1000, (PDWORD_PTR
) &hIcon
);
494 SendMessageTimeout(hwnd
, WM_GETICON
, ICON_SMALL
, 0, SMTO_ABORTIFHUNG
, 1000, (PDWORD_PTR
) &hIcon
);
498 SendMessageTimeout(hwnd
, WM_GETICON
, ICON_BIG
, 0, SMTO_ABORTIFHUNG
, 1000, (PDWORD_PTR
) &hIcon
);
502 hIcon
= (HICON
) GetClassLongPtr(hwnd
, GCL_HICONSM
);
506 hIcon
= (HICON
) GetClassLongPtr(hwnd
, GCL_HICON
);
511 INT
UpdateTaskItemButton(IN PTASK_ITEM TaskItem
)
513 TBBUTTONINFO tbbi
= { 0 };
515 WCHAR windowText
[255];
517 ASSERT(TaskItem
->Index
>= 0);
519 tbbi
.cbSize
= sizeof(tbbi
);
520 tbbi
.dwMask
= TBIF_BYINDEX
| TBIF_STATE
| TBIF_TEXT
| TBIF_IMAGE
;
521 tbbi
.fsState
= TBSTATE_ENABLED
;
522 if (m_ActiveTaskItem
== TaskItem
)
523 tbbi
.fsState
|= TBSTATE_CHECKED
;
525 if (TaskItem
->RenderFlashed
)
526 tbbi
.fsState
|= TBSTATE_MARKED
;
528 /* Check if we're updating a button that is the last one in the
529 line. If so, we need to set the TBSTATE_WRAP flag! */
530 if (!m_Tray
->IsHorizontal() || (m_ButtonsPerLine
!= 0 &&
531 (TaskItem
->Index
+ 1) % m_ButtonsPerLine
== 0))
533 tbbi
.fsState
|= TBSTATE_WRAP
;
536 if (GetWndTextFromTaskItem(TaskItem
, windowText
, _countof(windowText
)) > 0)
538 tbbi
.pszText
= windowText
;
541 icon
= GetWndIcon(TaskItem
->hWnd
);
543 icon
= static_cast<HICON
>(LoadImageW(NULL
, MAKEINTRESOURCEW(OIC_SAMPLE
), IMAGE_ICON
, 0, 0, LR_SHARED
| LR_DEFAULTSIZE
));
544 TaskItem
->IconIndex
= ImageList_ReplaceIcon(m_ImageList
, TaskItem
->IconIndex
, icon
);
545 tbbi
.iImage
= TaskItem
->IconIndex
;
547 if (!m_TaskBar
.SetButtonInfo(TaskItem
->Index
, &tbbi
))
549 TaskItem
->Index
= -1;
553 TRACE("Updated button %d for hwnd 0x%p\n", TaskItem
->Index
, TaskItem
->hWnd
);
554 return TaskItem
->Index
;
557 VOID
RemoveIcon(IN PTASK_ITEM TaskItem
)
560 PTASK_ITEM currentTaskItem
, LastItem
;
562 if (TaskItem
->IconIndex
== -1)
565 tbbi
.cbSize
= sizeof(tbbi
);
566 tbbi
.dwMask
= TBIF_IMAGE
;
568 currentTaskItem
= m_TaskItems
;
569 LastItem
= currentTaskItem
+ m_TaskItemCount
;
570 while (currentTaskItem
!= LastItem
)
572 if (currentTaskItem
->IconIndex
> TaskItem
->IconIndex
)
574 currentTaskItem
->IconIndex
--;
575 tbbi
.iImage
= currentTaskItem
->IconIndex
;
577 m_TaskBar
.SetButtonInfo(currentTaskItem
->Index
, &tbbi
);
582 ImageList_Remove(m_ImageList
, TaskItem
->IconIndex
);
585 PTASK_ITEM
FindLastTaskItemOfGroup(
586 IN PTASK_GROUP TaskGroup OPTIONAL
,
587 IN PTASK_ITEM NewTaskItem OPTIONAL
)
589 PTASK_ITEM TaskItem
, LastTaskItem
, FoundTaskItem
= NULL
;
592 ASSERT(m_IsGroupingEnabled
);
594 TaskItem
= m_TaskItems
;
595 LastTaskItem
= TaskItem
+ m_TaskItemCount
;
597 dwTaskCount
= (TaskGroup
!= NULL
? TaskGroup
->dwTaskCount
: MAX_TASKS_COUNT
);
599 ASSERT(dwTaskCount
> 0);
601 while (TaskItem
!= LastTaskItem
)
603 if (TaskItem
->Group
== TaskGroup
)
605 if ((NewTaskItem
!= NULL
&& TaskItem
!= NewTaskItem
) || NewTaskItem
== NULL
)
607 FoundTaskItem
= TaskItem
;
610 if (--dwTaskCount
== 0)
612 /* We found the last task item in the group! */
620 return FoundTaskItem
;
623 INT
CalculateTaskItemNewButtonIndex(IN PTASK_ITEM TaskItem
)
625 PTASK_GROUP TaskGroup
;
626 PTASK_ITEM LastTaskItem
;
628 /* NOTE: This routine assumes that the group is *not* collapsed! */
630 TaskGroup
= TaskItem
->Group
;
631 if (m_IsGroupingEnabled
)
633 if (TaskGroup
!= NULL
)
635 ASSERT(TaskGroup
->Index
< 0);
636 ASSERT(!TaskGroup
->IsCollapsed
);
638 if (TaskGroup
->dwTaskCount
> 1)
640 LastTaskItem
= FindLastTaskItemOfGroup(TaskGroup
, TaskItem
);
641 if (LastTaskItem
!= NULL
)
643 /* Since the group is expanded the task items must have an index */
644 ASSERT(LastTaskItem
->Index
>= 0);
646 return LastTaskItem
->Index
+ 1;
652 /* Find the last NULL group button. NULL groups are added at the end of the
653 task item list when grouping is enabled */
654 LastTaskItem
= FindLastTaskItemOfGroup(NULL
, TaskItem
);
655 if (LastTaskItem
!= NULL
)
657 ASSERT(LastTaskItem
->Index
>= 0);
659 return LastTaskItem
->Index
+ 1;
664 return m_ButtonCount
;
667 INT
AddTaskItemButton(IN OUT PTASK_ITEM TaskItem
)
669 WCHAR windowText
[255];
670 TBBUTTON tbBtn
= { 0 };
674 if (TaskItem
->Index
>= 0)
676 return UpdateTaskItemButton(TaskItem
);
679 if (TaskItem
->Group
!= NULL
&&
680 TaskItem
->Group
->IsCollapsed
)
682 /* The task group is collapsed, we only need to update the group button */
683 return UpdateTaskGroupButton(TaskItem
->Group
);
686 icon
= GetWndIcon(TaskItem
->hWnd
);
688 icon
= static_cast<HICON
>(LoadImageW(NULL
, MAKEINTRESOURCEW(OIC_SAMPLE
), IMAGE_ICON
, 0, 0, LR_SHARED
| LR_DEFAULTSIZE
));
689 TaskItem
->IconIndex
= ImageList_ReplaceIcon(m_ImageList
, -1, icon
);
691 tbBtn
.iBitmap
= TaskItem
->IconIndex
;
692 tbBtn
.fsState
= TBSTATE_ENABLED
| TBSTATE_ELLIPSES
;
693 tbBtn
.fsStyle
= BTNS_CHECK
| BTNS_NOPREFIX
| BTNS_SHOWTEXT
;
694 tbBtn
.dwData
= TaskItem
->Index
;
696 if (GetWndTextFromTaskItem(TaskItem
, windowText
, _countof(windowText
)) > 0)
698 tbBtn
.iString
= (DWORD_PTR
) windowText
;
701 /* Find out where to insert the new button */
702 iIndex
= CalculateTaskItemNewButtonIndex(TaskItem
);
704 tbBtn
.idCommand
= iIndex
;
706 m_TaskBar
.BeginUpdate();
708 if (m_TaskBar
.InsertButton(iIndex
, &tbBtn
))
710 UpdateIndexesAfter(iIndex
, TRUE
);
712 TRACE("Added button %d for hwnd 0x%p\n", iIndex
, TaskItem
->hWnd
);
714 TaskItem
->Index
= iIndex
;
717 /* Update button sizes and fix the button wrapping */
718 UpdateButtonsSize(TRUE
);
722 m_TaskBar
.EndUpdate();
727 BOOL
DeleteTaskItemButton(IN OUT PTASK_ITEM TaskItem
)
729 PTASK_GROUP TaskGroup
;
732 TaskGroup
= TaskItem
->Group
;
734 if (TaskItem
->Index
>= 0)
736 if ((TaskGroup
!= NULL
&& !TaskGroup
->IsCollapsed
) ||
739 m_TaskBar
.BeginUpdate();
741 RemoveIcon(TaskItem
);
742 iIndex
= TaskItem
->Index
;
743 if (m_TaskBar
.DeleteButton(iIndex
))
745 TaskItem
->Index
= -1;
748 UpdateIndexesAfter(iIndex
, FALSE
);
750 /* Update button sizes and fix the button wrapping */
751 UpdateButtonsSize(TRUE
);
755 m_TaskBar
.EndUpdate();
762 PTASK_GROUP
AddToTaskGroup(IN HWND hWnd
)
765 PTASK_GROUP TaskGroup
, *PrevLink
;
767 if (!GetWindowThreadProcessId(hWnd
,
770 TRACE("Cannot get process id of hwnd 0x%p\n", hWnd
);
774 /* Try to find an existing task group */
775 TaskGroup
= m_TaskGroups
;
776 PrevLink
= &m_TaskGroups
;
777 while (TaskGroup
!= NULL
)
779 if (TaskGroup
->dwProcessId
== dwProcessId
)
781 TaskGroup
->dwTaskCount
++;
785 PrevLink
= &TaskGroup
->Next
;
786 TaskGroup
= TaskGroup
->Next
;
789 /* Allocate a new task group */
790 TaskGroup
= (PTASK_GROUP
) HeapAlloc(hProcessHeap
,
793 if (TaskGroup
!= NULL
)
795 TaskGroup
->dwTaskCount
= 1;
796 TaskGroup
->dwProcessId
= dwProcessId
;
797 TaskGroup
->Index
= -1;
799 /* Add the task group to the list */
800 *PrevLink
= TaskGroup
;
806 VOID
RemoveTaskFromTaskGroup(IN OUT PTASK_ITEM TaskItem
)
808 PTASK_GROUP TaskGroup
, CurrentGroup
, *PrevLink
;
810 TaskGroup
= TaskItem
->Group
;
811 if (TaskGroup
!= NULL
)
813 DWORD dwNewTaskCount
= --TaskGroup
->dwTaskCount
;
814 if (dwNewTaskCount
== 0)
816 /* Find the previous pointer in the chain */
817 CurrentGroup
= m_TaskGroups
;
818 PrevLink
= &m_TaskGroups
;
819 while (CurrentGroup
!= TaskGroup
)
821 PrevLink
= &CurrentGroup
->Next
;
822 CurrentGroup
= CurrentGroup
->Next
;
825 /* Remove the group from the list */
826 ASSERT(TaskGroup
== CurrentGroup
);
827 *PrevLink
= TaskGroup
->Next
;
829 /* Free the task group */
830 HeapFree(hProcessHeap
,
834 else if (TaskGroup
->IsCollapsed
&&
835 TaskGroup
->Index
>= 0)
837 if (dwNewTaskCount
> 1)
839 /* FIXME: Check if we should expand the group */
840 /* Update the task group button */
841 UpdateTaskGroupButton(TaskGroup
);
845 /* Expand the group of one task button to a task button */
846 ExpandTaskGroup(TaskGroup
);
852 PTASK_ITEM
FindTaskItem(IN HWND hWnd
)
854 PTASK_ITEM TaskItem
, LastItem
;
856 TaskItem
= m_TaskItems
;
857 LastItem
= TaskItem
+ m_TaskItemCount
;
858 while (TaskItem
!= LastItem
)
860 if (TaskItem
->hWnd
== hWnd
)
869 PTASK_ITEM
FindOtherTaskItem(IN HWND hWnd
)
871 PTASK_ITEM LastItem
, TaskItem
;
872 PTASK_GROUP TaskGroup
;
875 if (!GetWindowThreadProcessId(hWnd
, &dwProcessId
))
880 /* Try to find another task that belongs to the same
881 process as the given window */
882 TaskItem
= m_TaskItems
;
883 LastItem
= TaskItem
+ m_TaskItemCount
;
884 while (TaskItem
!= LastItem
)
886 TaskGroup
= TaskItem
->Group
;
887 if (TaskGroup
!= NULL
)
889 if (TaskGroup
->dwProcessId
== dwProcessId
)
894 DWORD dwProcessIdTask
;
896 if (GetWindowThreadProcessId(TaskItem
->hWnd
,
898 dwProcessIdTask
== dwProcessId
)
910 PTASK_ITEM
AllocTaskItem()
912 if (m_TaskItemCount
>= MAX_TASKS_COUNT
)
914 /* We need the most significant bit in 16 bit command IDs to indicate whether it
915 is a task group or task item. WM_COMMAND limits command IDs to 16 bits! */
919 ASSERT(m_AllocatedTaskItems
>= m_TaskItemCount
);
921 if (m_TaskItemCount
== 0)
923 m_TaskItems
= (PTASK_ITEM
) HeapAlloc(hProcessHeap
,
925 TASK_ITEM_ARRAY_ALLOC
* sizeof(*m_TaskItems
));
926 if (m_TaskItems
!= NULL
)
928 m_AllocatedTaskItems
= TASK_ITEM_ARRAY_ALLOC
;
933 else if (m_TaskItemCount
>= m_AllocatedTaskItems
)
936 SIZE_T NewArrayLength
, ActiveTaskItemIndex
;
938 NewArrayLength
= m_AllocatedTaskItems
+ TASK_ITEM_ARRAY_ALLOC
;
940 NewArray
= (PTASK_ITEM
) HeapReAlloc(hProcessHeap
,
943 NewArrayLength
* sizeof(*m_TaskItems
));
944 if (NewArray
!= NULL
)
946 if (m_ActiveTaskItem
!= NULL
)
948 /* Fixup the ActiveTaskItem pointer */
949 ActiveTaskItemIndex
= m_ActiveTaskItem
- m_TaskItems
;
950 m_ActiveTaskItem
= NewArray
+ ActiveTaskItemIndex
;
952 m_AllocatedTaskItems
= (WORD
) NewArrayLength
;
953 m_TaskItems
= NewArray
;
959 return m_TaskItems
+ m_TaskItemCount
++;
962 VOID
FreeTaskItem(IN OUT PTASK_ITEM TaskItem
)
966 if (TaskItem
== m_ActiveTaskItem
)
967 m_ActiveTaskItem
= NULL
;
969 wIndex
= (WORD
) (TaskItem
- m_TaskItems
);
970 if (wIndex
+ 1 < m_TaskItemCount
)
974 (m_TaskItemCount
- wIndex
- 1) * sizeof(*TaskItem
));
980 VOID
DeleteTaskItem(IN OUT PTASK_ITEM TaskItem
)
984 /* Delete the task button from the toolbar */
985 DeleteTaskItemButton(TaskItem
);
988 /* Remove the task from it's group */
989 RemoveTaskFromTaskGroup(TaskItem
);
991 /* Free the task item */
992 FreeTaskItem(TaskItem
);
995 VOID
CheckActivateTaskItem(IN OUT PTASK_ITEM TaskItem
)
997 PTASK_ITEM CurrentTaskItem
;
998 PTASK_GROUP TaskGroup
= NULL
;
1000 CurrentTaskItem
= m_ActiveTaskItem
;
1002 if (TaskItem
!= NULL
)
1003 TaskGroup
= TaskItem
->Group
;
1005 if (m_IsGroupingEnabled
&&
1006 TaskGroup
!= NULL
&&
1007 TaskGroup
->IsCollapsed
)
1013 if (CurrentTaskItem
!= NULL
)
1015 PTASK_GROUP CurrentTaskGroup
;
1017 if (CurrentTaskItem
== TaskItem
)
1020 CurrentTaskGroup
= CurrentTaskItem
->Group
;
1022 if (m_IsGroupingEnabled
&&
1023 CurrentTaskGroup
!= NULL
&&
1024 CurrentTaskGroup
->IsCollapsed
)
1026 if (CurrentTaskGroup
== TaskGroup
)
1033 m_ActiveTaskItem
= NULL
;
1034 if (CurrentTaskItem
->Index
>= 0)
1036 UpdateTaskItemButton(CurrentTaskItem
);
1041 m_ActiveTaskItem
= TaskItem
;
1043 if (TaskItem
!= NULL
&& TaskItem
->Index
>= 0)
1045 UpdateTaskItemButton(TaskItem
);
1047 else if (TaskItem
== NULL
)
1049 TRACE("Active TaskItem now NULL\n");
1053 PTASK_ITEM
FindTaskItemByIndex(IN INT Index
)
1055 PTASK_ITEM TaskItem
, LastItem
;
1057 TaskItem
= m_TaskItems
;
1058 LastItem
= TaskItem
+ m_TaskItemCount
;
1059 while (TaskItem
!= LastItem
)
1061 if (TaskItem
->Index
== Index
)
1070 PTASK_GROUP
FindTaskGroupByIndex(IN INT Index
)
1072 PTASK_GROUP CurrentGroup
;
1074 CurrentGroup
= m_TaskGroups
;
1075 while (CurrentGroup
!= NULL
)
1077 if (CurrentGroup
->Index
== Index
)
1080 CurrentGroup
= CurrentGroup
->Next
;
1083 return CurrentGroup
;
1086 BOOL
AddTask(IN HWND hWnd
)
1088 PTASK_ITEM TaskItem
;
1090 if (!::IsWindow(hWnd
) || m_Tray
->IsSpecialHWND(hWnd
))
1093 TaskItem
= FindTaskItem(hWnd
);
1094 if (TaskItem
== NULL
)
1096 TRACE("Add window 0x%p\n", hWnd
);
1097 TaskItem
= AllocTaskItem();
1098 if (TaskItem
!= NULL
)
1100 ZeroMemory(TaskItem
, sizeof(*TaskItem
));
1101 TaskItem
->hWnd
= hWnd
;
1102 TaskItem
->Index
= -1;
1103 TaskItem
->Group
= AddToTaskGroup(hWnd
);
1105 if (!m_IsDestroying
)
1107 AddTaskItemButton(TaskItem
);
1112 return TaskItem
!= NULL
;
1115 BOOL
ActivateTaskItem(IN OUT PTASK_ITEM TaskItem OPTIONAL
)
1117 if (TaskItem
!= NULL
)
1119 TRACE("Activate window 0x%p on button %d\n", TaskItem
->hWnd
, TaskItem
->Index
);
1122 CheckActivateTaskItem(TaskItem
);
1127 BOOL
ActivateTask(IN HWND hWnd
)
1129 PTASK_ITEM TaskItem
;
1133 return ActivateTaskItem(NULL
);
1136 TaskItem
= FindTaskItem(hWnd
);
1137 if (TaskItem
== NULL
)
1139 TaskItem
= FindOtherTaskItem(hWnd
);
1142 if (TaskItem
== NULL
)
1144 WARN("Activate window 0x%p, could not find task\n", hWnd
);
1145 RefreshWindowList();
1148 return ActivateTaskItem(TaskItem
);
1151 BOOL
DeleteTask(IN HWND hWnd
)
1153 PTASK_ITEM TaskItem
;
1155 TaskItem
= FindTaskItem(hWnd
);
1156 if (TaskItem
!= NULL
)
1158 TRACE("Delete window 0x%p on button %d\n", hWnd
, TaskItem
->Index
);
1159 DeleteTaskItem(TaskItem
);
1163 //TRACE("Failed to delete window 0x%p\n", hWnd);
1168 VOID
DeleteAllTasks()
1170 PTASK_ITEM CurrentTask
;
1172 if (m_TaskItemCount
> 0)
1174 CurrentTask
= m_TaskItems
+ m_TaskItemCount
;
1177 DeleteTaskItem(--CurrentTask
);
1178 } while (CurrentTask
!= m_TaskItems
);
1182 VOID
FlashTaskItem(IN OUT PTASK_ITEM TaskItem
)
1184 TaskItem
->RenderFlashed
= 1;
1185 UpdateTaskItemButton(TaskItem
);
1188 BOOL
FlashTask(IN HWND hWnd
)
1190 PTASK_ITEM TaskItem
;
1192 TaskItem
= FindTaskItem(hWnd
);
1193 if (TaskItem
!= NULL
)
1195 TRACE("Flashing window 0x%p on button %d\n", hWnd
, TaskItem
->Index
);
1196 FlashTaskItem(TaskItem
);
1203 VOID
RedrawTaskItem(IN OUT PTASK_ITEM TaskItem
)
1205 PTASK_GROUP TaskGroup
;
1207 TaskGroup
= TaskItem
->Group
;
1208 if (m_IsGroupingEnabled
&& TaskGroup
!= NULL
)
1210 if (TaskGroup
->IsCollapsed
&& TaskGroup
->Index
>= 0)
1212 UpdateTaskGroupButton(TaskGroup
);
1214 else if (TaskItem
->Index
>= 0)
1216 goto UpdateTaskItem
;
1219 else if (TaskItem
->Index
>= 0)
1222 TaskItem
->RenderFlashed
= 0;
1223 UpdateTaskItemButton(TaskItem
);
1228 BOOL
RedrawTask(IN HWND hWnd
)
1230 PTASK_ITEM TaskItem
;
1232 TaskItem
= FindTaskItem(hWnd
);
1233 if (TaskItem
!= NULL
)
1235 RedrawTaskItem(TaskItem
);
1242 VOID
UpdateButtonsSize(IN BOOL bRedrawDisabled
)
1245 UINT uiRows
, uiMax
, uiMin
, uiBtnsPerLine
, ui
;
1249 /* Update the size of the image list if needed */
1251 ImageList_GetIconSize(m_ImageList
, &cx
, &cy
);
1252 if (cx
!= GetSystemMetrics(SM_CXSMICON
) || cy
!= GetSystemMetrics(SM_CYSMICON
))
1254 ImageList_SetIconSize(m_ImageList
, GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
));
1256 /* SetIconSize removes all icons so we have to reinsert them */
1257 PTASK_ITEM TaskItem
= m_TaskItems
;
1258 PTASK_ITEM LastTaskItem
= m_TaskItems
+ m_TaskItemCount
;
1259 while (TaskItem
!= LastTaskItem
)
1261 TaskItem
->IconIndex
= -1;
1262 UpdateTaskItemButton(TaskItem
);
1266 m_TaskBar
.SetImageList(m_ImageList
);
1269 if (GetClientRect(&rcClient
) && !IsRectEmpty(&rcClient
))
1271 if (m_ButtonCount
> 0)
1273 Horizontal
= m_Tray
->IsHorizontal();
1277 TBMETRICS tbm
= { 0 };
1278 tbm
.cbSize
= sizeof(tbm
);
1279 tbm
.dwMask
= TBMF_BUTTONSPACING
;
1280 m_TaskBar
.GetMetrics(&tbm
);
1282 if (m_ButtonSize
.cy
+ tbm
.cyButtonSpacing
!= 0)
1283 uiRows
= (rcClient
.bottom
+ tbm
.cyButtonSpacing
) / (m_ButtonSize
.cy
+ tbm
.cyButtonSpacing
);
1290 uiBtnsPerLine
= (m_ButtonCount
+ uiRows
- 1) / uiRows
;
1295 uiRows
= m_ButtonCount
;
1298 if (!bRedrawDisabled
)
1299 m_TaskBar
.BeginUpdate();
1301 /* We might need to update the button spacing */
1302 int cxButtonSpacing
= m_TaskBar
.UpdateTbButtonSpacing(
1303 Horizontal
, m_Theme
!= NULL
,
1304 uiRows
, uiBtnsPerLine
);
1306 /* Determine the minimum and maximum width of a button */
1307 uiMin
= GetSystemMetrics(SM_CXSIZE
) + (2 * GetSystemMetrics(SM_CXEDGE
));
1310 uiMax
= GetSystemMetrics(SM_CXMINIMIZED
);
1312 /* Calculate the ideal width and make sure it's within the allowed range */
1313 NewBtnSize
= (rcClient
.right
- (uiBtnsPerLine
* cxButtonSpacing
)) / uiBtnsPerLine
;
1315 if (NewBtnSize
< (LONG
) uiMin
)
1317 if (NewBtnSize
>(LONG
)uiMax
)
1320 /* Recalculate how many buttons actually fit into one line */
1321 uiBtnsPerLine
= rcClient
.right
/ (NewBtnSize
+ cxButtonSpacing
);
1322 if (uiBtnsPerLine
== 0)
1327 NewBtnSize
= uiMax
= rcClient
.right
;
1330 m_ButtonSize
.cx
= NewBtnSize
;
1332 m_ButtonsPerLine
= uiBtnsPerLine
;
1334 for (ui
= 0; ui
!= m_ButtonCount
; ui
++)
1336 TBBUTTONINFOW tbbi
= { 0 };
1337 tbbi
.cbSize
= sizeof(tbbi
);
1338 tbbi
.dwMask
= TBIF_BYINDEX
| TBIF_SIZE
| TBIF_STATE
;
1339 tbbi
.cx
= (INT
) NewBtnSize
;
1340 tbbi
.fsState
= TBSTATE_ENABLED
;
1342 /* Check if we're updating a button that is the last one in the
1343 line. If so, we need to set the TBSTATE_WRAP flag! */
1346 if ((ui
+ 1) % uiBtnsPerLine
== 0)
1347 tbbi
.fsState
|= TBSTATE_WRAP
;
1351 tbbi
.fsState
|= TBSTATE_WRAP
;
1354 if (m_ActiveTaskItem
!= NULL
&&
1355 m_ActiveTaskItem
->Index
== (INT
)ui
)
1357 tbbi
.fsState
|= TBSTATE_CHECKED
;
1360 m_TaskBar
.SetButtonInfo(ui
, &tbbi
);
1365 m_ButtonsPerLine
= 0;
1366 m_ButtonSize
.cx
= 0;
1370 // FIXME: This seems to be enabling redraws prematurely, but moving it to its right place doesn't work!
1371 m_TaskBar
.EndUpdate();
1374 BOOL CALLBACK
EnumWindowsProc(IN HWND hWnd
)
1376 /* Only show windows that still exist and are visible and none of explorer's
1377 special windows (such as the desktop or the tray window) */
1378 if (::IsWindow(hWnd
) && ::IsWindowVisible(hWnd
) &&
1379 !m_Tray
->IsSpecialHWND(hWnd
))
1381 DWORD exStyle
= ::GetWindowLong(hWnd
, GWL_EXSTYLE
);
1382 /* Don't list popup windows and also no tool windows */
1383 if ((::GetWindow(hWnd
, GW_OWNER
) == NULL
|| exStyle
& WS_EX_APPWINDOW
) &&
1384 !(exStyle
& WS_EX_TOOLWINDOW
))
1386 TRACE("Adding task for %p...\n", hWnd
);
1395 static BOOL CALLBACK
s_EnumWindowsProc(IN HWND hWnd
, IN LPARAM lParam
)
1397 CTaskSwitchWnd
* This
= (CTaskSwitchWnd
*) lParam
;
1399 return This
->EnumWindowsProc(hWnd
);
1402 BOOL
RefreshWindowList()
1404 TRACE("Refreshing window list...\n");
1405 /* Add all windows to the toolbar */
1406 return EnumWindows(s_EnumWindowsProc
, (LPARAM
)this);
1409 LRESULT
OnThemeChanged(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1411 TRACE("OmThemeChanged\n");
1414 CloseThemeData(m_Theme
);
1416 if (IsThemeActive())
1417 m_Theme
= OpenThemeData(m_hWnd
, L
"TaskBand");
1424 LRESULT
OnCreate(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1426 if (!m_TaskBar
.Initialize(m_hWnd
))
1429 SetWindowTheme(m_TaskBar
.m_hWnd
, L
"TaskBand", NULL
);
1431 m_ImageList
= ImageList_Create(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
), ILC_COLOR32
| ILC_MASK
, 0, 1000);
1432 m_TaskBar
.SetImageList(m_ImageList
);
1434 /* Set proper spacing between buttons */
1435 m_TaskBar
.UpdateTbButtonSpacing(m_Tray
->IsHorizontal(), m_Theme
!= NULL
);
1437 /* Register the shell hook */
1438 m_ShellHookMsg
= RegisterWindowMessageW(L
"SHELLHOOK");
1440 TRACE("ShellHookMsg got assigned number %d\n", m_ShellHookMsg
);
1442 RegisterShellHook(m_hWnd
, 3); /* 1 if no NT! We're targeting NT so we don't care! */
1444 RefreshWindowList();
1446 /* Recalculate the button size */
1447 UpdateButtonsSize(FALSE
);
1450 SetTimer(hwnd
, 1, 5000, NULL
);
1455 LRESULT
OnDestroy(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1457 m_IsDestroying
= TRUE
;
1459 /* Unregister the shell hook */
1460 RegisterShellHook(m_hWnd
, FALSE
);
1462 CloseThemeData(m_Theme
);
1467 BOOL
HandleAppCommand(IN WPARAM wParam
, IN LPARAM lParam
)
1471 switch (GET_APPCOMMAND_LPARAM(lParam
))
1473 case APPCOMMAND_BROWSER_SEARCH
:
1474 Ret
= SHFindFiles(NULL
,
1478 case APPCOMMAND_BROWSER_HOME
:
1479 case APPCOMMAND_LAUNCH_MAIL
:
1481 TRACE("Shell app command %d unhandled!\n", (INT
) GET_APPCOMMAND_LPARAM(lParam
));
1488 LRESULT
OnShellHook(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1492 /* In case the shell hook wasn't registered properly, ignore WM_NULLs*/
1499 TRACE("Received shell hook message: wParam=%08lx, lParam=%08lx\n", wParam
, lParam
);
1501 switch ((INT
) wParam
)
1503 case HSHELL_APPCOMMAND
:
1504 Ret
= HandleAppCommand(wParam
, lParam
);
1507 case HSHELL_WINDOWCREATED
:
1508 AddTask((HWND
) lParam
);
1511 case HSHELL_WINDOWDESTROYED
:
1512 /* The window still exists! Delay destroying it a bit */
1513 DeleteTask((HWND
) lParam
);
1516 case HSHELL_RUDEAPPACTIVATED
:
1517 case HSHELL_WINDOWACTIVATED
:
1518 ActivateTask((HWND
) lParam
);
1522 FlashTask((HWND
) lParam
);
1526 RedrawTask((HWND
) lParam
);
1529 case HSHELL_TASKMAN
:
1530 ::PostMessage(m_Tray
->GetHWND(), TWM_OPENSTARTMENU
, 0, 0);
1533 case HSHELL_ACTIVATESHELLWINDOW
:
1534 case HSHELL_LANGUAGE
:
1535 case HSHELL_SYSMENU
:
1536 case HSHELL_ENDTASK
:
1537 case HSHELL_ACCESSIBILITYSTATE
:
1538 case HSHELL_WINDOWREPLACED
:
1539 case HSHELL_WINDOWREPLACING
:
1541 case HSHELL_GETMINRECT
:
1544 #if DEBUG_SHELL_HOOK
1546 for (i
= 0, found
= 0; i
!= _countof(hshell_msg
); i
++)
1548 if (hshell_msg
[i
].msg
== (INT
) wParam
)
1550 TRACE("Shell message %ws unhandled (lParam = 0x%p)!\n", hshell_msg
[i
].msg_name
, lParam
);
1558 TRACE("Shell message %d unhandled (lParam = 0x%p)!\n", (INT
) wParam
, lParam
);
1566 VOID
HandleTaskItemClick(IN OUT PTASK_ITEM TaskItem
)
1571 if (::IsWindow(TaskItem
->hWnd
))
1573 bIsMinimized
= ::IsIconic(TaskItem
->hWnd
);
1574 bIsActive
= (TaskItem
== m_ActiveTaskItem
);
1576 TRACE("Active TaskItem %p, selected TaskItem %p\n", m_ActiveTaskItem
, TaskItem
);
1577 if (m_ActiveTaskItem
)
1578 TRACE("Active TaskItem hWnd=%p, TaskItem hWnd %p\n", m_ActiveTaskItem
->hWnd
, TaskItem
->hWnd
);
1580 TRACE("Valid button clicked. HWND=%p, IsMinimized=%s, IsActive=%s...\n",
1581 TaskItem
->hWnd
, bIsMinimized
? "Yes" : "No", bIsActive
? "Yes" : "No");
1583 if (!bIsMinimized
&& bIsActive
)
1585 ::ShowWindowAsync(TaskItem
->hWnd
, SW_MINIMIZE
);
1586 TRACE("Valid button clicked. App window Minimized.\n");
1590 ::SwitchToThisWindow(TaskItem
->hWnd
, TRUE
);
1591 TRACE("Valid button clicked. App window Restored.\n");
1596 VOID
HandleTaskGroupClick(IN OUT PTASK_GROUP TaskGroup
)
1598 /* TODO: Show task group menu */
1601 BOOL
HandleButtonClick(IN WORD wIndex
)
1603 PTASK_ITEM TaskItem
;
1604 PTASK_GROUP TaskGroup
;
1606 if (m_IsGroupingEnabled
)
1608 TaskGroup
= FindTaskGroupByIndex((INT
) wIndex
);
1609 if (TaskGroup
!= NULL
&& TaskGroup
->IsCollapsed
)
1611 HandleTaskGroupClick(TaskGroup
);
1616 TaskItem
= FindTaskItemByIndex((INT
) wIndex
);
1617 if (TaskItem
!= NULL
)
1619 HandleTaskItemClick(TaskItem
);
1627 VOID
HandleTaskItemRightClick(IN OUT PTASK_ITEM TaskItem
)
1632 SetForegroundWindow(TaskItem
->hWnd
);
1634 ActivateTask(TaskItem
->hWnd
);
1636 /* Wait up to 2 seconds for the window to process the foreground notification. */
1637 DWORD_PTR resultDummy
;
1638 if (!SendMessageTimeout(TaskItem
->hWnd
, WM_NULL
, 0, 0, 0, 2000, &resultDummy
))
1639 ERR("HandleTaskItemRightClick detected the window was unresponsive for 2 seconds, or was destroyed\n");
1640 if (GetForegroundWindow() != TaskItem
->hWnd
)
1641 ERR("HandleTaskItemRightClick detected the window did not become foreground\n");
1643 ::SendMessageW(TaskItem
->hWnd
, WM_POPUPSYSTEMMENU
, 0, MAKELPARAM(pt
.x
, pt
.y
));
1646 VOID
HandleTaskGroupRightClick(IN OUT PTASK_GROUP TaskGroup
)
1648 /* TODO: Show task group right click menu */
1651 BOOL
HandleButtonRightClick(IN WORD wIndex
)
1653 PTASK_ITEM TaskItem
;
1654 PTASK_GROUP TaskGroup
;
1655 if (m_IsGroupingEnabled
)
1657 TaskGroup
= FindTaskGroupByIndex((INT
) wIndex
);
1658 if (TaskGroup
!= NULL
&& TaskGroup
->IsCollapsed
)
1660 HandleTaskGroupRightClick(TaskGroup
);
1665 TaskItem
= FindTaskItemByIndex((INT
) wIndex
);
1667 if (TaskItem
!= NULL
)
1669 HandleTaskItemRightClick(TaskItem
);
1677 LRESULT
HandleItemPaint(IN OUT NMTBCUSTOMDRAW
*nmtbcd
)
1679 LRESULT Ret
= CDRF_DODEFAULT
;
1680 PTASK_GROUP TaskGroup
;
1681 PTASK_ITEM TaskItem
;
1683 TaskItem
= FindTaskItemByIndex((INT
) nmtbcd
->nmcd
.dwItemSpec
);
1684 TaskGroup
= FindTaskGroupByIndex((INT
) nmtbcd
->nmcd
.dwItemSpec
);
1685 if (TaskGroup
== NULL
&& TaskItem
!= NULL
)
1687 ASSERT(TaskItem
!= NULL
);
1689 if (TaskItem
!= NULL
&& ::IsWindow(TaskItem
->hWnd
))
1691 /* Make the entire button flashing if necessary */
1692 if (nmtbcd
->nmcd
.uItemState
& CDIS_MARKED
)
1694 Ret
= TBCDRF_NOBACKGROUND
;
1697 SelectObject(nmtbcd
->nmcd
.hdc
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1698 Rectangle(nmtbcd
->nmcd
.hdc
,
1699 nmtbcd
->nmcd
.rc
.left
,
1700 nmtbcd
->nmcd
.rc
.top
,
1701 nmtbcd
->nmcd
.rc
.right
,
1702 nmtbcd
->nmcd
.rc
.bottom
);
1706 DrawThemeBackground(m_Theme
, nmtbcd
->nmcd
.hdc
, TDP_FLASHBUTTON
, 0, &nmtbcd
->nmcd
.rc
, 0);
1708 nmtbcd
->clrText
= GetSysColor(COLOR_HIGHLIGHTTEXT
);
1713 else if (TaskGroup
!= NULL
)
1715 /* FIXME: Implement painting for task groups */
1720 LRESULT
HandleToolbarNotification(IN
const NMHDR
*nmh
)
1728 LPNMTBCUSTOMDRAW nmtbcd
= (LPNMTBCUSTOMDRAW
) nmh
;
1730 switch (nmtbcd
->nmcd
.dwDrawStage
)
1733 case CDDS_ITEMPREPAINT
:
1734 Ret
= HandleItemPaint(nmtbcd
);
1738 Ret
= CDRF_NOTIFYITEMDRAW
;
1742 Ret
= CDRF_DODEFAULT
;
1752 LRESULT
OnEraseBackground(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1754 HDC hdc
= (HDC
) wParam
;
1763 GetClientRect(&rect
);
1764 DrawThemeParentBackground(m_hWnd
, hdc
, &rect
);
1769 LRESULT
OnSize(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1773 szClient
.cx
= LOWORD(lParam
);
1774 szClient
.cy
= HIWORD(lParam
);
1775 if (m_TaskBar
.m_hWnd
!= NULL
)
1777 m_TaskBar
.SetWindowPos(NULL
, 0, 0, szClient
.cx
, szClient
.cy
, SWP_NOZORDER
);
1779 UpdateButtonsSize(FALSE
);
1784 LRESULT
OnNcHitTest(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1787 /* We want the tray window to be draggable everywhere, so make the control
1788 appear transparent */
1789 Ret
= DefWindowProc(uMsg
, wParam
, lParam
);
1790 if (Ret
!= HTVSCROLL
&& Ret
!= HTHSCROLL
)
1791 Ret
= HTTRANSPARENT
;
1795 LRESULT
OnCommand(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1798 if (lParam
!= 0 && (HWND
) lParam
== m_TaskBar
.m_hWnd
)
1800 HandleButtonClick(LOWORD(wParam
));
1805 LRESULT
OnNotify(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1808 const NMHDR
*nmh
= (const NMHDR
*) lParam
;
1810 if (nmh
->hwndFrom
== m_TaskBar
.m_hWnd
)
1812 Ret
= HandleToolbarNotification(nmh
);
1817 LRESULT
OnUpdateTaskbarPos(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1819 /* Update the button spacing */
1820 m_TaskBar
.UpdateTbButtonSpacing(m_Tray
->IsHorizontal(), m_Theme
!= NULL
);
1824 LRESULT
OnTaskbarSettingsChanged(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1826 TaskbarSettings
* newSettings
= (TaskbarSettings
*)lParam
;
1827 if (newSettings
->bGroupButtons
!= g_TaskbarSettings
.bGroupButtons
)
1829 g_TaskbarSettings
.bGroupButtons
= newSettings
->bGroupButtons
;
1830 m_IsGroupingEnabled
= g_TaskbarSettings
.bGroupButtons
;
1832 /* Collapse or expand groups if necessary */
1833 RefreshWindowList();
1834 UpdateButtonsSize(FALSE
);
1840 LRESULT
OnContextMenu(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1845 if (m_TaskBar
.m_hWnd
!= NULL
)
1849 pt
.x
= GET_X_LPARAM(lParam
);
1850 pt
.y
= GET_Y_LPARAM(lParam
);
1852 ::ScreenToClient(m_TaskBar
.m_hWnd
, &pt
);
1854 iBtn
= m_TaskBar
.HitTest(&pt
);
1857 HandleButtonRightClick(iBtn
);
1862 /* Not on a taskbar button, so forward message to tray */
1863 Ret
= SendMessage(m_Tray
->GetHWND(), uMsg
, wParam
, lParam
);
1868 LRESULT
OnKludgeItemRect(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1870 PTASK_ITEM TaskItem
= FindTaskItem((HWND
) wParam
);
1873 RECT
* prcMinRect
= (RECT
*) lParam
;
1874 RECT rcItem
, rcToolbar
;
1875 m_TaskBar
.GetItemRect(TaskItem
->Index
, &rcItem
);
1876 m_TaskBar
.GetWindowRect(&rcToolbar
);
1878 OffsetRect(&rcItem
, rcToolbar
.left
, rcToolbar
.top
);
1880 *prcMinRect
= rcItem
;
1886 LRESULT
OnMouseActivate(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1888 return MA_NOACTIVATE
;
1891 LRESULT
OnTimer(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1904 LRESULT
OnSetFont(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1906 return m_TaskBar
.SendMessageW(uMsg
, wParam
, lParam
);
1909 LRESULT
OnSettingChanged(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1911 if (wParam
== SPI_SETNONCLIENTMETRICS
)
1913 /* Don't update the font, this will be done when we get a WM_SETFONT from our parent */
1914 UpdateButtonsSize(FALSE
);
1920 LRESULT
OnCopyData(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1922 PCOPYDATASTRUCT cpData
= (PCOPYDATASTRUCT
)lParam
;
1923 if (cpData
->dwData
== m_uHardErrorMsg
)
1925 /* A hard error balloon message */
1926 PBALLOON_HARD_ERROR_DATA pData
= (PBALLOON_HARD_ERROR_DATA
)cpData
->lpData
;
1927 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
));
1928 if (pData
->cbHeaderSize
== sizeof(BALLOON_HARD_ERROR_DATA
))
1929 m_HardErrorThread
.StartThread(pData
);
1936 HRESULT
Initialize(IN HWND hWndParent
, IN OUT ITrayWindow
*tray
)
1939 m_IsGroupingEnabled
= g_TaskbarSettings
.bGroupButtons
;
1940 Create(hWndParent
, 0, szRunningApps
, WS_CHILD
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
| WS_TABSTOP
);
1946 HRESULT WINAPI
GetWindow(HWND
* phwnd
)
1949 return E_INVALIDARG
;
1954 HRESULT WINAPI
ContextSensitiveHelp(BOOL fEnterMode
)
1959 DECLARE_WND_CLASS_EX(szTaskSwitchWndClass
, CS_DBLCLKS
, COLOR_3DFACE
)
1961 BEGIN_MSG_MAP(CTaskSwitchWnd
)
1962 MESSAGE_HANDLER(WM_THEMECHANGED
, OnThemeChanged
)
1963 MESSAGE_HANDLER(WM_ERASEBKGND
, OnEraseBackground
)
1964 MESSAGE_HANDLER(WM_SIZE
, OnSize
)
1965 MESSAGE_HANDLER(WM_CREATE
, OnCreate
)
1966 MESSAGE_HANDLER(WM_DESTROY
, OnDestroy
)
1967 MESSAGE_HANDLER(WM_NCHITTEST
, OnNcHitTest
)
1968 MESSAGE_HANDLER(WM_COMMAND
, OnCommand
)
1969 MESSAGE_HANDLER(WM_NOTIFY
, OnNotify
)
1970 MESSAGE_HANDLER(TSWM_UPDATETASKBARPOS
, OnUpdateTaskbarPos
)
1971 MESSAGE_HANDLER(TWM_SETTINGSCHANGED
, OnTaskbarSettingsChanged
)
1972 MESSAGE_HANDLER(WM_CONTEXTMENU
, OnContextMenu
)
1973 MESSAGE_HANDLER(WM_TIMER
, OnTimer
)
1974 MESSAGE_HANDLER(WM_SETFONT
, OnSetFont
)
1975 MESSAGE_HANDLER(WM_SETTINGCHANGE
, OnSettingChanged
)
1976 MESSAGE_HANDLER(m_ShellHookMsg
, OnShellHook
)
1977 MESSAGE_HANDLER(WM_MOUSEACTIVATE
, OnMouseActivate
)
1978 MESSAGE_HANDLER(WM_KLUDGEMINRECT
, OnKludgeItemRect
)
1979 MESSAGE_HANDLER(WM_COPYDATA
, OnCopyData
)
1982 DECLARE_NOT_AGGREGATABLE(CTaskSwitchWnd
)
1984 DECLARE_PROTECT_FINAL_CONSTRUCT()
1985 BEGIN_COM_MAP(CTaskSwitchWnd
)
1986 COM_INTERFACE_ENTRY_IID(IID_IOleWindow
, IOleWindow
)
1990 HRESULT
CTaskSwitchWnd_CreateInstance(IN HWND hWndParent
, IN OUT ITrayWindow
*Tray
, REFIID riid
, void **ppv
)
1992 return ShellObjectCreatorInit
<CTaskSwitchWnd
>(hWndParent
, Tray
, riid
, ppv
);