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 // switch off group navigation because it clashes with tracker... 133 return false; 134 if (!SATKeyPressed() || (modifiers & B_COMMAND_KEY) == 0 135 || what != B_KEY_DOWN) 136 return false; 137 138 const int kArrowKeyUp = 87; 139 const int kArrowKeyDown = 98; 140 141 switch (key) { 142 case kArrowKeyDown: 143 { 144 SATWindow* frontWindow = GetSATWindow(fDesktop->FocusWindow()); 145 SATGroup* currentGroup = NULL; 146 if (frontWindow) 147 currentGroup = frontWindow->GetGroup(); 148 if (currentGroup && currentGroup->CountItems() <= 1) 149 currentGroup = NULL; 150 151 GroupIterator groups(this, fDesktop); 152 bool currentFound = false; 153 while (true) { 154 SATGroup* group = groups.NextGroup(); 155 if (group == NULL) 156 break; 157 if (group->CountItems() <= 1) 158 continue; 159 160 if (currentGroup == NULL) 161 currentFound = true; 162 // if no group is selected just activate the first one 163 else if (currentGroup == group) { 164 currentFound = true; 165 continue; 166 } 167 if (currentFound) { 168 _ActivateWindow(group->WindowAt(0)); 169 if (currentGroup) { 170 Window* window = currentGroup->WindowAt(0)->GetWindow(); 171 fDesktop->SendWindowBehind(window); 172 WindowSentBehind(window, NULL); 173 } 174 return true; 175 } 176 } 177 break; 178 } 179 180 case kArrowKeyUp: 181 { 182 SATWindow* frontWindow = GetSATWindow(fDesktop->FocusWindow()); 183 SATGroup* currentGroup = NULL; 184 if (frontWindow) 185 currentGroup = frontWindow->GetGroup(); 186 if (currentGroup && currentGroup->CountItems() <= 1) 187 currentGroup = NULL; 188 189 SATGroup* backmostGroup = NULL; 190 GroupIterator groups(this, fDesktop); 191 while (true) { 192 SATGroup* group = groups.NextGroup(); 193 if (group == NULL) 194 break; 195 if (group->CountItems() <= 1) 196 continue; 197 // if no group is selected just activate the first one 198 if (currentGroup == NULL) { 199 _ActivateWindow(group->WindowAt(0)); 200 return true; 201 } 202 backmostGroup = group; 203 } 204 if (backmostGroup && backmostGroup != currentGroup) { 205 _ActivateWindow(backmostGroup->WindowAt(0)); 206 return true; 207 } 208 break; 209 } 210 } 211 return false; 212 } 213 214 215 void 216 StackAndTile::MouseDown(Window* window, BMessage* message, const BPoint& where) 217 { 218 SATWindow* satWindow = GetSATWindow(window); 219 if (!satWindow || !satWindow->GetDecorator()) 220 return; 221 222 // fCurrentSATWindow is not zero if e.g. the secondary and the primary 223 // mouse button are pressed at the same time 224 if ((message->FindInt32("buttons") & B_PRIMARY_MOUSE_BUTTON) == 0 || 225 fCurrentSATWindow != NULL) 226 return; 227 228 // we are only interested in single clicks 229 if (message->FindInt32("clicks") == 2) 230 return; 231 232 int32 tab; 233 switch (satWindow->GetDecorator()->RegionAt(where, tab)) { 234 case Decorator::REGION_TAB: 235 case Decorator::REGION_LEFT_BORDER: 236 case Decorator::REGION_RIGHT_BORDER: 237 case Decorator::REGION_TOP_BORDER: 238 case Decorator::REGION_BOTTOM_BORDER: 239 case Decorator::REGION_LEFT_TOP_CORNER: 240 case Decorator::REGION_LEFT_BOTTOM_CORNER: 241 case Decorator::REGION_RIGHT_TOP_CORNER: 242 case Decorator::REGION_RIGHT_BOTTOM_CORNER: 243 break; 244 245 default: 246 return; 247 } 248 249 ASSERT(fCurrentSATWindow == NULL); 250 fCurrentSATWindow = satWindow; 251 252 if (!SATKeyPressed()) 253 return; 254 255 _StartSAT(); 256 } 257 258 259 void 260 StackAndTile::MouseUp(Window* window, BMessage* message, const BPoint& where) 261 { 262 if (fSATKeyPressed) 263 _StopSAT(); 264 265 fCurrentSATWindow = NULL; 266 } 267 268 269 void 270 StackAndTile::WindowMoved(Window* window) 271 { 272 SATWindow* satWindow = GetSATWindow(window); 273 if (satWindow == NULL) 274 return; 275 276 if (SATKeyPressed() && fCurrentSATWindow) 277 satWindow->FindSnappingCandidates(); 278 else 279 satWindow->DoGroupLayout(); 280 } 281 282 283 void 284 StackAndTile::WindowResized(Window* window) 285 { 286 SATWindow* satWindow = GetSATWindow(window); 287 if (satWindow == NULL) 288 return; 289 satWindow->Resized(); 290 291 if (SATKeyPressed() && fCurrentSATWindow) 292 satWindow->FindSnappingCandidates(); 293 else 294 satWindow->DoGroupLayout(); 295 } 296 297 298 void 299 StackAndTile::WindowActitvated(Window* window) 300 { 301 SATWindow* satWindow = GetSATWindow(window); 302 if (satWindow == NULL) 303 return; 304 _ActivateWindow(satWindow); 305 } 306 307 308 void 309 StackAndTile::WindowSentBehind(Window* window, Window* behindOf) 310 { 311 SATWindow* satWindow = GetSATWindow(window); 312 if (satWindow == NULL) 313 return; 314 SATGroup* group = satWindow->GetGroup(); 315 if (group == NULL) 316 return; 317 Desktop* desktop = satWindow->GetWindow()->Desktop(); 318 if (desktop == NULL) 319 return; 320 321 const WindowAreaList& areaList = group->GetAreaList(); 322 for (int32 i = 0; i < areaList.CountItems(); i++) { 323 WindowArea* area = areaList.ItemAt(i); 324 SATWindow* topWindow = area->TopWindow(); 325 if (topWindow == NULL || topWindow == satWindow) 326 continue; 327 desktop->SendWindowBehind(topWindow->GetWindow(), behindOf); 328 } 329 } 330 331 332 void 333 StackAndTile::WindowWorkspacesChanged(Window* window, uint32 workspaces) 334 { 335 SATWindow* satWindow = GetSATWindow(window); 336 if (satWindow == NULL) 337 return; 338 SATGroup* group = satWindow->GetGroup(); 339 if (group == NULL) 340 return; 341 Desktop* desktop = satWindow->GetWindow()->Desktop(); 342 if (desktop == NULL) 343 return; 344 345 for (int i = 0; i < group->CountItems(); i++) { 346 SATWindow* listWindow = group->WindowAt(i); 347 if (listWindow != satWindow) 348 desktop->SetWindowWorkspaces(listWindow->GetWindow(), workspaces); 349 } 350 } 351 352 353 void 354 StackAndTile::WindowHidden(Window* window, bool fromMinimize) 355 { 356 SATWindow* satWindow = GetSATWindow(window); 357 if (satWindow == NULL) 358 return; 359 SATGroup* group = satWindow->GetGroup(); 360 if (group == NULL) 361 return; 362 if (fromMinimize == false && group->CountItems() > 1) 363 group->RemoveWindow(satWindow, false); 364 } 365 366 367 void 368 StackAndTile::WindowMinimized(Window* window, bool minimize) 369 { 370 SATWindow* satWindow = GetSATWindow(window); 371 if (satWindow == NULL) 372 return; 373 SATGroup* group = satWindow->GetGroup(); 374 if (group == NULL) 375 return; 376 Desktop* desktop = satWindow->GetWindow()->Desktop(); 377 if (desktop == NULL) 378 return; 379 380 for (int i = 0; i < group->CountItems(); i++) { 381 SATWindow* listWindow = group->WindowAt(i); 382 if (listWindow != satWindow) 383 listWindow->GetWindow()->ServerWindow()->NotifyMinimize(minimize); 384 } 385 } 386 387 388 void 389 StackAndTile::WindowTabLocationChanged(Window* window, float location, 390 bool isShifting) 391 { 392 393 } 394 395 396 void 397 StackAndTile::SizeLimitsChanged(Window* window, int32 minWidth, int32 maxWidth, 398 int32 minHeight, int32 maxHeight) 399 { 400 SATWindow* satWindow = GetSATWindow(window); 401 if (!satWindow) 402 return; 403 satWindow->SetOriginalSizeLimits(minWidth, maxWidth, minHeight, maxHeight); 404 405 // trigger a relayout 406 WindowMoved(window); 407 } 408 409 410 void 411 StackAndTile::WindowLookChanged(Window* window, window_look look) 412 { 413 SATWindow* satWindow = GetSATWindow(window); 414 if (!satWindow) 415 return; 416 satWindow->WindowLookChanged(look); 417 } 418 419 420 void 421 StackAndTile::WindowFeelChanged(Window* window, window_feel feel) 422 { 423 // check if it is still a compatible feel 424 if (feel == B_NORMAL_WINDOW_FEEL) 425 return; 426 SATWindow* satWindow = GetSATWindow(window); 427 if (!satWindow) 428 return; 429 SATGroup* group = satWindow->GetGroup(); 430 if (!group) 431 return; 432 if (group->CountItems() > 1) 433 group->RemoveWindow(satWindow, false); 434 } 435 436 437 bool 438 StackAndTile::SetDecoratorSettings(Window* window, const BMessage& settings) 439 { 440 SATWindow* satWindow = GetSATWindow(window); 441 if (!satWindow) 442 return false; 443 444 return satWindow->SetSettings(settings); 445 } 446 447 448 void 449 StackAndTile::GetDecoratorSettings(Window* window, BMessage& settings) 450 { 451 SATWindow* satWindow = GetSATWindow(window); 452 if (!satWindow) 453 return; 454 455 satWindow->GetSettings(settings); 456 } 457 458 459 SATWindow* 460 StackAndTile::GetSATWindow(Window* window) 461 { 462 if (window == NULL) 463 return NULL; 464 465 SATWindowMap::const_iterator it = fSATWindowMap.find( 466 window); 467 if (it != fSATWindowMap.end()) 468 return it->second; 469 470 // TODO fix race condition with WindowAdded this method is called before 471 // WindowAdded and a SATWindow is created twice! 472 return NULL; 473 474 // If we don't know this window, memory allocation might has been failed 475 // previously. Try to add the window now. 476 SATWindow* satWindow = new (std::nothrow)SATWindow(this, window); 477 if (satWindow) 478 fSATWindowMap[window] = satWindow; 479 480 return satWindow; 481 } 482 483 484 SATWindow* 485 StackAndTile::FindSATWindow(uint64 id) 486 { 487 for (SATWindowMap::const_iterator it = fSATWindowMap.begin(); 488 it != fSATWindowMap.end(); it++) { 489 SATWindow* window = it->second; 490 if (window->Id() == id) 491 return window; 492 } 493 return NULL; 494 } 495 496 497 void 498 StackAndTile::_StartSAT() 499 { 500 STRACE_SAT("StackAndTile::_StartSAT()\n"); 501 if (!fCurrentSATWindow) 502 return; 503 504 // Remove window from the group. 505 SATGroup* group = fCurrentSATWindow->GetGroup(); 506 if (!group) 507 return; 508 509 group->RemoveWindow(fCurrentSATWindow, false); 510 // Bring window to the front. (in focus follow mouse this is not 511 // automatically the case) 512 _ActivateWindow(fCurrentSATWindow); 513 514 fCurrentSATWindow->FindSnappingCandidates(); 515 } 516 517 518 void 519 StackAndTile::_StopSAT() 520 { 521 STRACE_SAT("StackAndTile::_StopSAT()\n"); 522 if (!fCurrentSATWindow) 523 return; 524 if (fCurrentSATWindow->JoinCandidates()) 525 _ActivateWindow(fCurrentSATWindow); 526 } 527 528 529 void 530 StackAndTile::_ActivateWindow(SATWindow* satWindow) 531 { 532 SATGroup* group = satWindow->GetGroup(); 533 if (!group) 534 return; 535 Desktop* desktop = satWindow->GetWindow()->Desktop(); 536 if (!desktop) 537 return; 538 WindowArea* area = satWindow->GetWindowArea(); 539 if (!area) 540 return; 541 area->MoveToTopLayer(satWindow); 542 543 const WindowAreaList& areas = group->GetAreaList() ; 544 for (int32 i = 0; i < areas.CountItems(); i++) { 545 WindowArea* currentArea = areas.ItemAt(i); 546 if (currentArea == area) 547 continue; 548 desktop->ActivateWindow(currentArea->TopWindow()->GetWindow()); 549 } 550 551 desktop->ActivateWindow(satWindow->GetWindow()); 552 } 553 554 555 bool 556 StackAndTile::_HandleMessage(BPrivate::LinkReceiver& link, 557 BPrivate::LinkSender& reply) 558 { 559 int32 what; 560 link.Read<int32>(&what); 561 562 switch (what) { 563 case BPrivate::kSaveAllGroups: 564 { 565 BMessage allGroupsArchive; 566 GroupIterator groups(this, fDesktop); 567 while (true) { 568 SATGroup* group = groups.NextGroup(); 569 if (group == NULL) 570 break; 571 if (group->CountItems() <= 1) 572 continue; 573 BMessage groupArchive; 574 if (group->ArchiveGroup(groupArchive) != B_OK) 575 continue; 576 allGroupsArchive.AddMessage("group", &groupArchive); 577 } 578 int32 size = allGroupsArchive.FlattenedSize(); 579 char buffer[size]; 580 if (allGroupsArchive.Flatten(buffer, size) == B_OK) { 581 reply.StartMessage(B_OK); 582 reply.Attach<int32>(size); 583 reply.Attach(buffer, size); 584 } else 585 reply.StartMessage(B_ERROR); 586 reply.Flush(); 587 break; 588 } 589 590 case BPrivate::kRestoreGroup: 591 { 592 int32 size; 593 if (link.Read<int32>(&size) == B_OK) { 594 char buffer[size]; 595 BMessage group; 596 if (link.Read(buffer, size) == B_OK 597 && group.Unflatten(buffer) == B_OK) { 598 status_t status = SATGroup::RestoreGroup(group, this); 599 reply.StartMessage(status); 600 reply.Flush(); 601 } 602 } 603 break; 604 } 605 606 default: 607 return false; 608 } 609 610 return true; 611 } 612 613 614 GroupIterator::GroupIterator(StackAndTile* sat, Desktop* desktop) 615 : 616 fStackAndTile(sat), 617 fDesktop(desktop), 618 fCurrentGroup(NULL) 619 { 620 RewindToFront(); 621 } 622 623 624 void 625 GroupIterator::RewindToFront() 626 { 627 fCurrentWindow = fDesktop->CurrentWindows().LastWindow(); 628 } 629 630 631 SATGroup* 632 GroupIterator::NextGroup() 633 { 634 SATGroup* group = NULL; 635 do { 636 Window* window = fCurrentWindow; 637 if (window == NULL) { 638 group = NULL; 639 break; 640 } 641 fCurrentWindow = fCurrentWindow->PreviousWindow( 642 fCurrentWindow->CurrentWorkspace()); 643 if (window->IsHidden() 644 || strcmp(window->Title(), "Deskbar") == 0 645 || strcmp(window->Title(), "Desktop") == 0) 646 continue; 647 648 SATWindow* satWindow = fStackAndTile->GetSATWindow(window); 649 group = satWindow->GetGroup(); 650 } while (group == NULL || fCurrentGroup == group); 651 652 fCurrentGroup = group; 653 return fCurrentGroup; 654 } 655 656 657 WindowIterator::WindowIterator(SATGroup* group, bool reverseLayerOrder) 658 : 659 fGroup(group), 660 fReverseLayerOrder(reverseLayerOrder) 661 { 662 if (fReverseLayerOrder) 663 _ReverseRewind(); 664 else 665 Rewind(); 666 } 667 668 669 void 670 WindowIterator::Rewind() 671 { 672 fAreaIndex = 0; 673 fWindowIndex = 0; 674 fCurrentArea = fGroup->GetAreaList().ItemAt(fAreaIndex); 675 } 676 677 678 SATWindow* 679 WindowIterator::NextWindow() 680 { 681 if (fReverseLayerOrder) 682 return _ReverseNextWindow(); 683 684 if (fWindowIndex == fCurrentArea->LayerOrder().CountItems()) { 685 fAreaIndex++; 686 fWindowIndex = 0; 687 fCurrentArea = fGroup->GetAreaList().ItemAt(fAreaIndex); 688 if (!fCurrentArea) 689 return NULL; 690 } 691 SATWindow* window = fCurrentArea->LayerOrder().ItemAt(fWindowIndex); 692 fWindowIndex++; 693 return window; 694 } 695 696 697 SATWindow* 698 WindowIterator::_ReverseNextWindow() 699 { 700 if (fWindowIndex < 0) { 701 fAreaIndex++; 702 fCurrentArea = fGroup->GetAreaList().ItemAt(fAreaIndex); 703 if (!fCurrentArea) 704 return NULL; 705 fWindowIndex = fCurrentArea->LayerOrder().CountItems() - 1; 706 } 707 SATWindow* window = fCurrentArea->LayerOrder().ItemAt(fWindowIndex); 708 fWindowIndex--; 709 return window; 710 } 711 712 713 void 714 WindowIterator::_ReverseRewind() 715 { 716 Rewind(); 717 if (fCurrentArea) 718 fWindowIndex = fCurrentArea->LayerOrder().CountItems() - 1; 719 } 720 721 722 SATSnappingBehaviour::~SATSnappingBehaviour() 723 { 724 725 } 726