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