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