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 } 312 313 314 void 315 StackAndTile::WindowWorkspacesChanged(Window* window, uint32 workspaces) 316 { 317 SATWindow* satWindow = GetSATWindow(window); 318 if (satWindow == NULL) 319 return; 320 SATGroup* group = satWindow->GetGroup(); 321 if (group == NULL) 322 return; 323 Desktop* desktop = satWindow->GetWindow()->Desktop(); 324 if (desktop == NULL) 325 return; 326 327 for (int i = 0; i < group->CountItems(); i++) { 328 SATWindow* listWindow = group->WindowAt(i); 329 if (listWindow != satWindow) 330 desktop->SetWindowWorkspaces(listWindow->GetWindow(), workspaces); 331 } 332 } 333 334 335 void 336 StackAndTile::WindowHidden(Window* window, bool fromMinimize) 337 { 338 SATWindow* satWindow = GetSATWindow(window); 339 if (satWindow == NULL) 340 return; 341 SATGroup* group = satWindow->GetGroup(); 342 if (group == NULL) 343 return; 344 if (fromMinimize == false && group->CountItems() > 1) 345 group->RemoveWindow(satWindow, false); 346 } 347 348 349 void 350 StackAndTile::WindowMinimized(Window* window, bool minimize) 351 { 352 SATWindow* satWindow = GetSATWindow(window); 353 if (satWindow == NULL) 354 return; 355 SATGroup* group = satWindow->GetGroup(); 356 if (group == NULL) 357 return; 358 Desktop* desktop = satWindow->GetWindow()->Desktop(); 359 if (desktop == NULL) 360 return; 361 362 for (int i = 0; i < group->CountItems(); i++) { 363 SATWindow* listWindow = group->WindowAt(i); 364 if (listWindow != satWindow) 365 listWindow->GetWindow()->ServerWindow()->NotifyMinimize(minimize); 366 } 367 } 368 369 370 void 371 StackAndTile::WindowTabLocationChanged(Window* window, float location, 372 bool isShifting) 373 { 374 375 } 376 377 378 void 379 StackAndTile::SizeLimitsChanged(Window* window, int32 minWidth, int32 maxWidth, 380 int32 minHeight, int32 maxHeight) 381 { 382 SATWindow* satWindow = GetSATWindow(window); 383 if (!satWindow) 384 return; 385 satWindow->SetOriginalSizeLimits(minWidth, maxWidth, minHeight, maxHeight); 386 387 // trigger a relayout 388 WindowMoved(window); 389 } 390 391 392 void 393 StackAndTile::WindowLookChanged(Window* window, window_look look) 394 { 395 SATWindow* satWindow = GetSATWindow(window); 396 if (!satWindow) 397 return; 398 satWindow->WindowLookChanged(look); 399 } 400 401 402 void 403 StackAndTile::WindowFeelChanged(Window* window, window_feel feel) 404 { 405 // check if it is still a compatible feel 406 if (feel == B_NORMAL_WINDOW_FEEL) 407 return; 408 SATWindow* satWindow = GetSATWindow(window); 409 if (!satWindow) 410 return; 411 SATGroup* group = satWindow->GetGroup(); 412 if (!group) 413 return; 414 if (group->CountItems() > 1) 415 group->RemoveWindow(satWindow, false); 416 } 417 418 419 bool 420 StackAndTile::SetDecoratorSettings(Window* window, const BMessage& settings) 421 { 422 SATWindow* satWindow = GetSATWindow(window); 423 if (!satWindow) 424 return false; 425 426 return satWindow->SetSettings(settings); 427 } 428 429 430 void 431 StackAndTile::GetDecoratorSettings(Window* window, BMessage& settings) 432 { 433 SATWindow* satWindow = GetSATWindow(window); 434 if (!satWindow) 435 return; 436 437 satWindow->GetSettings(settings); 438 } 439 440 441 SATWindow* 442 StackAndTile::GetSATWindow(Window* window) 443 { 444 if (window == NULL) 445 return NULL; 446 447 SATWindowMap::const_iterator it = fSATWindowMap.find( 448 window); 449 if (it != fSATWindowMap.end()) 450 return it->second; 451 452 // TODO fix race condition with WindowAdded this method is called before 453 // WindowAdded and a SATWindow is created twice! 454 return NULL; 455 456 // If we don't know this window, memory allocation might has been failed 457 // previously. Try to add the window now. 458 SATWindow* satWindow = new (std::nothrow)SATWindow(this, window); 459 if (satWindow) 460 fSATWindowMap[window] = satWindow; 461 462 return satWindow; 463 } 464 465 466 SATWindow* 467 StackAndTile::FindSATWindow(uint64 id) 468 { 469 for (SATWindowMap::const_iterator it = fSATWindowMap.begin(); 470 it != fSATWindowMap.end(); it++) { 471 SATWindow* window = it->second; 472 if (window->Id() == id) 473 return window; 474 } 475 return NULL; 476 } 477 478 479 void 480 StackAndTile::_StartSAT() 481 { 482 STRACE_SAT("StackAndTile::_StartSAT()\n"); 483 if (!fCurrentSATWindow) 484 return; 485 486 // Remove window from the group. 487 SATGroup* group = fCurrentSATWindow->GetGroup(); 488 if (!group) 489 return; 490 491 group->RemoveWindow(fCurrentSATWindow, false); 492 // Bring window to the front. (in focus follow mouse this is not 493 // automatically the case) 494 _ActivateWindow(fCurrentSATWindow); 495 496 fCurrentSATWindow->FindSnappingCandidates(); 497 } 498 499 500 void 501 StackAndTile::_StopSAT() 502 { 503 STRACE_SAT("StackAndTile::_StopSAT()\n"); 504 if (!fCurrentSATWindow) 505 return; 506 if (fCurrentSATWindow->JoinCandidates()) 507 _ActivateWindow(fCurrentSATWindow); 508 } 509 510 511 void 512 StackAndTile::_ActivateWindow(SATWindow* satWindow) 513 { 514 SATGroup* group = satWindow->GetGroup(); 515 if (!group) 516 return; 517 Desktop* desktop = satWindow->GetWindow()->Desktop(); 518 if (!desktop) 519 return; 520 WindowArea* area = satWindow->GetWindowArea(); 521 if (!area) 522 return; 523 area->MoveToTopLayer(satWindow); 524 525 const WindowAreaList& areas = group->GetAreaList() ; 526 for (int32 i = 0; i < areas.CountItems(); i++) { 527 WindowArea* currentArea = areas.ItemAt(i); 528 if (currentArea == area) 529 continue; 530 desktop->ActivateWindow(currentArea->TopWindow()->GetWindow()); 531 } 532 533 desktop->ActivateWindow(satWindow->GetWindow()); 534 } 535 536 537 bool 538 StackAndTile::_HandleMessage(BPrivate::LinkReceiver& link, 539 BPrivate::LinkSender& reply) 540 { 541 int32 what; 542 link.Read<int32>(&what); 543 544 switch (what) { 545 case BPrivate::kSaveAllGroups: 546 { 547 BMessage allGroupsArchive; 548 GroupIterator groups(this, fDesktop); 549 while (true) { 550 SATGroup* group = groups.NextGroup(); 551 if (group == NULL) 552 break; 553 if (group->CountItems() <= 1) 554 continue; 555 BMessage groupArchive; 556 if (group->ArchiveGroup(groupArchive) != B_OK) 557 continue; 558 allGroupsArchive.AddMessage("group", &groupArchive); 559 } 560 int32 size = allGroupsArchive.FlattenedSize(); 561 char buffer[size]; 562 if (allGroupsArchive.Flatten(buffer, size) == B_OK) { 563 reply.StartMessage(B_OK); 564 reply.Attach<int32>(size); 565 reply.Attach(buffer, size); 566 } else 567 reply.StartMessage(B_ERROR); 568 reply.Flush(); 569 break; 570 } 571 572 case BPrivate::kRestoreGroup: 573 { 574 int32 size; 575 if (link.Read<int32>(&size) == B_OK) { 576 char buffer[size]; 577 BMessage group; 578 if (link.Read(buffer, size) == B_OK 579 && group.Unflatten(buffer) == B_OK) { 580 status_t status = SATGroup::RestoreGroup(group, this); 581 reply.StartMessage(status); 582 reply.Flush(); 583 } 584 } 585 break; 586 } 587 588 default: 589 return false; 590 } 591 592 return true; 593 } 594 595 596 GroupIterator::GroupIterator(StackAndTile* sat, Desktop* desktop) 597 : 598 fStackAndTile(sat), 599 fDesktop(desktop), 600 fCurrentGroup(NULL) 601 { 602 RewindToFront(); 603 } 604 605 606 void 607 GroupIterator::RewindToFront() 608 { 609 fCurrentWindow = fDesktop->CurrentWindows().LastWindow(); 610 } 611 612 613 SATGroup* 614 GroupIterator::NextGroup() 615 { 616 SATGroup* group = NULL; 617 do { 618 Window* window = fCurrentWindow; 619 if (window == NULL) { 620 group = NULL; 621 break; 622 } 623 fCurrentWindow = fCurrentWindow->PreviousWindow( 624 fCurrentWindow->CurrentWorkspace()); 625 if (window->IsHidden() 626 || strcmp(window->Title(), "Deskbar") == 0 627 || strcmp(window->Title(), "Desktop") == 0) 628 continue; 629 630 SATWindow* satWindow = fStackAndTile->GetSATWindow(window); 631 group = satWindow->GetGroup(); 632 } while (group == NULL || fCurrentGroup == group); 633 634 fCurrentGroup = group; 635 return fCurrentGroup; 636 } 637 638 639 WindowIterator::WindowIterator(SATGroup* group, bool reverseLayerOrder) 640 : 641 fGroup(group), 642 fReverseLayerOrder(reverseLayerOrder) 643 { 644 if (fReverseLayerOrder) 645 _ReverseRewind(); 646 else 647 Rewind(); 648 } 649 650 651 void 652 WindowIterator::Rewind() 653 { 654 fAreaIndex = 0; 655 fWindowIndex = 0; 656 fCurrentArea = fGroup->GetAreaList().ItemAt(fAreaIndex); 657 } 658 659 660 SATWindow* 661 WindowIterator::NextWindow() 662 { 663 if (fReverseLayerOrder) 664 return _ReverseNextWindow(); 665 666 if (fWindowIndex == fCurrentArea->LayerOrder().CountItems()) { 667 fAreaIndex++; 668 fWindowIndex = 0; 669 fCurrentArea = fGroup->GetAreaList().ItemAt(fAreaIndex); 670 if (!fCurrentArea) 671 return NULL; 672 } 673 SATWindow* window = fCurrentArea->LayerOrder().ItemAt(fWindowIndex); 674 fWindowIndex++; 675 return window; 676 } 677 678 679 SATWindow* 680 WindowIterator::_ReverseNextWindow() 681 { 682 if (fWindowIndex < 0) { 683 fAreaIndex++; 684 fCurrentArea = fGroup->GetAreaList().ItemAt(fAreaIndex); 685 if (!fCurrentArea) 686 return NULL; 687 fWindowIndex = fCurrentArea->LayerOrder().CountItems() - 1; 688 } 689 SATWindow* window = fCurrentArea->LayerOrder().ItemAt(fWindowIndex); 690 fWindowIndex--; 691 return window; 692 } 693 694 695 void 696 WindowIterator::_ReverseRewind() 697 { 698 Rewind(); 699 if (fCurrentArea) 700 fWindowIndex = fCurrentArea->LayerOrder().CountItems() - 1; 701 } 702 703 704 SATSnappingBehaviour::~SATSnappingBehaviour() 705 { 706 707 } 708