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 /* By default we don't use DrawCaptionTemp() because it causes some minimal
24 drawing glitches with the toolbar custom painting code */
25 #define TASK_USE_DRAWCAPTIONTEMP 1
27 /* Set DUMP_TASKS to 1 to enable a dump of the tasks and task groups every
31 static const TCHAR szTaskSwitchWndClass
[] = TEXT("MSTaskSwWClass");
32 static const TCHAR szRunningApps
[] = TEXT("Running Applications");
34 typedef struct _TASK_GROUP
36 /* We have to use a linked list instead of an array so we don't have to
37 update all pointers to groups in the task item array when removing
39 struct _TASK_GROUP
*Next
;
50 #if TASK_USE_DRAWCAPTIONTEMP != 0
52 /* DisplayTooltip is TRUE when the group button text didn't fit into
54 DWORD DisplayTooltip
: 1;
58 DWORD IsCollapsed
: 1;
61 } TASK_GROUP
, *PTASK_GROUP
;
63 typedef struct _TASK_ITEM
69 #if !(TASK_USE_DRAWCAPTIONTEMP != 0)
81 #if TASK_USE_DRAWCAPTIONTEMP != 0
83 /* DisplayTooltip is TRUE when the window text didn't fit into the
85 DWORD DisplayTooltip
: 1;
89 /* IsFlashing is TRUE when the task bar item should be flashing. */
92 /* RenderFlashed is only TRUE if the task bar item should be
93 drawn with a flash. */
94 DWORD RenderFlashed
: 1;
97 } TASK_ITEM
, *PTASK_ITEM
;
99 #define TASK_ITEM_ARRAY_ALLOC 64
101 typedef struct _TASK_SWITCH_WND
109 PTASK_GROUP TaskGroups
;
112 WORD AllocatedTaskItems
;
113 PTASK_ITEM TaskItems
;
114 PTASK_ITEM ActiveTaskItem
;
117 UINT TbButtonsPerLine
;
118 WORD ToolbarBtnCount
;
125 DWORD IsGroupingEnabled
: 1;
126 DWORD IsDestroying
: 1;
127 DWORD IsToolbarSubclassed
: 1;
133 } TASK_SWITCH_WND
, *PTASK_SWITCH_WND
;
135 #define TSW_TOOLBAR_SUBCLASS_ID 1
137 #define MAX_TASKS_COUNT (0x7FFF)
139 static VOID
TaskSwitchWnd_UpdateButtonsSize(IN OUT PTASK_SWITCH_WND This
,
140 IN BOOL bRedrawDisabled
);
142 #if TASK_USE_DRAWCAPTIONTEMP != 0
144 #define TaskSwitchWnd_GetWndTextFromTaskItem(a,b) NULL
146 #else /* !TASK_USE_DRAWCAPTIONTEMP */
149 TaskSwitchWnd_GetWndTextFromTaskItem(IN OUT PTASK_SWITCH_WND This
,
150 IN PTASK_ITEM TaskItem
)
152 /* Get the window text without sending a message so we don't hang if an
153 application isn't responding! */
154 if (InternalGetWindowText(TaskItem
->hWnd
,
156 sizeof(This
->szBuf
) / sizeof(This
->szBuf
[0])) > 0)
168 TaskSwitchWnd_DumpTasks(IN OUT PTASK_SWITCH_WND This
)
170 PTASK_GROUP CurrentGroup
;
171 PTASK_ITEM CurrentTaskItem
, LastTaskItem
;
173 DbgPrint("Tasks dump:\n");
174 if (This
->IsGroupingEnabled
)
176 CurrentGroup
= This
->TaskGroups
;
177 while (CurrentGroup
!= NULL
)
179 DbgPrint("- Group PID: 0x%p Tasks: %d Index: %d\n", CurrentGroup
->dwProcessId
, CurrentGroup
->dwTaskCount
, CurrentGroup
->Index
);
181 CurrentTaskItem
= This
->TaskItems
;
182 LastTaskItem
= CurrentTaskItem
+ This
->TaskItemCount
;
183 while (CurrentTaskItem
!= LastTaskItem
)
185 if (CurrentTaskItem
->Group
== CurrentGroup
)
187 DbgPrint(" + Task hwnd: 0x%p Index: %d\n", CurrentTaskItem
->hWnd
, CurrentTaskItem
->Index
);
192 CurrentGroup
= CurrentGroup
->Next
;
195 CurrentTaskItem
= This
->TaskItems
;
196 LastTaskItem
= CurrentTaskItem
+ This
->TaskItemCount
;
197 while (CurrentTaskItem
!= LastTaskItem
)
199 if (CurrentTaskItem
->Group
== NULL
)
201 DbgPrint("- Task hwnd: 0x%p Index: %d\n", CurrentTaskItem
->hWnd
, CurrentTaskItem
->Index
);
208 CurrentTaskItem
= This
->TaskItems
;
209 LastTaskItem
= CurrentTaskItem
+ This
->TaskItemCount
;
210 while (CurrentTaskItem
!= LastTaskItem
)
212 DbgPrint("- Task hwnd: 0x%p Index: %d\n", CurrentTaskItem
->hWnd
, CurrentTaskItem
->Index
);
220 TaskSwitchWnd_BeginUpdate(IN OUT PTASK_SWITCH_WND This
)
222 SendMessage(This
->hWndToolbar
,
229 TaskSwitchWnd_EndUpdate(IN OUT PTASK_SWITCH_WND This
)
231 SendMessage(This
->hWndToolbar
,
235 InvalidateRect(This
->hWndToolbar
,
241 TaskSwitchWnd_SetToolbarButtonCommandId(IN OUT PTASK_SWITCH_WND This
,
247 tbbi
.cbSize
= sizeof(tbbi
);
248 tbbi
.dwMask
= TBIF_BYINDEX
| TBIF_COMMAND
;
249 tbbi
.idCommand
= iCommandId
;
251 return SendMessage(This
->hWndToolbar
,
253 (WPARAM
)iButtonIndex
,
258 TaskSwitchWnd_UpdateIndexesAfterButtonInserted(IN OUT PTASK_SWITCH_WND This
,
261 PTASK_GROUP CurrentGroup
;
262 PTASK_ITEM CurrentTaskItem
, LastTaskItem
;
265 if (This
->IsGroupingEnabled
)
267 /* Update all affected groups */
268 CurrentGroup
= This
->TaskGroups
;
269 while (CurrentGroup
!= NULL
)
271 if (CurrentGroup
->IsCollapsed
&&
272 CurrentGroup
->Index
>= iIndex
)
274 /* Update the toolbar buttons */
275 NewIndex
= CurrentGroup
->Index
+ 1;
276 if (TaskSwitchWnd_SetToolbarButtonCommandId(This
,
277 CurrentGroup
->Index
+ 1,
280 CurrentGroup
->Index
= NewIndex
;
283 CurrentGroup
->Index
= -1;
286 CurrentGroup
= CurrentGroup
->Next
;
290 /* Update all affected task items */
291 CurrentTaskItem
= This
->TaskItems
;
292 LastTaskItem
= CurrentTaskItem
+ This
->TaskItemCount
;
293 while (CurrentTaskItem
!= LastTaskItem
)
295 CurrentGroup
= CurrentTaskItem
->Group
;
296 if (CurrentGroup
!= NULL
)
298 if (!CurrentGroup
->IsCollapsed
&&
299 CurrentTaskItem
->Index
>= iIndex
)
301 goto UpdateTaskItemBtn
;
304 else if (CurrentTaskItem
->Index
>= iIndex
)
307 /* Update the toolbar buttons */
308 NewIndex
= CurrentTaskItem
->Index
+ 1;
309 if (TaskSwitchWnd_SetToolbarButtonCommandId(This
,
310 CurrentTaskItem
->Index
+ 1,
313 CurrentTaskItem
->Index
= NewIndex
;
316 CurrentTaskItem
->Index
= -1;
324 TaskSwitchWnd_UpdateIndexesAfterButtonDeleted(IN OUT PTASK_SWITCH_WND This
,
327 PTASK_GROUP CurrentGroup
;
328 PTASK_ITEM CurrentTaskItem
, LastTaskItem
;
331 if (This
->IsGroupingEnabled
)
333 /* Update all affected groups */
334 CurrentGroup
= This
->TaskGroups
;
335 while (CurrentGroup
!= NULL
)
337 if (CurrentGroup
->IsCollapsed
&&
338 CurrentGroup
->Index
> iIndex
)
340 /* Update the toolbar buttons */
341 NewIndex
= CurrentGroup
->Index
- 1;
342 if (TaskSwitchWnd_SetToolbarButtonCommandId(This
,
343 CurrentGroup
->Index
- 1,
346 CurrentGroup
->Index
= NewIndex
;
349 CurrentGroup
->Index
= -1;
352 CurrentGroup
= CurrentGroup
->Next
;
356 /* Update all affected task items */
357 CurrentTaskItem
= This
->TaskItems
;
358 LastTaskItem
= CurrentTaskItem
+ This
->TaskItemCount
;
359 while (CurrentTaskItem
!= LastTaskItem
)
361 CurrentGroup
= CurrentTaskItem
->Group
;
362 if (CurrentGroup
!= NULL
)
364 if (!CurrentGroup
->IsCollapsed
&&
365 CurrentTaskItem
->Index
> iIndex
)
367 goto UpdateTaskItemBtn
;
370 else if (CurrentTaskItem
->Index
> iIndex
)
373 /* Update the toolbar buttons */
374 NewIndex
= CurrentTaskItem
->Index
- 1;
375 if (TaskSwitchWnd_SetToolbarButtonCommandId(This
,
376 CurrentTaskItem
->Index
- 1,
379 CurrentTaskItem
->Index
= NewIndex
;
382 CurrentTaskItem
->Index
= -1;
390 TaskSwitchWnd_UpdateTaskGroupButton(IN OUT PTASK_SWITCH_WND This
,
391 IN PTASK_GROUP TaskGroup
)
393 ASSERT(TaskGroup
->Index
>= 0);
395 /* FIXME: Implement */
397 return TaskGroup
->Index
;
401 TaskSwitchWnd_ExpandTaskGroup(IN OUT PTASK_SWITCH_WND This
,
402 IN PTASK_GROUP TaskGroup
)
404 ASSERT(TaskGroup
->dwTaskCount
> 0);
405 ASSERT(TaskGroup
->IsCollapsed
);
406 ASSERT(TaskGroup
->Index
>= 0);
408 /* FIXME: Implement */
412 TaskSwitchWnd_UpdateTaskItemButton(IN OUT PTASK_SWITCH_WND This
,
413 IN PTASK_ITEM TaskItem
)
417 ASSERT(TaskItem
->Index
>= 0);
419 tbbi
.cbSize
= sizeof(tbbi
);
420 tbbi
.dwMask
= TBIF_BYINDEX
| TBIF_STATE
| TBIF_TEXT
;
421 tbbi
.fsState
= TBSTATE_ENABLED
;
422 if (This
->ActiveTaskItem
== TaskItem
)
423 tbbi
.fsState
|= TBSTATE_CHECKED
;
425 /* Check if we're updating a button that is the last one in the
426 line. If so, we need to set the TBSTATE_WRAP flag! */
427 if (This
->TbButtonsPerLine
!= 0 &&
428 (TaskItem
->Index
+ 1) % This
->TbButtonsPerLine
== 0)
430 tbbi
.fsState
|= TBSTATE_WRAP
;
433 tbbi
.pszText
= TaskSwitchWnd_GetWndTextFromTaskItem(This
,
436 if (!SendMessage(This
->hWndToolbar
,
438 (WPARAM
)TaskItem
->Index
,
441 TaskItem
->Index
= -1;
445 DbgPrint("Updated button %d for hwnd 0x%p\n", TaskItem
->Index
, TaskItem
->hWnd
);
446 return TaskItem
->Index
;
450 TaskSwitchWnd_FindLastTaskItemOfGroup(IN OUT PTASK_SWITCH_WND This
,
451 IN PTASK_GROUP TaskGroup OPTIONAL
,
452 IN PTASK_ITEM NewTaskItem OPTIONAL
)
454 PTASK_ITEM TaskItem
, LastTaskItem
, FoundTaskItem
= NULL
;
457 ASSERT(This
->IsGroupingEnabled
);
459 TaskItem
= This
->TaskItems
;
460 LastTaskItem
= TaskItem
+ This
->TaskItemCount
;
462 dwTaskCount
= (TaskGroup
!= NULL
? TaskGroup
->dwTaskCount
: MAX_TASKS_COUNT
);
464 ASSERT(dwTaskCount
> 0);
466 while (TaskItem
!= LastTaskItem
)
468 if (TaskItem
->Group
== TaskGroup
)
470 if ((NewTaskItem
!= NULL
&& TaskItem
!= NewTaskItem
) || NewTaskItem
== NULL
)
472 FoundTaskItem
= TaskItem
;
475 if (--dwTaskCount
== 0)
477 /* We found the last task item in the group! */
485 return FoundTaskItem
;
489 TaskSwitchWnd_CalculateTaskItemNewButtonIndex(IN OUT PTASK_SWITCH_WND This
,
490 IN PTASK_ITEM TaskItem
)
492 PTASK_GROUP TaskGroup
;
493 PTASK_ITEM LastTaskItem
;
495 /* NOTE: This routine assumes that the group is *not* collapsed! */
497 TaskGroup
= TaskItem
->Group
;
498 if (This
->IsGroupingEnabled
)
500 if (TaskGroup
!= NULL
)
502 ASSERT(TaskGroup
->Index
< 0);
503 ASSERT(!TaskGroup
->IsCollapsed
);
505 if (TaskGroup
->dwTaskCount
> 1)
507 LastTaskItem
= TaskSwitchWnd_FindLastTaskItemOfGroup(This
,
510 if (LastTaskItem
!= NULL
)
512 /* Since the group is expanded the task items must have an index */
513 ASSERT(LastTaskItem
->Index
>= 0);
515 return LastTaskItem
->Index
+ 1;
521 /* Find the last NULL group button. NULL groups are added at the end of the
522 task item list when grouping is enabled */
523 LastTaskItem
= TaskSwitchWnd_FindLastTaskItemOfGroup(This
,
526 if (LastTaskItem
!= NULL
)
528 ASSERT(LastTaskItem
->Index
>= 0);
530 return LastTaskItem
->Index
+ 1;
535 return This
->ToolbarBtnCount
;
539 TaskSwitchWnd_AddTaskItemButton(IN OUT PTASK_SWITCH_WND This
,
540 IN OUT PTASK_ITEM TaskItem
)
545 if (TaskItem
->Index
>= 0)
547 return TaskSwitchWnd_UpdateTaskItemButton(This
,
551 if (TaskItem
->Group
!= NULL
&&
552 TaskItem
->Group
->IsCollapsed
)
554 /* The task group is collapsed, we only need to update the group button */
555 return TaskSwitchWnd_UpdateTaskGroupButton(This
,
560 tbBtn
.fsState
= TBSTATE_ENABLED
| TBSTATE_ELLIPSES
;
561 tbBtn
.fsStyle
= BTNS_CHECK
| BTNS_NOPREFIX
| BTNS_SHOWTEXT
;
562 tbBtn
.dwData
= TaskItem
->Index
;
564 tbBtn
.iString
= (DWORD_PTR
)TaskSwitchWnd_GetWndTextFromTaskItem(This
,
567 /* Find out where to insert the new button */
568 iIndex
= TaskSwitchWnd_CalculateTaskItemNewButtonIndex(This
,
571 tbBtn
.idCommand
= iIndex
;
573 TaskSwitchWnd_BeginUpdate(This
);
575 if (SendMessage(This
->hWndToolbar
,
580 TaskSwitchWnd_UpdateIndexesAfterButtonInserted(This
,
583 DbgPrint("Added button %d for hwnd 0x%p\n", iIndex
, TaskItem
->hWnd
);
585 TaskItem
->Index
= iIndex
;
586 This
->ToolbarBtnCount
++;
588 /* Update button sizes and fix the button wrapping */
589 TaskSwitchWnd_UpdateButtonsSize(This
,
594 TaskSwitchWnd_EndUpdate(This
);
600 TaskSwitchWnd_DeleteTaskItemButton(IN OUT PTASK_SWITCH_WND This
,
601 IN OUT PTASK_ITEM TaskItem
)
603 PTASK_GROUP TaskGroup
;
606 TaskGroup
= TaskItem
->Group
;
608 if (TaskItem
->Index
>= 0)
610 if ((TaskGroup
!= NULL
&& !TaskGroup
->IsCollapsed
) ||
613 TaskSwitchWnd_BeginUpdate(This
);
615 iIndex
= TaskItem
->Index
;
616 if (SendMessage(This
->hWndToolbar
,
621 TaskItem
->Index
= -1;
622 This
->ToolbarBtnCount
--;
624 TaskSwitchWnd_UpdateIndexesAfterButtonDeleted(This
,
627 /* Update button sizes and fix the button wrapping */
628 TaskSwitchWnd_UpdateButtonsSize(This
,
633 TaskSwitchWnd_EndUpdate(This
);
641 TaskSwitchWnd_AddToTaskGroup(IN OUT PTASK_SWITCH_WND This
,
645 PTASK_GROUP TaskGroup
, *PrevLink
;
647 if (!GetWindowThreadProcessId(hWnd
,
650 DbgPrint("Cannot get process id of hwnd 0x%p\n", hWnd
);
654 /* Try to find an existing task group */
655 TaskGroup
= This
->TaskGroups
;
656 PrevLink
= &This
->TaskGroups
;
657 while (TaskGroup
!= NULL
)
659 if (TaskGroup
->dwProcessId
== dwProcessId
)
661 TaskGroup
->dwTaskCount
++;
665 PrevLink
= &TaskGroup
->Next
;
666 TaskGroup
= TaskGroup
->Next
;
669 /* Allocate a new task group */
670 TaskGroup
= HeapAlloc(hProcessHeap
,
673 if (TaskGroup
!= NULL
)
675 ZeroMemory(TaskGroup
,
678 TaskGroup
->dwTaskCount
= 1;
679 TaskGroup
->dwProcessId
= dwProcessId
;
680 TaskGroup
->Index
= -1;
682 /* Add the task group to the list */
683 *PrevLink
= TaskGroup
;
690 TaskSwitchWnd_RemoveTaskFromTaskGroup(IN OUT PTASK_SWITCH_WND This
,
691 IN OUT PTASK_ITEM TaskItem
)
693 PTASK_GROUP TaskGroup
, CurrentGroup
, *PrevLink
;
695 TaskGroup
= TaskItem
->Group
;
696 if (TaskGroup
!= NULL
)
698 DWORD dwNewTaskCount
= --TaskGroup
->dwTaskCount
;
699 if (dwNewTaskCount
== 0)
701 /* Find the previous pointer in the chain */
702 CurrentGroup
= This
->TaskGroups
;
703 PrevLink
= &This
->TaskGroups
;
704 while (CurrentGroup
!= TaskGroup
)
706 PrevLink
= &CurrentGroup
->Next
;
707 CurrentGroup
= CurrentGroup
->Next
;
710 /* Remove the group from the list */
711 ASSERT(TaskGroup
== CurrentGroup
);
712 *PrevLink
= TaskGroup
->Next
;
714 /* Free the task group */
715 HeapFree(hProcessHeap
,
719 else if (TaskGroup
->IsCollapsed
&&
720 TaskGroup
->Index
>= 0)
722 if (dwNewTaskCount
> 1)
724 /* FIXME: Check if we should expand the group */
725 /* Update the task group button */
726 TaskSwitchWnd_UpdateTaskGroupButton(This
,
731 /* Expand the group of one task button to a task button */
732 TaskSwitchWnd_ExpandTaskGroup(This
,
740 TaskSwitchWnd_FindTaskItem(IN OUT PTASK_SWITCH_WND This
,
743 PTASK_ITEM TaskItem
, LastItem
;
745 TaskItem
= This
->TaskItems
;
746 LastItem
= TaskItem
+ This
->TaskItemCount
;
747 while (TaskItem
!= LastItem
)
749 if (TaskItem
->hWnd
== hWnd
)
759 TaskSwitchWnd_FindOtherTaskItem(IN OUT PTASK_SWITCH_WND This
,
762 PTASK_ITEM LastItem
, TaskItem
;
763 PTASK_GROUP TaskGroup
;
766 if (!GetWindowThreadProcessId(hWnd
,
772 /* Try to find another task that belongs to the same
773 process as the given window */
774 TaskItem
= This
->TaskItems
;
775 LastItem
= TaskItem
+ This
->TaskItemCount
;
776 while (TaskItem
!= LastItem
)
778 TaskGroup
= TaskItem
->Group
;
779 if (TaskGroup
!= NULL
)
781 if (TaskGroup
->dwProcessId
== dwProcessId
)
786 DWORD dwProcessIdTask
;
788 if (GetWindowThreadProcessId(TaskItem
->hWnd
,
790 dwProcessIdTask
== dwProcessId
)
803 TaskSwitchWnd_AllocTaskItem(IN OUT PTASK_SWITCH_WND This
)
805 if (This
->TaskItemCount
>= MAX_TASKS_COUNT
)
807 /* We need the most significant bit in 16 bit command IDs to indicate whether it
808 is a task group or task item. WM_COMMAND limits command IDs to 16 bits! */
812 ASSERT(This
->AllocatedTaskItems
>= This
->TaskItemCount
);
814 if (This
->TaskItemCount
!= 0)
817 SIZE_T NewArrayLength
, ActiveTaskItemIndex
;
819 NewArrayLength
= This
->AllocatedTaskItems
+ TASK_ITEM_ARRAY_ALLOC
;
821 NewArray
= HeapReAlloc(hProcessHeap
,
824 NewArrayLength
* sizeof(*This
->TaskItems
));
825 if (NewArray
!= NULL
)
827 if (This
->ActiveTaskItem
!= NULL
)
829 /* Fixup the ActiveTaskItem pointer */
830 ActiveTaskItemIndex
= This
->ActiveTaskItem
- This
->TaskItems
;
831 This
->ActiveTaskItem
= NewArray
+ ActiveTaskItemIndex
;
833 This
->AllocatedTaskItems
= (WORD
)NewArrayLength
;
834 This
->TaskItems
= NewArray
;
841 This
->TaskItems
= HeapAlloc(hProcessHeap
,
843 TASK_ITEM_ARRAY_ALLOC
* sizeof(*This
->TaskItems
));
844 if (This
->TaskItems
!= NULL
)
846 This
->AllocatedTaskItems
= TASK_ITEM_ARRAY_ALLOC
;
852 return This
->TaskItems
+ This
->TaskItemCount
++;
856 TaskSwitchWnd_FreeTaskItem(IN OUT PTASK_SWITCH_WND This
,
857 IN OUT PTASK_ITEM TaskItem
)
861 if (TaskItem
== This
->ActiveTaskItem
)
862 This
->ActiveTaskItem
= NULL
;
864 wIndex
= (WORD
)(TaskItem
- This
->TaskItems
);
865 if (wIndex
+ 1 < This
->TaskItemCount
)
869 (This
->TaskItemCount
- wIndex
- 1) * sizeof(*TaskItem
));
872 This
->TaskItemCount
--;
876 TaskSwitchWnd_DeleteTaskItem(IN OUT PTASK_SWITCH_WND This
,
877 IN OUT PTASK_ITEM TaskItem
)
879 if (!This
->IsDestroying
)
881 /* Delete the task button from the toolbar */
882 TaskSwitchWnd_DeleteTaskItemButton(This
,
886 /* Remove the task from it's group */
887 TaskSwitchWnd_RemoveTaskFromTaskGroup(This
,
890 /* Free the task item */
891 TaskSwitchWnd_FreeTaskItem(This
,
896 TaskSwitchWnd_CheckActivateTaskItem(IN OUT PTASK_SWITCH_WND This
,
897 IN OUT PTASK_ITEM TaskItem
)
899 PTASK_ITEM ActiveTaskItem
;
900 PTASK_GROUP TaskGroup
= NULL
;
902 ActiveTaskItem
= This
->ActiveTaskItem
;
904 if (TaskItem
!= NULL
)
905 TaskGroup
= TaskItem
->Group
;
907 if (This
->IsGroupingEnabled
&& TaskGroup
!= NULL
)
909 if (TaskGroup
->IsCollapsed
)
914 goto ChangeTaskItemButton
;
918 ChangeTaskItemButton
:
919 if (ActiveTaskItem
!= NULL
)
921 PTASK_GROUP ActiveTaskGroup
;
923 if (ActiveTaskItem
== TaskItem
)
926 ActiveTaskGroup
= ActiveTaskItem
->Group
;
928 if (This
->IsGroupingEnabled
&& ActiveTaskGroup
!= NULL
)
930 if (ActiveTaskGroup
->IsCollapsed
)
932 if (ActiveTaskGroup
== TaskGroup
)
938 goto ChangeActiveTaskItemButton
;
942 ChangeActiveTaskItemButton
:
943 This
->ActiveTaskItem
= NULL
;
944 if (ActiveTaskItem
->Index
>= 0)
946 TaskSwitchWnd_UpdateTaskItemButton(This
,
952 This
->ActiveTaskItem
= TaskItem
;
954 if (TaskItem
!= NULL
&& TaskItem
->Index
>= 0)
956 TaskSwitchWnd_UpdateTaskItemButton(This
,
963 FindTaskItemByIndex(IN OUT PTASK_SWITCH_WND This
,
966 PTASK_ITEM TaskItem
, LastItem
;
968 TaskItem
= This
->TaskItems
;
969 LastItem
= TaskItem
+ This
->TaskItemCount
;
970 while (TaskItem
!= LastItem
)
972 if (TaskItem
->Index
== Index
)
982 FindTaskGroupByIndex(IN OUT PTASK_SWITCH_WND This
,
985 PTASK_GROUP CurrentGroup
;
987 CurrentGroup
= This
->TaskGroups
;
988 while (CurrentGroup
!= NULL
)
990 if (CurrentGroup
->Index
== Index
)
993 CurrentGroup
= CurrentGroup
->Next
;
1000 TaskSwitchWnd_AddTask(IN OUT PTASK_SWITCH_WND This
,
1003 PTASK_ITEM TaskItem
;
1005 TaskItem
= TaskSwitchWnd_FindTaskItem(This
,
1007 if (TaskItem
== NULL
)
1009 DbgPrint("Add window 0x%p\n", hWnd
);
1010 TaskItem
= TaskSwitchWnd_AllocTaskItem(This
);
1011 if (TaskItem
!= NULL
)
1013 ZeroMemory(TaskItem
,
1015 TaskItem
->hWnd
= hWnd
;
1016 TaskItem
->Index
= -1;
1017 TaskItem
->Group
= TaskSwitchWnd_AddToTaskGroup(This
,
1020 if (!This
->IsDestroying
)
1022 TaskSwitchWnd_AddTaskItemButton(This
,
1028 return TaskItem
!= NULL
;
1032 TaskSwitchWnd_ActivateTaskItem(IN OUT PTASK_SWITCH_WND This
,
1033 IN OUT PTASK_ITEM TaskItem OPTIONAL
)
1035 if (TaskItem
!= NULL
)
1037 DbgPrint("Activate window 0x%p on button %d\n", TaskItem
->hWnd
, TaskItem
->Index
);
1040 TaskSwitchWnd_CheckActivateTaskItem(This
,
1047 TaskSwitchWnd_ActivateTask(IN OUT PTASK_SWITCH_WND This
,
1050 PTASK_ITEM TaskItem
;
1052 TaskItem
= TaskSwitchWnd_FindTaskItem(This
,
1054 if (TaskItem
== NULL
)
1056 TaskItem
= TaskSwitchWnd_FindOtherTaskItem(This
,
1060 if (TaskItem
== NULL
)
1062 DbgPrint("Activate window 0x%p, could not find task\n", hWnd
);
1065 return TaskSwitchWnd_ActivateTaskItem(This
,
1070 TaskSwitchWnd_DeleteTask(IN OUT PTASK_SWITCH_WND This
,
1073 PTASK_ITEM TaskItem
;
1075 TaskItem
= TaskSwitchWnd_FindTaskItem(This
,
1077 if (TaskItem
!= NULL
)
1079 DbgPrint("Delete window 0x%p on button %d\n", hWnd
, TaskItem
->Index
);
1080 TaskSwitchWnd_DeleteTaskItem(This
,
1085 //DbgPrint("Failed to delete window 0x%p\n", hWnd);
1091 TaskSwitchWnd_DeleteAllTasks(IN OUT PTASK_SWITCH_WND This
)
1093 PTASK_ITEM CurrentTask
;
1095 if (This
->TaskItemCount
> 0)
1097 CurrentTask
= This
->TaskItems
+ This
->TaskItemCount
;
1100 TaskSwitchWnd_DeleteTaskItem(This
,
1102 } while (CurrentTask
!= This
->TaskItems
);
1107 TaskSwitchWnd_FlashTaskItem(IN OUT PTASK_SWITCH_WND This
,
1108 IN OUT PTASK_ITEM TaskItem
)
1110 /* FIXME: Implement */
1114 TaskSwitchWnd_FlashTask(IN OUT PTASK_SWITCH_WND This
,
1117 PTASK_ITEM TaskItem
;
1119 TaskItem
= TaskSwitchWnd_FindTaskItem(This
,
1121 if (TaskItem
!= NULL
)
1123 DbgPrint("Flashing window 0x%p on button %d\n", hWnd
, TaskItem
->Index
);
1124 TaskSwitchWnd_FlashTaskItem(This
,
1133 TaskSwitchWnd_RedrawTaskItem(IN OUT PTASK_SWITCH_WND This
,
1134 IN OUT PTASK_ITEM TaskItem
)
1136 PTASK_GROUP TaskGroup
;
1138 TaskGroup
= TaskItem
->Group
;
1139 if (This
->IsGroupingEnabled
&& TaskGroup
!= NULL
)
1141 if (TaskGroup
->IsCollapsed
&& TaskGroup
->Index
>= 0)
1143 TaskSwitchWnd_UpdateTaskGroupButton(This
,
1146 else if (TaskItem
->Index
>= 0)
1148 goto UpdateTaskItem
;
1151 else if (TaskItem
->Index
>= 0)
1154 TaskSwitchWnd_UpdateTaskItemButton(This
,
1161 TaskSwitchWnd_RedrawTask(IN OUT PTASK_SWITCH_WND This
,
1164 PTASK_ITEM TaskItem
;
1166 TaskItem
= TaskSwitchWnd_FindTaskItem(This
,
1168 if (TaskItem
!= NULL
)
1170 TaskSwitchWnd_RedrawTaskItem(This
,
1179 TaskSwitchWnd_UpdateTbButtonSpacing(IN OUT PTASK_SWITCH_WND This
,
1180 IN BOOL bHorizontal
,
1182 IN UINT uiBtnsPerLine
)
1186 tbm
.cbSize
= sizeof(tbm
);
1187 tbm
.dwMask
= TBMF_BARPAD
| TBMF_BUTTONSPACING
;
1189 tbm
.cxBarPad
= tbm
.cyBarPad
= 0;
1191 if (bHorizontal
|| uiBtnsPerLine
> 1)
1192 tbm
.cxButtonSpacing
= (3 * GetSystemMetrics(SM_CXEDGE
) / 2);
1194 tbm
.cxButtonSpacing
= 0;
1196 if (!bHorizontal
|| uiRows
> 1)
1197 tbm
.cyButtonSpacing
= (3 * GetSystemMetrics(SM_CYEDGE
) / 2);
1199 tbm
.cyButtonSpacing
= 0;
1201 SendMessage(This
->hWndToolbar
,
1206 return tbm
.cxButtonSpacing
;
1211 TaskSwitchWnd_UpdateButtonsSize(IN OUT PTASK_SWITCH_WND This
,
1212 IN BOOL bRedrawDisabled
)
1215 UINT uiRows
, uiMax
, uiMin
, uiBtnsPerLine
, ui
;
1221 if (GetClientRect(This
->hWnd
,
1223 !IsRectEmpty(&rcClient
))
1225 if (This
->ToolbarBtnCount
> 0)
1227 ZeroMemory (&tbm
, sizeof (tbm
));
1228 tbm
.cbSize
= sizeof(tbm
);
1229 tbm
.dwMask
= TBMF_BUTTONSPACING
;
1230 SendMessage(This
->hWndToolbar
,
1235 uiRows
= (rcClient
.bottom
+ tbm
.cyButtonSpacing
) / (This
->ButtonSize
.cy
+ tbm
.cyButtonSpacing
);
1239 uiBtnsPerLine
= (This
->ToolbarBtnCount
+ uiRows
- 1) / uiRows
;
1241 Horizontal
= ITrayWindow_IsHorizontal(This
->Tray
);
1243 if (!bRedrawDisabled
)
1244 TaskSwitchWnd_BeginUpdate(This
);
1246 /* We might need to update the button spacing */
1247 tbm
.cxButtonSpacing
= TaskSwitchWnd_UpdateTbButtonSpacing(This
,
1252 /* Calculate the ideal width and make sure it's within the allowed range */
1253 NewBtnSize
= (rcClient
.right
- (uiBtnsPerLine
* tbm
.cxButtonSpacing
)) / uiBtnsPerLine
;
1255 /* Determine the minimum and maximum width of a button */
1257 uiMax
= GetSystemMetrics(SM_CXMINIMIZED
);
1259 uiMax
= rcClient
.right
;
1261 uiMin
= GetSystemMetrics(SM_CXSIZE
) + (2 * GetSystemMetrics(SM_CXEDGE
));
1263 if (NewBtnSize
< (LONG
)uiMin
)
1265 if (NewBtnSize
> (LONG
)uiMax
)
1268 This
->ButtonSize
.cx
= NewBtnSize
;
1270 /* Recalculate how many buttons actually fit into one line */
1271 uiBtnsPerLine
= rcClient
.right
/ (NewBtnSize
+ tbm
.cxButtonSpacing
);
1272 if (uiBtnsPerLine
== 0)
1274 This
->TbButtonsPerLine
= uiBtnsPerLine
;
1276 tbbi
.cbSize
= sizeof(tbbi
);
1277 tbbi
.dwMask
= TBIF_BYINDEX
| TBIF_SIZE
| TBIF_STATE
;
1278 tbbi
.cx
= (INT
)NewBtnSize
;
1280 for (ui
= 0; ui
!= This
->ToolbarBtnCount
; ui
++)
1282 tbbi
.fsState
= TBSTATE_ENABLED
;
1284 /* Check if we're updating a button that is the last one in the
1285 line. If so, we need to set the TBSTATE_WRAP flag! */
1286 if ((ui
+ 1) % uiBtnsPerLine
== 0)
1287 tbbi
.fsState
|= TBSTATE_WRAP
;
1289 if (This
->ActiveTaskItem
!= NULL
&&
1290 This
->ActiveTaskItem
->Index
== ui
)
1292 tbbi
.fsState
|= TBSTATE_CHECKED
;
1295 SendMessage(This
->hWndToolbar
,
1302 /* FIXME: Force the window to the correct position in case some idiot
1303 did something to us */
1304 SetWindowPos(This
->hWndToolbar
,
1308 rcClient
.right
, /* FIXME */
1309 rcClient
.bottom
, /* FIXME */
1310 SWP_NOACTIVATE
| SWP_NOZORDER
);
1315 This
->TbButtonsPerLine
= 0;
1316 This
->ButtonSize
.cx
= 0;
1320 TaskSwitchWnd_EndUpdate(This
);
1323 static BOOL CALLBACK
1324 TaskSwitchWnd_EnumWindowsProc(IN HWND hWnd
,
1327 PTASK_SWITCH_WND This
= (PTASK_SWITCH_WND
)lParam
;
1329 /* Only show windows that still exist and are visible and none of explorer's
1330 special windows (such as the desktop or the tray window) */
1331 if (IsWindow(hWnd
) && IsWindowVisible(hWnd
) &&
1332 !ITrayWindow_IsSpecialHWND(This
->Tray
,
1335 /* Don't list popup windows and also no tool windows */
1337 GW_OWNER
) == NULL
&&
1338 !(GetWindowLongPtr(hWnd
,
1339 GWL_EXSTYLE
) & WS_EX_TOOLWINDOW
))
1341 TaskSwitchWnd_AddTask(This
,
1349 static LRESULT CALLBACK
1350 TaskSwichWnd_ToolbarSubclassedProc(IN HWND hWnd
,
1354 IN UINT_PTR uIdSubclass
,
1355 IN DWORD_PTR dwRefData
)
1359 Ret
= DefSubclassProc(hWnd
,
1364 if (msg
== WM_NCHITTEST
&& Ret
== HTCLIENT
)
1368 /* See if the mouse is on a button */
1369 pt
.x
= (SHORT
)LOWORD(lParam
);
1370 pt
.y
= (SHORT
)HIWORD(lParam
);
1372 if (MapWindowPoints(HWND_DESKTOP
,
1376 (INT
)SendMessage(hWnd
,
1381 /* Make the control appear to be transparent outside of any buttons */
1382 Ret
= HTTRANSPARENT
;
1390 TaskSwitchWnd_Create(IN OUT PTASK_SWITCH_WND This
)
1392 This
->hWndToolbar
= CreateWindowEx(0,
1395 WS_CHILD
| WS_VISIBLE
| WS_CLIPCHILDREN
|
1396 TBSTYLE_TOOLTIPS
| TBSTYLE_WRAPABLE
| TBSTYLE_LIST
|
1397 TBSTYLE_TRANSPARENT
|
1398 CCS_TOP
| CCS_NORESIZE
| CCS_NODIVIDER
,
1408 if (This
->hWndToolbar
!= NULL
)
1413 /* Identify the version we're using */
1414 SendMessage(This
->hWndToolbar
,
1415 TB_BUTTONSTRUCTSIZE
,
1419 /* Calculate the default button size. Don't save this in This->ButtonSize.cx so that
1420 the actual button width gets updated correctly on the first recalculation */
1421 BtnSize
.cx
= GetSystemMetrics(SM_CXMINIMIZED
);
1422 This
->ButtonSize
.cy
= BtnSize
.cy
= GetSystemMetrics(SM_CYSIZE
) + (2 * GetSystemMetrics(SM_CYEDGE
));
1423 SendMessage(This
->hWndToolbar
,
1426 (LPARAM
)MAKELONG(BtnSize
.cx
,
1429 /* We don't want to see partially clipped buttons...not that we could see them... */
1431 SendMessage(This
->hWndToolbar
,
1432 TB_SETEXTENDEDSTYLE
,
1434 TBSTYLE_EX_HIDECLIPPEDBUTTONS
);
1437 /* Set proper spacing between buttons */
1438 TaskSwitchWnd_UpdateTbButtonSpacing(This
,
1439 ITrayWindow_IsHorizontal(This
->Tray
),
1443 /* Register the shell hook */
1444 This
->ShellHookMsg
= RegisterWindowMessage(TEXT("SHELLHOOK"));
1445 hShell32
= GetModuleHandle(TEXT("SHELL32.DLL"));
1446 if (hShell32
!= NULL
)
1448 REGSHELLHOOK RegShellHook
;
1450 /* RegisterShellHook */
1451 RegShellHook
= (REGSHELLHOOK
)GetProcAddress(hShell32
,
1452 (LPCSTR
)((LONG
)181));
1453 if (RegShellHook
!= NULL
)
1455 RegShellHook(This
->hWnd
,
1456 3); /* 1 if no NT! We're targeting NT so we don't care! */
1460 /* Add all windows to the toolbar */
1461 EnumWindows(TaskSwitchWnd_EnumWindowsProc
,
1464 /* Recalculate the button size */
1465 TaskSwitchWnd_UpdateButtonsSize(This
,
1468 /* Subclass the toolbar control because it doesn't provide a
1469 NM_NCHITTEST notification */
1470 This
->IsToolbarSubclassed
= SetWindowSubclass(This
->hWndToolbar
,
1471 TaskSwichWnd_ToolbarSubclassedProc
,
1472 TSW_TOOLBAR_SUBCLASS_ID
,
1478 TaskSwitchWnd_NCDestroy(IN OUT PTASK_SWITCH_WND This
)
1482 This
->IsDestroying
= TRUE
;
1484 /* Unregister the shell hook */
1485 hShell32
= GetModuleHandle(TEXT("SHELL32.DLL"));
1486 if (hShell32
!= NULL
)
1488 REGSHELLHOOK RegShellHook
;
1490 /* RegisterShellHook */
1491 RegShellHook
= (REGSHELLHOOK
)GetProcAddress(hShell32
,
1492 (LPCSTR
)((LONG
)181));
1493 if (RegShellHook
!= NULL
)
1495 RegShellHook(This
->hWnd
,
1500 TaskSwitchWnd_DeleteAllTasks(This
);
1504 TaskSwitchWnd_HandleAppCommand(IN OUT PTASK_SWITCH_WND This
,
1510 switch (GET_APPCOMMAND_LPARAM(lParam
))
1512 case APPCOMMAND_BROWSER_SEARCH
:
1513 Ret
= SHFindFiles(NULL
,
1517 case APPCOMMAND_BROWSER_HOME
:
1518 case APPCOMMAND_LAUNCH_MAIL
:
1520 DbgPrint("Shell app command %d unhandled!\n", (INT
)GET_APPCOMMAND_LPARAM(lParam
));
1529 TaskSwitchWnd_HandleShellHookMsg(IN OUT PTASK_SWITCH_WND This
,
1535 switch ((INT
)wParam
)
1537 case HSHELL_APPCOMMAND
:
1538 TaskSwitchWnd_HandleAppCommand(This
,
1544 case HSHELL_WINDOWCREATED
:
1545 TaskSwitchWnd_AddTask(This
,
1550 case HSHELL_WINDOWDESTROYED
:
1551 /* The window still exists! Delay destroying it a bit */
1552 TaskSwitchWnd_DeleteTask(This
,
1557 case HSHELL_ACTIVATESHELLWINDOW
:
1558 goto UnhandledShellMessage
;
1560 case HSHELL_RUDEAPPACTIVATED
:
1561 goto UnhandledShellMessage
;
1563 case HSHELL_WINDOWACTIVATED
:
1564 TaskSwitchWnd_ActivateTask(This
,
1569 case HSHELL_GETMINRECT
:
1570 goto UnhandledShellMessage
;
1573 TaskSwitchWnd_FlashTask(This
,
1579 TaskSwitchWnd_RedrawTask(This
,
1584 case HSHELL_TASKMAN
:
1585 case HSHELL_LANGUAGE
:
1586 case HSHELL_SYSMENU
:
1587 case HSHELL_ENDTASK
:
1588 case HSHELL_ACCESSIBILITYSTATE
:
1589 case HSHELL_WINDOWREPLACED
:
1590 case HSHELL_WINDOWREPLACING
:
1593 static const struct {
1597 {HSHELL_WINDOWCREATED
, L
"HSHELL_WINDOWCREATED"},
1598 {HSHELL_WINDOWDESTROYED
, L
"HSHELL_WINDOWDESTROYED"},
1599 {HSHELL_ACTIVATESHELLWINDOW
, L
"HSHELL_ACTIVATESHELLWINDOW"},
1600 {HSHELL_WINDOWACTIVATED
, L
"HSHELL_WINDOWACTIVATED"},
1601 {HSHELL_GETMINRECT
, L
"HSHELL_GETMINRECT"},
1602 {HSHELL_REDRAW
, L
"HSHELL_REDRAW"},
1603 {HSHELL_TASKMAN
, L
"HSHELL_TASKMAN"},
1604 {HSHELL_LANGUAGE
, L
"HSHELL_LANGUAGE"},
1605 {HSHELL_SYSMENU
, L
"HSHELL_SYSMENU"},
1606 {HSHELL_ENDTASK
, L
"HSHELL_ENDTASK"},
1607 {HSHELL_ACCESSIBILITYSTATE
, L
"HSHELL_ACCESSIBILITYSTATE"},
1608 {HSHELL_APPCOMMAND
, L
"HSHELL_APPCOMMAND"},
1609 {HSHELL_WINDOWREPLACED
, L
"HSHELL_WINDOWREPLACED"},
1610 {HSHELL_WINDOWREPLACING
, L
"HSHELL_WINDOWREPLACING"},
1611 {HSHELL_RUDEAPPACTIVATED
, L
"HSHELL_RUDEAPPACTIVATED"},
1614 UnhandledShellMessage
:
1615 for (i
= 0, found
= 0; i
!= sizeof(hshell_msg
) / sizeof(hshell_msg
[0]); i
++)
1617 if (hshell_msg
[i
].msg
== (INT
)wParam
)
1619 DbgPrint("Shell message %ws unhandled (lParam = 0x%p)!\n", hshell_msg
[i
].msg_name
, lParam
);
1626 DbgPrint("Shell message %d unhandled (lParam = 0x%p)!\n", (INT
)wParam
, lParam
);
1636 TaskSwitchWnd_EnableGrouping(IN OUT PTASK_SWITCH_WND This
,
1639 This
->IsGroupingEnabled
= bEnable
;
1641 /* Collapse or expand groups if neccessary */
1642 TaskSwitchWnd_UpdateButtonsSize(This
,
1647 TaskSwitchWnd_HandleTaskItemClick(IN OUT PTASK_SWITCH_WND This
,
1648 IN OUT PTASK_ITEM TaskItem
)
1653 if (IsWindow(TaskItem
->hWnd
))
1655 bIsMinimized
= IsIconic(TaskItem
->hWnd
);
1656 bIsActive
= (TaskItem
== This
->ActiveTaskItem
);
1658 if (!bIsMinimized
&& bIsActive
)
1660 PostMessage(TaskItem
->hWnd
,
1669 PostMessage(TaskItem
->hWnd
,
1675 SetForegroundWindow(TaskItem
->hWnd
);
1681 TaskSwitchWnd_HandleTaskGroupClick(IN OUT PTASK_SWITCH_WND This
,
1682 IN OUT PTASK_GROUP TaskGroup
)
1684 /* TODO: Show task group menu */
1688 TaskSwitchWnd_HandleButtonClick(IN OUT PTASK_SWITCH_WND This
,
1691 PTASK_ITEM TaskItem
;
1692 PTASK_GROUP TaskGroup
;
1694 if (This
->IsGroupingEnabled
)
1696 TaskGroup
= FindTaskGroupByIndex(This
,
1698 if (TaskGroup
!= NULL
&& TaskGroup
->IsCollapsed
)
1700 TaskSwitchWnd_HandleTaskGroupClick(This
,
1706 TaskItem
= FindTaskItemByIndex(This
,
1708 if (TaskItem
!= NULL
)
1710 TaskSwitchWnd_HandleTaskItemClick(This
,
1720 TaskSwitchWnd_HandleTaskItemRightClick(IN OUT PTASK_SWITCH_WND This
,
1721 IN OUT PTASK_ITEM TaskItem
)
1724 HMENU hmenu
= GetSystemMenu(TaskItem
->hWnd
, FALSE
);
1730 cmd
= TrackPopupMenu(hmenu
, TPM_LEFTBUTTON
|TPM_RIGHTBUTTON
|TPM_RETURNCMD
, pt
.x
, pt
.y
, 0, This
->hWndToolbar
, NULL
);
1732 SetForegroundWindow(TaskItem
->hWnd
); // reactivate window after the context menu has closed
1733 PostMessage(TaskItem
->hWnd
, WM_SYSCOMMAND
, cmd
, 0);
1739 TaskSwitchWnd_HandleTaskGroupRightClick(IN OUT PTASK_SWITCH_WND This
,
1740 IN OUT PTASK_GROUP TaskGroup
)
1742 /* TODO: Show task group right click menu */
1746 TaskSwitchWnd_HandleButtonRightClick(IN OUT PTASK_SWITCH_WND This
,
1749 PTASK_ITEM TaskItem
;
1750 PTASK_GROUP TaskGroup
;
1751 if (This
->IsGroupingEnabled
)
1753 TaskGroup
= FindTaskGroupByIndex(This
,
1755 if (TaskGroup
!= NULL
&& TaskGroup
->IsCollapsed
)
1757 TaskSwitchWnd_HandleTaskGroupRightClick(This
,
1763 TaskItem
= FindTaskItemByIndex(This
,
1766 if (TaskItem
!= NULL
)
1768 TaskSwitchWnd_HandleTaskItemRightClick(This
,
1778 TaskSwichWnd_HandleItemPaint(IN OUT PTASK_SWITCH_WND This
,
1779 IN OUT NMTBCUSTOMDRAW
*nmtbcd
)
1781 HFONT hCaptionFont
, hBoldCaptionFont
;
1782 LRESULT Ret
= CDRF_DODEFAULT
;
1783 PTASK_GROUP TaskGroup
;
1784 PTASK_ITEM TaskItem
;
1786 #if TASK_USE_DRAWCAPTIONTEMP != 0
1788 UINT uidctFlags
= DC_TEXT
| DC_ICON
| DC_NOSENDMSG
;
1791 TaskItem
= FindTaskItemByIndex(This
,
1792 (INT
)nmtbcd
->nmcd
.dwItemSpec
);
1793 TaskGroup
= FindTaskGroupByIndex(This
,
1794 (INT
)nmtbcd
->nmcd
.dwItemSpec
);
1795 if (TaskGroup
== NULL
&& TaskItem
!= NULL
)
1797 ASSERT(TaskItem
!= NULL
);
1799 if (TaskItem
!= NULL
&& IsWindow(TaskItem
->hWnd
))
1801 hCaptionFont
= ITrayWindow_GetCaptionFonts(This
->Tray
,
1803 if (nmtbcd
->nmcd
.uItemState
& CDIS_CHECKED
)
1804 hCaptionFont
= hBoldCaptionFont
;
1806 #if TASK_USE_DRAWCAPTIONTEMP != 0
1808 /* Make sure we don't draw on the button edges */
1809 InflateRect(&nmtbcd
->nmcd
.rc
,
1810 -GetSystemMetrics(SM_CXEDGE
),
1811 -GetSystemMetrics(SM_CYEDGE
));
1813 if ((nmtbcd
->nmcd
.uItemState
& CDIS_MARKED
) && TaskItem
->RenderFlashed
)
1815 /* This is a slight glitch. We have to move the rectangle so that
1816 the button content appears to be pressed. However, when flashing
1817 is enabled, we can see a light line at the top and left inner
1818 border. We need to fill that area with the flashing color. Note
1819 that since we're using DrawCaptionTemp() the flashing color is
1820 COLOR_ACTIVECAPTION, not COLOR_HIGHLIGHT! */
1821 FillRect(nmtbcd
->nmcd
.hdc
,
1823 (HBRUSH
)(COLOR_ACTIVECAPTION
+ 1));
1825 /* Make the button content appear pressed. This however draws a bit
1826 into the right and bottom border of the button edge, making it
1827 look a bit odd. However, selecting a clipping region to prevent
1828 that from happening causes problems with DrawCaptionTemp()! */
1829 OffsetRect(&nmtbcd
->nmcd
.rc
,
1833 /* Render flashed */
1834 uidctFlags
|= DC_ACTIVE
;
1838 uidctFlags
|= DC_INBUTTON
;
1839 if (nmtbcd
->nmcd
.uItemState
& CDIS_CHECKED
)
1840 uidctFlags
|= DC_ACTIVE
;
1843 if (DrawCapTemp
!= NULL
)
1845 /* Draw the button content */
1846 TaskItem
->DisplayTooltip
= !DrawCapTemp(TaskItem
->hWnd
,
1855 return CDRF_SKIPDEFAULT
;
1857 #else /* !TASK_USE_DRAWCAPTIONTEMP */
1859 /* Make the entire button flashing if neccessary */
1860 if (nmtbcd
->nmcd
.uItemState
& CDIS_MARKED
)
1862 if (TaskItem
->RenderFlashed
)
1864 nmtbcd
->hbrMonoDither
= GetSysColorBrush(COLOR_HIGHLIGHT
);
1865 nmtbcd
->clrTextHighlight
= GetSysColor(COLOR_HIGHLIGHTTEXT
);
1866 nmtbcd
->nHLStringBkMode
= TRANSPARENT
;
1868 /* We don't really need to set clrMark because we set the
1869 background mode to TRANSPARENT! */
1870 nmtbcd
->clrMark
= GetSysColor(COLOR_HIGHLIGHT
);
1872 Ret
|= TBCDRF_USECDCOLORS
;
1875 Ret
|= TBCDRF_NOMARK
;
1878 /* Select the font we want to use */
1879 SelectObject(nmtbcd
->nmcd
.hdc
,
1881 return Ret
| CDRF_NEWFONT
;
1887 else if (TaskGroup
!= NULL
)
1889 /* FIXME: Implement painting for task groups */
1896 TaskSwitchWnd_HandleToolbarNotification(IN OUT PTASK_SWITCH_WND This
,
1897 IN
const NMHDR
*nmh
)
1905 LPNMTBCUSTOMDRAW nmtbcd
= (LPNMTBCUSTOMDRAW
)nmh
;
1907 switch (nmtbcd
->nmcd
.dwDrawStage
)
1910 #if TASK_USE_DRAWCAPTIONTEMP != 0
1912 case CDDS_ITEMPREPAINT
:
1913 /* We handle drawing in the post-paint stage so that we
1914 don't have to draw the button edges, etc */
1915 Ret
= CDRF_NOTIFYPOSTPAINT
;
1918 case CDDS_ITEMPOSTPAINT
:
1920 #else /* !TASK_USE_DRAWCAPTIONTEMP */
1922 case CDDS_ITEMPREPAINT
:
1926 Ret
= TaskSwichWnd_HandleItemPaint(This
,
1931 Ret
= CDRF_NOTIFYITEMDRAW
;
1935 Ret
= CDRF_DODEFAULT
;
1945 static LRESULT CALLBACK
1946 TaskSwitchWndProc(IN HWND hwnd
,
1951 PTASK_SWITCH_WND This
= NULL
;
1952 LRESULT Ret
= FALSE
;
1954 if (uMsg
!= WM_NCCREATE
)
1956 This
= (PTASK_SWITCH_WND
)GetWindowLongPtr(hwnd
,
1960 if (This
!= NULL
|| uMsg
== WM_NCCREATE
)
1968 szClient
.cx
= LOWORD(lParam
);
1969 szClient
.cy
= HIWORD(lParam
);
1970 if (This
->hWndToolbar
!= NULL
)
1972 SetWindowPos(This
->hWndToolbar
,
1980 TaskSwitchWnd_UpdateButtonsSize(This
,
1988 /* We want the tray window to be draggable everywhere, so make the control
1989 appear transparent */
1990 Ret
= DefWindowProc(hwnd
,
1994 if (Ret
!= HTVSCROLL
&& Ret
!= HTHSCROLL
)
1995 Ret
= HTTRANSPARENT
;
2001 if (lParam
!= 0 && (HWND
)lParam
== This
->hWndToolbar
)
2003 TaskSwitchWnd_HandleButtonClick(This
,
2011 const NMHDR
*nmh
= (const NMHDR
*)lParam
;
2013 if (nmh
->hwndFrom
== This
->hWndToolbar
)
2015 Ret
= TaskSwitchWnd_HandleToolbarNotification(This
,
2021 case TSWM_ENABLEGROUPING
:
2023 Ret
= This
->IsGroupingEnabled
;
2024 if (wParam
!= This
->IsGroupingEnabled
)
2026 TaskSwitchWnd_EnableGrouping(This
,
2032 case TSWM_UPDATETASKBARPOS
:
2034 /* Update the button spacing */
2035 TaskSwitchWnd_UpdateTbButtonSpacing(This
,
2036 ITrayWindow_IsHorizontal(This
->Tray
),
2042 case WM_CONTEXTMENU
:
2044 if (This
->hWndToolbar
!= NULL
)
2049 pt
.x
= (LONG
)LOWORD(lParam
);
2050 pt
.y
= (LONG
)HIWORD(lParam
);
2052 MapWindowPoints(NULL
,
2057 iBtn
= (INT_PTR
)SendMessage(This
->hWndToolbar
,
2063 TaskSwitchWnd_HandleButtonRightClick(This
,
2067 goto ForwardContextMenuMsg
;
2071 ForwardContextMenuMsg
:
2072 /* Forward message */
2073 Ret
= SendMessage(ITrayWindow_GetHWND(This
->Tray
),
2083 LPCREATESTRUCT CreateStruct
= (LPCREATESTRUCT
)lParam
;
2084 This
= (PTASK_SWITCH_WND
)HeapAlloc(hProcessHeap
,
2093 This
->hWndNotify
= CreateStruct
->hwndParent
;
2094 This
->Tray
= (ITrayWindow
*)CreateStruct
->lpCreateParams
;
2095 This
->IsGroupingEnabled
= TRUE
; /* FIXME */
2096 SetWindowLongPtr(hwnd
,
2104 TaskSwitchWnd_Create(This
);
2116 if (This
->IsToolbarSubclassed
)
2118 if (RemoveWindowSubclass(This
->hWndToolbar
,
2119 TaskSwichWnd_ToolbarSubclassedProc
,
2120 TSW_TOOLBAR_SUBCLASS_ID
))
2122 This
->IsToolbarSubclassed
= FALSE
;
2128 TaskSwitchWnd_NCDestroy(This
);
2129 HeapFree(hProcessHeap
,
2132 SetWindowLongPtr(hwnd
,
2142 TaskSwitchWnd_DumpTasks(This
);
2149 /* HandleDefaultMessage: */
2150 if (uMsg
== This
->ShellHookMsg
&& This
->ShellHookMsg
!= 0)
2152 /* Process shell messages */
2153 Ret
= (LRESULT
)TaskSwitchWnd_HandleShellHookMsg(This
,
2159 Ret
= DefWindowProc(hwnd
,
2168 Ret
= DefWindowProc(hwnd
,
2179 CreateTaskSwitchWnd(IN HWND hWndParent
,
2180 IN OUT ITrayWindow
*Tray
)
2184 hwndTaskBar
= CreateWindowEx(0,
2185 szTaskSwitchWndClass
,
2187 WS_CHILD
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
| WS_TABSTOP
,
2201 RegisterTaskSwitchWndClass(VOID
)
2205 wc
.style
= CS_DBLCLKS
;
2206 wc
.lpfnWndProc
= TaskSwitchWndProc
;
2208 wc
.cbWndExtra
= sizeof(PTASK_SWITCH_WND
);
2209 wc
.hInstance
= hExplorerInstance
;
2211 wc
.hCursor
= LoadCursor(NULL
,
2213 wc
.hbrBackground
= (HBRUSH
)(COLOR_3DFACE
+ 1);
2214 wc
.lpszMenuName
= NULL
;
2215 wc
.lpszClassName
= szTaskSwitchWndClass
;
2217 return RegisterClass(&wc
) != 0;
2221 UnregisterTaskSwitchWndClass(VOID
)
2223 UnregisterClass(szTaskSwitchWndClass
,