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