1 /* 2 * Copyright 2001-2020, Haiku, Inc. 3 * Distributed under the terms of the MIT license. 4 * 5 * Authors: 6 * DarkWyrm, bpmagic@columbus.rr.com 7 * Adi Oanca, adioanca@gmail.com 8 * Stephan Aßmus, superstippi@gmx.de 9 * Axel Dörfler, axeld@pinc-software.de 10 * Brecht Machiels, brecht@mos6581.org 11 * Clemens Zeidler, haiku@clemens-zeidler.de 12 * Tri-Edge AI 13 * Jacob Secunda, secundja@gmail.com 14 */ 15 16 17 #include "Window.h" 18 19 #include <new> 20 #include <stdio.h> 21 22 #include <Debug.h> 23 24 #include <DirectWindow.h> 25 #include <PortLink.h> 26 #include <View.h> 27 #include <ViewPrivate.h> 28 #include <WindowPrivate.h> 29 30 #include "ClickTarget.h" 31 #include "Decorator.h" 32 #include "DecorManager.h" 33 #include "Desktop.h" 34 #include "DrawingEngine.h" 35 #include "HWInterface.h" 36 #include "MessagePrivate.h" 37 #include "PortLink.h" 38 #include "ServerApp.h" 39 #include "ServerWindow.h" 40 #include "WindowBehaviour.h" 41 #include "Workspace.h" 42 #include "WorkspacesView.h" 43 44 45 // Toggle debug output 46 //#define DEBUG_WINDOW 47 48 #ifdef DEBUG_WINDOW 49 # define STRACE(x) printf x 50 #else 51 # define STRACE(x) ; 52 #endif 53 54 // IMPORTANT: nested LockSingleWindow()s are not supported (by MultiLocker) 55 56 using std::nothrow; 57 58 // if the background clearing is delayed until 59 // the client draws the view, we have less flickering 60 // when contents have to be redrawn because of resizing 61 // a window or because the client invalidates parts. 62 // when redrawing something that has been exposed from underneath 63 // other windows, the other window will be seen longer at 64 // its previous position though if the exposed parts are not 65 // cleared right away. maybe there ought to be a flag in 66 // the update session, which tells us the cause of the update 67 68 69 //static rgb_color sPendingColor = (rgb_color){ 255, 255, 0, 255 }; 70 //static rgb_color sCurrentColor = (rgb_color){ 255, 0, 255, 255 }; 71 72 73 Window::Window(const BRect& frame, const char *name, 74 window_look look, window_feel feel, uint32 flags, uint32 workspaces, 75 ::ServerWindow* window, DrawingEngine* drawingEngine) 76 : 77 fTitle(name), 78 fFrame(frame), 79 fScreen(NULL), 80 81 fVisibleRegion(), 82 fVisibleContentRegion(), 83 fDirtyRegion(), 84 fDirtyCause(0), 85 86 fContentRegion(), 87 fEffectiveDrawingRegion(), 88 89 fVisibleContentRegionValid(false), 90 fContentRegionValid(false), 91 fEffectiveDrawingRegionValid(false), 92 93 fRegionPool(), 94 95 fWindow(window), 96 fDrawingEngine(drawingEngine), 97 fDesktop(window->Desktop()), 98 99 fCurrentUpdateSession(&fUpdateSessions[0]), 100 fPendingUpdateSession(&fUpdateSessions[1]), 101 fUpdateRequested(false), 102 fInUpdate(false), 103 fUpdatesEnabled(false), 104 105 // Windows start hidden 106 fHidden(true), 107 // Hidden is 1 or more 108 fShowLevel(1), 109 fMinimized(false), 110 fIsFocus(false), 111 112 fLook(look), 113 fFeel(feel), 114 fWorkspaces(workspaces), 115 fCurrentWorkspace(-1), 116 117 fMinWidth(1), 118 fMaxWidth(32768), 119 fMinHeight(1), 120 fMaxHeight(32768), 121 122 fWorkspacesViewCount(0) 123 { 124 _InitWindowStack(); 125 126 // make sure our arguments are valid 127 if (!IsValidLook(fLook)) 128 fLook = B_TITLED_WINDOW_LOOK; 129 if (!IsValidFeel(fFeel)) 130 fFeel = B_NORMAL_WINDOW_FEEL; 131 132 SetFlags(flags, NULL); 133 134 if (fLook != B_NO_BORDER_WINDOW_LOOK && fCurrentStack.IsSet()) { 135 // allocates a decorator 136 ::Decorator* decorator = Decorator(); 137 if (decorator != NULL) { 138 decorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth, 139 &fMaxHeight); 140 } 141 } 142 if (fFeel != kOffscreenWindowFeel) 143 fWindowBehaviour.SetTo(gDecorManager.AllocateWindowBehaviour(this)); 144 145 // do we need to change our size to let the decorator fit? 146 // _ResizeBy() will adapt the frame for validity before resizing 147 if (feel == kDesktopWindowFeel) { 148 // the desktop window spans over the whole screen 149 // TODO: this functionality should be moved somewhere else 150 // (so that it is always used when the workspace is changed) 151 uint16 width, height; 152 uint32 colorSpace; 153 float frequency; 154 if (Screen() != NULL) { 155 Screen()->GetMode(width, height, colorSpace, frequency); 156 // TODO: MOVE THIS AWAY!!! ResizeBy contains calls to virtual methods! 157 // Also, there is no TopView()! 158 fFrame.OffsetTo(B_ORIGIN); 159 // ResizeBy(width - frame.Width(), height - frame.Height(), NULL); 160 } 161 } 162 163 STRACE(("Window %p, %s:\n", this, Name())); 164 STRACE(("\tFrame: (%.1f, %.1f, %.1f, %.1f)\n", fFrame.left, fFrame.top, 165 fFrame.right, fFrame.bottom)); 166 STRACE(("\tWindow %s\n", window ? window->Title() : "NULL")); 167 } 168 169 170 Window::~Window() 171 { 172 if (fTopView.IsSet()) { 173 fTopView->DetachedFromWindow(); 174 } 175 176 DetachFromWindowStack(false); 177 178 gDecorManager.CleanupForWindow(this); 179 } 180 181 182 status_t 183 Window::InitCheck() const 184 { 185 if (GetDrawingEngine() == NULL 186 || (fFeel != kOffscreenWindowFeel && !fWindowBehaviour.IsSet())) 187 return B_NO_MEMORY; 188 // TODO: anything else? 189 return B_OK; 190 } 191 192 193 void 194 Window::SetClipping(BRegion* stillAvailableOnScreen) 195 { 196 // this function is only called from the Desktop thread 197 198 // start from full region (as if the window was fully visible) 199 GetFullRegion(&fVisibleRegion); 200 // clip to region still available on screen 201 fVisibleRegion.IntersectWith(stillAvailableOnScreen); 202 203 fVisibleContentRegionValid = false; 204 fEffectiveDrawingRegionValid = false; 205 } 206 207 208 void 209 Window::GetFullRegion(BRegion* region) 210 { 211 // TODO: if someone needs to call this from 212 // the outside, the clipping needs to be readlocked! 213 214 // start from the decorator border, extend to use the frame 215 GetBorderRegion(region); 216 region->Include(fFrame); 217 } 218 219 220 void 221 Window::GetBorderRegion(BRegion* region) 222 { 223 // TODO: if someone needs to call this from 224 // the outside, the clipping needs to be readlocked! 225 226 ::Decorator* decorator = Decorator(); 227 if (decorator) 228 *region = decorator->GetFootprint(); 229 else 230 region->MakeEmpty(); 231 } 232 233 234 void 235 Window::GetContentRegion(BRegion* region) 236 { 237 // TODO: if someone needs to call this from 238 // the outside, the clipping needs to be readlocked! 239 240 if (!fContentRegionValid) { 241 _UpdateContentRegion(); 242 } 243 244 *region = fContentRegion; 245 } 246 247 248 BRegion& 249 Window::VisibleContentRegion() 250 { 251 // TODO: if someone needs to call this from 252 // the outside, the clipping needs to be readlocked! 253 254 // regions expected to be locked 255 if (!fVisibleContentRegionValid) { 256 GetContentRegion(&fVisibleContentRegion); 257 fVisibleContentRegion.IntersectWith(&fVisibleRegion); 258 } 259 return fVisibleContentRegion; 260 } 261 262 263 // #pragma mark - 264 265 266 void 267 Window::_PropagatePosition() 268 { 269 if ((fFlags & B_SAME_POSITION_IN_ALL_WORKSPACES) == 0) 270 return; 271 272 for (int32 i = 0; i < kListCount; i++) { 273 Anchor(i).position = fFrame.LeftTop(); 274 } 275 } 276 277 278 void 279 Window::MoveBy(int32 x, int32 y, bool moveStack) 280 { 281 // this function is only called from the desktop thread 282 283 if (x == 0 && y == 0) 284 return; 285 286 fFrame.OffsetBy(x, y); 287 _PropagatePosition(); 288 289 // take along the dirty region which is not 290 // processed yet 291 fDirtyRegion.OffsetBy(x, y); 292 293 if (fContentRegionValid) 294 fContentRegion.OffsetBy(x, y); 295 296 if (fCurrentUpdateSession->IsUsed()) 297 fCurrentUpdateSession->MoveBy(x, y); 298 if (fPendingUpdateSession->IsUsed()) 299 fPendingUpdateSession->MoveBy(x, y); 300 301 fEffectiveDrawingRegionValid = false; 302 303 if (fTopView.IsSet()) { 304 fTopView->MoveBy(x, y, NULL); 305 fTopView->UpdateOverlay(); 306 } 307 308 ::Decorator* decorator = Decorator(); 309 if (moveStack && decorator) 310 decorator->MoveBy(x, y); 311 312 WindowStack* stack = GetWindowStack(); 313 if (moveStack && stack) { 314 for (int32 i = 0; i < stack->CountWindows(); i++) { 315 Window* window = stack->WindowList().ItemAt(i); 316 if (window == this) 317 continue; 318 window->MoveBy(x, y, false); 319 } 320 } 321 322 // the desktop will take care of dirty regions 323 324 // dispatch a message to the client informing about the changed size 325 BMessage msg(B_WINDOW_MOVED); 326 msg.AddInt64("when", system_time()); 327 msg.AddPoint("where", fFrame.LeftTop()); 328 fWindow->SendMessageToClient(&msg); 329 } 330 331 332 void 333 Window::ResizeBy(int32 x, int32 y, BRegion* dirtyRegion, bool resizeStack) 334 { 335 // this function is only called from the desktop thread 336 337 int32 wantWidth = fFrame.IntegerWidth() + x; 338 int32 wantHeight = fFrame.IntegerHeight() + y; 339 340 // enforce size limits 341 WindowStack* stack = GetWindowStack(); 342 if (resizeStack && stack) { 343 for (int32 i = 0; i < stack->CountWindows(); i++) { 344 Window* window = stack->WindowList().ItemAt(i); 345 346 if (wantWidth < window->fMinWidth) 347 wantWidth = window->fMinWidth; 348 if (wantWidth > window->fMaxWidth) 349 wantWidth = window->fMaxWidth; 350 351 if (wantHeight < window->fMinHeight) 352 wantHeight = window->fMinHeight; 353 if (wantHeight > window->fMaxHeight) 354 wantHeight = window->fMaxHeight; 355 } 356 } 357 358 x = wantWidth - fFrame.IntegerWidth(); 359 y = wantHeight - fFrame.IntegerHeight(); 360 361 if (x == 0 && y == 0) 362 return; 363 364 fFrame.right += x; 365 fFrame.bottom += y; 366 367 fContentRegionValid = false; 368 fEffectiveDrawingRegionValid = false; 369 370 if (fTopView.IsSet()) { 371 fTopView->ResizeBy(x, y, dirtyRegion); 372 fTopView->UpdateOverlay(); 373 } 374 375 ::Decorator* decorator = Decorator(); 376 if (decorator && resizeStack) 377 decorator->ResizeBy(x, y, dirtyRegion); 378 379 if (resizeStack && stack) { 380 for (int32 i = 0; i < stack->CountWindows(); i++) { 381 Window* window = stack->WindowList().ItemAt(i); 382 if (window == this) 383 continue; 384 window->ResizeBy(x, y, dirtyRegion, false); 385 } 386 } 387 388 // send a message to the client informing about the changed size 389 BRect frame(Frame()); 390 BMessage msg(B_WINDOW_RESIZED); 391 msg.AddInt64("when", system_time()); 392 msg.AddInt32("width", frame.IntegerWidth()); 393 msg.AddInt32("height", frame.IntegerHeight()); 394 fWindow->SendMessageToClient(&msg); 395 } 396 397 398 void 399 Window::SetOutlinesDelta(BPoint delta, BRegion* dirtyRegion) 400 { 401 float wantWidth = fFrame.IntegerWidth() + delta.x; 402 float wantHeight = fFrame.IntegerHeight() + delta.y; 403 404 // enforce size limits 405 WindowStack* stack = GetWindowStack(); 406 if (stack != NULL) { 407 for (int32 i = 0; i < stack->CountWindows(); i++) { 408 Window* window = stack->WindowList().ItemAt(i); 409 410 if (wantWidth < window->fMinWidth) 411 wantWidth = window->fMinWidth; 412 if (wantWidth > window->fMaxWidth) 413 wantWidth = window->fMaxWidth; 414 415 if (wantHeight < window->fMinHeight) 416 wantHeight = window->fMinHeight; 417 if (wantHeight > window->fMaxHeight) 418 wantHeight = window->fMaxHeight; 419 } 420 421 delta.x = wantWidth - fFrame.IntegerWidth(); 422 delta.y = wantHeight - fFrame.IntegerHeight(); 423 } 424 425 ::Decorator* decorator = Decorator(); 426 427 if (decorator != NULL) 428 decorator->SetOutlinesDelta(delta, dirtyRegion); 429 430 _UpdateContentRegion(); 431 } 432 433 434 void 435 Window::ScrollViewBy(View* view, int32 dx, int32 dy) 436 { 437 // this is executed in ServerWindow with the Readlock 438 // held 439 440 if (!view || view == fTopView.Get() || (dx == 0 && dy == 0)) 441 return; 442 443 BRegion* dirty = fRegionPool.GetRegion(); 444 if (!dirty) 445 return; 446 447 view->ScrollBy(dx, dy, dirty); 448 449 //fDrawingEngine->FillRegion(*dirty, (rgb_color){ 255, 0, 255, 255 }); 450 //snooze(20000); 451 452 if (!IsOffscreenWindow() && IsVisible() && view->IsVisible()) { 453 dirty->IntersectWith(&VisibleContentRegion()); 454 _TriggerContentRedraw(*dirty); 455 } 456 457 fRegionPool.Recycle(dirty); 458 } 459 460 461 //! Takes care of invalidating parts that could not be copied 462 void 463 Window::CopyContents(BRegion* region, int32 xOffset, int32 yOffset) 464 { 465 // executed in ServerWindow thread with the read lock held 466 if (!IsVisible()) 467 return; 468 469 BRegion* newDirty = fRegionPool.GetRegion(*region); 470 471 // clip the region to the visible contents at the 472 // source and destination location (note that VisibleContentRegion() 473 // is used once to make sure it is valid, then fVisibleContentRegion 474 // is used directly) 475 region->IntersectWith(&VisibleContentRegion()); 476 if (region->CountRects() > 0) { 477 // Constrain to content region at destination 478 region->OffsetBy(xOffset, yOffset); 479 region->IntersectWith(&fVisibleContentRegion); 480 if (region->CountRects() > 0) { 481 // if the region still contains any rects 482 // offset to source location again 483 region->OffsetBy(-xOffset, -yOffset); 484 485 BRegion* allDirtyRegions = fRegionPool.GetRegion(fDirtyRegion); 486 if (allDirtyRegions != NULL) { 487 if (fPendingUpdateSession->IsUsed()) { 488 allDirtyRegions->Include( 489 &fPendingUpdateSession->DirtyRegion()); 490 } 491 if (fCurrentUpdateSession->IsUsed()) { 492 allDirtyRegions->Include( 493 &fCurrentUpdateSession->DirtyRegion()); 494 } 495 // Get just the part of the dirty regions which is semantically 496 // copied along 497 allDirtyRegions->IntersectWith(region); 498 } 499 500 BRegion* copyRegion = fRegionPool.GetRegion(*region); 501 if (copyRegion != NULL) { 502 // never copy what's already dirty 503 if (allDirtyRegions != NULL) 504 copyRegion->Exclude(allDirtyRegions); 505 506 if (fDrawingEngine->LockParallelAccess()) { 507 fDrawingEngine->CopyRegion(copyRegion, xOffset, yOffset); 508 fDrawingEngine->UnlockParallelAccess(); 509 510 // Prevent those parts from being added to the dirty region... 511 newDirty->Exclude(copyRegion); 512 513 // The parts that could be copied are not dirty (at the 514 // target location!) 515 copyRegion->OffsetBy(xOffset, yOffset); 516 // ... and even exclude them from the pending dirty region! 517 if (fPendingUpdateSession->IsUsed()) 518 fPendingUpdateSession->DirtyRegion().Exclude(copyRegion); 519 } 520 521 fRegionPool.Recycle(copyRegion); 522 } else { 523 // Fallback, should never be here. 524 if (fDrawingEngine->LockParallelAccess()) { 525 fDrawingEngine->CopyRegion(region, xOffset, yOffset); 526 fDrawingEngine->UnlockParallelAccess(); 527 } 528 } 529 530 if (allDirtyRegions != NULL) 531 fRegionPool.Recycle(allDirtyRegions); 532 } 533 } 534 // what is left visible from the original region 535 // at the destination after the region which could be 536 // copied has been excluded, is considered dirty 537 // NOTE: it may look like dirty regions are not moved 538 // if no region could be copied, but that's alright, 539 // since these parts will now be in newDirty anyways 540 // (with the right offset) 541 newDirty->OffsetBy(xOffset, yOffset); 542 newDirty->IntersectWith(&fVisibleContentRegion); 543 if (newDirty->CountRects() > 0) 544 ProcessDirtyRegion(*newDirty); 545 546 fRegionPool.Recycle(newDirty); 547 } 548 549 550 // #pragma mark - 551 552 553 void 554 Window::SetTopView(View* topView) 555 { 556 if (fTopView.IsSet()) { 557 fTopView->DetachedFromWindow(); 558 } 559 560 fTopView.SetTo(topView); 561 562 if (fTopView.IsSet()) { 563 // the top view is special, it has a coordinate system 564 // as if it was attached directly to the desktop, therefor, 565 // the coordinate conversion through the view tree works 566 // as expected, since the top view has no "parent" but has 567 // fFrame as if it had 568 569 // make sure the location of the top view on screen matches ours 570 fTopView->MoveBy((int32)(fFrame.left - fTopView->Frame().left), 571 (int32)(fFrame.top - fTopView->Frame().top), NULL); 572 573 // make sure the size of the top view matches ours 574 fTopView->ResizeBy((int32)(fFrame.Width() - fTopView->Frame().Width()), 575 (int32)(fFrame.Height() - fTopView->Frame().Height()), NULL); 576 577 fTopView->AttachedToWindow(this); 578 } 579 } 580 581 582 View* 583 Window::ViewAt(const BPoint& where) 584 { 585 return fTopView->ViewAt(where); 586 } 587 588 589 window_anchor& 590 Window::Anchor(int32 index) 591 { 592 return fAnchor[index]; 593 } 594 595 596 Window* 597 Window::NextWindow(int32 index) const 598 { 599 return fAnchor[index].next; 600 } 601 602 603 Window* 604 Window::PreviousWindow(int32 index) const 605 { 606 return fAnchor[index].previous; 607 } 608 609 610 ::Decorator* 611 Window::Decorator() const 612 { 613 if (!fCurrentStack.IsSet()) 614 return NULL; 615 return fCurrentStack->Decorator(); 616 } 617 618 619 bool 620 Window::ReloadDecor() 621 { 622 ::Decorator* decorator = NULL; 623 WindowBehaviour* windowBehaviour = NULL; 624 WindowStack* stack = GetWindowStack(); 625 if (stack == NULL) 626 return false; 627 628 // only reload the window at the first position 629 if (stack->WindowAt(0) != this) 630 return true; 631 632 if (fLook != B_NO_BORDER_WINDOW_LOOK) { 633 // we need a new decorator 634 decorator = gDecorManager.AllocateDecorator(this); 635 if (decorator == NULL) 636 return false; 637 638 // add all tabs to the decorator 639 for (int32 i = 1; i < stack->CountWindows(); i++) { 640 Window* window = stack->WindowAt(i); 641 BRegion dirty; 642 DesktopSettings settings(fDesktop); 643 if (decorator->AddTab(settings, window->Title(), window->Look(), 644 window->Flags(), -1, &dirty) == NULL) { 645 delete decorator; 646 return false; 647 } 648 } 649 } else 650 return true; 651 652 windowBehaviour = gDecorManager.AllocateWindowBehaviour(this); 653 if (windowBehaviour == NULL) { 654 delete decorator; 655 return false; 656 } 657 658 stack->SetDecorator(decorator); 659 660 fWindowBehaviour.SetTo(windowBehaviour); 661 662 // set the correct focus and top layer tab 663 for (int32 i = 0; i < stack->CountWindows(); i++) { 664 Window* window = stack->WindowAt(i); 665 if (window->IsFocus()) 666 decorator->SetFocus(i, true); 667 if (window == stack->TopLayerWindow()) 668 decorator->SetTopTab(i); 669 } 670 671 return true; 672 } 673 674 675 void 676 Window::SetScreen(const ::Screen* screen) 677 { 678 // TODO this assert fails in Desktop::ShowWindow 679 //ASSERT_MULTI_WRITE_LOCKED(fDesktop->ScreenLocker()); 680 fScreen = screen; 681 } 682 683 684 const ::Screen* 685 Window::Screen() const 686 { 687 // TODO this assert also fails 688 //ASSERT_MULTI_READ_LOCKED(fDesktop->ScreenLocker()); 689 return fScreen; 690 } 691 692 693 // #pragma mark - 694 695 696 void 697 Window::GetEffectiveDrawingRegion(View* view, BRegion& region) 698 { 699 if (!fEffectiveDrawingRegionValid) { 700 fEffectiveDrawingRegion = VisibleContentRegion(); 701 if (fUpdateRequested && !fInUpdate) { 702 // We requested an update, but the client has not started it yet, 703 // so it is only allowed to draw outside the pending update sessions 704 // region 705 fEffectiveDrawingRegion.Exclude( 706 &fPendingUpdateSession->DirtyRegion()); 707 } else if (fInUpdate) { 708 // enforce the dirty region of the update session 709 fEffectiveDrawingRegion.IntersectWith( 710 &fCurrentUpdateSession->DirtyRegion()); 711 } else { 712 // not in update, the view can draw everywhere 713 //printf("Window(%s)::GetEffectiveDrawingRegion(for %s) - outside update\n", Title(), view->Name()); 714 } 715 716 fEffectiveDrawingRegionValid = true; 717 } 718 719 // TODO: this is a region that needs to be cached later in the server 720 // when the current view in ServerWindow is set, and we are currently 721 // in an update (fInUpdate), than we can set this region and remember 722 // it for the comming drawing commands until the current view changes 723 // again or fEffectiveDrawingRegionValid is suddenly false. 724 region = fEffectiveDrawingRegion; 725 if (!fContentRegionValid) 726 _UpdateContentRegion(); 727 728 region.IntersectWith(&view->ScreenAndUserClipping(&fContentRegion)); 729 } 730 731 732 bool 733 Window::DrawingRegionChanged(View* view) const 734 { 735 return !fEffectiveDrawingRegionValid || !view->IsScreenClippingValid(); 736 } 737 738 739 void 740 Window::ProcessDirtyRegion(BRegion& region) 741 { 742 // if this is executed in the desktop thread, 743 // it means that the window thread currently 744 // blocks to get the read lock, if it is 745 // executed from the window thread, it should 746 // have the read lock and the desktop thread 747 // is blocking to get the write lock. IAW, this 748 // is only executed in one thread. 749 if (fDirtyRegion.CountRects() == 0) { 750 // the window needs to be informed 751 // when the dirty region was empty. 752 // NOTE: when the window thread has processed 753 // the dirty region in MessageReceived(), 754 // it will make the region empty again, 755 // when it is empty here, we need to send 756 // the message to initiate the next update round. 757 // Until the message is processed in the window 758 // thread, the desktop thread can add parts to 759 // the region as it likes. 760 ServerWindow()->RequestRedraw(); 761 } 762 763 fDirtyRegion.Include(®ion); 764 fDirtyCause |= UPDATE_EXPOSE; 765 } 766 767 768 void 769 Window::RedrawDirtyRegion() 770 { 771 if (TopLayerStackWindow() != this) { 772 fDirtyRegion.MakeEmpty(); 773 fDirtyCause = 0; 774 return; 775 } 776 777 // executed from ServerWindow with the read lock held 778 if (IsVisible()) { 779 _DrawBorder(); 780 781 BRegion* dirtyContentRegion = 782 fRegionPool.GetRegion(VisibleContentRegion()); 783 dirtyContentRegion->IntersectWith(&fDirtyRegion); 784 785 _TriggerContentRedraw(*dirtyContentRegion); 786 787 fRegionPool.Recycle(dirtyContentRegion); 788 } 789 790 // reset the dirty region, since 791 // we're fully clean. If the desktop 792 // thread wanted to mark something 793 // dirty in the mean time, it was 794 // blocking on the global region lock to 795 // get write access, since we're holding 796 // the read lock for the whole time. 797 fDirtyRegion.MakeEmpty(); 798 fDirtyCause = 0; 799 } 800 801 802 void 803 Window::MarkDirty(BRegion& regionOnScreen) 804 { 805 // for marking any part of the desktop dirty 806 // this will get write access to the global 807 // region lock, and result in ProcessDirtyRegion() 808 // to be called for any windows affected 809 if (fDesktop) 810 fDesktop->MarkDirty(regionOnScreen); 811 } 812 813 814 void 815 Window::MarkContentDirty(BRegion& regionOnScreen) 816 { 817 // for triggering AS_REDRAW 818 // since this won't affect other windows, read locking 819 // is sufficient. If there was no dirty region before, 820 // an update message is triggered 821 if (fHidden || IsOffscreenWindow()) 822 return; 823 824 regionOnScreen.IntersectWith(&VisibleContentRegion()); 825 fDirtyCause |= UPDATE_REQUEST; 826 _TriggerContentRedraw(regionOnScreen); 827 } 828 829 830 void 831 Window::MarkContentDirtyAsync(BRegion& regionOnScreen) 832 { 833 // NOTE: see comments in ProcessDirtyRegion() 834 if (fHidden || IsOffscreenWindow()) 835 return; 836 837 regionOnScreen.IntersectWith(&VisibleContentRegion()); 838 839 if (fDirtyRegion.CountRects() == 0) { 840 ServerWindow()->RequestRedraw(); 841 } 842 843 fDirtyRegion.Include(®ionOnScreen); 844 fDirtyCause |= UPDATE_REQUEST; 845 } 846 847 848 void 849 Window::InvalidateView(View* view, BRegion& viewRegion) 850 { 851 if (view && IsVisible() && view->IsVisible()) { 852 if (!fContentRegionValid) 853 _UpdateContentRegion(); 854 855 view->LocalToScreenTransform().Apply(&viewRegion); 856 viewRegion.IntersectWith(&VisibleContentRegion()); 857 if (viewRegion.CountRects() > 0) { 858 viewRegion.IntersectWith( 859 &view->ScreenAndUserClipping(&fContentRegion)); 860 861 //fDrawingEngine->FillRegion(viewRegion, rgb_color{ 0, 255, 0, 255 }); 862 //snooze(10000); 863 fDirtyCause |= UPDATE_REQUEST; 864 _TriggerContentRedraw(viewRegion); 865 } 866 } 867 } 868 869 // DisableUpdateRequests 870 void 871 Window::DisableUpdateRequests() 872 { 873 fUpdatesEnabled = false; 874 } 875 876 877 // EnableUpdateRequests 878 void 879 Window::EnableUpdateRequests() 880 { 881 fUpdatesEnabled = true; 882 if (!fUpdateRequested && fPendingUpdateSession->IsUsed()) 883 _SendUpdateMessage(); 884 } 885 886 // #pragma mark - 887 888 889 /*! \brief Handles a mouse-down message for the window. 890 891 \param message The message. 892 \param where The point where the mouse click happened. 893 \param lastClickTarget The target of the previous click. 894 \param clickCount The number of subsequent, no longer than double-click 895 interval separated clicks that have happened so far. This number doesn't 896 necessarily match the value in the message. It has already been 897 pre-processed in order to avoid erroneous multi-clicks (e.g. when a 898 different button has been used or a different window was targeted). This 899 is an in-out variable. The method can reset the value to 1, if it 900 doesn't want this event handled as a multi-click. Returning a different 901 click target will also make the caller reset the click count. 902 \param _clickTarget Set by the method to a value identifying the clicked 903 element. If not explicitly set, an invalid click target is assumed. 904 */ 905 void 906 Window::MouseDown(BMessage* message, BPoint where, 907 const ClickTarget& lastClickTarget, int32& clickCount, 908 ClickTarget& _clickTarget) 909 { 910 // If the previous click hit our decorator, get the hit region. 911 int32 windowToken = fWindow->ServerToken(); 912 int32 lastHitRegion = 0; 913 if (lastClickTarget.GetType() == ClickTarget::TYPE_WINDOW_DECORATOR 914 && lastClickTarget.WindowToken() == windowToken) { 915 lastHitRegion = lastClickTarget.WindowElement(); 916 } 917 918 // Let the window behavior process the mouse event. 919 int32 hitRegion = 0; 920 bool eventEaten = fWindowBehaviour->MouseDown(message, where, lastHitRegion, 921 clickCount, hitRegion); 922 923 if (eventEaten) { 924 // click on the decorator (or equivalent) 925 _clickTarget = ClickTarget(ClickTarget::TYPE_WINDOW_DECORATOR, 926 windowToken, (int32)hitRegion); 927 } else { 928 // click was inside the window contents 929 int32 viewToken = B_NULL_TOKEN; 930 if (View* view = ViewAt(where)) { 931 if (HasModal()) 932 return; 933 934 // clicking a simple View 935 if (!IsFocus()) { 936 bool acceptFirstClick 937 = (Flags() & B_WILL_ACCEPT_FIRST_CLICK) != 0; 938 939 // Activate or focus the window in case it doesn't accept first 940 // click, depending on the mouse mode 941 if (!acceptFirstClick) { 942 bool avoidFocus = (Flags() & B_AVOID_FOCUS) != 0; 943 DesktopSettings desktopSettings(fDesktop); 944 if (desktopSettings.MouseMode() == B_NORMAL_MOUSE) 945 fDesktop->ActivateWindow(this); 946 else if (!avoidFocus) 947 fDesktop->SetFocusWindow(this); 948 949 // Eat the click if we don't accept first click 950 // (B_AVOID_FOCUS never gets the focus, so they always accept 951 // the first click) 952 // TODO: the latter is unlike BeOS - if we really wanted to 953 // imitate this behaviour, we would need to check if we're 954 // the front window instead of the focus window 955 if (!desktopSettings.AcceptFirstClick() && !avoidFocus) 956 return; 957 } 958 } 959 960 // fill out view token for the view under the mouse 961 viewToken = view->Token(); 962 view->MouseDown(message, where); 963 } 964 965 _clickTarget = ClickTarget(ClickTarget::TYPE_WINDOW_CONTENTS, 966 windowToken, viewToken); 967 } 968 } 969 970 971 void 972 Window::MouseUp(BMessage* message, BPoint where, int32* _viewToken) 973 { 974 fWindowBehaviour->MouseUp(message, where); 975 976 if (View* view = ViewAt(where)) { 977 if (HasModal()) 978 return; 979 980 *_viewToken = view->Token(); 981 view->MouseUp(message, where); 982 } 983 } 984 985 986 void 987 Window::MouseMoved(BMessage *message, BPoint where, int32* _viewToken, 988 bool isLatestMouseMoved, bool isFake) 989 { 990 View* view = ViewAt(where); 991 if (view != NULL) 992 *_viewToken = view->Token(); 993 994 // ignore pointer history 995 if (!isLatestMouseMoved) 996 return; 997 998 fWindowBehaviour->MouseMoved(message, where, isFake); 999 1000 // mouse cursor 1001 1002 if (view != NULL) { 1003 view->MouseMoved(message, where); 1004 1005 // TODO: there is more for real cursor support, ie. if a window is closed, 1006 // new app cursor shouldn't override view cursor, ... 1007 ServerWindow()->App()->SetCurrentCursor(view->Cursor()); 1008 } 1009 } 1010 1011 1012 void 1013 Window::ModifiersChanged(int32 modifiers) 1014 { 1015 fWindowBehaviour->ModifiersChanged(modifiers); 1016 } 1017 1018 1019 // #pragma mark - 1020 1021 1022 void 1023 Window::WorkspaceActivated(int32 index, bool active) 1024 { 1025 BMessage activatedMsg(B_WORKSPACE_ACTIVATED); 1026 activatedMsg.AddInt64("when", system_time()); 1027 activatedMsg.AddInt32("workspace", index); 1028 activatedMsg.AddBool("active", active); 1029 1030 ServerWindow()->SendMessageToClient(&activatedMsg); 1031 } 1032 1033 1034 void 1035 Window::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces) 1036 { 1037 fWorkspaces = newWorkspaces; 1038 1039 BMessage changedMsg(B_WORKSPACES_CHANGED); 1040 changedMsg.AddInt64("when", system_time()); 1041 changedMsg.AddInt32("old", oldWorkspaces); 1042 changedMsg.AddInt32("new", newWorkspaces); 1043 1044 ServerWindow()->SendMessageToClient(&changedMsg); 1045 } 1046 1047 1048 void 1049 Window::Activated(bool active) 1050 { 1051 BMessage msg(B_WINDOW_ACTIVATED); 1052 msg.AddBool("active", active); 1053 ServerWindow()->SendMessageToClient(&msg); 1054 } 1055 1056 1057 //# pragma mark - 1058 1059 1060 void 1061 Window::SetTitle(const char* name, BRegion& dirty) 1062 { 1063 // rebuild the clipping for the title area 1064 // and redraw it. 1065 1066 fTitle = name; 1067 1068 ::Decorator* decorator = Decorator(); 1069 if (decorator) { 1070 int32 index = PositionInStack(); 1071 decorator->SetTitle(index, name, &dirty); 1072 } 1073 } 1074 1075 1076 void 1077 Window::SetFocus(bool focus) 1078 { 1079 ::Decorator* decorator = Decorator(); 1080 1081 // executed from Desktop thread 1082 // it holds the clipping write lock, 1083 // so the window thread cannot be 1084 // accessing fIsFocus 1085 1086 BRegion* dirty = NULL; 1087 if (decorator) 1088 dirty = fRegionPool.GetRegion(decorator->GetFootprint()); 1089 if (dirty) { 1090 dirty->IntersectWith(&fVisibleRegion); 1091 fDesktop->MarkDirty(*dirty); 1092 fRegionPool.Recycle(dirty); 1093 } 1094 1095 fIsFocus = focus; 1096 if (decorator) { 1097 int32 index = PositionInStack(); 1098 decorator->SetFocus(index, focus); 1099 } 1100 1101 Activated(focus); 1102 } 1103 1104 1105 void 1106 Window::SetHidden(bool hidden) 1107 { 1108 // the desktop takes care of dirty regions 1109 if (fHidden != hidden) { 1110 fHidden = hidden; 1111 1112 fTopView->SetHidden(hidden); 1113 1114 // TODO: anything else? 1115 } 1116 } 1117 1118 1119 void 1120 Window::SetShowLevel(int32 showLevel) 1121 { 1122 if (showLevel == fShowLevel) 1123 return; 1124 1125 fShowLevel = showLevel; 1126 } 1127 1128 1129 void 1130 Window::SetMinimized(bool minimized) 1131 { 1132 if (minimized == fMinimized) 1133 return; 1134 1135 fMinimized = minimized; 1136 } 1137 1138 1139 bool 1140 Window::IsVisible() const 1141 { 1142 if (IsOffscreenWindow()) 1143 return true; 1144 1145 if (IsHidden()) 1146 return false; 1147 1148 /* 1149 if (fVisibleRegion.CountRects() == 0) 1150 return false; 1151 */ 1152 return fCurrentWorkspace >= 0 && fCurrentWorkspace < kWorkingList; 1153 } 1154 1155 1156 bool 1157 Window::IsDragging() const 1158 { 1159 if (!fWindowBehaviour.IsSet()) 1160 return false; 1161 return fWindowBehaviour->IsDragging(); 1162 } 1163 1164 1165 bool 1166 Window::IsResizing() const 1167 { 1168 if (!fWindowBehaviour.IsSet()) 1169 return false; 1170 return fWindowBehaviour->IsResizing(); 1171 } 1172 1173 1174 void 1175 Window::SetSizeLimits(int32 minWidth, int32 maxWidth, int32 minHeight, 1176 int32 maxHeight) 1177 { 1178 if (minWidth < 0) 1179 minWidth = 0; 1180 1181 if (minHeight < 0) 1182 minHeight = 0; 1183 1184 fMinWidth = minWidth; 1185 fMaxWidth = maxWidth; 1186 fMinHeight = minHeight; 1187 fMaxHeight = maxHeight; 1188 1189 // give the Decorator a say in this too 1190 ::Decorator* decorator = Decorator(); 1191 if (decorator) { 1192 decorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth, 1193 &fMaxHeight); 1194 } 1195 1196 _ObeySizeLimits(); 1197 } 1198 1199 1200 void 1201 Window::GetSizeLimits(int32* minWidth, int32* maxWidth, 1202 int32* minHeight, int32* maxHeight) const 1203 { 1204 *minWidth = fMinWidth; 1205 *maxWidth = fMaxWidth; 1206 *minHeight = fMinHeight; 1207 *maxHeight = fMaxHeight; 1208 } 1209 1210 1211 bool 1212 Window::SetTabLocation(float location, bool isShifting, BRegion& dirty) 1213 { 1214 ::Decorator* decorator = Decorator(); 1215 if (decorator) { 1216 int32 index = PositionInStack(); 1217 return decorator->SetTabLocation(index, location, isShifting, &dirty); 1218 } 1219 1220 return false; 1221 } 1222 1223 1224 float 1225 Window::TabLocation() const 1226 { 1227 ::Decorator* decorator = Decorator(); 1228 if (decorator) { 1229 int32 index = PositionInStack(); 1230 return decorator->TabLocation(index); 1231 } 1232 return 0.0; 1233 } 1234 1235 1236 bool 1237 Window::SetDecoratorSettings(const BMessage& settings, BRegion& dirty) 1238 { 1239 if (settings.what == 'prVu') { 1240 // 'prVu' == preview a decorator! 1241 BString path; 1242 if (settings.FindString("preview", &path) == B_OK) 1243 return gDecorManager.PreviewDecorator(path, this) == B_OK; 1244 return false; 1245 } 1246 1247 ::Decorator* decorator = Decorator(); 1248 if (decorator) 1249 return decorator->SetSettings(settings, &dirty); 1250 1251 return false; 1252 } 1253 1254 1255 bool 1256 Window::GetDecoratorSettings(BMessage* settings) 1257 { 1258 if (fDesktop) 1259 fDesktop->GetDecoratorSettings(this, *settings); 1260 1261 ::Decorator* decorator = Decorator(); 1262 if (decorator) 1263 return decorator->GetSettings(settings); 1264 1265 return false; 1266 } 1267 1268 1269 void 1270 Window::FontsChanged(BRegion* updateRegion) 1271 { 1272 ::Decorator* decorator = Decorator(); 1273 if (decorator != NULL) { 1274 DesktopSettings settings(fDesktop); 1275 decorator->FontsChanged(settings, updateRegion); 1276 } 1277 } 1278 1279 1280 void 1281 Window::ColorsChanged(BRegion* updateRegion) 1282 { 1283 ::Decorator* decorator = Decorator(); 1284 if (decorator != NULL) { 1285 DesktopSettings settings(fDesktop); 1286 decorator->ColorsChanged(settings, updateRegion); 1287 } 1288 } 1289 1290 1291 void 1292 Window::SetLook(window_look look, BRegion* updateRegion) 1293 { 1294 fLook = look; 1295 1296 fContentRegionValid = false; 1297 // mabye a resize handle was added... 1298 fEffectiveDrawingRegionValid = false; 1299 // ...and therefor the drawing region is 1300 // likely not valid anymore either 1301 1302 if (!fCurrentStack.IsSet()) 1303 return; 1304 1305 int32 stackPosition = PositionInStack(); 1306 1307 ::Decorator* decorator = Decorator(); 1308 if (decorator == NULL && look != B_NO_BORDER_WINDOW_LOOK) { 1309 // we need a new decorator 1310 decorator = gDecorManager.AllocateDecorator(this); 1311 fCurrentStack->SetDecorator(decorator); 1312 if (IsFocus()) 1313 decorator->SetFocus(stackPosition, true); 1314 } 1315 1316 if (decorator != NULL) { 1317 DesktopSettings settings(fDesktop); 1318 decorator->SetLook(stackPosition, settings, look, updateRegion); 1319 1320 // we might need to resize the window! 1321 decorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth, 1322 &fMaxHeight); 1323 _ObeySizeLimits(); 1324 } 1325 1326 if (look == B_NO_BORDER_WINDOW_LOOK) { 1327 // we don't need a decorator for this window 1328 fCurrentStack->SetDecorator(NULL); 1329 } 1330 } 1331 1332 1333 void 1334 Window::SetFeel(window_feel feel) 1335 { 1336 // if the subset list is no longer needed, clear it 1337 if ((fFeel == B_MODAL_SUBSET_WINDOW_FEEL 1338 || fFeel == B_FLOATING_SUBSET_WINDOW_FEEL) 1339 && feel != B_MODAL_SUBSET_WINDOW_FEEL 1340 && feel != B_FLOATING_SUBSET_WINDOW_FEEL) 1341 fSubsets.MakeEmpty(); 1342 1343 fFeel = feel; 1344 1345 // having modal windows with B_AVOID_FRONT or B_AVOID_FOCUS doesn't 1346 // make that much sense, so we filter those flags out on demand 1347 fFlags = fOriginalFlags; 1348 fFlags &= ValidWindowFlags(fFeel); 1349 1350 if (!IsNormal()) { 1351 fFlags |= B_SAME_POSITION_IN_ALL_WORKSPACES; 1352 _PropagatePosition(); 1353 } 1354 } 1355 1356 1357 void 1358 Window::SetFlags(uint32 flags, BRegion* updateRegion) 1359 { 1360 fOriginalFlags = flags; 1361 fFlags = flags & ValidWindowFlags(fFeel); 1362 if (!IsNormal()) 1363 fFlags |= B_SAME_POSITION_IN_ALL_WORKSPACES; 1364 1365 if ((fFlags & B_SAME_POSITION_IN_ALL_WORKSPACES) != 0) 1366 _PropagatePosition(); 1367 1368 ::Decorator* decorator = Decorator(); 1369 if (decorator == NULL) 1370 return; 1371 1372 int32 stackPosition = PositionInStack(); 1373 decorator->SetFlags(stackPosition, flags, updateRegion); 1374 1375 // we might need to resize the window! 1376 decorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth, &fMaxHeight); 1377 _ObeySizeLimits(); 1378 1379 // TODO: not sure if we want to do this 1380 #if 0 1381 if ((fOriginalFlags & kWindowScreenFlag) != (flags & kWindowScreenFlag)) { 1382 // TODO: disabling needs to be nestable (or we might lose the previous 1383 // update state) 1384 if ((flags & kWindowScreenFlag) != 0) 1385 DisableUpdateRequests(); 1386 else 1387 EnableUpdateRequests(); 1388 } 1389 #endif 1390 } 1391 1392 1393 /*! Returns whether or not a window is in the workspace list with the 1394 specified \a index. 1395 */ 1396 bool 1397 Window::InWorkspace(int32 index) const 1398 { 1399 return (fWorkspaces & (1UL << index)) != 0; 1400 } 1401 1402 1403 bool 1404 Window::SupportsFront() 1405 { 1406 if (fFeel == kDesktopWindowFeel 1407 || fFeel == kMenuWindowFeel 1408 || (fFlags & B_AVOID_FRONT) != 0) 1409 return false; 1410 1411 return true; 1412 } 1413 1414 1415 bool 1416 Window::IsModal() const 1417 { 1418 return IsModalFeel(fFeel); 1419 } 1420 1421 1422 bool 1423 Window::IsFloating() const 1424 { 1425 return IsFloatingFeel(fFeel); 1426 } 1427 1428 1429 bool 1430 Window::IsNormal() const 1431 { 1432 return !IsFloatingFeel(fFeel) && !IsModalFeel(fFeel); 1433 } 1434 1435 1436 bool 1437 Window::HasModal() const 1438 { 1439 for (Window* window = NextWindow(fCurrentWorkspace); window != NULL; 1440 window = window->NextWindow(fCurrentWorkspace)) { 1441 if (window->IsHidden() || !window->IsModal()) 1442 continue; 1443 1444 if (window->HasInSubset(this)) 1445 return true; 1446 } 1447 1448 return false; 1449 } 1450 1451 1452 /*! \brief Returns the windows that's in behind of the backmost position 1453 this window can get. 1454 Returns NULL is this window can be the backmost window. 1455 1456 \param workspace the workspace on which this check should be made. If 1457 the value is -1, the window's current workspace will be used. 1458 */ 1459 Window* 1460 Window::Backmost(Window* window, int32 workspace) 1461 { 1462 if (workspace == -1) 1463 workspace = fCurrentWorkspace; 1464 1465 ASSERT(workspace != -1); 1466 if (workspace == -1) 1467 return NULL; 1468 1469 // Desktop windows are always backmost 1470 if (fFeel == kDesktopWindowFeel) 1471 return NULL; 1472 1473 if (window == NULL) 1474 window = PreviousWindow(workspace); 1475 1476 for (; window != NULL; window = window->PreviousWindow(workspace)) { 1477 if (window->IsHidden() || window == this) 1478 continue; 1479 1480 if (HasInSubset(window)) 1481 return window; 1482 } 1483 1484 return NULL; 1485 } 1486 1487 1488 /*! \brief Returns the window that's in front of the frontmost position 1489 this window can get. 1490 Returns NULL if this window can be the frontmost window. 1491 1492 \param workspace the workspace on which this check should be made. If 1493 the value is -1, the window's current workspace will be used. 1494 */ 1495 Window* 1496 Window::Frontmost(Window* first, int32 workspace) 1497 { 1498 if (workspace == -1) 1499 workspace = fCurrentWorkspace; 1500 1501 ASSERT(workspace != -1); 1502 if (workspace == -1) 1503 return NULL; 1504 1505 if (fFeel == kDesktopWindowFeel) 1506 return first ? first : NextWindow(workspace); 1507 1508 if (first == NULL) 1509 first = NextWindow(workspace); 1510 1511 for (Window* window = first; window != NULL; 1512 window = window->NextWindow(workspace)) { 1513 if (window->IsHidden() || window == this) 1514 continue; 1515 1516 if (window->HasInSubset(this)) 1517 return window; 1518 } 1519 1520 return NULL; 1521 } 1522 1523 1524 bool 1525 Window::AddToSubset(Window* window) 1526 { 1527 return fSubsets.AddItem(window); 1528 } 1529 1530 1531 void 1532 Window::RemoveFromSubset(Window* window) 1533 { 1534 fSubsets.RemoveItem(window); 1535 } 1536 1537 1538 /*! Returns whether or not a window is in the subset of this window. 1539 If a window is in the subset of this window, it means it should always 1540 appear behind this window. 1541 */ 1542 bool 1543 Window::HasInSubset(const Window* window) const 1544 { 1545 if (window == NULL || fFeel == window->Feel() 1546 || fFeel == B_NORMAL_WINDOW_FEEL) 1547 return false; 1548 1549 // Menus are a special case: they will always be on-top of every window 1550 // of their application 1551 if (fFeel == kMenuWindowFeel) 1552 return window->ServerWindow()->App() == ServerWindow()->App(); 1553 if (window->Feel() == kMenuWindowFeel) 1554 return false; 1555 1556 // we have a few special feels that have a fixed order 1557 1558 const int32 kFeels[] = {kPasswordWindowFeel, kWindowScreenFeel, 1559 B_MODAL_ALL_WINDOW_FEEL, B_FLOATING_ALL_WINDOW_FEEL}; 1560 1561 for (uint32 order = 0; 1562 order < sizeof(kFeels) / sizeof(kFeels[0]); order++) { 1563 if (fFeel == kFeels[order]) 1564 return true; 1565 if (window->Feel() == kFeels[order]) 1566 return false; 1567 } 1568 1569 if ((fFeel == B_FLOATING_APP_WINDOW_FEEL 1570 && window->Feel() != B_MODAL_APP_WINDOW_FEEL) 1571 || fFeel == B_MODAL_APP_WINDOW_FEEL) 1572 return window->ServerWindow()->App() == ServerWindow()->App(); 1573 1574 return fSubsets.HasItem(window); 1575 } 1576 1577 1578 /*! \brief Collects all workspaces views in this window and puts it into \a list 1579 */ 1580 void 1581 Window::FindWorkspacesViews(BObjectList<WorkspacesView>& list) const 1582 { 1583 int32 count = fWorkspacesViewCount; 1584 fTopView->FindViews(kWorkspacesViewFlag, (BObjectList<View>&)list, count); 1585 } 1586 1587 1588 /*! \brief Returns on which workspaces the window should be visible. 1589 1590 A modal or floating window may be visible on a workspace if one 1591 of its subset windows is visible there. Floating windows also need 1592 to have a subset as front window to be visible. 1593 */ 1594 uint32 1595 Window::SubsetWorkspaces() const 1596 { 1597 if (fFeel == B_MODAL_ALL_WINDOW_FEEL 1598 || fFeel == B_FLOATING_ALL_WINDOW_FEEL) 1599 return B_ALL_WORKSPACES; 1600 1601 if (fFeel == B_FLOATING_APP_WINDOW_FEEL) { 1602 Window* front = fDesktop->FrontWindow(); 1603 if (front != NULL && front->IsNormal() 1604 && front->ServerWindow()->App() == ServerWindow()->App()) 1605 return ServerWindow()->App()->Workspaces(); 1606 1607 return 0; 1608 } 1609 1610 if (fFeel == B_MODAL_APP_WINDOW_FEEL) { 1611 uint32 workspaces = ServerWindow()->App()->Workspaces(); 1612 if (workspaces == 0) { 1613 // The application doesn't seem to have any other windows 1614 // open or visible - but we'd like to see modal windows 1615 // anyway, at least when they are first opened. 1616 return 1UL << fDesktop->CurrentWorkspace(); 1617 } 1618 return workspaces; 1619 } 1620 1621 if (fFeel == B_MODAL_SUBSET_WINDOW_FEEL 1622 || fFeel == B_FLOATING_SUBSET_WINDOW_FEEL) { 1623 uint32 workspaces = 0; 1624 bool hasNormalFront = false; 1625 for (int32 i = 0; i < fSubsets.CountItems(); i++) { 1626 Window* window = fSubsets.ItemAt(i); 1627 1628 if (!window->IsHidden()) 1629 workspaces |= window->Workspaces(); 1630 if (window == fDesktop->FrontWindow() && window->IsNormal()) 1631 hasNormalFront = true; 1632 } 1633 1634 if (fFeel == B_FLOATING_SUBSET_WINDOW_FEEL && !hasNormalFront) 1635 return 0; 1636 1637 return workspaces; 1638 } 1639 1640 return 0; 1641 } 1642 1643 1644 /*! Returns whether or not a window is in the subset workspace list with the 1645 specified \a index. 1646 See SubsetWorkspaces(). 1647 */ 1648 bool 1649 Window::InSubsetWorkspace(int32 index) const 1650 { 1651 return (SubsetWorkspaces() & (1UL << index)) != 0; 1652 } 1653 1654 1655 // #pragma mark - static 1656 1657 1658 /*static*/ bool 1659 Window::IsValidLook(window_look look) 1660 { 1661 return look == B_TITLED_WINDOW_LOOK 1662 || look == B_DOCUMENT_WINDOW_LOOK 1663 || look == B_MODAL_WINDOW_LOOK 1664 || look == B_FLOATING_WINDOW_LOOK 1665 || look == B_BORDERED_WINDOW_LOOK 1666 || look == B_NO_BORDER_WINDOW_LOOK 1667 || look == kDesktopWindowLook 1668 || look == kLeftTitledWindowLook; 1669 } 1670 1671 1672 /*static*/ bool 1673 Window::IsValidFeel(window_feel feel) 1674 { 1675 return feel == B_NORMAL_WINDOW_FEEL 1676 || feel == B_MODAL_SUBSET_WINDOW_FEEL 1677 || feel == B_MODAL_APP_WINDOW_FEEL 1678 || feel == B_MODAL_ALL_WINDOW_FEEL 1679 || feel == B_FLOATING_SUBSET_WINDOW_FEEL 1680 || feel == B_FLOATING_APP_WINDOW_FEEL 1681 || feel == B_FLOATING_ALL_WINDOW_FEEL 1682 || feel == kDesktopWindowFeel 1683 || feel == kMenuWindowFeel 1684 || feel == kWindowScreenFeel 1685 || feel == kPasswordWindowFeel 1686 || feel == kOffscreenWindowFeel; 1687 } 1688 1689 1690 /*static*/ bool 1691 Window::IsModalFeel(window_feel feel) 1692 { 1693 return feel == B_MODAL_SUBSET_WINDOW_FEEL 1694 || feel == B_MODAL_APP_WINDOW_FEEL 1695 || feel == B_MODAL_ALL_WINDOW_FEEL; 1696 } 1697 1698 1699 /*static*/ bool 1700 Window::IsFloatingFeel(window_feel feel) 1701 { 1702 return feel == B_FLOATING_SUBSET_WINDOW_FEEL 1703 || feel == B_FLOATING_APP_WINDOW_FEEL 1704 || feel == B_FLOATING_ALL_WINDOW_FEEL; 1705 } 1706 1707 1708 /*static*/ uint32 1709 Window::ValidWindowFlags() 1710 { 1711 return B_NOT_MOVABLE 1712 | B_NOT_CLOSABLE 1713 | B_NOT_ZOOMABLE 1714 | B_NOT_MINIMIZABLE 1715 | B_NOT_RESIZABLE 1716 | B_NOT_H_RESIZABLE 1717 | B_NOT_V_RESIZABLE 1718 | B_AVOID_FRONT 1719 | B_AVOID_FOCUS 1720 | B_WILL_ACCEPT_FIRST_CLICK 1721 | B_OUTLINE_RESIZE 1722 | B_NO_WORKSPACE_ACTIVATION 1723 | B_NOT_ANCHORED_ON_ACTIVATE 1724 | B_ASYNCHRONOUS_CONTROLS 1725 | B_QUIT_ON_WINDOW_CLOSE 1726 | B_SAME_POSITION_IN_ALL_WORKSPACES 1727 | B_AUTO_UPDATE_SIZE_LIMITS 1728 | B_CLOSE_ON_ESCAPE 1729 | B_NO_SERVER_SIDE_WINDOW_MODIFIERS 1730 | kWindowScreenFlag 1731 | kAcceptKeyboardFocusFlag; 1732 } 1733 1734 1735 /*static*/ uint32 1736 Window::ValidWindowFlags(window_feel feel) 1737 { 1738 uint32 flags = ValidWindowFlags(); 1739 if (IsModalFeel(feel)) 1740 return flags & ~(B_AVOID_FOCUS | B_AVOID_FRONT); 1741 1742 return flags; 1743 } 1744 1745 1746 // #pragma mark - private 1747 1748 1749 void 1750 Window::_ShiftPartOfRegion(BRegion* region, BRegion* regionToShift, 1751 int32 xOffset, int32 yOffset) 1752 { 1753 BRegion* common = fRegionPool.GetRegion(*regionToShift); 1754 if (!common) 1755 return; 1756 // see if there is a common part at all 1757 common->IntersectWith(region); 1758 if (common->CountRects() > 0) { 1759 // cut the common part from the region, 1760 // offset that to destination and include again 1761 region->Exclude(common); 1762 common->OffsetBy(xOffset, yOffset); 1763 region->Include(common); 1764 } 1765 fRegionPool.Recycle(common); 1766 } 1767 1768 1769 void 1770 Window::_TriggerContentRedraw(BRegion& dirtyContentRegion) 1771 { 1772 if (!IsVisible() || dirtyContentRegion.CountRects() == 0 1773 || (fFlags & kWindowScreenFlag) != 0) 1774 return; 1775 1776 // put this into the pending dirty region 1777 // to eventually trigger a client redraw 1778 1779 _TransferToUpdateSession(&dirtyContentRegion); 1780 } 1781 1782 1783 void 1784 Window::_DrawBorder() 1785 { 1786 // this is executed in the window thread, but only 1787 // in respond to a REDRAW message having been received, the 1788 // clipping lock is held for reading 1789 ::Decorator* decorator = Decorator(); 1790 if (!decorator) 1791 return; 1792 1793 // construct the region of the border that needs redrawing 1794 BRegion* dirtyBorderRegion = fRegionPool.GetRegion(); 1795 if (!dirtyBorderRegion) 1796 return; 1797 GetBorderRegion(dirtyBorderRegion); 1798 // intersect with our visible region 1799 dirtyBorderRegion->IntersectWith(&fVisibleRegion); 1800 // intersect with the dirty region 1801 dirtyBorderRegion->IntersectWith(&fDirtyRegion); 1802 1803 DrawingEngine* engine = decorator->GetDrawingEngine(); 1804 if (dirtyBorderRegion->CountRects() > 0 && engine->LockParallelAccess()) { 1805 engine->ConstrainClippingRegion(dirtyBorderRegion); 1806 bool copyToFrontEnabled = engine->CopyToFrontEnabled(); 1807 engine->SetCopyToFrontEnabled(false); 1808 1809 decorator->Draw(dirtyBorderRegion->Frame()); 1810 1811 engine->SetCopyToFrontEnabled(copyToFrontEnabled); 1812 engine->CopyToFront(*dirtyBorderRegion); 1813 1814 // TODO: remove this once the DrawState stuff is handled 1815 // more cleanly. The reason why this is needed is that 1816 // when the decorator draws strings, a draw state is set 1817 // on the Painter object, and this is were it might get 1818 // out of sync with what the ServerWindow things is the 1819 // current DrawState set on the Painter 1820 fWindow->ResyncDrawState(); 1821 1822 engine->UnlockParallelAccess(); 1823 } 1824 fRegionPool.Recycle(dirtyBorderRegion); 1825 } 1826 1827 1828 /*! pre: the clipping is readlocked (this function is 1829 only called from _TriggerContentRedraw()), which 1830 in turn is only called from MessageReceived() with 1831 the clipping lock held 1832 */ 1833 void 1834 Window::_TransferToUpdateSession(BRegion* contentDirtyRegion) 1835 { 1836 if (contentDirtyRegion->CountRects() <= 0) 1837 return; 1838 1839 //fDrawingEngine->FillRegion(*contentDirtyRegion, sPendingColor); 1840 //snooze(20000); 1841 1842 // add to pending 1843 fPendingUpdateSession->SetUsed(true); 1844 // if (!fPendingUpdateSession->IsExpose()) 1845 fPendingUpdateSession->AddCause(fDirtyCause); 1846 fPendingUpdateSession->Include(contentDirtyRegion); 1847 1848 if (!fUpdateRequested) { 1849 // send this to client 1850 _SendUpdateMessage(); 1851 // the pending region is now the current, 1852 // though the update does not start until 1853 // we received BEGIN_UPDATE from the client 1854 } 1855 } 1856 1857 1858 void 1859 Window::_SendUpdateMessage() 1860 { 1861 if (!fUpdatesEnabled) 1862 return; 1863 1864 BMessage message(_UPDATE_); 1865 if (ServerWindow()->SendMessageToClient(&message) != B_OK) { 1866 // If sending the message failed, we'll just keep adding to the dirty 1867 // region until sending was successful. 1868 // TODO: we might want to automatically resend this message in this case 1869 return; 1870 } 1871 1872 fUpdateRequested = true; 1873 fEffectiveDrawingRegionValid = false; 1874 } 1875 1876 1877 void 1878 Window::BeginUpdate(BPrivate::PortLink& link) 1879 { 1880 // NOTE: since we might "shift" parts of the 1881 // internal dirty regions from the desktop thread 1882 // in response to Window::ResizeBy(), which 1883 // might move arround views, the user of this function 1884 // needs to hold the global clipping lock so that the internal 1885 // dirty regions are not messed with from the Desktop thread 1886 // and ServerWindow thread at the same time. 1887 1888 if (!fUpdateRequested) { 1889 link.StartMessage(B_ERROR); 1890 link.Flush(); 1891 fprintf(stderr, "Window::BeginUpdate() - no update requested!\n"); 1892 return; 1893 } 1894 1895 // make the pending update session the current update session 1896 // (toggle the pointers) 1897 UpdateSession* temp = fCurrentUpdateSession; 1898 fCurrentUpdateSession = fPendingUpdateSession; 1899 fPendingUpdateSession = temp; 1900 fPendingUpdateSession->SetUsed(false); 1901 // all drawing command from the client 1902 // will have the dirty region from the update 1903 // session enforced 1904 fInUpdate = true; 1905 fEffectiveDrawingRegionValid = false; 1906 1907 // TODO: each view could be drawn individually 1908 // right before carrying out the first drawing 1909 // command from the client during an update 1910 // (View::IsBackgroundDirty() can be used 1911 // for this) 1912 if (!fContentRegionValid) 1913 _UpdateContentRegion(); 1914 1915 BRegion* dirty = fRegionPool.GetRegion( 1916 fCurrentUpdateSession->DirtyRegion()); 1917 if (!dirty) { 1918 link.StartMessage(B_ERROR); 1919 link.Flush(); 1920 return; 1921 } 1922 1923 dirty->IntersectWith(&VisibleContentRegion()); 1924 1925 //if (!fCurrentUpdateSession->IsExpose()) { 1926 ////sCurrentColor.red = rand() % 255; 1927 ////sCurrentColor.green = rand() % 255; 1928 ////sCurrentColor.blue = rand() % 255; 1929 ////sPendingColor.red = rand() % 255; 1930 ////sPendingColor.green = rand() % 255; 1931 ////sPendingColor.blue = rand() % 255; 1932 //fDrawingEngine->FillRegion(*dirty, sCurrentColor); 1933 //snooze(10000); 1934 //} 1935 1936 link.StartMessage(B_OK); 1937 // append the current window geometry to the 1938 // message, the client will need it 1939 link.Attach<BPoint>(fFrame.LeftTop()); 1940 link.Attach<float>(fFrame.Width()); 1941 link.Attach<float>(fFrame.Height()); 1942 // find and attach all views that intersect with 1943 // the dirty region 1944 fTopView->AddTokensForViewsInRegion(link, *dirty, &fContentRegion); 1945 // mark the end of the token "list" 1946 link.Attach<int32>(B_NULL_TOKEN); 1947 link.Flush(); 1948 1949 // supress back to front buffer copies in the drawing engine 1950 fDrawingEngine->SetCopyToFrontEnabled(false); 1951 1952 if (fDrawingEngine->LockParallelAccess()) { 1953 fDrawingEngine->SuspendAutoSync(); 1954 1955 fTopView->Draw(GetDrawingEngine(), dirty, &fContentRegion, true); 1956 1957 fDrawingEngine->Sync(); 1958 fDrawingEngine->UnlockParallelAccess(); 1959 } // else the background was cleared already 1960 1961 fRegionPool.Recycle(dirty); 1962 } 1963 1964 1965 void 1966 Window::EndUpdate() 1967 { 1968 // NOTE: see comment in _BeginUpdate() 1969 1970 if (fInUpdate) { 1971 // reenable copy to front 1972 fDrawingEngine->SetCopyToFrontEnabled(true); 1973 1974 BRegion* dirty = fRegionPool.GetRegion( 1975 fCurrentUpdateSession->DirtyRegion()); 1976 1977 if (dirty) { 1978 dirty->IntersectWith(&VisibleContentRegion()); 1979 1980 fDrawingEngine->CopyToFront(*dirty); 1981 fRegionPool.Recycle(dirty); 1982 } 1983 1984 fCurrentUpdateSession->SetUsed(false); 1985 1986 fInUpdate = false; 1987 fEffectiveDrawingRegionValid = false; 1988 } 1989 if (fPendingUpdateSession->IsUsed()) { 1990 // send this to client 1991 _SendUpdateMessage(); 1992 } else { 1993 fUpdateRequested = false; 1994 } 1995 } 1996 1997 1998 void 1999 Window::_UpdateContentRegion() 2000 { 2001 fContentRegion.Set(fFrame); 2002 2003 // resize handle 2004 ::Decorator* decorator = Decorator(); 2005 if (decorator) 2006 fContentRegion.Exclude(&decorator->GetFootprint()); 2007 2008 fContentRegionValid = true; 2009 } 2010 2011 2012 void 2013 Window::_ObeySizeLimits() 2014 { 2015 // make sure we even have valid size limits 2016 if (fMaxWidth < fMinWidth) 2017 fMaxWidth = fMinWidth; 2018 2019 if (fMaxHeight < fMinHeight) 2020 fMaxHeight = fMinHeight; 2021 2022 // Automatically resize the window to fit these new limits 2023 // if it does not already. 2024 2025 // On R5, Windows don't automatically resize, but since 2026 // BWindow::ResizeTo() even honors the limits, I would guess 2027 // this is a bug that we don't have to adopt. 2028 // Note that most current apps will do unnecessary resizing 2029 // after having set the limits, but the overhead is neglible. 2030 2031 float minWidthDiff = fMinWidth - fFrame.Width(); 2032 float minHeightDiff = fMinHeight - fFrame.Height(); 2033 float maxWidthDiff = fMaxWidth - fFrame.Width(); 2034 float maxHeightDiff = fMaxHeight - fFrame.Height(); 2035 2036 float xDiff = 0.0; 2037 if (minWidthDiff > 0.0) // we're currently smaller than minWidth 2038 xDiff = minWidthDiff; 2039 else if (maxWidthDiff < 0.0) // we're currently larger than maxWidth 2040 xDiff = maxWidthDiff; 2041 2042 float yDiff = 0.0; 2043 if (minHeightDiff > 0.0) // we're currently smaller than minHeight 2044 yDiff = minHeightDiff; 2045 else if (maxHeightDiff < 0.0) // we're currently larger than maxHeight 2046 yDiff = maxHeightDiff; 2047 2048 if (fDesktop) 2049 fDesktop->ResizeWindowBy(this, xDiff, yDiff); 2050 else 2051 ResizeBy((int32)xDiff, (int32)yDiff, NULL); 2052 } 2053 2054 2055 // #pragma mark - UpdateSession 2056 2057 2058 Window::UpdateSession::UpdateSession() 2059 : 2060 fDirtyRegion(), 2061 fInUse(false), 2062 fCause(0) 2063 { 2064 } 2065 2066 2067 Window::UpdateSession::~UpdateSession() 2068 { 2069 } 2070 2071 2072 void 2073 Window::UpdateSession::Include(BRegion* additionalDirty) 2074 { 2075 fDirtyRegion.Include(additionalDirty); 2076 } 2077 2078 2079 void 2080 Window::UpdateSession::Exclude(BRegion* dirtyInNextSession) 2081 { 2082 fDirtyRegion.Exclude(dirtyInNextSession); 2083 } 2084 2085 2086 void 2087 Window::UpdateSession::MoveBy(int32 x, int32 y) 2088 { 2089 fDirtyRegion.OffsetBy(x, y); 2090 } 2091 2092 2093 void 2094 Window::UpdateSession::SetUsed(bool used) 2095 { 2096 fInUse = used; 2097 if (!fInUse) { 2098 fDirtyRegion.MakeEmpty(); 2099 fCause = 0; 2100 } 2101 } 2102 2103 2104 void 2105 Window::UpdateSession::AddCause(uint8 cause) 2106 { 2107 fCause |= cause; 2108 } 2109 2110 2111 int32 2112 Window::PositionInStack() const 2113 { 2114 if (!fCurrentStack.IsSet()) 2115 return -1; 2116 return fCurrentStack->WindowList().IndexOf(this); 2117 } 2118 2119 2120 bool 2121 Window::DetachFromWindowStack(bool ownStackNeeded) 2122 { 2123 // The lock must normally be held but is not held when closing the window. 2124 //ASSERT_MULTI_WRITE_LOCKED(fDesktop->WindowLocker()); 2125 2126 if (!fCurrentStack.IsSet()) 2127 return false; 2128 if (fCurrentStack->CountWindows() == 1) 2129 return true; 2130 2131 int32 index = PositionInStack(); 2132 2133 if (fCurrentStack->RemoveWindow(this) == false) 2134 return false; 2135 2136 BRegion invalidatedRegion; 2137 ::Decorator* decorator = fCurrentStack->Decorator(); 2138 if (decorator != NULL) { 2139 decorator->RemoveTab(index, &invalidatedRegion); 2140 decorator->SetTopTab(fCurrentStack->LayerOrder().CountItems() - 1); 2141 } 2142 2143 Window* remainingTop = fCurrentStack->TopLayerWindow(); 2144 if (remainingTop != NULL) { 2145 if (decorator != NULL) 2146 decorator->SetDrawingEngine(remainingTop->GetDrawingEngine()); 2147 // propagate focus to the decorator 2148 remainingTop->SetFocus(remainingTop->IsFocus()); 2149 remainingTop->SetLook(remainingTop->Look(), NULL); 2150 } 2151 2152 fCurrentStack = NULL; 2153 if (ownStackNeeded == true) 2154 _InitWindowStack(); 2155 // propagate focus to the new decorator 2156 SetFocus(IsFocus()); 2157 2158 if (remainingTop != NULL) { 2159 invalidatedRegion.Include(&remainingTop->VisibleRegion()); 2160 fDesktop->RebuildAndRedrawAfterWindowChange(remainingTop, 2161 invalidatedRegion); 2162 } 2163 return true; 2164 } 2165 2166 2167 bool 2168 Window::AddWindowToStack(Window* window) 2169 { 2170 ASSERT_MULTI_WRITE_LOCKED(fDesktop->WindowLocker()); 2171 2172 WindowStack* stack = GetWindowStack(); 2173 if (stack == NULL) 2174 return false; 2175 2176 BRegion dirty; 2177 // move window to the own position 2178 BRect ownFrame = Frame(); 2179 BRect frame = window->Frame(); 2180 float deltaToX = round(ownFrame.left - frame.left); 2181 float deltaToY = round(ownFrame.top - frame.top); 2182 frame.OffsetBy(deltaToX, deltaToY); 2183 float deltaByX = round(ownFrame.right - frame.right); 2184 float deltaByY = round(ownFrame.bottom - frame.bottom); 2185 dirty.Include(&window->VisibleRegion()); 2186 window->MoveBy(deltaToX, deltaToY, false); 2187 window->ResizeBy(deltaByX, deltaByY, &dirty, false); 2188 2189 // first collect dirt from the window to add 2190 ::Decorator* otherDecorator = window->Decorator(); 2191 if (otherDecorator != NULL) 2192 dirty.Include(otherDecorator->TitleBarRect()); 2193 ::Decorator* decorator = stack->Decorator(); 2194 if (decorator != NULL) 2195 dirty.Include(decorator->TitleBarRect()); 2196 2197 int32 position = PositionInStack() + 1; 2198 if (position >= stack->CountWindows()) 2199 position = -1; 2200 if (stack->AddWindow(window, position) == false) 2201 return false; 2202 window->DetachFromWindowStack(false); 2203 window->fCurrentStack.SetTo(stack); 2204 2205 if (decorator != NULL) { 2206 DesktopSettings settings(fDesktop); 2207 decorator->AddTab(settings, window->Title(), window->Look(), 2208 window->Flags(), position, &dirty); 2209 } 2210 2211 window->SetLook(window->Look(), &dirty); 2212 fDesktop->RebuildAndRedrawAfterWindowChange(TopLayerStackWindow(), dirty); 2213 window->SetFocus(window->IsFocus()); 2214 return true; 2215 } 2216 2217 2218 Window* 2219 Window::StackedWindowAt(const BPoint& where) 2220 { 2221 ::Decorator* decorator = Decorator(); 2222 if (decorator == NULL) 2223 return this; 2224 2225 int tab = decorator->TabAt(where); 2226 // if we have a decorator we also have a stack 2227 Window* window = fCurrentStack->WindowAt(tab); 2228 if (window != NULL) 2229 return window; 2230 return this; 2231 } 2232 2233 2234 Window* 2235 Window::TopLayerStackWindow() 2236 { 2237 if (!fCurrentStack.IsSet()) 2238 return this; 2239 return fCurrentStack->TopLayerWindow(); 2240 } 2241 2242 2243 WindowStack* 2244 Window::GetWindowStack() 2245 { 2246 if (!fCurrentStack.IsSet()) 2247 return _InitWindowStack(); 2248 return fCurrentStack; 2249 } 2250 2251 2252 bool 2253 Window::MoveToTopStackLayer() 2254 { 2255 ::Decorator* decorator = Decorator(); 2256 if (decorator == NULL) 2257 return false; 2258 decorator->SetDrawingEngine(GetDrawingEngine()); 2259 SetLook(Look(), NULL); 2260 decorator->SetTopTab(PositionInStack()); 2261 return fCurrentStack->MoveToTopLayer(this); 2262 } 2263 2264 2265 bool 2266 Window::MoveToStackPosition(int32 to, bool isMoving) 2267 { 2268 if (!fCurrentStack.IsSet()) 2269 return false; 2270 int32 index = PositionInStack(); 2271 if (fCurrentStack->Move(index, to) == false) 2272 return false; 2273 2274 BRegion dirty; 2275 ::Decorator* decorator = Decorator(); 2276 if (decorator && decorator->MoveTab(index, to, isMoving, &dirty) == false) 2277 return false; 2278 2279 fDesktop->RebuildAndRedrawAfterWindowChange(this, dirty); 2280 return true; 2281 } 2282 2283 2284 WindowStack* 2285 Window::_InitWindowStack() 2286 { 2287 fCurrentStack = NULL; 2288 ::Decorator* decorator = NULL; 2289 if (fLook != B_NO_BORDER_WINDOW_LOOK) 2290 decorator = gDecorManager.AllocateDecorator(this); 2291 2292 WindowStack* stack = new(std::nothrow) WindowStack(decorator); 2293 if (stack == NULL) 2294 return NULL; 2295 2296 if (stack->AddWindow(this) != true) { 2297 delete stack; 2298 return NULL; 2299 } 2300 fCurrentStack.SetTo(stack, true); 2301 return stack; 2302 } 2303 2304 2305 WindowStack::WindowStack(::Decorator* decorator) 2306 : 2307 fDecorator(decorator) 2308 { 2309 2310 } 2311 2312 2313 WindowStack::~WindowStack() 2314 { 2315 } 2316 2317 2318 void 2319 WindowStack::SetDecorator(::Decorator* decorator) 2320 { 2321 fDecorator.SetTo(decorator); 2322 } 2323 2324 2325 ::Decorator* 2326 WindowStack::Decorator() 2327 { 2328 return fDecorator.Get(); 2329 } 2330 2331 2332 Window* 2333 WindowStack::TopLayerWindow() const 2334 { 2335 return fWindowLayerOrder.ItemAt(fWindowLayerOrder.CountItems() - 1); 2336 } 2337 2338 2339 int32 2340 WindowStack::CountWindows() 2341 { 2342 return fWindowList.CountItems(); 2343 } 2344 2345 2346 Window* 2347 WindowStack::WindowAt(int32 index) 2348 { 2349 return fWindowList.ItemAt(index); 2350 } 2351 2352 2353 bool 2354 WindowStack::AddWindow(Window* window, int32 position) 2355 { 2356 if (position >= 0) { 2357 if (fWindowList.AddItem(window, position) == false) 2358 return false; 2359 } else if (fWindowList.AddItem(window) == false) 2360 return false; 2361 2362 if (fWindowLayerOrder.AddItem(window) == false) { 2363 fWindowList.RemoveItem(window); 2364 return false; 2365 } 2366 return true; 2367 } 2368 2369 2370 bool 2371 WindowStack::RemoveWindow(Window* window) 2372 { 2373 if (fWindowList.RemoveItem(window) == false) 2374 return false; 2375 2376 fWindowLayerOrder.RemoveItem(window); 2377 return true; 2378 } 2379 2380 2381 bool 2382 WindowStack::MoveToTopLayer(Window* window) 2383 { 2384 int32 index = fWindowLayerOrder.IndexOf(window); 2385 return fWindowLayerOrder.MoveItem(index, 2386 fWindowLayerOrder.CountItems() - 1); 2387 } 2388 2389 2390 bool 2391 WindowStack::Move(int32 from, int32 to) 2392 { 2393 return fWindowList.MoveItem(from, to); 2394 } 2395