* Sync up to trunk head (r64894).
[reactos.git] / base / shell / explorer-new / taskswnd.c
1 /*
2 * ReactOS Explorer
3 *
4 * Copyright 2006 - 2007 Thomas Weidenmueller <w3seek@reactos.org>
5 *
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.
10 *
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.
15 *
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
19 */
20
21 #include "precomp.h"
22
23 /* Set DUMP_TASKS to 1 to enable a dump of the tasks and task groups every
24 5 seconds */
25 #define DUMP_TASKS 0
26
27 static const TCHAR szTaskSwitchWndClass[] = TEXT("MSTaskSwWClass");
28 static const TCHAR szRunningApps[] = TEXT("Running Applications");
29
30 typedef struct _TASK_GROUP
31 {
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
34 groups. */
35 struct _TASK_GROUP *Next;
36
37 DWORD dwTaskCount;
38 DWORD dwProcessId;
39 INT Index;
40 union
41 {
42 DWORD dwFlags;
43 struct
44 {
45
46 DWORD IsCollapsed : 1;
47 };
48 };
49 } TASK_GROUP, *PTASK_GROUP;
50
51 typedef struct _TASK_ITEM
52 {
53 HWND hWnd;
54 PTASK_GROUP Group;
55 INT Index;
56 INT IconIndex;
57
58
59
60 union
61 {
62 DWORD dwFlags;
63 struct
64 {
65
66 /* IsFlashing is TRUE when the task bar item should be flashing. */
67 DWORD IsFlashing : 1;
68
69 /* RenderFlashed is only TRUE if the task bar item should be
70 drawn with a flash. */
71 DWORD RenderFlashed : 1;
72 };
73 };
74 } TASK_ITEM, *PTASK_ITEM;
75
76 #define TASK_ITEM_ARRAY_ALLOC 64
77
78 typedef struct _TASK_SWITCH_WND
79 {
80 HWND hWnd;
81 HWND hWndNotify;
82
83 UINT ShellHookMsg;
84 ITrayWindow *Tray;
85
86 PTASK_GROUP TaskGroups;
87
88 WORD TaskItemCount;
89 WORD AllocatedTaskItems;
90 PTASK_ITEM TaskItems;
91 PTASK_ITEM ActiveTaskItem;
92
93 HTHEME TaskBandTheme;
94 HWND hWndToolbar;
95 UINT TbButtonsPerLine;
96 WORD ToolbarBtnCount;
97 HIMAGELIST TaskIcons;
98
99 union
100 {
101 DWORD dwFlags;
102 struct
103 {
104 DWORD IsGroupingEnabled : 1;
105 DWORD IsDestroying : 1;
106 DWORD IsToolbarSubclassed : 1;
107 };
108 };
109
110 SIZE ButtonSize;
111 TCHAR szBuf[255];
112 } TASK_SWITCH_WND, *PTASK_SWITCH_WND;
113
114 #define TSW_TOOLBAR_SUBCLASS_ID 1
115
116 #define MAX_TASKS_COUNT (0x7FFF)
117
118 static VOID TaskSwitchWnd_UpdateButtonsSize(IN OUT PTASK_SWITCH_WND This,
119 IN BOOL bRedrawDisabled);
120
121 static LPTSTR
122 TaskSwitchWnd_GetWndTextFromTaskItem(IN OUT PTASK_SWITCH_WND This,
123 IN PTASK_ITEM TaskItem)
124 {
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,
128 This->szBuf,
129 sizeof(This->szBuf) / sizeof(This->szBuf[0])) > 0)
130 {
131 return This->szBuf;
132 }
133
134 return NULL;
135 }
136
137
138 #if DUMP_TASKS != 0
139 static VOID
140 TaskSwitchWnd_DumpTasks(IN OUT PTASK_SWITCH_WND This)
141 {
142 PTASK_GROUP CurrentGroup;
143 PTASK_ITEM CurrentTaskItem, LastTaskItem;
144
145 TRACE("Tasks dump:\n");
146 if (This->IsGroupingEnabled)
147 {
148 CurrentGroup = This->TaskGroups;
149 while (CurrentGroup != NULL)
150 {
151 TRACE("- Group PID: 0x%p Tasks: %d Index: %d\n", CurrentGroup->dwProcessId, CurrentGroup->dwTaskCount, CurrentGroup->Index);
152
153 CurrentTaskItem = This->TaskItems;
154 LastTaskItem = CurrentTaskItem + This->TaskItemCount;
155 while (CurrentTaskItem != LastTaskItem)
156 {
157 if (CurrentTaskItem->Group == CurrentGroup)
158 {
159 TRACE(" + Task hwnd: 0x%p Index: %d\n", CurrentTaskItem->hWnd, CurrentTaskItem->Index);
160 }
161 CurrentTaskItem++;
162 }
163
164 CurrentGroup = CurrentGroup->Next;
165 }
166
167 CurrentTaskItem = This->TaskItems;
168 LastTaskItem = CurrentTaskItem + This->TaskItemCount;
169 while (CurrentTaskItem != LastTaskItem)
170 {
171 if (CurrentTaskItem->Group == NULL)
172 {
173 TRACE("- Task hwnd: 0x%p Index: %d\n", CurrentTaskItem->hWnd, CurrentTaskItem->Index);
174 }
175 CurrentTaskItem++;
176 }
177 }
178 else
179 {
180 CurrentTaskItem = This->TaskItems;
181 LastTaskItem = CurrentTaskItem + This->TaskItemCount;
182 while (CurrentTaskItem != LastTaskItem)
183 {
184 TRACE("- Task hwnd: 0x%p Index: %d\n", CurrentTaskItem->hWnd, CurrentTaskItem->Index);
185 CurrentTaskItem++;
186 }
187 }
188 }
189 #endif
190
191 static VOID
192 TaskSwitchWnd_BeginUpdate(IN OUT PTASK_SWITCH_WND This)
193 {
194 SendMessage(This->hWndToolbar,
195 WM_SETREDRAW,
196 FALSE,
197 0);
198 }
199
200 static VOID
201 TaskSwitchWnd_EndUpdate(IN OUT PTASK_SWITCH_WND This)
202 {
203 SendMessage(This->hWndToolbar,
204 WM_SETREDRAW,
205 TRUE,
206 0);
207 InvalidateRect(This->hWndToolbar,
208 NULL,
209 TRUE);
210 }
211
212 static BOOL
213 TaskSwitchWnd_SetToolbarButtonCommandId(IN OUT PTASK_SWITCH_WND This,
214 IN INT iButtonIndex,
215 IN INT iCommandId)
216 {
217 TBBUTTONINFO tbbi;
218
219 tbbi.cbSize = sizeof(tbbi);
220 tbbi.dwMask = TBIF_BYINDEX | TBIF_COMMAND;
221 tbbi.idCommand = iCommandId;
222
223 return SendMessage(This->hWndToolbar,
224 TB_SETBUTTONINFO,
225 (WPARAM)iButtonIndex,
226 (LPARAM)&tbbi) != 0;
227 }
228
229 static VOID
230 TaskSwitchWnd_UpdateIndexesAfterButtonInserted(IN OUT PTASK_SWITCH_WND This,
231 IN INT iIndex)
232 {
233 PTASK_GROUP CurrentGroup;
234 PTASK_ITEM CurrentTaskItem, LastTaskItem;
235 INT NewIndex;
236
237 if (This->IsGroupingEnabled)
238 {
239 /* Update all affected groups */
240 CurrentGroup = This->TaskGroups;
241 while (CurrentGroup != NULL)
242 {
243 if (CurrentGroup->IsCollapsed &&
244 CurrentGroup->Index >= iIndex)
245 {
246 /* Update the toolbar buttons */
247 NewIndex = CurrentGroup->Index + 1;
248 if (TaskSwitchWnd_SetToolbarButtonCommandId(This,
249 CurrentGroup->Index + 1,
250 NewIndex))
251 {
252 CurrentGroup->Index = NewIndex;
253 }
254 else
255 CurrentGroup->Index = -1;
256 }
257
258 CurrentGroup = CurrentGroup->Next;
259 }
260 }
261
262 /* Update all affected task items */
263 CurrentTaskItem = This->TaskItems;
264 LastTaskItem = CurrentTaskItem + This->TaskItemCount;
265 while (CurrentTaskItem != LastTaskItem)
266 {
267 CurrentGroup = CurrentTaskItem->Group;
268 if (CurrentGroup != NULL)
269 {
270 if (!CurrentGroup->IsCollapsed &&
271 CurrentTaskItem->Index >= iIndex)
272 {
273 goto UpdateTaskItemBtn;
274 }
275 }
276 else if (CurrentTaskItem->Index >= iIndex)
277 {
278 UpdateTaskItemBtn:
279 /* Update the toolbar buttons */
280 NewIndex = CurrentTaskItem->Index + 1;
281 if (TaskSwitchWnd_SetToolbarButtonCommandId(This,
282 CurrentTaskItem->Index + 1,
283 NewIndex))
284 {
285 CurrentTaskItem->Index = NewIndex;
286 }
287 else
288 CurrentTaskItem->Index = -1;
289 }
290
291 CurrentTaskItem++;
292 }
293 }
294
295 static VOID
296 TaskSwitchWnd_UpdateIndexesAfterButtonDeleted(IN OUT PTASK_SWITCH_WND This,
297 IN INT iIndex)
298 {
299 PTASK_GROUP CurrentGroup;
300 PTASK_ITEM CurrentTaskItem, LastTaskItem;
301 INT NewIndex;
302
303 if (This->IsGroupingEnabled)
304 {
305 /* Update all affected groups */
306 CurrentGroup = This->TaskGroups;
307 while (CurrentGroup != NULL)
308 {
309 if (CurrentGroup->IsCollapsed &&
310 CurrentGroup->Index > iIndex)
311 {
312 /* Update the toolbar buttons */
313 NewIndex = CurrentGroup->Index - 1;
314 if (TaskSwitchWnd_SetToolbarButtonCommandId(This,
315 CurrentGroup->Index - 1,
316 NewIndex))
317 {
318 CurrentGroup->Index = NewIndex;
319 }
320 else
321 CurrentGroup->Index = -1;
322 }
323
324 CurrentGroup = CurrentGroup->Next;
325 }
326 }
327
328 /* Update all affected task items */
329 CurrentTaskItem = This->TaskItems;
330 LastTaskItem = CurrentTaskItem + This->TaskItemCount;
331 while (CurrentTaskItem != LastTaskItem)
332 {
333 CurrentGroup = CurrentTaskItem->Group;
334 if (CurrentGroup != NULL)
335 {
336 if (!CurrentGroup->IsCollapsed &&
337 CurrentTaskItem->Index > iIndex)
338 {
339 goto UpdateTaskItemBtn;
340 }
341 }
342 else if (CurrentTaskItem->Index > iIndex)
343 {
344 UpdateTaskItemBtn:
345 /* Update the toolbar buttons */
346 NewIndex = CurrentTaskItem->Index - 1;
347 if (TaskSwitchWnd_SetToolbarButtonCommandId(This,
348 CurrentTaskItem->Index - 1,
349 NewIndex))
350 {
351 CurrentTaskItem->Index = NewIndex;
352 }
353 else
354 CurrentTaskItem->Index = -1;
355 }
356
357 CurrentTaskItem++;
358 }
359 }
360
361 static INT
362 TaskSwitchWnd_UpdateTaskGroupButton(IN OUT PTASK_SWITCH_WND This,
363 IN PTASK_GROUP TaskGroup)
364 {
365 ASSERT(TaskGroup->Index >= 0);
366
367 /* FIXME: Implement */
368
369 return TaskGroup->Index;
370 }
371
372 static VOID
373 TaskSwitchWnd_ExpandTaskGroup(IN OUT PTASK_SWITCH_WND This,
374 IN PTASK_GROUP TaskGroup)
375 {
376 ASSERT(TaskGroup->dwTaskCount > 0);
377 ASSERT(TaskGroup->IsCollapsed);
378 ASSERT(TaskGroup->Index >= 0);
379
380 /* FIXME: Implement */
381 }
382
383 static HICON
384 TaskSwitchWnd_GetWndIcon(HWND hwnd)
385 {
386 HICON hIcon = 0;
387
388 SendMessageTimeout(hwnd, WM_GETICON, ICON_SMALL2, 0, SMTO_ABORTIFHUNG, 1000, (PDWORD_PTR)&hIcon);
389
390 if (!hIcon)
391 SendMessageTimeout(hwnd, WM_GETICON, ICON_SMALL, 0, SMTO_ABORTIFHUNG, 1000, (PDWORD_PTR)&hIcon);
392
393 if (!hIcon)
394 SendMessageTimeout(hwnd, WM_GETICON, ICON_BIG, 0, SMTO_ABORTIFHUNG, 1000, (PDWORD_PTR)&hIcon);
395
396 if (!hIcon)
397 hIcon = (HICON)GetClassLongPtr(hwnd, GCL_HICONSM);
398
399 if (!hIcon)
400 hIcon = (HICON)GetClassLongPtr(hwnd, GCL_HICON);
401
402 return hIcon;
403 }
404 static INT
405 TaskSwitchWnd_UpdateTaskItemButton(IN OUT PTASK_SWITCH_WND This,
406 IN PTASK_ITEM TaskItem)
407 {
408 TBBUTTONINFO tbbi;
409 HICON icon;
410
411 ASSERT(TaskItem->Index >= 0);
412
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;
418
419 if (TaskItem->RenderFlashed)
420 tbbi.fsState |= TBSTATE_MARKED;
421
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)
426 {
427 tbbi.fsState |= TBSTATE_WRAP;
428 }
429
430 tbbi.pszText = TaskSwitchWnd_GetWndTextFromTaskItem(This,
431 TaskItem);
432
433 icon = TaskSwitchWnd_GetWndIcon(TaskItem->hWnd);
434 TaskItem->IconIndex = ImageList_ReplaceIcon(This->TaskIcons,
435 TaskItem->IconIndex,
436 icon);
437 tbbi.iImage = TaskItem->IconIndex;
438
439 if (!SendMessage(This->hWndToolbar,
440 TB_SETBUTTONINFO,
441 (WPARAM)TaskItem->Index,
442 (LPARAM)&tbbi))
443 {
444 TaskItem->Index = -1;
445 return -1;
446 }
447
448 TRACE("Updated button %d for hwnd 0x%p\n", TaskItem->Index, TaskItem->hWnd);
449 return TaskItem->Index;
450 }
451
452 static VOID
453 TaskSwitchWnd_RemoveIcon(IN OUT PTASK_SWITCH_WND This,
454 IN PTASK_ITEM TaskItem)
455 {
456 TBBUTTONINFO tbbi;
457 PTASK_ITEM currentTaskItem, LastItem;
458
459 if (TaskItem->IconIndex == -1)
460 return;
461
462 tbbi.cbSize = sizeof(tbbi);
463 tbbi.dwMask = TBIF_IMAGE;
464
465 currentTaskItem = This->TaskItems;
466 LastItem = currentTaskItem + This->TaskItemCount;
467 while (currentTaskItem != LastItem)
468 {
469 if (currentTaskItem->IconIndex > TaskItem->IconIndex)
470 {
471 currentTaskItem->IconIndex--;
472 tbbi.iImage = currentTaskItem->IconIndex;
473
474 SendMessage(This->hWndToolbar,
475 TB_SETBUTTONINFO,
476 currentTaskItem->Index,
477 (LPARAM)&tbbi);
478 }
479 currentTaskItem++;
480 }
481
482 ImageList_Remove(This->TaskIcons, TaskItem->IconIndex);
483 }
484
485 static PTASK_ITEM
486 TaskSwitchWnd_FindLastTaskItemOfGroup(IN OUT PTASK_SWITCH_WND This,
487 IN PTASK_GROUP TaskGroup OPTIONAL,
488 IN PTASK_ITEM NewTaskItem OPTIONAL)
489 {
490 PTASK_ITEM TaskItem, LastTaskItem, FoundTaskItem = NULL;
491 DWORD dwTaskCount;
492
493 ASSERT(This->IsGroupingEnabled);
494
495 TaskItem = This->TaskItems;
496 LastTaskItem = TaskItem + This->TaskItemCount;
497
498 dwTaskCount = (TaskGroup != NULL ? TaskGroup->dwTaskCount : MAX_TASKS_COUNT);
499
500 ASSERT(dwTaskCount > 0);
501
502 while (TaskItem != LastTaskItem)
503 {
504 if (TaskItem->Group == TaskGroup)
505 {
506 if ((NewTaskItem != NULL && TaskItem != NewTaskItem) || NewTaskItem == NULL)
507 {
508 FoundTaskItem = TaskItem;
509 }
510
511 if (--dwTaskCount == 0)
512 {
513 /* We found the last task item in the group! */
514 break;
515 }
516 }
517
518 TaskItem++;
519 }
520
521 return FoundTaskItem;
522 }
523
524 static INT
525 TaskSwitchWnd_CalculateTaskItemNewButtonIndex(IN OUT PTASK_SWITCH_WND This,
526 IN PTASK_ITEM TaskItem)
527 {
528 PTASK_GROUP TaskGroup;
529 PTASK_ITEM LastTaskItem;
530
531 /* NOTE: This routine assumes that the group is *not* collapsed! */
532
533 TaskGroup = TaskItem->Group;
534 if (This->IsGroupingEnabled)
535 {
536 if (TaskGroup != NULL)
537 {
538 ASSERT(TaskGroup->Index < 0);
539 ASSERT(!TaskGroup->IsCollapsed);
540
541 if (TaskGroup->dwTaskCount > 1)
542 {
543 LastTaskItem = TaskSwitchWnd_FindLastTaskItemOfGroup(This,
544 TaskGroup,
545 TaskItem);
546 if (LastTaskItem != NULL)
547 {
548 /* Since the group is expanded the task items must have an index */
549 ASSERT(LastTaskItem->Index >= 0);
550
551 return LastTaskItem->Index + 1;
552 }
553 }
554 }
555 else
556 {
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,
560 NULL,
561 TaskItem);
562 if (LastTaskItem != NULL)
563 {
564 ASSERT(LastTaskItem->Index >= 0);
565
566 return LastTaskItem->Index + 1;
567 }
568 }
569 }
570
571 return This->ToolbarBtnCount;
572 }
573
574 static INT
575 TaskSwitchWnd_AddTaskItemButton(IN OUT PTASK_SWITCH_WND This,
576 IN OUT PTASK_ITEM TaskItem)
577 {
578 TBBUTTON tbBtn;
579 INT iIndex;
580 HICON icon;
581
582 if (TaskItem->Index >= 0)
583 {
584 return TaskSwitchWnd_UpdateTaskItemButton(This,
585 TaskItem);
586 }
587
588 if (TaskItem->Group != NULL &&
589 TaskItem->Group->IsCollapsed)
590 {
591 /* The task group is collapsed, we only need to update the group button */
592 return TaskSwitchWnd_UpdateTaskGroupButton(This,
593 TaskItem->Group);
594 }
595
596 icon = TaskSwitchWnd_GetWndIcon(TaskItem->hWnd);
597 TaskItem->IconIndex = ImageList_AddIcon(This->TaskIcons, icon);
598
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;
603
604 tbBtn.iString = (DWORD_PTR)TaskSwitchWnd_GetWndTextFromTaskItem(This,
605 TaskItem);
606
607 /* Find out where to insert the new button */
608 iIndex = TaskSwitchWnd_CalculateTaskItemNewButtonIndex(This,
609 TaskItem);
610 ASSERT(iIndex >= 0);
611 tbBtn.idCommand = iIndex;
612
613 TaskSwitchWnd_BeginUpdate(This);
614
615 if (SendMessage(This->hWndToolbar,
616 TB_INSERTBUTTON,
617 (WPARAM)iIndex,
618 (LPARAM)&tbBtn))
619 {
620 TaskSwitchWnd_UpdateIndexesAfterButtonInserted(This,
621 iIndex);
622
623 TRACE("Added button %d for hwnd 0x%p\n", iIndex, TaskItem->hWnd);
624
625 TaskItem->Index = iIndex;
626 This->ToolbarBtnCount++;
627
628 /* Update button sizes and fix the button wrapping */
629 TaskSwitchWnd_UpdateButtonsSize(This,
630 TRUE);
631 return iIndex;
632 }
633
634 TaskSwitchWnd_EndUpdate(This);
635
636 return -1;
637 }
638
639 static BOOL
640 TaskSwitchWnd_DeleteTaskItemButton(IN OUT PTASK_SWITCH_WND This,
641 IN OUT PTASK_ITEM TaskItem)
642 {
643 PTASK_GROUP TaskGroup;
644 INT iIndex;
645
646 TaskGroup = TaskItem->Group;
647
648 if (TaskItem->Index >= 0)
649 {
650 if ((TaskGroup != NULL && !TaskGroup->IsCollapsed) ||
651 TaskGroup == NULL)
652 {
653 TaskSwitchWnd_BeginUpdate(This);
654
655 TaskSwitchWnd_RemoveIcon(This, TaskItem);
656 iIndex = TaskItem->Index;
657 if (SendMessage(This->hWndToolbar,
658 TB_DELETEBUTTON,
659 (WPARAM)iIndex,
660 0))
661 {
662 TaskItem->Index = -1;
663 This->ToolbarBtnCount--;
664
665 TaskSwitchWnd_UpdateIndexesAfterButtonDeleted(This,
666 iIndex);
667
668 /* Update button sizes and fix the button wrapping */
669 TaskSwitchWnd_UpdateButtonsSize(This,
670 TRUE);
671 return TRUE;
672 }
673
674 TaskSwitchWnd_EndUpdate(This);
675 }
676 }
677
678 return FALSE;
679 }
680
681 static PTASK_GROUP
682 TaskSwitchWnd_AddToTaskGroup(IN OUT PTASK_SWITCH_WND This,
683 IN HWND hWnd)
684 {
685 DWORD dwProcessId;
686 PTASK_GROUP TaskGroup, *PrevLink;
687
688 if (!GetWindowThreadProcessId(hWnd,
689 &dwProcessId))
690 {
691 TRACE("Cannot get process id of hwnd 0x%p\n", hWnd);
692 return NULL;
693 }
694
695 /* Try to find an existing task group */
696 TaskGroup = This->TaskGroups;
697 PrevLink = &This->TaskGroups;
698 while (TaskGroup != NULL)
699 {
700 if (TaskGroup->dwProcessId == dwProcessId)
701 {
702 TaskGroup->dwTaskCount++;
703 return TaskGroup;
704 }
705
706 PrevLink = &TaskGroup->Next;
707 TaskGroup = TaskGroup->Next;
708 }
709
710 /* Allocate a new task group */
711 TaskGroup = HeapAlloc(hProcessHeap,
712 HEAP_ZERO_MEMORY,
713 sizeof(*TaskGroup));
714 if (TaskGroup != NULL)
715 {
716 TaskGroup->dwTaskCount = 1;
717 TaskGroup->dwProcessId = dwProcessId;
718 TaskGroup->Index = -1;
719
720 /* Add the task group to the list */
721 *PrevLink = TaskGroup;
722 }
723
724 return TaskGroup;
725 }
726
727 static VOID
728 TaskSwitchWnd_RemoveTaskFromTaskGroup(IN OUT PTASK_SWITCH_WND This,
729 IN OUT PTASK_ITEM TaskItem)
730 {
731 PTASK_GROUP TaskGroup, CurrentGroup, *PrevLink;
732
733 TaskGroup = TaskItem->Group;
734 if (TaskGroup != NULL)
735 {
736 DWORD dwNewTaskCount = --TaskGroup->dwTaskCount;
737 if (dwNewTaskCount == 0)
738 {
739 /* Find the previous pointer in the chain */
740 CurrentGroup = This->TaskGroups;
741 PrevLink = &This->TaskGroups;
742 while (CurrentGroup != TaskGroup)
743 {
744 PrevLink = &CurrentGroup->Next;
745 CurrentGroup = CurrentGroup->Next;
746 }
747
748 /* Remove the group from the list */
749 ASSERT(TaskGroup == CurrentGroup);
750 *PrevLink = TaskGroup->Next;
751
752 /* Free the task group */
753 HeapFree(hProcessHeap,
754 0,
755 TaskGroup);
756 }
757 else if (TaskGroup->IsCollapsed &&
758 TaskGroup->Index >= 0)
759 {
760 if (dwNewTaskCount > 1)
761 {
762 /* FIXME: Check if we should expand the group */
763 /* Update the task group button */
764 TaskSwitchWnd_UpdateTaskGroupButton(This,
765 TaskGroup);
766 }
767 else
768 {
769 /* Expand the group of one task button to a task button */
770 TaskSwitchWnd_ExpandTaskGroup(This,
771 TaskGroup);
772 }
773 }
774 }
775 }
776
777 static PTASK_ITEM
778 TaskSwitchWnd_FindTaskItem(IN OUT PTASK_SWITCH_WND This,
779 IN HWND hWnd)
780 {
781 PTASK_ITEM TaskItem, LastItem;
782
783 TaskItem = This->TaskItems;
784 LastItem = TaskItem + This->TaskItemCount;
785 while (TaskItem != LastItem)
786 {
787 if (TaskItem->hWnd == hWnd)
788 return TaskItem;
789
790 TaskItem++;
791 }
792
793 return NULL;
794 }
795
796 static PTASK_ITEM
797 TaskSwitchWnd_FindOtherTaskItem(IN OUT PTASK_SWITCH_WND This,
798 IN HWND hWnd)
799 {
800 PTASK_ITEM LastItem, TaskItem;
801 PTASK_GROUP TaskGroup;
802 DWORD dwProcessId;
803
804 if (!GetWindowThreadProcessId(hWnd,
805 &dwProcessId))
806 {
807 return NULL;
808 }
809
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)
815 {
816 TaskGroup = TaskItem->Group;
817 if (TaskGroup != NULL)
818 {
819 if (TaskGroup->dwProcessId == dwProcessId)
820 return TaskItem;
821 }
822 else
823 {
824 DWORD dwProcessIdTask;
825
826 if (GetWindowThreadProcessId(TaskItem->hWnd,
827 &dwProcessIdTask) &&
828 dwProcessIdTask == dwProcessId)
829 {
830 return TaskItem;
831 }
832 }
833
834 TaskItem++;
835 }
836
837 return NULL;
838 }
839
840 static PTASK_ITEM
841 TaskSwitchWnd_AllocTaskItem(IN OUT PTASK_SWITCH_WND This)
842 {
843 if (This->TaskItemCount >= MAX_TASKS_COUNT)
844 {
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! */
847 return NULL;
848 }
849
850 ASSERT(This->AllocatedTaskItems >= This->TaskItemCount);
851
852 if (This->TaskItemCount == 0)
853 {
854 This->TaskItems = HeapAlloc(hProcessHeap,
855 0,
856 TASK_ITEM_ARRAY_ALLOC * sizeof(*This->TaskItems));
857 if (This->TaskItems != NULL)
858 {
859 This->AllocatedTaskItems = TASK_ITEM_ARRAY_ALLOC;
860 }
861 else
862 return NULL;
863 }
864 else if (This->TaskItemCount >= This->AllocatedTaskItems)
865 {
866 PTASK_ITEM NewArray;
867 SIZE_T NewArrayLength, ActiveTaskItemIndex;
868
869 NewArrayLength = This->AllocatedTaskItems + TASK_ITEM_ARRAY_ALLOC;
870
871 NewArray = HeapReAlloc(hProcessHeap,
872 0,
873 This->TaskItems,
874 NewArrayLength * sizeof(*This->TaskItems));
875 if (NewArray != NULL)
876 {
877 if (This->ActiveTaskItem != NULL)
878 {
879 /* Fixup the ActiveTaskItem pointer */
880 ActiveTaskItemIndex = This->ActiveTaskItem - This->TaskItems;
881 This->ActiveTaskItem = NewArray + ActiveTaskItemIndex;
882 }
883 This->AllocatedTaskItems = (WORD)NewArrayLength;
884 This->TaskItems = NewArray;
885 }
886 else
887 return NULL;
888 }
889
890 return This->TaskItems + This->TaskItemCount++;
891 }
892
893 static VOID
894 TaskSwitchWnd_FreeTaskItem(IN OUT PTASK_SWITCH_WND This,
895 IN OUT PTASK_ITEM TaskItem)
896 {
897 WORD wIndex;
898
899 if (TaskItem == This->ActiveTaskItem)
900 This->ActiveTaskItem = NULL;
901
902 wIndex = (WORD)(TaskItem - This->TaskItems);
903 if (wIndex + 1 < This->TaskItemCount)
904 {
905 MoveMemory(TaskItem,
906 TaskItem + 1,
907 (This->TaskItemCount - wIndex - 1) * sizeof(*TaskItem));
908 }
909
910 This->TaskItemCount--;
911 }
912
913 static VOID
914 TaskSwitchWnd_DeleteTaskItem(IN OUT PTASK_SWITCH_WND This,
915 IN OUT PTASK_ITEM TaskItem)
916 {
917 if (!This->IsDestroying)
918 {
919 /* Delete the task button from the toolbar */
920 TaskSwitchWnd_DeleteTaskItemButton(This,
921 TaskItem);
922 }
923
924 /* Remove the task from it's group */
925 TaskSwitchWnd_RemoveTaskFromTaskGroup(This,
926 TaskItem);
927
928 /* Free the task item */
929 TaskSwitchWnd_FreeTaskItem(This,
930 TaskItem);
931 }
932
933 static VOID
934 TaskSwitchWnd_CheckActivateTaskItem(IN OUT PTASK_SWITCH_WND This,
935 IN OUT PTASK_ITEM TaskItem)
936 {
937 PTASK_ITEM CurrentTaskItem;
938 PTASK_GROUP TaskGroup = NULL;
939
940 CurrentTaskItem = This->ActiveTaskItem;
941
942 if (TaskItem != NULL)
943 TaskGroup = TaskItem->Group;
944
945 if (This->IsGroupingEnabled &&
946 TaskGroup != NULL &&
947 TaskGroup->IsCollapsed)
948 {
949 /* FIXME */
950 return;
951 }
952
953 if (CurrentTaskItem != NULL)
954 {
955 PTASK_GROUP CurrentTaskGroup;
956
957 if (CurrentTaskItem == TaskItem)
958 return;
959
960 CurrentTaskGroup = CurrentTaskItem->Group;
961
962 if (This->IsGroupingEnabled &&
963 CurrentTaskGroup != NULL &&
964 CurrentTaskGroup->IsCollapsed)
965 {
966 if (CurrentTaskGroup == TaskGroup)
967 return;
968
969 /* FIXME */
970 }
971 else
972 {
973 This->ActiveTaskItem = NULL;
974 if (CurrentTaskItem->Index >= 0)
975 {
976 TaskSwitchWnd_UpdateTaskItemButton(This, CurrentTaskItem);
977 }
978 }
979 }
980
981 This->ActiveTaskItem = TaskItem;
982
983 if (TaskItem != NULL && TaskItem->Index >= 0)
984 {
985 TaskSwitchWnd_UpdateTaskItemButton(This, TaskItem);
986 }
987 else if (TaskItem == NULL)
988 {
989 TRACE("Active TaskItem now NULL\n");
990 }
991 }
992
993 static PTASK_ITEM
994 FindTaskItemByIndex(IN OUT PTASK_SWITCH_WND This,
995 IN INT Index)
996 {
997 PTASK_ITEM TaskItem, LastItem;
998
999 TaskItem = This->TaskItems;
1000 LastItem = TaskItem + This->TaskItemCount;
1001 while (TaskItem != LastItem)
1002 {
1003 if (TaskItem->Index == Index)
1004 return TaskItem;
1005
1006 TaskItem++;
1007 }
1008
1009 return NULL;
1010 }
1011
1012 static PTASK_GROUP
1013 FindTaskGroupByIndex(IN OUT PTASK_SWITCH_WND This,
1014 IN INT Index)
1015 {
1016 PTASK_GROUP CurrentGroup;
1017
1018 CurrentGroup = This->TaskGroups;
1019 while (CurrentGroup != NULL)
1020 {
1021 if (CurrentGroup->Index == Index)
1022 break;
1023
1024 CurrentGroup = CurrentGroup->Next;
1025 }
1026
1027 return CurrentGroup;
1028 }
1029
1030 static BOOL
1031 TaskSwitchWnd_AddTask(IN OUT PTASK_SWITCH_WND This,
1032 IN HWND hWnd)
1033 {
1034 PTASK_ITEM TaskItem;
1035
1036 if (!IsWindow(hWnd) || ITrayWindow_IsSpecialHWND(This->Tray, hWnd))
1037 return FALSE;
1038
1039 TaskItem = TaskSwitchWnd_FindTaskItem(This,
1040 hWnd);
1041 if (TaskItem == NULL)
1042 {
1043 TRACE("Add window 0x%p\n", hWnd);
1044 TaskItem = TaskSwitchWnd_AllocTaskItem(This);
1045 if (TaskItem != NULL)
1046 {
1047 ZeroMemory(TaskItem,
1048 sizeof(*TaskItem));
1049 TaskItem->hWnd = hWnd;
1050 TaskItem->Index = -1;
1051 TaskItem->Group = TaskSwitchWnd_AddToTaskGroup(This,
1052 hWnd);
1053
1054 if (!This->IsDestroying)
1055 {
1056 TaskSwitchWnd_AddTaskItemButton(This,
1057 TaskItem);
1058 }
1059 }
1060 }
1061
1062 return TaskItem != NULL;
1063 }
1064
1065 static BOOL
1066 TaskSwitchWnd_ActivateTaskItem(IN OUT PTASK_SWITCH_WND This,
1067 IN OUT PTASK_ITEM TaskItem OPTIONAL)
1068 {
1069 if (TaskItem != NULL)
1070 {
1071 TRACE("Activate window 0x%p on button %d\n", TaskItem->hWnd, TaskItem->Index);
1072 }
1073
1074 TaskSwitchWnd_CheckActivateTaskItem(This, TaskItem);
1075
1076 return FALSE;
1077 }
1078
1079 static BOOL
1080 TaskSwitchWnd_ActivateTask(IN OUT PTASK_SWITCH_WND This,
1081 IN HWND hWnd)
1082 {
1083 PTASK_ITEM TaskItem;
1084
1085 if (!hWnd)
1086 {
1087 return TaskSwitchWnd_ActivateTaskItem(This, NULL);
1088 }
1089
1090 TaskItem = TaskSwitchWnd_FindTaskItem(This,
1091 hWnd);
1092 if (TaskItem == NULL)
1093 {
1094 TaskItem = TaskSwitchWnd_FindOtherTaskItem(This,
1095 hWnd);
1096 }
1097
1098 if (TaskItem == NULL)
1099 {
1100 TRACE("Activate window 0x%p, could not find task\n", hWnd);
1101 }
1102
1103 return TaskSwitchWnd_ActivateTaskItem(This, TaskItem);
1104 }
1105
1106 static BOOL
1107 TaskSwitchWnd_DeleteTask(IN OUT PTASK_SWITCH_WND This,
1108 IN HWND hWnd)
1109 {
1110 PTASK_ITEM TaskItem;
1111
1112 TaskItem = TaskSwitchWnd_FindTaskItem(This,
1113 hWnd);
1114 if (TaskItem != NULL)
1115 {
1116 TRACE("Delete window 0x%p on button %d\n", hWnd, TaskItem->Index);
1117 TaskSwitchWnd_DeleteTaskItem(This,
1118 TaskItem);
1119 return TRUE;
1120 }
1121 //else
1122 //TRACE("Failed to delete window 0x%p\n", hWnd);
1123
1124 return FALSE;
1125 }
1126
1127 static VOID
1128 TaskSwitchWnd_DeleteAllTasks(IN OUT PTASK_SWITCH_WND This)
1129 {
1130 PTASK_ITEM CurrentTask;
1131
1132 if (This->TaskItemCount > 0)
1133 {
1134 CurrentTask = This->TaskItems + This->TaskItemCount;
1135 do
1136 {
1137 TaskSwitchWnd_DeleteTaskItem(This,
1138 --CurrentTask);
1139 } while (CurrentTask != This->TaskItems);
1140 }
1141 }
1142
1143 static VOID
1144 TaskSwitchWnd_FlashTaskItem(IN OUT PTASK_SWITCH_WND This,
1145 IN OUT PTASK_ITEM TaskItem)
1146 {
1147 TaskItem->RenderFlashed = 1;
1148 TaskSwitchWnd_UpdateTaskItemButton(This,
1149 TaskItem);
1150 }
1151
1152 static BOOL
1153 TaskSwitchWnd_FlashTask(IN OUT PTASK_SWITCH_WND This,
1154 IN HWND hWnd)
1155 {
1156 PTASK_ITEM TaskItem;
1157
1158 TaskItem = TaskSwitchWnd_FindTaskItem(This,
1159 hWnd);
1160 if (TaskItem != NULL)
1161 {
1162 TRACE("Flashing window 0x%p on button %d\n", hWnd, TaskItem->Index);
1163 TaskSwitchWnd_FlashTaskItem(This,
1164 TaskItem);
1165 return TRUE;
1166 }
1167
1168 return FALSE;
1169 }
1170
1171 static VOID
1172 TaskSwitchWnd_RedrawTaskItem(IN OUT PTASK_SWITCH_WND This,
1173 IN OUT PTASK_ITEM TaskItem)
1174 {
1175 PTASK_GROUP TaskGroup;
1176
1177 TaskGroup = TaskItem->Group;
1178 if (This->IsGroupingEnabled && TaskGroup != NULL)
1179 {
1180 if (TaskGroup->IsCollapsed && TaskGroup->Index >= 0)
1181 {
1182 TaskSwitchWnd_UpdateTaskGroupButton(This,
1183 TaskGroup);
1184 }
1185 else if (TaskItem->Index >= 0)
1186 {
1187 goto UpdateTaskItem;
1188 }
1189 }
1190 else if (TaskItem->Index >= 0)
1191 {
1192 UpdateTaskItem:
1193 TaskItem->RenderFlashed = 0;
1194 TaskSwitchWnd_UpdateTaskItemButton(This,
1195 TaskItem);
1196 }
1197 }
1198
1199
1200 static BOOL
1201 TaskSwitchWnd_RedrawTask(IN OUT PTASK_SWITCH_WND This,
1202 IN HWND hWnd)
1203 {
1204 PTASK_ITEM TaskItem;
1205
1206 TaskItem = TaskSwitchWnd_FindTaskItem(This,
1207 hWnd);
1208 if (TaskItem != NULL)
1209 {
1210 TaskSwitchWnd_RedrawTaskItem(This,
1211 TaskItem);
1212 return TRUE;
1213 }
1214
1215 return FALSE;
1216 }
1217
1218 static INT
1219 TaskSwitchWnd_UpdateTbButtonSpacing(IN OUT PTASK_SWITCH_WND This,
1220 IN BOOL bHorizontal,
1221 IN UINT uiRows,
1222 IN UINT uiBtnsPerLine)
1223 {
1224 TBMETRICS tbm;
1225
1226 tbm.cbSize = sizeof(tbm);
1227 tbm.dwMask = TBMF_BARPAD | TBMF_BUTTONSPACING;
1228
1229 tbm.cxBarPad = tbm.cyBarPad = 0;
1230
1231 if (bHorizontal || uiBtnsPerLine > 1)
1232 tbm.cxButtonSpacing = (3 * GetSystemMetrics(SM_CXEDGE) / 2);
1233 else
1234 tbm.cxButtonSpacing = 0;
1235
1236 if (!bHorizontal || uiRows > 1)
1237 tbm.cyButtonSpacing = (3 * GetSystemMetrics(SM_CYEDGE) / 2);
1238 else
1239 tbm.cyButtonSpacing = 0;
1240
1241 SendMessage(This->hWndToolbar,
1242 TB_SETMETRICS,
1243 0,
1244 (LPARAM)&tbm);
1245
1246 return tbm.cxButtonSpacing;
1247 }
1248
1249
1250 static VOID
1251 TaskSwitchWnd_UpdateButtonsSize(IN OUT PTASK_SWITCH_WND This,
1252 IN BOOL bRedrawDisabled)
1253 {
1254 RECT rcClient;
1255 UINT uiRows, uiMax, uiMin, uiBtnsPerLine, ui;
1256 LONG NewBtnSize;
1257 BOOL Horizontal;
1258 TBBUTTONINFO tbbi;
1259 TBMETRICS tbm;
1260
1261 if (GetClientRect(This->hWnd,
1262 &rcClient) &&
1263 !IsRectEmpty(&rcClient))
1264 {
1265 if (This->ToolbarBtnCount > 0)
1266 {
1267 ZeroMemory (&tbm, sizeof (tbm));
1268 tbm.cbSize = sizeof(tbm);
1269 tbm.dwMask = TBMF_BUTTONSPACING;
1270 SendMessage(This->hWndToolbar,
1271 TB_GETMETRICS,
1272 0,
1273 (LPARAM)&tbm);
1274
1275 uiRows = (rcClient.bottom + tbm.cyButtonSpacing) / (This->ButtonSize.cy + tbm.cyButtonSpacing);
1276 if (uiRows == 0)
1277 uiRows = 1;
1278
1279 uiBtnsPerLine = (This->ToolbarBtnCount + uiRows - 1) / uiRows;
1280
1281 Horizontal = ITrayWindow_IsHorizontal(This->Tray);
1282
1283 if (!bRedrawDisabled)
1284 TaskSwitchWnd_BeginUpdate(This);
1285
1286 /* We might need to update the button spacing */
1287 tbm.cxButtonSpacing = TaskSwitchWnd_UpdateTbButtonSpacing(This,
1288 Horizontal,
1289 uiRows,
1290 uiBtnsPerLine);
1291
1292 /* Calculate the ideal width and make sure it's within the allowed range */
1293 NewBtnSize = (rcClient.right - (uiBtnsPerLine * tbm.cxButtonSpacing)) / uiBtnsPerLine;
1294
1295 /* Determine the minimum and maximum width of a button */
1296 if (Horizontal)
1297 uiMax = GetSystemMetrics(SM_CXMINIMIZED);
1298 else
1299 uiMax = rcClient.right;
1300
1301 uiMin = GetSystemMetrics(SM_CXSIZE) + (2 * GetSystemMetrics(SM_CXEDGE));
1302
1303 if (NewBtnSize < (LONG)uiMin)
1304 NewBtnSize = uiMin;
1305 if (NewBtnSize > (LONG)uiMax)
1306 NewBtnSize = uiMax;
1307
1308 This->ButtonSize.cx = NewBtnSize;
1309
1310 /* Recalculate how many buttons actually fit into one line */
1311 uiBtnsPerLine = rcClient.right / (NewBtnSize + tbm.cxButtonSpacing);
1312 if (uiBtnsPerLine == 0)
1313 uiBtnsPerLine++;
1314 This->TbButtonsPerLine = uiBtnsPerLine;
1315
1316 tbbi.cbSize = sizeof(tbbi);
1317 tbbi.dwMask = TBIF_BYINDEX | TBIF_SIZE | TBIF_STATE;
1318 tbbi.cx = (INT)NewBtnSize;
1319
1320 for (ui = 0; ui != This->ToolbarBtnCount; ui++)
1321 {
1322 tbbi.fsState = TBSTATE_ENABLED;
1323
1324 /* Check if we're updating a button that is the last one in the
1325 line. If so, we need to set the TBSTATE_WRAP flag! */
1326 if ((ui + 1) % uiBtnsPerLine == 0)
1327 tbbi.fsState |= TBSTATE_WRAP;
1328
1329 if (This->ActiveTaskItem != NULL &&
1330 This->ActiveTaskItem->Index == ui)
1331 {
1332 tbbi.fsState |= TBSTATE_CHECKED;
1333 }
1334
1335 SendMessage(This->hWndToolbar,
1336 TB_SETBUTTONINFO,
1337 (WPARAM)ui,
1338 (LPARAM)&tbbi);
1339 }
1340
1341 #if 0
1342 /* FIXME: Force the window to the correct position in case some idiot
1343 did something to us */
1344 SetWindowPos(This->hWndToolbar,
1345 NULL,
1346 0,
1347 0,
1348 rcClient.right, /* FIXME */
1349 rcClient.bottom, /* FIXME */
1350 SWP_NOACTIVATE | SWP_NOZORDER);
1351 #endif
1352 }
1353 else
1354 {
1355 This->TbButtonsPerLine = 0;
1356 This->ButtonSize.cx = 0;
1357 }
1358 }
1359
1360 TaskSwitchWnd_EndUpdate(This);
1361 }
1362
1363 static BOOL CALLBACK
1364 TaskSwitchWnd_EnumWindowsProc(IN HWND hWnd,
1365 IN LPARAM lParam)
1366 {
1367 PTASK_SWITCH_WND This = (PTASK_SWITCH_WND)lParam;
1368
1369 /* Only show windows that still exist and are visible and none of explorer's
1370 special windows (such as the desktop or the tray window) */
1371 if (IsWindow(hWnd) && IsWindowVisible(hWnd) &&
1372 !ITrayWindow_IsSpecialHWND(This->Tray, hWnd))
1373 {
1374 DWORD exStyle = GetWindowLong(hWnd, GWL_EXSTYLE);
1375 /* Don't list popup windows and also no tool windows */
1376 if ((GetWindow(hWnd, GW_OWNER) == NULL || exStyle & WS_EX_APPWINDOW) &&
1377 !(exStyle & WS_EX_TOOLWINDOW))
1378 {
1379 TaskSwitchWnd_AddTask(This,
1380 hWnd);
1381 }
1382 }
1383
1384 return TRUE;
1385 }
1386
1387 static LRESULT CALLBACK
1388 TaskSwitchWnd_ToolbarSubclassedProc(IN HWND hWnd,
1389 IN UINT msg,
1390 IN WPARAM wParam,
1391 IN LPARAM lParam,
1392 IN UINT_PTR uIdSubclass,
1393 IN DWORD_PTR dwRefData)
1394 {
1395 LRESULT Ret;
1396
1397 Ret = DefSubclassProc(hWnd,
1398 msg,
1399 wParam,
1400 lParam);
1401
1402 if (msg == WM_NCHITTEST && Ret == HTCLIENT)
1403 {
1404 POINT pt;
1405
1406 /* See if the mouse is on a button */
1407 pt.x = (SHORT)LOWORD(lParam);
1408 pt.y = (SHORT)HIWORD(lParam);
1409
1410 if (MapWindowPoints(HWND_DESKTOP,
1411 hWnd,
1412 &pt,
1413 1) != 0 &&
1414 (INT)SendMessage(hWnd,
1415 TB_HITTEST,
1416 0,
1417 (LPARAM)&pt) < 0)
1418 {
1419 /* Make the control appear to be transparent outside of any buttons */
1420 Ret = HTTRANSPARENT;
1421 }
1422 }
1423
1424 return Ret;
1425 }
1426
1427 static VOID
1428 TaskSwitchWnd_UpdateTheme(IN OUT PTASK_SWITCH_WND This)
1429 {
1430 if (This->TaskBandTheme)
1431 CloseThemeData(This->TaskBandTheme);
1432
1433 if (IsThemeActive())
1434 This->TaskBandTheme = OpenThemeData(This->hWnd, L"TaskBand");
1435 else
1436 This->TaskBandTheme = NULL;
1437 }
1438
1439 static VOID
1440 TaskSwitchWnd_Create(IN OUT PTASK_SWITCH_WND This)
1441 {
1442 This->hWndToolbar = CreateWindowEx(0,
1443 TOOLBARCLASSNAME,
1444 szRunningApps,
1445 WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN |
1446 TBSTYLE_TOOLTIPS | TBSTYLE_WRAPABLE | TBSTYLE_LIST |
1447 TBSTYLE_TRANSPARENT |
1448 CCS_TOP | CCS_NORESIZE | CCS_NODIVIDER,
1449 0,
1450 0,
1451 0,
1452 0,
1453 This->hWnd,
1454 NULL,
1455 hExplorerInstance,
1456 NULL);
1457
1458 if (This->hWndToolbar != NULL)
1459 {
1460 HMODULE hShell32;
1461 SIZE BtnSize;
1462
1463 SetWindowTheme(This->hWndToolbar, L"TaskBand", NULL);
1464 TaskSwitchWnd_UpdateTheme(This);
1465 /* Identify the version we're using */
1466 SendMessage(This->hWndToolbar,
1467 TB_BUTTONSTRUCTSIZE,
1468 sizeof(TBBUTTON),
1469 0);
1470
1471 This->TaskIcons = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1000);
1472 SendMessage(This->hWndToolbar, TB_SETIMAGELIST, 0, (LPARAM)This->TaskIcons);
1473
1474 /* Calculate the default button size. Don't save this in This->ButtonSize.cx so that
1475 the actual button width gets updated correctly on the first recalculation */
1476 BtnSize.cx = GetSystemMetrics(SM_CXMINIMIZED);
1477 This->ButtonSize.cy = BtnSize.cy = GetSystemMetrics(SM_CYSIZE) + (2 * GetSystemMetrics(SM_CYEDGE));
1478 SendMessage(This->hWndToolbar,
1479 TB_SETBUTTONSIZE,
1480 0,
1481 (LPARAM)MAKELONG(BtnSize.cx,
1482 BtnSize.cy));
1483
1484 /* We don't want to see partially clipped buttons...not that we could see them... */
1485 #if 0
1486 SendMessage(This->hWndToolbar,
1487 TB_SETEXTENDEDSTYLE,
1488 0,
1489 TBSTYLE_EX_HIDECLIPPEDBUTTONS);
1490 #endif
1491
1492 /* Set proper spacing between buttons */
1493 TaskSwitchWnd_UpdateTbButtonSpacing(This,
1494 ITrayWindow_IsHorizontal(This->Tray),
1495 0,
1496 0);
1497
1498 /* Register the shell hook */
1499 This->ShellHookMsg = RegisterWindowMessage(TEXT("SHELLHOOK"));
1500 hShell32 = GetModuleHandle(TEXT("SHELL32.DLL"));
1501 if (hShell32 != NULL)
1502 {
1503 REGSHELLHOOK RegShellHook;
1504
1505 /* RegisterShellHook */
1506 RegShellHook = (REGSHELLHOOK)GetProcAddress(hShell32,
1507 (LPCSTR)((LONG)181));
1508 if (RegShellHook != NULL)
1509 {
1510 RegShellHook(This->hWnd,
1511 3); /* 1 if no NT! We're targeting NT so we don't care! */
1512 }
1513 }
1514
1515 /* Add all windows to the toolbar */
1516 EnumWindows(TaskSwitchWnd_EnumWindowsProc,
1517 (LPARAM)This);
1518
1519 /* Recalculate the button size */
1520 TaskSwitchWnd_UpdateButtonsSize(This,
1521 FALSE);
1522
1523 /* Subclass the toolbar control because it doesn't provide a
1524 NM_NCHITTEST notification */
1525 This->IsToolbarSubclassed = SetWindowSubclass(This->hWndToolbar,
1526 TaskSwitchWnd_ToolbarSubclassedProc,
1527 TSW_TOOLBAR_SUBCLASS_ID,
1528 (DWORD_PTR)This);
1529 }
1530 }
1531
1532 static VOID
1533 TaskSwitchWnd_NCDestroy(IN OUT PTASK_SWITCH_WND This)
1534 {
1535 HMODULE hShell32;
1536
1537 This->IsDestroying = TRUE;
1538
1539 /* Unregister the shell hook */
1540 hShell32 = GetModuleHandle(TEXT("SHELL32.DLL"));
1541 if (hShell32 != NULL)
1542 {
1543 REGSHELLHOOK RegShellHook;
1544
1545 /* RegisterShellHook */
1546 RegShellHook = (REGSHELLHOOK)GetProcAddress(hShell32,
1547 (LPCSTR)((LONG)181));
1548 if (RegShellHook != NULL)
1549 {
1550 RegShellHook(This->hWnd,
1551 FALSE);
1552 }
1553 }
1554
1555 CloseThemeData(This->TaskBandTheme);
1556 TaskSwitchWnd_DeleteAllTasks(This);
1557 }
1558
1559 static BOOL
1560 TaskSwitchWnd_HandleAppCommand(IN OUT PTASK_SWITCH_WND This,
1561 IN WPARAM wParam,
1562 IN LPARAM lParam)
1563 {
1564 BOOL Ret = FALSE;
1565
1566 switch (GET_APPCOMMAND_LPARAM(lParam))
1567 {
1568 case APPCOMMAND_BROWSER_SEARCH:
1569 Ret = SHFindFiles(NULL,
1570 NULL);
1571 break;
1572
1573 case APPCOMMAND_BROWSER_HOME:
1574 case APPCOMMAND_LAUNCH_MAIL:
1575 default:
1576 TRACE("Shell app command %d unhandled!\n", (INT)GET_APPCOMMAND_LPARAM(lParam));
1577 break;
1578 }
1579
1580 return Ret;
1581 }
1582
1583
1584 static LRESULT
1585 TaskSwitchWnd_HandleShellHookMsg(IN OUT PTASK_SWITCH_WND This,
1586 IN WPARAM wParam,
1587 IN LPARAM lParam)
1588 {
1589 BOOL Ret = FALSE;
1590
1591 switch ((INT)wParam)
1592 {
1593 case HSHELL_APPCOMMAND:
1594 TaskSwitchWnd_HandleAppCommand(This,
1595 wParam,
1596 lParam);
1597 Ret = TRUE;
1598 break;
1599
1600 case HSHELL_WINDOWCREATED:
1601 Ret = TaskSwitchWnd_AddTask(This,
1602 (HWND)lParam);
1603 break;
1604
1605 case HSHELL_WINDOWDESTROYED:
1606 /* The window still exists! Delay destroying it a bit */
1607 TaskSwitchWnd_DeleteTask(This, (HWND)lParam);
1608 Ret = TRUE;
1609 break;
1610
1611 case HSHELL_RUDEAPPACTIVATED:
1612 case HSHELL_WINDOWACTIVATED:
1613 if (lParam)
1614 {
1615 TaskSwitchWnd_ActivateTask(This, (HWND) lParam);
1616 Ret = TRUE;
1617 }
1618 break;
1619
1620 case HSHELL_GETMINRECT:
1621 goto UnhandledShellMessage;
1622
1623 case HSHELL_FLASH:
1624 TaskSwitchWnd_FlashTask(This,
1625 (HWND)lParam);
1626 Ret = TRUE;
1627 break;
1628
1629 case HSHELL_REDRAW:
1630 TaskSwitchWnd_RedrawTask(This,
1631 (HWND)lParam);
1632 Ret = TRUE;
1633 break;
1634
1635 case HSHELL_TASKMAN:
1636 PostMessage(ITrayWindow_GetHWND(This->Tray), TWM_OPENSTARTMENU,0, 0);
1637 break;
1638
1639 case HSHELL_ACTIVATESHELLWINDOW:
1640 case HSHELL_LANGUAGE:
1641 case HSHELL_SYSMENU:
1642 case HSHELL_ENDTASK:
1643 case HSHELL_ACCESSIBILITYSTATE:
1644 case HSHELL_WINDOWREPLACED:
1645 case HSHELL_WINDOWREPLACING:
1646 default:
1647 {
1648 static const struct {
1649 INT msg;
1650 LPCWSTR msg_name;
1651 } hshell_msg[] = {
1652 {HSHELL_WINDOWCREATED, L"HSHELL_WINDOWCREATED"},
1653 {HSHELL_WINDOWDESTROYED, L"HSHELL_WINDOWDESTROYED"},
1654 {HSHELL_ACTIVATESHELLWINDOW, L"HSHELL_ACTIVATESHELLWINDOW"},
1655 {HSHELL_WINDOWACTIVATED, L"HSHELL_WINDOWACTIVATED"},
1656 {HSHELL_GETMINRECT, L"HSHELL_GETMINRECT"},
1657 {HSHELL_REDRAW, L"HSHELL_REDRAW"},
1658 {HSHELL_TASKMAN, L"HSHELL_TASKMAN"},
1659 {HSHELL_LANGUAGE, L"HSHELL_LANGUAGE"},
1660 {HSHELL_SYSMENU, L"HSHELL_SYSMENU"},
1661 {HSHELL_ENDTASK, L"HSHELL_ENDTASK"},
1662 {HSHELL_ACCESSIBILITYSTATE, L"HSHELL_ACCESSIBILITYSTATE"},
1663 {HSHELL_APPCOMMAND, L"HSHELL_APPCOMMAND"},
1664 {HSHELL_WINDOWREPLACED, L"HSHELL_WINDOWREPLACED"},
1665 {HSHELL_WINDOWREPLACING, L"HSHELL_WINDOWREPLACING"},
1666 {HSHELL_RUDEAPPACTIVATED, L"HSHELL_RUDEAPPACTIVATED"},
1667 };
1668 int i, found;
1669 UnhandledShellMessage:
1670 for (i = 0, found = 0; i != sizeof(hshell_msg) / sizeof(hshell_msg[0]); i++)
1671 {
1672 if (hshell_msg[i].msg == (INT)wParam)
1673 {
1674 TRACE("Shell message %ws unhandled (lParam = 0x%p)!\n", hshell_msg[i].msg_name, lParam);
1675 found = 1;
1676 break;
1677 }
1678 }
1679 if (!found)
1680 {
1681 TRACE("Shell message %d unhandled (lParam = 0x%p)!\n", (INT)wParam, lParam);
1682 }
1683 break;
1684 }
1685 }
1686
1687 return Ret;
1688 }
1689
1690 static VOID
1691 TaskSwitchWnd_EnableGrouping(IN OUT PTASK_SWITCH_WND This,
1692 IN BOOL bEnable)
1693 {
1694 This->IsGroupingEnabled = bEnable;
1695
1696 /* Collapse or expand groups if neccessary */
1697 TaskSwitchWnd_UpdateButtonsSize(This,
1698 FALSE);
1699 }
1700
1701 static VOID
1702 TaskSwitchWnd_HandleTaskItemClick(IN OUT PTASK_SWITCH_WND This,
1703 IN OUT PTASK_ITEM TaskItem)
1704 {
1705 BOOL bIsMinimized;
1706 BOOL bIsActive;
1707
1708 if (IsWindow(TaskItem->hWnd))
1709 {
1710 bIsMinimized = IsIconic(TaskItem->hWnd);
1711 bIsActive = (TaskItem == This->ActiveTaskItem);
1712
1713 TRACE("Active TaskItem %p, selected TaskItem %p\n", This->ActiveTaskItem, TaskItem);
1714 if (This->ActiveTaskItem)
1715 TRACE("Active TaskItem hWnd=%p, TaskItem hWnd %p\n", This->ActiveTaskItem->hWnd, TaskItem->hWnd);
1716
1717 TRACE("Valid button clicked. HWND=%p, IsMinimized=%s, IsActive=%s...\n",
1718 TaskItem->hWnd, bIsMinimized ? "Yes" : "No", bIsActive ? "Yes" : "No");
1719
1720 if (!bIsMinimized && bIsActive)
1721 {
1722 PostMessage(TaskItem->hWnd,
1723 WM_SYSCOMMAND,
1724 SC_MINIMIZE,
1725 0);
1726 TRACE("Valid button clicked. App window Minimized.\n");
1727 }
1728 else
1729 {
1730 if (bIsMinimized)
1731 {
1732 PostMessage(TaskItem->hWnd,
1733 WM_SYSCOMMAND,
1734 SC_RESTORE,
1735 0);
1736 TRACE("Valid button clicked. App window Restored.\n");
1737 }
1738
1739 SetForegroundWindow(TaskItem->hWnd);
1740 TRACE("Valid button clicked. App window Activated.\n");
1741 }
1742 }
1743 }
1744
1745 static VOID
1746 TaskSwitchWnd_HandleTaskGroupClick(IN OUT PTASK_SWITCH_WND This,
1747 IN OUT PTASK_GROUP TaskGroup)
1748 {
1749 /* TODO: Show task group menu */
1750 }
1751
1752 static BOOL
1753 TaskSwitchWnd_HandleButtonClick(IN OUT PTASK_SWITCH_WND This,
1754 IN WORD wIndex)
1755 {
1756 PTASK_ITEM TaskItem;
1757 PTASK_GROUP TaskGroup;
1758
1759 if (This->IsGroupingEnabled)
1760 {
1761 TaskGroup = FindTaskGroupByIndex(This,
1762 (INT)wIndex);
1763 if (TaskGroup != NULL && TaskGroup->IsCollapsed)
1764 {
1765 TaskSwitchWnd_HandleTaskGroupClick(This,
1766 TaskGroup);
1767 return TRUE;
1768 }
1769 }
1770
1771 TaskItem = FindTaskItemByIndex(This,
1772 (INT)wIndex);
1773 if (TaskItem != NULL)
1774 {
1775 TaskSwitchWnd_HandleTaskItemClick(This,
1776 TaskItem);
1777 return TRUE;
1778 }
1779
1780 return FALSE;
1781 }
1782
1783
1784 static VOID
1785 TaskSwitchWnd_HandleTaskItemRightClick(IN OUT PTASK_SWITCH_WND This,
1786 IN OUT PTASK_ITEM TaskItem)
1787 {
1788
1789 HMENU hmenu = GetSystemMenu(TaskItem->hWnd, FALSE);
1790
1791 if (hmenu) {
1792 POINT pt;
1793 int cmd;
1794 GetCursorPos(&pt);
1795 cmd = TrackPopupMenu(hmenu, TPM_LEFTBUTTON|TPM_RIGHTBUTTON|TPM_RETURNCMD, pt.x, pt.y, 0, This->hWndToolbar, NULL);
1796 if (cmd) {
1797 SetForegroundWindow(TaskItem->hWnd); // reactivate window after the context menu has closed
1798 PostMessage(TaskItem->hWnd, WM_SYSCOMMAND, cmd, 0);
1799 }
1800 }
1801 }
1802
1803 static VOID
1804 TaskSwitchWnd_HandleTaskGroupRightClick(IN OUT PTASK_SWITCH_WND This,
1805 IN OUT PTASK_GROUP TaskGroup)
1806 {
1807 /* TODO: Show task group right click menu */
1808 }
1809
1810 static BOOL
1811 TaskSwitchWnd_HandleButtonRightClick(IN OUT PTASK_SWITCH_WND This,
1812 IN WORD wIndex)
1813 {
1814 PTASK_ITEM TaskItem;
1815 PTASK_GROUP TaskGroup;
1816 if (This->IsGroupingEnabled)
1817 {
1818 TaskGroup = FindTaskGroupByIndex(This,
1819 (INT)wIndex);
1820 if (TaskGroup != NULL && TaskGroup->IsCollapsed)
1821 {
1822 TaskSwitchWnd_HandleTaskGroupRightClick(This,
1823 TaskGroup);
1824 return TRUE;
1825 }
1826 }
1827
1828 TaskItem = FindTaskItemByIndex(This,
1829 (INT)wIndex);
1830
1831 if (TaskItem != NULL)
1832 {
1833 TaskSwitchWnd_HandleTaskItemRightClick(This,
1834 TaskItem);
1835 return TRUE;
1836 }
1837
1838 return FALSE;
1839 }
1840
1841
1842 static LRESULT
1843 TaskSwitchWnd_HandleItemPaint(IN OUT PTASK_SWITCH_WND This,
1844 IN OUT NMTBCUSTOMDRAW *nmtbcd)
1845 {
1846 LRESULT Ret = CDRF_DODEFAULT;
1847 PTASK_GROUP TaskGroup;
1848 PTASK_ITEM TaskItem;
1849
1850 TaskItem = FindTaskItemByIndex(This,
1851 (INT)nmtbcd->nmcd.dwItemSpec);
1852 TaskGroup = FindTaskGroupByIndex(This,
1853 (INT)nmtbcd->nmcd.dwItemSpec);
1854 if (TaskGroup == NULL && TaskItem != NULL)
1855 {
1856 ASSERT(TaskItem != NULL);
1857
1858 if (TaskItem != NULL && IsWindow(TaskItem->hWnd))
1859 {
1860 /* Make the entire button flashing if neccessary */
1861 if (nmtbcd->nmcd.uItemState & CDIS_MARKED)
1862 {
1863 Ret = TBCDRF_NOBACKGROUND;
1864 if (!This->TaskBandTheme)
1865 {
1866 SelectObject(nmtbcd->nmcd.hdc, GetSysColorBrush(COLOR_HIGHLIGHT));
1867 Rectangle(nmtbcd->nmcd.hdc,
1868 nmtbcd->nmcd.rc.left,
1869 nmtbcd->nmcd.rc.top,
1870 nmtbcd->nmcd.rc.right,
1871 nmtbcd->nmcd.rc.bottom);
1872 }
1873 else
1874 {
1875 DrawThemeBackground(This->TaskBandTheme, nmtbcd->nmcd.hdc, TDP_FLASHBUTTON, 0, &nmtbcd->nmcd.rc, 0);
1876 }
1877 nmtbcd->clrText = GetSysColor(COLOR_HIGHLIGHTTEXT);
1878 return Ret;
1879 }
1880 }
1881 }
1882 else if (TaskGroup != NULL)
1883 {
1884 /* FIXME: Implement painting for task groups */
1885 }
1886 return Ret;
1887 }
1888
1889 static LRESULT
1890 TaskSwitchWnd_HandleToolbarNotification(IN OUT PTASK_SWITCH_WND This,
1891 IN const NMHDR *nmh)
1892 {
1893 LRESULT Ret = 0;
1894
1895 switch (nmh->code)
1896 {
1897 case NM_CUSTOMDRAW:
1898 {
1899 LPNMTBCUSTOMDRAW nmtbcd = (LPNMTBCUSTOMDRAW)nmh;
1900
1901 switch (nmtbcd->nmcd.dwDrawStage)
1902 {
1903
1904 case CDDS_ITEMPREPAINT:
1905 Ret = TaskSwitchWnd_HandleItemPaint(This,
1906 nmtbcd);
1907 break;
1908
1909 case CDDS_PREPAINT:
1910 Ret = CDRF_NOTIFYITEMDRAW;
1911 break;
1912
1913 default:
1914 Ret = CDRF_DODEFAULT;
1915 break;
1916 }
1917 break;
1918 }
1919 }
1920
1921 return Ret;
1922 }
1923
1924 static VOID
1925 TaskSwitchWnd_DrawBackground(HWND hwnd,
1926 HDC hdc)
1927 {
1928 RECT rect;
1929
1930 GetClientRect(hwnd, &rect);
1931 DrawThemeParentBackground(hwnd, hdc, &rect);
1932 }
1933
1934 static LRESULT CALLBACK
1935 TaskSwitchWndProc(IN HWND hwnd,
1936 IN UINT uMsg,
1937 IN WPARAM wParam,
1938 IN LPARAM lParam)
1939 {
1940 PTASK_SWITCH_WND This = NULL;
1941 LRESULT Ret = FALSE;
1942
1943 if (uMsg != WM_NCCREATE)
1944 {
1945 This = (PTASK_SWITCH_WND)GetWindowLongPtr(hwnd,
1946 0);
1947 }
1948
1949 if (This != NULL || uMsg == WM_NCCREATE)
1950 {
1951 switch (uMsg)
1952 {
1953 case WM_THEMECHANGED:
1954 TaskSwitchWnd_UpdateTheme(This);
1955 break;
1956 case WM_ERASEBKGND:
1957 TaskSwitchWnd_DrawBackground(hwnd, (HDC)wParam);
1958 break;
1959 case WM_SIZE:
1960 {
1961 SIZE szClient;
1962
1963 szClient.cx = LOWORD(lParam);
1964 szClient.cy = HIWORD(lParam);
1965 if (This->hWndToolbar != NULL)
1966 {
1967 SetWindowPos(This->hWndToolbar,
1968 NULL,
1969 0,
1970 0,
1971 szClient.cx,
1972 szClient.cy,
1973 SWP_NOZORDER);
1974
1975 TaskSwitchWnd_UpdateButtonsSize(This,
1976 FALSE);
1977 }
1978 break;
1979 }
1980
1981 case WM_NCHITTEST:
1982 {
1983 /* We want the tray window to be draggable everywhere, so make the control
1984 appear transparent */
1985 Ret = DefWindowProc(hwnd,
1986 uMsg,
1987 wParam,
1988 lParam);
1989 if (Ret != HTVSCROLL && Ret != HTHSCROLL)
1990 Ret = HTTRANSPARENT;
1991 break;
1992 }
1993
1994 case WM_COMMAND:
1995 {
1996 if (lParam != 0 && (HWND)lParam == This->hWndToolbar)
1997 {
1998 TaskSwitchWnd_HandleButtonClick(This,
1999 LOWORD(wParam));
2000 }
2001 break;
2002 }
2003
2004 case WM_NOTIFY:
2005 {
2006 const NMHDR *nmh = (const NMHDR *)lParam;
2007
2008 if (nmh->hwndFrom == This->hWndToolbar)
2009 {
2010 Ret = TaskSwitchWnd_HandleToolbarNotification(This,
2011 nmh);
2012 }
2013 break;
2014 }
2015
2016 case TSWM_ENABLEGROUPING:
2017 {
2018 Ret = This->IsGroupingEnabled;
2019 if (wParam != This->IsGroupingEnabled)
2020 {
2021 TaskSwitchWnd_EnableGrouping(This,
2022 (BOOL)wParam);
2023 }
2024 break;
2025 }
2026
2027 case TSWM_UPDATETASKBARPOS:
2028 {
2029 /* Update the button spacing */
2030 TaskSwitchWnd_UpdateTbButtonSpacing(This,
2031 ITrayWindow_IsHorizontal(This->Tray),
2032 0,
2033 0);
2034 break;
2035 }
2036
2037 case WM_CONTEXTMENU:
2038 {
2039 if (This->hWndToolbar != NULL)
2040 {
2041 POINT pt;
2042 INT_PTR iBtn;
2043
2044 pt.x = (LONG)LOWORD(lParam);
2045 pt.y = (LONG)HIWORD(lParam);
2046
2047 MapWindowPoints(NULL,
2048 This->hWndToolbar,
2049 &pt,
2050 1);
2051
2052 iBtn = (INT_PTR)SendMessage(This->hWndToolbar,
2053 TB_HITTEST,
2054 0,
2055 (LPARAM)&pt);
2056 if (iBtn >= 0)
2057 {
2058 TaskSwitchWnd_HandleButtonRightClick(This,
2059 iBtn);
2060 }
2061 else
2062 goto ForwardContextMenuMsg;
2063 }
2064 else
2065 {
2066 ForwardContextMenuMsg:
2067 /* Forward message */
2068 Ret = SendMessage(ITrayWindow_GetHWND(This->Tray),
2069 uMsg,
2070 wParam,
2071 lParam);
2072 }
2073 break;
2074 }
2075
2076 case WM_NCCREATE:
2077 {
2078 LPCREATESTRUCT CreateStruct = (LPCREATESTRUCT)lParam;
2079 This = HeapAlloc(hProcessHeap,
2080 HEAP_ZERO_MEMORY,
2081 sizeof(*This));
2082 if (This == NULL)
2083 return FALSE;
2084
2085 This->hWnd = hwnd;
2086 This->hWndNotify = CreateStruct->hwndParent;
2087 This->Tray = (ITrayWindow*)CreateStruct->lpCreateParams;
2088 This->IsGroupingEnabled = TRUE; /* FIXME */
2089 SetWindowLongPtr(hwnd,
2090 0,
2091 (LONG_PTR)This);
2092
2093 return TRUE;
2094 }
2095
2096 case WM_CREATE:
2097 TaskSwitchWnd_Create(This);
2098
2099 #if DUMP_TASKS != 0
2100 SetTimer(hwnd,
2101 1,
2102 5000,
2103 NULL);
2104 #endif
2105
2106 break;
2107
2108 case WM_DESTROY:
2109 if (This->IsToolbarSubclassed)
2110 {
2111 if (RemoveWindowSubclass(This->hWndToolbar,
2112 TaskSwitchWnd_ToolbarSubclassedProc,
2113 TSW_TOOLBAR_SUBCLASS_ID))
2114 {
2115 This->IsToolbarSubclassed = FALSE;
2116 }
2117 }
2118 break;
2119
2120 case WM_NCDESTROY:
2121 TaskSwitchWnd_NCDestroy(This);
2122 HeapFree(hProcessHeap,
2123 0,
2124 This);
2125 SetWindowLongPtr(hwnd,
2126 0,
2127 0);
2128 break;
2129
2130 #if DUMP_TASKS != 0
2131 case WM_TIMER:
2132 switch (wParam)
2133 {
2134 case 1:
2135 TaskSwitchWnd_DumpTasks(This);
2136 break;
2137 }
2138 break;
2139 #endif
2140
2141 case WM_KLUDGEMINRECT:
2142 {
2143 PTASK_ITEM TaskItem = TaskSwitchWnd_FindTaskItem(This, (HWND)wParam);
2144 if (TaskItem)
2145 {
2146 RECT* prcMinRect = (RECT*)lParam;
2147 RECT rcItem, rcToolbar;
2148 SendMessageW(This->hWndToolbar,TB_GETITEMRECT, TaskItem->Index, (LPARAM)&rcItem);
2149 GetWindowRect(This->hWndToolbar, &rcToolbar);
2150
2151 OffsetRect(&rcItem, rcToolbar.left, rcToolbar.top);
2152
2153 *prcMinRect = rcItem;
2154 return TRUE;
2155 }
2156 return FALSE;
2157 }
2158
2159 default:
2160 /* HandleDefaultMessage: */
2161 if (uMsg == This->ShellHookMsg && This->ShellHookMsg != 0)
2162 {
2163 /* Process shell messages */
2164 Ret = (LRESULT)TaskSwitchWnd_HandleShellHookMsg(This,
2165 wParam,
2166 lParam);
2167 break;
2168 }
2169
2170 Ret = DefWindowProc(hwnd,
2171 uMsg,
2172 wParam,
2173 lParam);
2174 break;
2175 }
2176 }
2177 else
2178 {
2179 Ret = DefWindowProc(hwnd,
2180 uMsg,
2181 wParam,
2182 lParam);
2183 }
2184
2185 return Ret;
2186 }
2187
2188
2189 HWND
2190 CreateTaskSwitchWnd(IN HWND hWndParent,
2191 IN OUT ITrayWindow *Tray)
2192 {
2193 HWND hwndTaskBar;
2194
2195 hwndTaskBar = CreateWindowEx(0,
2196 szTaskSwitchWndClass,
2197 szRunningApps,
2198 WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP,
2199 0,
2200 0,
2201 0,
2202 0,
2203 hWndParent,
2204 NULL,
2205 hExplorerInstance,
2206 (LPVOID)Tray);
2207
2208 return hwndTaskBar;
2209 }
2210
2211 BOOL
2212 RegisterTaskSwitchWndClass(VOID)
2213 {
2214 WNDCLASS wc;
2215
2216 wc.style = CS_DBLCLKS;
2217 wc.lpfnWndProc = TaskSwitchWndProc;
2218 wc.cbClsExtra = 0;
2219 wc.cbWndExtra = sizeof(PTASK_SWITCH_WND);
2220 wc.hInstance = hExplorerInstance;
2221 wc.hIcon = NULL;
2222 wc.hCursor = LoadCursor(NULL,
2223 IDC_ARROW);
2224 wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
2225 wc.lpszMenuName = NULL;
2226 wc.lpszClassName = szTaskSwitchWndClass;
2227
2228 return RegisterClass(&wc) != 0;
2229 }
2230
2231 VOID
2232 UnregisterTaskSwitchWndClass(VOID)
2233 {
2234 UnregisterClass(szTaskSwitchWndClass,
2235 hExplorerInstance);
2236 }