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 TaskGroup
->dwTaskCount
= 1;
717 TaskGroup
->dwProcessId
= dwProcessId
;
718 TaskGroup
->Index
= -1;
720 /* Add the task group to the list */
721 *PrevLink
= TaskGroup
;
728 TaskSwitchWnd_RemoveTaskFromTaskGroup(IN OUT PTASK_SWITCH_WND This
,
729 IN OUT PTASK_ITEM TaskItem
)
731 PTASK_GROUP TaskGroup
, CurrentGroup
, *PrevLink
;
733 TaskGroup
= TaskItem
->Group
;
734 if (TaskGroup
!= NULL
)
736 DWORD dwNewTaskCount
= --TaskGroup
->dwTaskCount
;
737 if (dwNewTaskCount
== 0)
739 /* Find the previous pointer in the chain */
740 CurrentGroup
= This
->TaskGroups
;
741 PrevLink
= &This
->TaskGroups
;
742 while (CurrentGroup
!= TaskGroup
)
744 PrevLink
= &CurrentGroup
->Next
;
745 CurrentGroup
= CurrentGroup
->Next
;
748 /* Remove the group from the list */
749 ASSERT(TaskGroup
== CurrentGroup
);
750 *PrevLink
= TaskGroup
->Next
;
752 /* Free the task group */
753 HeapFree(hProcessHeap
,
757 else if (TaskGroup
->IsCollapsed
&&
758 TaskGroup
->Index
>= 0)
760 if (dwNewTaskCount
> 1)
762 /* FIXME: Check if we should expand the group */
763 /* Update the task group button */
764 TaskSwitchWnd_UpdateTaskGroupButton(This
,
769 /* Expand the group of one task button to a task button */
770 TaskSwitchWnd_ExpandTaskGroup(This
,
778 TaskSwitchWnd_FindTaskItem(IN OUT PTASK_SWITCH_WND This
,
781 PTASK_ITEM TaskItem
, LastItem
;
783 TaskItem
= This
->TaskItems
;
784 LastItem
= TaskItem
+ This
->TaskItemCount
;
785 while (TaskItem
!= LastItem
)
787 if (TaskItem
->hWnd
== hWnd
)
797 TaskSwitchWnd_FindOtherTaskItem(IN OUT PTASK_SWITCH_WND This
,
800 PTASK_ITEM LastItem
, TaskItem
;
801 PTASK_GROUP TaskGroup
;
804 if (!GetWindowThreadProcessId(hWnd
,
810 /* Try to find another task that belongs to the same
811 process as the given window */
812 TaskItem
= This
->TaskItems
;
813 LastItem
= TaskItem
+ This
->TaskItemCount
;
814 while (TaskItem
!= LastItem
)
816 TaskGroup
= TaskItem
->Group
;
817 if (TaskGroup
!= NULL
)
819 if (TaskGroup
->dwProcessId
== dwProcessId
)
824 DWORD dwProcessIdTask
;
826 if (GetWindowThreadProcessId(TaskItem
->hWnd
,
828 dwProcessIdTask
== dwProcessId
)
841 TaskSwitchWnd_AllocTaskItem(IN OUT PTASK_SWITCH_WND This
)
843 if (This
->TaskItemCount
>= MAX_TASKS_COUNT
)
845 /* We need the most significant bit in 16 bit command IDs to indicate whether it
846 is a task group or task item. WM_COMMAND limits command IDs to 16 bits! */
850 ASSERT(This
->AllocatedTaskItems
>= This
->TaskItemCount
);
852 if (This
->TaskItemCount
== 0)
854 This
->TaskItems
= HeapAlloc(hProcessHeap
,
856 TASK_ITEM_ARRAY_ALLOC
* sizeof(*This
->TaskItems
));
857 if (This
->TaskItems
!= NULL
)
859 This
->AllocatedTaskItems
= TASK_ITEM_ARRAY_ALLOC
;
864 else if (This
->TaskItemCount
>= This
->AllocatedTaskItems
)
867 SIZE_T NewArrayLength
, ActiveTaskItemIndex
;
869 NewArrayLength
= This
->AllocatedTaskItems
+ TASK_ITEM_ARRAY_ALLOC
;
871 NewArray
= HeapReAlloc(hProcessHeap
,
874 NewArrayLength
* sizeof(*This
->TaskItems
));
875 if (NewArray
!= NULL
)
877 if (This
->ActiveTaskItem
!= NULL
)
879 /* Fixup the ActiveTaskItem pointer */
880 ActiveTaskItemIndex
= This
->ActiveTaskItem
- This
->TaskItems
;
881 This
->ActiveTaskItem
= NewArray
+ ActiveTaskItemIndex
;
883 This
->AllocatedTaskItems
= (WORD
)NewArrayLength
;
884 This
->TaskItems
= NewArray
;
890 return This
->TaskItems
+ This
->TaskItemCount
++;
894 TaskSwitchWnd_FreeTaskItem(IN OUT PTASK_SWITCH_WND This
,
895 IN OUT PTASK_ITEM TaskItem
)
899 if (TaskItem
== This
->ActiveTaskItem
)
900 This
->ActiveTaskItem
= NULL
;
902 wIndex
= (WORD
)(TaskItem
- This
->TaskItems
);
903 if (wIndex
+ 1 < This
->TaskItemCount
)
907 (This
->TaskItemCount
- wIndex
- 1) * sizeof(*TaskItem
));
910 This
->TaskItemCount
--;
914 TaskSwitchWnd_DeleteTaskItem(IN OUT PTASK_SWITCH_WND This
,
915 IN OUT PTASK_ITEM TaskItem
)
917 if (!This
->IsDestroying
)
919 /* Delete the task button from the toolbar */
920 TaskSwitchWnd_DeleteTaskItemButton(This
,
924 /* Remove the task from it's group */
925 TaskSwitchWnd_RemoveTaskFromTaskGroup(This
,
928 /* Free the task item */
929 TaskSwitchWnd_FreeTaskItem(This
,
934 TaskSwitchWnd_CheckActivateTaskItem(IN OUT PTASK_SWITCH_WND This
,
935 IN OUT PTASK_ITEM TaskItem
)
937 PTASK_ITEM ActiveTaskItem
;
938 PTASK_GROUP TaskGroup
= NULL
;
940 ActiveTaskItem
= This
->ActiveTaskItem
;
942 if (TaskItem
!= NULL
)
943 TaskGroup
= TaskItem
->Group
;
945 if (This
->IsGroupingEnabled
&&
947 TaskGroup
->IsCollapsed
)
953 if (ActiveTaskItem
!= NULL
)
955 PTASK_GROUP ActiveTaskGroup
;
957 if (ActiveTaskItem
== TaskItem
)
960 ActiveTaskGroup
= ActiveTaskItem
->Group
;
962 if (This
->IsGroupingEnabled
&&
963 ActiveTaskGroup
!= NULL
&&
964 ActiveTaskGroup
->IsCollapsed
)
966 if (ActiveTaskGroup
== TaskGroup
)
973 This
->ActiveTaskItem
= NULL
;
974 if (ActiveTaskItem
->Index
>= 0)
976 TaskSwitchWnd_UpdateTaskItemButton(This
,
982 This
->ActiveTaskItem
= TaskItem
;
984 if (TaskItem
!= NULL
&& TaskItem
->Index
>= 0)
986 TaskSwitchWnd_UpdateTaskItemButton(This
,
992 FindTaskItemByIndex(IN OUT PTASK_SWITCH_WND This
,
995 PTASK_ITEM TaskItem
, LastItem
;
997 TaskItem
= This
->TaskItems
;
998 LastItem
= TaskItem
+ This
->TaskItemCount
;
999 while (TaskItem
!= LastItem
)
1001 if (TaskItem
->Index
== Index
)
1011 FindTaskGroupByIndex(IN OUT PTASK_SWITCH_WND This
,
1014 PTASK_GROUP CurrentGroup
;
1016 CurrentGroup
= This
->TaskGroups
;
1017 while (CurrentGroup
!= NULL
)
1019 if (CurrentGroup
->Index
== Index
)
1022 CurrentGroup
= CurrentGroup
->Next
;
1025 return CurrentGroup
;
1029 TaskSwitchWnd_AddTask(IN OUT PTASK_SWITCH_WND This
,
1032 PTASK_ITEM TaskItem
;
1034 TaskItem
= TaskSwitchWnd_FindTaskItem(This
,
1036 if (TaskItem
== NULL
)
1038 DbgPrint("Add window 0x%p\n", hWnd
);
1039 TaskItem
= TaskSwitchWnd_AllocTaskItem(This
);
1040 if (TaskItem
!= NULL
)
1042 ZeroMemory(TaskItem
,
1044 TaskItem
->hWnd
= hWnd
;
1045 TaskItem
->Index
= -1;
1046 TaskItem
->Group
= TaskSwitchWnd_AddToTaskGroup(This
,
1049 if (!This
->IsDestroying
)
1051 TaskSwitchWnd_AddTaskItemButton(This
,
1057 return TaskItem
!= NULL
;
1061 TaskSwitchWnd_ActivateTaskItem(IN OUT PTASK_SWITCH_WND This
,
1062 IN OUT PTASK_ITEM TaskItem OPTIONAL
)
1064 if (TaskItem
!= NULL
)
1066 DbgPrint("Activate window 0x%p on button %d\n", TaskItem
->hWnd
, TaskItem
->Index
);
1069 TaskSwitchWnd_CheckActivateTaskItem(This
,
1076 TaskSwitchWnd_ActivateTask(IN OUT PTASK_SWITCH_WND This
,
1079 PTASK_ITEM TaskItem
;
1081 TaskItem
= TaskSwitchWnd_FindTaskItem(This
,
1083 if (TaskItem
== NULL
)
1085 TaskItem
= TaskSwitchWnd_FindOtherTaskItem(This
,
1089 if (TaskItem
== NULL
)
1091 DbgPrint("Activate window 0x%p, could not find task\n", hWnd
);
1094 return TaskSwitchWnd_ActivateTaskItem(This
,
1099 TaskSwitchWnd_DeleteTask(IN OUT PTASK_SWITCH_WND This
,
1102 PTASK_ITEM TaskItem
;
1104 TaskItem
= TaskSwitchWnd_FindTaskItem(This
,
1106 if (TaskItem
!= NULL
)
1108 DbgPrint("Delete window 0x%p on button %d\n", hWnd
, TaskItem
->Index
);
1109 TaskSwitchWnd_DeleteTaskItem(This
,
1114 //DbgPrint("Failed to delete window 0x%p\n", hWnd);
1120 TaskSwitchWnd_DeleteAllTasks(IN OUT PTASK_SWITCH_WND This
)
1122 PTASK_ITEM CurrentTask
;
1124 if (This
->TaskItemCount
> 0)
1126 CurrentTask
= This
->TaskItems
+ This
->TaskItemCount
;
1129 TaskSwitchWnd_DeleteTaskItem(This
,
1131 } while (CurrentTask
!= This
->TaskItems
);
1136 TaskSwitchWnd_FlashTaskItem(IN OUT PTASK_SWITCH_WND This
,
1137 IN OUT PTASK_ITEM TaskItem
)
1139 TaskItem
->RenderFlashed
= 1;
1140 TaskSwitchWnd_UpdateTaskItemButton(This
,
1145 TaskSwitchWnd_FlashTask(IN OUT PTASK_SWITCH_WND This
,
1148 PTASK_ITEM TaskItem
;
1150 TaskItem
= TaskSwitchWnd_FindTaskItem(This
,
1152 if (TaskItem
!= NULL
)
1154 DbgPrint("Flashing window 0x%p on button %d\n", hWnd
, TaskItem
->Index
);
1155 TaskSwitchWnd_FlashTaskItem(This
,
1164 TaskSwitchWnd_RedrawTaskItem(IN OUT PTASK_SWITCH_WND This
,
1165 IN OUT PTASK_ITEM TaskItem
)
1167 PTASK_GROUP TaskGroup
;
1169 TaskGroup
= TaskItem
->Group
;
1170 if (This
->IsGroupingEnabled
&& TaskGroup
!= NULL
)
1172 if (TaskGroup
->IsCollapsed
&& TaskGroup
->Index
>= 0)
1174 TaskSwitchWnd_UpdateTaskGroupButton(This
,
1177 else if (TaskItem
->Index
>= 0)
1179 goto UpdateTaskItem
;
1182 else if (TaskItem
->Index
>= 0)
1185 TaskItem
->RenderFlashed
= 0;
1186 TaskSwitchWnd_UpdateTaskItemButton(This
,
1193 TaskSwitchWnd_RedrawTask(IN OUT PTASK_SWITCH_WND This
,
1196 PTASK_ITEM TaskItem
;
1198 TaskItem
= TaskSwitchWnd_FindTaskItem(This
,
1200 if (TaskItem
!= NULL
)
1202 TaskSwitchWnd_RedrawTaskItem(This
,
1211 TaskSwitchWnd_UpdateTbButtonSpacing(IN OUT PTASK_SWITCH_WND This
,
1212 IN BOOL bHorizontal
,
1214 IN UINT uiBtnsPerLine
)
1218 tbm
.cbSize
= sizeof(tbm
);
1219 tbm
.dwMask
= TBMF_BARPAD
| TBMF_BUTTONSPACING
;
1221 tbm
.cxBarPad
= tbm
.cyBarPad
= 0;
1223 if (bHorizontal
|| uiBtnsPerLine
> 1)
1224 tbm
.cxButtonSpacing
= (3 * GetSystemMetrics(SM_CXEDGE
) / 2);
1226 tbm
.cxButtonSpacing
= 0;
1228 if (!bHorizontal
|| uiRows
> 1)
1229 tbm
.cyButtonSpacing
= (3 * GetSystemMetrics(SM_CYEDGE
) / 2);
1231 tbm
.cyButtonSpacing
= 0;
1233 SendMessage(This
->hWndToolbar
,
1238 return tbm
.cxButtonSpacing
;
1243 TaskSwitchWnd_UpdateButtonsSize(IN OUT PTASK_SWITCH_WND This
,
1244 IN BOOL bRedrawDisabled
)
1247 UINT uiRows
, uiMax
, uiMin
, uiBtnsPerLine
, ui
;
1253 if (GetClientRect(This
->hWnd
,
1255 !IsRectEmpty(&rcClient
))
1257 if (This
->ToolbarBtnCount
> 0)
1259 ZeroMemory (&tbm
, sizeof (tbm
));
1260 tbm
.cbSize
= sizeof(tbm
);
1261 tbm
.dwMask
= TBMF_BUTTONSPACING
;
1262 SendMessage(This
->hWndToolbar
,
1267 uiRows
= (rcClient
.bottom
+ tbm
.cyButtonSpacing
) / (This
->ButtonSize
.cy
+ tbm
.cyButtonSpacing
);
1271 uiBtnsPerLine
= (This
->ToolbarBtnCount
+ uiRows
- 1) / uiRows
;
1273 Horizontal
= ITrayWindow_IsHorizontal(This
->Tray
);
1275 if (!bRedrawDisabled
)
1276 TaskSwitchWnd_BeginUpdate(This
);
1278 /* We might need to update the button spacing */
1279 tbm
.cxButtonSpacing
= TaskSwitchWnd_UpdateTbButtonSpacing(This
,
1284 /* Calculate the ideal width and make sure it's within the allowed range */
1285 NewBtnSize
= (rcClient
.right
- (uiBtnsPerLine
* tbm
.cxButtonSpacing
)) / uiBtnsPerLine
;
1287 /* Determine the minimum and maximum width of a button */
1289 uiMax
= GetSystemMetrics(SM_CXMINIMIZED
);
1291 uiMax
= rcClient
.right
;
1293 uiMin
= GetSystemMetrics(SM_CXSIZE
) + (2 * GetSystemMetrics(SM_CXEDGE
));
1295 if (NewBtnSize
< (LONG
)uiMin
)
1297 if (NewBtnSize
> (LONG
)uiMax
)
1300 This
->ButtonSize
.cx
= NewBtnSize
;
1302 /* Recalculate how many buttons actually fit into one line */
1303 uiBtnsPerLine
= rcClient
.right
/ (NewBtnSize
+ tbm
.cxButtonSpacing
);
1304 if (uiBtnsPerLine
== 0)
1306 This
->TbButtonsPerLine
= uiBtnsPerLine
;
1308 tbbi
.cbSize
= sizeof(tbbi
);
1309 tbbi
.dwMask
= TBIF_BYINDEX
| TBIF_SIZE
| TBIF_STATE
;
1310 tbbi
.cx
= (INT
)NewBtnSize
;
1312 for (ui
= 0; ui
!= This
->ToolbarBtnCount
; ui
++)
1314 tbbi
.fsState
= TBSTATE_ENABLED
;
1316 /* Check if we're updating a button that is the last one in the
1317 line. If so, we need to set the TBSTATE_WRAP flag! */
1318 if ((ui
+ 1) % uiBtnsPerLine
== 0)
1319 tbbi
.fsState
|= TBSTATE_WRAP
;
1321 if (This
->ActiveTaskItem
!= NULL
&&
1322 This
->ActiveTaskItem
->Index
== ui
)
1324 tbbi
.fsState
|= TBSTATE_CHECKED
;
1327 SendMessage(This
->hWndToolbar
,
1334 /* FIXME: Force the window to the correct position in case some idiot
1335 did something to us */
1336 SetWindowPos(This
->hWndToolbar
,
1340 rcClient
.right
, /* FIXME */
1341 rcClient
.bottom
, /* FIXME */
1342 SWP_NOACTIVATE
| SWP_NOZORDER
);
1347 This
->TbButtonsPerLine
= 0;
1348 This
->ButtonSize
.cx
= 0;
1352 TaskSwitchWnd_EndUpdate(This
);
1355 static BOOL CALLBACK
1356 TaskSwitchWnd_EnumWindowsProc(IN HWND hWnd
,
1359 PTASK_SWITCH_WND This
= (PTASK_SWITCH_WND
)lParam
;
1361 /* Only show windows that still exist and are visible and none of explorer's
1362 special windows (such as the desktop or the tray window) */
1363 if (IsWindow(hWnd
) && IsWindowVisible(hWnd
) &&
1364 !ITrayWindow_IsSpecialHWND(This
->Tray
,
1367 /* Don't list popup windows and also no tool windows */
1369 GW_OWNER
) == NULL
&&
1370 !(GetWindowLong(hWnd
,
1371 GWL_EXSTYLE
) & WS_EX_TOOLWINDOW
))
1373 TaskSwitchWnd_AddTask(This
,
1381 static LRESULT CALLBACK
1382 TaskSwichWnd_ToolbarSubclassedProc(IN HWND hWnd
,
1386 IN UINT_PTR uIdSubclass
,
1387 IN DWORD_PTR dwRefData
)
1391 Ret
= DefSubclassProc(hWnd
,
1396 if (msg
== WM_NCHITTEST
&& Ret
== HTCLIENT
)
1400 /* See if the mouse is on a button */
1401 pt
.x
= (SHORT
)LOWORD(lParam
);
1402 pt
.y
= (SHORT
)HIWORD(lParam
);
1404 if (MapWindowPoints(HWND_DESKTOP
,
1408 (INT
)SendMessage(hWnd
,
1413 /* Make the control appear to be transparent outside of any buttons */
1414 Ret
= HTTRANSPARENT
;
1422 TaskSwitchWnd_UpdateTheme(IN OUT PTASK_SWITCH_WND This
)
1424 if (This
->TaskBandTheme
)
1425 CloseThemeData(This
->TaskBandTheme
);
1427 if (IsThemeActive())
1428 This
->TaskBandTheme
= OpenThemeData(This
->hWnd
, L
"TaskBand");
1430 This
->TaskBandTheme
= NULL
;
1434 TaskSwitchWnd_Create(IN OUT PTASK_SWITCH_WND This
)
1436 This
->hWndToolbar
= CreateWindowEx(0,
1439 WS_CHILD
| WS_VISIBLE
| WS_CLIPCHILDREN
|
1440 TBSTYLE_TOOLTIPS
| TBSTYLE_WRAPABLE
| TBSTYLE_LIST
|
1441 TBSTYLE_TRANSPARENT
|
1442 CCS_TOP
| CCS_NORESIZE
| CCS_NODIVIDER
,
1452 if (This
->hWndToolbar
!= NULL
)
1457 SetWindowTheme(This
->hWndToolbar
, L
"TaskBand", NULL
);
1458 TaskSwitchWnd_UpdateTheme(This
);
1459 /* Identify the version we're using */
1460 SendMessage(This
->hWndToolbar
,
1461 TB_BUTTONSTRUCTSIZE
,
1465 This
->TaskIcons
= ImageList_Create(16, 16, ILC_COLOR32
| ILC_MASK
, 0, 1000);
1466 SendMessage(This
->hWndToolbar
, TB_SETIMAGELIST
, 0, (LPARAM
)This
->TaskIcons
);
1468 /* Calculate the default button size. Don't save this in This->ButtonSize.cx so that
1469 the actual button width gets updated correctly on the first recalculation */
1470 BtnSize
.cx
= GetSystemMetrics(SM_CXMINIMIZED
);
1471 This
->ButtonSize
.cy
= BtnSize
.cy
= GetSystemMetrics(SM_CYSIZE
) + (2 * GetSystemMetrics(SM_CYEDGE
));
1472 SendMessage(This
->hWndToolbar
,
1475 (LPARAM
)MAKELONG(BtnSize
.cx
,
1478 /* We don't want to see partially clipped buttons...not that we could see them... */
1480 SendMessage(This
->hWndToolbar
,
1481 TB_SETEXTENDEDSTYLE
,
1483 TBSTYLE_EX_HIDECLIPPEDBUTTONS
);
1486 /* Set proper spacing between buttons */
1487 TaskSwitchWnd_UpdateTbButtonSpacing(This
,
1488 ITrayWindow_IsHorizontal(This
->Tray
),
1492 /* Register the shell hook */
1493 This
->ShellHookMsg
= RegisterWindowMessage(TEXT("SHELLHOOK"));
1494 hShell32
= GetModuleHandle(TEXT("SHELL32.DLL"));
1495 if (hShell32
!= NULL
)
1497 REGSHELLHOOK RegShellHook
;
1499 /* RegisterShellHook */
1500 RegShellHook
= (REGSHELLHOOK
)GetProcAddress(hShell32
,
1501 (LPCSTR
)((LONG
)181));
1502 if (RegShellHook
!= NULL
)
1504 RegShellHook(This
->hWnd
,
1505 3); /* 1 if no NT! We're targeting NT so we don't care! */
1509 /* Add all windows to the toolbar */
1510 EnumWindows(TaskSwitchWnd_EnumWindowsProc
,
1513 /* Recalculate the button size */
1514 TaskSwitchWnd_UpdateButtonsSize(This
,
1517 /* Subclass the toolbar control because it doesn't provide a
1518 NM_NCHITTEST notification */
1519 This
->IsToolbarSubclassed
= SetWindowSubclass(This
->hWndToolbar
,
1520 TaskSwichWnd_ToolbarSubclassedProc
,
1521 TSW_TOOLBAR_SUBCLASS_ID
,
1527 TaskSwitchWnd_NCDestroy(IN OUT PTASK_SWITCH_WND This
)
1531 This
->IsDestroying
= TRUE
;
1533 /* Unregister the shell hook */
1534 hShell32
= GetModuleHandle(TEXT("SHELL32.DLL"));
1535 if (hShell32
!= NULL
)
1537 REGSHELLHOOK RegShellHook
;
1539 /* RegisterShellHook */
1540 RegShellHook
= (REGSHELLHOOK
)GetProcAddress(hShell32
,
1541 (LPCSTR
)((LONG
)181));
1542 if (RegShellHook
!= NULL
)
1544 RegShellHook(This
->hWnd
,
1549 CloseThemeData(This
->TaskBandTheme
);
1550 TaskSwitchWnd_DeleteAllTasks(This
);
1554 TaskSwitchWnd_HandleAppCommand(IN OUT PTASK_SWITCH_WND This
,
1560 switch (GET_APPCOMMAND_LPARAM(lParam
))
1562 case APPCOMMAND_BROWSER_SEARCH
:
1563 Ret
= SHFindFiles(NULL
,
1567 case APPCOMMAND_BROWSER_HOME
:
1568 case APPCOMMAND_LAUNCH_MAIL
:
1570 DbgPrint("Shell app command %d unhandled!\n", (INT
)GET_APPCOMMAND_LPARAM(lParam
));
1579 TaskSwitchWnd_HandleShellHookMsg(IN OUT PTASK_SWITCH_WND This
,
1585 switch ((INT
)wParam
)
1587 case HSHELL_APPCOMMAND
:
1588 TaskSwitchWnd_HandleAppCommand(This
,
1594 case HSHELL_WINDOWCREATED
:
1595 TaskSwitchWnd_AddTask(This
,
1600 case HSHELL_WINDOWDESTROYED
:
1601 /* The window still exists! Delay destroying it a bit */
1602 TaskSwitchWnd_DeleteTask(This
,
1607 case HSHELL_ACTIVATESHELLWINDOW
:
1608 goto UnhandledShellMessage
;
1610 case HSHELL_RUDEAPPACTIVATED
:
1611 goto UnhandledShellMessage
;
1613 case HSHELL_WINDOWACTIVATED
:
1614 TaskSwitchWnd_ActivateTask(This
,
1619 case HSHELL_GETMINRECT
:
1620 goto UnhandledShellMessage
;
1623 TaskSwitchWnd_FlashTask(This
,
1629 TaskSwitchWnd_RedrawTask(This
,
1634 case HSHELL_TASKMAN
:
1635 case HSHELL_LANGUAGE
:
1636 case HSHELL_SYSMENU
:
1637 case HSHELL_ENDTASK
:
1638 case HSHELL_ACCESSIBILITYSTATE
:
1639 case HSHELL_WINDOWREPLACED
:
1640 case HSHELL_WINDOWREPLACING
:
1643 static const struct {
1647 {HSHELL_WINDOWCREATED
, L
"HSHELL_WINDOWCREATED"},
1648 {HSHELL_WINDOWDESTROYED
, L
"HSHELL_WINDOWDESTROYED"},
1649 {HSHELL_ACTIVATESHELLWINDOW
, L
"HSHELL_ACTIVATESHELLWINDOW"},
1650 {HSHELL_WINDOWACTIVATED
, L
"HSHELL_WINDOWACTIVATED"},
1651 {HSHELL_GETMINRECT
, L
"HSHELL_GETMINRECT"},
1652 {HSHELL_REDRAW
, L
"HSHELL_REDRAW"},
1653 {HSHELL_TASKMAN
, L
"HSHELL_TASKMAN"},
1654 {HSHELL_LANGUAGE
, L
"HSHELL_LANGUAGE"},
1655 {HSHELL_SYSMENU
, L
"HSHELL_SYSMENU"},
1656 {HSHELL_ENDTASK
, L
"HSHELL_ENDTASK"},
1657 {HSHELL_ACCESSIBILITYSTATE
, L
"HSHELL_ACCESSIBILITYSTATE"},
1658 {HSHELL_APPCOMMAND
, L
"HSHELL_APPCOMMAND"},
1659 {HSHELL_WINDOWREPLACED
, L
"HSHELL_WINDOWREPLACED"},
1660 {HSHELL_WINDOWREPLACING
, L
"HSHELL_WINDOWREPLACING"},
1661 {HSHELL_RUDEAPPACTIVATED
, L
"HSHELL_RUDEAPPACTIVATED"},
1664 UnhandledShellMessage
:
1665 for (i
= 0, found
= 0; i
!= sizeof(hshell_msg
) / sizeof(hshell_msg
[0]); i
++)
1667 if (hshell_msg
[i
].msg
== (INT
)wParam
)
1669 DbgPrint("Shell message %ws unhandled (lParam = 0x%p)!\n", hshell_msg
[i
].msg_name
, lParam
);
1676 DbgPrint("Shell message %d unhandled (lParam = 0x%p)!\n", (INT
)wParam
, lParam
);
1686 TaskSwitchWnd_EnableGrouping(IN OUT PTASK_SWITCH_WND This
,
1689 This
->IsGroupingEnabled
= bEnable
;
1691 /* Collapse or expand groups if neccessary */
1692 TaskSwitchWnd_UpdateButtonsSize(This
,
1697 TaskSwitchWnd_HandleTaskItemClick(IN OUT PTASK_SWITCH_WND This
,
1698 IN OUT PTASK_ITEM TaskItem
)
1703 if (IsWindow(TaskItem
->hWnd
))
1705 bIsMinimized
= IsIconic(TaskItem
->hWnd
);
1706 bIsActive
= (TaskItem
== This
->ActiveTaskItem
);
1708 if (!bIsMinimized
&& bIsActive
)
1710 PostMessage(TaskItem
->hWnd
,
1719 PostMessage(TaskItem
->hWnd
,
1725 SetForegroundWindow(TaskItem
->hWnd
);
1731 TaskSwitchWnd_HandleTaskGroupClick(IN OUT PTASK_SWITCH_WND This
,
1732 IN OUT PTASK_GROUP TaskGroup
)
1734 /* TODO: Show task group menu */
1738 TaskSwitchWnd_HandleButtonClick(IN OUT PTASK_SWITCH_WND This
,
1741 PTASK_ITEM TaskItem
;
1742 PTASK_GROUP TaskGroup
;
1744 if (This
->IsGroupingEnabled
)
1746 TaskGroup
= FindTaskGroupByIndex(This
,
1748 if (TaskGroup
!= NULL
&& TaskGroup
->IsCollapsed
)
1750 TaskSwitchWnd_HandleTaskGroupClick(This
,
1756 TaskItem
= FindTaskItemByIndex(This
,
1758 if (TaskItem
!= NULL
)
1760 TaskSwitchWnd_HandleTaskItemClick(This
,
1770 TaskSwitchWnd_HandleTaskItemRightClick(IN OUT PTASK_SWITCH_WND This
,
1771 IN OUT PTASK_ITEM TaskItem
)
1774 HMENU hmenu
= GetSystemMenu(TaskItem
->hWnd
, FALSE
);
1780 cmd
= TrackPopupMenu(hmenu
, TPM_LEFTBUTTON
|TPM_RIGHTBUTTON
|TPM_RETURNCMD
, pt
.x
, pt
.y
, 0, This
->hWndToolbar
, NULL
);
1782 SetForegroundWindow(TaskItem
->hWnd
); // reactivate window after the context menu has closed
1783 PostMessage(TaskItem
->hWnd
, WM_SYSCOMMAND
, cmd
, 0);
1789 TaskSwitchWnd_HandleTaskGroupRightClick(IN OUT PTASK_SWITCH_WND This
,
1790 IN OUT PTASK_GROUP TaskGroup
)
1792 /* TODO: Show task group right click menu */
1796 TaskSwitchWnd_HandleButtonRightClick(IN OUT PTASK_SWITCH_WND This
,
1799 PTASK_ITEM TaskItem
;
1800 PTASK_GROUP TaskGroup
;
1801 if (This
->IsGroupingEnabled
)
1803 TaskGroup
= FindTaskGroupByIndex(This
,
1805 if (TaskGroup
!= NULL
&& TaskGroup
->IsCollapsed
)
1807 TaskSwitchWnd_HandleTaskGroupRightClick(This
,
1813 TaskItem
= FindTaskItemByIndex(This
,
1816 if (TaskItem
!= NULL
)
1818 TaskSwitchWnd_HandleTaskItemRightClick(This
,
1828 TaskSwichWnd_HandleItemPaint(IN OUT PTASK_SWITCH_WND This
,
1829 IN OUT NMTBCUSTOMDRAW
*nmtbcd
)
1831 LRESULT Ret
= CDRF_DODEFAULT
;
1832 PTASK_GROUP TaskGroup
;
1833 PTASK_ITEM TaskItem
;
1835 TaskItem
= FindTaskItemByIndex(This
,
1836 (INT
)nmtbcd
->nmcd
.dwItemSpec
);
1837 TaskGroup
= FindTaskGroupByIndex(This
,
1838 (INT
)nmtbcd
->nmcd
.dwItemSpec
);
1839 if (TaskGroup
== NULL
&& TaskItem
!= NULL
)
1841 ASSERT(TaskItem
!= NULL
);
1843 if (TaskItem
!= NULL
&& IsWindow(TaskItem
->hWnd
))
1845 /* Make the entire button flashing if neccessary */
1846 if (nmtbcd
->nmcd
.uItemState
& CDIS_MARKED
)
1848 Ret
= TBCDRF_NOBACKGROUND
;
1849 if (!This
->TaskBandTheme
)
1851 SelectObject(nmtbcd
->nmcd
.hdc
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1852 Rectangle(nmtbcd
->nmcd
.hdc
,
1853 nmtbcd
->nmcd
.rc
.left
,
1854 nmtbcd
->nmcd
.rc
.top
,
1855 nmtbcd
->nmcd
.rc
.right
,
1856 nmtbcd
->nmcd
.rc
.bottom
);
1860 DrawThemeBackground(This
->TaskBandTheme
, nmtbcd
->nmcd
.hdc
, TDP_FLASHBUTTON
, 0, &nmtbcd
->nmcd
.rc
, 0);
1862 nmtbcd
->clrText
= GetSysColor(COLOR_HIGHLIGHTTEXT
);
1867 else if (TaskGroup
!= NULL
)
1869 /* FIXME: Implement painting for task groups */
1875 TaskSwitchWnd_HandleToolbarNotification(IN OUT PTASK_SWITCH_WND This
,
1876 IN
const NMHDR
*nmh
)
1884 LPNMTBCUSTOMDRAW nmtbcd
= (LPNMTBCUSTOMDRAW
)nmh
;
1886 switch (nmtbcd
->nmcd
.dwDrawStage
)
1889 case CDDS_ITEMPREPAINT
:
1890 Ret
= TaskSwichWnd_HandleItemPaint(This
,
1895 Ret
= CDRF_NOTIFYITEMDRAW
;
1899 Ret
= CDRF_DODEFAULT
;
1910 TaskSwitchWnd_DrawBackground(HWND hwnd
,
1915 GetClientRect(hwnd
, &rect
);
1916 DrawThemeParentBackground(hwnd
, hdc
, &rect
);
1919 static LRESULT CALLBACK
1920 TaskSwitchWndProc(IN HWND hwnd
,
1925 PTASK_SWITCH_WND This
= NULL
;
1926 LRESULT Ret
= FALSE
;
1928 if (uMsg
!= WM_NCCREATE
)
1930 This
= (PTASK_SWITCH_WND
)GetWindowLongPtr(hwnd
,
1934 if (This
!= NULL
|| uMsg
== WM_NCCREATE
)
1938 case WM_THEMECHANGED
:
1939 TaskSwitchWnd_UpdateTheme(This
);
1942 TaskSwitchWnd_DrawBackground(hwnd
, (HDC
)wParam
);
1948 szClient
.cx
= LOWORD(lParam
);
1949 szClient
.cy
= HIWORD(lParam
);
1950 if (This
->hWndToolbar
!= NULL
)
1952 SetWindowPos(This
->hWndToolbar
,
1960 TaskSwitchWnd_UpdateButtonsSize(This
,
1968 /* We want the tray window to be draggable everywhere, so make the control
1969 appear transparent */
1970 Ret
= DefWindowProc(hwnd
,
1974 if (Ret
!= HTVSCROLL
&& Ret
!= HTHSCROLL
)
1975 Ret
= HTTRANSPARENT
;
1981 if (lParam
!= 0 && (HWND
)lParam
== This
->hWndToolbar
)
1983 TaskSwitchWnd_HandleButtonClick(This
,
1991 const NMHDR
*nmh
= (const NMHDR
*)lParam
;
1993 if (nmh
->hwndFrom
== This
->hWndToolbar
)
1995 Ret
= TaskSwitchWnd_HandleToolbarNotification(This
,
2001 case TSWM_ENABLEGROUPING
:
2003 Ret
= This
->IsGroupingEnabled
;
2004 if (wParam
!= This
->IsGroupingEnabled
)
2006 TaskSwitchWnd_EnableGrouping(This
,
2012 case TSWM_UPDATETASKBARPOS
:
2014 /* Update the button spacing */
2015 TaskSwitchWnd_UpdateTbButtonSpacing(This
,
2016 ITrayWindow_IsHorizontal(This
->Tray
),
2022 case WM_CONTEXTMENU
:
2024 if (This
->hWndToolbar
!= NULL
)
2029 pt
.x
= (LONG
)LOWORD(lParam
);
2030 pt
.y
= (LONG
)HIWORD(lParam
);
2032 MapWindowPoints(NULL
,
2037 iBtn
= (INT_PTR
)SendMessage(This
->hWndToolbar
,
2043 TaskSwitchWnd_HandleButtonRightClick(This
,
2047 goto ForwardContextMenuMsg
;
2051 ForwardContextMenuMsg
:
2052 /* Forward message */
2053 Ret
= SendMessage(ITrayWindow_GetHWND(This
->Tray
),
2063 LPCREATESTRUCT CreateStruct
= (LPCREATESTRUCT
)lParam
;
2064 This
= HeapAlloc(hProcessHeap
,
2071 This
->hWndNotify
= CreateStruct
->hwndParent
;
2072 This
->Tray
= (ITrayWindow
*)CreateStruct
->lpCreateParams
;
2073 This
->IsGroupingEnabled
= TRUE
; /* FIXME */
2074 SetWindowLongPtr(hwnd
,
2082 TaskSwitchWnd_Create(This
);
2094 if (This
->IsToolbarSubclassed
)
2096 if (RemoveWindowSubclass(This
->hWndToolbar
,
2097 TaskSwichWnd_ToolbarSubclassedProc
,
2098 TSW_TOOLBAR_SUBCLASS_ID
))
2100 This
->IsToolbarSubclassed
= FALSE
;
2106 TaskSwitchWnd_NCDestroy(This
);
2107 HeapFree(hProcessHeap
,
2110 SetWindowLongPtr(hwnd
,
2120 TaskSwitchWnd_DumpTasks(This
);
2127 /* HandleDefaultMessage: */
2128 if (uMsg
== This
->ShellHookMsg
&& This
->ShellHookMsg
!= 0)
2130 /* Process shell messages */
2131 Ret
= (LRESULT
)TaskSwitchWnd_HandleShellHookMsg(This
,
2137 Ret
= DefWindowProc(hwnd
,
2146 Ret
= DefWindowProc(hwnd
,
2157 CreateTaskSwitchWnd(IN HWND hWndParent
,
2158 IN OUT ITrayWindow
*Tray
)
2162 hwndTaskBar
= CreateWindowEx(0,
2163 szTaskSwitchWndClass
,
2165 WS_CHILD
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
| WS_TABSTOP
,
2179 RegisterTaskSwitchWndClass(VOID
)
2183 wc
.style
= CS_DBLCLKS
;
2184 wc
.lpfnWndProc
= TaskSwitchWndProc
;
2186 wc
.cbWndExtra
= sizeof(PTASK_SWITCH_WND
);
2187 wc
.hInstance
= hExplorerInstance
;
2189 wc
.hCursor
= LoadCursor(NULL
,
2191 wc
.hbrBackground
= (HBRUSH
)(COLOR_3DFACE
+ 1);
2192 wc
.lpszMenuName
= NULL
;
2193 wc
.lpszClassName
= szTaskSwitchWndClass
;
2195 return RegisterClass(&wc
) != 0;
2199 UnregisterTaskSwitchWndClass(VOID
)
2201 UnregisterClass(szTaskSwitchWndClass
,