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
23 /* Set DUMP_TASKS to 1 to enable a dump of the tasks and task groups every
27 static const TCHAR szTaskSwitchWndClass
[] = TEXT("MSTaskSwWClass");
28 static const TCHAR szRunningApps
[] = TEXT("Running Applications");
30 typedef struct _TASK_GROUP
32 /* We have to use a linked list instead of an array so we don't have to
33 update all pointers to groups in the task item array when removing
35 struct _TASK_GROUP
*Next
;
46 DWORD IsCollapsed
: 1;
49 } TASK_GROUP
, *PTASK_GROUP
;
51 typedef struct _TASK_ITEM
66 /* IsFlashing is TRUE when the task bar item should be flashing. */
69 /* RenderFlashed is only TRUE if the task bar item should be
70 drawn with a flash. */
71 DWORD RenderFlashed
: 1;
74 } TASK_ITEM
, *PTASK_ITEM
;
76 #define TASK_ITEM_ARRAY_ALLOC 64
78 typedef struct _TASK_SWITCH_WND
86 PTASK_GROUP TaskGroups
;
89 WORD AllocatedTaskItems
;
91 PTASK_ITEM ActiveTaskItem
;
95 UINT TbButtonsPerLine
;
104 DWORD IsGroupingEnabled
: 1;
105 DWORD IsDestroying
: 1;
106 DWORD IsToolbarSubclassed
: 1;
112 } TASK_SWITCH_WND
, *PTASK_SWITCH_WND
;
114 #define TSW_TOOLBAR_SUBCLASS_ID 1
116 #define MAX_TASKS_COUNT (0x7FFF)
118 static VOID
TaskSwitchWnd_UpdateButtonsSize(IN OUT PTASK_SWITCH_WND This
,
119 IN BOOL bRedrawDisabled
);
122 TaskSwitchWnd_GetWndTextFromTaskItem(IN OUT PTASK_SWITCH_WND This
,
123 IN PTASK_ITEM TaskItem
)
125 /* Get the window text without sending a message so we don't hang if an
126 application isn't responding! */
127 if (InternalGetWindowText(TaskItem
->hWnd
,
129 sizeof(This
->szBuf
) / sizeof(This
->szBuf
[0])) > 0)
140 TaskSwitchWnd_DumpTasks(IN OUT PTASK_SWITCH_WND This
)
142 PTASK_GROUP CurrentGroup
;
143 PTASK_ITEM CurrentTaskItem
, LastTaskItem
;
145 DbgPrint("Tasks dump:\n");
146 if (This
->IsGroupingEnabled
)
148 CurrentGroup
= This
->TaskGroups
;
149 while (CurrentGroup
!= NULL
)
151 DbgPrint("- Group PID: 0x%p Tasks: %d Index: %d\n", CurrentGroup
->dwProcessId
, CurrentGroup
->dwTaskCount
, CurrentGroup
->Index
);
153 CurrentTaskItem
= This
->TaskItems
;
154 LastTaskItem
= CurrentTaskItem
+ This
->TaskItemCount
;
155 while (CurrentTaskItem
!= LastTaskItem
)
157 if (CurrentTaskItem
->Group
== CurrentGroup
)
159 DbgPrint(" + Task hwnd: 0x%p Index: %d\n", CurrentTaskItem
->hWnd
, CurrentTaskItem
->Index
);
164 CurrentGroup
= CurrentGroup
->Next
;
167 CurrentTaskItem
= This
->TaskItems
;
168 LastTaskItem
= CurrentTaskItem
+ This
->TaskItemCount
;
169 while (CurrentTaskItem
!= LastTaskItem
)
171 if (CurrentTaskItem
->Group
== NULL
)
173 DbgPrint("- Task hwnd: 0x%p Index: %d\n", CurrentTaskItem
->hWnd
, CurrentTaskItem
->Index
);
180 CurrentTaskItem
= This
->TaskItems
;
181 LastTaskItem
= CurrentTaskItem
+ This
->TaskItemCount
;
182 while (CurrentTaskItem
!= LastTaskItem
)
184 DbgPrint("- Task hwnd: 0x%p Index: %d\n", CurrentTaskItem
->hWnd
, CurrentTaskItem
->Index
);
192 TaskSwitchWnd_BeginUpdate(IN OUT PTASK_SWITCH_WND This
)
194 SendMessage(This
->hWndToolbar
,
201 TaskSwitchWnd_EndUpdate(IN OUT PTASK_SWITCH_WND This
)
203 SendMessage(This
->hWndToolbar
,
207 InvalidateRect(This
->hWndToolbar
,
213 TaskSwitchWnd_SetToolbarButtonCommandId(IN OUT PTASK_SWITCH_WND This
,
219 tbbi
.cbSize
= sizeof(tbbi
);
220 tbbi
.dwMask
= TBIF_BYINDEX
| TBIF_COMMAND
;
221 tbbi
.idCommand
= iCommandId
;
223 return SendMessage(This
->hWndToolbar
,
225 (WPARAM
)iButtonIndex
,
230 TaskSwitchWnd_UpdateIndexesAfterButtonInserted(IN OUT PTASK_SWITCH_WND This
,
233 PTASK_GROUP CurrentGroup
;
234 PTASK_ITEM CurrentTaskItem
, LastTaskItem
;
237 if (This
->IsGroupingEnabled
)
239 /* Update all affected groups */
240 CurrentGroup
= This
->TaskGroups
;
241 while (CurrentGroup
!= NULL
)
243 if (CurrentGroup
->IsCollapsed
&&
244 CurrentGroup
->Index
>= iIndex
)
246 /* Update the toolbar buttons */
247 NewIndex
= CurrentGroup
->Index
+ 1;
248 if (TaskSwitchWnd_SetToolbarButtonCommandId(This
,
249 CurrentGroup
->Index
+ 1,
252 CurrentGroup
->Index
= NewIndex
;
255 CurrentGroup
->Index
= -1;
258 CurrentGroup
= CurrentGroup
->Next
;
262 /* Update all affected task items */
263 CurrentTaskItem
= This
->TaskItems
;
264 LastTaskItem
= CurrentTaskItem
+ This
->TaskItemCount
;
265 while (CurrentTaskItem
!= LastTaskItem
)
267 CurrentGroup
= CurrentTaskItem
->Group
;
268 if (CurrentGroup
!= NULL
)
270 if (!CurrentGroup
->IsCollapsed
&&
271 CurrentTaskItem
->Index
>= iIndex
)
273 goto UpdateTaskItemBtn
;
276 else if (CurrentTaskItem
->Index
>= iIndex
)
279 /* Update the toolbar buttons */
280 NewIndex
= CurrentTaskItem
->Index
+ 1;
281 if (TaskSwitchWnd_SetToolbarButtonCommandId(This
,
282 CurrentTaskItem
->Index
+ 1,
285 CurrentTaskItem
->Index
= NewIndex
;
288 CurrentTaskItem
->Index
= -1;
296 TaskSwitchWnd_UpdateIndexesAfterButtonDeleted(IN OUT PTASK_SWITCH_WND This
,
299 PTASK_GROUP CurrentGroup
;
300 PTASK_ITEM CurrentTaskItem
, LastTaskItem
;
303 if (This
->IsGroupingEnabled
)
305 /* Update all affected groups */
306 CurrentGroup
= This
->TaskGroups
;
307 while (CurrentGroup
!= NULL
)
309 if (CurrentGroup
->IsCollapsed
&&
310 CurrentGroup
->Index
> iIndex
)
312 /* Update the toolbar buttons */
313 NewIndex
= CurrentGroup
->Index
- 1;
314 if (TaskSwitchWnd_SetToolbarButtonCommandId(This
,
315 CurrentGroup
->Index
- 1,
318 CurrentGroup
->Index
= NewIndex
;
321 CurrentGroup
->Index
= -1;
324 CurrentGroup
= CurrentGroup
->Next
;
328 /* Update all affected task items */
329 CurrentTaskItem
= This
->TaskItems
;
330 LastTaskItem
= CurrentTaskItem
+ This
->TaskItemCount
;
331 while (CurrentTaskItem
!= LastTaskItem
)
333 CurrentGroup
= CurrentTaskItem
->Group
;
334 if (CurrentGroup
!= NULL
)
336 if (!CurrentGroup
->IsCollapsed
&&
337 CurrentTaskItem
->Index
> iIndex
)
339 goto UpdateTaskItemBtn
;
342 else if (CurrentTaskItem
->Index
> iIndex
)
345 /* Update the toolbar buttons */
346 NewIndex
= CurrentTaskItem
->Index
- 1;
347 if (TaskSwitchWnd_SetToolbarButtonCommandId(This
,
348 CurrentTaskItem
->Index
- 1,
351 CurrentTaskItem
->Index
= NewIndex
;
354 CurrentTaskItem
->Index
= -1;
362 TaskSwitchWnd_UpdateTaskGroupButton(IN OUT PTASK_SWITCH_WND This
,
363 IN PTASK_GROUP TaskGroup
)
365 ASSERT(TaskGroup
->Index
>= 0);
367 /* FIXME: Implement */
369 return TaskGroup
->Index
;
373 TaskSwitchWnd_ExpandTaskGroup(IN OUT PTASK_SWITCH_WND This
,
374 IN PTASK_GROUP TaskGroup
)
376 ASSERT(TaskGroup
->dwTaskCount
> 0);
377 ASSERT(TaskGroup
->IsCollapsed
);
378 ASSERT(TaskGroup
->Index
>= 0);
380 /* FIXME: Implement */
384 TaskSwitchWnd_GetWndIcon(HWND hwnd
)
388 SendMessageTimeout(hwnd
, WM_GETICON
, ICON_SMALL2
, 0, SMTO_ABORTIFHUNG
, 1000, (PDWORD_PTR
)&hIcon
);
391 SendMessageTimeout(hwnd
, WM_GETICON
, ICON_SMALL
, 0, SMTO_ABORTIFHUNG
, 1000, (PDWORD_PTR
)&hIcon
);
394 SendMessageTimeout(hwnd
, WM_GETICON
, ICON_BIG
, 0, SMTO_ABORTIFHUNG
, 1000, (PDWORD_PTR
)&hIcon
);
397 hIcon
= (HICON
)GetClassLongPtr(hwnd
, GCL_HICONSM
);
400 hIcon
= (HICON
)GetClassLongPtr(hwnd
, GCL_HICON
);
405 TaskSwitchWnd_UpdateTaskItemButton(IN OUT PTASK_SWITCH_WND This
,
406 IN PTASK_ITEM TaskItem
)
411 ASSERT(TaskItem
->Index
>= 0);
413 tbbi
.cbSize
= sizeof(tbbi
);
414 tbbi
.dwMask
= TBIF_BYINDEX
| TBIF_STATE
| TBIF_TEXT
| TBIF_IMAGE
;
415 tbbi
.fsState
= TBSTATE_ENABLED
;
416 if (This
->ActiveTaskItem
== TaskItem
)
417 tbbi
.fsState
|= TBSTATE_CHECKED
;
419 if (TaskItem
->RenderFlashed
)
420 tbbi
.fsState
|= TBSTATE_MARKED
;
422 /* Check if we're updating a button that is the last one in the
423 line. If so, we need to set the TBSTATE_WRAP flag! */
424 if (This
->TbButtonsPerLine
!= 0 &&
425 (TaskItem
->Index
+ 1) % This
->TbButtonsPerLine
== 0)
427 tbbi
.fsState
|= TBSTATE_WRAP
;
430 tbbi
.pszText
= TaskSwitchWnd_GetWndTextFromTaskItem(This
,
433 icon
= TaskSwitchWnd_GetWndIcon(TaskItem
->hWnd
);
434 TaskItem
->IconIndex
= ImageList_ReplaceIcon(This
->TaskIcons
,
437 tbbi
.iImage
= TaskItem
->IconIndex
;
439 if (!SendMessage(This
->hWndToolbar
,
441 (WPARAM
)TaskItem
->Index
,
444 TaskItem
->Index
= -1;
448 DbgPrint("Updated button %d for hwnd 0x%p\n", TaskItem
->Index
, TaskItem
->hWnd
);
449 return TaskItem
->Index
;
453 TaskSwitchWnd_RemoveIcon(IN OUT PTASK_SWITCH_WND This
,
454 IN PTASK_ITEM TaskItem
)
457 PTASK_ITEM currentTaskItem
, LastItem
;
459 if (TaskItem
->IconIndex
== -1)
462 tbbi
.cbSize
= sizeof(tbbi
);
463 tbbi
.dwMask
= TBIF_IMAGE
;
465 currentTaskItem
= This
->TaskItems
;
466 LastItem
= currentTaskItem
+ This
->TaskItemCount
;
467 while (currentTaskItem
!= LastItem
)
469 if (currentTaskItem
->IconIndex
> TaskItem
->IconIndex
)
471 currentTaskItem
->IconIndex
--;
472 tbbi
.iImage
= currentTaskItem
->IconIndex
;
474 SendMessage(This
->hWndToolbar
,
476 currentTaskItem
->Index
,
482 ImageList_Remove(This
->TaskIcons
, TaskItem
->IconIndex
);
486 TaskSwitchWnd_FindLastTaskItemOfGroup(IN OUT PTASK_SWITCH_WND This
,
487 IN PTASK_GROUP TaskGroup OPTIONAL
,
488 IN PTASK_ITEM NewTaskItem OPTIONAL
)
490 PTASK_ITEM TaskItem
, LastTaskItem
, FoundTaskItem
= NULL
;
493 ASSERT(This
->IsGroupingEnabled
);
495 TaskItem
= This
->TaskItems
;
496 LastTaskItem
= TaskItem
+ This
->TaskItemCount
;
498 dwTaskCount
= (TaskGroup
!= NULL
? TaskGroup
->dwTaskCount
: MAX_TASKS_COUNT
);
500 ASSERT(dwTaskCount
> 0);
502 while (TaskItem
!= LastTaskItem
)
504 if (TaskItem
->Group
== TaskGroup
)
506 if ((NewTaskItem
!= NULL
&& TaskItem
!= NewTaskItem
) || NewTaskItem
== NULL
)
508 FoundTaskItem
= TaskItem
;
511 if (--dwTaskCount
== 0)
513 /* We found the last task item in the group! */
521 return FoundTaskItem
;
525 TaskSwitchWnd_CalculateTaskItemNewButtonIndex(IN OUT PTASK_SWITCH_WND This
,
526 IN PTASK_ITEM TaskItem
)
528 PTASK_GROUP TaskGroup
;
529 PTASK_ITEM LastTaskItem
;
531 /* NOTE: This routine assumes that the group is *not* collapsed! */
533 TaskGroup
= TaskItem
->Group
;
534 if (This
->IsGroupingEnabled
)
536 if (TaskGroup
!= NULL
)
538 ASSERT(TaskGroup
->Index
< 0);
539 ASSERT(!TaskGroup
->IsCollapsed
);
541 if (TaskGroup
->dwTaskCount
> 1)
543 LastTaskItem
= TaskSwitchWnd_FindLastTaskItemOfGroup(This
,
546 if (LastTaskItem
!= NULL
)
548 /* Since the group is expanded the task items must have an index */
549 ASSERT(LastTaskItem
->Index
>= 0);
551 return LastTaskItem
->Index
+ 1;
557 /* Find the last NULL group button. NULL groups are added at the end of the
558 task item list when grouping is enabled */
559 LastTaskItem
= TaskSwitchWnd_FindLastTaskItemOfGroup(This
,
562 if (LastTaskItem
!= NULL
)
564 ASSERT(LastTaskItem
->Index
>= 0);
566 return LastTaskItem
->Index
+ 1;
571 return This
->ToolbarBtnCount
;
575 TaskSwitchWnd_AddTaskItemButton(IN OUT PTASK_SWITCH_WND This
,
576 IN OUT PTASK_ITEM TaskItem
)
582 if (TaskItem
->Index
>= 0)
584 return TaskSwitchWnd_UpdateTaskItemButton(This
,
588 if (TaskItem
->Group
!= NULL
&&
589 TaskItem
->Group
->IsCollapsed
)
591 /* The task group is collapsed, we only need to update the group button */
592 return TaskSwitchWnd_UpdateTaskGroupButton(This
,
596 icon
= TaskSwitchWnd_GetWndIcon(TaskItem
->hWnd
);
597 TaskItem
->IconIndex
= ImageList_AddIcon(This
->TaskIcons
, icon
);
599 tbBtn
.iBitmap
= TaskItem
->IconIndex
;
600 tbBtn
.fsState
= TBSTATE_ENABLED
| TBSTATE_ELLIPSES
;
601 tbBtn
.fsStyle
= BTNS_CHECK
| BTNS_NOPREFIX
| BTNS_SHOWTEXT
;
602 tbBtn
.dwData
= TaskItem
->Index
;
604 tbBtn
.iString
= (DWORD_PTR
)TaskSwitchWnd_GetWndTextFromTaskItem(This
,
607 /* Find out where to insert the new button */
608 iIndex
= TaskSwitchWnd_CalculateTaskItemNewButtonIndex(This
,
611 tbBtn
.idCommand
= iIndex
;
613 TaskSwitchWnd_BeginUpdate(This
);
615 if (SendMessage(This
->hWndToolbar
,
620 TaskSwitchWnd_UpdateIndexesAfterButtonInserted(This
,
623 DbgPrint("Added button %d for hwnd 0x%p\n", iIndex
, TaskItem
->hWnd
);
625 TaskItem
->Index
= iIndex
;
626 This
->ToolbarBtnCount
++;
628 /* Update button sizes and fix the button wrapping */
629 TaskSwitchWnd_UpdateButtonsSize(This
,
634 TaskSwitchWnd_EndUpdate(This
);
640 TaskSwitchWnd_DeleteTaskItemButton(IN OUT PTASK_SWITCH_WND This
,
641 IN OUT PTASK_ITEM TaskItem
)
643 PTASK_GROUP TaskGroup
;
646 TaskGroup
= TaskItem
->Group
;
648 if (TaskItem
->Index
>= 0)
650 if ((TaskGroup
!= NULL
&& !TaskGroup
->IsCollapsed
) ||
653 TaskSwitchWnd_BeginUpdate(This
);
655 TaskSwitchWnd_RemoveIcon(This
, TaskItem
);
656 iIndex
= TaskItem
->Index
;
657 if (SendMessage(This
->hWndToolbar
,
662 TaskItem
->Index
= -1;
663 This
->ToolbarBtnCount
--;
665 TaskSwitchWnd_UpdateIndexesAfterButtonDeleted(This
,
668 /* Update button sizes and fix the button wrapping */
669 TaskSwitchWnd_UpdateButtonsSize(This
,
674 TaskSwitchWnd_EndUpdate(This
);
682 TaskSwitchWnd_AddToTaskGroup(IN OUT PTASK_SWITCH_WND This
,
686 PTASK_GROUP TaskGroup
, *PrevLink
;
688 if (!GetWindowThreadProcessId(hWnd
,
691 DbgPrint("Cannot get process id of hwnd 0x%p\n", hWnd
);
695 /* Try to find an existing task group */
696 TaskGroup
= This
->TaskGroups
;
697 PrevLink
= &This
->TaskGroups
;
698 while (TaskGroup
!= NULL
)
700 if (TaskGroup
->dwProcessId
== dwProcessId
)
702 TaskGroup
->dwTaskCount
++;
706 PrevLink
= &TaskGroup
->Next
;
707 TaskGroup
= TaskGroup
->Next
;
710 /* Allocate a new task group */
711 TaskGroup
= HeapAlloc(hProcessHeap
,
714 if (TaskGroup
!= NULL
)
716 ZeroMemory(TaskGroup
,
719 TaskGroup
->dwTaskCount
= 1;
720 TaskGroup
->dwProcessId
= dwProcessId
;
721 TaskGroup
->Index
= -1;
723 /* Add the task group to the list */
724 *PrevLink
= TaskGroup
;
731 TaskSwitchWnd_RemoveTaskFromTaskGroup(IN OUT PTASK_SWITCH_WND This
,
732 IN OUT PTASK_ITEM TaskItem
)
734 PTASK_GROUP TaskGroup
, CurrentGroup
, *PrevLink
;
736 TaskGroup
= TaskItem
->Group
;
737 if (TaskGroup
!= NULL
)
739 DWORD dwNewTaskCount
= --TaskGroup
->dwTaskCount
;
740 if (dwNewTaskCount
== 0)
742 /* Find the previous pointer in the chain */
743 CurrentGroup
= This
->TaskGroups
;
744 PrevLink
= &This
->TaskGroups
;
745 while (CurrentGroup
!= TaskGroup
)
747 PrevLink
= &CurrentGroup
->Next
;
748 CurrentGroup
= CurrentGroup
->Next
;
751 /* Remove the group from the list */
752 ASSERT(TaskGroup
== CurrentGroup
);
753 *PrevLink
= TaskGroup
->Next
;
755 /* Free the task group */
756 HeapFree(hProcessHeap
,
760 else if (TaskGroup
->IsCollapsed
&&
761 TaskGroup
->Index
>= 0)
763 if (dwNewTaskCount
> 1)
765 /* FIXME: Check if we should expand the group */
766 /* Update the task group button */
767 TaskSwitchWnd_UpdateTaskGroupButton(This
,
772 /* Expand the group of one task button to a task button */
773 TaskSwitchWnd_ExpandTaskGroup(This
,
781 TaskSwitchWnd_FindTaskItem(IN OUT PTASK_SWITCH_WND This
,
784 PTASK_ITEM TaskItem
, LastItem
;
786 TaskItem
= This
->TaskItems
;
787 LastItem
= TaskItem
+ This
->TaskItemCount
;
788 while (TaskItem
!= LastItem
)
790 if (TaskItem
->hWnd
== hWnd
)
800 TaskSwitchWnd_FindOtherTaskItem(IN OUT PTASK_SWITCH_WND This
,
803 PTASK_ITEM LastItem
, TaskItem
;
804 PTASK_GROUP TaskGroup
;
807 if (!GetWindowThreadProcessId(hWnd
,
813 /* Try to find another task that belongs to the same
814 process as the given window */
815 TaskItem
= This
->TaskItems
;
816 LastItem
= TaskItem
+ This
->TaskItemCount
;
817 while (TaskItem
!= LastItem
)
819 TaskGroup
= TaskItem
->Group
;
820 if (TaskGroup
!= NULL
)
822 if (TaskGroup
->dwProcessId
== dwProcessId
)
827 DWORD dwProcessIdTask
;
829 if (GetWindowThreadProcessId(TaskItem
->hWnd
,
831 dwProcessIdTask
== dwProcessId
)
844 TaskSwitchWnd_AllocTaskItem(IN OUT PTASK_SWITCH_WND This
)
846 if (This
->TaskItemCount
>= MAX_TASKS_COUNT
)
848 /* We need the most significant bit in 16 bit command IDs to indicate whether it
849 is a task group or task item. WM_COMMAND limits command IDs to 16 bits! */
853 ASSERT(This
->AllocatedTaskItems
>= This
->TaskItemCount
);
855 if (This
->TaskItemCount
== 0)
857 This
->TaskItems
= HeapAlloc(hProcessHeap
,
859 TASK_ITEM_ARRAY_ALLOC
* sizeof(*This
->TaskItems
));
860 if (This
->TaskItems
!= NULL
)
862 This
->AllocatedTaskItems
= TASK_ITEM_ARRAY_ALLOC
;
867 else if (This
->TaskItemCount
>= This
->AllocatedTaskItems
)
870 SIZE_T NewArrayLength
, ActiveTaskItemIndex
;
872 NewArrayLength
= This
->AllocatedTaskItems
+ TASK_ITEM_ARRAY_ALLOC
;
874 NewArray
= HeapReAlloc(hProcessHeap
,
877 NewArrayLength
* sizeof(*This
->TaskItems
));
878 if (NewArray
!= NULL
)
880 if (This
->ActiveTaskItem
!= NULL
)
882 /* Fixup the ActiveTaskItem pointer */
883 ActiveTaskItemIndex
= This
->ActiveTaskItem
- This
->TaskItems
;
884 This
->ActiveTaskItem
= NewArray
+ ActiveTaskItemIndex
;
886 This
->AllocatedTaskItems
= (WORD
)NewArrayLength
;
887 This
->TaskItems
= NewArray
;
893 return This
->TaskItems
+ This
->TaskItemCount
++;
897 TaskSwitchWnd_FreeTaskItem(IN OUT PTASK_SWITCH_WND This
,
898 IN OUT PTASK_ITEM TaskItem
)
902 if (TaskItem
== This
->ActiveTaskItem
)
903 This
->ActiveTaskItem
= NULL
;
905 wIndex
= (WORD
)(TaskItem
- This
->TaskItems
);
906 if (wIndex
+ 1 < This
->TaskItemCount
)
910 (This
->TaskItemCount
- wIndex
- 1) * sizeof(*TaskItem
));
913 This
->TaskItemCount
--;
917 TaskSwitchWnd_DeleteTaskItem(IN OUT PTASK_SWITCH_WND This
,
918 IN OUT PTASK_ITEM TaskItem
)
920 if (!This
->IsDestroying
)
922 /* Delete the task button from the toolbar */
923 TaskSwitchWnd_DeleteTaskItemButton(This
,
927 /* Remove the task from it's group */
928 TaskSwitchWnd_RemoveTaskFromTaskGroup(This
,
931 /* Free the task item */
932 TaskSwitchWnd_FreeTaskItem(This
,
937 TaskSwitchWnd_CheckActivateTaskItem(IN OUT PTASK_SWITCH_WND This
,
938 IN OUT PTASK_ITEM TaskItem
)
940 PTASK_ITEM ActiveTaskItem
;
941 PTASK_GROUP TaskGroup
= NULL
;
943 ActiveTaskItem
= This
->ActiveTaskItem
;
945 if (TaskItem
!= NULL
)
946 TaskGroup
= TaskItem
->Group
;
948 if (This
->IsGroupingEnabled
&&
950 TaskGroup
->IsCollapsed
)
956 if (ActiveTaskItem
!= NULL
)
958 PTASK_GROUP ActiveTaskGroup
;
960 if (ActiveTaskItem
== TaskItem
)
963 ActiveTaskGroup
= ActiveTaskItem
->Group
;
965 if (This
->IsGroupingEnabled
&&
966 ActiveTaskGroup
!= NULL
&&
967 ActiveTaskGroup
->IsCollapsed
)
969 if (ActiveTaskGroup
== TaskGroup
)
976 This
->ActiveTaskItem
= NULL
;
977 if (ActiveTaskItem
->Index
>= 0)
979 TaskSwitchWnd_UpdateTaskItemButton(This
,
985 This
->ActiveTaskItem
= TaskItem
;
987 if (TaskItem
!= NULL
&& TaskItem
->Index
>= 0)
989 TaskSwitchWnd_UpdateTaskItemButton(This
,
995 FindTaskItemByIndex(IN OUT PTASK_SWITCH_WND This
,
998 PTASK_ITEM TaskItem
, LastItem
;
1000 TaskItem
= This
->TaskItems
;
1001 LastItem
= TaskItem
+ This
->TaskItemCount
;
1002 while (TaskItem
!= LastItem
)
1004 if (TaskItem
->Index
== Index
)
1014 FindTaskGroupByIndex(IN OUT PTASK_SWITCH_WND This
,
1017 PTASK_GROUP CurrentGroup
;
1019 CurrentGroup
= This
->TaskGroups
;
1020 while (CurrentGroup
!= NULL
)
1022 if (CurrentGroup
->Index
== Index
)
1025 CurrentGroup
= CurrentGroup
->Next
;
1028 return CurrentGroup
;
1032 TaskSwitchWnd_AddTask(IN OUT PTASK_SWITCH_WND This
,
1035 PTASK_ITEM TaskItem
;
1037 TaskItem
= TaskSwitchWnd_FindTaskItem(This
,
1039 if (TaskItem
== NULL
)
1041 DbgPrint("Add window 0x%p\n", hWnd
);
1042 TaskItem
= TaskSwitchWnd_AllocTaskItem(This
);
1043 if (TaskItem
!= NULL
)
1045 ZeroMemory(TaskItem
,
1047 TaskItem
->hWnd
= hWnd
;
1048 TaskItem
->Index
= -1;
1049 TaskItem
->Group
= TaskSwitchWnd_AddToTaskGroup(This
,
1052 if (!This
->IsDestroying
)
1054 TaskSwitchWnd_AddTaskItemButton(This
,
1060 return TaskItem
!= NULL
;
1064 TaskSwitchWnd_ActivateTaskItem(IN OUT PTASK_SWITCH_WND This
,
1065 IN OUT PTASK_ITEM TaskItem OPTIONAL
)
1067 if (TaskItem
!= NULL
)
1069 DbgPrint("Activate window 0x%p on button %d\n", TaskItem
->hWnd
, TaskItem
->Index
);
1072 TaskSwitchWnd_CheckActivateTaskItem(This
,
1079 TaskSwitchWnd_ActivateTask(IN OUT PTASK_SWITCH_WND This
,
1082 PTASK_ITEM TaskItem
;
1084 TaskItem
= TaskSwitchWnd_FindTaskItem(This
,
1086 if (TaskItem
== NULL
)
1088 TaskItem
= TaskSwitchWnd_FindOtherTaskItem(This
,
1092 if (TaskItem
== NULL
)
1094 DbgPrint("Activate window 0x%p, could not find task\n", hWnd
);
1097 return TaskSwitchWnd_ActivateTaskItem(This
,
1102 TaskSwitchWnd_DeleteTask(IN OUT PTASK_SWITCH_WND This
,
1105 PTASK_ITEM TaskItem
;
1107 TaskItem
= TaskSwitchWnd_FindTaskItem(This
,
1109 if (TaskItem
!= NULL
)
1111 DbgPrint("Delete window 0x%p on button %d\n", hWnd
, TaskItem
->Index
);
1112 TaskSwitchWnd_DeleteTaskItem(This
,
1117 //DbgPrint("Failed to delete window 0x%p\n", hWnd);
1123 TaskSwitchWnd_DeleteAllTasks(IN OUT PTASK_SWITCH_WND This
)
1125 PTASK_ITEM CurrentTask
;
1127 if (This
->TaskItemCount
> 0)
1129 CurrentTask
= This
->TaskItems
+ This
->TaskItemCount
;
1132 TaskSwitchWnd_DeleteTaskItem(This
,
1134 } while (CurrentTask
!= This
->TaskItems
);
1139 TaskSwitchWnd_FlashTaskItem(IN OUT PTASK_SWITCH_WND This
,
1140 IN OUT PTASK_ITEM TaskItem
)
1142 TaskItem
->RenderFlashed
= 1;
1143 TaskSwitchWnd_UpdateTaskItemButton(This
,
1148 TaskSwitchWnd_FlashTask(IN OUT PTASK_SWITCH_WND This
,
1151 PTASK_ITEM TaskItem
;
1153 TaskItem
= TaskSwitchWnd_FindTaskItem(This
,
1155 if (TaskItem
!= NULL
)
1157 DbgPrint("Flashing window 0x%p on button %d\n", hWnd
, TaskItem
->Index
);
1158 TaskSwitchWnd_FlashTaskItem(This
,
1167 TaskSwitchWnd_RedrawTaskItem(IN OUT PTASK_SWITCH_WND This
,
1168 IN OUT PTASK_ITEM TaskItem
)
1170 PTASK_GROUP TaskGroup
;
1172 TaskGroup
= TaskItem
->Group
;
1173 if (This
->IsGroupingEnabled
&& TaskGroup
!= NULL
)
1175 if (TaskGroup
->IsCollapsed
&& TaskGroup
->Index
>= 0)
1177 TaskSwitchWnd_UpdateTaskGroupButton(This
,
1180 else if (TaskItem
->Index
>= 0)
1182 goto UpdateTaskItem
;
1185 else if (TaskItem
->Index
>= 0)
1188 TaskItem
->RenderFlashed
= 0;
1189 TaskSwitchWnd_UpdateTaskItemButton(This
,
1196 TaskSwitchWnd_RedrawTask(IN OUT PTASK_SWITCH_WND This
,
1199 PTASK_ITEM TaskItem
;
1201 TaskItem
= TaskSwitchWnd_FindTaskItem(This
,
1203 if (TaskItem
!= NULL
)
1205 TaskSwitchWnd_RedrawTaskItem(This
,
1214 TaskSwitchWnd_UpdateTbButtonSpacing(IN OUT PTASK_SWITCH_WND This
,
1215 IN BOOL bHorizontal
,
1217 IN UINT uiBtnsPerLine
)
1221 tbm
.cbSize
= sizeof(tbm
);
1222 tbm
.dwMask
= TBMF_BARPAD
| TBMF_BUTTONSPACING
;
1224 tbm
.cxBarPad
= tbm
.cyBarPad
= 0;
1226 if (bHorizontal
|| uiBtnsPerLine
> 1)
1227 tbm
.cxButtonSpacing
= (3 * GetSystemMetrics(SM_CXEDGE
) / 2);
1229 tbm
.cxButtonSpacing
= 0;
1231 if (!bHorizontal
|| uiRows
> 1)
1232 tbm
.cyButtonSpacing
= (3 * GetSystemMetrics(SM_CYEDGE
) / 2);
1234 tbm
.cyButtonSpacing
= 0;
1236 SendMessage(This
->hWndToolbar
,
1241 return tbm
.cxButtonSpacing
;
1246 TaskSwitchWnd_UpdateButtonsSize(IN OUT PTASK_SWITCH_WND This
,
1247 IN BOOL bRedrawDisabled
)
1250 UINT uiRows
, uiMax
, uiMin
, uiBtnsPerLine
, ui
;
1256 if (GetClientRect(This
->hWnd
,
1258 !IsRectEmpty(&rcClient
))
1260 if (This
->ToolbarBtnCount
> 0)
1262 ZeroMemory (&tbm
, sizeof (tbm
));
1263 tbm
.cbSize
= sizeof(tbm
);
1264 tbm
.dwMask
= TBMF_BUTTONSPACING
;
1265 SendMessage(This
->hWndToolbar
,
1270 uiRows
= (rcClient
.bottom
+ tbm
.cyButtonSpacing
) / (This
->ButtonSize
.cy
+ tbm
.cyButtonSpacing
);
1274 uiBtnsPerLine
= (This
->ToolbarBtnCount
+ uiRows
- 1) / uiRows
;
1276 Horizontal
= ITrayWindow_IsHorizontal(This
->Tray
);
1278 if (!bRedrawDisabled
)
1279 TaskSwitchWnd_BeginUpdate(This
);
1281 /* We might need to update the button spacing */
1282 tbm
.cxButtonSpacing
= TaskSwitchWnd_UpdateTbButtonSpacing(This
,
1287 /* Calculate the ideal width and make sure it's within the allowed range */
1288 NewBtnSize
= (rcClient
.right
- (uiBtnsPerLine
* tbm
.cxButtonSpacing
)) / uiBtnsPerLine
;
1290 /* Determine the minimum and maximum width of a button */
1292 uiMax
= GetSystemMetrics(SM_CXMINIMIZED
);
1294 uiMax
= rcClient
.right
;
1296 uiMin
= GetSystemMetrics(SM_CXSIZE
) + (2 * GetSystemMetrics(SM_CXEDGE
));
1298 if (NewBtnSize
< (LONG
)uiMin
)
1300 if (NewBtnSize
> (LONG
)uiMax
)
1303 This
->ButtonSize
.cx
= NewBtnSize
;
1305 /* Recalculate how many buttons actually fit into one line */
1306 uiBtnsPerLine
= rcClient
.right
/ (NewBtnSize
+ tbm
.cxButtonSpacing
);
1307 if (uiBtnsPerLine
== 0)
1309 This
->TbButtonsPerLine
= uiBtnsPerLine
;
1311 tbbi
.cbSize
= sizeof(tbbi
);
1312 tbbi
.dwMask
= TBIF_BYINDEX
| TBIF_SIZE
| TBIF_STATE
;
1313 tbbi
.cx
= (INT
)NewBtnSize
;
1315 for (ui
= 0; ui
!= This
->ToolbarBtnCount
; ui
++)
1317 tbbi
.fsState
= TBSTATE_ENABLED
;
1319 /* Check if we're updating a button that is the last one in the
1320 line. If so, we need to set the TBSTATE_WRAP flag! */
1321 if ((ui
+ 1) % uiBtnsPerLine
== 0)
1322 tbbi
.fsState
|= TBSTATE_WRAP
;
1324 if (This
->ActiveTaskItem
!= NULL
&&
1325 This
->ActiveTaskItem
->Index
== ui
)
1327 tbbi
.fsState
|= TBSTATE_CHECKED
;
1330 SendMessage(This
->hWndToolbar
,
1337 /* FIXME: Force the window to the correct position in case some idiot
1338 did something to us */
1339 SetWindowPos(This
->hWndToolbar
,
1343 rcClient
.right
, /* FIXME */
1344 rcClient
.bottom
, /* FIXME */
1345 SWP_NOACTIVATE
| SWP_NOZORDER
);
1350 This
->TbButtonsPerLine
= 0;
1351 This
->ButtonSize
.cx
= 0;
1355 TaskSwitchWnd_EndUpdate(This
);
1358 static BOOL CALLBACK
1359 TaskSwitchWnd_EnumWindowsProc(IN HWND hWnd
,
1362 PTASK_SWITCH_WND This
= (PTASK_SWITCH_WND
)lParam
;
1364 /* Only show windows that still exist and are visible and none of explorer's
1365 special windows (such as the desktop or the tray window) */
1366 if (IsWindow(hWnd
) && IsWindowVisible(hWnd
) &&
1367 !ITrayWindow_IsSpecialHWND(This
->Tray
,
1370 /* Don't list popup windows and also no tool windows */
1372 GW_OWNER
) == NULL
&&
1373 !(GetWindowLong(hWnd
,
1374 GWL_EXSTYLE
) & WS_EX_TOOLWINDOW
))
1376 TaskSwitchWnd_AddTask(This
,
1384 static LRESULT CALLBACK
1385 TaskSwichWnd_ToolbarSubclassedProc(IN HWND hWnd
,
1389 IN UINT_PTR uIdSubclass
,
1390 IN DWORD_PTR dwRefData
)
1394 Ret
= DefSubclassProc(hWnd
,
1399 if (msg
== WM_NCHITTEST
&& Ret
== HTCLIENT
)
1403 /* See if the mouse is on a button */
1404 pt
.x
= (SHORT
)LOWORD(lParam
);
1405 pt
.y
= (SHORT
)HIWORD(lParam
);
1407 if (MapWindowPoints(HWND_DESKTOP
,
1411 (INT
)SendMessage(hWnd
,
1416 /* Make the control appear to be transparent outside of any buttons */
1417 Ret
= HTTRANSPARENT
;
1425 TaskSwitchWnd_UpdateTheme(IN OUT PTASK_SWITCH_WND This
)
1427 if (This
->TaskBandTheme
)
1428 CloseThemeData(This
->TaskBandTheme
);
1430 if (IsThemeActive())
1431 This
->TaskBandTheme
= OpenThemeData(This
->hWnd
, L
"TaskBand");
1433 This
->TaskBandTheme
= NULL
;
1437 TaskSwitchWnd_Create(IN OUT PTASK_SWITCH_WND This
)
1439 This
->hWndToolbar
= CreateWindowEx(0,
1442 WS_CHILD
| WS_VISIBLE
| WS_CLIPCHILDREN
|
1443 TBSTYLE_TOOLTIPS
| TBSTYLE_WRAPABLE
| TBSTYLE_LIST
|
1444 TBSTYLE_TRANSPARENT
|
1445 CCS_TOP
| CCS_NORESIZE
| CCS_NODIVIDER
,
1455 if (This
->hWndToolbar
!= NULL
)
1460 SetWindowTheme(This
->hWndToolbar
, L
"TaskBand", NULL
);
1461 TaskSwitchWnd_UpdateTheme(This
);
1462 /* Identify the version we're using */
1463 SendMessage(This
->hWndToolbar
,
1464 TB_BUTTONSTRUCTSIZE
,
1468 This
->TaskIcons
= ImageList_Create(16, 16, ILC_COLOR32
, 0, 1000);
1469 SendMessage(This
->hWndToolbar
, TB_SETIMAGELIST
, 0, (LPARAM
)This
->TaskIcons
);
1471 /* Calculate the default button size. Don't save this in This->ButtonSize.cx so that
1472 the actual button width gets updated correctly on the first recalculation */
1473 BtnSize
.cx
= GetSystemMetrics(SM_CXMINIMIZED
);
1474 This
->ButtonSize
.cy
= BtnSize
.cy
= GetSystemMetrics(SM_CYSIZE
) + (2 * GetSystemMetrics(SM_CYEDGE
));
1475 SendMessage(This
->hWndToolbar
,
1478 (LPARAM
)MAKELONG(BtnSize
.cx
,
1481 /* We don't want to see partially clipped buttons...not that we could see them... */
1483 SendMessage(This
->hWndToolbar
,
1484 TB_SETEXTENDEDSTYLE
,
1486 TBSTYLE_EX_HIDECLIPPEDBUTTONS
);
1489 /* Set proper spacing between buttons */
1490 TaskSwitchWnd_UpdateTbButtonSpacing(This
,
1491 ITrayWindow_IsHorizontal(This
->Tray
),
1495 /* Register the shell hook */
1496 This
->ShellHookMsg
= RegisterWindowMessage(TEXT("SHELLHOOK"));
1497 hShell32
= GetModuleHandle(TEXT("SHELL32.DLL"));
1498 if (hShell32
!= NULL
)
1500 REGSHELLHOOK RegShellHook
;
1502 /* RegisterShellHook */
1503 RegShellHook
= (REGSHELLHOOK
)GetProcAddress(hShell32
,
1504 (LPCSTR
)((LONG
)181));
1505 if (RegShellHook
!= NULL
)
1507 RegShellHook(This
->hWnd
,
1508 3); /* 1 if no NT! We're targeting NT so we don't care! */
1512 /* Add all windows to the toolbar */
1513 EnumWindows(TaskSwitchWnd_EnumWindowsProc
,
1516 /* Recalculate the button size */
1517 TaskSwitchWnd_UpdateButtonsSize(This
,
1520 /* Subclass the toolbar control because it doesn't provide a
1521 NM_NCHITTEST notification */
1522 This
->IsToolbarSubclassed
= SetWindowSubclass(This
->hWndToolbar
,
1523 TaskSwichWnd_ToolbarSubclassedProc
,
1524 TSW_TOOLBAR_SUBCLASS_ID
,
1530 TaskSwitchWnd_NCDestroy(IN OUT PTASK_SWITCH_WND This
)
1534 This
->IsDestroying
= TRUE
;
1536 /* Unregister the shell hook */
1537 hShell32
= GetModuleHandle(TEXT("SHELL32.DLL"));
1538 if (hShell32
!= NULL
)
1540 REGSHELLHOOK RegShellHook
;
1542 /* RegisterShellHook */
1543 RegShellHook
= (REGSHELLHOOK
)GetProcAddress(hShell32
,
1544 (LPCSTR
)((LONG
)181));
1545 if (RegShellHook
!= NULL
)
1547 RegShellHook(This
->hWnd
,
1552 CloseThemeData(This
->TaskBandTheme
);
1553 TaskSwitchWnd_DeleteAllTasks(This
);
1557 TaskSwitchWnd_HandleAppCommand(IN OUT PTASK_SWITCH_WND This
,
1563 switch (GET_APPCOMMAND_LPARAM(lParam
))
1565 case APPCOMMAND_BROWSER_SEARCH
:
1566 Ret
= SHFindFiles(NULL
,
1570 case APPCOMMAND_BROWSER_HOME
:
1571 case APPCOMMAND_LAUNCH_MAIL
:
1573 DbgPrint("Shell app command %d unhandled!\n", (INT
)GET_APPCOMMAND_LPARAM(lParam
));
1582 TaskSwitchWnd_HandleShellHookMsg(IN OUT PTASK_SWITCH_WND This
,
1588 switch ((INT
)wParam
)
1590 case HSHELL_APPCOMMAND
:
1591 TaskSwitchWnd_HandleAppCommand(This
,
1597 case HSHELL_WINDOWCREATED
:
1598 TaskSwitchWnd_AddTask(This
,
1603 case HSHELL_WINDOWDESTROYED
:
1604 /* The window still exists! Delay destroying it a bit */
1605 TaskSwitchWnd_DeleteTask(This
,
1610 case HSHELL_ACTIVATESHELLWINDOW
:
1611 goto UnhandledShellMessage
;
1613 case HSHELL_RUDEAPPACTIVATED
:
1614 goto UnhandledShellMessage
;
1616 case HSHELL_WINDOWACTIVATED
:
1617 TaskSwitchWnd_ActivateTask(This
,
1622 case HSHELL_GETMINRECT
:
1623 goto UnhandledShellMessage
;
1626 TaskSwitchWnd_FlashTask(This
,
1632 TaskSwitchWnd_RedrawTask(This
,
1637 case HSHELL_TASKMAN
:
1638 case HSHELL_LANGUAGE
:
1639 case HSHELL_SYSMENU
:
1640 case HSHELL_ENDTASK
:
1641 case HSHELL_ACCESSIBILITYSTATE
:
1642 case HSHELL_WINDOWREPLACED
:
1643 case HSHELL_WINDOWREPLACING
:
1646 static const struct {
1650 {HSHELL_WINDOWCREATED
, L
"HSHELL_WINDOWCREATED"},
1651 {HSHELL_WINDOWDESTROYED
, L
"HSHELL_WINDOWDESTROYED"},
1652 {HSHELL_ACTIVATESHELLWINDOW
, L
"HSHELL_ACTIVATESHELLWINDOW"},
1653 {HSHELL_WINDOWACTIVATED
, L
"HSHELL_WINDOWACTIVATED"},
1654 {HSHELL_GETMINRECT
, L
"HSHELL_GETMINRECT"},
1655 {HSHELL_REDRAW
, L
"HSHELL_REDRAW"},
1656 {HSHELL_TASKMAN
, L
"HSHELL_TASKMAN"},
1657 {HSHELL_LANGUAGE
, L
"HSHELL_LANGUAGE"},
1658 {HSHELL_SYSMENU
, L
"HSHELL_SYSMENU"},
1659 {HSHELL_ENDTASK
, L
"HSHELL_ENDTASK"},
1660 {HSHELL_ACCESSIBILITYSTATE
, L
"HSHELL_ACCESSIBILITYSTATE"},
1661 {HSHELL_APPCOMMAND
, L
"HSHELL_APPCOMMAND"},
1662 {HSHELL_WINDOWREPLACED
, L
"HSHELL_WINDOWREPLACED"},
1663 {HSHELL_WINDOWREPLACING
, L
"HSHELL_WINDOWREPLACING"},
1664 {HSHELL_RUDEAPPACTIVATED
, L
"HSHELL_RUDEAPPACTIVATED"},
1667 UnhandledShellMessage
:
1668 for (i
= 0, found
= 0; i
!= sizeof(hshell_msg
) / sizeof(hshell_msg
[0]); i
++)
1670 if (hshell_msg
[i
].msg
== (INT
)wParam
)
1672 DbgPrint("Shell message %ws unhandled (lParam = 0x%p)!\n", hshell_msg
[i
].msg_name
, lParam
);
1679 DbgPrint("Shell message %d unhandled (lParam = 0x%p)!\n", (INT
)wParam
, lParam
);
1689 TaskSwitchWnd_EnableGrouping(IN OUT PTASK_SWITCH_WND This
,
1692 This
->IsGroupingEnabled
= bEnable
;
1694 /* Collapse or expand groups if neccessary */
1695 TaskSwitchWnd_UpdateButtonsSize(This
,
1700 TaskSwitchWnd_HandleTaskItemClick(IN OUT PTASK_SWITCH_WND This
,
1701 IN OUT PTASK_ITEM TaskItem
)
1706 if (IsWindow(TaskItem
->hWnd
))
1708 bIsMinimized
= IsIconic(TaskItem
->hWnd
);
1709 bIsActive
= (TaskItem
== This
->ActiveTaskItem
);
1711 if (!bIsMinimized
&& bIsActive
)
1713 PostMessage(TaskItem
->hWnd
,
1722 PostMessage(TaskItem
->hWnd
,
1728 SetForegroundWindow(TaskItem
->hWnd
);
1734 TaskSwitchWnd_HandleTaskGroupClick(IN OUT PTASK_SWITCH_WND This
,
1735 IN OUT PTASK_GROUP TaskGroup
)
1737 /* TODO: Show task group menu */
1741 TaskSwitchWnd_HandleButtonClick(IN OUT PTASK_SWITCH_WND This
,
1744 PTASK_ITEM TaskItem
;
1745 PTASK_GROUP TaskGroup
;
1747 if (This
->IsGroupingEnabled
)
1749 TaskGroup
= FindTaskGroupByIndex(This
,
1751 if (TaskGroup
!= NULL
&& TaskGroup
->IsCollapsed
)
1753 TaskSwitchWnd_HandleTaskGroupClick(This
,
1759 TaskItem
= FindTaskItemByIndex(This
,
1761 if (TaskItem
!= NULL
)
1763 TaskSwitchWnd_HandleTaskItemClick(This
,
1773 TaskSwitchWnd_HandleTaskItemRightClick(IN OUT PTASK_SWITCH_WND This
,
1774 IN OUT PTASK_ITEM TaskItem
)
1777 HMENU hmenu
= GetSystemMenu(TaskItem
->hWnd
, FALSE
);
1783 cmd
= TrackPopupMenu(hmenu
, TPM_LEFTBUTTON
|TPM_RIGHTBUTTON
|TPM_RETURNCMD
, pt
.x
, pt
.y
, 0, This
->hWndToolbar
, NULL
);
1785 SetForegroundWindow(TaskItem
->hWnd
); // reactivate window after the context menu has closed
1786 PostMessage(TaskItem
->hWnd
, WM_SYSCOMMAND
, cmd
, 0);
1792 TaskSwitchWnd_HandleTaskGroupRightClick(IN OUT PTASK_SWITCH_WND This
,
1793 IN OUT PTASK_GROUP TaskGroup
)
1795 /* TODO: Show task group right click menu */
1799 TaskSwitchWnd_HandleButtonRightClick(IN OUT PTASK_SWITCH_WND This
,
1802 PTASK_ITEM TaskItem
;
1803 PTASK_GROUP TaskGroup
;
1804 if (This
->IsGroupingEnabled
)
1806 TaskGroup
= FindTaskGroupByIndex(This
,
1808 if (TaskGroup
!= NULL
&& TaskGroup
->IsCollapsed
)
1810 TaskSwitchWnd_HandleTaskGroupRightClick(This
,
1816 TaskItem
= FindTaskItemByIndex(This
,
1819 if (TaskItem
!= NULL
)
1821 TaskSwitchWnd_HandleTaskItemRightClick(This
,
1831 TaskSwichWnd_HandleItemPaint(IN OUT PTASK_SWITCH_WND This
,
1832 IN OUT NMTBCUSTOMDRAW
*nmtbcd
)
1834 LRESULT Ret
= CDRF_DODEFAULT
;
1835 PTASK_GROUP TaskGroup
;
1836 PTASK_ITEM TaskItem
;
1838 TaskItem
= FindTaskItemByIndex(This
,
1839 (INT
)nmtbcd
->nmcd
.dwItemSpec
);
1840 TaskGroup
= FindTaskGroupByIndex(This
,
1841 (INT
)nmtbcd
->nmcd
.dwItemSpec
);
1842 if (TaskGroup
== NULL
&& TaskItem
!= NULL
)
1844 ASSERT(TaskItem
!= NULL
);
1846 if (TaskItem
!= NULL
&& IsWindow(TaskItem
->hWnd
))
1848 /* Make the entire button flashing if neccessary */
1849 if (nmtbcd
->nmcd
.uItemState
& CDIS_MARKED
)
1851 Ret
= TBCDRF_NOBACKGROUND
;
1852 if (!This
->TaskBandTheme
)
1854 SelectObject(nmtbcd
->nmcd
.hdc
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1855 Rectangle(nmtbcd
->nmcd
.hdc
,
1856 nmtbcd
->nmcd
.rc
.left
,
1857 nmtbcd
->nmcd
.rc
.top
,
1858 nmtbcd
->nmcd
.rc
.right
,
1859 nmtbcd
->nmcd
.rc
.bottom
);
1863 DrawThemeBackground(This
->TaskBandTheme
, nmtbcd
->nmcd
.hdc
, TDP_FLASHBUTTON
, 0, &nmtbcd
->nmcd
.rc
, 0);
1865 nmtbcd
->clrText
= GetSysColor(COLOR_HIGHLIGHTTEXT
);
1870 else if (TaskGroup
!= NULL
)
1872 /* FIXME: Implement painting for task groups */
1878 TaskSwitchWnd_HandleToolbarNotification(IN OUT PTASK_SWITCH_WND This
,
1879 IN
const NMHDR
*nmh
)
1887 LPNMTBCUSTOMDRAW nmtbcd
= (LPNMTBCUSTOMDRAW
)nmh
;
1889 switch (nmtbcd
->nmcd
.dwDrawStage
)
1892 case CDDS_ITEMPREPAINT
:
1893 Ret
= TaskSwichWnd_HandleItemPaint(This
,
1898 Ret
= CDRF_NOTIFYITEMDRAW
;
1902 Ret
= CDRF_DODEFAULT
;
1913 TaskSwitchWnd_DrawBackground(HWND hwnd
,
1918 GetClientRect(hwnd
, &rect
);
1919 DrawThemeParentBackground(hwnd
, hdc
, &rect
);
1922 static LRESULT CALLBACK
1923 TaskSwitchWndProc(IN HWND hwnd
,
1928 PTASK_SWITCH_WND This
= NULL
;
1929 LRESULT Ret
= FALSE
;
1931 if (uMsg
!= WM_NCCREATE
)
1933 This
= (PTASK_SWITCH_WND
)GetWindowLongPtr(hwnd
,
1937 if (This
!= NULL
|| uMsg
== WM_NCCREATE
)
1941 case WM_THEMECHANGED
:
1942 TaskSwitchWnd_UpdateTheme(This
);
1945 TaskSwitchWnd_DrawBackground(hwnd
, (HDC
)wParam
);
1951 szClient
.cx
= LOWORD(lParam
);
1952 szClient
.cy
= HIWORD(lParam
);
1953 if (This
->hWndToolbar
!= NULL
)
1955 SetWindowPos(This
->hWndToolbar
,
1963 TaskSwitchWnd_UpdateButtonsSize(This
,
1971 /* We want the tray window to be draggable everywhere, so make the control
1972 appear transparent */
1973 Ret
= DefWindowProc(hwnd
,
1977 if (Ret
!= HTVSCROLL
&& Ret
!= HTHSCROLL
)
1978 Ret
= HTTRANSPARENT
;
1984 if (lParam
!= 0 && (HWND
)lParam
== This
->hWndToolbar
)
1986 TaskSwitchWnd_HandleButtonClick(This
,
1994 const NMHDR
*nmh
= (const NMHDR
*)lParam
;
1996 if (nmh
->hwndFrom
== This
->hWndToolbar
)
1998 Ret
= TaskSwitchWnd_HandleToolbarNotification(This
,
2004 case TSWM_ENABLEGROUPING
:
2006 Ret
= This
->IsGroupingEnabled
;
2007 if (wParam
!= This
->IsGroupingEnabled
)
2009 TaskSwitchWnd_EnableGrouping(This
,
2015 case TSWM_UPDATETASKBARPOS
:
2017 /* Update the button spacing */
2018 TaskSwitchWnd_UpdateTbButtonSpacing(This
,
2019 ITrayWindow_IsHorizontal(This
->Tray
),
2025 case WM_CONTEXTMENU
:
2027 if (This
->hWndToolbar
!= NULL
)
2032 pt
.x
= (LONG
)LOWORD(lParam
);
2033 pt
.y
= (LONG
)HIWORD(lParam
);
2035 MapWindowPoints(NULL
,
2040 iBtn
= (INT_PTR
)SendMessage(This
->hWndToolbar
,
2046 TaskSwitchWnd_HandleButtonRightClick(This
,
2050 goto ForwardContextMenuMsg
;
2054 ForwardContextMenuMsg
:
2055 /* Forward message */
2056 Ret
= SendMessage(ITrayWindow_GetHWND(This
->Tray
),
2066 LPCREATESTRUCT CreateStruct
= (LPCREATESTRUCT
)lParam
;
2067 This
= HeapAlloc(hProcessHeap
,
2076 This
->hWndNotify
= CreateStruct
->hwndParent
;
2077 This
->Tray
= (ITrayWindow
*)CreateStruct
->lpCreateParams
;
2078 This
->IsGroupingEnabled
= TRUE
; /* FIXME */
2079 SetWindowLongPtr(hwnd
,
2087 TaskSwitchWnd_Create(This
);
2099 if (This
->IsToolbarSubclassed
)
2101 if (RemoveWindowSubclass(This
->hWndToolbar
,
2102 TaskSwichWnd_ToolbarSubclassedProc
,
2103 TSW_TOOLBAR_SUBCLASS_ID
))
2105 This
->IsToolbarSubclassed
= FALSE
;
2111 TaskSwitchWnd_NCDestroy(This
);
2112 HeapFree(hProcessHeap
,
2115 SetWindowLongPtr(hwnd
,
2125 TaskSwitchWnd_DumpTasks(This
);
2132 /* HandleDefaultMessage: */
2133 if (uMsg
== This
->ShellHookMsg
&& This
->ShellHookMsg
!= 0)
2135 /* Process shell messages */
2136 Ret
= (LRESULT
)TaskSwitchWnd_HandleShellHookMsg(This
,
2142 Ret
= DefWindowProc(hwnd
,
2151 Ret
= DefWindowProc(hwnd
,
2162 CreateTaskSwitchWnd(IN HWND hWndParent
,
2163 IN OUT ITrayWindow
*Tray
)
2167 hwndTaskBar
= CreateWindowEx(0,
2168 szTaskSwitchWndClass
,
2170 WS_CHILD
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
| WS_TABSTOP
,
2184 RegisterTaskSwitchWndClass(VOID
)
2188 wc
.style
= CS_DBLCLKS
;
2189 wc
.lpfnWndProc
= TaskSwitchWndProc
;
2191 wc
.cbWndExtra
= sizeof(PTASK_SWITCH_WND
);
2192 wc
.hInstance
= hExplorerInstance
;
2194 wc
.hCursor
= LoadCursor(NULL
,
2196 wc
.hbrBackground
= (HBRUSH
)(COLOR_3DFACE
+ 1);
2197 wc
.lpszMenuName
= NULL
;
2198 wc
.lpszClassName
= szTaskSwitchWndClass
;
2200 return RegisterClass(&wc
) != 0;
2204 UnregisterTaskSwitchWndClass(VOID
)
2206 UnregisterClass(szTaskSwitchWndClass
,