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->LocalToScreenTransform().Apply(&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 904 // Activate or focus the window in case it doesn't accept first 905 // click, depending on the mouse mode 906 if (!acceptFirstClick) { 907 bool avoidFocus = (Flags() & B_AVOID_FOCUS) != 0; 908 DesktopSettings desktopSettings(fDesktop); 909 if (desktopSettings.MouseMode() == B_NORMAL_MOUSE) 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 (!desktopSettings.AcceptFirstClick() && !avoidFocus) 921 return; 922 } 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::ColorsChanged(BRegion* updateRegion) 1247 { 1248 ::Decorator* decorator = Decorator(); 1249 if (decorator != NULL) { 1250 DesktopSettings settings(fDesktop); 1251 decorator->ColorsChanged(settings, updateRegion); 1252 } 1253 } 1254 1255 1256 void 1257 Window::SetLook(window_look look, BRegion* updateRegion) 1258 { 1259 fLook = look; 1260 1261 fContentRegionValid = false; 1262 // mabye a resize handle was added... 1263 fEffectiveDrawingRegionValid = false; 1264 // ...and therefor the drawing region is 1265 // likely not valid anymore either 1266 1267 if (fCurrentStack.Get() == NULL) 1268 return; 1269 1270 int32 stackPosition = PositionInStack(); 1271 1272 ::Decorator* decorator = Decorator(); 1273 if (decorator == NULL && look != B_NO_BORDER_WINDOW_LOOK) { 1274 // we need a new decorator 1275 decorator = gDecorManager.AllocateDecorator(this); 1276 fCurrentStack->SetDecorator(decorator); 1277 if (IsFocus()) 1278 decorator->SetFocus(stackPosition, true); 1279 } 1280 1281 if (decorator != NULL) { 1282 DesktopSettings settings(fDesktop); 1283 decorator->SetLook(stackPosition, settings, look, updateRegion); 1284 1285 // we might need to resize the window! 1286 decorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth, 1287 &fMaxHeight); 1288 _ObeySizeLimits(); 1289 } 1290 1291 if (look == B_NO_BORDER_WINDOW_LOOK) { 1292 // we don't need a decorator for this window 1293 fCurrentStack->SetDecorator(NULL); 1294 } 1295 } 1296 1297 1298 void 1299 Window::SetFeel(window_feel feel) 1300 { 1301 // if the subset list is no longer needed, clear it 1302 if ((fFeel == B_MODAL_SUBSET_WINDOW_FEEL 1303 || fFeel == B_FLOATING_SUBSET_WINDOW_FEEL) 1304 && feel != B_MODAL_SUBSET_WINDOW_FEEL 1305 && feel != B_FLOATING_SUBSET_WINDOW_FEEL) 1306 fSubsets.MakeEmpty(); 1307 1308 fFeel = feel; 1309 1310 // having modal windows with B_AVOID_FRONT or B_AVOID_FOCUS doesn't 1311 // make that much sense, so we filter those flags out on demand 1312 fFlags = fOriginalFlags; 1313 fFlags &= ValidWindowFlags(fFeel); 1314 1315 if (!IsNormal()) { 1316 fFlags |= B_SAME_POSITION_IN_ALL_WORKSPACES; 1317 _PropagatePosition(); 1318 } 1319 } 1320 1321 1322 void 1323 Window::SetFlags(uint32 flags, BRegion* updateRegion) 1324 { 1325 fOriginalFlags = flags; 1326 fFlags = flags & ValidWindowFlags(fFeel); 1327 if (!IsNormal()) 1328 fFlags |= B_SAME_POSITION_IN_ALL_WORKSPACES; 1329 1330 if ((fFlags & B_SAME_POSITION_IN_ALL_WORKSPACES) != 0) 1331 _PropagatePosition(); 1332 1333 ::Decorator* decorator = Decorator(); 1334 if (decorator == NULL) 1335 return; 1336 1337 int32 stackPosition = PositionInStack(); 1338 decorator->SetFlags(stackPosition, flags, updateRegion); 1339 1340 // we might need to resize the window! 1341 decorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth, &fMaxHeight); 1342 _ObeySizeLimits(); 1343 1344 // TODO: not sure if we want to do this 1345 #if 0 1346 if ((fOriginalFlags & kWindowScreenFlag) != (flags & kWindowScreenFlag)) { 1347 // TODO: disabling needs to be nestable (or we might lose the previous 1348 // update state) 1349 if ((flags & kWindowScreenFlag) != 0) 1350 DisableUpdateRequests(); 1351 else 1352 EnableUpdateRequests(); 1353 } 1354 #endif 1355 } 1356 1357 1358 /*! Returns whether or not a window is in the workspace list with the 1359 specified \a index. 1360 */ 1361 bool 1362 Window::InWorkspace(int32 index) const 1363 { 1364 return (fWorkspaces & (1UL << index)) != 0; 1365 } 1366 1367 1368 bool 1369 Window::SupportsFront() 1370 { 1371 if (fFeel == kDesktopWindowFeel 1372 || fFeel == kMenuWindowFeel 1373 || (fFlags & B_AVOID_FRONT) != 0) 1374 return false; 1375 1376 return true; 1377 } 1378 1379 1380 bool 1381 Window::IsModal() const 1382 { 1383 return IsModalFeel(fFeel); 1384 } 1385 1386 1387 bool 1388 Window::IsFloating() const 1389 { 1390 return IsFloatingFeel(fFeel); 1391 } 1392 1393 1394 bool 1395 Window::IsNormal() const 1396 { 1397 return !IsFloatingFeel(fFeel) && !IsModalFeel(fFeel); 1398 } 1399 1400 1401 bool 1402 Window::HasModal() const 1403 { 1404 for (Window* window = NextWindow(fCurrentWorkspace); window != NULL; 1405 window = window->NextWindow(fCurrentWorkspace)) { 1406 if (window->IsHidden() || !window->IsModal()) 1407 continue; 1408 1409 if (window->HasInSubset(this)) 1410 return true; 1411 } 1412 1413 return false; 1414 } 1415 1416 1417 /*! \brief Returns the windows that's in behind of the backmost position 1418 this window can get. 1419 Returns NULL is this window can be the backmost window. 1420 1421 \param workspace the workspace on which this check should be made. If 1422 the value is -1, the window's current workspace will be used. 1423 */ 1424 Window* 1425 Window::Backmost(Window* window, int32 workspace) 1426 { 1427 if (workspace == -1) 1428 workspace = fCurrentWorkspace; 1429 1430 ASSERT(workspace != -1); 1431 if (workspace == -1) 1432 return NULL; 1433 1434 // Desktop windows are always backmost 1435 if (fFeel == kDesktopWindowFeel) 1436 return NULL; 1437 1438 if (window == NULL) 1439 window = PreviousWindow(workspace); 1440 1441 for (; window != NULL; window = window->PreviousWindow(workspace)) { 1442 if (window->IsHidden() || window == this) 1443 continue; 1444 1445 if (HasInSubset(window)) 1446 return window; 1447 } 1448 1449 return NULL; 1450 } 1451 1452 1453 /*! \brief Returns the window that's in front of the frontmost position 1454 this window can get. 1455 Returns NULL if this window can be the frontmost window. 1456 1457 \param workspace the workspace on which this check should be made. If 1458 the value is -1, the window's current workspace will be used. 1459 */ 1460 Window* 1461 Window::Frontmost(Window* first, int32 workspace) 1462 { 1463 if (workspace == -1) 1464 workspace = fCurrentWorkspace; 1465 1466 ASSERT(workspace != -1); 1467 if (workspace == -1) 1468 return NULL; 1469 1470 if (fFeel == kDesktopWindowFeel) 1471 return first ? first : NextWindow(workspace); 1472 1473 if (first == NULL) 1474 first = NextWindow(workspace); 1475 1476 for (Window* window = first; window != NULL; 1477 window = window->NextWindow(workspace)) { 1478 if (window->IsHidden() || window == this) 1479 continue; 1480 1481 if (window->HasInSubset(this)) 1482 return window; 1483 } 1484 1485 return NULL; 1486 } 1487 1488 1489 bool 1490 Window::AddToSubset(Window* window) 1491 { 1492 return fSubsets.AddItem(window); 1493 } 1494 1495 1496 void 1497 Window::RemoveFromSubset(Window* window) 1498 { 1499 fSubsets.RemoveItem(window); 1500 } 1501 1502 1503 /*! Returns whether or not a window is in the subset of this window. 1504 If a window is in the subset of this window, it means it should always 1505 appear behind this window. 1506 */ 1507 bool 1508 Window::HasInSubset(const Window* window) const 1509 { 1510 if (window == NULL || fFeel == window->Feel() 1511 || fFeel == B_NORMAL_WINDOW_FEEL) 1512 return false; 1513 1514 // Menus are a special case: they will always be on-top of every window 1515 // of their application 1516 if (fFeel == kMenuWindowFeel) 1517 return window->ServerWindow()->App() == ServerWindow()->App(); 1518 if (window->Feel() == kMenuWindowFeel) 1519 return false; 1520 1521 // we have a few special feels that have a fixed order 1522 1523 const int32 kFeels[] = {kPasswordWindowFeel, kWindowScreenFeel, 1524 B_MODAL_ALL_WINDOW_FEEL, B_FLOATING_ALL_WINDOW_FEEL}; 1525 1526 for (uint32 order = 0; 1527 order < sizeof(kFeels) / sizeof(kFeels[0]); order++) { 1528 if (fFeel == kFeels[order]) 1529 return true; 1530 if (window->Feel() == kFeels[order]) 1531 return false; 1532 } 1533 1534 if ((fFeel == B_FLOATING_APP_WINDOW_FEEL 1535 && window->Feel() != B_MODAL_APP_WINDOW_FEEL) 1536 || fFeel == B_MODAL_APP_WINDOW_FEEL) 1537 return window->ServerWindow()->App() == ServerWindow()->App(); 1538 1539 return fSubsets.HasItem(window); 1540 } 1541 1542 1543 /*! \brief Collects all workspaces views in this window and puts it into \a list 1544 */ 1545 void 1546 Window::FindWorkspacesViews(BObjectList<WorkspacesView>& list) const 1547 { 1548 int32 count = fWorkspacesViewCount; 1549 fTopView->FindViews(kWorkspacesViewFlag, (BObjectList<View>&)list, count); 1550 } 1551 1552 1553 /*! \brief Returns on which workspaces the window should be visible. 1554 1555 A modal or floating window may be visible on a workspace if one 1556 of its subset windows is visible there. Floating windows also need 1557 to have a subset as front window to be visible. 1558 */ 1559 uint32 1560 Window::SubsetWorkspaces() const 1561 { 1562 if (fFeel == B_MODAL_ALL_WINDOW_FEEL 1563 || fFeel == B_FLOATING_ALL_WINDOW_FEEL) 1564 return B_ALL_WORKSPACES; 1565 1566 if (fFeel == B_FLOATING_APP_WINDOW_FEEL) { 1567 Window* front = fDesktop->FrontWindow(); 1568 if (front != NULL && front->IsNormal() 1569 && front->ServerWindow()->App() == ServerWindow()->App()) 1570 return ServerWindow()->App()->Workspaces(); 1571 1572 return 0; 1573 } 1574 1575 if (fFeel == B_MODAL_APP_WINDOW_FEEL) { 1576 uint32 workspaces = ServerWindow()->App()->Workspaces(); 1577 if (workspaces == 0) { 1578 // The application doesn't seem to have any other windows 1579 // open or visible - but we'd like to see modal windows 1580 // anyway, at least when they are first opened. 1581 return 1UL << fDesktop->CurrentWorkspace(); 1582 } 1583 return workspaces; 1584 } 1585 1586 if (fFeel == B_MODAL_SUBSET_WINDOW_FEEL 1587 || fFeel == B_FLOATING_SUBSET_WINDOW_FEEL) { 1588 uint32 workspaces = 0; 1589 bool hasNormalFront = false; 1590 for (int32 i = 0; i < fSubsets.CountItems(); i++) { 1591 Window* window = fSubsets.ItemAt(i); 1592 1593 if (!window->IsHidden()) 1594 workspaces |= window->Workspaces(); 1595 if (window == fDesktop->FrontWindow() && window->IsNormal()) 1596 hasNormalFront = true; 1597 } 1598 1599 if (fFeel == B_FLOATING_SUBSET_WINDOW_FEEL && !hasNormalFront) 1600 return 0; 1601 1602 return workspaces; 1603 } 1604 1605 return 0; 1606 } 1607 1608 1609 /*! Returns whether or not a window is in the subset workspace list with the 1610 specified \a index. 1611 See SubsetWorkspaces(). 1612 */ 1613 bool 1614 Window::InSubsetWorkspace(int32 index) const 1615 { 1616 return (SubsetWorkspaces() & (1UL << index)) != 0; 1617 } 1618 1619 1620 // #pragma mark - static 1621 1622 1623 /*static*/ bool 1624 Window::IsValidLook(window_look look) 1625 { 1626 return look == B_TITLED_WINDOW_LOOK 1627 || look == B_DOCUMENT_WINDOW_LOOK 1628 || look == B_MODAL_WINDOW_LOOK 1629 || look == B_FLOATING_WINDOW_LOOK 1630 || look == B_BORDERED_WINDOW_LOOK 1631 || look == B_NO_BORDER_WINDOW_LOOK 1632 || look == kDesktopWindowLook 1633 || look == kLeftTitledWindowLook; 1634 } 1635 1636 1637 /*static*/ bool 1638 Window::IsValidFeel(window_feel feel) 1639 { 1640 return feel == B_NORMAL_WINDOW_FEEL 1641 || feel == B_MODAL_SUBSET_WINDOW_FEEL 1642 || feel == B_MODAL_APP_WINDOW_FEEL 1643 || feel == B_MODAL_ALL_WINDOW_FEEL 1644 || feel == B_FLOATING_SUBSET_WINDOW_FEEL 1645 || feel == B_FLOATING_APP_WINDOW_FEEL 1646 || feel == B_FLOATING_ALL_WINDOW_FEEL 1647 || feel == kDesktopWindowFeel 1648 || feel == kMenuWindowFeel 1649 || feel == kWindowScreenFeel 1650 || feel == kPasswordWindowFeel 1651 || feel == kOffscreenWindowFeel; 1652 } 1653 1654 1655 /*static*/ bool 1656 Window::IsModalFeel(window_feel feel) 1657 { 1658 return feel == B_MODAL_SUBSET_WINDOW_FEEL 1659 || feel == B_MODAL_APP_WINDOW_FEEL 1660 || feel == B_MODAL_ALL_WINDOW_FEEL; 1661 } 1662 1663 1664 /*static*/ bool 1665 Window::IsFloatingFeel(window_feel feel) 1666 { 1667 return feel == B_FLOATING_SUBSET_WINDOW_FEEL 1668 || feel == B_FLOATING_APP_WINDOW_FEEL 1669 || feel == B_FLOATING_ALL_WINDOW_FEEL; 1670 } 1671 1672 1673 /*static*/ uint32 1674 Window::ValidWindowFlags() 1675 { 1676 return B_NOT_MOVABLE 1677 | B_NOT_CLOSABLE 1678 | B_NOT_ZOOMABLE 1679 | B_NOT_MINIMIZABLE 1680 | B_NOT_RESIZABLE 1681 | B_NOT_H_RESIZABLE 1682 | B_NOT_V_RESIZABLE 1683 | B_AVOID_FRONT 1684 | B_AVOID_FOCUS 1685 | B_WILL_ACCEPT_FIRST_CLICK 1686 | B_OUTLINE_RESIZE 1687 | B_NO_WORKSPACE_ACTIVATION 1688 | B_NOT_ANCHORED_ON_ACTIVATE 1689 | B_ASYNCHRONOUS_CONTROLS 1690 | B_QUIT_ON_WINDOW_CLOSE 1691 | B_SAME_POSITION_IN_ALL_WORKSPACES 1692 | B_AUTO_UPDATE_SIZE_LIMITS 1693 | B_CLOSE_ON_ESCAPE 1694 | B_NO_SERVER_SIDE_WINDOW_MODIFIERS 1695 | kWindowScreenFlag 1696 | kAcceptKeyboardFocusFlag; 1697 } 1698 1699 1700 /*static*/ uint32 1701 Window::ValidWindowFlags(window_feel feel) 1702 { 1703 uint32 flags = ValidWindowFlags(); 1704 if (IsModalFeel(feel)) 1705 return flags & ~(B_AVOID_FOCUS | B_AVOID_FRONT); 1706 1707 return flags; 1708 } 1709 1710 1711 // #pragma mark - private 1712 1713 1714 void 1715 Window::_ShiftPartOfRegion(BRegion* region, BRegion* regionToShift, 1716 int32 xOffset, int32 yOffset) 1717 { 1718 BRegion* common = fRegionPool.GetRegion(*regionToShift); 1719 if (!common) 1720 return; 1721 // see if there is a common part at all 1722 common->IntersectWith(region); 1723 if (common->CountRects() > 0) { 1724 // cut the common part from the region, 1725 // offset that to destination and include again 1726 region->Exclude(common); 1727 common->OffsetBy(xOffset, yOffset); 1728 region->Include(common); 1729 } 1730 fRegionPool.Recycle(common); 1731 } 1732 1733 1734 void 1735 Window::_TriggerContentRedraw(BRegion& dirtyContentRegion) 1736 { 1737 if (!IsVisible() || dirtyContentRegion.CountRects() == 0 1738 || (fFlags & kWindowScreenFlag) != 0) 1739 return; 1740 1741 // put this into the pending dirty region 1742 // to eventually trigger a client redraw 1743 1744 _TransferToUpdateSession(&dirtyContentRegion); 1745 } 1746 1747 1748 void 1749 Window::_DrawBorder() 1750 { 1751 // this is executed in the window thread, but only 1752 // in respond to a REDRAW message having been received, the 1753 // clipping lock is held for reading 1754 ::Decorator* decorator = Decorator(); 1755 if (!decorator) 1756 return; 1757 1758 // construct the region of the border that needs redrawing 1759 BRegion* dirtyBorderRegion = fRegionPool.GetRegion(); 1760 if (!dirtyBorderRegion) 1761 return; 1762 GetBorderRegion(dirtyBorderRegion); 1763 // intersect with our visible region 1764 dirtyBorderRegion->IntersectWith(&fVisibleRegion); 1765 // intersect with the dirty region 1766 dirtyBorderRegion->IntersectWith(&fDirtyRegion); 1767 1768 DrawingEngine* engine = decorator->GetDrawingEngine(); 1769 if (dirtyBorderRegion->CountRects() > 0 && engine->LockParallelAccess()) { 1770 engine->ConstrainClippingRegion(dirtyBorderRegion); 1771 bool copyToFrontEnabled = engine->CopyToFrontEnabled(); 1772 engine->SetCopyToFrontEnabled(false); 1773 1774 decorator->Draw(dirtyBorderRegion->Frame()); 1775 1776 engine->SetCopyToFrontEnabled(copyToFrontEnabled); 1777 engine->CopyToFront(*dirtyBorderRegion); 1778 1779 // TODO: remove this once the DrawState stuff is handled 1780 // more cleanly. The reason why this is needed is that 1781 // when the decorator draws strings, a draw state is set 1782 // on the Painter object, and this is were it might get 1783 // out of sync with what the ServerWindow things is the 1784 // current DrawState set on the Painter 1785 fWindow->ResyncDrawState(); 1786 1787 engine->UnlockParallelAccess(); 1788 } 1789 fRegionPool.Recycle(dirtyBorderRegion); 1790 } 1791 1792 1793 /*! pre: the clipping is readlocked (this function is 1794 only called from _TriggerContentRedraw()), which 1795 in turn is only called from MessageReceived() with 1796 the clipping lock held 1797 */ 1798 void 1799 Window::_TransferToUpdateSession(BRegion* contentDirtyRegion) 1800 { 1801 if (contentDirtyRegion->CountRects() <= 0) 1802 return; 1803 1804 //fDrawingEngine->FillRegion(*contentDirtyRegion, sPendingColor); 1805 //snooze(20000); 1806 1807 // add to pending 1808 fPendingUpdateSession->SetUsed(true); 1809 // if (!fPendingUpdateSession->IsExpose()) 1810 fPendingUpdateSession->AddCause(fDirtyCause); 1811 fPendingUpdateSession->Include(contentDirtyRegion); 1812 1813 if (!fUpdateRequested) { 1814 // send this to client 1815 _SendUpdateMessage(); 1816 // the pending region is now the current, 1817 // though the update does not start until 1818 // we received BEGIN_UPDATE from the client 1819 } 1820 } 1821 1822 1823 void 1824 Window::_SendUpdateMessage() 1825 { 1826 if (!fUpdatesEnabled) 1827 return; 1828 1829 BMessage message(_UPDATE_); 1830 if (ServerWindow()->SendMessageToClient(&message) != B_OK) { 1831 // If sending the message failed, we'll just keep adding to the dirty 1832 // region until sending was successful. 1833 // TODO: we might want to automatically resend this message in this case 1834 return; 1835 } 1836 1837 fUpdateRequested = true; 1838 fEffectiveDrawingRegionValid = false; 1839 } 1840 1841 1842 void 1843 Window::BeginUpdate(BPrivate::PortLink& link) 1844 { 1845 // NOTE: since we might "shift" parts of the 1846 // internal dirty regions from the desktop thread 1847 // in response to Window::ResizeBy(), which 1848 // might move arround views, the user of this function 1849 // needs to hold the global clipping lock so that the internal 1850 // dirty regions are not messed with from the Desktop thread 1851 // and ServerWindow thread at the same time. 1852 1853 if (!fUpdateRequested) { 1854 link.StartMessage(B_ERROR); 1855 link.Flush(); 1856 fprintf(stderr, "Window::BeginUpdate() - no update requested!\n"); 1857 return; 1858 } 1859 1860 // make the pending update session the current update session 1861 // (toggle the pointers) 1862 UpdateSession* temp = fCurrentUpdateSession; 1863 fCurrentUpdateSession = fPendingUpdateSession; 1864 fPendingUpdateSession = temp; 1865 fPendingUpdateSession->SetUsed(false); 1866 // all drawing command from the client 1867 // will have the dirty region from the update 1868 // session enforced 1869 fInUpdate = true; 1870 fEffectiveDrawingRegionValid = false; 1871 1872 // TODO: each view could be drawn individually 1873 // right before carrying out the first drawing 1874 // command from the client during an update 1875 // (View::IsBackgroundDirty() can be used 1876 // for this) 1877 if (!fContentRegionValid) 1878 _UpdateContentRegion(); 1879 1880 BRegion* dirty = fRegionPool.GetRegion( 1881 fCurrentUpdateSession->DirtyRegion()); 1882 if (!dirty) { 1883 link.StartMessage(B_ERROR); 1884 link.Flush(); 1885 return; 1886 } 1887 1888 dirty->IntersectWith(&VisibleContentRegion()); 1889 1890 //if (!fCurrentUpdateSession->IsExpose()) { 1891 ////sCurrentColor.red = rand() % 255; 1892 ////sCurrentColor.green = rand() % 255; 1893 ////sCurrentColor.blue = rand() % 255; 1894 ////sPendingColor.red = rand() % 255; 1895 ////sPendingColor.green = rand() % 255; 1896 ////sPendingColor.blue = rand() % 255; 1897 //fDrawingEngine->FillRegion(*dirty, sCurrentColor); 1898 //snooze(10000); 1899 //} 1900 1901 link.StartMessage(B_OK); 1902 // append the current window geometry to the 1903 // message, the client will need it 1904 link.Attach<BPoint>(fFrame.LeftTop()); 1905 link.Attach<float>(fFrame.Width()); 1906 link.Attach<float>(fFrame.Height()); 1907 // find and attach all views that intersect with 1908 // the dirty region 1909 fTopView->AddTokensForViewsInRegion(link, *dirty, &fContentRegion); 1910 // mark the end of the token "list" 1911 link.Attach<int32>(B_NULL_TOKEN); 1912 link.Flush(); 1913 1914 // supress back to front buffer copies in the drawing engine 1915 fDrawingEngine->SetCopyToFrontEnabled(false); 1916 1917 if (fDrawingEngine->LockParallelAccess()) { 1918 fDrawingEngine->SuspendAutoSync(); 1919 1920 fTopView->Draw(fDrawingEngine, dirty, &fContentRegion, true); 1921 1922 fDrawingEngine->Sync(); 1923 fDrawingEngine->UnlockParallelAccess(); 1924 } // else the background was cleared already 1925 1926 fRegionPool.Recycle(dirty); 1927 } 1928 1929 1930 void 1931 Window::EndUpdate() 1932 { 1933 // NOTE: see comment in _BeginUpdate() 1934 1935 if (fInUpdate) { 1936 // reenable copy to front 1937 fDrawingEngine->SetCopyToFrontEnabled(true); 1938 1939 BRegion* dirty = fRegionPool.GetRegion( 1940 fCurrentUpdateSession->DirtyRegion()); 1941 1942 if (dirty) { 1943 dirty->IntersectWith(&VisibleContentRegion()); 1944 1945 fDrawingEngine->CopyToFront(*dirty); 1946 fRegionPool.Recycle(dirty); 1947 } 1948 1949 fCurrentUpdateSession->SetUsed(false); 1950 1951 fInUpdate = false; 1952 fEffectiveDrawingRegionValid = false; 1953 } 1954 if (fPendingUpdateSession->IsUsed()) { 1955 // send this to client 1956 _SendUpdateMessage(); 1957 } else { 1958 fUpdateRequested = false; 1959 } 1960 } 1961 1962 1963 void 1964 Window::_UpdateContentRegion() 1965 { 1966 fContentRegion.Set(fFrame); 1967 1968 // resize handle 1969 ::Decorator* decorator = Decorator(); 1970 if (decorator) 1971 fContentRegion.Exclude(&decorator->GetFootprint()); 1972 1973 fContentRegionValid = true; 1974 } 1975 1976 1977 void 1978 Window::_ObeySizeLimits() 1979 { 1980 // make sure we even have valid size limits 1981 if (fMaxWidth < fMinWidth) 1982 fMaxWidth = fMinWidth; 1983 1984 if (fMaxHeight < fMinHeight) 1985 fMaxHeight = fMinHeight; 1986 1987 // Automatically resize the window to fit these new limits 1988 // if it does not already. 1989 1990 // On R5, Windows don't automatically resize, but since 1991 // BWindow::ResizeTo() even honors the limits, I would guess 1992 // this is a bug that we don't have to adopt. 1993 // Note that most current apps will do unnecessary resizing 1994 // after having set the limits, but the overhead is neglible. 1995 1996 float minWidthDiff = fMinWidth - fFrame.Width(); 1997 float minHeightDiff = fMinHeight - fFrame.Height(); 1998 float maxWidthDiff = fMaxWidth - fFrame.Width(); 1999 float maxHeightDiff = fMaxHeight - fFrame.Height(); 2000 2001 float xDiff = 0.0; 2002 if (minWidthDiff > 0.0) // we're currently smaller than minWidth 2003 xDiff = minWidthDiff; 2004 else if (maxWidthDiff < 0.0) // we're currently larger than maxWidth 2005 xDiff = maxWidthDiff; 2006 2007 float yDiff = 0.0; 2008 if (minHeightDiff > 0.0) // we're currently smaller than minHeight 2009 yDiff = minHeightDiff; 2010 else if (maxHeightDiff < 0.0) // we're currently larger than maxHeight 2011 yDiff = maxHeightDiff; 2012 2013 if (fDesktop) 2014 fDesktop->ResizeWindowBy(this, xDiff, yDiff); 2015 else 2016 ResizeBy((int32)xDiff, (int32)yDiff, NULL); 2017 } 2018 2019 2020 // #pragma mark - UpdateSession 2021 2022 2023 Window::UpdateSession::UpdateSession() 2024 : 2025 fDirtyRegion(), 2026 fInUse(false), 2027 fCause(0) 2028 { 2029 } 2030 2031 2032 Window::UpdateSession::~UpdateSession() 2033 { 2034 } 2035 2036 2037 void 2038 Window::UpdateSession::Include(BRegion* additionalDirty) 2039 { 2040 fDirtyRegion.Include(additionalDirty); 2041 } 2042 2043 2044 void 2045 Window::UpdateSession::Exclude(BRegion* dirtyInNextSession) 2046 { 2047 fDirtyRegion.Exclude(dirtyInNextSession); 2048 } 2049 2050 2051 void 2052 Window::UpdateSession::MoveBy(int32 x, int32 y) 2053 { 2054 fDirtyRegion.OffsetBy(x, y); 2055 } 2056 2057 2058 void 2059 Window::UpdateSession::SetUsed(bool used) 2060 { 2061 fInUse = used; 2062 if (!fInUse) { 2063 fDirtyRegion.MakeEmpty(); 2064 fCause = 0; 2065 } 2066 } 2067 2068 2069 void 2070 Window::UpdateSession::AddCause(uint8 cause) 2071 { 2072 fCause |= cause; 2073 } 2074 2075 2076 int32 2077 Window::PositionInStack() const 2078 { 2079 if (fCurrentStack.Get() == NULL) 2080 return -1; 2081 return fCurrentStack->WindowList().IndexOf(this); 2082 } 2083 2084 2085 bool 2086 Window::DetachFromWindowStack(bool ownStackNeeded) 2087 { 2088 // The lock must normally be held but is not held when closing the window. 2089 //ASSERT_MULTI_WRITE_LOCKED(fDesktop->WindowLocker()); 2090 2091 if (fCurrentStack.Get() == NULL) 2092 return false; 2093 if (fCurrentStack->CountWindows() == 1) 2094 return true; 2095 2096 int32 index = PositionInStack(); 2097 2098 if (fCurrentStack->RemoveWindow(this) == false) 2099 return false; 2100 2101 BRegion invalidatedRegion; 2102 ::Decorator* decorator = fCurrentStack->Decorator(); 2103 if (decorator != NULL) { 2104 decorator->RemoveTab(index, &invalidatedRegion); 2105 decorator->SetTopTab(fCurrentStack->LayerOrder().CountItems() - 1); 2106 } 2107 2108 Window* remainingTop = fCurrentStack->TopLayerWindow(); 2109 if (remainingTop != NULL) { 2110 if (decorator != NULL) 2111 decorator->SetDrawingEngine(remainingTop->fDrawingEngine); 2112 // propagate focus to the decorator 2113 remainingTop->SetFocus(remainingTop->IsFocus()); 2114 remainingTop->SetLook(remainingTop->Look(), NULL); 2115 } 2116 2117 fCurrentStack = NULL; 2118 if (ownStackNeeded == true) 2119 _InitWindowStack(); 2120 // propagate focus to the new decorator 2121 SetFocus(IsFocus()); 2122 2123 if (remainingTop != NULL) { 2124 invalidatedRegion.Include(&remainingTop->VisibleRegion()); 2125 fDesktop->RebuildAndRedrawAfterWindowChange(remainingTop, 2126 invalidatedRegion); 2127 } 2128 return true; 2129 } 2130 2131 2132 bool 2133 Window::AddWindowToStack(Window* window) 2134 { 2135 ASSERT_MULTI_WRITE_LOCKED(fDesktop->WindowLocker()); 2136 2137 WindowStack* stack = GetWindowStack(); 2138 if (stack == NULL) 2139 return false; 2140 2141 BRegion dirty; 2142 // move window to the own position 2143 BRect ownFrame = Frame(); 2144 BRect frame = window->Frame(); 2145 float deltaToX = round(ownFrame.left - frame.left); 2146 float deltaToY = round(ownFrame.top - frame.top); 2147 frame.OffsetBy(deltaToX, deltaToY); 2148 float deltaByX = round(ownFrame.right - frame.right); 2149 float deltaByY = round(ownFrame.bottom - frame.bottom); 2150 dirty.Include(&window->VisibleRegion()); 2151 window->MoveBy(deltaToX, deltaToY, false); 2152 window->ResizeBy(deltaByX, deltaByY, &dirty, false); 2153 2154 // first collect dirt from the window to add 2155 ::Decorator* otherDecorator = window->Decorator(); 2156 if (otherDecorator != NULL) 2157 dirty.Include(otherDecorator->TitleBarRect()); 2158 ::Decorator* decorator = stack->Decorator(); 2159 if (decorator != NULL) 2160 dirty.Include(decorator->TitleBarRect()); 2161 2162 int32 position = PositionInStack() + 1; 2163 if (position >= stack->CountWindows()) 2164 position = -1; 2165 if (stack->AddWindow(window, position) == false) 2166 return false; 2167 window->DetachFromWindowStack(false); 2168 window->fCurrentStack.SetTo(stack); 2169 2170 if (decorator != NULL) { 2171 DesktopSettings settings(fDesktop); 2172 decorator->AddTab(settings, window->Title(), window->Look(), 2173 window->Flags(), position, &dirty); 2174 } 2175 2176 window->SetLook(window->Look(), &dirty); 2177 fDesktop->RebuildAndRedrawAfterWindowChange(TopLayerStackWindow(), dirty); 2178 window->SetFocus(window->IsFocus()); 2179 return true; 2180 } 2181 2182 2183 Window* 2184 Window::StackedWindowAt(const BPoint& where) 2185 { 2186 ::Decorator* decorator = Decorator(); 2187 if (decorator == NULL) 2188 return this; 2189 2190 int tab = decorator->TabAt(where); 2191 // if we have a decorator we also have a stack 2192 Window* window = fCurrentStack->WindowAt(tab); 2193 if (window != NULL) 2194 return window; 2195 return this; 2196 } 2197 2198 2199 Window* 2200 Window::TopLayerStackWindow() 2201 { 2202 if (fCurrentStack.Get() == NULL) 2203 return this; 2204 return fCurrentStack->TopLayerWindow(); 2205 } 2206 2207 2208 WindowStack* 2209 Window::GetWindowStack() 2210 { 2211 if (fCurrentStack.Get() == NULL) 2212 return _InitWindowStack(); 2213 return fCurrentStack; 2214 } 2215 2216 2217 bool 2218 Window::MoveToTopStackLayer() 2219 { 2220 ::Decorator* decorator = Decorator(); 2221 if (decorator == NULL) 2222 return false; 2223 decorator->SetDrawingEngine(fDrawingEngine); 2224 SetLook(Look(), NULL); 2225 decorator->SetTopTab(PositionInStack()); 2226 return fCurrentStack->MoveToTopLayer(this); 2227 } 2228 2229 2230 bool 2231 Window::MoveToStackPosition(int32 to, bool isMoving) 2232 { 2233 if (fCurrentStack.Get() == NULL) 2234 return false; 2235 int32 index = PositionInStack(); 2236 if (fCurrentStack->Move(index, to) == false) 2237 return false; 2238 2239 BRegion dirty; 2240 ::Decorator* decorator = Decorator(); 2241 if (decorator && decorator->MoveTab(index, to, isMoving, &dirty) == false) 2242 return false; 2243 2244 fDesktop->RebuildAndRedrawAfterWindowChange(this, dirty); 2245 return true; 2246 } 2247 2248 2249 WindowStack* 2250 Window::_InitWindowStack() 2251 { 2252 fCurrentStack = NULL; 2253 ::Decorator* decorator = NULL; 2254 if (fLook != B_NO_BORDER_WINDOW_LOOK) 2255 decorator = gDecorManager.AllocateDecorator(this); 2256 2257 WindowStack* stack = new(std::nothrow) WindowStack(decorator); 2258 if (stack == NULL) 2259 return NULL; 2260 2261 if (stack->AddWindow(this) != true) { 2262 delete stack; 2263 return NULL; 2264 } 2265 fCurrentStack.SetTo(stack, true); 2266 return stack; 2267 } 2268 2269 2270 WindowStack::WindowStack(::Decorator* decorator) 2271 : 2272 fDecorator(decorator) 2273 { 2274 2275 } 2276 2277 2278 WindowStack::~WindowStack() 2279 { 2280 delete fDecorator; 2281 } 2282 2283 2284 void 2285 WindowStack::SetDecorator(::Decorator* decorator) 2286 { 2287 delete fDecorator; 2288 fDecorator = decorator; 2289 } 2290 2291 2292 ::Decorator* 2293 WindowStack::Decorator() 2294 { 2295 return fDecorator; 2296 } 2297 2298 2299 Window* 2300 WindowStack::TopLayerWindow() const 2301 { 2302 return fWindowLayerOrder.ItemAt(fWindowLayerOrder.CountItems() - 1); 2303 } 2304 2305 2306 int32 2307 WindowStack::CountWindows() 2308 { 2309 return fWindowList.CountItems(); 2310 } 2311 2312 2313 Window* 2314 WindowStack::WindowAt(int32 index) 2315 { 2316 return fWindowList.ItemAt(index); 2317 } 2318 2319 2320 bool 2321 WindowStack::AddWindow(Window* window, int32 position) 2322 { 2323 if (position >= 0) { 2324 if (fWindowList.AddItem(window, position) == false) 2325 return false; 2326 } else if (fWindowList.AddItem(window) == false) 2327 return false; 2328 2329 if (fWindowLayerOrder.AddItem(window) == false) { 2330 fWindowList.RemoveItem(window); 2331 return false; 2332 } 2333 return true; 2334 } 2335 2336 2337 bool 2338 WindowStack::RemoveWindow(Window* window) 2339 { 2340 if (fWindowList.RemoveItem(window) == false) 2341 return false; 2342 2343 fWindowLayerOrder.RemoveItem(window); 2344 return true; 2345 } 2346 2347 2348 bool 2349 WindowStack::MoveToTopLayer(Window* window) 2350 { 2351 int32 index = fWindowLayerOrder.IndexOf(window); 2352 return fWindowLayerOrder.MoveItem(index, 2353 fWindowLayerOrder.CountItems() - 1); 2354 } 2355 2356 2357 bool 2358 WindowStack::Move(int32 from, int32 to) 2359 { 2360 return fWindowList.MoveItem(from, to); 2361 } 2362