Reactos
at master 2299 lines 68 kB view raw
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}