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 85 fContentRegion(), 86 fEffectiveDrawingRegion(), 87 88 fVisibleContentRegionValid(false), 89 fContentRegionValid(false), 90 fEffectiveDrawingRegionValid(false), 91 92 fRegionPool(), 93 94 fWindow(window), 95 fDrawingEngine(drawingEngine), 96 fDesktop(window->Desktop()), 97 98 fCurrentUpdateSession(&fUpdateSessions[0]), 99 fPendingUpdateSession(&fUpdateSessions[1]), 100 fUpdateRequested(false), 101 fInUpdate(false), 102 fUpdatesEnabled(false), 103 104 // Windows start hidden 105 fHidden(true), 106 // Hidden is 1 or more 107 fShowLevel(1), 108 fMinimized(false), 109 fIsFocus(false), 110 111 fLook(look), 112 fFeel(feel), 113 fWorkspaces(workspaces), 114 fCurrentWorkspace(-1), 115 116 fMinWidth(1), 117 fMaxWidth(32768), 118 fMinHeight(1), 119 fMaxHeight(32768), 120 121 fWorkspacesViewCount(0) 122 { 123 _InitWindowStack(); 124 125 // make sure our arguments are valid 126 if (!IsValidLook(fLook)) 127 fLook = B_TITLED_WINDOW_LOOK; 128 if (!IsValidFeel(fFeel)) 129 fFeel = B_NORMAL_WINDOW_FEEL; 130 131 SetFlags(flags, NULL); 132 133 if (fLook != B_NO_BORDER_WINDOW_LOOK && fCurrentStack.IsSet()) { 134 // allocates a decorator 135 ::Decorator* decorator = Decorator(); 136 if (decorator != NULL) { 137 decorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth, 138 &fMaxHeight); 139 } 140 } 141 if (fFeel != kOffscreenWindowFeel) 142 fWindowBehaviour.SetTo(gDecorManager.AllocateWindowBehaviour(this)); 143 144 // do we need to change our size to let the decorator fit? 145 // _ResizeBy() will adapt the frame for validity before resizing 146 if (feel == kDesktopWindowFeel) { 147 // the desktop window spans over the whole screen 148 // TODO: this functionality should be moved somewhere else 149 // (so that it is always used when the workspace is changed) 150 uint16 width, height; 151 uint32 colorSpace; 152 float frequency; 153 if (Screen() != NULL) { 154 Screen()->GetMode(width, height, colorSpace, frequency); 155 // TODO: MOVE THIS AWAY!!! ResizeBy contains calls to virtual methods! 156 // Also, there is no TopView()! 157 fFrame.OffsetTo(B_ORIGIN); 158 // ResizeBy(width - frame.Width(), height - frame.Height(), NULL); 159 } 160 } 161 162 STRACE(("Window %p, %s:\n", this, Name())); 163 STRACE(("\tFrame: (%.1f, %.1f, %.1f, %.1f)\n", fFrame.left, fFrame.top, 164 fFrame.right, fFrame.bottom)); 165 STRACE(("\tWindow %s\n", window ? window->Title() : "NULL")); 166 } 167 168 169 Window::~Window() 170 { 171 if (fTopView.IsSet()) { 172 fTopView->DetachedFromWindow(); 173 } 174 175 DetachFromWindowStack(false); 176 177 gDecorManager.CleanupForWindow(this); 178 } 179 180 181 status_t 182 Window::InitCheck() const 183 { 184 if (GetDrawingEngine() == NULL 185 || (fFeel != kOffscreenWindowFeel && !fWindowBehaviour.IsSet())) 186 return B_NO_MEMORY; 187 // TODO: anything else? 188 return B_OK; 189 } 190 191 192 void 193 Window::SetClipping(BRegion* stillAvailableOnScreen) 194 { 195 // this function is only called from the Desktop thread 196 197 // start from full region (as if the window was fully visible) 198 GetFullRegion(&fVisibleRegion); 199 // clip to region still available on screen 200 fVisibleRegion.IntersectWith(stillAvailableOnScreen); 201 202 fVisibleContentRegionValid = false; 203 fEffectiveDrawingRegionValid = false; 204 } 205 206 207 void 208 Window::GetFullRegion(BRegion* region) 209 { 210 // TODO: if someone needs to call this from 211 // the outside, the clipping needs to be readlocked! 212 213 // start from the decorator border, extend to use the frame 214 GetBorderRegion(region); 215 region->Include(fFrame); 216 } 217 218 219 void 220 Window::GetBorderRegion(BRegion* region) 221 { 222 // TODO: if someone needs to call this from 223 // the outside, the clipping needs to be readlocked! 224 225 ::Decorator* decorator = Decorator(); 226 if (decorator) 227 *region = decorator->GetFootprint(); 228 else 229 region->MakeEmpty(); 230 } 231 232 233 void 234 Window::GetContentRegion(BRegion* region) 235 { 236 // TODO: if someone needs to call this from 237 // the outside, the clipping needs to be readlocked! 238 239 if (!fContentRegionValid) { 240 _UpdateContentRegion(); 241 } 242 243 *region = fContentRegion; 244 } 245 246 247 BRegion& 248 Window::VisibleContentRegion() 249 { 250 // TODO: if someone needs to call this from 251 // the outside, the clipping needs to be readlocked! 252 253 // regions expected to be locked 254 if (!fVisibleContentRegionValid) { 255 GetContentRegion(&fVisibleContentRegion); 256 fVisibleContentRegion.IntersectWith(&fVisibleRegion); 257 } 258 return fVisibleContentRegion; 259 } 260 261 262 // #pragma mark - 263 264 265 void 266 Window::_PropagatePosition() 267 { 268 if ((fFlags & B_SAME_POSITION_IN_ALL_WORKSPACES) == 0) 269 return; 270 271 for (int32 i = 0; i < kListCount; i++) { 272 Anchor(i).position = fFrame.LeftTop(); 273 } 274 } 275 276 277 void 278 Window::MoveBy(int32 x, int32 y, bool moveStack) 279 { 280 // this function is only called from the desktop thread 281 282 if (x == 0 && y == 0) 283 return; 284 285 fFrame.OffsetBy(x, y); 286 _PropagatePosition(); 287 288 // take along the dirty region which is not 289 // processed yet 290 fDirtyRegion.OffsetBy(x, y); 291 fExposeRegion.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(const BRegion& dirtyRegion, const BRegion& exposeRegion) 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(&dirtyRegion); 764 fExposeRegion.Include(&exposeRegion); 765 } 766 767 768 void 769 Window::RedrawDirtyRegion() 770 { 771 if (TopLayerStackWindow() != this) { 772 fDirtyRegion.MakeEmpty(); 773 fExposeRegion.MakeEmpty(); 774 return; 775 } 776 777 // executed from ServerWindow with the read lock held 778 if (IsVisible()) { 779 _DrawBorder(); 780 781 BRegion* dirtyContentRegion = fRegionPool.GetRegion(VisibleContentRegion()); 782 BRegion* exposeContentRegion = fRegionPool.GetRegion(VisibleContentRegion()); 783 dirtyContentRegion->IntersectWith(&fDirtyRegion); 784 exposeContentRegion->IntersectWith(&fExposeRegion); 785 786 _TriggerContentRedraw(*dirtyContentRegion, *exposeContentRegion); 787 788 fRegionPool.Recycle(dirtyContentRegion); 789 fRegionPool.Recycle(exposeContentRegion); 790 } 791 792 // reset the dirty region, since 793 // we're fully clean. If the desktop 794 // thread wanted to mark something 795 // dirty in the mean time, it was 796 // blocking on the global region lock to 797 // get write access, since we're holding 798 // the read lock for the whole time. 799 fDirtyRegion.MakeEmpty(); 800 fExposeRegion.MakeEmpty(); 801 } 802 803 804 void 805 Window::MarkDirty(BRegion& regionOnScreen) 806 { 807 // for marking any part of the desktop dirty 808 // this will get write access to the global 809 // region lock, and result in ProcessDirtyRegion() 810 // to be called for any windows affected 811 if (fDesktop) 812 fDesktop->MarkDirty(regionOnScreen); 813 } 814 815 816 void 817 Window::MarkContentDirty(BRegion& dirtyRegion, BRegion& exposeRegion) 818 { 819 // for triggering AS_REDRAW 820 // since this won't affect other windows, read locking 821 // is sufficient. If there was no dirty region before, 822 // an update message is triggered 823 if (fHidden || IsOffscreenWindow()) 824 return; 825 826 dirtyRegion.IntersectWith(&VisibleContentRegion()); 827 exposeRegion.IntersectWith(&VisibleContentRegion()); 828 _TriggerContentRedraw(dirtyRegion, exposeRegion); 829 } 830 831 832 void 833 Window::MarkContentDirtyAsync(BRegion& dirtyRegion) 834 { 835 // NOTE: see comments in ProcessDirtyRegion() 836 if (fHidden || IsOffscreenWindow()) 837 return; 838 839 dirtyRegion.IntersectWith(&VisibleContentRegion()); 840 841 if (fDirtyRegion.CountRects() == 0) { 842 ServerWindow()->RequestRedraw(); 843 } 844 845 fDirtyRegion.Include(&dirtyRegion); 846 } 847 848 849 void 850 Window::InvalidateView(View* view, BRegion& viewRegion) 851 { 852 if (view && IsVisible() && view->IsVisible()) { 853 if (!fContentRegionValid) 854 _UpdateContentRegion(); 855 856 view->LocalToScreenTransform().Apply(&viewRegion); 857 viewRegion.IntersectWith(&VisibleContentRegion()); 858 if (viewRegion.CountRects() > 0) { 859 viewRegion.IntersectWith( 860 &view->ScreenAndUserClipping(&fContentRegion)); 861 862 //fDrawingEngine->FillRegion(viewRegion, rgb_color{ 0, 255, 0, 255 }); 863 //snooze(10000); 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& dirty, const BRegion& expose) 1771 { 1772 if (!IsVisible() || dirty.CountRects() == 0 || (fFlags & kWindowScreenFlag) != 0) 1773 return; 1774 1775 // put this into the pending dirty region 1776 // to eventually trigger a client redraw 1777 _TransferToUpdateSession(&dirty); 1778 1779 if (expose.CountRects() > 0) { 1780 // draw exposed region background right now to avoid stamping artifacts 1781 if (fDrawingEngine->LockParallelAccess()) { 1782 bool copyToFrontEnabled = fDrawingEngine->CopyToFrontEnabled(); 1783 fDrawingEngine->SetCopyToFrontEnabled(true); 1784 fDrawingEngine->SuspendAutoSync(); 1785 fTopView->Draw(fDrawingEngine.Get(), &expose, &fContentRegion, true); 1786 fDrawingEngine->Sync(); 1787 fDrawingEngine->SetCopyToFrontEnabled(copyToFrontEnabled); 1788 fDrawingEngine->UnlockParallelAccess(); 1789 } 1790 } 1791 } 1792 1793 1794 void 1795 Window::_DrawBorder() 1796 { 1797 // this is executed in the window thread, but only 1798 // in respond to a REDRAW message having been received, the 1799 // clipping lock is held for reading 1800 ::Decorator* decorator = Decorator(); 1801 if (!decorator) 1802 return; 1803 1804 // construct the region of the border that needs redrawing 1805 BRegion* dirtyBorderRegion = fRegionPool.GetRegion(); 1806 if (!dirtyBorderRegion) 1807 return; 1808 GetBorderRegion(dirtyBorderRegion); 1809 // intersect with our visible region 1810 dirtyBorderRegion->IntersectWith(&fVisibleRegion); 1811 // intersect with the dirty region 1812 dirtyBorderRegion->IntersectWith(&fDirtyRegion); 1813 1814 DrawingEngine* engine = decorator->GetDrawingEngine(); 1815 if (dirtyBorderRegion->CountRects() > 0 && engine->LockParallelAccess()) { 1816 engine->ConstrainClippingRegion(dirtyBorderRegion); 1817 bool copyToFrontEnabled = engine->CopyToFrontEnabled(); 1818 engine->SetCopyToFrontEnabled(false); 1819 1820 decorator->Draw(dirtyBorderRegion->Frame()); 1821 1822 engine->SetCopyToFrontEnabled(copyToFrontEnabled); 1823 engine->CopyToFront(*dirtyBorderRegion); 1824 1825 // TODO: remove this once the DrawState stuff is handled 1826 // more cleanly. The reason why this is needed is that 1827 // when the decorator draws strings, a draw state is set 1828 // on the Painter object, and this is were it might get 1829 // out of sync with what the ServerWindow things is the 1830 // current DrawState set on the Painter 1831 fWindow->ResyncDrawState(); 1832 1833 engine->UnlockParallelAccess(); 1834 } 1835 fRegionPool.Recycle(dirtyBorderRegion); 1836 } 1837 1838 1839 /*! pre: the clipping is readlocked (this function is 1840 only called from _TriggerContentRedraw()), which 1841 in turn is only called from MessageReceived() with 1842 the clipping lock held 1843 */ 1844 void 1845 Window::_TransferToUpdateSession(BRegion* contentDirtyRegion) 1846 { 1847 if (contentDirtyRegion->CountRects() <= 0) 1848 return; 1849 1850 //fDrawingEngine->FillRegion(*contentDirtyRegion, sPendingColor); 1851 //snooze(20000); 1852 1853 // add to pending 1854 fPendingUpdateSession->SetUsed(true); 1855 fPendingUpdateSession->Include(contentDirtyRegion); 1856 1857 if (!fUpdateRequested) { 1858 // send this to client 1859 _SendUpdateMessage(); 1860 // the pending region is now the current, 1861 // though the update does not start until 1862 // we received BEGIN_UPDATE from the client 1863 } 1864 } 1865 1866 1867 void 1868 Window::_SendUpdateMessage() 1869 { 1870 if (!fUpdatesEnabled) 1871 return; 1872 1873 BMessage message(_UPDATE_); 1874 if (ServerWindow()->SendMessageToClient(&message) != B_OK) { 1875 // If sending the message failed, we'll just keep adding to the dirty 1876 // region until sending was successful. 1877 // TODO: we might want to automatically resend this message in this case 1878 return; 1879 } 1880 1881 fUpdateRequested = true; 1882 fEffectiveDrawingRegionValid = false; 1883 } 1884 1885 1886 void 1887 Window::BeginUpdate(BPrivate::PortLink& link) 1888 { 1889 // NOTE: since we might "shift" parts of the 1890 // internal dirty regions from the desktop thread 1891 // in response to Window::ResizeBy(), which 1892 // might move arround views, the user of this function 1893 // needs to hold the global clipping lock so that the internal 1894 // dirty regions are not messed with from the Desktop thread 1895 // and ServerWindow thread at the same time. 1896 1897 if (!fUpdateRequested) { 1898 link.StartMessage(B_ERROR); 1899 link.Flush(); 1900 fprintf(stderr, "Window::BeginUpdate() - no update requested!\n"); 1901 return; 1902 } 1903 1904 // make the pending update session the current update session 1905 // (toggle the pointers) 1906 UpdateSession* temp = fCurrentUpdateSession; 1907 fCurrentUpdateSession = fPendingUpdateSession; 1908 fPendingUpdateSession = temp; 1909 fPendingUpdateSession->SetUsed(false); 1910 // all drawing command from the client 1911 // will have the dirty region from the update 1912 // session enforced 1913 fInUpdate = true; 1914 fEffectiveDrawingRegionValid = false; 1915 1916 // TODO: each view could be drawn individually 1917 // right before carrying out the first drawing 1918 // command from the client during an update 1919 // (View::IsBackgroundDirty() can be used 1920 // for this) 1921 if (!fContentRegionValid) 1922 _UpdateContentRegion(); 1923 1924 BRegion* dirty = fRegionPool.GetRegion( 1925 fCurrentUpdateSession->DirtyRegion()); 1926 if (!dirty) { 1927 link.StartMessage(B_ERROR); 1928 link.Flush(); 1929 return; 1930 } 1931 1932 dirty->IntersectWith(&VisibleContentRegion()); 1933 1934 //if (!fCurrentUpdateSession->IsExpose()) { 1935 ////sCurrentColor.red = rand() % 255; 1936 ////sCurrentColor.green = rand() % 255; 1937 ////sCurrentColor.blue = rand() % 255; 1938 ////sPendingColor.red = rand() % 255; 1939 ////sPendingColor.green = rand() % 255; 1940 ////sPendingColor.blue = rand() % 255; 1941 //fDrawingEngine->FillRegion(*dirty, sCurrentColor); 1942 //snooze(10000); 1943 //} 1944 1945 link.StartMessage(B_OK); 1946 // append the current window geometry to the 1947 // message, the client will need it 1948 link.Attach<BPoint>(fFrame.LeftTop()); 1949 link.Attach<float>(fFrame.Width()); 1950 link.Attach<float>(fFrame.Height()); 1951 // find and attach all views that intersect with 1952 // the dirty region 1953 fTopView->AddTokensForViewsInRegion(link, *dirty, &fContentRegion); 1954 // mark the end of the token "list" 1955 link.Attach<int32>(B_NULL_TOKEN); 1956 link.Flush(); 1957 1958 // supress back to front buffer copies in the drawing engine 1959 fDrawingEngine->SetCopyToFrontEnabled(false); 1960 1961 if (fDrawingEngine->LockParallelAccess()) { 1962 fDrawingEngine->SuspendAutoSync(); 1963 1964 fTopView->Draw(GetDrawingEngine(), dirty, &fContentRegion, true); 1965 1966 fDrawingEngine->Sync(); 1967 fDrawingEngine->UnlockParallelAccess(); 1968 } // else the background was cleared already 1969 1970 fRegionPool.Recycle(dirty); 1971 } 1972 1973 1974 void 1975 Window::EndUpdate() 1976 { 1977 // NOTE: see comment in _BeginUpdate() 1978 1979 if (fInUpdate) { 1980 // reenable copy to front 1981 fDrawingEngine->SetCopyToFrontEnabled(true); 1982 1983 BRegion* dirty = fRegionPool.GetRegion( 1984 fCurrentUpdateSession->DirtyRegion()); 1985 1986 if (dirty) { 1987 dirty->IntersectWith(&VisibleContentRegion()); 1988 1989 fDrawingEngine->CopyToFront(*dirty); 1990 fRegionPool.Recycle(dirty); 1991 } 1992 1993 fCurrentUpdateSession->SetUsed(false); 1994 1995 fInUpdate = false; 1996 fEffectiveDrawingRegionValid = false; 1997 } 1998 if (fPendingUpdateSession->IsUsed()) { 1999 // send this to client 2000 _SendUpdateMessage(); 2001 } else { 2002 fUpdateRequested = false; 2003 } 2004 } 2005 2006 2007 void 2008 Window::_UpdateContentRegion() 2009 { 2010 fContentRegion.Set(fFrame); 2011 2012 // resize handle 2013 ::Decorator* decorator = Decorator(); 2014 if (decorator) 2015 fContentRegion.Exclude(&decorator->GetFootprint()); 2016 2017 fContentRegionValid = true; 2018 } 2019 2020 2021 void 2022 Window::_ObeySizeLimits() 2023 { 2024 // make sure we even have valid size limits 2025 if (fMaxWidth < fMinWidth) 2026 fMaxWidth = fMinWidth; 2027 2028 if (fMaxHeight < fMinHeight) 2029 fMaxHeight = fMinHeight; 2030 2031 // Automatically resize the window to fit these new limits 2032 // if it does not already. 2033 2034 // On R5, Windows don't automatically resize, but since 2035 // BWindow::ResizeTo() even honors the limits, I would guess 2036 // this is a bug that we don't have to adopt. 2037 // Note that most current apps will do unnecessary resizing 2038 // after having set the limits, but the overhead is neglible. 2039 2040 float minWidthDiff = fMinWidth - fFrame.Width(); 2041 float minHeightDiff = fMinHeight - fFrame.Height(); 2042 float maxWidthDiff = fMaxWidth - fFrame.Width(); 2043 float maxHeightDiff = fMaxHeight - fFrame.Height(); 2044 2045 float xDiff = 0.0; 2046 if (minWidthDiff > 0.0) // we're currently smaller than minWidth 2047 xDiff = minWidthDiff; 2048 else if (maxWidthDiff < 0.0) // we're currently larger than maxWidth 2049 xDiff = maxWidthDiff; 2050 2051 float yDiff = 0.0; 2052 if (minHeightDiff > 0.0) // we're currently smaller than minHeight 2053 yDiff = minHeightDiff; 2054 else if (maxHeightDiff < 0.0) // we're currently larger than maxHeight 2055 yDiff = maxHeightDiff; 2056 2057 if (fDesktop) 2058 fDesktop->ResizeWindowBy(this, xDiff, yDiff); 2059 else 2060 ResizeBy((int32)xDiff, (int32)yDiff, NULL); 2061 } 2062 2063 2064 // #pragma mark - UpdateSession 2065 2066 2067 Window::UpdateSession::UpdateSession() 2068 : 2069 fDirtyRegion(), 2070 fInUse(false) 2071 { 2072 } 2073 2074 2075 void 2076 Window::UpdateSession::Include(BRegion* additionalDirty) 2077 { 2078 fDirtyRegion.Include(additionalDirty); 2079 } 2080 2081 2082 void 2083 Window::UpdateSession::Exclude(BRegion* dirtyInNextSession) 2084 { 2085 fDirtyRegion.Exclude(dirtyInNextSession); 2086 } 2087 2088 2089 void 2090 Window::UpdateSession::MoveBy(int32 x, int32 y) 2091 { 2092 fDirtyRegion.OffsetBy(x, y); 2093 } 2094 2095 2096 void 2097 Window::UpdateSession::SetUsed(bool used) 2098 { 2099 fInUse = used; 2100 if (!fInUse) 2101 fDirtyRegion.MakeEmpty(); 2102 } 2103 2104 2105 int32 2106 Window::PositionInStack() const 2107 { 2108 if (!fCurrentStack.IsSet()) 2109 return -1; 2110 return fCurrentStack->WindowList().IndexOf(this); 2111 } 2112 2113 2114 bool 2115 Window::DetachFromWindowStack(bool ownStackNeeded) 2116 { 2117 // The lock must normally be held but is not held when closing the window. 2118 //ASSERT_MULTI_WRITE_LOCKED(fDesktop->WindowLocker()); 2119 2120 if (!fCurrentStack.IsSet()) 2121 return false; 2122 if (fCurrentStack->CountWindows() == 1) 2123 return true; 2124 2125 int32 index = PositionInStack(); 2126 2127 if (fCurrentStack->RemoveWindow(this) == false) 2128 return false; 2129 2130 BRegion invalidatedRegion; 2131 ::Decorator* decorator = fCurrentStack->Decorator(); 2132 if (decorator != NULL) { 2133 decorator->RemoveTab(index, &invalidatedRegion); 2134 decorator->SetTopTab(fCurrentStack->LayerOrder().CountItems() - 1); 2135 } 2136 2137 Window* remainingTop = fCurrentStack->TopLayerWindow(); 2138 if (remainingTop != NULL) { 2139 if (decorator != NULL) 2140 decorator->SetDrawingEngine(remainingTop->GetDrawingEngine()); 2141 // propagate focus to the decorator 2142 remainingTop->SetFocus(remainingTop->IsFocus()); 2143 remainingTop->SetLook(remainingTop->Look(), NULL); 2144 } 2145 2146 fCurrentStack = NULL; 2147 if (ownStackNeeded == true) 2148 _InitWindowStack(); 2149 // propagate focus to the new decorator 2150 SetFocus(IsFocus()); 2151 2152 if (remainingTop != NULL) { 2153 invalidatedRegion.Include(&remainingTop->VisibleRegion()); 2154 fDesktop->RebuildAndRedrawAfterWindowChange(remainingTop, 2155 invalidatedRegion); 2156 } 2157 return true; 2158 } 2159 2160 2161 bool 2162 Window::AddWindowToStack(Window* window) 2163 { 2164 ASSERT_MULTI_WRITE_LOCKED(fDesktop->WindowLocker()); 2165 2166 WindowStack* stack = GetWindowStack(); 2167 if (stack == NULL) 2168 return false; 2169 2170 BRegion dirty; 2171 // move window to the own position 2172 BRect ownFrame = Frame(); 2173 BRect frame = window->Frame(); 2174 float deltaToX = round(ownFrame.left - frame.left); 2175 float deltaToY = round(ownFrame.top - frame.top); 2176 frame.OffsetBy(deltaToX, deltaToY); 2177 float deltaByX = round(ownFrame.right - frame.right); 2178 float deltaByY = round(ownFrame.bottom - frame.bottom); 2179 dirty.Include(&window->VisibleRegion()); 2180 window->MoveBy(deltaToX, deltaToY, false); 2181 window->ResizeBy(deltaByX, deltaByY, &dirty, false); 2182 2183 // first collect dirt from the window to add 2184 ::Decorator* otherDecorator = window->Decorator(); 2185 if (otherDecorator != NULL) 2186 dirty.Include(otherDecorator->TitleBarRect()); 2187 ::Decorator* decorator = stack->Decorator(); 2188 if (decorator != NULL) 2189 dirty.Include(decorator->TitleBarRect()); 2190 2191 int32 position = PositionInStack() + 1; 2192 if (position >= stack->CountWindows()) 2193 position = -1; 2194 if (stack->AddWindow(window, position) == false) 2195 return false; 2196 window->DetachFromWindowStack(false); 2197 window->fCurrentStack.SetTo(stack); 2198 2199 if (decorator != NULL) { 2200 DesktopSettings settings(fDesktop); 2201 decorator->AddTab(settings, window->Title(), window->Look(), 2202 window->Flags(), position, &dirty); 2203 } 2204 2205 window->SetLook(window->Look(), &dirty); 2206 fDesktop->RebuildAndRedrawAfterWindowChange(TopLayerStackWindow(), dirty); 2207 window->SetFocus(window->IsFocus()); 2208 return true; 2209 } 2210 2211 2212 Window* 2213 Window::StackedWindowAt(const BPoint& where) 2214 { 2215 ::Decorator* decorator = Decorator(); 2216 if (decorator == NULL) 2217 return this; 2218 2219 int tab = decorator->TabAt(where); 2220 // if we have a decorator we also have a stack 2221 Window* window = fCurrentStack->WindowAt(tab); 2222 if (window != NULL) 2223 return window; 2224 return this; 2225 } 2226 2227 2228 Window* 2229 Window::TopLayerStackWindow() 2230 { 2231 if (!fCurrentStack.IsSet()) 2232 return this; 2233 return fCurrentStack->TopLayerWindow(); 2234 } 2235 2236 2237 WindowStack* 2238 Window::GetWindowStack() 2239 { 2240 if (!fCurrentStack.IsSet()) 2241 return _InitWindowStack(); 2242 return fCurrentStack; 2243 } 2244 2245 2246 bool 2247 Window::MoveToTopStackLayer() 2248 { 2249 ::Decorator* decorator = Decorator(); 2250 if (decorator == NULL) 2251 return false; 2252 decorator->SetDrawingEngine(GetDrawingEngine()); 2253 SetLook(Look(), NULL); 2254 decorator->SetTopTab(PositionInStack()); 2255 return fCurrentStack->MoveToTopLayer(this); 2256 } 2257 2258 2259 bool 2260 Window::MoveToStackPosition(int32 to, bool isMoving) 2261 { 2262 if (!fCurrentStack.IsSet()) 2263 return false; 2264 int32 index = PositionInStack(); 2265 if (fCurrentStack->Move(index, to) == false) 2266 return false; 2267 2268 BRegion dirty; 2269 ::Decorator* decorator = Decorator(); 2270 if (decorator && decorator->MoveTab(index, to, isMoving, &dirty) == false) 2271 return false; 2272 2273 fDesktop->RebuildAndRedrawAfterWindowChange(this, dirty); 2274 return true; 2275 } 2276 2277 2278 WindowStack* 2279 Window::_InitWindowStack() 2280 { 2281 fCurrentStack = NULL; 2282 ::Decorator* decorator = NULL; 2283 if (fLook != B_NO_BORDER_WINDOW_LOOK) 2284 decorator = gDecorManager.AllocateDecorator(this); 2285 2286 WindowStack* stack = new(std::nothrow) WindowStack(decorator); 2287 if (stack == NULL) 2288 return NULL; 2289 2290 if (stack->AddWindow(this) != true) { 2291 delete stack; 2292 return NULL; 2293 } 2294 fCurrentStack.SetTo(stack, true); 2295 return stack; 2296 } 2297 2298 2299 WindowStack::WindowStack(::Decorator* decorator) 2300 : 2301 fDecorator(decorator) 2302 { 2303 2304 } 2305 2306 2307 WindowStack::~WindowStack() 2308 { 2309 } 2310 2311 2312 void 2313 WindowStack::SetDecorator(::Decorator* decorator) 2314 { 2315 fDecorator.SetTo(decorator); 2316 } 2317 2318 2319 ::Decorator* 2320 WindowStack::Decorator() 2321 { 2322 return fDecorator.Get(); 2323 } 2324 2325 2326 Window* 2327 WindowStack::TopLayerWindow() const 2328 { 2329 return fWindowLayerOrder.ItemAt(fWindowLayerOrder.CountItems() - 1); 2330 } 2331 2332 2333 int32 2334 WindowStack::CountWindows() 2335 { 2336 return fWindowList.CountItems(); 2337 } 2338 2339 2340 Window* 2341 WindowStack::WindowAt(int32 index) 2342 { 2343 return fWindowList.ItemAt(index); 2344 } 2345 2346 2347 bool 2348 WindowStack::AddWindow(Window* window, int32 position) 2349 { 2350 if (position >= 0) { 2351 if (fWindowList.AddItem(window, position) == false) 2352 return false; 2353 } else if (fWindowList.AddItem(window) == false) 2354 return false; 2355 2356 if (fWindowLayerOrder.AddItem(window) == false) { 2357 fWindowList.RemoveItem(window); 2358 return false; 2359 } 2360 return true; 2361 } 2362 2363 2364 bool 2365 WindowStack::RemoveWindow(Window* window) 2366 { 2367 if (fWindowList.RemoveItem(window) == false) 2368 return false; 2369 2370 fWindowLayerOrder.RemoveItem(window); 2371 return true; 2372 } 2373 2374 2375 bool 2376 WindowStack::MoveToTopLayer(Window* window) 2377 { 2378 int32 index = fWindowLayerOrder.IndexOf(window); 2379 return fWindowLayerOrder.MoveItem(index, 2380 fWindowLayerOrder.CountItems() - 1); 2381 } 2382 2383 2384 bool 2385 WindowStack::Move(int32 from, int32 to) 2386 { 2387 return fWindowList.MoveItem(from, to); 2388 } 2389