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