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