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