Branching for 0.3.15 release after two days of no response from a certain sphere...
[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 DbgPrint("Tasks dump:\n");
146 if (This->IsGroupingEnabled)
147 {
148 CurrentGroup = This->TaskGroups;
149 while (CurrentGroup != NULL)
150 {
151 DbgPrint("- 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 DbgPrint(" + 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 DbgPrint("- 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 DbgPrint("- 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 DbgPrint("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 DbgPrint("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 DbgPrint("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 ActiveTaskItem;
938 PTASK_GROUP TaskGroup = NULL;
939
940 ActiveTaskItem = 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 (ActiveTaskItem != NULL)
954 {
955 PTASK_GROUP ActiveTaskGroup;
956
957 if (ActiveTaskItem == TaskItem)
958 return;
959
960 ActiveTaskGroup = ActiveTaskItem->Group;
961
962 if (This->IsGroupingEnabled &&
963 ActiveTaskGroup != NULL &&
964 ActiveTaskGroup->IsCollapsed)
965 {
966 if (ActiveTaskGroup == TaskGroup)
967 return;
968
969 /* FIXME */
970 }
971 else
972 {
973 This->ActiveTaskItem = NULL;
974 if (ActiveTaskItem->Index >= 0)
975 {
976 TaskSwitchWnd_UpdateTaskItemButton(This,
977 ActiveTaskItem);
978 }
979 }
980 }
981
982 This->ActiveTaskItem = TaskItem;
983
984 if (TaskItem != NULL && TaskItem->Index >= 0)
985 {
986 TaskSwitchWnd_UpdateTaskItemButton(This,
987 TaskItem);
988 }
989 }
990
991 static PTASK_ITEM
992 FindTaskItemByIndex(IN OUT PTASK_SWITCH_WND This,
993 IN INT Index)
994 {
995 PTASK_ITEM TaskItem, LastItem;
996
997 TaskItem = This->TaskItems;
998 LastItem = TaskItem + This->TaskItemCount;
999 while (TaskItem != LastItem)
1000 {
1001 if (TaskItem->Index == Index)
1002 return TaskItem;
1003
1004 TaskItem++;
1005 }
1006
1007 return NULL;
1008 }
1009
1010 static PTASK_GROUP
1011 FindTaskGroupByIndex(IN OUT PTASK_SWITCH_WND This,
1012 IN INT Index)
1013 {
1014 PTASK_GROUP CurrentGroup;
1015
1016 CurrentGroup = This->TaskGroups;
1017 while (CurrentGroup != NULL)
1018 {
1019 if (CurrentGroup->Index == Index)
1020 break;
1021
1022 CurrentGroup = CurrentGroup->Next;
1023 }
1024
1025 return CurrentGroup;
1026 }
1027
1028 static BOOL
1029 TaskSwitchWnd_AddTask(IN OUT PTASK_SWITCH_WND This,
1030 IN HWND hWnd)
1031 {
1032 PTASK_ITEM TaskItem;
1033
1034 TaskItem = TaskSwitchWnd_FindTaskItem(This,
1035 hWnd);
1036 if (TaskItem == NULL)
1037 {
1038 DbgPrint("Add window 0x%p\n", hWnd);
1039 TaskItem = TaskSwitchWnd_AllocTaskItem(This);
1040 if (TaskItem != NULL)
1041 {
1042 ZeroMemory(TaskItem,
1043 sizeof(*TaskItem));
1044 TaskItem->hWnd = hWnd;
1045 TaskItem->Index = -1;
1046 TaskItem->Group = TaskSwitchWnd_AddToTaskGroup(This,
1047 hWnd);
1048
1049 if (!This->IsDestroying)
1050 {
1051 TaskSwitchWnd_AddTaskItemButton(This,
1052 TaskItem);
1053 }
1054 }
1055 }
1056
1057 return TaskItem != NULL;
1058 }
1059
1060 static BOOL
1061 TaskSwitchWnd_ActivateTaskItem(IN OUT PTASK_SWITCH_WND This,
1062 IN OUT PTASK_ITEM TaskItem OPTIONAL)
1063 {
1064 if (TaskItem != NULL)
1065 {
1066 DbgPrint("Activate window 0x%p on button %d\n", TaskItem->hWnd, TaskItem->Index);
1067 }
1068
1069 TaskSwitchWnd_CheckActivateTaskItem(This,
1070 TaskItem);
1071
1072 return FALSE;
1073 }
1074
1075 static BOOL
1076 TaskSwitchWnd_ActivateTask(IN OUT PTASK_SWITCH_WND This,
1077 IN HWND hWnd)
1078 {
1079 PTASK_ITEM TaskItem;
1080
1081 TaskItem = TaskSwitchWnd_FindTaskItem(This,
1082 hWnd);
1083 if (TaskItem == NULL)
1084 {
1085 TaskItem = TaskSwitchWnd_FindOtherTaskItem(This,
1086 hWnd);
1087 }
1088
1089 if (TaskItem == NULL)
1090 {
1091 DbgPrint("Activate window 0x%p, could not find task\n", hWnd);
1092 }
1093
1094 return TaskSwitchWnd_ActivateTaskItem(This,
1095 TaskItem);
1096 }
1097
1098 static BOOL
1099 TaskSwitchWnd_DeleteTask(IN OUT PTASK_SWITCH_WND This,
1100 IN HWND hWnd)
1101 {
1102 PTASK_ITEM TaskItem;
1103
1104 TaskItem = TaskSwitchWnd_FindTaskItem(This,
1105 hWnd);
1106 if (TaskItem != NULL)
1107 {
1108 DbgPrint("Delete window 0x%p on button %d\n", hWnd, TaskItem->Index);
1109 TaskSwitchWnd_DeleteTaskItem(This,
1110 TaskItem);
1111 return TRUE;
1112 }
1113 //else
1114 //DbgPrint("Failed to delete window 0x%p\n", hWnd);
1115
1116 return FALSE;
1117 }
1118
1119 static VOID
1120 TaskSwitchWnd_DeleteAllTasks(IN OUT PTASK_SWITCH_WND This)
1121 {
1122 PTASK_ITEM CurrentTask;
1123
1124 if (This->TaskItemCount > 0)
1125 {
1126 CurrentTask = This->TaskItems + This->TaskItemCount;
1127 do
1128 {
1129 TaskSwitchWnd_DeleteTaskItem(This,
1130 --CurrentTask);
1131 } while (CurrentTask != This->TaskItems);
1132 }
1133 }
1134
1135 static VOID
1136 TaskSwitchWnd_FlashTaskItem(IN OUT PTASK_SWITCH_WND This,
1137 IN OUT PTASK_ITEM TaskItem)
1138 {
1139 TaskItem->RenderFlashed = 1;
1140 TaskSwitchWnd_UpdateTaskItemButton(This,
1141 TaskItem);
1142 }
1143
1144 static BOOL
1145 TaskSwitchWnd_FlashTask(IN OUT PTASK_SWITCH_WND This,
1146 IN HWND hWnd)
1147 {
1148 PTASK_ITEM TaskItem;
1149
1150 TaskItem = TaskSwitchWnd_FindTaskItem(This,
1151 hWnd);
1152 if (TaskItem != NULL)
1153 {
1154 DbgPrint("Flashing window 0x%p on button %d\n", hWnd, TaskItem->Index);
1155 TaskSwitchWnd_FlashTaskItem(This,
1156 TaskItem);
1157 return TRUE;
1158 }
1159
1160 return FALSE;
1161 }
1162
1163 static VOID
1164 TaskSwitchWnd_RedrawTaskItem(IN OUT PTASK_SWITCH_WND This,
1165 IN OUT PTASK_ITEM TaskItem)
1166 {
1167 PTASK_GROUP TaskGroup;
1168
1169 TaskGroup = TaskItem->Group;
1170 if (This->IsGroupingEnabled && TaskGroup != NULL)
1171 {
1172 if (TaskGroup->IsCollapsed && TaskGroup->Index >= 0)
1173 {
1174 TaskSwitchWnd_UpdateTaskGroupButton(This,
1175 TaskGroup);
1176 }
1177 else if (TaskItem->Index >= 0)
1178 {
1179 goto UpdateTaskItem;
1180 }
1181 }
1182 else if (TaskItem->Index >= 0)
1183 {
1184 UpdateTaskItem:
1185 TaskItem->RenderFlashed = 0;
1186 TaskSwitchWnd_UpdateTaskItemButton(This,
1187 TaskItem);
1188 }
1189 }
1190
1191
1192 static BOOL
1193 TaskSwitchWnd_RedrawTask(IN OUT PTASK_SWITCH_WND This,
1194 IN HWND hWnd)
1195 {
1196 PTASK_ITEM TaskItem;
1197
1198 TaskItem = TaskSwitchWnd_FindTaskItem(This,
1199 hWnd);
1200 if (TaskItem != NULL)
1201 {
1202 TaskSwitchWnd_RedrawTaskItem(This,
1203 TaskItem);
1204 return TRUE;
1205 }
1206
1207 return FALSE;
1208 }
1209
1210 static INT
1211 TaskSwitchWnd_UpdateTbButtonSpacing(IN OUT PTASK_SWITCH_WND This,
1212 IN BOOL bHorizontal,
1213 IN UINT uiRows,
1214 IN UINT uiBtnsPerLine)
1215 {
1216 TBMETRICS tbm;
1217
1218 tbm.cbSize = sizeof(tbm);
1219 tbm.dwMask = TBMF_BARPAD | TBMF_BUTTONSPACING;
1220
1221 tbm.cxBarPad = tbm.cyBarPad = 0;
1222
1223 if (bHorizontal || uiBtnsPerLine > 1)
1224 tbm.cxButtonSpacing = (3 * GetSystemMetrics(SM_CXEDGE) / 2);
1225 else
1226 tbm.cxButtonSpacing = 0;
1227
1228 if (!bHorizontal || uiRows > 1)
1229 tbm.cyButtonSpacing = (3 * GetSystemMetrics(SM_CYEDGE) / 2);
1230 else
1231 tbm.cyButtonSpacing = 0;
1232
1233 SendMessage(This->hWndToolbar,
1234 TB_SETMETRICS,
1235 0,
1236 (LPARAM)&tbm);
1237
1238 return tbm.cxButtonSpacing;
1239 }
1240
1241
1242 static VOID
1243 TaskSwitchWnd_UpdateButtonsSize(IN OUT PTASK_SWITCH_WND This,
1244 IN BOOL bRedrawDisabled)
1245 {
1246 RECT rcClient;
1247 UINT uiRows, uiMax, uiMin, uiBtnsPerLine, ui;
1248 LONG NewBtnSize;
1249 BOOL Horizontal;
1250 TBBUTTONINFO tbbi;
1251 TBMETRICS tbm;
1252
1253 if (GetClientRect(This->hWnd,
1254 &rcClient) &&
1255 !IsRectEmpty(&rcClient))
1256 {
1257 if (This->ToolbarBtnCount > 0)
1258 {
1259 ZeroMemory (&tbm, sizeof (tbm));
1260 tbm.cbSize = sizeof(tbm);
1261 tbm.dwMask = TBMF_BUTTONSPACING;
1262 SendMessage(This->hWndToolbar,
1263 TB_GETMETRICS,
1264 0,
1265 (LPARAM)&tbm);
1266
1267 uiRows = (rcClient.bottom + tbm.cyButtonSpacing) / (This->ButtonSize.cy + tbm.cyButtonSpacing);
1268 if (uiRows == 0)
1269 uiRows = 1;
1270
1271 uiBtnsPerLine = (This->ToolbarBtnCount + uiRows - 1) / uiRows;
1272
1273 Horizontal = ITrayWindow_IsHorizontal(This->Tray);
1274
1275 if (!bRedrawDisabled)
1276 TaskSwitchWnd_BeginUpdate(This);
1277
1278 /* We might need to update the button spacing */
1279 tbm.cxButtonSpacing = TaskSwitchWnd_UpdateTbButtonSpacing(This,
1280 Horizontal,
1281 uiRows,
1282 uiBtnsPerLine);
1283
1284 /* Calculate the ideal width and make sure it's within the allowed range */
1285 NewBtnSize = (rcClient.right - (uiBtnsPerLine * tbm.cxButtonSpacing)) / uiBtnsPerLine;
1286
1287 /* Determine the minimum and maximum width of a button */
1288 if (Horizontal)
1289 uiMax = GetSystemMetrics(SM_CXMINIMIZED);
1290 else
1291 uiMax = rcClient.right;
1292
1293 uiMin = GetSystemMetrics(SM_CXSIZE) + (2 * GetSystemMetrics(SM_CXEDGE));
1294
1295 if (NewBtnSize < (LONG)uiMin)
1296 NewBtnSize = uiMin;
1297 if (NewBtnSize > (LONG)uiMax)
1298 NewBtnSize = uiMax;
1299
1300 This->ButtonSize.cx = NewBtnSize;
1301
1302 /* Recalculate how many buttons actually fit into one line */
1303 uiBtnsPerLine = rcClient.right / (NewBtnSize + tbm.cxButtonSpacing);
1304 if (uiBtnsPerLine == 0)
1305 uiBtnsPerLine++;
1306 This->TbButtonsPerLine = uiBtnsPerLine;
1307
1308 tbbi.cbSize = sizeof(tbbi);
1309 tbbi.dwMask = TBIF_BYINDEX | TBIF_SIZE | TBIF_STATE;
1310 tbbi.cx = (INT)NewBtnSize;
1311
1312 for (ui = 0; ui != This->ToolbarBtnCount; ui++)
1313 {
1314 tbbi.fsState = TBSTATE_ENABLED;
1315
1316 /* Check if we're updating a button that is the last one in the
1317 line. If so, we need to set the TBSTATE_WRAP flag! */
1318 if ((ui + 1) % uiBtnsPerLine == 0)
1319 tbbi.fsState |= TBSTATE_WRAP;
1320
1321 if (This->ActiveTaskItem != NULL &&
1322 This->ActiveTaskItem->Index == ui)
1323 {
1324 tbbi.fsState |= TBSTATE_CHECKED;
1325 }
1326
1327 SendMessage(This->hWndToolbar,
1328 TB_SETBUTTONINFO,
1329 (WPARAM)ui,
1330 (LPARAM)&tbbi);
1331 }
1332
1333 #if 0
1334 /* FIXME: Force the window to the correct position in case some idiot
1335 did something to us */
1336 SetWindowPos(This->hWndToolbar,
1337 NULL,
1338 0,
1339 0,
1340 rcClient.right, /* FIXME */
1341 rcClient.bottom, /* FIXME */
1342 SWP_NOACTIVATE | SWP_NOZORDER);
1343 #endif
1344 }
1345 else
1346 {
1347 This->TbButtonsPerLine = 0;
1348 This->ButtonSize.cx = 0;
1349 }
1350 }
1351
1352 TaskSwitchWnd_EndUpdate(This);
1353 }
1354
1355 static BOOL CALLBACK
1356 TaskSwitchWnd_EnumWindowsProc(IN HWND hWnd,
1357 IN LPARAM lParam)
1358 {
1359 PTASK_SWITCH_WND This = (PTASK_SWITCH_WND)lParam;
1360
1361 /* Only show windows that still exist and are visible and none of explorer's
1362 special windows (such as the desktop or the tray window) */
1363 if (IsWindow(hWnd) && IsWindowVisible(hWnd) &&
1364 !ITrayWindow_IsSpecialHWND(This->Tray,
1365 hWnd))
1366 {
1367 /* Don't list popup windows and also no tool windows */
1368 if (GetWindow(hWnd,
1369 GW_OWNER) == NULL &&
1370 !(GetWindowLong(hWnd,
1371 GWL_EXSTYLE) & WS_EX_TOOLWINDOW))
1372 {
1373 TaskSwitchWnd_AddTask(This,
1374 hWnd);
1375 }
1376 }
1377
1378 return TRUE;
1379 }
1380
1381 static LRESULT CALLBACK
1382 TaskSwichWnd_ToolbarSubclassedProc(IN HWND hWnd,
1383 IN UINT msg,
1384 IN WPARAM wParam,
1385 IN LPARAM lParam,
1386 IN UINT_PTR uIdSubclass,
1387 IN DWORD_PTR dwRefData)
1388 {
1389 LRESULT Ret;
1390
1391 Ret = DefSubclassProc(hWnd,
1392 msg,
1393 wParam,
1394 lParam);
1395
1396 if (msg == WM_NCHITTEST && Ret == HTCLIENT)
1397 {
1398 POINT pt;
1399
1400 /* See if the mouse is on a button */
1401 pt.x = (SHORT)LOWORD(lParam);
1402 pt.y = (SHORT)HIWORD(lParam);
1403
1404 if (MapWindowPoints(HWND_DESKTOP,
1405 hWnd,
1406 &pt,
1407 1) != 0 &&
1408 (INT)SendMessage(hWnd,
1409 TB_HITTEST,
1410 0,
1411 (LPARAM)&pt) < 0)
1412 {
1413 /* Make the control appear to be transparent outside of any buttons */
1414 Ret = HTTRANSPARENT;
1415 }
1416 }
1417
1418 return Ret;
1419 }
1420
1421 static VOID
1422 TaskSwitchWnd_UpdateTheme(IN OUT PTASK_SWITCH_WND This)
1423 {
1424 if (This->TaskBandTheme)
1425 CloseThemeData(This->TaskBandTheme);
1426
1427 if (IsThemeActive())
1428 This->TaskBandTheme = OpenThemeData(This->hWnd, L"TaskBand");
1429 else
1430 This->TaskBandTheme = NULL;
1431 }
1432
1433 static VOID
1434 TaskSwitchWnd_Create(IN OUT PTASK_SWITCH_WND This)
1435 {
1436 This->hWndToolbar = CreateWindowEx(0,
1437 TOOLBARCLASSNAME,
1438 szRunningApps,
1439 WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN |
1440 TBSTYLE_TOOLTIPS | TBSTYLE_WRAPABLE | TBSTYLE_LIST |
1441 TBSTYLE_TRANSPARENT |
1442 CCS_TOP | CCS_NORESIZE | CCS_NODIVIDER,
1443 0,
1444 0,
1445 0,
1446 0,
1447 This->hWnd,
1448 NULL,
1449 hExplorerInstance,
1450 NULL);
1451
1452 if (This->hWndToolbar != NULL)
1453 {
1454 HMODULE hShell32;
1455 SIZE BtnSize;
1456
1457 SetWindowTheme(This->hWndToolbar, L"TaskBand", NULL);
1458 TaskSwitchWnd_UpdateTheme(This);
1459 /* Identify the version we're using */
1460 SendMessage(This->hWndToolbar,
1461 TB_BUTTONSTRUCTSIZE,
1462 sizeof(TBBUTTON),
1463 0);
1464
1465 This->TaskIcons = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1000);
1466 SendMessage(This->hWndToolbar, TB_SETIMAGELIST, 0, (LPARAM)This->TaskIcons);
1467
1468 /* Calculate the default button size. Don't save this in This->ButtonSize.cx so that
1469 the actual button width gets updated correctly on the first recalculation */
1470 BtnSize.cx = GetSystemMetrics(SM_CXMINIMIZED);
1471 This->ButtonSize.cy = BtnSize.cy = GetSystemMetrics(SM_CYSIZE) + (2 * GetSystemMetrics(SM_CYEDGE));
1472 SendMessage(This->hWndToolbar,
1473 TB_SETBUTTONSIZE,
1474 0,
1475 (LPARAM)MAKELONG(BtnSize.cx,
1476 BtnSize.cy));
1477
1478 /* We don't want to see partially clipped buttons...not that we could see them... */
1479 #if 0
1480 SendMessage(This->hWndToolbar,
1481 TB_SETEXTENDEDSTYLE,
1482 0,
1483 TBSTYLE_EX_HIDECLIPPEDBUTTONS);
1484 #endif
1485
1486 /* Set proper spacing between buttons */
1487 TaskSwitchWnd_UpdateTbButtonSpacing(This,
1488 ITrayWindow_IsHorizontal(This->Tray),
1489 0,
1490 0);
1491
1492 /* Register the shell hook */
1493 This->ShellHookMsg = RegisterWindowMessage(TEXT("SHELLHOOK"));
1494 hShell32 = GetModuleHandle(TEXT("SHELL32.DLL"));
1495 if (hShell32 != NULL)
1496 {
1497 REGSHELLHOOK RegShellHook;
1498
1499 /* RegisterShellHook */
1500 RegShellHook = (REGSHELLHOOK)GetProcAddress(hShell32,
1501 (LPCSTR)((LONG)181));
1502 if (RegShellHook != NULL)
1503 {
1504 RegShellHook(This->hWnd,
1505 3); /* 1 if no NT! We're targeting NT so we don't care! */
1506 }
1507 }
1508
1509 /* Add all windows to the toolbar */
1510 EnumWindows(TaskSwitchWnd_EnumWindowsProc,
1511 (LPARAM)This);
1512
1513 /* Recalculate the button size */
1514 TaskSwitchWnd_UpdateButtonsSize(This,
1515 FALSE);
1516
1517 /* Subclass the toolbar control because it doesn't provide a
1518 NM_NCHITTEST notification */
1519 This->IsToolbarSubclassed = SetWindowSubclass(This->hWndToolbar,
1520 TaskSwichWnd_ToolbarSubclassedProc,
1521 TSW_TOOLBAR_SUBCLASS_ID,
1522 (DWORD_PTR)This);
1523 }
1524 }
1525
1526 static VOID
1527 TaskSwitchWnd_NCDestroy(IN OUT PTASK_SWITCH_WND This)
1528 {
1529 HMODULE hShell32;
1530
1531 This->IsDestroying = TRUE;
1532
1533 /* Unregister the shell hook */
1534 hShell32 = GetModuleHandle(TEXT("SHELL32.DLL"));
1535 if (hShell32 != NULL)
1536 {
1537 REGSHELLHOOK RegShellHook;
1538
1539 /* RegisterShellHook */
1540 RegShellHook = (REGSHELLHOOK)GetProcAddress(hShell32,
1541 (LPCSTR)((LONG)181));
1542 if (RegShellHook != NULL)
1543 {
1544 RegShellHook(This->hWnd,
1545 FALSE);
1546 }
1547 }
1548
1549 CloseThemeData(This->TaskBandTheme);
1550 TaskSwitchWnd_DeleteAllTasks(This);
1551 }
1552
1553 static BOOL
1554 TaskSwitchWnd_HandleAppCommand(IN OUT PTASK_SWITCH_WND This,
1555 IN WPARAM wParam,
1556 IN LPARAM lParam)
1557 {
1558 BOOL Ret = FALSE;
1559
1560 switch (GET_APPCOMMAND_LPARAM(lParam))
1561 {
1562 case APPCOMMAND_BROWSER_SEARCH:
1563 Ret = SHFindFiles(NULL,
1564 NULL);
1565 break;
1566
1567 case APPCOMMAND_BROWSER_HOME:
1568 case APPCOMMAND_LAUNCH_MAIL:
1569 default:
1570 DbgPrint("Shell app command %d unhandled!\n", (INT)GET_APPCOMMAND_LPARAM(lParam));
1571 break;
1572 }
1573
1574 return Ret;
1575 }
1576
1577
1578 static LRESULT
1579 TaskSwitchWnd_HandleShellHookMsg(IN OUT PTASK_SWITCH_WND This,
1580 IN WPARAM wParam,
1581 IN LPARAM lParam)
1582 {
1583 BOOL Ret = FALSE;
1584
1585 switch ((INT)wParam)
1586 {
1587 case HSHELL_APPCOMMAND:
1588 TaskSwitchWnd_HandleAppCommand(This,
1589 wParam,
1590 lParam);
1591 Ret = TRUE;
1592 break;
1593
1594 case HSHELL_WINDOWCREATED:
1595 TaskSwitchWnd_AddTask(This,
1596 (HWND)lParam);
1597 Ret = TRUE;
1598 break;
1599
1600 case HSHELL_WINDOWDESTROYED:
1601 /* The window still exists! Delay destroying it a bit */
1602 TaskSwitchWnd_DeleteTask(This,
1603 (HWND)lParam);
1604 Ret = TRUE;
1605 break;
1606
1607 case HSHELL_ACTIVATESHELLWINDOW:
1608 goto UnhandledShellMessage;
1609
1610 case HSHELL_RUDEAPPACTIVATED:
1611 goto UnhandledShellMessage;
1612
1613 case HSHELL_WINDOWACTIVATED:
1614 TaskSwitchWnd_ActivateTask(This,
1615 (HWND)lParam);
1616 Ret = TRUE;
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 case HSHELL_LANGUAGE:
1636 case HSHELL_SYSMENU:
1637 case HSHELL_ENDTASK:
1638 case HSHELL_ACCESSIBILITYSTATE:
1639 case HSHELL_WINDOWREPLACED:
1640 case HSHELL_WINDOWREPLACING:
1641 default:
1642 {
1643 static const struct {
1644 INT msg;
1645 LPCWSTR msg_name;
1646 } hshell_msg[] = {
1647 {HSHELL_WINDOWCREATED, L"HSHELL_WINDOWCREATED"},
1648 {HSHELL_WINDOWDESTROYED, L"HSHELL_WINDOWDESTROYED"},
1649 {HSHELL_ACTIVATESHELLWINDOW, L"HSHELL_ACTIVATESHELLWINDOW"},
1650 {HSHELL_WINDOWACTIVATED, L"HSHELL_WINDOWACTIVATED"},
1651 {HSHELL_GETMINRECT, L"HSHELL_GETMINRECT"},
1652 {HSHELL_REDRAW, L"HSHELL_REDRAW"},
1653 {HSHELL_TASKMAN, L"HSHELL_TASKMAN"},
1654 {HSHELL_LANGUAGE, L"HSHELL_LANGUAGE"},
1655 {HSHELL_SYSMENU, L"HSHELL_SYSMENU"},
1656 {HSHELL_ENDTASK, L"HSHELL_ENDTASK"},
1657 {HSHELL_ACCESSIBILITYSTATE, L"HSHELL_ACCESSIBILITYSTATE"},
1658 {HSHELL_APPCOMMAND, L"HSHELL_APPCOMMAND"},
1659 {HSHELL_WINDOWREPLACED, L"HSHELL_WINDOWREPLACED"},
1660 {HSHELL_WINDOWREPLACING, L"HSHELL_WINDOWREPLACING"},
1661 {HSHELL_RUDEAPPACTIVATED, L"HSHELL_RUDEAPPACTIVATED"},
1662 };
1663 int i, found;
1664 UnhandledShellMessage:
1665 for (i = 0, found = 0; i != sizeof(hshell_msg) / sizeof(hshell_msg[0]); i++)
1666 {
1667 if (hshell_msg[i].msg == (INT)wParam)
1668 {
1669 DbgPrint("Shell message %ws unhandled (lParam = 0x%p)!\n", hshell_msg[i].msg_name, lParam);
1670 found = 1;
1671 break;
1672 }
1673 }
1674 if (!found)
1675 {
1676 DbgPrint("Shell message %d unhandled (lParam = 0x%p)!\n", (INT)wParam, lParam);
1677 }
1678 break;
1679 }
1680 }
1681
1682 return Ret;
1683 }
1684
1685 static VOID
1686 TaskSwitchWnd_EnableGrouping(IN OUT PTASK_SWITCH_WND This,
1687 IN BOOL bEnable)
1688 {
1689 This->IsGroupingEnabled = bEnable;
1690
1691 /* Collapse or expand groups if neccessary */
1692 TaskSwitchWnd_UpdateButtonsSize(This,
1693 FALSE);
1694 }
1695
1696 static VOID
1697 TaskSwitchWnd_HandleTaskItemClick(IN OUT PTASK_SWITCH_WND This,
1698 IN OUT PTASK_ITEM TaskItem)
1699 {
1700 BOOL bIsMinimized;
1701 BOOL bIsActive;
1702
1703 if (IsWindow(TaskItem->hWnd))
1704 {
1705 bIsMinimized = IsIconic(TaskItem->hWnd);
1706 bIsActive = (TaskItem == This->ActiveTaskItem);
1707
1708 if (!bIsMinimized && bIsActive)
1709 {
1710 PostMessage(TaskItem->hWnd,
1711 WM_SYSCOMMAND,
1712 SC_MINIMIZE,
1713 0);
1714 }
1715 else
1716 {
1717 if (bIsMinimized)
1718 {
1719 PostMessage(TaskItem->hWnd,
1720 WM_SYSCOMMAND,
1721 SC_RESTORE,
1722 0);
1723 }
1724
1725 SetForegroundWindow(TaskItem->hWnd);
1726 }
1727 }
1728 }
1729
1730 static VOID
1731 TaskSwitchWnd_HandleTaskGroupClick(IN OUT PTASK_SWITCH_WND This,
1732 IN OUT PTASK_GROUP TaskGroup)
1733 {
1734 /* TODO: Show task group menu */
1735 }
1736
1737 static BOOL
1738 TaskSwitchWnd_HandleButtonClick(IN OUT PTASK_SWITCH_WND This,
1739 IN WORD wIndex)
1740 {
1741 PTASK_ITEM TaskItem;
1742 PTASK_GROUP TaskGroup;
1743
1744 if (This->IsGroupingEnabled)
1745 {
1746 TaskGroup = FindTaskGroupByIndex(This,
1747 (INT)wIndex);
1748 if (TaskGroup != NULL && TaskGroup->IsCollapsed)
1749 {
1750 TaskSwitchWnd_HandleTaskGroupClick(This,
1751 TaskGroup);
1752 return TRUE;
1753 }
1754 }
1755
1756 TaskItem = FindTaskItemByIndex(This,
1757 (INT)wIndex);
1758 if (TaskItem != NULL)
1759 {
1760 TaskSwitchWnd_HandleTaskItemClick(This,
1761 TaskItem);
1762 return TRUE;
1763 }
1764
1765 return FALSE;
1766 }
1767
1768
1769 static VOID
1770 TaskSwitchWnd_HandleTaskItemRightClick(IN OUT PTASK_SWITCH_WND This,
1771 IN OUT PTASK_ITEM TaskItem)
1772 {
1773
1774 HMENU hmenu = GetSystemMenu(TaskItem->hWnd, FALSE);
1775
1776 if (hmenu) {
1777 POINT pt;
1778 int cmd;
1779 GetCursorPos(&pt);
1780 cmd = TrackPopupMenu(hmenu, TPM_LEFTBUTTON|TPM_RIGHTBUTTON|TPM_RETURNCMD, pt.x, pt.y, 0, This->hWndToolbar, NULL);
1781 if (cmd) {
1782 SetForegroundWindow(TaskItem->hWnd); // reactivate window after the context menu has closed
1783 PostMessage(TaskItem->hWnd, WM_SYSCOMMAND, cmd, 0);
1784 }
1785 }
1786 }
1787
1788 static VOID
1789 TaskSwitchWnd_HandleTaskGroupRightClick(IN OUT PTASK_SWITCH_WND This,
1790 IN OUT PTASK_GROUP TaskGroup)
1791 {
1792 /* TODO: Show task group right click menu */
1793 }
1794
1795 static BOOL
1796 TaskSwitchWnd_HandleButtonRightClick(IN OUT PTASK_SWITCH_WND This,
1797 IN WORD wIndex)
1798 {
1799 PTASK_ITEM TaskItem;
1800 PTASK_GROUP TaskGroup;
1801 if (This->IsGroupingEnabled)
1802 {
1803 TaskGroup = FindTaskGroupByIndex(This,
1804 (INT)wIndex);
1805 if (TaskGroup != NULL && TaskGroup->IsCollapsed)
1806 {
1807 TaskSwitchWnd_HandleTaskGroupRightClick(This,
1808 TaskGroup);
1809 return TRUE;
1810 }
1811 }
1812
1813 TaskItem = FindTaskItemByIndex(This,
1814 (INT)wIndex);
1815
1816 if (TaskItem != NULL)
1817 {
1818 TaskSwitchWnd_HandleTaskItemRightClick(This,
1819 TaskItem);
1820 return TRUE;
1821 }
1822
1823 return FALSE;
1824 }
1825
1826
1827 static LRESULT
1828 TaskSwichWnd_HandleItemPaint(IN OUT PTASK_SWITCH_WND This,
1829 IN OUT NMTBCUSTOMDRAW *nmtbcd)
1830 {
1831 LRESULT Ret = CDRF_DODEFAULT;
1832 PTASK_GROUP TaskGroup;
1833 PTASK_ITEM TaskItem;
1834
1835 TaskItem = FindTaskItemByIndex(This,
1836 (INT)nmtbcd->nmcd.dwItemSpec);
1837 TaskGroup = FindTaskGroupByIndex(This,
1838 (INT)nmtbcd->nmcd.dwItemSpec);
1839 if (TaskGroup == NULL && TaskItem != NULL)
1840 {
1841 ASSERT(TaskItem != NULL);
1842
1843 if (TaskItem != NULL && IsWindow(TaskItem->hWnd))
1844 {
1845 /* Make the entire button flashing if neccessary */
1846 if (nmtbcd->nmcd.uItemState & CDIS_MARKED)
1847 {
1848 Ret = TBCDRF_NOBACKGROUND;
1849 if (!This->TaskBandTheme)
1850 {
1851 SelectObject(nmtbcd->nmcd.hdc, GetSysColorBrush(COLOR_HIGHLIGHT));
1852 Rectangle(nmtbcd->nmcd.hdc,
1853 nmtbcd->nmcd.rc.left,
1854 nmtbcd->nmcd.rc.top,
1855 nmtbcd->nmcd.rc.right,
1856 nmtbcd->nmcd.rc.bottom);
1857 }
1858 else
1859 {
1860 DrawThemeBackground(This->TaskBandTheme, nmtbcd->nmcd.hdc, TDP_FLASHBUTTON, 0, &nmtbcd->nmcd.rc, 0);
1861 }
1862 nmtbcd->clrText = GetSysColor(COLOR_HIGHLIGHTTEXT);
1863 return Ret;
1864 }
1865 }
1866 }
1867 else if (TaskGroup != NULL)
1868 {
1869 /* FIXME: Implement painting for task groups */
1870 }
1871 return Ret;
1872 }
1873
1874 static LRESULT
1875 TaskSwitchWnd_HandleToolbarNotification(IN OUT PTASK_SWITCH_WND This,
1876 IN const NMHDR *nmh)
1877 {
1878 LRESULT Ret = 0;
1879
1880 switch (nmh->code)
1881 {
1882 case NM_CUSTOMDRAW:
1883 {
1884 LPNMTBCUSTOMDRAW nmtbcd = (LPNMTBCUSTOMDRAW)nmh;
1885
1886 switch (nmtbcd->nmcd.dwDrawStage)
1887 {
1888
1889 case CDDS_ITEMPREPAINT:
1890 Ret = TaskSwichWnd_HandleItemPaint(This,
1891 nmtbcd);
1892 break;
1893
1894 case CDDS_PREPAINT:
1895 Ret = CDRF_NOTIFYITEMDRAW;
1896 break;
1897
1898 default:
1899 Ret = CDRF_DODEFAULT;
1900 break;
1901 }
1902 break;
1903 }
1904 }
1905
1906 return Ret;
1907 }
1908
1909 static VOID
1910 TaskSwitchWnd_DrawBackground(HWND hwnd,
1911 HDC hdc)
1912 {
1913 RECT rect;
1914
1915 GetClientRect(hwnd, &rect);
1916 DrawThemeParentBackground(hwnd, hdc, &rect);
1917 }
1918
1919 static LRESULT CALLBACK
1920 TaskSwitchWndProc(IN HWND hwnd,
1921 IN UINT uMsg,
1922 IN WPARAM wParam,
1923 IN LPARAM lParam)
1924 {
1925 PTASK_SWITCH_WND This = NULL;
1926 LRESULT Ret = FALSE;
1927
1928 if (uMsg != WM_NCCREATE)
1929 {
1930 This = (PTASK_SWITCH_WND)GetWindowLongPtr(hwnd,
1931 0);
1932 }
1933
1934 if (This != NULL || uMsg == WM_NCCREATE)
1935 {
1936 switch (uMsg)
1937 {
1938 case WM_THEMECHANGED:
1939 TaskSwitchWnd_UpdateTheme(This);
1940 break;
1941 case WM_ERASEBKGND:
1942 TaskSwitchWnd_DrawBackground(hwnd, (HDC)wParam);
1943 break;
1944 case WM_SIZE:
1945 {
1946 SIZE szClient;
1947
1948 szClient.cx = LOWORD(lParam);
1949 szClient.cy = HIWORD(lParam);
1950 if (This->hWndToolbar != NULL)
1951 {
1952 SetWindowPos(This->hWndToolbar,
1953 NULL,
1954 0,
1955 0,
1956 szClient.cx,
1957 szClient.cy,
1958 SWP_NOZORDER);
1959
1960 TaskSwitchWnd_UpdateButtonsSize(This,
1961 FALSE);
1962 }
1963 break;
1964 }
1965
1966 case WM_NCHITTEST:
1967 {
1968 /* We want the tray window to be draggable everywhere, so make the control
1969 appear transparent */
1970 Ret = DefWindowProc(hwnd,
1971 uMsg,
1972 wParam,
1973 lParam);
1974 if (Ret != HTVSCROLL && Ret != HTHSCROLL)
1975 Ret = HTTRANSPARENT;
1976 break;
1977 }
1978
1979 case WM_COMMAND:
1980 {
1981 if (lParam != 0 && (HWND)lParam == This->hWndToolbar)
1982 {
1983 TaskSwitchWnd_HandleButtonClick(This,
1984 LOWORD(wParam));
1985 }
1986 break;
1987 }
1988
1989 case WM_NOTIFY:
1990 {
1991 const NMHDR *nmh = (const NMHDR *)lParam;
1992
1993 if (nmh->hwndFrom == This->hWndToolbar)
1994 {
1995 Ret = TaskSwitchWnd_HandleToolbarNotification(This,
1996 nmh);
1997 }
1998 break;
1999 }
2000
2001 case TSWM_ENABLEGROUPING:
2002 {
2003 Ret = This->IsGroupingEnabled;
2004 if (wParam != This->IsGroupingEnabled)
2005 {
2006 TaskSwitchWnd_EnableGrouping(This,
2007 (BOOL)wParam);
2008 }
2009 break;
2010 }
2011
2012 case TSWM_UPDATETASKBARPOS:
2013 {
2014 /* Update the button spacing */
2015 TaskSwitchWnd_UpdateTbButtonSpacing(This,
2016 ITrayWindow_IsHorizontal(This->Tray),
2017 0,
2018 0);
2019 break;
2020 }
2021
2022 case WM_CONTEXTMENU:
2023 {
2024 if (This->hWndToolbar != NULL)
2025 {
2026 POINT pt;
2027 INT_PTR iBtn;
2028
2029 pt.x = (LONG)LOWORD(lParam);
2030 pt.y = (LONG)HIWORD(lParam);
2031
2032 MapWindowPoints(NULL,
2033 This->hWndToolbar,
2034 &pt,
2035 1);
2036
2037 iBtn = (INT_PTR)SendMessage(This->hWndToolbar,
2038 TB_HITTEST,
2039 0,
2040 (LPARAM)&pt);
2041 if (iBtn >= 0)
2042 {
2043 TaskSwitchWnd_HandleButtonRightClick(This,
2044 iBtn);
2045 }
2046 else
2047 goto ForwardContextMenuMsg;
2048 }
2049 else
2050 {
2051 ForwardContextMenuMsg:
2052 /* Forward message */
2053 Ret = SendMessage(ITrayWindow_GetHWND(This->Tray),
2054 uMsg,
2055 wParam,
2056 lParam);
2057 }
2058 break;
2059 }
2060
2061 case WM_NCCREATE:
2062 {
2063 LPCREATESTRUCT CreateStruct = (LPCREATESTRUCT)lParam;
2064 This = HeapAlloc(hProcessHeap,
2065 HEAP_ZERO_MEMORY,
2066 sizeof(*This));
2067 if (This == NULL)
2068 return FALSE;
2069
2070 This->hWnd = hwnd;
2071 This->hWndNotify = CreateStruct->hwndParent;
2072 This->Tray = (ITrayWindow*)CreateStruct->lpCreateParams;
2073 This->IsGroupingEnabled = TRUE; /* FIXME */
2074 SetWindowLongPtr(hwnd,
2075 0,
2076 (LONG_PTR)This);
2077
2078 return TRUE;
2079 }
2080
2081 case WM_CREATE:
2082 TaskSwitchWnd_Create(This);
2083
2084 #if DUMP_TASKS != 0
2085 SetTimer(hwnd,
2086 1,
2087 5000,
2088 NULL);
2089 #endif
2090
2091 break;
2092
2093 case WM_DESTROY:
2094 if (This->IsToolbarSubclassed)
2095 {
2096 if (RemoveWindowSubclass(This->hWndToolbar,
2097 TaskSwichWnd_ToolbarSubclassedProc,
2098 TSW_TOOLBAR_SUBCLASS_ID))
2099 {
2100 This->IsToolbarSubclassed = FALSE;
2101 }
2102 }
2103 break;
2104
2105 case WM_NCDESTROY:
2106 TaskSwitchWnd_NCDestroy(This);
2107 HeapFree(hProcessHeap,
2108 0,
2109 This);
2110 SetWindowLongPtr(hwnd,
2111 0,
2112 0);
2113 break;
2114
2115 #if DUMP_TASKS != 0
2116 case WM_TIMER:
2117 switch (wParam)
2118 {
2119 case 1:
2120 TaskSwitchWnd_DumpTasks(This);
2121 break;
2122 }
2123 break;
2124 #endif
2125
2126 default:
2127 /* HandleDefaultMessage: */
2128 if (uMsg == This->ShellHookMsg && This->ShellHookMsg != 0)
2129 {
2130 /* Process shell messages */
2131 Ret = (LRESULT)TaskSwitchWnd_HandleShellHookMsg(This,
2132 wParam,
2133 lParam);
2134 break;
2135 }
2136
2137 Ret = DefWindowProc(hwnd,
2138 uMsg,
2139 wParam,
2140 lParam);
2141 break;
2142 }
2143 }
2144 else
2145 {
2146 Ret = DefWindowProc(hwnd,
2147 uMsg,
2148 wParam,
2149 lParam);
2150 }
2151
2152 return Ret;
2153 }
2154
2155
2156 HWND
2157 CreateTaskSwitchWnd(IN HWND hWndParent,
2158 IN OUT ITrayWindow *Tray)
2159 {
2160 HWND hwndTaskBar;
2161
2162 hwndTaskBar = CreateWindowEx(0,
2163 szTaskSwitchWndClass,
2164 szRunningApps,
2165 WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP,
2166 0,
2167 0,
2168 0,
2169 0,
2170 hWndParent,
2171 NULL,
2172 hExplorerInstance,
2173 (LPVOID)Tray);
2174
2175 return hwndTaskBar;
2176 }
2177
2178 BOOL
2179 RegisterTaskSwitchWndClass(VOID)
2180 {
2181 WNDCLASS wc;
2182
2183 wc.style = CS_DBLCLKS;
2184 wc.lpfnWndProc = TaskSwitchWndProc;
2185 wc.cbClsExtra = 0;
2186 wc.cbWndExtra = sizeof(PTASK_SWITCH_WND);
2187 wc.hInstance = hExplorerInstance;
2188 wc.hIcon = NULL;
2189 wc.hCursor = LoadCursor(NULL,
2190 IDC_ARROW);
2191 wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
2192 wc.lpszMenuName = NULL;
2193 wc.lpszClassName = szTaskSwitchWndClass;
2194
2195 return RegisterClass(&wc) != 0;
2196 }
2197
2198 VOID
2199 UnregisterTaskSwitchWndClass(VOID)
2200 {
2201 UnregisterClass(szTaskSwitchWndClass,
2202 hExplorerInstance);
2203 }