[EXPLORER] -Make CSysPagerWnd, CTaskSwitchWnd, CTrayClockWnd and CTrayNotifyWnd prope...
[reactos.git] / base / shell / explorer / taskswnd.cpp
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 #include <commoncontrols.h>
23
24 /* Set DUMP_TASKS to 1 to enable a dump of the tasks and task groups every
25 5 seconds */
26 #define DUMP_TASKS 0
27 #define DEBUG_SHELL_HOOK 0
28
29 #define MAX_TASKS_COUNT (0x7FFF)
30 #define TASK_ITEM_ARRAY_ALLOC 64
31
32 const WCHAR szTaskSwitchWndClass[] = L"MSTaskSwWClass";
33 const WCHAR szRunningApps[] = L"Running Applications";
34
35 #if DEBUG_SHELL_HOOK
36 const struct {
37 INT msg;
38 LPCWSTR msg_name;
39 } hshell_msg [] = {
40 { HSHELL_WINDOWCREATED, L"HSHELL_WINDOWCREATED" },
41 { HSHELL_WINDOWDESTROYED, L"HSHELL_WINDOWDESTROYED" },
42 { HSHELL_ACTIVATESHELLWINDOW, L"HSHELL_ACTIVATESHELLWINDOW" },
43 { HSHELL_WINDOWACTIVATED, L"HSHELL_WINDOWACTIVATED" },
44 { HSHELL_GETMINRECT, L"HSHELL_GETMINRECT" },
45 { HSHELL_REDRAW, L"HSHELL_REDRAW" },
46 { HSHELL_TASKMAN, L"HSHELL_TASKMAN" },
47 { HSHELL_LANGUAGE, L"HSHELL_LANGUAGE" },
48 { HSHELL_SYSMENU, L"HSHELL_SYSMENU" },
49 { HSHELL_ENDTASK, L"HSHELL_ENDTASK" },
50 { HSHELL_ACCESSIBILITYSTATE, L"HSHELL_ACCESSIBILITYSTATE" },
51 { HSHELL_APPCOMMAND, L"HSHELL_APPCOMMAND" },
52 { HSHELL_WINDOWREPLACED, L"HSHELL_WINDOWREPLACED" },
53 { HSHELL_WINDOWREPLACING, L"HSHELL_WINDOWREPLACING" },
54 { HSHELL_RUDEAPPACTIVATED, L"HSHELL_RUDEAPPACTIVATED" },
55 };
56 #endif
57
58 typedef struct _TASK_GROUP
59 {
60 /* We have to use a linked list instead of an array so we don't have to
61 update all pointers to groups in the task item array when removing
62 groups. */
63 struct _TASK_GROUP *Next;
64
65 DWORD dwTaskCount;
66 DWORD dwProcessId;
67 INT Index;
68 union
69 {
70 DWORD dwFlags;
71 struct
72 {
73
74 DWORD IsCollapsed : 1;
75 };
76 };
77 } TASK_GROUP, *PTASK_GROUP;
78
79 typedef struct _TASK_ITEM
80 {
81 HWND hWnd;
82 PTASK_GROUP Group;
83 INT Index;
84 INT IconIndex;
85
86 union
87 {
88 DWORD dwFlags;
89 struct
90 {
91
92 /* IsFlashing is TRUE when the task bar item should be flashing. */
93 DWORD IsFlashing : 1;
94
95 /* RenderFlashed is only TRUE if the task bar item should be
96 drawn with a flash. */
97 DWORD RenderFlashed : 1;
98 };
99 };
100 } TASK_ITEM, *PTASK_ITEM;
101
102 class CTaskToolbar :
103 public CWindowImplBaseT< CToolbar<TASK_ITEM>, CControlWinTraits >
104 {
105 public:
106 INT UpdateTbButtonSpacing(IN BOOL bHorizontal, IN BOOL bThemed, IN UINT uiRows = 0, IN UINT uiBtnsPerLine = 0)
107 {
108 TBMETRICS tbm;
109
110 tbm.cbSize = sizeof(tbm);
111 tbm.dwMask = TBMF_BARPAD | TBMF_BUTTONSPACING;
112
113 tbm.cxBarPad = tbm.cyBarPad = 0;
114
115 if (bThemed)
116 {
117 tbm.cxButtonSpacing = 0;
118 tbm.cyButtonSpacing = 0;
119 }
120 else
121 {
122 if (bHorizontal || uiBtnsPerLine > 1)
123 tbm.cxButtonSpacing = (3 * GetSystemMetrics(SM_CXEDGE) / 2);
124 else
125 tbm.cxButtonSpacing = 0;
126
127 if (!bHorizontal || uiRows > 1)
128 tbm.cyButtonSpacing = (3 * GetSystemMetrics(SM_CYEDGE) / 2);
129 else
130 tbm.cyButtonSpacing = 0;
131 }
132
133 SetMetrics(&tbm);
134
135 return tbm.cxButtonSpacing;
136 }
137
138 VOID BeginUpdate()
139 {
140 SetRedraw(FALSE);
141 }
142
143 VOID EndUpdate()
144 {
145 SendMessageW(WM_SETREDRAW, TRUE);
146 InvalidateRect(NULL, TRUE);
147 }
148
149 BOOL SetButtonCommandId(IN INT iButtonIndex, IN INT iCommandId)
150 {
151 TBBUTTONINFO tbbi;
152
153 tbbi.cbSize = sizeof(tbbi);
154 tbbi.dwMask = TBIF_BYINDEX | TBIF_COMMAND;
155 tbbi.idCommand = iCommandId;
156
157 return SetButtonInfo(iButtonIndex, &tbbi) != 0;
158 }
159
160 LRESULT OnNcHitTestToolbar(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
161 {
162 POINT pt;
163
164 /* See if the mouse is on a button */
165 pt.x = GET_X_LPARAM(lParam);
166 pt.y = GET_Y_LPARAM(lParam);
167 ScreenToClient(&pt);
168
169 INT index = HitTest(&pt);
170 if (index < 0)
171 {
172 /* Make the control appear to be transparent outside of any buttons */
173 return HTTRANSPARENT;
174 }
175
176 bHandled = FALSE;
177 return 0;
178 }
179
180 public:
181 BEGIN_MSG_MAP(CNotifyToolbar)
182 MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTestToolbar)
183 END_MSG_MAP()
184
185 BOOL Initialize(HWND hWndParent)
186 {
187 DWORD styles = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN |
188 TBSTYLE_TOOLTIPS | TBSTYLE_WRAPABLE | TBSTYLE_LIST | TBSTYLE_TRANSPARENT |
189 CCS_TOP | CCS_NORESIZE | CCS_NODIVIDER;
190
191 return SubclassWindow(CToolbar::Create(hWndParent, styles));
192 }
193 };
194
195 class CTaskSwitchWnd :
196 public CComCoClass<CTaskSwitchWnd>,
197 public CComObjectRootEx<CComMultiThreadModelNoCS>,
198 public CWindowImpl < CTaskSwitchWnd, CWindow, CControlWinTraits >,
199 public IOleWindow
200 {
201 CTaskToolbar m_TaskBar;
202
203 CComPtr<ITrayWindow> m_Tray;
204
205 UINT m_ShellHookMsg;
206
207 WORD m_TaskItemCount;
208 WORD m_AllocatedTaskItems;
209
210 PTASK_GROUP m_TaskGroups;
211 PTASK_ITEM m_TaskItems;
212 PTASK_ITEM m_ActiveTaskItem;
213
214 HTHEME m_Theme;
215 UINT m_ButtonsPerLine;
216 WORD m_ButtonCount;
217
218 HIMAGELIST m_ImageList;
219
220 BOOL m_IsGroupingEnabled;
221 BOOL m_IsDestroying;
222
223 SIZE m_ButtonSize;
224
225 public:
226 CTaskSwitchWnd() :
227 m_ShellHookMsg(NULL),
228 m_TaskItemCount(0),
229 m_AllocatedTaskItems(0),
230 m_TaskGroups(NULL),
231 m_TaskItems(NULL),
232 m_ActiveTaskItem(NULL),
233 m_Theme(NULL),
234 m_ButtonsPerLine(0),
235 m_ButtonCount(0),
236 m_ImageList(NULL),
237 m_IsGroupingEnabled(FALSE),
238 m_IsDestroying(FALSE)
239 {
240 ZeroMemory(&m_ButtonSize, sizeof(m_ButtonSize));
241 }
242 virtual ~CTaskSwitchWnd() { }
243
244 INT GetWndTextFromTaskItem(IN PTASK_ITEM TaskItem, LPWSTR szBuf, DWORD cchBuf)
245 {
246 /* Get the window text without sending a message so we don't hang if an
247 application isn't responding! */
248 return InternalGetWindowText(TaskItem->hWnd, szBuf, cchBuf);
249 }
250
251
252 #if DUMP_TASKS != 0
253 VOID DumpTasks()
254 {
255 PTASK_GROUP CurrentGroup;
256 PTASK_ITEM CurrentTaskItem, LastTaskItem;
257
258 TRACE("Tasks dump:\n");
259 if (m_IsGroupingEnabled)
260 {
261 CurrentGroup = m_TaskGroups;
262 while (CurrentGroup != NULL)
263 {
264 TRACE("- Group PID: 0x%p Tasks: %d Index: %d\n", CurrentGroup->dwProcessId, CurrentGroup->dwTaskCount, CurrentGroup->Index);
265
266 CurrentTaskItem = m_TaskItems;
267 LastTaskItem = CurrentTaskItem + m_TaskItemCount;
268 while (CurrentTaskItem != LastTaskItem)
269 {
270 if (CurrentTaskItem->Group == CurrentGroup)
271 {
272 TRACE(" + Task hwnd: 0x%p Index: %d\n", CurrentTaskItem->hWnd, CurrentTaskItem->Index);
273 }
274 CurrentTaskItem++;
275 }
276
277 CurrentGroup = CurrentGroup->Next;
278 }
279
280 CurrentTaskItem = m_TaskItems;
281 LastTaskItem = CurrentTaskItem + m_TaskItemCount;
282 while (CurrentTaskItem != LastTaskItem)
283 {
284 if (CurrentTaskItem->Group == NULL)
285 {
286 TRACE("- Task hwnd: 0x%p Index: %d\n", CurrentTaskItem->hWnd, CurrentTaskItem->Index);
287 }
288 CurrentTaskItem++;
289 }
290 }
291 else
292 {
293 CurrentTaskItem = m_TaskItems;
294 LastTaskItem = CurrentTaskItem + m_TaskItemCount;
295 while (CurrentTaskItem != LastTaskItem)
296 {
297 TRACE("- Task hwnd: 0x%p Index: %d\n", CurrentTaskItem->hWnd, CurrentTaskItem->Index);
298 CurrentTaskItem++;
299 }
300 }
301 }
302 #endif
303
304 VOID UpdateIndexesAfter(IN INT iIndex, BOOL bInserted)
305 {
306 PTASK_GROUP CurrentGroup;
307 PTASK_ITEM CurrentTaskItem, LastTaskItem;
308 INT NewIndex;
309
310 int offset = bInserted ? +1 : -1;
311
312 if (m_IsGroupingEnabled)
313 {
314 /* Update all affected groups */
315 CurrentGroup = m_TaskGroups;
316 while (CurrentGroup != NULL)
317 {
318 if (CurrentGroup->IsCollapsed &&
319 CurrentGroup->Index >= iIndex)
320 {
321 /* Update the toolbar buttons */
322 NewIndex = CurrentGroup->Index + offset;
323 if (m_TaskBar.SetButtonCommandId(CurrentGroup->Index + offset, NewIndex))
324 {
325 CurrentGroup->Index = NewIndex;
326 }
327 else
328 CurrentGroup->Index = -1;
329 }
330
331 CurrentGroup = CurrentGroup->Next;
332 }
333 }
334
335 /* Update all affected task items */
336 CurrentTaskItem = m_TaskItems;
337 LastTaskItem = CurrentTaskItem + m_TaskItemCount;
338 while (CurrentTaskItem != LastTaskItem)
339 {
340 CurrentGroup = CurrentTaskItem->Group;
341 if (CurrentGroup != NULL)
342 {
343 if (!CurrentGroup->IsCollapsed &&
344 CurrentTaskItem->Index >= iIndex)
345 {
346 goto UpdateTaskItemBtn;
347 }
348 }
349 else if (CurrentTaskItem->Index >= iIndex)
350 {
351 UpdateTaskItemBtn:
352 /* Update the toolbar buttons */
353 NewIndex = CurrentTaskItem->Index + offset;
354 if (m_TaskBar.SetButtonCommandId(CurrentTaskItem->Index + offset, NewIndex))
355 {
356 CurrentTaskItem->Index = NewIndex;
357 }
358 else
359 CurrentTaskItem->Index = -1;
360 }
361
362 CurrentTaskItem++;
363 }
364 }
365
366
367 INT UpdateTaskGroupButton(IN PTASK_GROUP TaskGroup)
368 {
369 ASSERT(TaskGroup->Index >= 0);
370
371 /* FIXME: Implement */
372
373 return TaskGroup->Index;
374 }
375
376 VOID ExpandTaskGroup(IN PTASK_GROUP TaskGroup)
377 {
378 ASSERT(TaskGroup->dwTaskCount > 0);
379 ASSERT(TaskGroup->IsCollapsed);
380 ASSERT(TaskGroup->Index >= 0);
381
382 /* FIXME: Implement */
383 }
384
385 HICON GetWndIcon(HWND hwnd)
386 {
387 HICON hIcon = 0;
388
389 SendMessageTimeout(hwnd, WM_GETICON, ICON_SMALL2, 0, SMTO_ABORTIFHUNG, 1000, (PDWORD_PTR) &hIcon);
390 if (hIcon)
391 return hIcon;
392
393 SendMessageTimeout(hwnd, WM_GETICON, ICON_SMALL, 0, SMTO_ABORTIFHUNG, 1000, (PDWORD_PTR) &hIcon);
394 if (hIcon)
395 return hIcon;
396
397 SendMessageTimeout(hwnd, WM_GETICON, ICON_BIG, 0, SMTO_ABORTIFHUNG, 1000, (PDWORD_PTR) &hIcon);
398 if (hIcon)
399 return hIcon;
400
401 hIcon = (HICON) GetClassLongPtr(hwnd, GCL_HICONSM);
402 if (hIcon)
403 return hIcon;
404
405 hIcon = (HICON) GetClassLongPtr(hwnd, GCL_HICON);
406
407 return hIcon;
408 }
409
410 INT UpdateTaskItemButton(IN PTASK_ITEM TaskItem)
411 {
412 TBBUTTONINFO tbbi = { 0 };
413 HICON icon;
414 WCHAR windowText[255];
415
416 ASSERT(TaskItem->Index >= 0);
417
418 tbbi.cbSize = sizeof(tbbi);
419 tbbi.dwMask = TBIF_BYINDEX | TBIF_STATE | TBIF_TEXT | TBIF_IMAGE;
420 tbbi.fsState = TBSTATE_ENABLED;
421 if (m_ActiveTaskItem == TaskItem)
422 tbbi.fsState |= TBSTATE_CHECKED;
423
424 if (TaskItem->RenderFlashed)
425 tbbi.fsState |= TBSTATE_MARKED;
426
427 /* Check if we're updating a button that is the last one in the
428 line. If so, we need to set the TBSTATE_WRAP flag! */
429 if (!m_Tray->IsHorizontal() || (m_ButtonsPerLine != 0 &&
430 (TaskItem->Index + 1) % m_ButtonsPerLine == 0))
431 {
432 tbbi.fsState |= TBSTATE_WRAP;
433 }
434
435 if (GetWndTextFromTaskItem(TaskItem, windowText, _countof(windowText)) > 0)
436 {
437 tbbi.pszText = windowText;
438 }
439
440 icon = GetWndIcon(TaskItem->hWnd);
441 if (!icon)
442 icon = static_cast<HICON>(LoadImageW(NULL, MAKEINTRESOURCEW(OIC_SAMPLE), IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE));
443 TaskItem->IconIndex = ImageList_ReplaceIcon(m_ImageList, TaskItem->IconIndex, icon);
444 tbbi.iImage = TaskItem->IconIndex;
445
446 if (!m_TaskBar.SetButtonInfo(TaskItem->Index, &tbbi))
447 {
448 TaskItem->Index = -1;
449 return -1;
450 }
451
452 TRACE("Updated button %d for hwnd 0x%p\n", TaskItem->Index, TaskItem->hWnd);
453 return TaskItem->Index;
454 }
455
456 VOID RemoveIcon(IN PTASK_ITEM TaskItem)
457 {
458 TBBUTTONINFO tbbi;
459 PTASK_ITEM currentTaskItem, LastItem;
460
461 if (TaskItem->IconIndex == -1)
462 return;
463
464 tbbi.cbSize = sizeof(tbbi);
465 tbbi.dwMask = TBIF_IMAGE;
466
467 currentTaskItem = m_TaskItems;
468 LastItem = currentTaskItem + m_TaskItemCount;
469 while (currentTaskItem != LastItem)
470 {
471 if (currentTaskItem->IconIndex > TaskItem->IconIndex)
472 {
473 currentTaskItem->IconIndex--;
474 tbbi.iImage = currentTaskItem->IconIndex;
475
476 m_TaskBar.SetButtonInfo(currentTaskItem->Index, &tbbi);
477 }
478 currentTaskItem++;
479 }
480
481 ImageList_Remove(m_ImageList, TaskItem->IconIndex);
482 }
483
484 PTASK_ITEM FindLastTaskItemOfGroup(
485 IN PTASK_GROUP TaskGroup OPTIONAL,
486 IN PTASK_ITEM NewTaskItem OPTIONAL)
487 {
488 PTASK_ITEM TaskItem, LastTaskItem, FoundTaskItem = NULL;
489 DWORD dwTaskCount;
490
491 ASSERT(m_IsGroupingEnabled);
492
493 TaskItem = m_TaskItems;
494 LastTaskItem = TaskItem + m_TaskItemCount;
495
496 dwTaskCount = (TaskGroup != NULL ? TaskGroup->dwTaskCount : MAX_TASKS_COUNT);
497
498 ASSERT(dwTaskCount > 0);
499
500 while (TaskItem != LastTaskItem)
501 {
502 if (TaskItem->Group == TaskGroup)
503 {
504 if ((NewTaskItem != NULL && TaskItem != NewTaskItem) || NewTaskItem == NULL)
505 {
506 FoundTaskItem = TaskItem;
507 }
508
509 if (--dwTaskCount == 0)
510 {
511 /* We found the last task item in the group! */
512 break;
513 }
514 }
515
516 TaskItem++;
517 }
518
519 return FoundTaskItem;
520 }
521
522 INT CalculateTaskItemNewButtonIndex(IN PTASK_ITEM TaskItem)
523 {
524 PTASK_GROUP TaskGroup;
525 PTASK_ITEM LastTaskItem;
526
527 /* NOTE: This routine assumes that the group is *not* collapsed! */
528
529 TaskGroup = TaskItem->Group;
530 if (m_IsGroupingEnabled)
531 {
532 if (TaskGroup != NULL)
533 {
534 ASSERT(TaskGroup->Index < 0);
535 ASSERT(!TaskGroup->IsCollapsed);
536
537 if (TaskGroup->dwTaskCount > 1)
538 {
539 LastTaskItem = FindLastTaskItemOfGroup(TaskGroup, TaskItem);
540 if (LastTaskItem != NULL)
541 {
542 /* Since the group is expanded the task items must have an index */
543 ASSERT(LastTaskItem->Index >= 0);
544
545 return LastTaskItem->Index + 1;
546 }
547 }
548 }
549 else
550 {
551 /* Find the last NULL group button. NULL groups are added at the end of the
552 task item list when grouping is enabled */
553 LastTaskItem = FindLastTaskItemOfGroup(NULL, TaskItem);
554 if (LastTaskItem != NULL)
555 {
556 ASSERT(LastTaskItem->Index >= 0);
557
558 return LastTaskItem->Index + 1;
559 }
560 }
561 }
562
563 return m_ButtonCount;
564 }
565
566 INT AddTaskItemButton(IN OUT PTASK_ITEM TaskItem)
567 {
568 WCHAR windowText[255];
569 TBBUTTON tbBtn = { 0 };
570 INT iIndex;
571 HICON icon;
572
573 if (TaskItem->Index >= 0)
574 {
575 return UpdateTaskItemButton(TaskItem);
576 }
577
578 if (TaskItem->Group != NULL &&
579 TaskItem->Group->IsCollapsed)
580 {
581 /* The task group is collapsed, we only need to update the group button */
582 return UpdateTaskGroupButton(TaskItem->Group);
583 }
584
585 icon = GetWndIcon(TaskItem->hWnd);
586 if (!icon)
587 icon = static_cast<HICON>(LoadImageW(NULL, MAKEINTRESOURCEW(OIC_SAMPLE), IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE));
588 TaskItem->IconIndex = ImageList_ReplaceIcon(m_ImageList, -1, icon);
589
590 tbBtn.iBitmap = TaskItem->IconIndex;
591 tbBtn.fsState = TBSTATE_ENABLED | TBSTATE_ELLIPSES;
592 tbBtn.fsStyle = BTNS_CHECK | BTNS_NOPREFIX | BTNS_SHOWTEXT;
593 tbBtn.dwData = TaskItem->Index;
594
595 if (GetWndTextFromTaskItem(TaskItem, windowText, _countof(windowText)) > 0)
596 {
597 tbBtn.iString = (DWORD_PTR) windowText;
598 }
599
600 /* Find out where to insert the new button */
601 iIndex = CalculateTaskItemNewButtonIndex(TaskItem);
602 ASSERT(iIndex >= 0);
603 tbBtn.idCommand = iIndex;
604
605 m_TaskBar.BeginUpdate();
606
607 if (m_TaskBar.InsertButton(iIndex, &tbBtn))
608 {
609 UpdateIndexesAfter(iIndex, TRUE);
610
611 TRACE("Added button %d for hwnd 0x%p\n", iIndex, TaskItem->hWnd);
612
613 TaskItem->Index = iIndex;
614 m_ButtonCount++;
615
616 /* Update button sizes and fix the button wrapping */
617 UpdateButtonsSize(TRUE);
618 return iIndex;
619 }
620
621 m_TaskBar.EndUpdate();
622
623 return -1;
624 }
625
626 BOOL DeleteTaskItemButton(IN OUT PTASK_ITEM TaskItem)
627 {
628 PTASK_GROUP TaskGroup;
629 INT iIndex;
630
631 TaskGroup = TaskItem->Group;
632
633 if (TaskItem->Index >= 0)
634 {
635 if ((TaskGroup != NULL && !TaskGroup->IsCollapsed) ||
636 TaskGroup == NULL)
637 {
638 m_TaskBar.BeginUpdate();
639
640 RemoveIcon(TaskItem);
641 iIndex = TaskItem->Index;
642 if (m_TaskBar.DeleteButton(iIndex))
643 {
644 TaskItem->Index = -1;
645 m_ButtonCount--;
646
647 UpdateIndexesAfter(iIndex, FALSE);
648
649 /* Update button sizes and fix the button wrapping */
650 UpdateButtonsSize(TRUE);
651 return TRUE;
652 }
653
654 m_TaskBar.EndUpdate();
655 }
656 }
657
658 return FALSE;
659 }
660
661 PTASK_GROUP AddToTaskGroup(IN HWND hWnd)
662 {
663 DWORD dwProcessId;
664 PTASK_GROUP TaskGroup, *PrevLink;
665
666 if (!GetWindowThreadProcessId(hWnd,
667 &dwProcessId))
668 {
669 TRACE("Cannot get process id of hwnd 0x%p\n", hWnd);
670 return NULL;
671 }
672
673 /* Try to find an existing task group */
674 TaskGroup = m_TaskGroups;
675 PrevLink = &m_TaskGroups;
676 while (TaskGroup != NULL)
677 {
678 if (TaskGroup->dwProcessId == dwProcessId)
679 {
680 TaskGroup->dwTaskCount++;
681 return TaskGroup;
682 }
683
684 PrevLink = &TaskGroup->Next;
685 TaskGroup = TaskGroup->Next;
686 }
687
688 /* Allocate a new task group */
689 TaskGroup = (PTASK_GROUP) HeapAlloc(hProcessHeap,
690 HEAP_ZERO_MEMORY,
691 sizeof(*TaskGroup));
692 if (TaskGroup != NULL)
693 {
694 TaskGroup->dwTaskCount = 1;
695 TaskGroup->dwProcessId = dwProcessId;
696 TaskGroup->Index = -1;
697
698 /* Add the task group to the list */
699 *PrevLink = TaskGroup;
700 }
701
702 return TaskGroup;
703 }
704
705 VOID RemoveTaskFromTaskGroup(IN OUT PTASK_ITEM TaskItem)
706 {
707 PTASK_GROUP TaskGroup, CurrentGroup, *PrevLink;
708
709 TaskGroup = TaskItem->Group;
710 if (TaskGroup != NULL)
711 {
712 DWORD dwNewTaskCount = --TaskGroup->dwTaskCount;
713 if (dwNewTaskCount == 0)
714 {
715 /* Find the previous pointer in the chain */
716 CurrentGroup = m_TaskGroups;
717 PrevLink = &m_TaskGroups;
718 while (CurrentGroup != TaskGroup)
719 {
720 PrevLink = &CurrentGroup->Next;
721 CurrentGroup = CurrentGroup->Next;
722 }
723
724 /* Remove the group from the list */
725 ASSERT(TaskGroup == CurrentGroup);
726 *PrevLink = TaskGroup->Next;
727
728 /* Free the task group */
729 HeapFree(hProcessHeap,
730 0,
731 TaskGroup);
732 }
733 else if (TaskGroup->IsCollapsed &&
734 TaskGroup->Index >= 0)
735 {
736 if (dwNewTaskCount > 1)
737 {
738 /* FIXME: Check if we should expand the group */
739 /* Update the task group button */
740 UpdateTaskGroupButton(TaskGroup);
741 }
742 else
743 {
744 /* Expand the group of one task button to a task button */
745 ExpandTaskGroup(TaskGroup);
746 }
747 }
748 }
749 }
750
751 PTASK_ITEM FindTaskItem(IN HWND hWnd)
752 {
753 PTASK_ITEM TaskItem, LastItem;
754
755 TaskItem = m_TaskItems;
756 LastItem = TaskItem + m_TaskItemCount;
757 while (TaskItem != LastItem)
758 {
759 if (TaskItem->hWnd == hWnd)
760 return TaskItem;
761
762 TaskItem++;
763 }
764
765 return NULL;
766 }
767
768 PTASK_ITEM FindOtherTaskItem(IN HWND hWnd)
769 {
770 PTASK_ITEM LastItem, TaskItem;
771 PTASK_GROUP TaskGroup;
772 DWORD dwProcessId;
773
774 if (!GetWindowThreadProcessId(hWnd, &dwProcessId))
775 {
776 return NULL;
777 }
778
779 /* Try to find another task that belongs to the same
780 process as the given window */
781 TaskItem = m_TaskItems;
782 LastItem = TaskItem + m_TaskItemCount;
783 while (TaskItem != LastItem)
784 {
785 TaskGroup = TaskItem->Group;
786 if (TaskGroup != NULL)
787 {
788 if (TaskGroup->dwProcessId == dwProcessId)
789 return TaskItem;
790 }
791 else
792 {
793 DWORD dwProcessIdTask;
794
795 if (GetWindowThreadProcessId(TaskItem->hWnd,
796 &dwProcessIdTask) &&
797 dwProcessIdTask == dwProcessId)
798 {
799 return TaskItem;
800 }
801 }
802
803 TaskItem++;
804 }
805
806 return NULL;
807 }
808
809 PTASK_ITEM AllocTaskItem()
810 {
811 if (m_TaskItemCount >= MAX_TASKS_COUNT)
812 {
813 /* We need the most significant bit in 16 bit command IDs to indicate whether it
814 is a task group or task item. WM_COMMAND limits command IDs to 16 bits! */
815 return NULL;
816 }
817
818 ASSERT(m_AllocatedTaskItems >= m_TaskItemCount);
819
820 if (m_TaskItemCount == 0)
821 {
822 m_TaskItems = (PTASK_ITEM) HeapAlloc(hProcessHeap,
823 0,
824 TASK_ITEM_ARRAY_ALLOC * sizeof(*m_TaskItems));
825 if (m_TaskItems != NULL)
826 {
827 m_AllocatedTaskItems = TASK_ITEM_ARRAY_ALLOC;
828 }
829 else
830 return NULL;
831 }
832 else if (m_TaskItemCount >= m_AllocatedTaskItems)
833 {
834 PTASK_ITEM NewArray;
835 SIZE_T NewArrayLength, ActiveTaskItemIndex;
836
837 NewArrayLength = m_AllocatedTaskItems + TASK_ITEM_ARRAY_ALLOC;
838
839 NewArray = (PTASK_ITEM) HeapReAlloc(hProcessHeap,
840 0,
841 m_TaskItems,
842 NewArrayLength * sizeof(*m_TaskItems));
843 if (NewArray != NULL)
844 {
845 if (m_ActiveTaskItem != NULL)
846 {
847 /* Fixup the ActiveTaskItem pointer */
848 ActiveTaskItemIndex = m_ActiveTaskItem - m_TaskItems;
849 m_ActiveTaskItem = NewArray + ActiveTaskItemIndex;
850 }
851 m_AllocatedTaskItems = (WORD) NewArrayLength;
852 m_TaskItems = NewArray;
853 }
854 else
855 return NULL;
856 }
857
858 return m_TaskItems + m_TaskItemCount++;
859 }
860
861 VOID FreeTaskItem(IN OUT PTASK_ITEM TaskItem)
862 {
863 WORD wIndex;
864
865 if (TaskItem == m_ActiveTaskItem)
866 m_ActiveTaskItem = NULL;
867
868 wIndex = (WORD) (TaskItem - m_TaskItems);
869 if (wIndex + 1 < m_TaskItemCount)
870 {
871 MoveMemory(TaskItem,
872 TaskItem + 1,
873 (m_TaskItemCount - wIndex - 1) * sizeof(*TaskItem));
874 }
875
876 m_TaskItemCount--;
877 }
878
879 VOID DeleteTaskItem(IN OUT PTASK_ITEM TaskItem)
880 {
881 if (!m_IsDestroying)
882 {
883 /* Delete the task button from the toolbar */
884 DeleteTaskItemButton(TaskItem);
885 }
886
887 /* Remove the task from it's group */
888 RemoveTaskFromTaskGroup(TaskItem);
889
890 /* Free the task item */
891 FreeTaskItem(TaskItem);
892 }
893
894 VOID CheckActivateTaskItem(IN OUT PTASK_ITEM TaskItem)
895 {
896 PTASK_ITEM CurrentTaskItem;
897 PTASK_GROUP TaskGroup = NULL;
898
899 CurrentTaskItem = m_ActiveTaskItem;
900
901 if (TaskItem != NULL)
902 TaskGroup = TaskItem->Group;
903
904 if (m_IsGroupingEnabled &&
905 TaskGroup != NULL &&
906 TaskGroup->IsCollapsed)
907 {
908 /* FIXME */
909 return;
910 }
911
912 if (CurrentTaskItem != NULL)
913 {
914 PTASK_GROUP CurrentTaskGroup;
915
916 if (CurrentTaskItem == TaskItem)
917 return;
918
919 CurrentTaskGroup = CurrentTaskItem->Group;
920
921 if (m_IsGroupingEnabled &&
922 CurrentTaskGroup != NULL &&
923 CurrentTaskGroup->IsCollapsed)
924 {
925 if (CurrentTaskGroup == TaskGroup)
926 return;
927
928 /* FIXME */
929 }
930 else
931 {
932 m_ActiveTaskItem = NULL;
933 if (CurrentTaskItem->Index >= 0)
934 {
935 UpdateTaskItemButton(CurrentTaskItem);
936 }
937 }
938 }
939
940 m_ActiveTaskItem = TaskItem;
941
942 if (TaskItem != NULL && TaskItem->Index >= 0)
943 {
944 UpdateTaskItemButton(TaskItem);
945 }
946 else if (TaskItem == NULL)
947 {
948 TRACE("Active TaskItem now NULL\n");
949 }
950 }
951
952 PTASK_ITEM FindTaskItemByIndex(IN INT Index)
953 {
954 PTASK_ITEM TaskItem, LastItem;
955
956 TaskItem = m_TaskItems;
957 LastItem = TaskItem + m_TaskItemCount;
958 while (TaskItem != LastItem)
959 {
960 if (TaskItem->Index == Index)
961 return TaskItem;
962
963 TaskItem++;
964 }
965
966 return NULL;
967 }
968
969 PTASK_GROUP FindTaskGroupByIndex(IN INT Index)
970 {
971 PTASK_GROUP CurrentGroup;
972
973 CurrentGroup = m_TaskGroups;
974 while (CurrentGroup != NULL)
975 {
976 if (CurrentGroup->Index == Index)
977 break;
978
979 CurrentGroup = CurrentGroup->Next;
980 }
981
982 return CurrentGroup;
983 }
984
985 BOOL AddTask(IN HWND hWnd)
986 {
987 PTASK_ITEM TaskItem;
988
989 if (!::IsWindow(hWnd) || m_Tray->IsSpecialHWND(hWnd))
990 return FALSE;
991
992 TaskItem = FindTaskItem(hWnd);
993 if (TaskItem == NULL)
994 {
995 TRACE("Add window 0x%p\n", hWnd);
996 TaskItem = AllocTaskItem();
997 if (TaskItem != NULL)
998 {
999 ZeroMemory(TaskItem, sizeof(*TaskItem));
1000 TaskItem->hWnd = hWnd;
1001 TaskItem->Index = -1;
1002 TaskItem->Group = AddToTaskGroup(hWnd);
1003
1004 if (!m_IsDestroying)
1005 {
1006 AddTaskItemButton(TaskItem);
1007 }
1008 }
1009 }
1010
1011 return TaskItem != NULL;
1012 }
1013
1014 BOOL ActivateTaskItem(IN OUT PTASK_ITEM TaskItem OPTIONAL)
1015 {
1016 if (TaskItem != NULL)
1017 {
1018 TRACE("Activate window 0x%p on button %d\n", TaskItem->hWnd, TaskItem->Index);
1019 }
1020
1021 CheckActivateTaskItem(TaskItem);
1022
1023 return FALSE;
1024 }
1025
1026 BOOL ActivateTask(IN HWND hWnd)
1027 {
1028 PTASK_ITEM TaskItem;
1029
1030 if (!hWnd)
1031 {
1032 return ActivateTaskItem(NULL);
1033 }
1034
1035 TaskItem = FindTaskItem(hWnd);
1036 if (TaskItem == NULL)
1037 {
1038 TaskItem = FindOtherTaskItem(hWnd);
1039 }
1040
1041 if (TaskItem == NULL)
1042 {
1043 WARN("Activate window 0x%p, could not find task\n", hWnd);
1044 RefreshWindowList();
1045 }
1046
1047 return ActivateTaskItem(TaskItem);
1048 }
1049
1050 BOOL DeleteTask(IN HWND hWnd)
1051 {
1052 PTASK_ITEM TaskItem;
1053
1054 TaskItem = FindTaskItem(hWnd);
1055 if (TaskItem != NULL)
1056 {
1057 TRACE("Delete window 0x%p on button %d\n", hWnd, TaskItem->Index);
1058 DeleteTaskItem(TaskItem);
1059 return TRUE;
1060 }
1061 //else
1062 //TRACE("Failed to delete window 0x%p\n", hWnd);
1063
1064 return FALSE;
1065 }
1066
1067 VOID DeleteAllTasks()
1068 {
1069 PTASK_ITEM CurrentTask;
1070
1071 if (m_TaskItemCount > 0)
1072 {
1073 CurrentTask = m_TaskItems + m_TaskItemCount;
1074 do
1075 {
1076 DeleteTaskItem(--CurrentTask);
1077 } while (CurrentTask != m_TaskItems);
1078 }
1079 }
1080
1081 VOID FlashTaskItem(IN OUT PTASK_ITEM TaskItem)
1082 {
1083 TaskItem->RenderFlashed = 1;
1084 UpdateTaskItemButton(TaskItem);
1085 }
1086
1087 BOOL FlashTask(IN HWND hWnd)
1088 {
1089 PTASK_ITEM TaskItem;
1090
1091 TaskItem = FindTaskItem(hWnd);
1092 if (TaskItem != NULL)
1093 {
1094 TRACE("Flashing window 0x%p on button %d\n", hWnd, TaskItem->Index);
1095 FlashTaskItem(TaskItem);
1096 return TRUE;
1097 }
1098
1099 return FALSE;
1100 }
1101
1102 VOID RedrawTaskItem(IN OUT PTASK_ITEM TaskItem)
1103 {
1104 PTASK_GROUP TaskGroup;
1105
1106 TaskGroup = TaskItem->Group;
1107 if (m_IsGroupingEnabled && TaskGroup != NULL)
1108 {
1109 if (TaskGroup->IsCollapsed && TaskGroup->Index >= 0)
1110 {
1111 UpdateTaskGroupButton(TaskGroup);
1112 }
1113 else if (TaskItem->Index >= 0)
1114 {
1115 goto UpdateTaskItem;
1116 }
1117 }
1118 else if (TaskItem->Index >= 0)
1119 {
1120 UpdateTaskItem:
1121 TaskItem->RenderFlashed = 0;
1122 UpdateTaskItemButton(TaskItem);
1123 }
1124 }
1125
1126
1127 BOOL RedrawTask(IN HWND hWnd)
1128 {
1129 PTASK_ITEM TaskItem;
1130
1131 TaskItem = FindTaskItem(hWnd);
1132 if (TaskItem != NULL)
1133 {
1134 RedrawTaskItem(TaskItem);
1135 return TRUE;
1136 }
1137
1138 return FALSE;
1139 }
1140
1141 VOID UpdateButtonsSize(IN BOOL bRedrawDisabled)
1142 {
1143 RECT rcClient;
1144 UINT uiRows, uiMax, uiMin, uiBtnsPerLine, ui;
1145 LONG NewBtnSize;
1146 BOOL Horizontal;
1147
1148 /* Update the size of the image list if needed */
1149 int cx, cy;
1150 ImageList_GetIconSize(m_ImageList, &cx, &cy);
1151 if (cx != GetSystemMetrics(SM_CXSMICON) || cy != GetSystemMetrics(SM_CYSMICON))
1152 {
1153 ImageList_SetIconSize(m_ImageList, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
1154
1155 /* SetIconSize removes all icons so we have to reinsert them */
1156 PTASK_ITEM TaskItem = m_TaskItems;
1157 PTASK_ITEM LastTaskItem = m_TaskItems + m_TaskItemCount;
1158 while (TaskItem != LastTaskItem)
1159 {
1160 TaskItem->IconIndex = -1;
1161 UpdateTaskItemButton(TaskItem);
1162
1163 TaskItem++;
1164 }
1165 m_TaskBar.SetImageList(m_ImageList);
1166 }
1167
1168 if (GetClientRect(&rcClient) && !IsRectEmpty(&rcClient))
1169 {
1170 if (m_ButtonCount > 0)
1171 {
1172 Horizontal = m_Tray->IsHorizontal();
1173
1174 if (Horizontal)
1175 {
1176 TBMETRICS tbm = { 0 };
1177 tbm.cbSize = sizeof(tbm);
1178 tbm.dwMask = TBMF_BUTTONSPACING;
1179 m_TaskBar.GetMetrics(&tbm);
1180
1181 if (m_ButtonSize.cy + tbm.cyButtonSpacing != 0)
1182 uiRows = (rcClient.bottom + tbm.cyButtonSpacing) / (m_ButtonSize.cy + tbm.cyButtonSpacing);
1183 else
1184 uiRows = 1;
1185
1186 if (uiRows == 0)
1187 uiRows = 1;
1188
1189 uiBtnsPerLine = (m_ButtonCount + uiRows - 1) / uiRows;
1190 }
1191 else
1192 {
1193 uiBtnsPerLine = 1;
1194 uiRows = m_ButtonCount;
1195 }
1196
1197 if (!bRedrawDisabled)
1198 m_TaskBar.BeginUpdate();
1199
1200 /* We might need to update the button spacing */
1201 int cxButtonSpacing = m_TaskBar.UpdateTbButtonSpacing(
1202 Horizontal, m_Theme != NULL,
1203 uiRows, uiBtnsPerLine);
1204
1205 /* Determine the minimum and maximum width of a button */
1206 uiMin = GetSystemMetrics(SM_CXSIZE) + (2 * GetSystemMetrics(SM_CXEDGE));
1207 if (Horizontal)
1208 {
1209 uiMax = GetSystemMetrics(SM_CXMINIMIZED);
1210
1211 /* Calculate the ideal width and make sure it's within the allowed range */
1212 NewBtnSize = (rcClient.right - (uiBtnsPerLine * cxButtonSpacing)) / uiBtnsPerLine;
1213
1214 if (NewBtnSize < (LONG) uiMin)
1215 NewBtnSize = uiMin;
1216 if (NewBtnSize >(LONG)uiMax)
1217 NewBtnSize = uiMax;
1218
1219 /* Recalculate how many buttons actually fit into one line */
1220 uiBtnsPerLine = rcClient.right / (NewBtnSize + cxButtonSpacing);
1221 if (uiBtnsPerLine == 0)
1222 uiBtnsPerLine++;
1223 }
1224 else
1225 {
1226 NewBtnSize = uiMax = rcClient.right;
1227 }
1228
1229 m_ButtonSize.cx = NewBtnSize;
1230
1231 m_ButtonsPerLine = uiBtnsPerLine;
1232
1233 for (ui = 0; ui != m_ButtonCount; ui++)
1234 {
1235 TBBUTTONINFOW tbbi = { 0 };
1236 tbbi.cbSize = sizeof(tbbi);
1237 tbbi.dwMask = TBIF_BYINDEX | TBIF_SIZE | TBIF_STATE;
1238 tbbi.cx = (INT) NewBtnSize;
1239 tbbi.fsState = TBSTATE_ENABLED;
1240
1241 /* Check if we're updating a button that is the last one in the
1242 line. If so, we need to set the TBSTATE_WRAP flag! */
1243 if (Horizontal)
1244 {
1245 if ((ui + 1) % uiBtnsPerLine == 0)
1246 tbbi.fsState |= TBSTATE_WRAP;
1247 }
1248 else
1249 {
1250 tbbi.fsState |= TBSTATE_WRAP;
1251 }
1252
1253 if (m_ActiveTaskItem != NULL &&
1254 m_ActiveTaskItem->Index == (INT)ui)
1255 {
1256 tbbi.fsState |= TBSTATE_CHECKED;
1257 }
1258
1259 m_TaskBar.SetButtonInfo(ui, &tbbi);
1260 }
1261 }
1262 else
1263 {
1264 m_ButtonsPerLine = 0;
1265 m_ButtonSize.cx = 0;
1266 }
1267 }
1268
1269 // FIXME: This seems to be enabling redraws prematurely, but moving it to its right place doesn't work!
1270 m_TaskBar.EndUpdate();
1271 }
1272
1273 BOOL CALLBACK EnumWindowsProc(IN HWND hWnd)
1274 {
1275 /* Only show windows that still exist and are visible and none of explorer's
1276 special windows (such as the desktop or the tray window) */
1277 if (::IsWindow(hWnd) && ::IsWindowVisible(hWnd) &&
1278 !m_Tray->IsSpecialHWND(hWnd))
1279 {
1280 DWORD exStyle = ::GetWindowLong(hWnd, GWL_EXSTYLE);
1281 /* Don't list popup windows and also no tool windows */
1282 if ((::GetWindow(hWnd, GW_OWNER) == NULL || exStyle & WS_EX_APPWINDOW) &&
1283 !(exStyle & WS_EX_TOOLWINDOW))
1284 {
1285 TRACE("Adding task for %p...\n", hWnd);
1286 AddTask(hWnd);
1287 }
1288
1289 }
1290
1291 return TRUE;
1292 }
1293
1294 static BOOL CALLBACK s_EnumWindowsProc(IN HWND hWnd, IN LPARAM lParam)
1295 {
1296 CTaskSwitchWnd * This = (CTaskSwitchWnd *) lParam;
1297
1298 return This->EnumWindowsProc(hWnd);
1299 }
1300
1301 BOOL RefreshWindowList()
1302 {
1303 TRACE("Refreshing window list...\n");
1304 /* Add all windows to the toolbar */
1305 return EnumWindows(s_EnumWindowsProc, (LPARAM)this);
1306 }
1307
1308 LRESULT OnThemeChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1309 {
1310 TRACE("OmThemeChanged\n");
1311
1312 if (m_Theme)
1313 CloseThemeData(m_Theme);
1314
1315 if (IsThemeActive())
1316 m_Theme = OpenThemeData(m_hWnd, L"TaskBand");
1317 else
1318 m_Theme = NULL;
1319
1320 return TRUE;
1321 }
1322
1323 LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1324 {
1325 if (!m_TaskBar.Initialize(m_hWnd))
1326 return FALSE;
1327
1328 SetWindowTheme(m_TaskBar.m_hWnd, L"TaskBand", NULL);
1329
1330 m_ImageList = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR32 | ILC_MASK, 0, 1000);
1331 m_TaskBar.SetImageList(m_ImageList);
1332
1333 /* Set proper spacing between buttons */
1334 m_TaskBar.UpdateTbButtonSpacing(m_Tray->IsHorizontal(), m_Theme != NULL);
1335
1336 /* Register the shell hook */
1337 m_ShellHookMsg = RegisterWindowMessageW(L"SHELLHOOK");
1338
1339 TRACE("ShellHookMsg got assigned number %d\n", m_ShellHookMsg);
1340
1341 RegisterShellHook(m_hWnd, 3); /* 1 if no NT! We're targeting NT so we don't care! */
1342
1343 RefreshWindowList();
1344
1345 /* Recalculate the button size */
1346 UpdateButtonsSize(FALSE);
1347
1348 #if DUMP_TASKS != 0
1349 SetTimer(hwnd, 1, 5000, NULL);
1350 #endif
1351 return TRUE;
1352 }
1353
1354 LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1355 {
1356 m_IsDestroying = TRUE;
1357
1358 /* Unregister the shell hook */
1359 RegisterShellHook(m_hWnd, FALSE);
1360
1361 CloseThemeData(m_Theme);
1362 DeleteAllTasks();
1363 return TRUE;
1364 }
1365
1366 BOOL HandleAppCommand(IN WPARAM wParam, IN LPARAM lParam)
1367 {
1368 BOOL Ret = FALSE;
1369
1370 switch (GET_APPCOMMAND_LPARAM(lParam))
1371 {
1372 case APPCOMMAND_BROWSER_SEARCH:
1373 Ret = SHFindFiles(NULL,
1374 NULL);
1375 break;
1376
1377 case APPCOMMAND_BROWSER_HOME:
1378 case APPCOMMAND_LAUNCH_MAIL:
1379 default:
1380 TRACE("Shell app command %d unhandled!\n", (INT) GET_APPCOMMAND_LPARAM(lParam));
1381 break;
1382 }
1383
1384 return Ret;
1385 }
1386
1387 LRESULT OnShellHook(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1388 {
1389 BOOL Ret = FALSE;
1390
1391 /* In case the shell hook wasn't registered properly, ignore WM_NULLs*/
1392 if (uMsg == 0)
1393 {
1394 bHandled = FALSE;
1395 return 0;
1396 }
1397
1398 TRACE("Received shell hook message: wParam=%08lx, lParam=%08lx\n", wParam, lParam);
1399
1400 switch ((INT) wParam)
1401 {
1402 case HSHELL_APPCOMMAND:
1403 Ret = HandleAppCommand(wParam, lParam);
1404 break;
1405
1406 case HSHELL_WINDOWCREATED:
1407 AddTask((HWND) lParam);
1408 break;
1409
1410 case HSHELL_WINDOWDESTROYED:
1411 /* The window still exists! Delay destroying it a bit */
1412 DeleteTask((HWND) lParam);
1413 break;
1414
1415 case HSHELL_RUDEAPPACTIVATED:
1416 case HSHELL_WINDOWACTIVATED:
1417 ActivateTask((HWND) lParam);
1418 break;
1419
1420 case HSHELL_FLASH:
1421 FlashTask((HWND) lParam);
1422 break;
1423
1424 case HSHELL_REDRAW:
1425 RedrawTask((HWND) lParam);
1426 break;
1427
1428 case HSHELL_TASKMAN:
1429 ::PostMessage(m_Tray->GetHWND(), TWM_OPENSTARTMENU, 0, 0);
1430 break;
1431
1432 case HSHELL_ACTIVATESHELLWINDOW:
1433 case HSHELL_LANGUAGE:
1434 case HSHELL_SYSMENU:
1435 case HSHELL_ENDTASK:
1436 case HSHELL_ACCESSIBILITYSTATE:
1437 case HSHELL_WINDOWREPLACED:
1438 case HSHELL_WINDOWREPLACING:
1439
1440 case HSHELL_GETMINRECT:
1441 default:
1442 {
1443 #if DEBUG_SHELL_HOOK
1444 int i, found;
1445 for (i = 0, found = 0; i != _countof(hshell_msg); i++)
1446 {
1447 if (hshell_msg[i].msg == (INT) wParam)
1448 {
1449 TRACE("Shell message %ws unhandled (lParam = 0x%p)!\n", hshell_msg[i].msg_name, lParam);
1450 found = 1;
1451 break;
1452 }
1453 }
1454 if (found)
1455 break;
1456 #endif
1457 TRACE("Shell message %d unhandled (lParam = 0x%p)!\n", (INT) wParam, lParam);
1458 break;
1459 }
1460 }
1461
1462 return Ret;
1463 }
1464
1465 VOID HandleTaskItemClick(IN OUT PTASK_ITEM TaskItem)
1466 {
1467 BOOL bIsMinimized;
1468 BOOL bIsActive;
1469
1470 if (::IsWindow(TaskItem->hWnd))
1471 {
1472 bIsMinimized = ::IsIconic(TaskItem->hWnd);
1473 bIsActive = (TaskItem == m_ActiveTaskItem);
1474
1475 TRACE("Active TaskItem %p, selected TaskItem %p\n", m_ActiveTaskItem, TaskItem);
1476 if (m_ActiveTaskItem)
1477 TRACE("Active TaskItem hWnd=%p, TaskItem hWnd %p\n", m_ActiveTaskItem->hWnd, TaskItem->hWnd);
1478
1479 TRACE("Valid button clicked. HWND=%p, IsMinimized=%s, IsActive=%s...\n",
1480 TaskItem->hWnd, bIsMinimized ? "Yes" : "No", bIsActive ? "Yes" : "No");
1481
1482 if (!bIsMinimized && bIsActive)
1483 {
1484 ::PostMessage(TaskItem->hWnd,
1485 WM_SYSCOMMAND,
1486 SC_MINIMIZE,
1487 0);
1488 TRACE("Valid button clicked. App window Minimized.\n");
1489 }
1490 else
1491 {
1492 if (bIsMinimized)
1493 {
1494 ::PostMessage(TaskItem->hWnd,
1495 WM_SYSCOMMAND,
1496 SC_RESTORE,
1497 0);
1498 TRACE("Valid button clicked. App window Restored.\n");
1499 }
1500
1501 SetForegroundWindow(TaskItem->hWnd);
1502 TRACE("Valid button clicked. App window Activated.\n");
1503 }
1504 }
1505 }
1506
1507 VOID HandleTaskGroupClick(IN OUT PTASK_GROUP TaskGroup)
1508 {
1509 /* TODO: Show task group menu */
1510 }
1511
1512 BOOL HandleButtonClick(IN WORD wIndex)
1513 {
1514 PTASK_ITEM TaskItem;
1515 PTASK_GROUP TaskGroup;
1516
1517 if (m_IsGroupingEnabled)
1518 {
1519 TaskGroup = FindTaskGroupByIndex((INT) wIndex);
1520 if (TaskGroup != NULL && TaskGroup->IsCollapsed)
1521 {
1522 HandleTaskGroupClick(TaskGroup);
1523 return TRUE;
1524 }
1525 }
1526
1527 TaskItem = FindTaskItemByIndex((INT) wIndex);
1528 if (TaskItem != NULL)
1529 {
1530 HandleTaskItemClick(TaskItem);
1531 return TRUE;
1532 }
1533
1534 return FALSE;
1535 }
1536
1537
1538 VOID HandleTaskItemRightClick(IN OUT PTASK_ITEM TaskItem)
1539 {
1540 POINT pt;
1541 GetCursorPos(&pt);
1542
1543 SetForegroundWindow(TaskItem->hWnd);
1544
1545 ActivateTask(TaskItem->hWnd);
1546
1547 ::SendMessageW(TaskItem->hWnd, WM_POPUPSYSTEMMENU, 0, MAKELPARAM(pt.x, pt.y));
1548 }
1549
1550 VOID HandleTaskGroupRightClick(IN OUT PTASK_GROUP TaskGroup)
1551 {
1552 /* TODO: Show task group right click menu */
1553 }
1554
1555 BOOL HandleButtonRightClick(IN WORD wIndex)
1556 {
1557 PTASK_ITEM TaskItem;
1558 PTASK_GROUP TaskGroup;
1559 if (m_IsGroupingEnabled)
1560 {
1561 TaskGroup = FindTaskGroupByIndex((INT) wIndex);
1562 if (TaskGroup != NULL && TaskGroup->IsCollapsed)
1563 {
1564 HandleTaskGroupRightClick(TaskGroup);
1565 return TRUE;
1566 }
1567 }
1568
1569 TaskItem = FindTaskItemByIndex((INT) wIndex);
1570
1571 if (TaskItem != NULL)
1572 {
1573 HandleTaskItemRightClick(TaskItem);
1574 return TRUE;
1575 }
1576
1577 return FALSE;
1578 }
1579
1580
1581 LRESULT HandleItemPaint(IN OUT NMTBCUSTOMDRAW *nmtbcd)
1582 {
1583 LRESULT Ret = CDRF_DODEFAULT;
1584 PTASK_GROUP TaskGroup;
1585 PTASK_ITEM TaskItem;
1586
1587 TaskItem = FindTaskItemByIndex((INT) nmtbcd->nmcd.dwItemSpec);
1588 TaskGroup = FindTaskGroupByIndex((INT) nmtbcd->nmcd.dwItemSpec);
1589 if (TaskGroup == NULL && TaskItem != NULL)
1590 {
1591 ASSERT(TaskItem != NULL);
1592
1593 if (TaskItem != NULL && ::IsWindow(TaskItem->hWnd))
1594 {
1595 /* Make the entire button flashing if necessary */
1596 if (nmtbcd->nmcd.uItemState & CDIS_MARKED)
1597 {
1598 Ret = TBCDRF_NOBACKGROUND;
1599 if (!m_Theme)
1600 {
1601 SelectObject(nmtbcd->nmcd.hdc, GetSysColorBrush(COLOR_HIGHLIGHT));
1602 Rectangle(nmtbcd->nmcd.hdc,
1603 nmtbcd->nmcd.rc.left,
1604 nmtbcd->nmcd.rc.top,
1605 nmtbcd->nmcd.rc.right,
1606 nmtbcd->nmcd.rc.bottom);
1607 }
1608 else
1609 {
1610 DrawThemeBackground(m_Theme, nmtbcd->nmcd.hdc, TDP_FLASHBUTTON, 0, &nmtbcd->nmcd.rc, 0);
1611 }
1612 nmtbcd->clrText = GetSysColor(COLOR_HIGHLIGHTTEXT);
1613 return Ret;
1614 }
1615 }
1616 }
1617 else if (TaskGroup != NULL)
1618 {
1619 /* FIXME: Implement painting for task groups */
1620 }
1621 return Ret;
1622 }
1623
1624 LRESULT HandleToolbarNotification(IN const NMHDR *nmh)
1625 {
1626 LRESULT Ret = 0;
1627
1628 switch (nmh->code)
1629 {
1630 case NM_CUSTOMDRAW:
1631 {
1632 LPNMTBCUSTOMDRAW nmtbcd = (LPNMTBCUSTOMDRAW) nmh;
1633
1634 switch (nmtbcd->nmcd.dwDrawStage)
1635 {
1636
1637 case CDDS_ITEMPREPAINT:
1638 Ret = HandleItemPaint(nmtbcd);
1639 break;
1640
1641 case CDDS_PREPAINT:
1642 Ret = CDRF_NOTIFYITEMDRAW;
1643 break;
1644
1645 default:
1646 Ret = CDRF_DODEFAULT;
1647 break;
1648 }
1649 break;
1650 }
1651 }
1652
1653 return Ret;
1654 }
1655
1656 LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1657 {
1658 HDC hdc = (HDC) wParam;
1659
1660 if (!IsAppThemed())
1661 {
1662 bHandled = FALSE;
1663 return 0;
1664 }
1665
1666 RECT rect;
1667 GetClientRect(&rect);
1668 DrawThemeParentBackground(m_hWnd, hdc, &rect);
1669
1670 return TRUE;
1671 }
1672
1673 LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1674 {
1675 SIZE szClient;
1676
1677 szClient.cx = LOWORD(lParam);
1678 szClient.cy = HIWORD(lParam);
1679 if (m_TaskBar.m_hWnd != NULL)
1680 {
1681 m_TaskBar.SetWindowPos(NULL, 0, 0, szClient.cx, szClient.cy, SWP_NOZORDER);
1682
1683 UpdateButtonsSize(FALSE);
1684 }
1685 return TRUE;
1686 }
1687
1688 LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1689 {
1690 LRESULT Ret = TRUE;
1691 /* We want the tray window to be draggable everywhere, so make the control
1692 appear transparent */
1693 Ret = DefWindowProc(uMsg, wParam, lParam);
1694 if (Ret != HTVSCROLL && Ret != HTHSCROLL)
1695 Ret = HTTRANSPARENT;
1696 return Ret;
1697 }
1698
1699 LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1700 {
1701 LRESULT Ret = TRUE;
1702 if (lParam != 0 && (HWND) lParam == m_TaskBar.m_hWnd)
1703 {
1704 HandleButtonClick(LOWORD(wParam));
1705 }
1706 return Ret;
1707 }
1708
1709 LRESULT OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1710 {
1711 LRESULT Ret = TRUE;
1712 const NMHDR *nmh = (const NMHDR *) lParam;
1713
1714 if (nmh->hwndFrom == m_TaskBar.m_hWnd)
1715 {
1716 Ret = HandleToolbarNotification(nmh);
1717 }
1718 return Ret;
1719 }
1720
1721 LRESULT OnEnableGrouping(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1722 {
1723 LRESULT Ret = m_IsGroupingEnabled;
1724 if ((BOOL)wParam != m_IsGroupingEnabled)
1725 {
1726 m_IsGroupingEnabled = (BOOL)wParam;
1727
1728 /* Collapse or expand groups if necessary */
1729 UpdateButtonsSize(FALSE);
1730 }
1731 return Ret;
1732 }
1733
1734 LRESULT OnUpdateTaskbarPos(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1735 {
1736 /* Update the button spacing */
1737 m_TaskBar.UpdateTbButtonSpacing(m_Tray->IsHorizontal(), m_Theme != NULL);
1738 return TRUE;
1739 }
1740
1741 LRESULT OnTaskbarSettingsChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1742 {
1743 TaskbarSettings* newSettings = (TaskbarSettings*)lParam;
1744 if (newSettings->bGroupButtons != g_TaskbarSettings.bGroupButtons)
1745 {
1746 /* TODO: Toggle grouping */
1747 g_TaskbarSettings.bGroupButtons = newSettings->bGroupButtons;
1748 }
1749
1750 return 0;
1751 }
1752
1753 LRESULT OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1754 {
1755 LRESULT Ret = 0;
1756 INT_PTR iBtn = -1;
1757
1758 if (m_TaskBar.m_hWnd != NULL)
1759 {
1760 POINT pt;
1761
1762 pt.x = GET_X_LPARAM(lParam);
1763 pt.y = GET_Y_LPARAM(lParam);
1764
1765 ::ScreenToClient(m_TaskBar.m_hWnd, &pt);
1766
1767 iBtn = m_TaskBar.HitTest(&pt);
1768 if (iBtn >= 0)
1769 {
1770 HandleButtonRightClick(iBtn);
1771 }
1772 }
1773 if (iBtn < 0)
1774 {
1775 /* Not on a taskbar button, so forward message to tray */
1776 Ret = SendMessage(m_Tray->GetHWND(), uMsg, wParam, lParam);
1777 }
1778 return Ret;
1779 }
1780
1781 LRESULT OnKludgeItemRect(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1782 {
1783 PTASK_ITEM TaskItem = FindTaskItem((HWND) wParam);
1784 if (TaskItem)
1785 {
1786 RECT* prcMinRect = (RECT*) lParam;
1787 RECT rcItem, rcToolbar;
1788 m_TaskBar.GetItemRect(TaskItem->Index, &rcItem);
1789 m_TaskBar.GetWindowRect(&rcToolbar);
1790
1791 OffsetRect(&rcItem, rcToolbar.left, rcToolbar.top);
1792
1793 *prcMinRect = rcItem;
1794 return TRUE;
1795 }
1796 return FALSE;
1797 }
1798
1799 LRESULT OnMouseActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1800 {
1801 return MA_NOACTIVATE;
1802 }
1803
1804 LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1805 {
1806 #if DUMP_TASKS != 0
1807 switch (wParam)
1808 {
1809 case 1:
1810 DumpTasks();
1811 break;
1812 }
1813 #endif
1814 return TRUE;
1815 }
1816
1817 LRESULT OnSetFont(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1818 {
1819 return m_TaskBar.SendMessageW(uMsg, wParam, lParam);
1820 }
1821
1822 LRESULT OnSettingChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1823 {
1824 if (wParam == SPI_SETNONCLIENTMETRICS)
1825 {
1826 /* Don't update the font, this will be done when we get a WM_SETFONT from our parent */
1827 UpdateButtonsSize(FALSE);
1828 }
1829
1830 return 0;
1831 }
1832
1833 HRESULT Initialize(IN HWND hWndParent, IN OUT ITrayWindow *tray)
1834 {
1835 m_Tray = tray;
1836 m_IsGroupingEnabled = TRUE; /* FIXME */
1837 Create(hWndParent, 0, szRunningApps, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP);
1838 if (!m_hWnd)
1839 return E_FAIL;
1840 return S_OK;
1841 }
1842
1843 HRESULT WINAPI GetWindow(HWND* phwnd)
1844 {
1845 if (!phwnd)
1846 return E_INVALIDARG;
1847 *phwnd = m_hWnd;
1848 return S_OK;
1849 }
1850
1851 HRESULT WINAPI ContextSensitiveHelp(BOOL fEnterMode)
1852 {
1853 return E_NOTIMPL;
1854 }
1855
1856 DECLARE_WND_CLASS_EX(szTaskSwitchWndClass, CS_DBLCLKS, COLOR_3DFACE)
1857
1858 BEGIN_MSG_MAP(CTaskSwitchWnd)
1859 MESSAGE_HANDLER(WM_THEMECHANGED, OnThemeChanged)
1860 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
1861 MESSAGE_HANDLER(WM_SIZE, OnSize)
1862 MESSAGE_HANDLER(WM_CREATE, OnCreate)
1863 MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
1864 MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest)
1865 MESSAGE_HANDLER(WM_COMMAND, OnCommand)
1866 MESSAGE_HANDLER(WM_NOTIFY, OnNotify)
1867 MESSAGE_HANDLER(TSWM_ENABLEGROUPING, OnEnableGrouping)
1868 MESSAGE_HANDLER(TSWM_UPDATETASKBARPOS, OnUpdateTaskbarPos)
1869 MESSAGE_HANDLER(TWM_SETTINGSCHANGED, OnTaskbarSettingsChanged)
1870 MESSAGE_HANDLER(WM_CONTEXTMENU, OnContextMenu)
1871 MESSAGE_HANDLER(WM_TIMER, OnTimer)
1872 MESSAGE_HANDLER(WM_SETFONT, OnSetFont)
1873 MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChanged)
1874 MESSAGE_HANDLER(m_ShellHookMsg, OnShellHook)
1875 MESSAGE_HANDLER(WM_MOUSEACTIVATE, OnMouseActivate)
1876 MESSAGE_HANDLER(WM_KLUDGEMINRECT, OnKludgeItemRect)
1877 END_MSG_MAP()
1878
1879 DECLARE_NOT_AGGREGATABLE(CTaskSwitchWnd)
1880
1881 DECLARE_PROTECT_FINAL_CONSTRUCT()
1882 BEGIN_COM_MAP(CTaskSwitchWnd)
1883 COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow)
1884 END_COM_MAP()
1885 };
1886
1887 HRESULT CTaskSwitchWnd_CreateInstance(IN HWND hWndParent, IN OUT ITrayWindow *Tray, REFIID riid, void **ppv)
1888 {
1889 return ShellObjectCreatorInit<CTaskSwitchWnd>(hWndParent, Tray, riid, ppv);
1890 }