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 #define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
25 #define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp))
27 /* Set DUMP_TASKS to 1 to enable a dump of the tasks and task groups every
30 #define DEBUG_SHELL_HOOK 0
32 const WCHAR szTaskSwitchWndClass
[] = TEXT("MSTaskSwWClass");
33 const WCHAR szRunningApps
[] = TEXT("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
94 /* IsFlashing is TRUE when the task bar item should be flashing. */
97 /* RenderFlashed is only TRUE if the task bar item should be
98 drawn with a flash. */
99 DWORD RenderFlashed
: 1;
102 } TASK_ITEM
, *PTASK_ITEM
;
104 #define TASK_ITEM_ARRAY_ALLOC 64
107 public CToolbar
<TASK_ITEM
>
110 INT
UpdateTbButtonSpacing(IN BOOL bHorizontal
, IN BOOL bThemed
, IN UINT uiRows
= 0, IN UINT uiBtnsPerLine
= 0)
114 tbm
.cbSize
= sizeof(tbm
);
115 tbm
.dwMask
= TBMF_BARPAD
| TBMF_BUTTONSPACING
;
117 tbm
.cxBarPad
= tbm
.cyBarPad
= 0;
121 tbm
.cxButtonSpacing
= 0;
122 tbm
.cyButtonSpacing
= 0;
126 if (bHorizontal
|| uiBtnsPerLine
> 1)
127 tbm
.cxButtonSpacing
= (3 * GetSystemMetrics(SM_CXEDGE
) / 2);
129 tbm
.cxButtonSpacing
= 0;
131 if (!bHorizontal
|| uiRows
> 1)
132 tbm
.cyButtonSpacing
= (3 * GetSystemMetrics(SM_CYEDGE
) / 2);
134 tbm
.cyButtonSpacing
= 0;
139 return tbm
.cxButtonSpacing
;
149 SendMessageW(WM_SETREDRAW
, TRUE
);
150 InvalidateRect(NULL
, TRUE
);
153 BOOL
SetButtonCommandId(IN INT iButtonIndex
, IN INT iCommandId
)
157 tbbi
.cbSize
= sizeof(tbbi
);
158 tbbi
.dwMask
= TBIF_BYINDEX
| TBIF_COMMAND
;
159 tbbi
.idCommand
= iCommandId
;
161 return SetButtonInfo(iButtonIndex
, &tbbi
) != 0;
165 BEGIN_MSG_MAP(CNotifyToolbar
)
168 BOOL
Initialize(HWND hWndParent
)
170 DWORD styles
= WS_CHILD
| WS_VISIBLE
| WS_CLIPCHILDREN
|
171 TBSTYLE_TOOLTIPS
| TBSTYLE_WRAPABLE
| TBSTYLE_LIST
| TBSTYLE_TRANSPARENT
|
172 CCS_TOP
| CCS_NORESIZE
| CCS_NODIVIDER
;
174 return SubclassWindow(Create(hWndParent
, styles
));
178 class CTaskSwitchWnd
:
179 public CWindowImpl
< CTaskSwitchWnd
, CWindow
, CControlWinTraits
>
181 CTaskToolbar TaskBar
;
186 CComPtr
<ITrayWindow
> Tray
;
188 PTASK_GROUP TaskGroups
;
191 WORD AllocatedTaskItems
;
192 PTASK_ITEM TaskItems
;
193 PTASK_ITEM ActiveTaskItem
;
195 HTHEME TaskBandTheme
;
196 UINT TbButtonsPerLine
;
197 WORD ToolbarBtnCount
;
199 HIMAGELIST TaskIcons
;
201 BOOL IsGroupingEnabled
;
213 AllocatedTaskItems(0),
220 IsGroupingEnabled(FALSE
),
223 ZeroMemory(&ButtonSize
, sizeof(ButtonSize
));
226 virtual ~CTaskSwitchWnd() { }
228 #define MAX_TASKS_COUNT (0x7FFF)
230 VOID
TaskSwitchWnd_UpdateButtonsSize(IN BOOL bRedrawDisabled
);
232 LPTSTR
GetWndTextFromTaskItem(IN PTASK_ITEM TaskItem
)
234 /* Get the window text without sending a message so we don't hang if an
235 application isn't responding! */
236 if (InternalGetWindowText(TaskItem
->hWnd
,
238 sizeof(szBuf
) / sizeof(szBuf
[0])) > 0)
250 PTASK_GROUP CurrentGroup
;
251 PTASK_ITEM CurrentTaskItem
, LastTaskItem
;
253 TRACE("Tasks dump:\n");
254 if (IsGroupingEnabled
)
256 CurrentGroup
= TaskGroups
;
257 while (CurrentGroup
!= NULL
)
259 TRACE("- Group PID: 0x%p Tasks: %d Index: %d\n", CurrentGroup
->dwProcessId
, CurrentGroup
->dwTaskCount
, CurrentGroup
->Index
);
261 CurrentTaskItem
= TaskItems
;
262 LastTaskItem
= CurrentTaskItem
+ TaskItemCount
;
263 while (CurrentTaskItem
!= LastTaskItem
)
265 if (CurrentTaskItem
->Group
== CurrentGroup
)
267 TRACE(" + Task hwnd: 0x%p Index: %d\n", CurrentTaskItem
->hWnd
, CurrentTaskItem
->Index
);
272 CurrentGroup
= CurrentGroup
->Next
;
275 CurrentTaskItem
= TaskItems
;
276 LastTaskItem
= CurrentTaskItem
+ TaskItemCount
;
277 while (CurrentTaskItem
!= LastTaskItem
)
279 if (CurrentTaskItem
->Group
== NULL
)
281 TRACE("- Task hwnd: 0x%p Index: %d\n", CurrentTaskItem
->hWnd
, CurrentTaskItem
->Index
);
288 CurrentTaskItem
= TaskItems
;
289 LastTaskItem
= CurrentTaskItem
+ TaskItemCount
;
290 while (CurrentTaskItem
!= LastTaskItem
)
292 TRACE("- Task hwnd: 0x%p Index: %d\n", CurrentTaskItem
->hWnd
, CurrentTaskItem
->Index
);
299 VOID
UpdateIndexesAfter(IN INT iIndex
, BOOL bInserted
)
301 PTASK_GROUP CurrentGroup
;
302 PTASK_ITEM CurrentTaskItem
, LastTaskItem
;
305 int offset
= bInserted
? +1 : -1;
307 if (IsGroupingEnabled
)
309 /* Update all affected groups */
310 CurrentGroup
= TaskGroups
;
311 while (CurrentGroup
!= NULL
)
313 if (CurrentGroup
->IsCollapsed
&&
314 CurrentGroup
->Index
>= iIndex
)
316 /* Update the toolbar buttons */
317 NewIndex
= CurrentGroup
->Index
+ offset
;
318 if (TaskBar
.SetButtonCommandId(CurrentGroup
->Index
+ offset
, NewIndex
))
320 CurrentGroup
->Index
= NewIndex
;
323 CurrentGroup
->Index
= -1;
326 CurrentGroup
= CurrentGroup
->Next
;
330 /* Update all affected task items */
331 CurrentTaskItem
= TaskItems
;
332 LastTaskItem
= CurrentTaskItem
+ TaskItemCount
;
333 while (CurrentTaskItem
!= LastTaskItem
)
335 CurrentGroup
= CurrentTaskItem
->Group
;
336 if (CurrentGroup
!= NULL
)
338 if (!CurrentGroup
->IsCollapsed
&&
339 CurrentTaskItem
->Index
>= iIndex
)
341 goto UpdateTaskItemBtn
;
344 else if (CurrentTaskItem
->Index
>= iIndex
)
347 /* Update the toolbar buttons */
348 NewIndex
= CurrentTaskItem
->Index
+ offset
;
349 if (TaskBar
.SetButtonCommandId(CurrentTaskItem
->Index
+ offset
, NewIndex
))
351 CurrentTaskItem
->Index
= NewIndex
;
354 CurrentTaskItem
->Index
= -1;
362 INT
UpdateTaskGroupButton(IN PTASK_GROUP TaskGroup
)
364 ASSERT(TaskGroup
->Index
>= 0);
366 /* FIXME: Implement */
368 return TaskGroup
->Index
;
371 VOID
ExpandTaskGroup(IN PTASK_GROUP TaskGroup
)
373 ASSERT(TaskGroup
->dwTaskCount
> 0);
374 ASSERT(TaskGroup
->IsCollapsed
);
375 ASSERT(TaskGroup
->Index
>= 0);
377 /* FIXME: Implement */
380 HICON
GetWndIcon(HWND hwnd
)
384 SendMessageTimeout(hwnd
, WM_GETICON
, ICON_SMALL2
, 0, SMTO_ABORTIFHUNG
, 1000, (PDWORD_PTR
) &hIcon
);
388 SendMessageTimeout(hwnd
, WM_GETICON
, ICON_SMALL
, 0, SMTO_ABORTIFHUNG
, 1000, (PDWORD_PTR
) &hIcon
);
392 SendMessageTimeout(hwnd
, WM_GETICON
, ICON_BIG
, 0, SMTO_ABORTIFHUNG
, 1000, (PDWORD_PTR
) &hIcon
);
396 hIcon
= (HICON
) GetClassLongPtr(hwnd
, GCL_HICONSM
);
400 hIcon
= (HICON
) GetClassLongPtr(hwnd
, GCL_HICON
);
405 INT
UpdateTaskItemButton(IN PTASK_ITEM TaskItem
)
410 ASSERT(TaskItem
->Index
>= 0);
412 tbbi
.cbSize
= sizeof(tbbi
);
413 tbbi
.dwMask
= TBIF_BYINDEX
| TBIF_STATE
| TBIF_TEXT
| TBIF_IMAGE
;
414 tbbi
.fsState
= TBSTATE_ENABLED
;
415 if (ActiveTaskItem
== TaskItem
)
416 tbbi
.fsState
|= TBSTATE_CHECKED
;
418 if (TaskItem
->RenderFlashed
)
419 tbbi
.fsState
|= TBSTATE_MARKED
;
421 /* Check if we're updating a button that is the last one in the
422 line. If so, we need to set the TBSTATE_WRAP flag! */
423 if (!Tray
->IsHorizontal() || (TbButtonsPerLine
!= 0 &&
424 (TaskItem
->Index
+ 1) % TbButtonsPerLine
== 0))
426 tbbi
.fsState
|= TBSTATE_WRAP
;
429 tbbi
.pszText
= GetWndTextFromTaskItem(TaskItem
);
431 icon
= GetWndIcon(TaskItem
->hWnd
);
432 TaskItem
->IconIndex
= ImageList_ReplaceIcon(TaskIcons
, TaskItem
->IconIndex
, icon
);
433 tbbi
.iImage
= TaskItem
->IconIndex
;
435 if (!TaskBar
.SetButtonInfo(TaskItem
->Index
, &tbbi
))
437 TaskItem
->Index
= -1;
441 TRACE("Updated button %d for hwnd 0x%p\n", TaskItem
->Index
, TaskItem
->hWnd
);
442 return TaskItem
->Index
;
445 VOID
RemoveIcon(IN PTASK_ITEM TaskItem
)
448 PTASK_ITEM currentTaskItem
, LastItem
;
450 if (TaskItem
->IconIndex
== -1)
453 tbbi
.cbSize
= sizeof(tbbi
);
454 tbbi
.dwMask
= TBIF_IMAGE
;
456 currentTaskItem
= TaskItems
;
457 LastItem
= currentTaskItem
+ TaskItemCount
;
458 while (currentTaskItem
!= LastItem
)
460 if (currentTaskItem
->IconIndex
> TaskItem
->IconIndex
)
462 currentTaskItem
->IconIndex
--;
463 tbbi
.iImage
= currentTaskItem
->IconIndex
;
465 TaskBar
.SetButtonInfo(currentTaskItem
->Index
, &tbbi
);
470 ImageList_Remove(TaskIcons
, TaskItem
->IconIndex
);
473 PTASK_ITEM
FindLastTaskItemOfGroup(
474 IN PTASK_GROUP TaskGroup OPTIONAL
,
475 IN PTASK_ITEM NewTaskItem OPTIONAL
)
477 PTASK_ITEM TaskItem
, LastTaskItem
, FoundTaskItem
= NULL
;
480 ASSERT(IsGroupingEnabled
);
482 TaskItem
= TaskItems
;
483 LastTaskItem
= TaskItem
+ TaskItemCount
;
485 dwTaskCount
= (TaskGroup
!= NULL
? TaskGroup
->dwTaskCount
: MAX_TASKS_COUNT
);
487 ASSERT(dwTaskCount
> 0);
489 while (TaskItem
!= LastTaskItem
)
491 if (TaskItem
->Group
== TaskGroup
)
493 if ((NewTaskItem
!= NULL
&& TaskItem
!= NewTaskItem
) || NewTaskItem
== NULL
)
495 FoundTaskItem
= TaskItem
;
498 if (--dwTaskCount
== 0)
500 /* We found the last task item in the group! */
508 return FoundTaskItem
;
511 INT
CalculateTaskItemNewButtonIndex(IN PTASK_ITEM TaskItem
)
513 PTASK_GROUP TaskGroup
;
514 PTASK_ITEM LastTaskItem
;
516 /* NOTE: This routine assumes that the group is *not* collapsed! */
518 TaskGroup
= TaskItem
->Group
;
519 if (IsGroupingEnabled
)
521 if (TaskGroup
!= NULL
)
523 ASSERT(TaskGroup
->Index
< 0);
524 ASSERT(!TaskGroup
->IsCollapsed
);
526 if (TaskGroup
->dwTaskCount
> 1)
528 LastTaskItem
= FindLastTaskItemOfGroup(TaskGroup
, TaskItem
);
529 if (LastTaskItem
!= NULL
)
531 /* Since the group is expanded the task items must have an index */
532 ASSERT(LastTaskItem
->Index
>= 0);
534 return LastTaskItem
->Index
+ 1;
540 /* Find the last NULL group button. NULL groups are added at the end of the
541 task item list when grouping is enabled */
542 LastTaskItem
= FindLastTaskItemOfGroup(NULL
, TaskItem
);
543 if (LastTaskItem
!= NULL
)
545 ASSERT(LastTaskItem
->Index
>= 0);
547 return LastTaskItem
->Index
+ 1;
552 return ToolbarBtnCount
;
555 INT
AddTaskItemButton(IN OUT PTASK_ITEM TaskItem
)
561 if (TaskItem
->Index
>= 0)
563 return UpdateTaskItemButton(TaskItem
);
566 if (TaskItem
->Group
!= NULL
&&
567 TaskItem
->Group
->IsCollapsed
)
569 /* The task group is collapsed, we only need to update the group button */
570 return UpdateTaskGroupButton(TaskItem
->Group
);
573 icon
= GetWndIcon(TaskItem
->hWnd
);
574 TaskItem
->IconIndex
= ImageList_ReplaceIcon(TaskIcons
, -1, icon
);
576 tbBtn
.iBitmap
= TaskItem
->IconIndex
;
577 tbBtn
.fsState
= TBSTATE_ENABLED
| TBSTATE_ELLIPSES
;
578 tbBtn
.fsStyle
= BTNS_CHECK
| BTNS_NOPREFIX
| BTNS_SHOWTEXT
;
579 tbBtn
.dwData
= TaskItem
->Index
;
581 tbBtn
.iString
= (DWORD_PTR
) GetWndTextFromTaskItem(TaskItem
);
583 /* Find out where to insert the new button */
584 iIndex
= CalculateTaskItemNewButtonIndex(TaskItem
);
586 tbBtn
.idCommand
= iIndex
;
588 TaskBar
.BeginUpdate();
590 if (TaskBar
.InsertButton(iIndex
, &tbBtn
))
592 UpdateIndexesAfter(iIndex
, TRUE
);
594 TRACE("Added button %d for hwnd 0x%p\n", iIndex
, TaskItem
->hWnd
);
596 TaskItem
->Index
= iIndex
;
599 /* Update button sizes and fix the button wrapping */
600 UpdateButtonsSize(TRUE
);
609 BOOL
DeleteTaskItemButton(IN OUT PTASK_ITEM TaskItem
)
611 PTASK_GROUP TaskGroup
;
614 TaskGroup
= TaskItem
->Group
;
616 if (TaskItem
->Index
>= 0)
618 if ((TaskGroup
!= NULL
&& !TaskGroup
->IsCollapsed
) ||
621 TaskBar
.BeginUpdate();
623 RemoveIcon(TaskItem
);
624 iIndex
= TaskItem
->Index
;
625 if (TaskBar
.DeleteButton(iIndex
))
627 TaskItem
->Index
= -1;
630 UpdateIndexesAfter(iIndex
, FALSE
);
632 /* Update button sizes and fix the button wrapping */
633 UpdateButtonsSize(TRUE
);
644 PTASK_GROUP
AddToTaskGroup(IN HWND hWnd
)
647 PTASK_GROUP TaskGroup
, *PrevLink
;
649 if (!GetWindowThreadProcessId(hWnd
,
652 TRACE("Cannot get process id of hwnd 0x%p\n", hWnd
);
656 /* Try to find an existing task group */
657 TaskGroup
= TaskGroups
;
658 PrevLink
= &TaskGroups
;
659 while (TaskGroup
!= NULL
)
661 if (TaskGroup
->dwProcessId
== dwProcessId
)
663 TaskGroup
->dwTaskCount
++;
667 PrevLink
= &TaskGroup
->Next
;
668 TaskGroup
= TaskGroup
->Next
;
671 /* Allocate a new task group */
672 TaskGroup
= (PTASK_GROUP
) HeapAlloc(hProcessHeap
,
675 if (TaskGroup
!= NULL
)
677 TaskGroup
->dwTaskCount
= 1;
678 TaskGroup
->dwProcessId
= dwProcessId
;
679 TaskGroup
->Index
= -1;
681 /* Add the task group to the list */
682 *PrevLink
= TaskGroup
;
688 VOID
RemoveTaskFromTaskGroup(IN OUT PTASK_ITEM TaskItem
)
690 PTASK_GROUP TaskGroup
, CurrentGroup
, *PrevLink
;
692 TaskGroup
= TaskItem
->Group
;
693 if (TaskGroup
!= NULL
)
695 DWORD dwNewTaskCount
= --TaskGroup
->dwTaskCount
;
696 if (dwNewTaskCount
== 0)
698 /* Find the previous pointer in the chain */
699 CurrentGroup
= TaskGroups
;
700 PrevLink
= &TaskGroups
;
701 while (CurrentGroup
!= TaskGroup
)
703 PrevLink
= &CurrentGroup
->Next
;
704 CurrentGroup
= CurrentGroup
->Next
;
707 /* Remove the group from the list */
708 ASSERT(TaskGroup
== CurrentGroup
);
709 *PrevLink
= TaskGroup
->Next
;
711 /* Free the task group */
712 HeapFree(hProcessHeap
,
716 else if (TaskGroup
->IsCollapsed
&&
717 TaskGroup
->Index
>= 0)
719 if (dwNewTaskCount
> 1)
721 /* FIXME: Check if we should expand the group */
722 /* Update the task group button */
723 UpdateTaskGroupButton(TaskGroup
);
727 /* Expand the group of one task button to a task button */
728 ExpandTaskGroup(TaskGroup
);
734 PTASK_ITEM
FindTaskItem(IN HWND hWnd
)
736 PTASK_ITEM TaskItem
, LastItem
;
738 TaskItem
= TaskItems
;
739 LastItem
= TaskItem
+ TaskItemCount
;
740 while (TaskItem
!= LastItem
)
742 if (TaskItem
->hWnd
== hWnd
)
751 PTASK_ITEM
FindOtherTaskItem(IN HWND hWnd
)
753 PTASK_ITEM LastItem
, TaskItem
;
754 PTASK_GROUP TaskGroup
;
757 if (!GetWindowThreadProcessId(hWnd
, &dwProcessId
))
762 /* Try to find another task that belongs to the same
763 process as the given window */
764 TaskItem
= TaskItems
;
765 LastItem
= TaskItem
+ TaskItemCount
;
766 while (TaskItem
!= LastItem
)
768 TaskGroup
= TaskItem
->Group
;
769 if (TaskGroup
!= NULL
)
771 if (TaskGroup
->dwProcessId
== dwProcessId
)
776 DWORD dwProcessIdTask
;
778 if (GetWindowThreadProcessId(TaskItem
->hWnd
,
780 dwProcessIdTask
== dwProcessId
)
792 PTASK_ITEM
AllocTaskItem()
794 if (TaskItemCount
>= MAX_TASKS_COUNT
)
796 /* We need the most significant bit in 16 bit command IDs to indicate whether it
797 is a task group or task item. WM_COMMAND limits command IDs to 16 bits! */
801 ASSERT(AllocatedTaskItems
>= TaskItemCount
);
803 if (TaskItemCount
== 0)
805 TaskItems
= (PTASK_ITEM
) HeapAlloc(hProcessHeap
,
807 TASK_ITEM_ARRAY_ALLOC
* sizeof(*TaskItems
));
808 if (TaskItems
!= NULL
)
810 AllocatedTaskItems
= TASK_ITEM_ARRAY_ALLOC
;
815 else if (TaskItemCount
>= AllocatedTaskItems
)
818 SIZE_T NewArrayLength
, ActiveTaskItemIndex
;
820 NewArrayLength
= AllocatedTaskItems
+ TASK_ITEM_ARRAY_ALLOC
;
822 NewArray
= (PTASK_ITEM
) HeapReAlloc(hProcessHeap
,
825 NewArrayLength
* sizeof(*TaskItems
));
826 if (NewArray
!= NULL
)
828 if (ActiveTaskItem
!= NULL
)
830 /* Fixup the ActiveTaskItem pointer */
831 ActiveTaskItemIndex
= ActiveTaskItem
- TaskItems
;
832 ActiveTaskItem
= NewArray
+ ActiveTaskItemIndex
;
834 AllocatedTaskItems
= (WORD
) NewArrayLength
;
835 TaskItems
= NewArray
;
841 return TaskItems
+ TaskItemCount
++;
844 VOID
FreeTaskItem(IN OUT PTASK_ITEM TaskItem
)
848 if (TaskItem
== ActiveTaskItem
)
849 ActiveTaskItem
= NULL
;
851 wIndex
= (WORD
) (TaskItem
- TaskItems
);
852 if (wIndex
+ 1 < TaskItemCount
)
856 (TaskItemCount
- wIndex
- 1) * sizeof(*TaskItem
));
862 VOID
DeleteTaskItem(IN OUT PTASK_ITEM TaskItem
)
866 /* Delete the task button from the toolbar */
867 DeleteTaskItemButton(TaskItem
);
870 /* Remove the task from it's group */
871 RemoveTaskFromTaskGroup(TaskItem
);
873 /* Free the task item */
874 FreeTaskItem(TaskItem
);
877 VOID
CheckActivateTaskItem(IN OUT PTASK_ITEM TaskItem
)
879 PTASK_ITEM CurrentTaskItem
;
880 PTASK_GROUP TaskGroup
= NULL
;
882 CurrentTaskItem
= ActiveTaskItem
;
884 if (TaskItem
!= NULL
)
885 TaskGroup
= TaskItem
->Group
;
887 if (IsGroupingEnabled
&&
889 TaskGroup
->IsCollapsed
)
895 if (CurrentTaskItem
!= NULL
)
897 PTASK_GROUP CurrentTaskGroup
;
899 if (CurrentTaskItem
== TaskItem
)
902 CurrentTaskGroup
= CurrentTaskItem
->Group
;
904 if (IsGroupingEnabled
&&
905 CurrentTaskGroup
!= NULL
&&
906 CurrentTaskGroup
->IsCollapsed
)
908 if (CurrentTaskGroup
== TaskGroup
)
915 ActiveTaskItem
= NULL
;
916 if (CurrentTaskItem
->Index
>= 0)
918 UpdateTaskItemButton(CurrentTaskItem
);
923 ActiveTaskItem
= TaskItem
;
925 if (TaskItem
!= NULL
&& TaskItem
->Index
>= 0)
927 UpdateTaskItemButton(TaskItem
);
929 else if (TaskItem
== NULL
)
931 TRACE("Active TaskItem now NULL\n");
935 PTASK_ITEM
FindTaskItemByIndex(IN INT Index
)
937 PTASK_ITEM TaskItem
, LastItem
;
939 TaskItem
= TaskItems
;
940 LastItem
= TaskItem
+ TaskItemCount
;
941 while (TaskItem
!= LastItem
)
943 if (TaskItem
->Index
== Index
)
952 PTASK_GROUP
FindTaskGroupByIndex(IN INT Index
)
954 PTASK_GROUP CurrentGroup
;
956 CurrentGroup
= TaskGroups
;
957 while (CurrentGroup
!= NULL
)
959 if (CurrentGroup
->Index
== Index
)
962 CurrentGroup
= CurrentGroup
->Next
;
968 BOOL
AddTask(IN HWND hWnd
)
972 if (!::IsWindow(hWnd
) || Tray
->IsSpecialHWND(hWnd
))
975 TaskItem
= FindTaskItem(hWnd
);
976 if (TaskItem
== NULL
)
978 TRACE("Add window 0x%p\n", hWnd
);
979 TaskItem
= AllocTaskItem();
980 if (TaskItem
!= NULL
)
984 TaskItem
->hWnd
= hWnd
;
985 TaskItem
->Index
= -1;
986 TaskItem
->Group
= AddToTaskGroup(hWnd
);
990 AddTaskItemButton(TaskItem
);
995 return TaskItem
!= NULL
;
998 BOOL
ActivateTaskItem(IN OUT PTASK_ITEM TaskItem OPTIONAL
)
1000 if (TaskItem
!= NULL
)
1002 TRACE("Activate window 0x%p on button %d\n", TaskItem
->hWnd
, TaskItem
->Index
);
1005 CheckActivateTaskItem(TaskItem
);
1010 BOOL
ActivateTask(IN HWND hWnd
)
1012 PTASK_ITEM TaskItem
;
1016 return ActivateTaskItem(NULL
);
1019 TaskItem
= FindTaskItem(hWnd
);
1020 if (TaskItem
== NULL
)
1022 TaskItem
= FindOtherTaskItem(hWnd
);
1025 if (TaskItem
== NULL
)
1027 WARN("Activate window 0x%p, could not find task\n", hWnd
);
1028 RefreshWindowList();
1031 return ActivateTaskItem(TaskItem
);
1034 BOOL
DeleteTask(IN HWND hWnd
)
1036 PTASK_ITEM TaskItem
;
1038 TaskItem
= FindTaskItem(hWnd
);
1039 if (TaskItem
!= NULL
)
1041 TRACE("Delete window 0x%p on button %d\n", hWnd
, TaskItem
->Index
);
1042 DeleteTaskItem(TaskItem
);
1046 //TRACE("Failed to delete window 0x%p\n", hWnd);
1051 VOID
DeleteAllTasks()
1053 PTASK_ITEM CurrentTask
;
1055 if (TaskItemCount
> 0)
1057 CurrentTask
= TaskItems
+ TaskItemCount
;
1060 DeleteTaskItem(--CurrentTask
);
1061 } while (CurrentTask
!= TaskItems
);
1065 VOID
FlashTaskItem(IN OUT PTASK_ITEM TaskItem
)
1067 TaskItem
->RenderFlashed
= 1;
1068 UpdateTaskItemButton(TaskItem
);
1071 BOOL
FlashTask(IN HWND hWnd
)
1073 PTASK_ITEM TaskItem
;
1075 TaskItem
= FindTaskItem(hWnd
);
1076 if (TaskItem
!= NULL
)
1078 TRACE("Flashing window 0x%p on button %d\n", hWnd
, TaskItem
->Index
);
1079 FlashTaskItem(TaskItem
);
1086 VOID
RedrawTaskItem(IN OUT PTASK_ITEM TaskItem
)
1088 PTASK_GROUP TaskGroup
;
1090 TaskGroup
= TaskItem
->Group
;
1091 if (IsGroupingEnabled
&& TaskGroup
!= NULL
)
1093 if (TaskGroup
->IsCollapsed
&& TaskGroup
->Index
>= 0)
1095 UpdateTaskGroupButton(TaskGroup
);
1097 else if (TaskItem
->Index
>= 0)
1099 goto UpdateTaskItem
;
1102 else if (TaskItem
->Index
>= 0)
1105 TaskItem
->RenderFlashed
= 0;
1106 UpdateTaskItemButton(TaskItem
);
1111 BOOL
RedrawTask(IN HWND hWnd
)
1113 PTASK_ITEM TaskItem
;
1115 TaskItem
= FindTaskItem(hWnd
);
1116 if (TaskItem
!= NULL
)
1118 RedrawTaskItem(TaskItem
);
1125 VOID
UpdateButtonsSize(IN BOOL bRedrawDisabled
)
1128 UINT uiRows
, uiMax
, uiMin
, uiBtnsPerLine
, ui
;
1132 if (GetClientRect(&rcClient
) && !IsRectEmpty(&rcClient
))
1134 if (ToolbarBtnCount
> 0)
1136 Horizontal
= Tray
->IsHorizontal();
1140 DbgPrint("HORIZONTAL!\n");
1141 TBMETRICS tbm
= { 0 };
1142 tbm
.cbSize
= sizeof(tbm
);
1143 tbm
.dwMask
= TBMF_BUTTONSPACING
;
1144 TaskBar
.GetMetrics(&tbm
);
1146 uiRows
= (rcClient
.bottom
+ tbm
.cyButtonSpacing
) / (ButtonSize
.cy
+ tbm
.cyButtonSpacing
);
1150 uiBtnsPerLine
= (ToolbarBtnCount
+ uiRows
- 1) / uiRows
;
1154 DbgPrint("VERTICAL!\n");
1156 uiRows
= ToolbarBtnCount
;
1159 if (!bRedrawDisabled
)
1160 TaskBar
.BeginUpdate();
1162 /* We might need to update the button spacing */
1163 int cxButtonSpacing
= TaskBar
.UpdateTbButtonSpacing(
1164 Horizontal
, TaskBandTheme
!= NULL
,
1165 uiRows
, uiBtnsPerLine
);
1167 /* Determine the minimum and maximum width of a button */
1168 uiMin
= GetSystemMetrics(SM_CXSIZE
) + (2 * GetSystemMetrics(SM_CXEDGE
));
1171 uiMax
= GetSystemMetrics(SM_CXMINIMIZED
);
1173 /* Calculate the ideal width and make sure it's within the allowed range */
1174 NewBtnSize
= (rcClient
.right
- (uiBtnsPerLine
* cxButtonSpacing
)) / uiBtnsPerLine
;
1176 if (NewBtnSize
< (LONG
) uiMin
)
1178 if (NewBtnSize
>(LONG
)uiMax
)
1181 /* Recalculate how many buttons actually fit into one line */
1182 uiBtnsPerLine
= rcClient
.right
/ (NewBtnSize
+ cxButtonSpacing
);
1183 if (uiBtnsPerLine
== 0)
1188 NewBtnSize
= uiMax
= rcClient
.right
;
1191 ButtonSize
.cx
= NewBtnSize
;
1193 TbButtonsPerLine
= uiBtnsPerLine
;
1195 for (ui
= 0; ui
!= ToolbarBtnCount
; ui
++)
1197 TBBUTTONINFOW tbbi
= { 0 };
1198 tbbi
.cbSize
= sizeof(tbbi
);
1199 tbbi
.dwMask
= TBIF_BYINDEX
| TBIF_SIZE
| TBIF_STATE
;
1200 tbbi
.cx
= (INT
) NewBtnSize
;
1201 tbbi
.fsState
= TBSTATE_ENABLED
;
1203 /* Check if we're updating a button that is the last one in the
1204 line. If so, we need to set the TBSTATE_WRAP flag! */
1207 if ((ui
+ 1) % uiBtnsPerLine
== 0)
1208 tbbi
.fsState
|= TBSTATE_WRAP
;
1212 tbbi
.fsState
|= TBSTATE_WRAP
;
1215 if (ActiveTaskItem
!= NULL
&&
1216 ActiveTaskItem
->Index
== (INT
)ui
)
1218 tbbi
.fsState
|= TBSTATE_CHECKED
;
1221 TaskBar
.SetButtonInfo(ui
, &tbbi
);
1226 TbButtonsPerLine
= 0;
1231 // FIXME: This seems to be enabling redraws prematurely, but moving it to its right place doesn't work!
1232 TaskBar
.EndUpdate();
1235 BOOL CALLBACK
EnumWindowsProc(IN HWND hWnd
)
1237 /* Only show windows that still exist and are visible and none of explorer's
1238 special windows (such as the desktop or the tray window) */
1239 if (::IsWindow(hWnd
) && ::IsWindowVisible(hWnd
) &&
1240 !Tray
->IsSpecialHWND(hWnd
))
1242 DWORD exStyle
= GetWindowLong(hWnd
, GWL_EXSTYLE
);
1243 /* Don't list popup windows and also no tool windows */
1244 if ((GetWindow(hWnd
, GW_OWNER
) == NULL
|| exStyle
& WS_EX_APPWINDOW
) &&
1245 !(exStyle
& WS_EX_TOOLWINDOW
))
1247 TRACE("Adding task for %p...\n", hWnd
);
1256 static BOOL CALLBACK
s_EnumWindowsProc(IN HWND hWnd
, IN LPARAM lParam
)
1258 CTaskSwitchWnd
* This
= (CTaskSwitchWnd
*) lParam
;
1260 return This
->EnumWindowsProc(hWnd
);
1263 BOOL
RefreshWindowList()
1265 TRACE("Refreshing window list...\n");
1266 /* Add all windows to the toolbar */
1267 return EnumWindows(s_EnumWindowsProc
, (LPARAM
)this);
1270 LRESULT
OnThemeChanged()
1272 TRACE("OmThemeChanged\n");
1275 CloseThemeData(TaskBandTheme
);
1277 if (IsThemeActive())
1278 TaskBandTheme
= OpenThemeData(m_hWnd
, L
"TaskBand");
1280 TaskBandTheme
= NULL
;
1285 LRESULT
OnThemeChanged(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1287 return OnThemeChanged();
1290 LRESULT
OnCreate(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1292 if (!TaskBar
.Initialize(m_hWnd
))
1295 SetWindowTheme(TaskBar
.m_hWnd
, L
"TaskBand", NULL
);
1298 TaskIcons
= ImageList_Create(16, 16, ILC_COLOR32
| ILC_MASK
, 0, 1000);
1299 TaskBar
.SetImageList(TaskIcons
);
1301 /* Calculate the default button size. Don't save this in ButtonSize.cx so that
1302 the actual button width gets updated correctly on the first recalculation */
1303 int cx
= GetSystemMetrics(SM_CXMINIMIZED
);
1304 int cy
= ButtonSize
.cy
= GetSystemMetrics(SM_CYSIZE
) + (2 * GetSystemMetrics(SM_CYEDGE
));
1305 TaskBar
.SetButtonSize(cx
, cy
);
1307 /* Set proper spacing between buttons */
1308 TaskBar
.UpdateTbButtonSpacing(Tray
->IsHorizontal(), TaskBandTheme
!= NULL
);
1310 /* Register the shell hook */
1311 ShellHookMsg
= RegisterWindowMessage(TEXT("SHELLHOOK"));
1313 TRACE("ShellHookMsg got assigned number %d\n", ShellHookMsg
);
1315 HMODULE hShell32
= GetModuleHandle(TEXT("SHELL32.DLL"));
1316 if (hShell32
!= NULL
)
1318 REGSHELLHOOK RegShellHook
;
1320 /* RegisterShellHook */
1321 RegShellHook
= (REGSHELLHOOK
) GetProcAddress(hShell32
, (LPCSTR
) ((LONG
) 181));
1322 if (RegShellHook
!= NULL
)
1324 RegShellHook(m_hWnd
, 3); /* 1 if no NT! We're targeting NT so we don't care! */
1328 RefreshWindowList();
1330 /* Recalculate the button size */
1331 UpdateButtonsSize(FALSE
);
1334 SetTimer(hwnd
, 1, 5000, NULL
);
1339 LRESULT
OnDestroy(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1343 IsDestroying
= TRUE
;
1345 /* Unregister the shell hook */
1346 hShell32
= GetModuleHandle(TEXT("SHELL32.DLL"));
1347 if (hShell32
!= NULL
)
1349 REGSHELLHOOK RegShellHook
;
1351 /* RegisterShellHook */
1352 RegShellHook
= (REGSHELLHOOK
) GetProcAddress(hShell32
,
1353 (LPCSTR
) ((LONG
) 181));
1354 if (RegShellHook
!= NULL
)
1356 RegShellHook(m_hWnd
,
1361 CloseThemeData(TaskBandTheme
);
1366 BOOL
HandleAppCommand(IN WPARAM wParam
, IN LPARAM lParam
)
1370 switch (GET_APPCOMMAND_LPARAM(lParam
))
1372 case APPCOMMAND_BROWSER_SEARCH
:
1373 Ret
= SHFindFiles(NULL
,
1377 case APPCOMMAND_BROWSER_HOME
:
1378 case APPCOMMAND_LAUNCH_MAIL
:
1380 TRACE("Shell app command %d unhandled!\n", (INT
) GET_APPCOMMAND_LPARAM(lParam
));
1387 LRESULT
HandleShellHookMsg(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1391 /* In case the shell hook wasn't registered properly, ignore WM_NULLs*/
1398 TRACE("Received shell hook message: wParam=%08lx, lParam=%08lx\n", wParam
, lParam
);
1400 switch ((INT
) wParam
)
1402 case HSHELL_APPCOMMAND
:
1403 HandleAppCommand(wParam
, lParam
);
1407 case HSHELL_WINDOWCREATED
:
1408 Ret
= AddTask((HWND
) lParam
);
1411 case HSHELL_WINDOWDESTROYED
:
1412 /* The window still exists! Delay destroying it a bit */
1413 DeleteTask((HWND
) lParam
);
1417 case HSHELL_RUDEAPPACTIVATED
:
1418 case HSHELL_WINDOWACTIVATED
:
1421 ActivateTask((HWND
) lParam
);
1427 FlashTask((HWND
) lParam
);
1432 RedrawTask((HWND
) lParam
);
1436 case HSHELL_TASKMAN
:
1437 PostMessage(Tray
->GetHWND(), TWM_OPENSTARTMENU
, 0, 0);
1440 case HSHELL_ACTIVATESHELLWINDOW
:
1441 case HSHELL_LANGUAGE
:
1442 case HSHELL_SYSMENU
:
1443 case HSHELL_ENDTASK
:
1444 case HSHELL_ACCESSIBILITYSTATE
:
1445 case HSHELL_WINDOWREPLACED
:
1446 case HSHELL_WINDOWREPLACING
:
1448 case HSHELL_GETMINRECT
:
1451 #if DEBUG_SHELL_HOOK
1453 for (i
= 0, found
= 0; i
!= sizeof(hshell_msg
) / sizeof(hshell_msg
[0]); i
++)
1455 if (hshell_msg
[i
].msg
== (INT
) wParam
)
1457 TRACE("Shell message %ws unhandled (lParam = 0x%p)!\n", hshell_msg
[i
].msg_name
, lParam
);
1465 TRACE("Shell message %d unhandled (lParam = 0x%p)!\n", (INT
) wParam
, lParam
);
1473 VOID
EnableGrouping(IN BOOL bEnable
)
1475 IsGroupingEnabled
= bEnable
;
1477 /* Collapse or expand groups if neccessary */
1478 UpdateButtonsSize(FALSE
);
1481 VOID
HandleTaskItemClick(IN OUT PTASK_ITEM TaskItem
)
1486 if (::IsWindow(TaskItem
->hWnd
))
1488 bIsMinimized
= IsIconic(TaskItem
->hWnd
);
1489 bIsActive
= (TaskItem
== ActiveTaskItem
);
1491 TRACE("Active TaskItem %p, selected TaskItem %p\n", ActiveTaskItem
, TaskItem
);
1493 TRACE("Active TaskItem hWnd=%p, TaskItem hWnd %p\n", ActiveTaskItem
->hWnd
, TaskItem
->hWnd
);
1495 TRACE("Valid button clicked. HWND=%p, IsMinimized=%s, IsActive=%s...\n",
1496 TaskItem
->hWnd
, bIsMinimized
? "Yes" : "No", bIsActive
? "Yes" : "No");
1498 if (!bIsMinimized
&& bIsActive
)
1500 PostMessage(TaskItem
->hWnd
,
1504 TRACE("Valid button clicked. App window Minimized.\n");
1510 PostMessage(TaskItem
->hWnd
,
1514 TRACE("Valid button clicked. App window Restored.\n");
1517 SetForegroundWindow(TaskItem
->hWnd
);
1518 TRACE("Valid button clicked. App window Activated.\n");
1523 VOID
HandleTaskGroupClick(IN OUT PTASK_GROUP TaskGroup
)
1525 /* TODO: Show task group menu */
1528 BOOL
HandleButtonClick(IN WORD wIndex
)
1530 PTASK_ITEM TaskItem
;
1531 PTASK_GROUP TaskGroup
;
1533 if (IsGroupingEnabled
)
1535 TaskGroup
= FindTaskGroupByIndex((INT
) wIndex
);
1536 if (TaskGroup
!= NULL
&& TaskGroup
->IsCollapsed
)
1538 HandleTaskGroupClick(TaskGroup
);
1543 TaskItem
= FindTaskItemByIndex((INT
) wIndex
);
1544 if (TaskItem
!= NULL
)
1546 HandleTaskItemClick(TaskItem
);
1554 VOID
HandleTaskItemRightClick(IN OUT PTASK_ITEM TaskItem
)
1557 HMENU hmenu
= GetSystemMenu(TaskItem
->hWnd
, FALSE
);
1564 cmd
= TrackPopupMenu(hmenu
, TPM_LEFTBUTTON
| TPM_RIGHTBUTTON
| TPM_RETURNCMD
, pt
.x
, pt
.y
, 0, TaskBar
.m_hWnd
, NULL
);
1567 SetForegroundWindow(TaskItem
->hWnd
); // reactivate window after the context menu has closed
1568 PostMessage(TaskItem
->hWnd
, WM_SYSCOMMAND
, cmd
, 0);
1573 VOID
HandleTaskGroupRightClick(IN OUT PTASK_GROUP TaskGroup
)
1575 /* TODO: Show task group right click menu */
1578 BOOL
HandleButtonRightClick(IN WORD wIndex
)
1580 PTASK_ITEM TaskItem
;
1581 PTASK_GROUP TaskGroup
;
1582 if (IsGroupingEnabled
)
1584 TaskGroup
= FindTaskGroupByIndex((INT
) wIndex
);
1585 if (TaskGroup
!= NULL
&& TaskGroup
->IsCollapsed
)
1587 HandleTaskGroupRightClick(TaskGroup
);
1592 TaskItem
= FindTaskItemByIndex((INT
) wIndex
);
1594 if (TaskItem
!= NULL
)
1596 HandleTaskItemRightClick(TaskItem
);
1604 LRESULT
HandleItemPaint(IN OUT NMTBCUSTOMDRAW
*nmtbcd
)
1606 LRESULT Ret
= CDRF_DODEFAULT
;
1607 PTASK_GROUP TaskGroup
;
1608 PTASK_ITEM TaskItem
;
1610 TaskItem
= FindTaskItemByIndex((INT
) nmtbcd
->nmcd
.dwItemSpec
);
1611 TaskGroup
= FindTaskGroupByIndex((INT
) nmtbcd
->nmcd
.dwItemSpec
);
1612 if (TaskGroup
== NULL
&& TaskItem
!= NULL
)
1614 ASSERT(TaskItem
!= NULL
);
1616 if (TaskItem
!= NULL
&& ::IsWindow(TaskItem
->hWnd
))
1618 /* Make the entire button flashing if neccessary */
1619 if (nmtbcd
->nmcd
.uItemState
& CDIS_MARKED
)
1621 Ret
= TBCDRF_NOBACKGROUND
;
1624 SelectObject(nmtbcd
->nmcd
.hdc
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1625 Rectangle(nmtbcd
->nmcd
.hdc
,
1626 nmtbcd
->nmcd
.rc
.left
,
1627 nmtbcd
->nmcd
.rc
.top
,
1628 nmtbcd
->nmcd
.rc
.right
,
1629 nmtbcd
->nmcd
.rc
.bottom
);
1633 DrawThemeBackground(TaskBandTheme
, nmtbcd
->nmcd
.hdc
, TDP_FLASHBUTTON
, 0, &nmtbcd
->nmcd
.rc
, 0);
1635 nmtbcd
->clrText
= GetSysColor(COLOR_HIGHLIGHTTEXT
);
1640 else if (TaskGroup
!= NULL
)
1642 /* FIXME: Implement painting for task groups */
1647 LRESULT
HandleToolbarNotification(IN
const NMHDR
*nmh
)
1655 LPNMTBCUSTOMDRAW nmtbcd
= (LPNMTBCUSTOMDRAW
) nmh
;
1657 switch (nmtbcd
->nmcd
.dwDrawStage
)
1660 case CDDS_ITEMPREPAINT
:
1661 Ret
= HandleItemPaint(nmtbcd
);
1665 Ret
= CDRF_NOTIFYITEMDRAW
;
1669 Ret
= CDRF_DODEFAULT
;
1679 LRESULT
DrawBackground(HDC hdc
)
1683 GetClientRect(&rect
);
1684 DrawThemeParentBackground(m_hWnd
, hdc
, &rect
);
1689 LRESULT
OnEraseBackground(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1691 HDC hdc
= (HDC
) wParam
;
1699 return DrawBackground(hdc
);
1702 LRESULT
OnSize(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1706 szClient
.cx
= LOWORD(lParam
);
1707 szClient
.cy
= HIWORD(lParam
);
1708 if (TaskBar
.m_hWnd
!= NULL
)
1710 TaskBar
.SetWindowPos(NULL
, 0, 0, szClient
.cx
, szClient
.cy
, SWP_NOZORDER
);
1712 UpdateButtonsSize(FALSE
);
1717 LRESULT
OnNcHitTestToolbar(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1721 /* See if the mouse is on a button */
1722 pt
.x
= GET_X_LPARAM(lParam
);
1723 pt
.y
= GET_Y_LPARAM(lParam
);
1724 ScreenToClient(&pt
);
1726 INT index
= TaskBar
.SendMessage(TB_HITTEST
, 0, (LPARAM
) &pt
);
1729 /* Make the control appear to be transparent outside of any buttons */
1730 return HTTRANSPARENT
;
1737 LRESULT
OnNcHitTest(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1740 /* We want the tray window to be draggable everywhere, so make the control
1741 appear transparent */
1742 Ret
= DefWindowProc(uMsg
, wParam
, lParam
);
1743 if (Ret
!= HTVSCROLL
&& Ret
!= HTHSCROLL
)
1744 Ret
= HTTRANSPARENT
;
1748 LRESULT
OnCommand(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1751 if (lParam
!= 0 && (HWND
) lParam
== TaskBar
.m_hWnd
)
1753 HandleButtonClick(LOWORD(wParam
));
1758 LRESULT
OnNotify(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1761 const NMHDR
*nmh
= (const NMHDR
*) lParam
;
1763 if (nmh
->hwndFrom
== TaskBar
.m_hWnd
)
1765 Ret
= HandleToolbarNotification(nmh
);
1770 LRESULT
OnEnableGrouping(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1772 LRESULT Ret
= IsGroupingEnabled
;
1773 if ((BOOL
)wParam
!= IsGroupingEnabled
)
1775 EnableGrouping((BOOL
) wParam
);
1780 LRESULT
OnUpdateTaskbarPos(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1782 /* Update the button spacing */
1783 TaskBar
.UpdateTbButtonSpacing(Tray
->IsHorizontal(), TaskBandTheme
!= NULL
);
1787 LRESULT
OnContextMenu(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1790 if (TaskBar
.m_hWnd
!= NULL
)
1795 pt
.x
= GET_X_LPARAM(lParam
);
1796 pt
.y
= GET_Y_LPARAM(lParam
);
1798 ::ScreenToClient(TaskBar
.m_hWnd
, &pt
);
1800 iBtn
= TaskBar
.HitTest(&pt
);
1803 HandleButtonRightClick(iBtn
);
1806 goto ForwardContextMenuMsg
;
1810 ForwardContextMenuMsg
:
1811 /* Forward message */
1812 Ret
= SendMessage(Tray
->GetHWND(), uMsg
, wParam
, lParam
);
1817 LRESULT
OnKludgeItemRect(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1819 PTASK_ITEM TaskItem
= FindTaskItem((HWND
) wParam
);
1822 RECT
* prcMinRect
= (RECT
*) lParam
;
1823 RECT rcItem
, rcToolbar
;
1824 TaskBar
.GetItemRect(TaskItem
->Index
, &rcItem
);
1825 GetWindowRect(TaskBar
.m_hWnd
, &rcToolbar
);
1827 OffsetRect(&rcItem
, rcToolbar
.left
, rcToolbar
.top
);
1829 *prcMinRect
= rcItem
;
1835 LRESULT
OnTimer(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1848 DECLARE_WND_CLASS_EX(szTaskSwitchWndClass
, CS_DBLCLKS
, COLOR_3DFACE
)
1850 BEGIN_MSG_MAP(CTaskSwitchWnd
)
1851 MESSAGE_HANDLER(WM_THEMECHANGED
, OnThemeChanged
)
1852 MESSAGE_HANDLER(WM_ERASEBKGND
, OnEraseBackground
)
1853 MESSAGE_HANDLER(WM_SIZE
, OnSize
)
1854 MESSAGE_HANDLER(WM_CREATE
, OnCreate
)
1855 MESSAGE_HANDLER(WM_DESTROY
, OnDestroy
)
1856 MESSAGE_HANDLER(WM_NCHITTEST
, OnNcHitTest
)
1857 MESSAGE_HANDLER(WM_COMMAND
, OnCommand
)
1858 MESSAGE_HANDLER(WM_NOTIFY
, OnNotify
)
1859 MESSAGE_HANDLER(TSWM_ENABLEGROUPING
, OnEnableGrouping
)
1860 MESSAGE_HANDLER(TSWM_UPDATETASKBARPOS
, OnUpdateTaskbarPos
)
1861 MESSAGE_HANDLER(WM_CONTEXTMENU
, OnContextMenu
)
1862 MESSAGE_HANDLER(WM_TIMER
, OnTimer
)
1863 MESSAGE_HANDLER(ShellHookMsg
, HandleShellHookMsg
)
1865 MESSAGE_HANDLER(WM_NCHITTEST
, OnNcHitTestToolbar
)
1868 HWND
_Init(IN HWND hWndParent
, IN OUT ITrayWindow
*tray
)
1871 hWndNotify
= GetParent();
1872 IsGroupingEnabled
= TRUE
; /* FIXME */
1873 return Create(hWndParent
, 0, szRunningApps
, WS_CHILD
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
| WS_TABSTOP
);
1878 CreateTaskSwitchWnd(IN HWND hWndParent
, IN OUT ITrayWindow
*Tray
)
1880 CTaskSwitchWnd
* instance
;
1882 // TODO: Destroy after the window is destroyed
1883 instance
= new CTaskSwitchWnd();
1885 return instance
->_Init(hWndParent
, Tray
);