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