1 /* 2 * Copyright 2010-2014 Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * John Scipione, jscipione@gmail.com 7 * Clemens Zeidler, haiku@clemens-zeidler.de 8 */ 9 10 11 #include "StackAndTile.h" 12 13 #include <Debug.h> 14 15 #include "StackAndTilePrivate.h" 16 17 #include "Desktop.h" 18 #include "SATWindow.h" 19 #include "Tiling.h" 20 #include "Window.h" 21 22 23 static const int32 kRightOptionKey = 0x67; 24 static const int32 kTabKey = 0x26; 25 static const int32 kPageUpKey = 0x21; 26 static const int32 kPageDownKey = 0x36; 27 static const int32 kLeftArrowKey = 0x61; 28 static const int32 kUpArrowKey = 0x57; 29 static const int32 kRightArrowKey = 0x63; 30 static const int32 kDownArrowKey = 0x62; 31 32 static const int32 kModifiers = B_SHIFT_KEY | B_COMMAND_KEY 33 | B_CONTROL_KEY | B_OPTION_KEY | B_MENU_KEY; 34 35 36 using namespace std; 37 38 39 // #pragma mark - StackAndTile 40 41 42 StackAndTile::StackAndTile() 43 : 44 fDesktop(NULL), 45 fSATKeyPressed(false), 46 fCurrentSATWindow(NULL) 47 { 48 49 } 50 51 52 StackAndTile::~StackAndTile() 53 { 54 55 } 56 57 58 int32 59 StackAndTile::Identifier() 60 { 61 return BPrivate::kMagicSATIdentifier; 62 } 63 64 65 void 66 StackAndTile::ListenerRegistered(Desktop* desktop) 67 { 68 fDesktop = desktop; 69 70 WindowList& windows = desktop->AllWindows(); 71 for (Window *window = windows.FirstWindow(); window != NULL; 72 window = window->NextWindow(kAllWindowList)) 73 WindowAdded(window); 74 } 75 76 77 void 78 StackAndTile::ListenerUnregistered() 79 { 80 for (SATWindowMap::iterator it = fSATWindowMap.begin(); 81 it != fSATWindowMap.end(); it++) { 82 SATWindow* satWindow = it->second; 83 delete satWindow; 84 } 85 fSATWindowMap.clear(); 86 } 87 88 89 bool 90 StackAndTile::HandleMessage(Window* sender, BPrivate::LinkReceiver& link, 91 BPrivate::LinkSender& reply) 92 { 93 if (sender == NULL) 94 return _HandleMessage(link, reply); 95 96 SATWindow* satWindow = GetSATWindow(sender); 97 if (!satWindow) 98 return false; 99 100 return satWindow->HandleMessage(satWindow, link, reply); 101 } 102 103 104 void 105 StackAndTile::WindowAdded(Window* window) 106 { 107 SATWindow* satWindow = new (std::nothrow)SATWindow(this, window); 108 if (!satWindow) 109 return; 110 111 ASSERT(fSATWindowMap.find(window) == fSATWindowMap.end()); 112 fSATWindowMap[window] = satWindow; 113 } 114 115 116 void 117 StackAndTile::WindowRemoved(Window* window) 118 { 119 STRACE_SAT("StackAndTile::WindowRemoved %s\n", window->Title()); 120 121 SATWindowMap::iterator it = fSATWindowMap.find(window); 122 if (it == fSATWindowMap.end()) 123 return; 124 125 SATWindow* satWindow = it->second; 126 // delete SATWindow 127 delete satWindow; 128 fSATWindowMap.erase(it); 129 } 130 131 132 bool 133 StackAndTile::KeyPressed(uint32 what, int32 key, int32 modifiers) 134 { 135 if (what == B_MODIFIERS_CHANGED 136 || (what == B_UNMAPPED_KEY_DOWN && key == kRightOptionKey) 137 || (what == B_UNMAPPED_KEY_UP && key == kRightOptionKey)) { 138 // switch to and from stacking and snapping mode 139 bool wasPressed = fSATKeyPressed; 140 fSATKeyPressed = (what == B_MODIFIERS_CHANGED 141 && (modifiers & kModifiers) == B_OPTION_KEY) 142 || (what == B_UNMAPPED_KEY_DOWN && key == kRightOptionKey); 143 if (wasPressed && !fSATKeyPressed) 144 _StopSAT(); 145 if (!wasPressed && fSATKeyPressed) 146 _StartSAT(); 147 } 148 149 if (!SATKeyPressed() || what != B_KEY_DOWN) 150 return false; 151 152 SATWindow* frontWindow = GetSATWindow(fDesktop->FocusWindow()); 153 SATGroup* currentGroup = _GetSATGroup(frontWindow); 154 155 switch (key) { 156 case kLeftArrowKey: 157 case kRightArrowKey: 158 case kTabKey: 159 { 160 // go to previous or next window tab in current window group 161 if (currentGroup == NULL) 162 return false; 163 164 int32 groupSize = currentGroup->CountItems(); 165 if (groupSize <= 1) 166 return false; 167 168 for (int32 i = 0; i < groupSize; i++) { 169 SATWindow* targetWindow = currentGroup->WindowAt(i); 170 if (targetWindow == frontWindow) { 171 if (key == kLeftArrowKey 172 || (key == kTabKey && (modifiers & B_SHIFT_KEY) != 0)) { 173 // Go to previous window tab (wrap around) 174 int32 previousIndex = i > 0 ? i - 1 : groupSize - 1; 175 targetWindow = currentGroup->WindowAt(previousIndex); 176 } else { 177 // Go to next window tab (wrap around) 178 int32 nextIndex = i < groupSize - 1 ? i + 1 : 0; 179 targetWindow = currentGroup->WindowAt(nextIndex); 180 } 181 182 _ActivateWindow(targetWindow); 183 return true; 184 } 185 } 186 break; 187 } 188 189 case kUpArrowKey: 190 case kPageUpKey: 191 { 192 // go to previous window group 193 GroupIterator groups(this, fDesktop); 194 groups.SetCurrentGroup(currentGroup); 195 SATGroup* backmostGroup = NULL; 196 197 while (true) { 198 SATGroup* group = groups.NextGroup(); 199 if (group == NULL || group == currentGroup) 200 break; 201 else if (group->CountItems() < 1) 202 continue; 203 204 if (currentGroup == NULL) { 205 SATWindow* activeWindow = group->ActiveWindow(); 206 if (activeWindow != NULL) 207 _ActivateWindow(activeWindow); 208 else 209 _ActivateWindow(group->WindowAt(0)); 210 211 return true; 212 } 213 backmostGroup = group; 214 } 215 if (backmostGroup != NULL && backmostGroup != currentGroup) { 216 SATWindow* activeWindow = backmostGroup->ActiveWindow(); 217 if (activeWindow != NULL) 218 _ActivateWindow(activeWindow); 219 else 220 _ActivateWindow(backmostGroup->WindowAt(0)); 221 222 return true; 223 } 224 225 break; 226 } 227 228 case kDownArrowKey: 229 case kPageDownKey: 230 { 231 // go to next window group 232 GroupIterator groups(this, fDesktop); 233 groups.SetCurrentGroup(currentGroup); 234 235 while (true) { 236 SATGroup* group = groups.NextGroup(); 237 if (group == NULL || group == currentGroup) 238 break; 239 else if (group->CountItems() < 1) 240 continue; 241 242 SATWindow* activeWindow = group->ActiveWindow(); 243 if (activeWindow != NULL) 244 _ActivateWindow(activeWindow); 245 else 246 _ActivateWindow(group->WindowAt(0)); 247 248 if (currentGroup != NULL && frontWindow != NULL) { 249 Window* window = frontWindow->GetWindow(); 250 fDesktop->SendWindowBehind(window); 251 WindowSentBehind(window, NULL); 252 } 253 return true; 254 } 255 break; 256 } 257 } 258 259 return false; 260 } 261 262 263 void 264 StackAndTile::MouseDown(Window* window, BMessage* message, const BPoint& where) 265 { 266 SATWindow* satWindow = GetSATWindow(window); 267 if (!satWindow || !satWindow->GetDecorator()) 268 return; 269 270 // fCurrentSATWindow is not zero if e.g. the secondary and the primary 271 // mouse button are pressed at the same time 272 if ((message->FindInt32("buttons") & B_PRIMARY_MOUSE_BUTTON) == 0 || 273 fCurrentSATWindow != NULL) 274 return; 275 276 // we are only interested in single clicks 277 if (message->FindInt32("clicks") == 2) 278 return; 279 280 int32 tab; 281 switch (satWindow->GetDecorator()->RegionAt(where, tab)) { 282 case Decorator::REGION_TAB: 283 case Decorator::REGION_LEFT_BORDER: 284 case Decorator::REGION_RIGHT_BORDER: 285 case Decorator::REGION_TOP_BORDER: 286 case Decorator::REGION_BOTTOM_BORDER: 287 case Decorator::REGION_LEFT_TOP_CORNER: 288 case Decorator::REGION_LEFT_BOTTOM_CORNER: 289 case Decorator::REGION_RIGHT_TOP_CORNER: 290 case Decorator::REGION_RIGHT_BOTTOM_CORNER: 291 break; 292 293 default: 294 return; 295 } 296 297 ASSERT(fCurrentSATWindow == NULL); 298 fCurrentSATWindow = satWindow; 299 300 if (!SATKeyPressed()) 301 return; 302 303 _StartSAT(); 304 } 305 306 307 void 308 StackAndTile::MouseUp(Window* window, BMessage* message, const BPoint& where) 309 { 310 if (fSATKeyPressed) 311 _StopSAT(); 312 313 fCurrentSATWindow = NULL; 314 } 315 316 317 void 318 StackAndTile::WindowMoved(Window* window) 319 { 320 SATWindow* satWindow = GetSATWindow(window); 321 if (satWindow == NULL) 322 return; 323 324 if (SATKeyPressed() && fCurrentSATWindow) 325 satWindow->FindSnappingCandidates(); 326 else 327 satWindow->DoGroupLayout(); 328 } 329 330 331 void 332 StackAndTile::WindowResized(Window* window) 333 { 334 SATWindow* satWindow = GetSATWindow(window); 335 if (satWindow == NULL) 336 return; 337 satWindow->Resized(); 338 339 if (SATKeyPressed() && fCurrentSATWindow) 340 satWindow->FindSnappingCandidates(); 341 else 342 satWindow->DoGroupLayout(); 343 } 344 345 346 void 347 StackAndTile::WindowActivated(Window* window) 348 { 349 SATWindow* satWindow = GetSATWindow(window); 350 if (satWindow == NULL) 351 return; 352 353 _ActivateWindow(satWindow); 354 } 355 356 357 void 358 StackAndTile::WindowSentBehind(Window* window, Window* behindOf) 359 { 360 SATWindow* satWindow = GetSATWindow(window); 361 if (satWindow == NULL) 362 return; 363 364 SATGroup* group = satWindow->GetGroup(); 365 if (group == NULL) 366 return; 367 368 Desktop* desktop = satWindow->GetWindow()->Desktop(); 369 if (desktop == NULL) 370 return; 371 372 const WindowAreaList& areaList = group->GetAreaList(); 373 for (int32 i = 0; i < areaList.CountItems(); i++) { 374 WindowArea* area = areaList.ItemAt(i); 375 SATWindow* topWindow = area->TopWindow(); 376 if (topWindow == NULL || topWindow == satWindow) 377 continue; 378 desktop->SendWindowBehind(topWindow->GetWindow(), behindOf); 379 } 380 } 381 382 383 void 384 StackAndTile::WindowWorkspacesChanged(Window* window, uint32 workspaces) 385 { 386 SATWindow* satWindow = GetSATWindow(window); 387 if (satWindow == NULL) 388 return; 389 390 SATGroup* group = satWindow->GetGroup(); 391 if (group == NULL) 392 return; 393 394 Desktop* desktop = satWindow->GetWindow()->Desktop(); 395 if (desktop == NULL) 396 return; 397 398 const WindowAreaList& areaList = group->GetAreaList(); 399 for (int32 i = 0; i < areaList.CountItems(); i++) { 400 WindowArea* area = areaList.ItemAt(i); 401 if (area->WindowList().HasItem(satWindow)) 402 continue; 403 SATWindow* topWindow = area->TopWindow(); 404 desktop->SetWindowWorkspaces(topWindow->GetWindow(), workspaces); 405 } 406 } 407 408 409 void 410 StackAndTile::WindowHidden(Window* window, bool fromMinimize) 411 { 412 SATWindow* satWindow = GetSATWindow(window); 413 if (satWindow == NULL) 414 return; 415 416 SATGroup* group = satWindow->GetGroup(); 417 if (group == NULL) 418 return; 419 420 if (fromMinimize == false && group->CountItems() > 1) 421 group->RemoveWindow(satWindow, false); 422 } 423 424 425 void 426 StackAndTile::WindowMinimized(Window* window, bool minimize) 427 { 428 SATWindow* satWindow = GetSATWindow(window); 429 if (satWindow == NULL) 430 return; 431 432 SATGroup* group = satWindow->GetGroup(); 433 if (group == NULL) 434 return; 435 436 Desktop* desktop = satWindow->GetWindow()->Desktop(); 437 if (desktop == NULL) 438 return; 439 440 for (int i = 0; i < group->CountItems(); i++) { 441 SATWindow* listWindow = group->WindowAt(i); 442 if (listWindow != satWindow) 443 listWindow->GetWindow()->ServerWindow()->NotifyMinimize(minimize); 444 } 445 } 446 447 448 void 449 StackAndTile::WindowTabLocationChanged(Window* window, float location, 450 bool isShifting) 451 { 452 453 } 454 455 456 void 457 StackAndTile::SizeLimitsChanged(Window* window, int32 minWidth, int32 maxWidth, 458 int32 minHeight, int32 maxHeight) 459 { 460 SATWindow* satWindow = GetSATWindow(window); 461 if (!satWindow) 462 return; 463 satWindow->SetOriginalSizeLimits(minWidth, maxWidth, minHeight, maxHeight); 464 465 // trigger a relayout 466 WindowMoved(window); 467 } 468 469 470 void 471 StackAndTile::WindowLookChanged(Window* window, window_look look) 472 { 473 SATWindow* satWindow = GetSATWindow(window); 474 if (!satWindow) 475 return; 476 satWindow->WindowLookChanged(look); 477 } 478 479 480 void 481 StackAndTile::WindowFeelChanged(Window* window, window_feel feel) 482 { 483 // check if it is still a compatible feel 484 if (feel == B_NORMAL_WINDOW_FEEL) 485 return; 486 SATWindow* satWindow = GetSATWindow(window); 487 if (satWindow == NULL) 488 return; 489 490 SATGroup* group = satWindow->GetGroup(); 491 if (group == NULL) 492 return; 493 494 if (group->CountItems() > 1) 495 group->RemoveWindow(satWindow, false); 496 } 497 498 499 bool 500 StackAndTile::SetDecoratorSettings(Window* window, const BMessage& settings) 501 { 502 SATWindow* satWindow = GetSATWindow(window); 503 if (!satWindow) 504 return false; 505 506 return satWindow->SetSettings(settings); 507 } 508 509 510 void 511 StackAndTile::GetDecoratorSettings(Window* window, BMessage& settings) 512 { 513 SATWindow* satWindow = GetSATWindow(window); 514 if (!satWindow) 515 return; 516 517 satWindow->GetSettings(settings); 518 } 519 520 521 SATWindow* 522 StackAndTile::GetSATWindow(Window* window) 523 { 524 if (window == NULL) 525 return NULL; 526 527 SATWindowMap::const_iterator it = fSATWindowMap.find( 528 window); 529 if (it != fSATWindowMap.end()) 530 return it->second; 531 532 // TODO fix race condition with WindowAdded this method is called before 533 // WindowAdded and a SATWindow is created twice! 534 return NULL; 535 536 // If we don't know this window, memory allocation might has been failed 537 // previously. Try to add the window now. 538 SATWindow* satWindow = new (std::nothrow)SATWindow(this, window); 539 if (satWindow) 540 fSATWindowMap[window] = satWindow; 541 542 return satWindow; 543 } 544 545 546 SATWindow* 547 StackAndTile::FindSATWindow(uint64 id) 548 { 549 for (SATWindowMap::const_iterator it = fSATWindowMap.begin(); 550 it != fSATWindowMap.end(); it++) { 551 SATWindow* window = it->second; 552 if (window->Id() == id) 553 return window; 554 } 555 556 return NULL; 557 } 558 559 560 // #pragma mark - StackAndTile private methods 561 562 563 void 564 StackAndTile::_StartSAT() 565 { 566 STRACE_SAT("StackAndTile::_StartSAT()\n"); 567 if (!fCurrentSATWindow) 568 return; 569 570 // Remove window from the group. 571 SATGroup* group = fCurrentSATWindow->GetGroup(); 572 if (group == NULL) 573 return; 574 575 group->RemoveWindow(fCurrentSATWindow, false); 576 // Bring window to the front. (in focus follow mouse this is not 577 // automatically the case) 578 _ActivateWindow(fCurrentSATWindow); 579 580 fCurrentSATWindow->FindSnappingCandidates(); 581 } 582 583 584 void 585 StackAndTile::_StopSAT() 586 { 587 STRACE_SAT("StackAndTile::_StopSAT()\n"); 588 if (!fCurrentSATWindow) 589 return; 590 if (fCurrentSATWindow->JoinCandidates()) 591 _ActivateWindow(fCurrentSATWindow); 592 } 593 594 595 void 596 StackAndTile::_ActivateWindow(SATWindow* satWindow) 597 { 598 if (satWindow == NULL) 599 return; 600 601 SATGroup* group = satWindow->GetGroup(); 602 if (group == NULL) 603 return; 604 605 Desktop* desktop = satWindow->GetWindow()->Desktop(); 606 if (desktop == NULL) 607 return; 608 609 WindowArea* area = satWindow->GetWindowArea(); 610 if (area == NULL) 611 return; 612 613 area->MoveToTopLayer(satWindow); 614 615 // save the active window of the current group 616 SATWindow* frontWindow = GetSATWindow(fDesktop->FocusWindow()); 617 SATGroup* currentGroup = _GetSATGroup(frontWindow); 618 if (currentGroup != NULL && currentGroup != group && frontWindow != NULL) 619 currentGroup->SetActiveWindow(frontWindow); 620 else 621 group->SetActiveWindow(satWindow); 622 623 const WindowAreaList& areas = group->GetAreaList(); 624 int32 areasCount = areas.CountItems(); 625 for (int32 i = 0; i < areasCount; i++) { 626 WindowArea* currentArea = areas.ItemAt(i); 627 if (currentArea == area) 628 continue; 629 630 desktop->ActivateWindow(currentArea->TopWindow()->GetWindow()); 631 } 632 633 desktop->ActivateWindow(satWindow->GetWindow()); 634 } 635 636 637 bool 638 StackAndTile::_HandleMessage(BPrivate::LinkReceiver& link, 639 BPrivate::LinkSender& reply) 640 { 641 int32 what; 642 link.Read<int32>(&what); 643 644 switch (what) { 645 case BPrivate::kSaveAllGroups: 646 { 647 BMessage allGroupsArchive; 648 GroupIterator groups(this, fDesktop); 649 while (true) { 650 SATGroup* group = groups.NextGroup(); 651 if (group == NULL) 652 break; 653 if (group->CountItems() <= 1) 654 continue; 655 BMessage groupArchive; 656 if (group->ArchiveGroup(groupArchive) != B_OK) 657 continue; 658 allGroupsArchive.AddMessage("group", &groupArchive); 659 } 660 int32 size = allGroupsArchive.FlattenedSize(); 661 char buffer[size]; 662 if (allGroupsArchive.Flatten(buffer, size) == B_OK) { 663 reply.StartMessage(B_OK); 664 reply.Attach<int32>(size); 665 reply.Attach(buffer, size); 666 } else 667 reply.StartMessage(B_ERROR); 668 reply.Flush(); 669 break; 670 } 671 672 case BPrivate::kRestoreGroup: 673 { 674 int32 size; 675 if (link.Read<int32>(&size) == B_OK) { 676 char buffer[size]; 677 BMessage group; 678 if (link.Read(buffer, size) == B_OK 679 && group.Unflatten(buffer) == B_OK) { 680 status_t status = SATGroup::RestoreGroup(group, this); 681 reply.StartMessage(status); 682 reply.Flush(); 683 } 684 } 685 break; 686 } 687 688 default: 689 return false; 690 } 691 692 return true; 693 } 694 695 696 SATGroup* 697 StackAndTile::_GetSATGroup(SATWindow* window) 698 { 699 if (window == NULL) 700 return NULL; 701 702 SATGroup* group = window->GetGroup(); 703 if (group == NULL) 704 return NULL; 705 706 if (group->CountItems() < 1) 707 return NULL; 708 709 return group; 710 } 711 712 713 // #pragma mark - GroupIterator 714 715 716 GroupIterator::GroupIterator(StackAndTile* sat, Desktop* desktop) 717 : 718 fStackAndTile(sat), 719 fDesktop(desktop), 720 fCurrentGroup(NULL) 721 { 722 RewindToFront(); 723 } 724 725 726 void 727 GroupIterator::RewindToFront() 728 { 729 fCurrentWindow = fDesktop->CurrentWindows().LastWindow(); 730 } 731 732 733 SATGroup* 734 GroupIterator::NextGroup() 735 { 736 SATGroup* group = NULL; 737 do { 738 Window* window = fCurrentWindow; 739 if (window == NULL) { 740 group = NULL; 741 break; 742 } 743 fCurrentWindow = fCurrentWindow->PreviousWindow( 744 fCurrentWindow->CurrentWorkspace()); 745 if (window->IsHidden() 746 || strcmp(window->Title(), "Deskbar") == 0 747 || strcmp(window->Title(), "Desktop") == 0) { 748 continue; 749 } 750 751 SATWindow* satWindow = fStackAndTile->GetSATWindow(window); 752 group = satWindow->GetGroup(); 753 } while (group == NULL || fCurrentGroup == group); 754 755 fCurrentGroup = group; 756 return fCurrentGroup; 757 } 758 759 760 // #pragma mark - WindowIterator 761 762 763 WindowIterator::WindowIterator(SATGroup* group, bool reverseLayerOrder) 764 : 765 fGroup(group), 766 fReverseLayerOrder(reverseLayerOrder) 767 { 768 if (fReverseLayerOrder) 769 _ReverseRewind(); 770 else 771 Rewind(); 772 } 773 774 775 void 776 WindowIterator::Rewind() 777 { 778 fAreaIndex = 0; 779 fWindowIndex = 0; 780 fCurrentArea = fGroup->GetAreaList().ItemAt(fAreaIndex); 781 } 782 783 784 SATWindow* 785 WindowIterator::NextWindow() 786 { 787 if (fReverseLayerOrder) 788 return _ReverseNextWindow(); 789 790 if (fWindowIndex == fCurrentArea->LayerOrder().CountItems()) { 791 fAreaIndex++; 792 fWindowIndex = 0; 793 fCurrentArea = fGroup->GetAreaList().ItemAt(fAreaIndex); 794 if (!fCurrentArea) 795 return NULL; 796 } 797 SATWindow* window = fCurrentArea->LayerOrder().ItemAt(fWindowIndex); 798 fWindowIndex++; 799 return window; 800 } 801 802 803 // #pragma mark - WindowIterator private methods 804 805 806 SATWindow* 807 WindowIterator::_ReverseNextWindow() 808 { 809 if (fWindowIndex < 0) { 810 fAreaIndex++; 811 fCurrentArea = fGroup->GetAreaList().ItemAt(fAreaIndex); 812 if (!fCurrentArea) 813 return NULL; 814 fWindowIndex = fCurrentArea->LayerOrder().CountItems() - 1; 815 } 816 SATWindow* window = fCurrentArea->LayerOrder().ItemAt(fWindowIndex); 817 fWindowIndex--; 818 return window; 819 } 820 821 822 void 823 WindowIterator::_ReverseRewind() 824 { 825 Rewind(); 826 if (fCurrentArea) 827 fWindowIndex = fCurrentArea->LayerOrder().CountItems() - 1; 828 } 829