fe3f896d90b0199c69df5409d81e989798e60a10
[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 | TBSTYLE_TRANSPARENT |
1447 CCS_TOP | CCS_NORESIZE | CCS_NODIVIDER,
1448 0,
1449 0,
1450 0,
1451 0,
1452 This->hWnd,
1453 NULL,
1454 hExplorerInstance,
1455 NULL);
1456
1457 if (This->hWndToolbar != NULL)
1458 {
1459 HMODULE hShell32;
1460 SIZE BtnSize;
1461
1462 SetWindowTheme(This->hWndToolbar, L"TaskBand", NULL);
1463 TaskSwitchWnd_UpdateTheme(This);
1464 /* Identify the version we're using */
1465 SendMessage(This->hWndToolbar,
1466 TB_BUTTONSTRUCTSIZE,
1467 sizeof(TBBUTTON),
1468 0);
1469
1470 This->TaskIcons = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1000);
1471 SendMessage(This->hWndToolbar, TB_SETIMAGELIST, 0, (LPARAM)This->TaskIcons);
1472
1473 /* Calculate the default button size. Don't save this in This->ButtonSize.cx so that
1474 the actual button width gets updated correctly on the first recalculation */
1475 BtnSize.cx = GetSystemMetrics(SM_CXMINIMIZED);
1476 This->ButtonSize.cy = BtnSize.cy = GetSystemMetrics(SM_CYSIZE) + (2 * GetSystemMetrics(SM_CYEDGE));
1477 SendMessage(This->hWndToolbar,
1478 TB_SETBUTTONSIZE,
1479 0,
1480 (LPARAM)MAKELONG(BtnSize.cx,
1481 BtnSize.cy));
1482
1483 /* We don't want to see partially clipped buttons...not that we could see them... */
1484 #if 0
1485 SendMessage(This->hWndToolbar,
1486 TB_SETEXTENDEDSTYLE,
1487 0,
1488 TBSTYLE_EX_HIDECLIPPEDBUTTONS);
1489 #endif
1490
1491 /* Set proper spacing between buttons */
1492 TaskSwitchWnd_UpdateTbButtonSpacing(This,
1493 ITrayWindow_IsHorizontal(This->Tray),
1494 0,
1495 0);
1496
1497 /* Register the shell hook */
1498 This->ShellHookMsg = RegisterWindowMessage(TEXT("SHELLHOOK"));
1499 hShell32 = GetModuleHandle(TEXT("SHELL32.DLL"));
1500 if (hShell32 != NULL)
1501 {
1502 REGSHELLHOOK RegShellHook;
1503
1504 /* RegisterShellHook */
1505 RegShellHook = (REGSHELLHOOK)GetProcAddress(hShell32,
1506 (LPCSTR)((LONG)181));
1507 if (RegShellHook != NULL)
1508 {
1509 RegShellHook(This->hWnd,
1510 3); /* 1 if no NT! We're targeting NT so we don't care! */
1511 }
1512 }
1513
1514 /* Add all windows to the toolbar */
1515 EnumWindows(TaskSwitchWnd_EnumWindowsProc,
1516 (LPARAM)This);
1517
1518 /* Recalculate the button size */
1519 TaskSwitchWnd_UpdateButtonsSize(This,
1520 FALSE);
1521
1522 /* Subclass the toolbar control because it doesn't provide a
1523 NM_NCHITTEST notification */
1524 This->IsToolbarSubclassed = SetWindowSubclass(This->hWndToolbar,
1525 TaskSwitchWnd_ToolbarSubclassedProc,
1526 TSW_TOOLBAR_SUBCLASS_ID,
1527 (DWORD_PTR)This);
1528 }
1529 }
1530
1531 static VOID
1532 TaskSwitchWnd_NCDestroy(IN OUT PTASK_SWITCH_WND This)
1533 {
1534 HMODULE hShell32;
1535
1536 This->IsDestroying = TRUE;
1537
1538 /* Unregister the shell hook */
1539 hShell32 = GetModuleHandle(TEXT("SHELL32.DLL"));
1540 if (hShell32 != NULL)
1541 {
1542 REGSHELLHOOK RegShellHook;
1543
1544 /* RegisterShellHook */
1545 RegShellHook = (REGSHELLHOOK)GetProcAddress(hShell32,
1546 (LPCSTR)((LONG)181));
1547 if (RegShellHook != NULL)
1548 {
1549 RegShellHook(This->hWnd,
1550 FALSE);
1551 }
1552 }
1553
1554 CloseThemeData(This->TaskBandTheme);
1555 TaskSwitchWnd_DeleteAllTasks(This);
1556 }
1557
1558 static BOOL
1559 TaskSwitchWnd_HandleAppCommand(IN OUT PTASK_SWITCH_WND This,
1560 IN WPARAM wParam,
1561 IN LPARAM lParam)
1562 {
1563 BOOL Ret = FALSE;
1564
1565 switch (GET_APPCOMMAND_LPARAM(lParam))
1566 {
1567 case APPCOMMAND_BROWSER_SEARCH:
1568 Ret = SHFindFiles(NULL,
1569 NULL);
1570 break;
1571
1572 case APPCOMMAND_BROWSER_HOME:
1573 case APPCOMMAND_LAUNCH_MAIL:
1574 default:
1575 TRACE("Shell app command %d unhandled!\n", (INT)GET_APPCOMMAND_LPARAM(lParam));
1576 break;
1577 }
1578
1579 return Ret;
1580 }
1581
1582
1583 static LRESULT
1584 TaskSwitchWnd_HandleShellHookMsg(IN OUT PTASK_SWITCH_WND This,
1585 IN WPARAM wParam,
1586 IN LPARAM lParam)
1587 {
1588 BOOL Ret = FALSE;
1589
1590 switch ((INT)wParam)
1591 {
1592 case HSHELL_APPCOMMAND:
1593 TaskSwitchWnd_HandleAppCommand(This,
1594 wParam,
1595 lParam);
1596 Ret = TRUE;
1597 break;
1598
1599 case HSHELL_WINDOWCREATED:
1600 Ret = TaskSwitchWnd_AddTask(This,
1601 (HWND)lParam);
1602 break;
1603
1604 case HSHELL_WINDOWDESTROYED:
1605 /* The window still exists! Delay destroying it a bit */
1606 TaskSwitchWnd_DeleteTask(This, (HWND)lParam);
1607 Ret = TRUE;
1608 break;
1609
1610 case HSHELL_RUDEAPPACTIVATED:
1611 case HSHELL_WINDOWACTIVATED:
1612 if (lParam)
1613 {
1614 TaskSwitchWnd_ActivateTask(This, (HWND) lParam);
1615 Ret = TRUE;
1616 }
1617 break;
1618
1619 case HSHELL_GETMINRECT:
1620 goto UnhandledShellMessage;
1621
1622 case HSHELL_FLASH:
1623 TaskSwitchWnd_FlashTask(This,
1624 (HWND)lParam);
1625 Ret = TRUE;
1626 break;
1627
1628 case HSHELL_REDRAW:
1629 TaskSwitchWnd_RedrawTask(This,
1630 (HWND)lParam);
1631 Ret = TRUE;
1632 break;
1633
1634 case HSHELL_TASKMAN:
1635 PostMessage(ITrayWindow_GetHWND(This->Tray), TWM_OPENSTARTMENU,0, 0);
1636 break;
1637
1638 case HSHELL_ACTIVATESHELLWINDOW:
1639 case HSHELL_LANGUAGE:
1640 case HSHELL_SYSMENU:
1641 case HSHELL_ENDTASK:
1642 case HSHELL_ACCESSIBILITYSTATE:
1643 case HSHELL_WINDOWREPLACED:
1644 case HSHELL_WINDOWREPLACING:
1645 default:
1646 {
1647 static const struct {
1648 INT msg;
1649 LPCWSTR msg_name;
1650 } hshell_msg[] = {
1651 {HSHELL_WINDOWCREATED, L"HSHELL_WINDOWCREATED"},
1652 {HSHELL_WINDOWDESTROYED, L"HSHELL_WINDOWDESTROYED"},
1653 {HSHELL_ACTIVATESHELLWINDOW, L"HSHELL_ACTIVATESHELLWINDOW"},
1654 {HSHELL_WINDOWACTIVATED, L"HSHELL_WINDOWACTIVATED"},
1655 {HSHELL_GETMINRECT, L"HSHELL_GETMINRECT"},
1656 {HSHELL_REDRAW, L"HSHELL_REDRAW"},
1657 {HSHELL_TASKMAN, L"HSHELL_TASKMAN"},
1658 {HSHELL_LANGUAGE, L"HSHELL_LANGUAGE"},
1659 {HSHELL_SYSMENU, L"HSHELL_SYSMENU"},
1660 {HSHELL_ENDTASK, L"HSHELL_ENDTASK"},
1661 {HSHELL_ACCESSIBILITYSTATE, L"HSHELL_ACCESSIBILITYSTATE"},
1662 {HSHELL_APPCOMMAND, L"HSHELL_APPCOMMAND"},
1663 {HSHELL_WINDOWREPLACED, L"HSHELL_WINDOWREPLACED"},
1664 {HSHELL_WINDOWREPLACING, L"HSHELL_WINDOWREPLACING"},
1665 {HSHELL_RUDEAPPACTIVATED, L"HSHELL_RUDEAPPACTIVATED"},
1666 };
1667 int i, found;
1668 UnhandledShellMessage:
1669 for (i = 0, found = 0; i != sizeof(hshell_msg) / sizeof(hshell_msg[0]); i++)
1670 {
1671 if (hshell_msg[i].msg == (INT)wParam)
1672 {
1673 TRACE("Shell message %ws unhandled (lParam = 0x%p)!\n", hshell_msg[i].msg_name, lParam);
1674 found = 1;
1675 break;
1676 }
1677 }
1678 if (!found)
1679 {
1680 TRACE("Shell message %d unhandled (lParam = 0x%p)!\n", (INT)wParam, lParam);
1681 }
1682 break;
1683 }
1684 }
1685
1686 return Ret;
1687 }
1688
1689 static VOID
1690 TaskSwitchWnd_EnableGrouping(IN OUT PTASK_SWITCH_WND This,
1691 IN BOOL bEnable)
1692 {
1693 This->IsGroupingEnabled = bEnable;
1694
1695 /* Collapse or expand groups if neccessary */
1696 TaskSwitchWnd_UpdateButtonsSize(This,
1697 FALSE);
1698 }
1699
1700 static VOID
1701 TaskSwitchWnd_HandleTaskItemClick(IN OUT PTASK_SWITCH_WND This,
1702 IN OUT PTASK_ITEM TaskItem)
1703 {
1704 BOOL bIsMinimized;
1705 BOOL bIsActive;
1706
1707 if (IsWindow(TaskItem->hWnd))
1708 {
1709 bIsMinimized = IsIconic(TaskItem->hWnd);
1710 bIsActive = (TaskItem == This->ActiveTaskItem);
1711
1712 TRACE("Active TaskItem %p, selected TaskItem %p\n", This->ActiveTaskItem, TaskItem);
1713 if (This->ActiveTaskItem)
1714 TRACE("Active TaskItem hWnd=%p, TaskItem hWnd %p\n", This->ActiveTaskItem->hWnd, TaskItem->hWnd);
1715
1716 TRACE("Valid button clicked. HWND=%p, IsMinimized=%s, IsActive=%s...\n",
1717 TaskItem->hWnd, bIsMinimized ? "Yes" : "No", bIsActive ? "Yes" : "No");
1718
1719 if (!bIsMinimized && bIsActive)
1720 {
1721 PostMessage(TaskItem->hWnd,
1722 WM_SYSCOMMAND,
1723 SC_MINIMIZE,
1724 0);
1725 TRACE("Valid button clicked. App window Minimized.\n");
1726 }
1727 else
1728 {
1729 if (bIsMinimized)
1730 {
1731 PostMessage(TaskItem->hWnd,
1732 WM_SYSCOMMAND,
1733 SC_RESTORE,
1734 0);
1735 TRACE("Valid button clicked. App window Restored.\n");
1736 }
1737
1738 SetForegroundWindow(TaskItem->hWnd);
1739 TRACE("Valid button clicked. App window Activated.\n");
1740 }
1741 }
1742 }
1743
1744 static VOID
1745 TaskSwitchWnd_HandleTaskGroupClick(IN OUT PTASK_SWITCH_WND This,
1746 IN OUT PTASK_GROUP TaskGroup)
1747 {
1748 /* TODO: Show task group menu */
1749 }
1750
1751 static BOOL
1752 TaskSwitchWnd_HandleButtonClick(IN OUT PTASK_SWITCH_WND This,
1753 IN WORD wIndex)
1754 {
1755 PTASK_ITEM TaskItem;
1756 PTASK_GROUP TaskGroup;
1757
1758 if (This->IsGroupingEnabled)
1759 {
1760 TaskGroup = FindTaskGroupByIndex(This,
1761 (INT)wIndex);
1762 if (TaskGroup != NULL && TaskGroup->IsCollapsed)
1763 {
1764 TaskSwitchWnd_HandleTaskGroupClick(This,
1765 TaskGroup);
1766 return TRUE;
1767 }
1768 }
1769
1770 TaskItem = FindTaskItemByIndex(This,
1771 (INT)wIndex);
1772 if (TaskItem != NULL)
1773 {
1774 TaskSwitchWnd_HandleTaskItemClick(This,
1775 TaskItem);
1776 return TRUE;
1777 }
1778
1779 return FALSE;
1780 }
1781
1782
1783 static VOID
1784 TaskSwitchWnd_HandleTaskItemRightClick(IN OUT PTASK_SWITCH_WND This,
1785 IN OUT PTASK_ITEM TaskItem)
1786 {
1787
1788 HMENU hmenu = GetSystemMenu(TaskItem->hWnd, FALSE);
1789
1790 if (hmenu) {
1791 POINT pt;
1792 int cmd;
1793 GetCursorPos(&pt);
1794 cmd = TrackPopupMenu(hmenu, TPM_LEFTBUTTON|TPM_RIGHTBUTTON|TPM_RETURNCMD, pt.x, pt.y, 0, This->hWndToolbar, NULL);
1795 if (cmd) {
1796 SetForegroundWindow(TaskItem->hWnd); // reactivate window after the context menu has closed
1797 PostMessage(TaskItem->hWnd, WM_SYSCOMMAND, cmd, 0);
1798 }
1799 }
1800 }
1801
1802 static VOID
1803 TaskSwitchWnd_HandleTaskGroupRightClick(IN OUT PTASK_SWITCH_WND This,
1804 IN OUT PTASK_GROUP TaskGroup)
1805 {
1806 /* TODO: Show task group right click menu */
1807 }
1808
1809 static BOOL
1810 TaskSwitchWnd_HandleButtonRightClick(IN OUT PTASK_SWITCH_WND This,
1811 IN WORD wIndex)
1812 {
1813 PTASK_ITEM TaskItem;
1814 PTASK_GROUP TaskGroup;
1815 if (This->IsGroupingEnabled)
1816 {
1817 TaskGroup = FindTaskGroupByIndex(This,
1818 (INT)wIndex);
1819 if (TaskGroup != NULL && TaskGroup->IsCollapsed)
1820 {
1821 TaskSwitchWnd_HandleTaskGroupRightClick(This,
1822 TaskGroup);
1823 return TRUE;
1824 }
1825 }
1826
1827 TaskItem = FindTaskItemByIndex(This,
1828 (INT)wIndex);
1829
1830 if (TaskItem != NULL)
1831 {
1832 TaskSwitchWnd_HandleTaskItemRightClick(This,
1833 TaskItem);
1834 return TRUE;
1835 }
1836
1837 return FALSE;
1838 }
1839
1840
1841 static LRESULT
1842 TaskSwitchWnd_HandleItemPaint(IN OUT PTASK_SWITCH_WND This,
1843 IN OUT NMTBCUSTOMDRAW *nmtbcd)
1844 {
1845 LRESULT Ret = CDRF_DODEFAULT;
1846 PTASK_GROUP TaskGroup;
1847 PTASK_ITEM TaskItem;
1848
1849 TaskItem = FindTaskItemByIndex(This,
1850 (INT)nmtbcd->nmcd.dwItemSpec);
1851 TaskGroup = FindTaskGroupByIndex(This,
1852 (INT)nmtbcd->nmcd.dwItemSpec);
1853 if (TaskGroup == NULL && TaskItem != NULL)
1854 {
1855 ASSERT(TaskItem != NULL);
1856
1857 if (TaskItem != NULL && IsWindow(TaskItem->hWnd))
1858 {
1859 /* Make the entire button flashing if neccessary */
1860 if (nmtbcd->nmcd.uItemState & CDIS_MARKED)
1861 {
1862 Ret = TBCDRF_NOBACKGROUND;
1863 if (!This->TaskBandTheme)
1864 {
1865 SelectObject(nmtbcd->nmcd.hdc, GetSysColorBrush(COLOR_HIGHLIGHT));
1866 Rectangle(nmtbcd->nmcd.hdc,
1867 nmtbcd->nmcd.rc.left,
1868 nmtbcd->nmcd.rc.top,
1869 nmtbcd->nmcd.rc.right,
1870 nmtbcd->nmcd.rc.bottom);
1871 }
1872 else
1873 {
1874 DrawThemeBackground(This->TaskBandTheme, nmtbcd->nmcd.hdc, TDP_FLASHBUTTON, 0, &nmtbcd->nmcd.rc, 0);
1875 }
1876 nmtbcd->clrText = GetSysColor(COLOR_HIGHLIGHTTEXT);
1877 return Ret;
1878 }
1879 }
1880 }
1881 else if (TaskGroup != NULL)
1882 {
1883 /* FIXME: Implement painting for task groups */
1884 }
1885 return Ret;
1886 }
1887
1888 static LRESULT
1889 TaskSwitchWnd_HandleToolbarNotification(IN OUT PTASK_SWITCH_WND This,
1890 IN const NMHDR *nmh)
1891 {
1892 LRESULT Ret = 0;
1893
1894 switch (nmh->code)
1895 {
1896 case NM_CUSTOMDRAW:
1897 {
1898 LPNMTBCUSTOMDRAW nmtbcd = (LPNMTBCUSTOMDRAW)nmh;
1899
1900 switch (nmtbcd->nmcd.dwDrawStage)
1901 {
1902
1903 case CDDS_ITEMPREPAINT:
1904 Ret = TaskSwitchWnd_HandleItemPaint(This,
1905 nmtbcd);
1906 break;
1907
1908 case CDDS_PREPAINT:
1909 Ret = CDRF_NOTIFYITEMDRAW;
1910 break;
1911
1912 default:
1913 Ret = CDRF_DODEFAULT;
1914 break;
1915 }
1916 break;
1917 }
1918 }
1919
1920 return Ret;
1921 }
1922
1923 static VOID
1924 TaskSwitchWnd_DrawBackground(HWND hwnd,
1925 HDC hdc)
1926 {
1927 RECT rect;
1928
1929 GetClientRect(hwnd, &rect);
1930 DrawThemeParentBackground(hwnd, hdc, &rect);
1931 }
1932
1933 static LRESULT CALLBACK
1934 TaskSwitchWndProc(IN HWND hwnd,
1935 IN UINT uMsg,
1936 IN WPARAM wParam,
1937 IN LPARAM lParam)
1938 {
1939 PTASK_SWITCH_WND This = NULL;
1940 LRESULT Ret = FALSE;
1941
1942 if (uMsg != WM_NCCREATE)
1943 {
1944 This = (PTASK_SWITCH_WND)GetWindowLongPtr(hwnd, 0);
1945 }
1946
1947 if (This != NULL || uMsg == WM_NCCREATE)
1948 {
1949 switch (uMsg)
1950 {
1951 case WM_THEMECHANGED:
1952 TaskSwitchWnd_UpdateTheme(This);
1953 break;
1954 case WM_ERASEBKGND:
1955 if (!This->TaskBandTheme)
1956 break;
1957 TaskSwitchWnd_DrawBackground(hwnd, (HDC) wParam);
1958 return TRUE;
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, FALSE);
1976 }
1977 break;
1978 }
1979
1980 case WM_NCHITTEST:
1981 {
1982 /* We want the tray window to be draggable everywhere, so make the control
1983 appear transparent */
1984 Ret = DefWindowProc(hwnd, uMsg, wParam, lParam);
1985 if (Ret != HTVSCROLL && Ret != HTHSCROLL)
1986 return HTTRANSPARENT;
1987 return Ret;
1988 }
1989
1990 case WM_COMMAND:
1991 {
1992 if (lParam != 0 && (HWND)lParam == This->hWndToolbar)
1993 {
1994 TaskSwitchWnd_HandleButtonClick(This, LOWORD(wParam));
1995 }
1996 break;
1997 }
1998
1999 case WM_NOTIFY:
2000 {
2001 const NMHDR *nmh = (const NMHDR *)lParam;
2002
2003 if (nmh->hwndFrom == This->hWndToolbar)
2004 {
2005 return TaskSwitchWnd_HandleToolbarNotification(This, nmh);
2006 }
2007 return 0;
2008 }
2009
2010 case TSWM_ENABLEGROUPING:
2011 {
2012 Ret = This->IsGroupingEnabled;
2013 if (wParam != This->IsGroupingEnabled)
2014 {
2015 TaskSwitchWnd_EnableGrouping(This,
2016 (BOOL)wParam);
2017 }
2018 return Ret;
2019 }
2020
2021 case TSWM_UPDATETASKBARPOS:
2022 {
2023 /* Update the button spacing */
2024 TaskSwitchWnd_UpdateTbButtonSpacing(This, ITrayWindow_IsHorizontal(This->Tray), 0, 0);
2025 break;
2026 }
2027
2028 case WM_CONTEXTMENU:
2029 {
2030 if (This->hWndToolbar != NULL)
2031 {
2032 POINT pt;
2033 INT_PTR iBtn;
2034
2035 pt.x = (LONG)LOWORD(lParam);
2036 pt.y = (LONG)HIWORD(lParam);
2037
2038 MapWindowPoints(NULL, This->hWndToolbar, &pt, 1);
2039
2040 iBtn = (INT_PTR) SendMessage(This->hWndToolbar, TB_HITTEST, 0, (LPARAM) &pt);
2041 if (iBtn >= 0)
2042 {
2043 TaskSwitchWnd_HandleButtonRightClick(This, iBtn);
2044 }
2045 else
2046 goto ForwardContextMenuMsg;
2047 }
2048 else
2049 {
2050 ForwardContextMenuMsg:
2051 /* Forward message */
2052 Ret = SendMessage(ITrayWindow_GetHWND(This->Tray), uMsg, wParam, lParam);
2053 }
2054 break;
2055 }
2056
2057 case WM_NCCREATE:
2058 {
2059 LPCREATESTRUCT CreateStruct = (LPCREATESTRUCT)lParam;
2060 This = HeapAlloc(hProcessHeap,
2061 HEAP_ZERO_MEMORY,
2062 sizeof(*This));
2063 if (This == NULL)
2064 return FALSE;
2065
2066 This->hWnd = hwnd;
2067 This->hWndNotify = CreateStruct->hwndParent;
2068 This->Tray = (ITrayWindow*)CreateStruct->lpCreateParams;
2069 This->IsGroupingEnabled = TRUE; /* FIXME */
2070 SetWindowLongPtr(hwnd,
2071 0,
2072 (LONG_PTR)This);
2073
2074 return TRUE;
2075 }
2076
2077 case WM_CREATE:
2078 TaskSwitchWnd_Create(This);
2079
2080 #if DUMP_TASKS != 0
2081 SetTimer(hwnd, 1, 5000, NULL);
2082 #endif
2083
2084 break;
2085
2086 case WM_DESTROY:
2087 if (This->IsToolbarSubclassed)
2088 {
2089 if (RemoveWindowSubclass(This->hWndToolbar,
2090 TaskSwitchWnd_ToolbarSubclassedProc,
2091 TSW_TOOLBAR_SUBCLASS_ID))
2092 {
2093 This->IsToolbarSubclassed = FALSE;
2094 }
2095 }
2096 break;
2097
2098 case WM_NCDESTROY:
2099 TaskSwitchWnd_NCDestroy(This);
2100 HeapFree(hProcessHeap, 0, This);
2101 SetWindowLongPtr(hwnd, 0, 0);
2102 break;
2103
2104 #if DUMP_TASKS != 0
2105 case WM_TIMER:
2106 switch (wParam)
2107 {
2108 case 1:
2109 TaskSwitchWnd_DumpTasks(This);
2110 break;
2111 }
2112 break;
2113 #endif
2114
2115 case WM_KLUDGEMINRECT:
2116 {
2117 PTASK_ITEM TaskItem = TaskSwitchWnd_FindTaskItem(This, (HWND)wParam);
2118 if (TaskItem)
2119 {
2120 RECT* prcMinRect = (RECT*)lParam;
2121 RECT rcItem, rcToolbar;
2122 SendMessageW(This->hWndToolbar,TB_GETITEMRECT, TaskItem->Index, (LPARAM)&rcItem);
2123 GetWindowRect(This->hWndToolbar, &rcToolbar);
2124
2125 OffsetRect(&rcItem, rcToolbar.left, rcToolbar.top);
2126
2127 *prcMinRect = rcItem;
2128 return TRUE;
2129 }
2130 return FALSE;
2131 }
2132
2133 default:
2134 /* HandleDefaultMessage: */
2135 if (uMsg == This->ShellHookMsg && This->ShellHookMsg != 0)
2136 {
2137 /* Process shell messages */
2138 return (LRESULT) TaskSwitchWnd_HandleShellHookMsg(This, wParam, lParam);
2139 }
2140
2141 break;
2142 }
2143 }
2144
2145 return DefWindowProc(hwnd, uMsg, wParam, lParam);
2146 }
2147
2148
2149 HWND
2150 CreateTaskSwitchWnd(IN HWND hWndParent,
2151 IN OUT ITrayWindow *Tray)
2152 {
2153 HWND hwndTaskBar;
2154
2155 hwndTaskBar = CreateWindowEx(0,
2156 szTaskSwitchWndClass,
2157 szRunningApps,
2158 WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP,
2159 0,
2160 0,
2161 0,
2162 0,
2163 hWndParent,
2164 NULL,
2165 hExplorerInstance,
2166 (LPVOID)Tray);
2167
2168 return hwndTaskBar;
2169 }
2170
2171 BOOL
2172 RegisterTaskSwitchWndClass(VOID)
2173 {
2174 WNDCLASS wc;
2175
2176 wc.style = CS_DBLCLKS;
2177 wc.lpfnWndProc = TaskSwitchWndProc;
2178 wc.cbClsExtra = 0;
2179 wc.cbWndExtra = sizeof(PTASK_SWITCH_WND);
2180 wc.hInstance = hExplorerInstance;
2181 wc.hIcon = NULL;
2182 wc.hCursor = LoadCursor(NULL,
2183 IDC_ARROW);
2184 wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
2185 wc.lpszMenuName = NULL;
2186 wc.lpszClassName = szTaskSwitchWndClass;
2187
2188 return RegisterClass(&wc) != 0;
2189 }
2190
2191 VOID
2192 UnregisterTaskSwitchWndClass(VOID)
2193 {
2194 UnregisterClass(szTaskSwitchWndClass,
2195 hExplorerInstance);
2196 }