Reactos
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#include <regstr.h>
24#include <shlwapi_undoc.h>
25
26/* Set DUMP_TASKS to 1 to enable a dump of the tasks and task groups every
27 5 seconds */
28#define DUMP_TASKS 0
29#define DEBUG_SHELL_HOOK 0
30
31#define MAX_TASKS_COUNT (0x7FFF)
32#define TASK_ITEM_ARRAY_ALLOC 64
33
34//************************************************************************
35// Fullscreen windows (a.k.a. rude apps) checker
36
37#define TIMER_ID_VALIDATE_RUDE_APP 5
38#define VALIDATE_RUDE_INTERVAL 1000
39#define VALIDATE_RUDE_MAX_COUNT 5
40
41static BOOL
42SHELL_GetMonitorRect(
43 _In_opt_ HMONITOR hMonitor,
44 _Out_opt_ PRECT prcDest,
45 _In_ BOOL bWorkAreaOnly)
46{
47 MONITORINFO mi = { sizeof(mi) };
48 if (!hMonitor || !::GetMonitorInfoW(hMonitor, &mi))
49 {
50 if (!prcDest)
51 return FALSE;
52
53 if (bWorkAreaOnly)
54 ::SystemParametersInfoW(SPI_GETWORKAREA, 0, prcDest, 0);
55 else
56 ::SetRect(prcDest, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
57
58 return FALSE;
59 }
60
61 if (prcDest)
62 *prcDest = (bWorkAreaOnly ? mi.rcWork : mi.rcMonitor);
63 return TRUE;
64}
65
66static BOOL
67SHELL_IsParentOwnerOrSelf(_In_ HWND hwndTarget, _In_ HWND hWnd)
68{
69 for (; hWnd; hWnd = ::GetParent(hWnd))
70 {
71 if (hWnd == hwndTarget)
72 return TRUE;
73 }
74 return FALSE;
75}
76
77static BOOL
78SHELL_IsRudeWindowActive(_In_ HWND hWnd)
79{
80 HWND hwndFore = ::GetForegroundWindow();
81 DWORD dwThreadId = ::GetWindowThreadProcessId(hWnd, NULL);
82 return dwThreadId == ::GetWindowThreadProcessId(hwndFore, NULL) ||
83 SHELL_IsParentOwnerOrSelf(hWnd, hwndFore);
84}
85
86static BOOL
87SHELL_IsRudeWindow(_In_opt_ HMONITOR hMonitor, _In_ HWND hWnd, _In_ BOOL bDontCheckActive)
88{
89 if (!::IsWindowVisible(hWnd) || hWnd == ::GetDesktopWindow())
90 return FALSE;
91
92 RECT rcMonitor;
93 SHELL_GetMonitorRect(hMonitor, &rcMonitor, FALSE);
94
95 DWORD style = ::GetWindowLongPtrW(hWnd, GWL_STYLE);
96
97 RECT rcWnd;
98 enum { CHECK_STYLE = WS_THICKFRAME | WS_DLGFRAME | WS_BORDER };
99 if ((style & CHECK_STYLE) == CHECK_STYLE)
100 {
101 ::GetClientRect(hWnd, &rcWnd); // Ignore frame
102 ::MapWindowPoints(hWnd, NULL, (PPOINT)&rcWnd, sizeof(RECT) / sizeof(POINT));
103 }
104 else
105 {
106 ::GetWindowRect(hWnd, &rcWnd);
107 }
108
109 RECT rcUnion;
110 ::UnionRect(&rcUnion, &rcWnd, &rcMonitor);
111
112 return ::EqualRect(&rcUnion, &rcWnd) && (bDontCheckActive || SHELL_IsRudeWindowActive(hWnd));
113}
114
115////////////////////////////////////////////////////////////////
116
117const WCHAR szTaskSwitchWndClass[] = L"MSTaskSwWClass";
118const WCHAR szRunningApps[] = L"Running Applications";
119
120#if DEBUG_SHELL_HOOK
121const struct {
122 INT msg;
123 LPCWSTR msg_name;
124} hshell_msg [] = {
125 { HSHELL_WINDOWCREATED, L"HSHELL_WINDOWCREATED" },
126 { HSHELL_WINDOWDESTROYED, L"HSHELL_WINDOWDESTROYED" },
127 { HSHELL_ACTIVATESHELLWINDOW, L"HSHELL_ACTIVATESHELLWINDOW" },
128 { HSHELL_WINDOWACTIVATED, L"HSHELL_WINDOWACTIVATED" },
129 { HSHELL_GETMINRECT, L"HSHELL_GETMINRECT" },
130 { HSHELL_REDRAW, L"HSHELL_REDRAW" },
131 { HSHELL_TASKMAN, L"HSHELL_TASKMAN" },
132 { HSHELL_LANGUAGE, L"HSHELL_LANGUAGE" },
133 { HSHELL_SYSMENU, L"HSHELL_SYSMENU" },
134 { HSHELL_ENDTASK, L"HSHELL_ENDTASK" },
135 { HSHELL_ACCESSIBILITYSTATE, L"HSHELL_ACCESSIBILITYSTATE" },
136 { HSHELL_APPCOMMAND, L"HSHELL_APPCOMMAND" },
137 { HSHELL_WINDOWREPLACED, L"HSHELL_WINDOWREPLACED" },
138 { HSHELL_WINDOWREPLACING, L"HSHELL_WINDOWREPLACING" },
139 { HSHELL_RUDEAPPACTIVATED, L"HSHELL_RUDEAPPACTIVATED" },
140};
141#endif
142
143typedef struct _TASK_GROUP
144{
145 /* We have to use a linked list instead of an array so we don't have to
146 update all pointers to groups in the task item array when removing
147 groups. */
148 struct _TASK_GROUP *Next;
149
150 DWORD dwTaskCount;
151 DWORD dwProcessId;
152 INT Index;
153 union
154 {
155 DWORD dwFlags;
156 struct
157 {
158
159 DWORD IsCollapsed : 1;
160 };
161 };
162} TASK_GROUP, *PTASK_GROUP;
163
164typedef struct _TASK_ITEM
165{
166 HWND hWnd;
167 PTASK_GROUP Group;
168 INT Index;
169 INT IconIndex;
170
171 union
172 {
173 DWORD dwFlags;
174 struct
175 {
176
177 /* IsFlashing is TRUE when the task bar item should be flashing. */
178 DWORD IsFlashing : 1;
179
180 /* RenderFlashed is only TRUE if the task bar item should be
181 drawn with a flash. */
182 DWORD RenderFlashed : 1;
183 };
184 };
185} TASK_ITEM, *PTASK_ITEM;
186
187
188class CHardErrorThread
189{
190 DWORD m_ThreadId;
191 HANDLE m_hThread;
192 LONG m_bThreadRunning;
193 DWORD m_Status;
194 DWORD m_dwType;
195 CStringW m_Title;
196 CStringW m_Text;
197public:
198
199 CHardErrorThread():
200 m_ThreadId(0),
201 m_hThread(NULL),
202 m_bThreadRunning(FALSE),
203 m_Status(NULL),
204 m_dwType(NULL)
205 {
206 }
207
208 ~CHardErrorThread()
209 {
210 if (m_bThreadRunning)
211 {
212 /* Try to unstuck Show */
213 PostThreadMessage(m_ThreadId, WM_QUIT, 0, 0);
214 DWORD ret = WaitForSingleObject(m_hThread, 3*1000);
215 if (ret == WAIT_TIMEOUT)
216 TerminateThread(m_hThread, 0);
217 CloseHandle(m_hThread);
218 }
219 }
220
221 HRESULT ThreadProc()
222 {
223 HRESULT hr;
224 CComPtr<IUserNotification> pnotification;
225
226 hr = OleInitialize(NULL);
227 if (FAILED_UNEXPECTEDLY(hr))
228 return hr;
229
230 hr = CoCreateInstance(CLSID_UserNotification,
231 NULL,
232 CLSCTX_INPROC_SERVER,
233 IID_PPV_ARG(IUserNotification, &pnotification));
234 if (FAILED_UNEXPECTEDLY(hr))
235 return hr;
236
237 hr = pnotification->SetBalloonInfo(m_Title, m_Text, NIIF_WARNING);
238 if (FAILED_UNEXPECTEDLY(hr))
239 return hr;
240
241 hr = pnotification->SetIconInfo(NULL, NULL);
242 if (FAILED_UNEXPECTEDLY(hr))
243 return hr;
244
245 /* Show will block until the balloon closes */
246 hr = pnotification->Show(NULL, 0);
247 if (FAILED_UNEXPECTEDLY(hr))
248 return hr;
249
250 return S_OK;
251 }
252
253 static DWORD CALLBACK s_HardErrorThreadProc(IN OUT LPVOID lpParameter)
254 {
255 CHardErrorThread* pThis = reinterpret_cast<CHardErrorThread*>(lpParameter);
256 pThis->ThreadProc();
257 CloseHandle(pThis->m_hThread);
258 OleUninitialize();
259 InterlockedExchange(&pThis->m_bThreadRunning, FALSE);
260 return 0;
261 }
262
263 void StartThread(PBALLOON_HARD_ERROR_DATA pData)
264 {
265 BOOL bIsRunning = InterlockedExchange(&m_bThreadRunning, TRUE);
266
267 /* Ignore the new message if we are already showing one */
268 if (bIsRunning)
269 return;
270
271 m_Status = pData->Status;
272 m_dwType = pData->dwType;
273 m_Title = (PWCHAR)((ULONG_PTR)pData + pData->TitleOffset);
274 m_Text = (PWCHAR)((ULONG_PTR)pData + pData->MessageOffset);
275 m_hThread = CreateThread(NULL, 0, s_HardErrorThreadProc, this, 0, &m_ThreadId);
276 if (!m_hThread)
277 {
278 m_bThreadRunning = FALSE;
279 }
280 }
281};
282
283class CTaskToolbar :
284 public CWindowImplBaseT< CToolbar<TASK_ITEM>, CControlWinTraits >
285{
286public:
287 INT UpdateTbButtonSpacing(IN BOOL bHorizontal, IN BOOL bThemed, IN UINT uiRows = 0, IN UINT uiBtnsPerLine = 0)
288 {
289 TBMETRICS tbm;
290
291 tbm.cbSize = sizeof(tbm);
292 tbm.dwMask = TBMF_BARPAD | TBMF_BUTTONSPACING;
293
294 tbm.cxBarPad = tbm.cyBarPad = 0;
295
296 if (bThemed)
297 {
298 tbm.cxButtonSpacing = 0;
299 tbm.cyButtonSpacing = 0;
300 }
301 else
302 {
303 if (bHorizontal || uiBtnsPerLine > 1)
304 tbm.cxButtonSpacing = (3 * GetSystemMetrics(SM_CXEDGE) / 2);
305 else
306 tbm.cxButtonSpacing = 0;
307
308 if (!bHorizontal || uiRows > 1)
309 tbm.cyButtonSpacing = (3 * GetSystemMetrics(SM_CYEDGE) / 2);
310 else
311 tbm.cyButtonSpacing = 0;
312 }
313
314 SetMetrics(&tbm);
315
316 return tbm.cxButtonSpacing;
317 }
318
319 VOID BeginUpdate()
320 {
321 SetRedraw(FALSE);
322 }
323
324 VOID EndUpdate()
325 {
326 SendMessageW(WM_SETREDRAW, TRUE);
327 InvalidateRect(NULL, TRUE);
328 }
329
330 BOOL SetButtonCommandId(IN INT iButtonIndex, IN INT iCommandId)
331 {
332 TBBUTTONINFO tbbi;
333
334 tbbi.cbSize = sizeof(tbbi);
335 tbbi.dwMask = TBIF_BYINDEX | TBIF_COMMAND;
336 tbbi.idCommand = iCommandId;
337
338 return SetButtonInfo(iButtonIndex, &tbbi) != 0;
339 }
340
341 LRESULT OnNcHitTestToolbar(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
342 {
343 POINT pt;
344
345 /* See if the mouse is on a button */
346 pt.x = GET_X_LPARAM(lParam);
347 pt.y = GET_Y_LPARAM(lParam);
348 ScreenToClient(&pt);
349
350 INT index = HitTest(&pt);
351 if (index < 0)
352 {
353 /* Make the control appear to be transparent outside of any buttons */
354 return HTTRANSPARENT;
355 }
356
357 bHandled = FALSE;
358 return 0;
359 }
360
361public:
362 BEGIN_MSG_MAP(CNotifyToolbar)
363 MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTestToolbar)
364 END_MSG_MAP()
365
366 BOOL Initialize(HWND hWndParent)
367 {
368 DWORD styles = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN |
369 TBSTYLE_TOOLTIPS | TBSTYLE_WRAPABLE | TBSTYLE_LIST | TBSTYLE_TRANSPARENT |
370 CCS_TOP | CCS_NORESIZE | CCS_NODIVIDER;
371
372 // HACK & FIXME: CORE-18016
373 HWND toolbar = CToolbar::Create(hWndParent, styles);
374 m_hWnd = NULL;
375 return SubclassWindow(toolbar);
376 }
377};
378
379class CTaskSwitchWnd :
380 public CComCoClass<CTaskSwitchWnd>,
381 public CComObjectRootEx<CComMultiThreadModelNoCS>,
382 public CWindowImpl < CTaskSwitchWnd, CWindow, CControlWinTraits >,
383 public IOleWindow
384{
385 CTaskToolbar m_TaskBar;
386
387 CComPtr<ITrayWindow> m_Tray;
388
389 UINT m_ShellHookMsg;
390
391 WORD m_TaskItemCount;
392 WORD m_AllocatedTaskItems;
393
394 PTASK_GROUP m_TaskGroups;
395 PTASK_ITEM m_TaskItems;
396 PTASK_ITEM m_ActiveTaskItem;
397
398 HTHEME m_Theme;
399 UINT m_ButtonsPerLine;
400 WORD m_ButtonCount;
401
402 HIMAGELIST m_ImageList;
403
404 BOOL m_IsGroupingEnabled;
405 BOOL m_IsDestroying;
406
407 INT m_nRudeAppValidationCounter;
408
409 SIZE m_ButtonSize;
410
411 UINT m_uHardErrorMsg;
412 CHardErrorThread m_HardErrorThread;
413
414public:
415 CTaskSwitchWnd() :
416 m_ShellHookMsg(NULL),
417 m_TaskItemCount(0),
418 m_AllocatedTaskItems(0),
419 m_TaskGroups(NULL),
420 m_TaskItems(NULL),
421 m_ActiveTaskItem(NULL),
422 m_Theme(NULL),
423 m_ButtonsPerLine(0),
424 m_ButtonCount(0),
425 m_ImageList(NULL),
426 m_IsGroupingEnabled(FALSE),
427 m_IsDestroying(FALSE),
428 m_nRudeAppValidationCounter(0)
429 {
430 ZeroMemory(&m_ButtonSize, sizeof(m_ButtonSize));
431 m_uHardErrorMsg = RegisterWindowMessageW(L"HardError");
432 }
433 virtual ~CTaskSwitchWnd() { }
434
435 INT GetWndTextFromTaskItem(IN PTASK_ITEM TaskItem, LPWSTR szBuf, DWORD cchBuf)
436 {
437 /* Get the window text without sending a message so we don't hang if an
438 application isn't responding! */
439 return InternalGetWindowText(TaskItem->hWnd, szBuf, cchBuf);
440 }
441
442
443#if DUMP_TASKS != 0
444 VOID DumpTasks()
445 {
446 PTASK_GROUP CurrentGroup;
447 PTASK_ITEM CurrentTaskItem, LastTaskItem;
448
449 TRACE("Tasks dump:\n");
450 if (m_IsGroupingEnabled)
451 {
452 CurrentGroup = m_TaskGroups;
453 while (CurrentGroup != NULL)
454 {
455 TRACE("- Group PID: 0x%p Tasks: %d Index: %d\n", CurrentGroup->dwProcessId, CurrentGroup->dwTaskCount, CurrentGroup->Index);
456
457 CurrentTaskItem = m_TaskItems;
458 LastTaskItem = CurrentTaskItem + m_TaskItemCount;
459 while (CurrentTaskItem != LastTaskItem)
460 {
461 if (CurrentTaskItem->Group == CurrentGroup)
462 {
463 TRACE(" + Task hwnd: 0x%p Index: %d\n", CurrentTaskItem->hWnd, CurrentTaskItem->Index);
464 }
465 CurrentTaskItem++;
466 }
467
468 CurrentGroup = CurrentGroup->Next;
469 }
470
471 CurrentTaskItem = m_TaskItems;
472 LastTaskItem = CurrentTaskItem + m_TaskItemCount;
473 while (CurrentTaskItem != LastTaskItem)
474 {
475 if (CurrentTaskItem->Group == NULL)
476 {
477 TRACE("- Task hwnd: 0x%p Index: %d\n", CurrentTaskItem->hWnd, CurrentTaskItem->Index);
478 }
479 CurrentTaskItem++;
480 }
481 }
482 else
483 {
484 CurrentTaskItem = m_TaskItems;
485 LastTaskItem = CurrentTaskItem + m_TaskItemCount;
486 while (CurrentTaskItem != LastTaskItem)
487 {
488 TRACE("- Task hwnd: 0x%p Index: %d\n", CurrentTaskItem->hWnd, CurrentTaskItem->Index);
489 CurrentTaskItem++;
490 }
491 }
492 }
493#endif
494
495 VOID UpdateIndexesAfter(IN INT iIndex, BOOL bInserted)
496 {
497 PTASK_GROUP CurrentGroup;
498 PTASK_ITEM CurrentTaskItem, LastTaskItem;
499 INT NewIndex;
500
501 int offset = bInserted ? +1 : -1;
502
503 if (m_IsGroupingEnabled)
504 {
505 /* Update all affected groups */
506 CurrentGroup = m_TaskGroups;
507 while (CurrentGroup != NULL)
508 {
509 if (CurrentGroup->IsCollapsed &&
510 CurrentGroup->Index >= iIndex)
511 {
512 /* Update the toolbar buttons */
513 NewIndex = CurrentGroup->Index + offset;
514 if (m_TaskBar.SetButtonCommandId(CurrentGroup->Index + offset, NewIndex))
515 {
516 CurrentGroup->Index = NewIndex;
517 }
518 else
519 CurrentGroup->Index = -1;
520 }
521
522 CurrentGroup = CurrentGroup->Next;
523 }
524 }
525
526 /* Update all affected task items */
527 CurrentTaskItem = m_TaskItems;
528 LastTaskItem = CurrentTaskItem + m_TaskItemCount;
529 while (CurrentTaskItem != LastTaskItem)
530 {
531 CurrentGroup = CurrentTaskItem->Group;
532 if (CurrentGroup != NULL)
533 {
534 if (!CurrentGroup->IsCollapsed &&
535 CurrentTaskItem->Index >= iIndex)
536 {
537 goto UpdateTaskItemBtn;
538 }
539 }
540 else if (CurrentTaskItem->Index >= iIndex)
541 {
542 UpdateTaskItemBtn:
543 /* Update the toolbar buttons */
544 NewIndex = CurrentTaskItem->Index + offset;
545 if (m_TaskBar.SetButtonCommandId(CurrentTaskItem->Index + offset, NewIndex))
546 {
547 CurrentTaskItem->Index = NewIndex;
548 }
549 else
550 CurrentTaskItem->Index = -1;
551 }
552
553 CurrentTaskItem++;
554 }
555 }
556
557
558 INT UpdateTaskGroupButton(IN PTASK_GROUP TaskGroup)
559 {
560 ASSERT(TaskGroup->Index >= 0);
561
562 /* FIXME: Implement */
563
564 return TaskGroup->Index;
565 }
566
567 VOID ExpandTaskGroup(IN PTASK_GROUP TaskGroup)
568 {
569 ASSERT(TaskGroup->dwTaskCount > 0);
570 ASSERT(TaskGroup->IsCollapsed);
571 ASSERT(TaskGroup->Index >= 0);
572
573 /* FIXME: Implement */
574 }
575
576 HICON GetWndIcon(HWND hwnd)
577 {
578 HICON hIcon = NULL;
579
580 /* Retrieve icon by sending a message */
581#define GET_ICON(type) \
582 SendMessageTimeout(hwnd, WM_GETICON, (type), 0, SMTO_NOTIMEOUTIFNOTHUNG, 100, (PDWORD_PTR)&hIcon)
583
584 LRESULT bAlive = GET_ICON(g_TaskbarSettings.bSmallIcons ? ICON_SMALL2 : ICON_BIG);
585 if (hIcon)
586 return hIcon;
587
588 if (bAlive)
589 {
590 bAlive = GET_ICON(ICON_SMALL);
591 if (hIcon)
592 return hIcon;
593 }
594
595 if (bAlive)
596 {
597 GET_ICON(g_TaskbarSettings.bSmallIcons ? ICON_BIG : ICON_SMALL2);
598 if (hIcon)
599 return hIcon;
600 }
601#undef GET_ICON
602
603 /* If we failed, retrieve icon from the window class */
604 hIcon = (HICON)GetClassLongPtr(hwnd, g_TaskbarSettings.bSmallIcons ? GCLP_HICONSM : GCLP_HICON);
605 if (hIcon)
606 return hIcon;
607
608 return (HICON)GetClassLongPtr(hwnd, g_TaskbarSettings.bSmallIcons ? GCLP_HICON : GCLP_HICONSM);
609 }
610
611 INT UpdateTaskItemButton(IN PTASK_ITEM TaskItem)
612 {
613 TBBUTTONINFO tbbi = { 0 };
614 HICON icon;
615 WCHAR windowText[255];
616
617 ASSERT(TaskItem->Index >= 0);
618
619 tbbi.cbSize = sizeof(tbbi);
620 tbbi.dwMask = TBIF_BYINDEX | TBIF_STATE | TBIF_TEXT | TBIF_IMAGE;
621 tbbi.fsState = TBSTATE_ENABLED;
622 if (m_ActiveTaskItem == TaskItem)
623 tbbi.fsState |= TBSTATE_CHECKED;
624
625 if (TaskItem->RenderFlashed)
626 tbbi.fsState |= TBSTATE_MARKED;
627
628 /* Check if we're updating a button that is the last one in the
629 line. If so, we need to set the TBSTATE_WRAP flag! */
630 if (!m_Tray->IsHorizontal() || (m_ButtonsPerLine != 0 &&
631 (TaskItem->Index + 1) % m_ButtonsPerLine == 0))
632 {
633 tbbi.fsState |= TBSTATE_WRAP;
634 }
635
636 if (GetWndTextFromTaskItem(TaskItem, windowText, _countof(windowText)) > 0)
637 {
638 tbbi.pszText = windowText;
639 }
640
641 icon = GetWndIcon(TaskItem->hWnd);
642 if (!icon)
643 icon = static_cast<HICON>(LoadImageW(NULL, MAKEINTRESOURCEW(OIC_SAMPLE), IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE));
644 TaskItem->IconIndex = ImageList_ReplaceIcon(m_ImageList, TaskItem->IconIndex, icon);
645 tbbi.iImage = TaskItem->IconIndex;
646
647 if (!m_TaskBar.SetButtonInfo(TaskItem->Index, &tbbi))
648 {
649 TaskItem->Index = -1;
650 return -1;
651 }
652
653 TRACE("Updated button %d for hwnd 0x%p\n", TaskItem->Index, TaskItem->hWnd);
654 return TaskItem->Index;
655 }
656
657 VOID RemoveIcon(IN PTASK_ITEM TaskItem)
658 {
659 TBBUTTONINFO tbbi;
660 PTASK_ITEM currentTaskItem, LastItem;
661
662 if (TaskItem->IconIndex == -1)
663 return;
664
665 tbbi.cbSize = sizeof(tbbi);
666 tbbi.dwMask = TBIF_IMAGE;
667
668 currentTaskItem = m_TaskItems;
669 LastItem = currentTaskItem + m_TaskItemCount;
670 while (currentTaskItem != LastItem)
671 {
672 if (currentTaskItem->IconIndex > TaskItem->IconIndex)
673 {
674 currentTaskItem->IconIndex--;
675 tbbi.iImage = currentTaskItem->IconIndex;
676
677 m_TaskBar.SetButtonInfo(currentTaskItem->Index, &tbbi);
678 }
679 currentTaskItem++;
680 }
681
682 ImageList_Remove(m_ImageList, TaskItem->IconIndex);
683 }
684
685 PTASK_ITEM FindLastTaskItemOfGroup(
686 IN PTASK_GROUP TaskGroup OPTIONAL,
687 IN PTASK_ITEM NewTaskItem OPTIONAL)
688 {
689 PTASK_ITEM TaskItem, LastTaskItem, FoundTaskItem = NULL;
690 DWORD dwTaskCount;
691
692 ASSERT(m_IsGroupingEnabled);
693
694 TaskItem = m_TaskItems;
695 LastTaskItem = TaskItem + m_TaskItemCount;
696
697 dwTaskCount = (TaskGroup != NULL ? TaskGroup->dwTaskCount : MAX_TASKS_COUNT);
698
699 ASSERT(dwTaskCount > 0);
700
701 while (TaskItem != LastTaskItem)
702 {
703 if (TaskItem->Group == TaskGroup)
704 {
705 if ((NewTaskItem != NULL && TaskItem != NewTaskItem) || NewTaskItem == NULL)
706 {
707 FoundTaskItem = TaskItem;
708 }
709
710 if (--dwTaskCount == 0)
711 {
712 /* We found the last task item in the group! */
713 break;
714 }
715 }
716
717 TaskItem++;
718 }
719
720 return FoundTaskItem;
721 }
722
723 INT CalculateTaskItemNewButtonIndex(IN PTASK_ITEM TaskItem)
724 {
725 PTASK_GROUP TaskGroup;
726 PTASK_ITEM LastTaskItem;
727
728 /* NOTE: This routine assumes that the group is *not* collapsed! */
729
730 TaskGroup = TaskItem->Group;
731 if (m_IsGroupingEnabled)
732 {
733 if (TaskGroup != NULL)
734 {
735 ASSERT(TaskGroup->Index < 0);
736 ASSERT(!TaskGroup->IsCollapsed);
737
738 if (TaskGroup->dwTaskCount > 1)
739 {
740 LastTaskItem = FindLastTaskItemOfGroup(TaskGroup, TaskItem);
741 if (LastTaskItem != NULL)
742 {
743 /* Since the group is expanded the task items must have an index */
744 ASSERT(LastTaskItem->Index >= 0);
745
746 return LastTaskItem->Index + 1;
747 }
748 }
749 }
750 else
751 {
752 /* Find the last NULL group button. NULL groups are added at the end of the
753 task item list when grouping is enabled */
754 LastTaskItem = FindLastTaskItemOfGroup(NULL, TaskItem);
755 if (LastTaskItem != NULL)
756 {
757 ASSERT(LastTaskItem->Index >= 0);
758
759 return LastTaskItem->Index + 1;
760 }
761 }
762 }
763
764 return m_ButtonCount;
765 }
766
767 INT AddTaskItemButton(IN OUT PTASK_ITEM TaskItem)
768 {
769 WCHAR windowText[255];
770 TBBUTTON tbBtn = { 0 };
771 INT iIndex;
772 HICON icon;
773
774 if (TaskItem->Index >= 0)
775 {
776 return UpdateTaskItemButton(TaskItem);
777 }
778
779 if (TaskItem->Group != NULL &&
780 TaskItem->Group->IsCollapsed)
781 {
782 /* The task group is collapsed, we only need to update the group button */
783 return UpdateTaskGroupButton(TaskItem->Group);
784 }
785
786 icon = GetWndIcon(TaskItem->hWnd);
787 if (!icon)
788 icon = static_cast<HICON>(LoadImageW(NULL, MAKEINTRESOURCEW(OIC_SAMPLE), IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE));
789 TaskItem->IconIndex = ImageList_ReplaceIcon(m_ImageList, -1, icon);
790
791 tbBtn.iBitmap = TaskItem->IconIndex;
792 tbBtn.fsState = TBSTATE_ENABLED | TBSTATE_ELLIPSES;
793 tbBtn.fsStyle = BTNS_CHECK | BTNS_NOPREFIX | BTNS_SHOWTEXT;
794 tbBtn.dwData = TaskItem->Index;
795
796 if (GetWndTextFromTaskItem(TaskItem, windowText, _countof(windowText)) > 0)
797 {
798 tbBtn.iString = (DWORD_PTR) windowText;
799 }
800
801 /* Find out where to insert the new button */
802 iIndex = CalculateTaskItemNewButtonIndex(TaskItem);
803 ASSERT(iIndex >= 0);
804 tbBtn.idCommand = iIndex;
805
806 m_TaskBar.BeginUpdate();
807
808 if (m_TaskBar.InsertButton(iIndex, &tbBtn))
809 {
810 UpdateIndexesAfter(iIndex, TRUE);
811
812 TRACE("Added button %d for hwnd 0x%p\n", iIndex, TaskItem->hWnd);
813
814 TaskItem->Index = iIndex;
815 m_ButtonCount++;
816
817 /* Update button sizes and fix the button wrapping */
818 UpdateButtonsSize(TRUE);
819 return iIndex;
820 }
821
822 m_TaskBar.EndUpdate();
823
824 return -1;
825 }
826
827 BOOL DeleteTaskItemButton(IN OUT PTASK_ITEM TaskItem)
828 {
829 PTASK_GROUP TaskGroup;
830 INT iIndex;
831
832 TaskGroup = TaskItem->Group;
833
834 if (TaskItem->Index >= 0)
835 {
836 if ((TaskGroup != NULL && !TaskGroup->IsCollapsed) ||
837 TaskGroup == NULL)
838 {
839 m_TaskBar.BeginUpdate();
840
841 RemoveIcon(TaskItem);
842 iIndex = TaskItem->Index;
843 if (m_TaskBar.DeleteButton(iIndex))
844 {
845 TaskItem->Index = -1;
846 m_ButtonCount--;
847
848 UpdateIndexesAfter(iIndex, FALSE);
849
850 /* Update button sizes and fix the button wrapping */
851 UpdateButtonsSize(TRUE);
852 return TRUE;
853 }
854
855 m_TaskBar.EndUpdate();
856 }
857 }
858
859 return FALSE;
860 }
861
862 PTASK_GROUP AddToTaskGroup(IN HWND hWnd)
863 {
864 DWORD dwProcessId;
865 PTASK_GROUP TaskGroup, *PrevLink;
866
867 if (!GetWindowThreadProcessId(hWnd,
868 &dwProcessId))
869 {
870 TRACE("Cannot get process id of hwnd 0x%p\n", hWnd);
871 return NULL;
872 }
873
874 /* Try to find an existing task group */
875 TaskGroup = m_TaskGroups;
876 PrevLink = &m_TaskGroups;
877 while (TaskGroup != NULL)
878 {
879 if (TaskGroup->dwProcessId == dwProcessId)
880 {
881 TaskGroup->dwTaskCount++;
882 return TaskGroup;
883 }
884
885 PrevLink = &TaskGroup->Next;
886 TaskGroup = TaskGroup->Next;
887 }
888
889 /* Allocate a new task group */
890 TaskGroup = (PTASK_GROUP) HeapAlloc(hProcessHeap,
891 HEAP_ZERO_MEMORY,
892 sizeof(*TaskGroup));
893 if (TaskGroup != NULL)
894 {
895 TaskGroup->dwTaskCount = 1;
896 TaskGroup->dwProcessId = dwProcessId;
897 TaskGroup->Index = -1;
898
899 /* Add the task group to the list */
900 *PrevLink = TaskGroup;
901 }
902
903 return TaskGroup;
904 }
905
906 VOID RemoveTaskFromTaskGroup(IN OUT PTASK_ITEM TaskItem)
907 {
908 PTASK_GROUP TaskGroup, CurrentGroup, *PrevLink;
909
910 TaskGroup = TaskItem->Group;
911 if (TaskGroup != NULL)
912 {
913 DWORD dwNewTaskCount = --TaskGroup->dwTaskCount;
914 if (dwNewTaskCount == 0)
915 {
916 /* Find the previous pointer in the chain */
917 CurrentGroup = m_TaskGroups;
918 PrevLink = &m_TaskGroups;
919 while (CurrentGroup != TaskGroup)
920 {
921 PrevLink = &CurrentGroup->Next;
922 CurrentGroup = CurrentGroup->Next;
923 }
924
925 /* Remove the group from the list */
926 ASSERT(TaskGroup == CurrentGroup);
927 *PrevLink = TaskGroup->Next;
928
929 /* Free the task group */
930 HeapFree(hProcessHeap,
931 0,
932 TaskGroup);
933 }
934 else if (TaskGroup->IsCollapsed &&
935 TaskGroup->Index >= 0)
936 {
937 if (dwNewTaskCount > 1)
938 {
939 /* FIXME: Check if we should expand the group */
940 /* Update the task group button */
941 UpdateTaskGroupButton(TaskGroup);
942 }
943 else
944 {
945 /* Expand the group of one task button to a task button */
946 ExpandTaskGroup(TaskGroup);
947 }
948 }
949 }
950 }
951
952 PTASK_ITEM FindTaskItem(IN HWND hWnd)
953 {
954 PTASK_ITEM TaskItem, LastItem;
955
956 TaskItem = m_TaskItems;
957 LastItem = TaskItem + m_TaskItemCount;
958 while (TaskItem != LastItem)
959 {
960 if (TaskItem->hWnd == hWnd)
961 return TaskItem;
962
963 TaskItem++;
964 }
965
966 return NULL;
967 }
968
969 PTASK_ITEM FindOtherTaskItem(IN HWND hWnd)
970 {
971 PTASK_ITEM LastItem, TaskItem;
972 PTASK_GROUP TaskGroup;
973 DWORD dwProcessId;
974
975 if (!GetWindowThreadProcessId(hWnd, &dwProcessId))
976 {
977 return NULL;
978 }
979
980 /* Try to find another task that belongs to the same
981 process as the given window */
982 TaskItem = m_TaskItems;
983 LastItem = TaskItem + m_TaskItemCount;
984 while (TaskItem != LastItem)
985 {
986 TaskGroup = TaskItem->Group;
987 if (TaskGroup != NULL)
988 {
989 if (TaskGroup->dwProcessId == dwProcessId)
990 return TaskItem;
991 }
992 else
993 {
994 DWORD dwProcessIdTask;
995
996 if (GetWindowThreadProcessId(TaskItem->hWnd,
997 &dwProcessIdTask) &&
998 dwProcessIdTask == dwProcessId)
999 {
1000 return TaskItem;
1001 }
1002 }
1003
1004 TaskItem++;
1005 }
1006
1007 return NULL;
1008 }
1009
1010 PTASK_ITEM AllocTaskItem()
1011 {
1012 if (m_TaskItemCount >= MAX_TASKS_COUNT)
1013 {
1014 /* We need the most significant bit in 16 bit command IDs to indicate whether it
1015 is a task group or task item. WM_COMMAND limits command IDs to 16 bits! */
1016 return NULL;
1017 }
1018
1019 ASSERT(m_AllocatedTaskItems >= m_TaskItemCount);
1020
1021 if (m_TaskItemCount == 0)
1022 {
1023 m_TaskItems = (PTASK_ITEM) HeapAlloc(hProcessHeap,
1024 0,
1025 TASK_ITEM_ARRAY_ALLOC * sizeof(*m_TaskItems));
1026 if (m_TaskItems != NULL)
1027 {
1028 m_AllocatedTaskItems = TASK_ITEM_ARRAY_ALLOC;
1029 }
1030 else
1031 return NULL;
1032 }
1033 else if (m_TaskItemCount >= m_AllocatedTaskItems)
1034 {
1035 PTASK_ITEM NewArray;
1036 SIZE_T NewArrayLength, ActiveTaskItemIndex;
1037
1038 NewArrayLength = m_AllocatedTaskItems + TASK_ITEM_ARRAY_ALLOC;
1039
1040 NewArray = (PTASK_ITEM) HeapReAlloc(hProcessHeap,
1041 0,
1042 m_TaskItems,
1043 NewArrayLength * sizeof(*m_TaskItems));
1044 if (NewArray != NULL)
1045 {
1046 if (m_ActiveTaskItem != NULL)
1047 {
1048 /* Fixup the ActiveTaskItem pointer */
1049 ActiveTaskItemIndex = m_ActiveTaskItem - m_TaskItems;
1050 m_ActiveTaskItem = NewArray + ActiveTaskItemIndex;
1051 }
1052 m_AllocatedTaskItems = (WORD) NewArrayLength;
1053 m_TaskItems = NewArray;
1054 }
1055 else
1056 return NULL;
1057 }
1058
1059 return m_TaskItems + m_TaskItemCount++;
1060 }
1061
1062 VOID FreeTaskItem(IN OUT PTASK_ITEM TaskItem)
1063 {
1064 WORD wIndex;
1065
1066 if (TaskItem == m_ActiveTaskItem)
1067 m_ActiveTaskItem = NULL;
1068
1069 wIndex = (WORD) (TaskItem - m_TaskItems);
1070 if (wIndex + 1 < m_TaskItemCount)
1071 {
1072 MoveMemory(TaskItem,
1073 TaskItem + 1,
1074 (m_TaskItemCount - wIndex - 1) * sizeof(*TaskItem));
1075 }
1076
1077 m_TaskItemCount--;
1078 }
1079
1080 VOID DeleteTaskItem(IN OUT PTASK_ITEM TaskItem)
1081 {
1082 if (!m_IsDestroying)
1083 {
1084 /* Delete the task button from the toolbar */
1085 DeleteTaskItemButton(TaskItem);
1086 }
1087
1088 /* Remove the task from it's group */
1089 RemoveTaskFromTaskGroup(TaskItem);
1090
1091 /* Free the task item */
1092 FreeTaskItem(TaskItem);
1093 }
1094
1095 VOID CheckActivateTaskItem(IN OUT PTASK_ITEM TaskItem)
1096 {
1097 PTASK_ITEM CurrentTaskItem;
1098 PTASK_GROUP TaskGroup = NULL;
1099
1100 CurrentTaskItem = m_ActiveTaskItem;
1101
1102 if (TaskItem != NULL)
1103 TaskGroup = TaskItem->Group;
1104
1105 if (m_IsGroupingEnabled &&
1106 TaskGroup != NULL &&
1107 TaskGroup->IsCollapsed)
1108 {
1109 /* FIXME */
1110 return;
1111 }
1112
1113 if (CurrentTaskItem != NULL)
1114 {
1115 PTASK_GROUP CurrentTaskGroup;
1116
1117 if (CurrentTaskItem == TaskItem)
1118 return;
1119
1120 CurrentTaskGroup = CurrentTaskItem->Group;
1121
1122 if (m_IsGroupingEnabled &&
1123 CurrentTaskGroup != NULL &&
1124 CurrentTaskGroup->IsCollapsed)
1125 {
1126 if (CurrentTaskGroup == TaskGroup)
1127 return;
1128
1129 /* FIXME */
1130 }
1131 else
1132 {
1133 m_ActiveTaskItem = NULL;
1134 if (CurrentTaskItem->Index >= 0)
1135 {
1136 UpdateTaskItemButton(CurrentTaskItem);
1137 }
1138 }
1139 }
1140
1141 m_ActiveTaskItem = TaskItem;
1142
1143 if (TaskItem != NULL && TaskItem->Index >= 0)
1144 {
1145 UpdateTaskItemButton(TaskItem);
1146 }
1147 else if (TaskItem == NULL)
1148 {
1149 TRACE("Active TaskItem now NULL\n");
1150 }
1151 }
1152
1153 PTASK_ITEM FindTaskItemByIndex(IN INT Index)
1154 {
1155 PTASK_ITEM TaskItem, LastItem;
1156
1157 TaskItem = m_TaskItems;
1158 LastItem = TaskItem + m_TaskItemCount;
1159 while (TaskItem != LastItem)
1160 {
1161 if (TaskItem->Index == Index)
1162 return TaskItem;
1163
1164 TaskItem++;
1165 }
1166
1167 return NULL;
1168 }
1169
1170 PTASK_GROUP FindTaskGroupByIndex(IN INT Index)
1171 {
1172 PTASK_GROUP CurrentGroup;
1173
1174 CurrentGroup = m_TaskGroups;
1175 while (CurrentGroup != NULL)
1176 {
1177 if (CurrentGroup->Index == Index)
1178 break;
1179
1180 CurrentGroup = CurrentGroup->Next;
1181 }
1182
1183 return CurrentGroup;
1184 }
1185
1186 BOOL AddTask(IN HWND hWnd)
1187 {
1188 PTASK_ITEM TaskItem;
1189
1190 if (!::IsWindow(hWnd) || m_Tray->IsSpecialHWND(hWnd))
1191 return FALSE;
1192
1193 TaskItem = FindTaskItem(hWnd);
1194 if (TaskItem == NULL)
1195 {
1196 TRACE("Add window 0x%p\n", hWnd);
1197 TaskItem = AllocTaskItem();
1198 if (TaskItem != NULL)
1199 {
1200 ZeroMemory(TaskItem, sizeof(*TaskItem));
1201 TaskItem->hWnd = hWnd;
1202 TaskItem->Index = -1;
1203 TaskItem->Group = AddToTaskGroup(hWnd);
1204
1205 if (!m_IsDestroying)
1206 {
1207 AddTaskItemButton(TaskItem);
1208 }
1209 }
1210 }
1211
1212 return TaskItem != NULL;
1213 }
1214
1215 BOOL ActivateTaskItem(IN OUT PTASK_ITEM TaskItem OPTIONAL)
1216 {
1217 if (TaskItem != NULL)
1218 {
1219 TRACE("Activate window 0x%p on button %d\n", TaskItem->hWnd, TaskItem->Index);
1220 }
1221
1222 CheckActivateTaskItem(TaskItem);
1223 return FALSE;
1224 }
1225
1226 BOOL ActivateTask(IN HWND hWnd)
1227 {
1228 PTASK_ITEM TaskItem;
1229
1230 if (!hWnd)
1231 {
1232 return ActivateTaskItem(NULL);
1233 }
1234
1235 TaskItem = FindTaskItem(hWnd);
1236 if (TaskItem == NULL)
1237 {
1238 TaskItem = FindOtherTaskItem(hWnd);
1239 }
1240
1241 if (TaskItem == NULL)
1242 {
1243 WARN("Activate window 0x%p, could not find task\n", hWnd);
1244 RefreshWindowList();
1245 }
1246
1247 return ActivateTaskItem(TaskItem);
1248 }
1249
1250 BOOL DeleteTask(IN HWND hWnd)
1251 {
1252 PTASK_ITEM TaskItem;
1253
1254 TaskItem = FindTaskItem(hWnd);
1255 if (TaskItem != NULL)
1256 {
1257 TRACE("Delete window 0x%p on button %d\n", hWnd, TaskItem->Index);
1258 DeleteTaskItem(TaskItem);
1259 return TRUE;
1260 }
1261 //else
1262 //TRACE("Failed to delete window 0x%p\n", hWnd);
1263
1264 return FALSE;
1265 }
1266
1267 VOID DeleteAllTasks()
1268 {
1269 PTASK_ITEM CurrentTask;
1270
1271 if (m_TaskItemCount > 0)
1272 {
1273 CurrentTask = m_TaskItems + m_TaskItemCount;
1274 do
1275 {
1276 DeleteTaskItem(--CurrentTask);
1277 } while (CurrentTask != m_TaskItems);
1278 }
1279 }
1280
1281 VOID FlashTaskItem(IN OUT PTASK_ITEM TaskItem)
1282 {
1283 TaskItem->RenderFlashed = 1;
1284 UpdateTaskItemButton(TaskItem);
1285 }
1286
1287 BOOL FlashTask(IN HWND hWnd)
1288 {
1289 PTASK_ITEM TaskItem;
1290
1291 TaskItem = FindTaskItem(hWnd);
1292 if (TaskItem != NULL)
1293 {
1294 TRACE("Flashing window 0x%p on button %d\n", hWnd, TaskItem->Index);
1295 FlashTaskItem(TaskItem);
1296 return TRUE;
1297 }
1298
1299 return FALSE;
1300 }
1301
1302 VOID RedrawTaskItem(IN OUT PTASK_ITEM TaskItem)
1303 {
1304 PTASK_GROUP TaskGroup;
1305
1306 TaskGroup = TaskItem->Group;
1307 if (m_IsGroupingEnabled && TaskGroup != NULL)
1308 {
1309 if (TaskGroup->IsCollapsed && TaskGroup->Index >= 0)
1310 {
1311 UpdateTaskGroupButton(TaskGroup);
1312 }
1313 else if (TaskItem->Index >= 0)
1314 {
1315 goto UpdateTaskItem;
1316 }
1317 }
1318 else if (TaskItem->Index >= 0)
1319 {
1320 UpdateTaskItem:
1321 TaskItem->RenderFlashed = 0;
1322 UpdateTaskItemButton(TaskItem);
1323 }
1324 }
1325
1326
1327 BOOL RedrawTask(IN HWND hWnd)
1328 {
1329 PTASK_ITEM TaskItem;
1330
1331 TaskItem = FindTaskItem(hWnd);
1332 if (TaskItem != NULL)
1333 {
1334 RedrawTaskItem(TaskItem);
1335 return TRUE;
1336 }
1337
1338 return FALSE;
1339 }
1340
1341 VOID UpdateButtonsSize(IN BOOL bRedrawDisabled)
1342 {
1343 RECT rcClient;
1344 UINT uiRows, uiMax, uiMin, uiBtnsPerLine, ui;
1345 LONG NewBtnSize;
1346 BOOL Horizontal;
1347
1348 /* Update the size of the image list if needed */
1349 int cx, cy;
1350 ImageList_GetIconSize(m_ImageList, &cx, &cy);
1351 if (cx != GetSystemMetrics(g_TaskbarSettings.bSmallIcons ? SM_CXSMICON : SM_CXICON) ||
1352 cy != GetSystemMetrics(g_TaskbarSettings.bSmallIcons ? SM_CYSMICON : SM_CYICON))
1353 {
1354 ImageList_SetIconSize(m_ImageList,
1355 GetSystemMetrics(g_TaskbarSettings.bSmallIcons ? SM_CXSMICON : SM_CXICON),
1356 GetSystemMetrics(g_TaskbarSettings.bSmallIcons ? SM_CYSMICON : SM_CYICON));
1357
1358 /* SetIconSize removes all icons so we have to reinsert them */
1359 PTASK_ITEM TaskItem = m_TaskItems;
1360 PTASK_ITEM LastTaskItem = m_TaskItems + m_TaskItemCount;
1361 while (TaskItem != LastTaskItem)
1362 {
1363 TaskItem->IconIndex = -1;
1364 UpdateTaskItemButton(TaskItem);
1365
1366 TaskItem++;
1367 }
1368 m_TaskBar.SetImageList(m_ImageList);
1369 }
1370
1371 if (GetClientRect(&rcClient) && !IsRectEmpty(&rcClient))
1372 {
1373 if (m_ButtonCount > 0)
1374 {
1375 Horizontal = m_Tray->IsHorizontal();
1376
1377 if (Horizontal)
1378 {
1379 TBMETRICS tbm = { 0 };
1380 tbm.cbSize = sizeof(tbm);
1381 tbm.dwMask = TBMF_BUTTONSPACING;
1382 m_TaskBar.GetMetrics(&tbm);
1383
1384 if (m_ButtonSize.cy + tbm.cyButtonSpacing != 0)
1385 uiRows = (rcClient.bottom + tbm.cyButtonSpacing) / (m_ButtonSize.cy + tbm.cyButtonSpacing);
1386 else
1387 uiRows = 1;
1388
1389 if (uiRows == 0)
1390 uiRows = 1;
1391
1392 uiBtnsPerLine = (m_ButtonCount + uiRows - 1) / uiRows;
1393 }
1394 else
1395 {
1396 uiBtnsPerLine = 1;
1397 uiRows = m_ButtonCount;
1398 }
1399
1400 if (!bRedrawDisabled)
1401 m_TaskBar.BeginUpdate();
1402
1403 /* We might need to update the button spacing */
1404 int cxButtonSpacing = m_TaskBar.UpdateTbButtonSpacing(
1405 Horizontal, m_Theme != NULL,
1406 uiRows, uiBtnsPerLine);
1407
1408 /* Determine the minimum and maximum width of a button */
1409 uiMin = GetSystemMetrics(SM_CXSIZE) + (2 * GetSystemMetrics(SM_CXEDGE));
1410 if (Horizontal)
1411 {
1412 uiMax = GetSystemMetrics(SM_CXMINIMIZED);
1413
1414 /* Calculate the ideal width and make sure it's within the allowed range */
1415 NewBtnSize = (rcClient.right - (uiBtnsPerLine * cxButtonSpacing)) / uiBtnsPerLine;
1416
1417 if (NewBtnSize < (LONG) uiMin)
1418 NewBtnSize = uiMin;
1419 if (NewBtnSize >(LONG)uiMax)
1420 NewBtnSize = uiMax;
1421
1422 /* Recalculate how many buttons actually fit into one line */
1423 uiBtnsPerLine = rcClient.right / (NewBtnSize + cxButtonSpacing);
1424 if (uiBtnsPerLine == 0)
1425 uiBtnsPerLine++;
1426 }
1427 else
1428 {
1429 NewBtnSize = uiMax = rcClient.right;
1430 }
1431
1432 m_ButtonSize.cx = NewBtnSize;
1433
1434 m_ButtonsPerLine = uiBtnsPerLine;
1435
1436 for (ui = 0; ui != m_ButtonCount; ui++)
1437 {
1438 TBBUTTONINFOW tbbi = { 0 };
1439 tbbi.cbSize = sizeof(tbbi);
1440 tbbi.dwMask = TBIF_BYINDEX | TBIF_SIZE | TBIF_STATE;
1441 tbbi.cx = (INT) NewBtnSize;
1442 tbbi.fsState = TBSTATE_ENABLED;
1443
1444 /* Check if we're updating a button that is the last one in the
1445 line. If so, we need to set the TBSTATE_WRAP flag! */
1446 if (Horizontal)
1447 {
1448 if ((ui + 1) % uiBtnsPerLine == 0)
1449 tbbi.fsState |= TBSTATE_WRAP;
1450 }
1451 else
1452 {
1453 tbbi.fsState |= TBSTATE_WRAP;
1454 }
1455
1456 if (m_ActiveTaskItem != NULL &&
1457 m_ActiveTaskItem->Index == (INT)ui)
1458 {
1459 tbbi.fsState |= TBSTATE_CHECKED;
1460 }
1461
1462 m_TaskBar.SetButtonInfo(ui, &tbbi);
1463 }
1464 }
1465 else
1466 {
1467 m_ButtonsPerLine = 0;
1468 m_ButtonSize.cx = 0;
1469 }
1470 }
1471
1472 // FIXME: This seems to be enabling redraws prematurely, but moving it to its right place doesn't work!
1473 m_TaskBar.EndUpdate();
1474 }
1475
1476 BOOL CALLBACK EnumWindowsProc(IN HWND hWnd)
1477 {
1478 if (m_Tray->IsTaskWnd(hWnd))
1479 {
1480 TRACE("Adding task for %p...\n", hWnd);
1481 AddTask(hWnd);
1482 }
1483 return TRUE;
1484 }
1485
1486 static BOOL CALLBACK s_EnumWindowsProc(IN HWND hWnd, IN LPARAM lParam)
1487 {
1488 CTaskSwitchWnd * This = (CTaskSwitchWnd *) lParam;
1489
1490 return This->EnumWindowsProc(hWnd);
1491 }
1492
1493 BOOL RefreshWindowList()
1494 {
1495 TRACE("Refreshing window list...\n");
1496 /* Add all windows to the toolbar */
1497 return EnumWindows(s_EnumWindowsProc, (LPARAM)this);
1498 }
1499
1500 LRESULT OnThemeChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1501 {
1502 TRACE("OmThemeChanged\n");
1503
1504 if (m_Theme)
1505 CloseThemeData(m_Theme);
1506
1507 if (IsThemeActive())
1508 m_Theme = OpenThemeData(m_hWnd, L"TaskBand");
1509 else
1510 m_Theme = NULL;
1511
1512 return TRUE;
1513 }
1514
1515 LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1516 {
1517 if (!m_TaskBar.Initialize(m_hWnd))
1518 return FALSE;
1519
1520 SetWindowTheme(m_TaskBar.m_hWnd, L"TaskBand", NULL);
1521
1522 m_ImageList = ImageList_Create(GetSystemMetrics(g_TaskbarSettings.bSmallIcons ? SM_CXSMICON : SM_CXICON),
1523 GetSystemMetrics(g_TaskbarSettings.bSmallIcons ? SM_CYSMICON : SM_CYICON),
1524 ILC_COLOR32 | ILC_MASK, 0, 1000);
1525 m_TaskBar.SetImageList(m_ImageList);
1526
1527 /* Set proper spacing between buttons */
1528 m_TaskBar.UpdateTbButtonSpacing(m_Tray->IsHorizontal(), m_Theme != NULL);
1529
1530 /* Register the shell hook */
1531 m_ShellHookMsg = RegisterWindowMessageW(L"SHELLHOOK");
1532
1533 TRACE("ShellHookMsg got assigned number %d\n", m_ShellHookMsg);
1534
1535 RegisterShellHook(m_hWnd, 3); /* 1 if no NT! We're targeting NT so we don't care! */
1536
1537 RefreshWindowList();
1538
1539 /* Recalculate the button size */
1540 UpdateButtonsSize(FALSE);
1541
1542#if DUMP_TASKS != 0
1543 SetTimer(hwnd, 1, 5000, NULL);
1544#endif
1545 return TRUE;
1546 }
1547
1548 LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1549 {
1550 m_IsDestroying = TRUE;
1551
1552 KillTimer(TIMER_ID_VALIDATE_RUDE_APP);
1553
1554 /* Unregister the shell hook */
1555 RegisterShellHook(m_hWnd, FALSE);
1556
1557 CloseThemeData(m_Theme);
1558 DeleteAllTasks();
1559
1560 if (m_ImageList)
1561 {
1562 ImageList_Destroy(m_ImageList);
1563 m_ImageList = NULL;
1564 }
1565
1566 return TRUE;
1567 }
1568
1569 static BOOL InvokeRegistryAppKeyCommand(UINT uAppCmd)
1570 {
1571 BOOL bResult = FALSE;
1572 WCHAR szBuf[MAX_PATH * 2];
1573 wsprintfW(szBuf, L"%s\\AppKey\\%u", REGSTR_PATH_EXPLORER, uAppCmd);
1574 HUSKEY hKey;
1575 if (SHRegOpenUSKeyW(szBuf, KEY_READ, NULL, &hKey, FALSE) != ERROR_SUCCESS)
1576 return bResult;
1577
1578 DWORD cb = sizeof(szBuf);
1579 if (!bResult && SHRegQueryUSValueW(hKey, L"ShellExecute", NULL, szBuf, &cb, FALSE, NULL, 0) == ERROR_SUCCESS)
1580 {
1581 bResult = TRUE;
1582 }
1583 cb = sizeof(szBuf);
1584 if (!bResult && SHRegQueryUSValueW(hKey, L"Association", NULL, szBuf, &cb, FALSE, NULL, 0) == ERROR_SUCCESS)
1585 {
1586 bResult = TRUE;
1587 cb = _countof(szBuf);
1588 if (AssocQueryString(ASSOCF_NOTRUNCATE, ASSOCSTR_EXECUTABLE, szBuf, NULL, szBuf, &cb) != S_OK)
1589 *szBuf = UNICODE_NULL;
1590 }
1591 cb = sizeof(szBuf);
1592 if (!bResult && SHRegQueryUSValueW(hKey, L"RegisteredApp", NULL, szBuf, &cb, FALSE, NULL, 0) == ERROR_SUCCESS)
1593 {
1594 bResult = TRUE;
1595 SHRunIndirectRegClientCommand(NULL, szBuf);
1596 *szBuf = UNICODE_NULL; // Don't execute again
1597 }
1598 SHRegCloseUSKey(hKey);
1599
1600 // Note: Tweak UI uses an empty string for its "Do nothing" option.
1601 if (bResult && *szBuf)
1602 ShellExec_RunDLLW(NULL, NULL, szBuf, SW_SHOW);
1603 return bResult;
1604 }
1605
1606 BOOL HandleAppCommand(IN WPARAM wParam, IN LPARAM lParam)
1607 {
1608 const UINT uAppCmd = GET_APPCOMMAND_LPARAM(lParam);
1609 if (InvokeRegistryAppKeyCommand(uAppCmd))
1610 return TRUE;
1611 switch (uAppCmd)
1612 {
1613 case APPCOMMAND_VOLUME_MUTE:
1614 case APPCOMMAND_VOLUME_DOWN:
1615 case APPCOMMAND_VOLUME_UP:
1616 // TODO: Try IMMDeviceEnumerator::GetDefaultAudioEndpoint first and then fall back to mixer.
1617 FIXME("Call the mixer API to change the global volume\n");
1618 return TRUE;
1619 case APPCOMMAND_BROWSER_SEARCH:
1620 return SHFindFiles(NULL, NULL);
1621 }
1622 return FALSE;
1623 }
1624
1625 LRESULT OnShellHook(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1626 {
1627 BOOL Ret = FALSE;
1628
1629 /* In case the shell hook wasn't registered properly, ignore WM_NULLs*/
1630 if (uMsg == 0)
1631 {
1632 bHandled = FALSE;
1633 return 0;
1634 }
1635
1636 TRACE("Received shell hook message: wParam=%08lx, lParam=%08lx\n", wParam, lParam);
1637
1638 switch ((INT) wParam)
1639 {
1640 case HSHELL_APPCOMMAND:
1641 Ret = HandleAppCommand(0, lParam);
1642 break;
1643
1644 case HSHELL_WINDOWCREATED:
1645 AddTask((HWND) lParam);
1646 break;
1647
1648 case HSHELL_WINDOWDESTROYED:
1649 /* The window still exists! Delay destroying it a bit */
1650 OnWindowDestroyed((HWND)lParam);
1651 DeleteTask((HWND)lParam);
1652 break;
1653
1654 case HSHELL_RUDEAPPACTIVATED:
1655 case HSHELL_WINDOWACTIVATED:
1656 OnWindowActivated((HWND)lParam);
1657 ActivateTask((HWND)lParam);
1658 break;
1659
1660 case HSHELL_FLASH:
1661 FlashTask((HWND) lParam);
1662 break;
1663
1664 case HSHELL_REDRAW:
1665 RedrawTask((HWND) lParam);
1666 break;
1667
1668 case HSHELL_TASKMAN:
1669 ::PostMessage(m_Tray->GetHWND(), TWM_OPENSTARTMENU, 0, 0);
1670 break;
1671
1672 case HSHELL_ACTIVATESHELLWINDOW:
1673 ::SwitchToThisWindow(m_Tray->GetHWND(), TRUE);
1674 ::SetForegroundWindow(m_Tray->GetHWND());
1675 break;
1676
1677 case HSHELL_LANGUAGE:
1678 case HSHELL_SYSMENU:
1679 case HSHELL_ENDTASK:
1680 case HSHELL_ACCESSIBILITYSTATE:
1681 case HSHELL_WINDOWREPLACED:
1682 case HSHELL_WINDOWREPLACING:
1683
1684 case HSHELL_GETMINRECT:
1685 default:
1686 {
1687#if DEBUG_SHELL_HOOK
1688 int i, found;
1689 for (i = 0, found = 0; i != _countof(hshell_msg); i++)
1690 {
1691 if (hshell_msg[i].msg == (INT) wParam)
1692 {
1693 TRACE("Shell message %ws unhandled (lParam = 0x%p)!\n", hshell_msg[i].msg_name, lParam);
1694 found = 1;
1695 break;
1696 }
1697 }
1698 if (found)
1699 break;
1700#endif
1701 TRACE("Shell message %d unhandled (lParam = 0x%p)!\n", (INT) wParam, lParam);
1702 break;
1703 }
1704 }
1705
1706 return Ret;
1707 }
1708
1709 VOID HandleTaskItemClick(IN OUT PTASK_ITEM TaskItem)
1710 {
1711 BOOL bIsMinimized;
1712 BOOL bIsActive;
1713
1714 if (::IsWindow(TaskItem->hWnd))
1715 {
1716 bIsMinimized = ::IsIconic(TaskItem->hWnd);
1717 bIsActive = (TaskItem == m_ActiveTaskItem);
1718
1719 TRACE("Active TaskItem %p, selected TaskItem %p\n", m_ActiveTaskItem, TaskItem);
1720 if (m_ActiveTaskItem)
1721 TRACE("Active TaskItem hWnd=%p, TaskItem hWnd %p\n", m_ActiveTaskItem->hWnd, TaskItem->hWnd);
1722
1723 TRACE("Valid button clicked. HWND=%p, IsMinimized=%s, IsActive=%s...\n",
1724 TaskItem->hWnd, bIsMinimized ? "Yes" : "No", bIsActive ? "Yes" : "No");
1725
1726 if (!bIsMinimized && bIsActive)
1727 {
1728 if (!::IsHungAppWindow(TaskItem->hWnd))
1729 ::ShowWindowAsync(TaskItem->hWnd, SW_MINIMIZE);
1730 TRACE("Valid button clicked. App window Minimized.\n");
1731 }
1732 else
1733 {
1734 ::SwitchToThisWindow(TaskItem->hWnd, TRUE);
1735
1736 TRACE("Valid button clicked. App window Restored.\n");
1737 }
1738 }
1739 }
1740
1741 VOID HandleTaskGroupClick(IN OUT PTASK_GROUP TaskGroup)
1742 {
1743 /* TODO: Show task group menu */
1744 }
1745
1746 BOOL HandleButtonClick(IN WORD wIndex)
1747 {
1748 PTASK_ITEM TaskItem;
1749 PTASK_GROUP TaskGroup;
1750
1751 if (m_IsGroupingEnabled)
1752 {
1753 TaskGroup = FindTaskGroupByIndex((INT) wIndex);
1754 if (TaskGroup != NULL && TaskGroup->IsCollapsed)
1755 {
1756 HandleTaskGroupClick(TaskGroup);
1757 return TRUE;
1758 }
1759 }
1760
1761 TaskItem = FindTaskItemByIndex((INT) wIndex);
1762 if (TaskItem != NULL)
1763 {
1764 HandleTaskItemClick(TaskItem);
1765 return TRUE;
1766 }
1767
1768 return FALSE;
1769 }
1770
1771 static VOID CALLBACK
1772 SendAsyncProc(HWND hwnd, UINT uMsg, DWORD_PTR dwData, LRESULT lResult)
1773 {
1774 ::PostMessageW(hwnd, WM_NULL, 0, 0);
1775 }
1776
1777 VOID HandleTaskItemRightClick(IN OUT PTASK_ITEM TaskItem)
1778 {
1779 POINT pt;
1780 GetCursorPos(&pt);
1781
1782 SetForegroundWindow(TaskItem->hWnd);
1783
1784 ActivateTask(TaskItem->hWnd);
1785
1786 if (GetForegroundWindow() != TaskItem->hWnd)
1787 ERR("HandleTaskItemRightClick detected the window did not become foreground\n");
1788
1789 ::SendMessageCallbackW(TaskItem->hWnd, WM_POPUPSYSTEMMENU, 0, MAKELPARAM(pt.x, pt.y),
1790 SendAsyncProc, (ULONG_PTR)TaskItem);
1791 }
1792
1793 VOID HandleTaskGroupRightClick(IN OUT PTASK_GROUP TaskGroup)
1794 {
1795 /* TODO: Show task group right click menu */
1796 }
1797
1798 BOOL HandleButtonRightClick(IN WORD wIndex)
1799 {
1800 PTASK_ITEM TaskItem;
1801 PTASK_GROUP TaskGroup;
1802 if (m_IsGroupingEnabled)
1803 {
1804 TaskGroup = FindTaskGroupByIndex((INT) wIndex);
1805 if (TaskGroup != NULL && TaskGroup->IsCollapsed)
1806 {
1807 HandleTaskGroupRightClick(TaskGroup);
1808 return TRUE;
1809 }
1810 }
1811
1812 TaskItem = FindTaskItemByIndex((INT) wIndex);
1813
1814 if (TaskItem != NULL)
1815 {
1816 HandleTaskItemRightClick(TaskItem);
1817 return TRUE;
1818 }
1819
1820 return FALSE;
1821 }
1822
1823
1824 LRESULT HandleItemPaint(IN OUT NMTBCUSTOMDRAW *nmtbcd)
1825 {
1826 LRESULT Ret = CDRF_DODEFAULT;
1827 PTASK_GROUP TaskGroup;
1828 PTASK_ITEM TaskItem;
1829
1830 TaskItem = FindTaskItemByIndex((INT) nmtbcd->nmcd.dwItemSpec);
1831 TaskGroup = FindTaskGroupByIndex((INT) nmtbcd->nmcd.dwItemSpec);
1832 if (TaskGroup == NULL && TaskItem != NULL)
1833 {
1834 ASSERT(TaskItem != NULL);
1835
1836 if (TaskItem != NULL && ::IsWindow(TaskItem->hWnd))
1837 {
1838 /* Make the entire button flashing if necessary */
1839 if (nmtbcd->nmcd.uItemState & CDIS_MARKED)
1840 {
1841 Ret = TBCDRF_NOBACKGROUND;
1842 if (!m_Theme)
1843 {
1844 SelectObject(nmtbcd->nmcd.hdc, GetSysColorBrush(COLOR_HIGHLIGHT));
1845 Rectangle(nmtbcd->nmcd.hdc,
1846 nmtbcd->nmcd.rc.left,
1847 nmtbcd->nmcd.rc.top,
1848 nmtbcd->nmcd.rc.right,
1849 nmtbcd->nmcd.rc.bottom);
1850 }
1851 else
1852 {
1853 DrawThemeBackground(m_Theme, nmtbcd->nmcd.hdc, TDP_FLASHBUTTON, 0, &nmtbcd->nmcd.rc, 0);
1854 }
1855 nmtbcd->clrText = GetSysColor(COLOR_HIGHLIGHTTEXT);
1856 return Ret;
1857 }
1858 }
1859 }
1860 else if (TaskGroup != NULL)
1861 {
1862 /* FIXME: Implement painting for task groups */
1863 }
1864 return Ret;
1865 }
1866
1867 LRESULT HandleToolbarNotification(IN const NMHDR *nmh)
1868 {
1869 LRESULT Ret = 0;
1870
1871 switch (nmh->code)
1872 {
1873 case NM_CUSTOMDRAW:
1874 {
1875 LPNMTBCUSTOMDRAW nmtbcd = (LPNMTBCUSTOMDRAW) nmh;
1876
1877 switch (nmtbcd->nmcd.dwDrawStage)
1878 {
1879
1880 case CDDS_ITEMPREPAINT:
1881 Ret = HandleItemPaint(nmtbcd);
1882 break;
1883
1884 case CDDS_PREPAINT:
1885 Ret = CDRF_NOTIFYITEMDRAW;
1886 break;
1887
1888 default:
1889 Ret = CDRF_DODEFAULT;
1890 break;
1891 }
1892 break;
1893 }
1894 }
1895
1896 return Ret;
1897 }
1898
1899 // Internal structure for IsRudeEnumProc
1900 typedef struct tagRUDEAPPDATA
1901 {
1902 HMONITOR hTargetMonitor;
1903 HWND hwndFound;
1904 HWND hwndFirstCheck;
1905 } RUDEAPPDATA, *PRUDEAPPDATA;
1906
1907 // Find any rude app
1908 static BOOL CALLBACK
1909 IsRudeEnumProc(_In_ HWND hwnd, _In_ LPARAM lParam)
1910 {
1911 PRUDEAPPDATA pData = (PRUDEAPPDATA)lParam;
1912
1913 HMONITOR hMon = ::MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
1914 if (!hMon ||
1915 (pData->hTargetMonitor && pData->hTargetMonitor != hMon) ||
1916 !SHELL_IsRudeWindow(hMon, hwnd, (hwnd == pData->hwndFirstCheck)))
1917 {
1918 return TRUE; // Continue
1919 }
1920
1921 pData->hwndFound = hwnd;
1922 return FALSE; // Finish
1923 }
1924
1925 // Internal structure for FullScreenEnumProc
1926 typedef struct tagFULLSCREENDATA
1927 {
1928 const RECT *pRect;
1929 HMONITOR hTargetMonitor;
1930 ITrayWindow *pTray;
1931 } FULLSCREENDATA, *PFULLSCREENDATA;
1932
1933 // Notify ABN_FULLSCREENAPP for each monitor
1934 static BOOL CALLBACK
1935 FullScreenEnumProc(_In_ HMONITOR hMonitor, _In_opt_ HDC hDC, _In_ LPRECT prc, _In_ LPARAM lParam)
1936 {
1937 PFULLSCREENDATA pData = (PFULLSCREENDATA)lParam;
1938
1939 BOOL bFullOpening = (pData->hTargetMonitor == hMonitor);
1940 if (!bFullOpening && pData->pRect)
1941 {
1942 RECT rc, rcMon;
1943 SHELL_GetMonitorRect(hMonitor, &rcMon, FALSE);
1944 ::IntersectRect(&rc, &rcMon, pData->pRect);
1945 bFullOpening = ::EqualRect(&rc, &rcMon);
1946 }
1947
1948 // Notify ABN_FULLSCREENAPP to appbars
1949 pData->pTray->NotifyFullScreenToAppBars(hMonitor, bFullOpening);
1950 return TRUE;
1951 }
1952
1953 void HandleFullScreenApp(_In_opt_ HWND hwndRude)
1954 {
1955 // Notify ABN_FULLSCREENAPP for every monitor
1956 RECT rc;
1957 FULLSCREENDATA Data = { NULL, NULL, NULL };
1958 if (hwndRude && ::GetWindowRect(hwndRude, &rc))
1959 {
1960 Data.pRect = &rc;
1961 Data.hTargetMonitor = ::MonitorFromWindow(hwndRude, MONITOR_DEFAULTTONULL);
1962 }
1963 Data.pTray = m_Tray;
1964 ::EnumDisplayMonitors(NULL, NULL, FullScreenEnumProc, (LPARAM)&Data);
1965
1966 if (hwndRude)
1967 {
1968 // Make the taskbar bottom
1969 UINT uFlags = SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER;
1970 HWND hwndTray = m_Tray->GetHWND();
1971 ::SetWindowPos(hwndTray, HWND_BOTTOM, 0, 0, 0, 0, uFlags);
1972
1973 // Switch to the rude app if necessary
1974 DWORD exstyle = (DWORD)::GetWindowLongPtrW(hwndRude, GWL_EXSTYLE);
1975 if (!(exstyle & WS_EX_TOPMOST) && !SHELL_IsRudeWindowActive(hwndRude))
1976 ::SwitchToThisWindow(hwndRude, TRUE);
1977 }
1978 }
1979
1980 HWND FindRudeApp(_In_opt_ HWND hwndFirstCheck)
1981 {
1982 // Quick check
1983 HMONITOR hMon = MonitorFromWindow(hwndFirstCheck, MONITOR_DEFAULTTONEAREST);
1984 RUDEAPPDATA data = { hMon, NULL, hwndFirstCheck };
1985 if (::IsWindow(hwndFirstCheck) && !IsRudeEnumProc(hwndFirstCheck, (LPARAM)&data))
1986 return hwndFirstCheck;
1987
1988 // Slow check
1989 ::EnumWindows(IsRudeEnumProc, (LPARAM)&data);
1990
1991 return data.hwndFound;
1992 }
1993
1994 // WM_WINDOWPOSCHANGED
1995 LRESULT OnWindowPosChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1996 {
1997 // Re-start rude app validation
1998 KillTimer(TIMER_ID_VALIDATE_RUDE_APP);
1999 SetTimer(TIMER_ID_VALIDATE_RUDE_APP, VALIDATE_RUDE_INTERVAL, NULL);
2000 m_nRudeAppValidationCounter = 0;
2001 bHandled = FALSE;
2002 return 0;
2003 }
2004
2005 // HSHELL_WINDOWACTIVATED, HSHELL_RUDEAPPACTIVATED
2006 void OnWindowActivated(_In_ HWND hwndTarget)
2007 {
2008 // Re-start rude app validation
2009 KillTimer(TIMER_ID_VALIDATE_RUDE_APP);
2010 SetTimer(TIMER_ID_VALIDATE_RUDE_APP, VALIDATE_RUDE_INTERVAL, NULL);
2011 m_nRudeAppValidationCounter = 0;
2012 }
2013
2014 // HSHELL_WINDOWDESTROYED
2015 void OnWindowDestroyed(_In_ HWND hwndTarget)
2016 {
2017 if (!FindTaskItem(hwndTarget))
2018 return;
2019 HWND hwndRude = FindRudeApp(hwndTarget);
2020 HandleFullScreenApp(hwndRude);
2021 }
2022
2023 LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2024 {
2025 HDC hdc = (HDC) wParam;
2026
2027 if (!IsAppThemed())
2028 {
2029 bHandled = FALSE;
2030 return 0;
2031 }
2032
2033 RECT rect;
2034 GetClientRect(&rect);
2035 DrawThemeParentBackground(m_hWnd, hdc, &rect);
2036
2037 return TRUE;
2038 }
2039
2040 LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2041 {
2042 SIZE szClient;
2043
2044 szClient.cx = LOWORD(lParam);
2045 szClient.cy = HIWORD(lParam);
2046 if (m_TaskBar.m_hWnd != NULL)
2047 {
2048 m_TaskBar.SetWindowPos(NULL, 0, 0, szClient.cx, szClient.cy, SWP_NOZORDER);
2049
2050 UpdateButtonsSize(FALSE);
2051 }
2052 return TRUE;
2053 }
2054
2055 LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2056 {
2057 LRESULT Ret = TRUE;
2058 /* We want the tray window to be draggable everywhere, so make the control
2059 appear transparent */
2060 Ret = DefWindowProc(uMsg, wParam, lParam);
2061 if (Ret != HTVSCROLL && Ret != HTHSCROLL)
2062 Ret = HTTRANSPARENT;
2063 return Ret;
2064 }
2065
2066 LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2067 {
2068 LRESULT Ret = TRUE;
2069 if (lParam != 0 && (HWND) lParam == m_TaskBar.m_hWnd)
2070 {
2071 HandleButtonClick(LOWORD(wParam));
2072 }
2073 return Ret;
2074 }
2075
2076 LRESULT OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2077 {
2078 LRESULT Ret = TRUE;
2079 const NMHDR *nmh = (const NMHDR *) lParam;
2080
2081 if (nmh->hwndFrom == m_TaskBar.m_hWnd)
2082 {
2083 Ret = HandleToolbarNotification(nmh);
2084 }
2085 return Ret;
2086 }
2087
2088 LRESULT OnUpdateTaskbarPos(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2089 {
2090 /* Update the button spacing */
2091 m_TaskBar.UpdateTbButtonSpacing(m_Tray->IsHorizontal(), m_Theme != NULL);
2092 return TRUE;
2093 }
2094
2095 LRESULT OnTaskbarSettingsChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2096 {
2097 BOOL bSettingsChanged = FALSE;
2098 TaskbarSettings* newSettings = (TaskbarSettings*)lParam;
2099
2100 if (newSettings->bGroupButtons != g_TaskbarSettings.bGroupButtons)
2101 {
2102 bSettingsChanged = TRUE;
2103 g_TaskbarSettings.bGroupButtons = newSettings->bGroupButtons;
2104 m_IsGroupingEnabled = g_TaskbarSettings.bGroupButtons;
2105 }
2106
2107 if (newSettings->bSmallIcons != g_TaskbarSettings.bSmallIcons)
2108 {
2109 bSettingsChanged = TRUE;
2110 g_TaskbarSettings.bSmallIcons = newSettings->bSmallIcons;
2111 }
2112
2113 if (bSettingsChanged)
2114 {
2115 /* Refresh each task item view */
2116 RefreshWindowList();
2117 UpdateButtonsSize(FALSE);
2118 }
2119
2120 return 0;
2121 }
2122
2123 LRESULT OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2124 {
2125 LRESULT Ret = 0;
2126 INT_PTR iBtn = -1;
2127
2128 if (m_TaskBar.m_hWnd != NULL)
2129 {
2130 POINT pt;
2131
2132 pt.x = GET_X_LPARAM(lParam);
2133 pt.y = GET_Y_LPARAM(lParam);
2134
2135 ::ScreenToClient(m_TaskBar.m_hWnd, &pt);
2136
2137 iBtn = m_TaskBar.HitTest(&pt);
2138 if (iBtn >= 0)
2139 {
2140 HandleButtonRightClick(iBtn);
2141 }
2142 }
2143 if (iBtn < 0)
2144 {
2145 /* Not on a taskbar button, so forward message to tray */
2146 Ret = SendMessage(m_Tray->GetHWND(), uMsg, wParam, lParam);
2147 }
2148 return Ret;
2149 }
2150
2151 LRESULT OnKludgeItemRect(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2152 {
2153 PTASK_ITEM TaskItem = FindTaskItem((HWND) wParam);
2154 if (TaskItem)
2155 {
2156 RECT* prcMinRect = (RECT*) lParam;
2157 RECT rcItem, rcToolbar;
2158 m_TaskBar.GetItemRect(TaskItem->Index, &rcItem);
2159 m_TaskBar.GetWindowRect(&rcToolbar);
2160
2161 OffsetRect(&rcItem, rcToolbar.left, rcToolbar.top);
2162
2163 *prcMinRect = rcItem;
2164 return TRUE;
2165 }
2166 return FALSE;
2167 }
2168
2169 LRESULT OnMouseActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2170 {
2171 return MA_NOACTIVATE;
2172 }
2173
2174 LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2175 {
2176 switch (wParam)
2177 {
2178#if DUMP_TASKS != 0
2179 case 1:
2180 DumpTasks();
2181 break;
2182#endif
2183 case TIMER_ID_VALIDATE_RUDE_APP:
2184 {
2185 // Real activation of rude app might take some time after HSHELL_...ACTIVATED.
2186 // Wait up to 5 seconds with validating the rude app at each second.
2187 HWND hwndRude = FindRudeApp(NULL);
2188 HandleFullScreenApp(hwndRude);
2189
2190 KillTimer(wParam);
2191 ++m_nRudeAppValidationCounter;
2192 if (m_nRudeAppValidationCounter < VALIDATE_RUDE_MAX_COUNT && !hwndRude)
2193 SetTimer(wParam, VALIDATE_RUDE_INTERVAL, NULL);
2194 break;
2195 }
2196 default:
2197 {
2198 WARN("Unknown timer ID: %p\n", wParam);
2199 break;
2200 }
2201 }
2202 return TRUE;
2203 }
2204
2205 LRESULT OnSetFont(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2206 {
2207 return m_TaskBar.SendMessageW(uMsg, wParam, lParam);
2208 }
2209
2210 LRESULT OnSettingChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2211 {
2212 if (wParam == SPI_SETNONCLIENTMETRICS)
2213 {
2214 /* Don't update the font, this will be done when we get a WM_SETFONT from our parent */
2215 UpdateButtonsSize(FALSE);
2216 }
2217
2218 return 0;
2219 }
2220
2221 LRESULT OnCopyData(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2222 {
2223 PCOPYDATASTRUCT cpData = (PCOPYDATASTRUCT)lParam;
2224 if (cpData->dwData == m_uHardErrorMsg)
2225 {
2226 /* A hard error balloon message */
2227 PBALLOON_HARD_ERROR_DATA pData = (PBALLOON_HARD_ERROR_DATA)cpData->lpData;
2228 ERR("Got balloon data 0x%x, 0x%x, '%S', '%S'\n", pData->Status, pData->dwType, (WCHAR*)((ULONG_PTR)pData + pData->TitleOffset), (WCHAR*)((ULONG_PTR)pData + pData->MessageOffset));
2229 if (pData->cbHeaderSize == sizeof(BALLOON_HARD_ERROR_DATA))
2230 m_HardErrorThread.StartThread(pData);
2231 return TRUE;
2232 }
2233
2234 return FALSE;
2235 }
2236
2237 HRESULT Initialize(IN HWND hWndParent, IN OUT ITrayWindow *tray)
2238 {
2239 m_Tray = tray;
2240 m_IsGroupingEnabled = g_TaskbarSettings.bGroupButtons;
2241 Create(hWndParent, 0, szRunningApps, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP);
2242 if (!m_hWnd)
2243 return E_FAIL;
2244 return S_OK;
2245 }
2246
2247 // *** IOleWindow methods ***
2248
2249 STDMETHODIMP
2250 GetWindow(HWND* phwnd) override
2251 {
2252 if (!phwnd)
2253 return E_INVALIDARG;
2254 *phwnd = m_hWnd;
2255 return S_OK;
2256 }
2257
2258 STDMETHODIMP
2259 ContextSensitiveHelp(BOOL fEnterMode) override
2260 {
2261 return E_NOTIMPL;
2262 }
2263
2264 DECLARE_WND_CLASS_EX(szTaskSwitchWndClass, CS_DBLCLKS, COLOR_3DFACE)
2265
2266 BEGIN_MSG_MAP(CTaskSwitchWnd)
2267 MESSAGE_HANDLER(WM_THEMECHANGED, OnThemeChanged)
2268 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
2269 MESSAGE_HANDLER(WM_SIZE, OnSize)
2270 MESSAGE_HANDLER(WM_CREATE, OnCreate)
2271 MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
2272 MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest)
2273 MESSAGE_HANDLER(WM_COMMAND, OnCommand)
2274 MESSAGE_HANDLER(WM_NOTIFY, OnNotify)
2275 MESSAGE_HANDLER(TSWM_UPDATETASKBARPOS, OnUpdateTaskbarPos)
2276 MESSAGE_HANDLER(TWM_SETTINGSCHANGED, OnTaskbarSettingsChanged)
2277 MESSAGE_HANDLER(WM_CONTEXTMENU, OnContextMenu)
2278 MESSAGE_HANDLER(WM_TIMER, OnTimer)
2279 MESSAGE_HANDLER(WM_SETFONT, OnSetFont)
2280 MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChanged)
2281 MESSAGE_HANDLER(m_ShellHookMsg, OnShellHook)
2282 MESSAGE_HANDLER(WM_MOUSEACTIVATE, OnMouseActivate)
2283 MESSAGE_HANDLER(WM_KLUDGEMINRECT, OnKludgeItemRect)
2284 MESSAGE_HANDLER(WM_COPYDATA, OnCopyData)
2285 MESSAGE_HANDLER(WM_WINDOWPOSCHANGED, OnWindowPosChanged)
2286 END_MSG_MAP()
2287
2288 DECLARE_NOT_AGGREGATABLE(CTaskSwitchWnd)
2289
2290 DECLARE_PROTECT_FINAL_CONSTRUCT()
2291 BEGIN_COM_MAP(CTaskSwitchWnd)
2292 COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow)
2293 END_COM_MAP()
2294};
2295
2296HRESULT CTaskSwitchWnd_CreateInstance(IN HWND hWndParent, IN OUT ITrayWindow *Tray, REFIID riid, void **ppv)
2297{
2298 return ShellObjectCreatorInit<CTaskSwitchWnd>(hWndParent, Tray, riid, ppv);
2299}