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 bool avoidFocus = (Flags() & B_AVOID_FOCUS) != 0; 904 905 // Activate or focus the window in case it doesn't accept first 906 // click, depending on the mouse mode 907 DesktopSettings desktopSettings(fDesktop); 908 if (desktopSettings.MouseMode() == B_NORMAL_MOUSE 909 && !acceptFirstClick) 910 fDesktop->ActivateWindow(this); 911 else if (!avoidFocus) 912 fDesktop->SetFocusWindow(this); 913 914 // Eat the click if we don't accept first click 915 // (B_AVOID_FOCUS never gets the focus, so they always accept 916 // the first click) 917 // TODO: the latter is unlike BeOS - if we really wanted to 918 // imitate this behaviour, we would need to check if we're 919 // the front window instead of the focus window 920 if (!acceptFirstClick && !desktopSettings.AcceptFirstClick() 921 && !avoidFocus) 922 return; 923 } 924 925 // fill out view token for the view under the mouse 926 viewToken = view->Token(); 927 view->MouseDown(message, where); 928 } 929 930 _clickTarget = ClickTarget(ClickTarget::TYPE_WINDOW_CONTENTS, 931 windowToken, viewToken); 932 } 933 } 934 935 936 void 937 Window::MouseUp(BMessage* message, BPoint where, int32* _viewToken) 938 { 939 fWindowBehaviour->MouseUp(message, where); 940 941 if (View* view = ViewAt(where)) { 942 if (HasModal()) 943 return; 944 945 *_viewToken = view->Token(); 946 view->MouseUp(message, where); 947 } 948 } 949 950 951 void 952 Window::MouseMoved(BMessage *message, BPoint where, int32* _viewToken, 953 bool isLatestMouseMoved, bool isFake) 954 { 955 View* view = ViewAt(where); 956 if (view != NULL) 957 *_viewToken = view->Token(); 958 959 // ignore pointer history 960 if (!isLatestMouseMoved) 961 return; 962 963 fWindowBehaviour->MouseMoved(message, where, isFake); 964 965 // mouse cursor 966 967 if (view != NULL) { 968 view->MouseMoved(message, where); 969 970 // TODO: there is more for real cursor support, ie. if a window is closed, 971 // new app cursor shouldn't override view cursor, ... 972 ServerWindow()->App()->SetCurrentCursor(view->Cursor()); 973 } 974 } 975 976 977 void 978 Window::ModifiersChanged(int32 modifiers) 979 { 980 fWindowBehaviour->ModifiersChanged(modifiers); 981 } 982 983 984 // #pragma mark - 985 986 987 void 988 Window::WorkspaceActivated(int32 index, bool active) 989 { 990 BMessage activatedMsg(B_WORKSPACE_ACTIVATED); 991 activatedMsg.AddInt64("when", system_time()); 992 activatedMsg.AddInt32("workspace", index); 993 activatedMsg.AddBool("active", active); 994 995 ServerWindow()->SendMessageToClient(&activatedMsg); 996 } 997 998 999 void 1000 Window::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces) 1001 { 1002 fWorkspaces = newWorkspaces; 1003 1004 BMessage changedMsg(B_WORKSPACES_CHANGED); 1005 changedMsg.AddInt64("when", system_time()); 1006 changedMsg.AddInt32("old", oldWorkspaces); 1007 changedMsg.AddInt32("new", newWorkspaces); 1008 1009 ServerWindow()->SendMessageToClient(&changedMsg); 1010 } 1011 1012 1013 void 1014 Window::Activated(bool active) 1015 { 1016 BMessage msg(B_WINDOW_ACTIVATED); 1017 msg.AddBool("active", active); 1018 ServerWindow()->SendMessageToClient(&msg); 1019 } 1020 1021 1022 //# pragma mark - 1023 1024 1025 void 1026 Window::SetTitle(const char* name, BRegion& dirty) 1027 { 1028 // rebuild the clipping for the title area 1029 // and redraw it. 1030 1031 fTitle = name; 1032 1033 ::Decorator* decorator = Decorator(); 1034 if (decorator) { 1035 int32 index = PositionInStack(); 1036 decorator->SetTitle(index, name, &dirty); 1037 } 1038 } 1039 1040 1041 void 1042 Window::SetFocus(bool focus) 1043 { 1044 ::Decorator* decorator = Decorator(); 1045 1046 // executed from Desktop thread 1047 // it holds the clipping write lock, 1048 // so the window thread cannot be 1049 // accessing fIsFocus 1050 1051 BRegion* dirty = NULL; 1052 if (decorator) 1053 dirty = fRegionPool.GetRegion(decorator->GetFootprint()); 1054 if (dirty) { 1055 dirty->IntersectWith(&fVisibleRegion); 1056 fDesktop->MarkDirty(*dirty); 1057 fRegionPool.Recycle(dirty); 1058 } 1059 1060 fIsFocus = focus; 1061 if (decorator) { 1062 int32 index = PositionInStack(); 1063 decorator->SetFocus(index, focus); 1064 } 1065 1066 Activated(focus); 1067 } 1068 1069 1070 void 1071 Window::SetHidden(bool hidden) 1072 { 1073 // the desktop takes care of dirty regions 1074 if (fHidden != hidden) { 1075 fHidden = hidden; 1076 1077 fTopView->SetHidden(hidden); 1078 1079 // TODO: anything else? 1080 } 1081 } 1082 1083 1084 void 1085 Window::SetShowLevel(int32 showLevel) 1086 { 1087 if (showLevel == fShowLevel) 1088 return; 1089 1090 fShowLevel = showLevel; 1091 } 1092 1093 1094 void 1095 Window::SetMinimized(bool minimized) 1096 { 1097 if (minimized == fMinimized) 1098 return; 1099 1100 fMinimized = minimized; 1101 } 1102 1103 1104 bool 1105 Window::IsVisible() const 1106 { 1107 if (IsOffscreenWindow()) 1108 return true; 1109 1110 if (IsHidden()) 1111 return false; 1112 1113 /* 1114 if (fVisibleRegion.CountRects() == 0) 1115 return false; 1116 */ 1117 return fCurrentWorkspace >= 0 && fCurrentWorkspace < kWorkingList; 1118 } 1119 1120 1121 bool 1122 Window::IsDragging() const 1123 { 1124 if (!fWindowBehaviour) 1125 return false; 1126 return fWindowBehaviour->IsDragging(); 1127 } 1128 1129 1130 bool 1131 Window::IsResizing() const 1132 { 1133 if (!fWindowBehaviour) 1134 return false; 1135 return fWindowBehaviour->IsResizing(); 1136 } 1137 1138 1139 void 1140 Window::SetSizeLimits(int32 minWidth, int32 maxWidth, int32 minHeight, 1141 int32 maxHeight) 1142 { 1143 if (minWidth < 0) 1144 minWidth = 0; 1145 1146 if (minHeight < 0) 1147 minHeight = 0; 1148 1149 fMinWidth = minWidth; 1150 fMaxWidth = maxWidth; 1151 fMinHeight = minHeight; 1152 fMaxHeight = maxHeight; 1153 1154 // give the Decorator a say in this too 1155 ::Decorator* decorator = Decorator(); 1156 if (decorator) { 1157 decorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth, 1158 &fMaxHeight); 1159 } 1160 1161 _ObeySizeLimits(); 1162 } 1163 1164 1165 void 1166 Window::GetSizeLimits(int32* minWidth, int32* maxWidth, 1167 int32* minHeight, int32* maxHeight) const 1168 { 1169 *minWidth = fMinWidth; 1170 *maxWidth = fMaxWidth; 1171 *minHeight = fMinHeight; 1172 *maxHeight = fMaxHeight; 1173 } 1174 1175 1176 bool 1177 Window::SetTabLocation(float location, bool isShifting, BRegion& dirty) 1178 { 1179 ::Decorator* decorator = Decorator(); 1180 if (decorator) { 1181 int32 index = PositionInStack(); 1182 return decorator->SetTabLocation(index, location, isShifting, &dirty); 1183 } 1184 1185 return false; 1186 } 1187 1188 1189 float 1190 Window::TabLocation() const 1191 { 1192 ::Decorator* decorator = Decorator(); 1193 if (decorator) { 1194 int32 index = PositionInStack(); 1195 return decorator->TabLocation(index); 1196 } 1197 return 0.0; 1198 } 1199 1200 1201 bool 1202 Window::SetDecoratorSettings(const BMessage& settings, BRegion& dirty) 1203 { 1204 if (settings.what == 'prVu') { 1205 // 'prVu' == preview a decorator! 1206 BString path; 1207 if (settings.FindString("preview", &path) == B_OK) 1208 return gDecorManager.PreviewDecorator(path, this) == B_OK; 1209 return false; 1210 } 1211 1212 ::Decorator* decorator = Decorator(); 1213 if (decorator) 1214 return decorator->SetSettings(settings, &dirty); 1215 1216 return false; 1217 } 1218 1219 1220 bool 1221 Window::GetDecoratorSettings(BMessage* settings) 1222 { 1223 if (fDesktop) 1224 fDesktop->GetDecoratorSettings(this, *settings); 1225 1226 ::Decorator* decorator = Decorator(); 1227 if (decorator) 1228 return decorator->GetSettings(settings); 1229 1230 return false; 1231 } 1232 1233 1234 void 1235 Window::FontsChanged(BRegion* updateRegion) 1236 { 1237 ::Decorator* decorator = Decorator(); 1238 if (decorator != NULL) { 1239 DesktopSettings settings(fDesktop); 1240 decorator->FontsChanged(settings, updateRegion); 1241 } 1242 } 1243 1244 1245 void 1246 Window::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 bool wasExpose = fPendingUpdateSession->IsExpose(); 1744 BRegion* backgroundClearingRegion = &dirtyContentRegion; 1745 1746 _TransferToUpdateSession(&dirtyContentRegion); 1747 1748 if (fPendingUpdateSession->IsExpose()) { 1749 if (!fContentRegionValid) 1750 _UpdateContentRegion(); 1751 1752 if (!wasExpose) { 1753 // there was suddenly added a dirty region 1754 // caused by exposing content, we need to clear 1755 // the entire background 1756 backgroundClearingRegion = &fPendingUpdateSession->DirtyRegion(); 1757 } 1758 1759 if (fDrawingEngine->LockParallelAccess()) { 1760 bool copyToFrontEnabled = fDrawingEngine->CopyToFrontEnabled(); 1761 fDrawingEngine->SetCopyToFrontEnabled(true); 1762 fDrawingEngine->SuspendAutoSync(); 1763 1764 //sCurrentColor.red = rand() % 255; 1765 //sCurrentColor.green = rand() % 255; 1766 //sCurrentColor.blue = rand() % 255; 1767 //sPendingColor.red = rand() % 255; 1768 //sPendingColor.green = rand() % 255; 1769 //sPendingColor.blue = rand() % 255; 1770 //fDrawingEngine->FillRegion(*backgroundClearingRegion, sCurrentColor); 1771 //snooze(10000); 1772 1773 fTopView->Draw(fDrawingEngine, backgroundClearingRegion, 1774 &fContentRegion, true); 1775 1776 fDrawingEngine->Sync(); 1777 fDrawingEngine->SetCopyToFrontEnabled(copyToFrontEnabled); 1778 fDrawingEngine->UnlockParallelAccess(); 1779 } 1780 } 1781 } 1782 1783 1784 void 1785 Window::_DrawBorder() 1786 { 1787 // this is executed in the window thread, but only 1788 // in respond to a REDRAW message having been received, the 1789 // clipping lock is held for reading 1790 ::Decorator* decorator = Decorator(); 1791 if (!decorator) 1792 return; 1793 1794 // construct the region of the border that needs redrawing 1795 BRegion* dirtyBorderRegion = fRegionPool.GetRegion(); 1796 if (!dirtyBorderRegion) 1797 return; 1798 GetBorderRegion(dirtyBorderRegion); 1799 // intersect with our visible region 1800 dirtyBorderRegion->IntersectWith(&fVisibleRegion); 1801 // intersect with the dirty region 1802 dirtyBorderRegion->IntersectWith(&fDirtyRegion); 1803 1804 DrawingEngine* engine = decorator->GetDrawingEngine(); 1805 if (dirtyBorderRegion->CountRects() > 0 && engine->LockParallelAccess()) { 1806 engine->ConstrainClippingRegion(dirtyBorderRegion); 1807 bool copyToFrontEnabled = engine->CopyToFrontEnabled(); 1808 engine->SetCopyToFrontEnabled(false); 1809 1810 decorator->Draw(dirtyBorderRegion->Frame()); 1811 1812 engine->SetCopyToFrontEnabled(copyToFrontEnabled); 1813 engine->CopyToFront(*dirtyBorderRegion); 1814 1815 // TODO: remove this once the DrawState stuff is handled 1816 // more cleanly. The reason why this is needed is that 1817 // when the decorator draws strings, a draw state is set 1818 // on the Painter object, and this is were it might get 1819 // out of sync with what the ServerWindow things is the 1820 // current DrawState set on the Painter 1821 fWindow->ResyncDrawState(); 1822 1823 engine->UnlockParallelAccess(); 1824 } 1825 fRegionPool.Recycle(dirtyBorderRegion); 1826 } 1827 1828 1829 /*! pre: the clipping is readlocked (this function is 1830 only called from _TriggerContentRedraw()), which 1831 in turn is only called from MessageReceived() with 1832 the clipping lock held 1833 */ 1834 void 1835 Window::_TransferToUpdateSession(BRegion* contentDirtyRegion) 1836 { 1837 if (contentDirtyRegion->CountRects() <= 0) 1838 return; 1839 1840 //fDrawingEngine->FillRegion(*contentDirtyRegion, sPendingColor); 1841 //snooze(20000); 1842 1843 // add to pending 1844 fPendingUpdateSession->SetUsed(true); 1845 // if (!fPendingUpdateSession->IsExpose()) 1846 fPendingUpdateSession->AddCause(fDirtyCause); 1847 fPendingUpdateSession->Include(contentDirtyRegion); 1848 1849 if (!fUpdateRequested) { 1850 // send this to client 1851 _SendUpdateMessage(); 1852 // the pending region is now the current, 1853 // though the update does not start until 1854 // we received BEGIN_UPDATE from the client 1855 } 1856 } 1857 1858 1859 void 1860 Window::_SendUpdateMessage() 1861 { 1862 if (!fUpdatesEnabled) 1863 return; 1864 1865 BMessage message(_UPDATE_); 1866 if (ServerWindow()->SendMessageToClient(&message) != B_OK) { 1867 // If sending the message failed, we'll just keep adding to the dirty 1868 // region until sending was successful. 1869 // TODO: we might want to automatically resend this message in this case 1870 return; 1871 } 1872 1873 fUpdateRequested = true; 1874 fEffectiveDrawingRegionValid = false; 1875 } 1876 1877 1878 void 1879 Window::BeginUpdate(BPrivate::PortLink& link) 1880 { 1881 // NOTE: since we might "shift" parts of the 1882 // internal dirty regions from the desktop thread 1883 // in response to Window::ResizeBy(), which 1884 // might move arround views, the user of this function 1885 // needs to hold the global clipping lock so that the internal 1886 // dirty regions are not messed with from the Desktop thread 1887 // and ServerWindow thread at the same time. 1888 1889 if (!fUpdateRequested) { 1890 link.StartMessage(B_ERROR); 1891 link.Flush(); 1892 fprintf(stderr, "Window::BeginUpdate() - no update requested!\n"); 1893 return; 1894 } 1895 1896 // make the pending update session the current update session 1897 // (toggle the pointers) 1898 UpdateSession* temp = fCurrentUpdateSession; 1899 fCurrentUpdateSession = fPendingUpdateSession; 1900 fPendingUpdateSession = temp; 1901 fPendingUpdateSession->SetUsed(false); 1902 // all drawing command from the client 1903 // will have the dirty region from the update 1904 // session enforced 1905 fInUpdate = true; 1906 fEffectiveDrawingRegionValid = false; 1907 1908 // TODO: each view could be drawn individually 1909 // right before carrying out the first drawing 1910 // command from the client during an update 1911 // (View::IsBackgroundDirty() can be used 1912 // for this) 1913 if (!fContentRegionValid) 1914 _UpdateContentRegion(); 1915 1916 BRegion* dirty = fRegionPool.GetRegion( 1917 fCurrentUpdateSession->DirtyRegion()); 1918 if (!dirty) { 1919 link.StartMessage(B_ERROR); 1920 link.Flush(); 1921 return; 1922 } 1923 1924 dirty->IntersectWith(&VisibleContentRegion()); 1925 1926 //if (!fCurrentUpdateSession->IsExpose()) { 1927 ////sCurrentColor.red = rand() % 255; 1928 ////sCurrentColor.green = rand() % 255; 1929 ////sCurrentColor.blue = rand() % 255; 1930 ////sPendingColor.red = rand() % 255; 1931 ////sPendingColor.green = rand() % 255; 1932 ////sPendingColor.blue = rand() % 255; 1933 //fDrawingEngine->FillRegion(*dirty, sCurrentColor); 1934 //snooze(10000); 1935 //} 1936 1937 link.StartMessage(B_OK); 1938 // append the current window geometry to the 1939 // message, the client will need it 1940 link.Attach<BPoint>(fFrame.LeftTop()); 1941 link.Attach<float>(fFrame.Width()); 1942 link.Attach<float>(fFrame.Height()); 1943 // find and attach all views that intersect with 1944 // the dirty region 1945 fTopView->AddTokensForViewsInRegion(link, *dirty, &fContentRegion); 1946 // mark the end of the token "list" 1947 link.Attach<int32>(B_NULL_TOKEN); 1948 link.Flush(); 1949 1950 // supress back to front buffer copies in the drawing engine 1951 fDrawingEngine->SetCopyToFrontEnabled(false); 1952 1953 if (!fCurrentUpdateSession->IsExpose() 1954 && fDrawingEngine->LockParallelAccess()) { 1955 fDrawingEngine->SuspendAutoSync(); 1956 1957 fTopView->Draw(fDrawingEngine, dirty, &fContentRegion, true); 1958 1959 fDrawingEngine->Sync(); 1960 fDrawingEngine->UnlockParallelAccess(); 1961 } // else the background was cleared already 1962 1963 fRegionPool.Recycle(dirty); 1964 } 1965 1966 1967 void 1968 Window::EndUpdate() 1969 { 1970 // NOTE: see comment in _BeginUpdate() 1971 1972 if (fInUpdate) { 1973 // reenable copy to front 1974 fDrawingEngine->SetCopyToFrontEnabled(true); 1975 1976 BRegion* dirty = fRegionPool.GetRegion( 1977 fCurrentUpdateSession->DirtyRegion()); 1978 1979 if (dirty) { 1980 dirty->IntersectWith(&VisibleContentRegion()); 1981 1982 fDrawingEngine->CopyToFront(*dirty); 1983 fRegionPool.Recycle(dirty); 1984 } 1985 1986 fCurrentUpdateSession->SetUsed(false); 1987 1988 fInUpdate = false; 1989 fEffectiveDrawingRegionValid = false; 1990 } 1991 if (fPendingUpdateSession->IsUsed()) { 1992 // send this to client 1993 _SendUpdateMessage(); 1994 } else { 1995 fUpdateRequested = false; 1996 } 1997 } 1998 1999 2000 void 2001 Window::_UpdateContentRegion() 2002 { 2003 fContentRegion.Set(fFrame); 2004 2005 // resize handle 2006 ::Decorator* decorator = Decorator(); 2007 if (decorator) 2008 fContentRegion.Exclude(&decorator->GetFootprint()); 2009 2010 fContentRegionValid = true; 2011 } 2012 2013 2014 void 2015 Window::_ObeySizeLimits() 2016 { 2017 // make sure we even have valid size limits 2018 if (fMaxWidth < fMinWidth) 2019 fMaxWidth = fMinWidth; 2020 2021 if (fMaxHeight < fMinHeight) 2022 fMaxHeight = fMinHeight; 2023 2024 // Automatically resize the window to fit these new limits 2025 // if it does not already. 2026 2027 // On R5, Windows don't automatically resize, but since 2028 // BWindow::ResizeTo() even honors the limits, I would guess 2029 // this is a bug that we don't have to adopt. 2030 // Note that most current apps will do unnecessary resizing 2031 // after having set the limits, but the overhead is neglible. 2032 2033 float minWidthDiff = fMinWidth - fFrame.Width(); 2034 float minHeightDiff = fMinHeight - fFrame.Height(); 2035 float maxWidthDiff = fMaxWidth - fFrame.Width(); 2036 float maxHeightDiff = fMaxHeight - fFrame.Height(); 2037 2038 float xDiff = 0.0; 2039 if (minWidthDiff > 0.0) // we're currently smaller than minWidth 2040 xDiff = minWidthDiff; 2041 else if (maxWidthDiff < 0.0) // we're currently larger than maxWidth 2042 xDiff = maxWidthDiff; 2043 2044 float yDiff = 0.0; 2045 if (minHeightDiff > 0.0) // we're currently smaller than minHeight 2046 yDiff = minHeightDiff; 2047 else if (maxHeightDiff < 0.0) // we're currently larger than maxHeight 2048 yDiff = maxHeightDiff; 2049 2050 if (fDesktop) 2051 fDesktop->ResizeWindowBy(this, xDiff, yDiff); 2052 else 2053 ResizeBy((int32)xDiff, (int32)yDiff, NULL); 2054 } 2055 2056 2057 // #pragma mark - UpdateSession 2058 2059 2060 Window::UpdateSession::UpdateSession() 2061 : 2062 fDirtyRegion(), 2063 fInUse(false), 2064 fCause(0) 2065 { 2066 } 2067 2068 2069 Window::UpdateSession::~UpdateSession() 2070 { 2071 } 2072 2073 2074 void 2075 Window::UpdateSession::Include(BRegion* additionalDirty) 2076 { 2077 fDirtyRegion.Include(additionalDirty); 2078 } 2079 2080 2081 void 2082 Window::UpdateSession::Exclude(BRegion* dirtyInNextSession) 2083 { 2084 fDirtyRegion.Exclude(dirtyInNextSession); 2085 } 2086 2087 2088 void 2089 Window::UpdateSession::MoveBy(int32 x, int32 y) 2090 { 2091 fDirtyRegion.OffsetBy(x, y); 2092 } 2093 2094 2095 void 2096 Window::UpdateSession::SetUsed(bool used) 2097 { 2098 fInUse = used; 2099 if (!fInUse) { 2100 fDirtyRegion.MakeEmpty(); 2101 fCause = 0; 2102 } 2103 } 2104 2105 2106 void 2107 Window::UpdateSession::AddCause(uint8 cause) 2108 { 2109 fCause |= cause; 2110 } 2111 2112 2113 int32 2114 Window::PositionInStack() const 2115 { 2116 if (fCurrentStack.Get() == NULL) 2117 return -1; 2118 return fCurrentStack->WindowList().IndexOf(this); 2119 } 2120 2121 2122 bool 2123 Window::DetachFromWindowStack(bool ownStackNeeded) 2124 { 2125 // The lock must normally be held but is not held when closing the window. 2126 //ASSERT_MULTI_WRITE_LOCKED(fDesktop->WindowLocker()); 2127 2128 if (fCurrentStack.Get() == NULL) 2129 return false; 2130 if (fCurrentStack->CountWindows() == 1) 2131 return true; 2132 2133 int32 index = PositionInStack(); 2134 2135 if (fCurrentStack->RemoveWindow(this) == false) 2136 return false; 2137 2138 ::Decorator* decorator = fCurrentStack->Decorator(); 2139 if (decorator != NULL) { 2140 decorator->RemoveTab(index); 2141 decorator->SetTopTab(fCurrentStack->LayerOrder().CountItems() - 1); 2142 } 2143 2144 Window* remainingTop = fCurrentStack->TopLayerWindow(); 2145 if (remainingTop != NULL) { 2146 if (decorator != NULL) 2147 decorator->SetDrawingEngine(remainingTop->fDrawingEngine); 2148 // propagate focus to the decorator 2149 remainingTop->SetFocus(remainingTop->IsFocus()); 2150 remainingTop->SetLook(remainingTop->Look(), NULL); 2151 } 2152 2153 fCurrentStack = NULL; 2154 if (ownStackNeeded == true) 2155 _InitWindowStack(); 2156 // propagate focus to the new decorator 2157 SetFocus(IsFocus()); 2158 2159 if (remainingTop != NULL) { 2160 fDesktop->RebuildAndRedrawAfterWindowChange(remainingTop, 2161 remainingTop->VisibleRegion()); 2162 } 2163 return true; 2164 } 2165 2166 2167 bool 2168 Window::AddWindowToStack(Window* window) 2169 { 2170 ASSERT_MULTI_WRITE_LOCKED(fDesktop->WindowLocker()); 2171 2172 WindowStack* stack = GetWindowStack(); 2173 if (stack == NULL) 2174 return false; 2175 2176 BRegion dirty; 2177 // move window to the own position 2178 BRect ownFrame = Frame(); 2179 BRect frame = window->Frame(); 2180 float deltaToX = round(ownFrame.left - frame.left); 2181 float deltaToY = round(ownFrame.top - frame.top); 2182 frame.OffsetBy(deltaToX, deltaToY); 2183 float deltaByX = round(ownFrame.right - frame.right); 2184 float deltaByY = round(ownFrame.bottom - frame.bottom); 2185 dirty.Include(&window->VisibleRegion()); 2186 window->MoveBy(deltaToX, deltaToY, false); 2187 window->ResizeBy(deltaByX, deltaByY, &dirty, false); 2188 2189 // first collect dirt from the window to add 2190 ::Decorator* otherDecorator = window->Decorator(); 2191 if (otherDecorator != NULL) 2192 dirty.Include(otherDecorator->TitleBarRect()); 2193 ::Decorator* decorator = stack->Decorator(); 2194 if (decorator != NULL) 2195 dirty.Include(decorator->TitleBarRect()); 2196 2197 int32 position = PositionInStack() + 1; 2198 if (position >= stack->CountWindows()) 2199 position = -1; 2200 if (stack->AddWindow(window, position) == false) 2201 return false; 2202 window->DetachFromWindowStack(false); 2203 window->fCurrentStack.SetTo(stack); 2204 2205 if (decorator != NULL) { 2206 DesktopSettings settings(fDesktop); 2207 decorator->AddTab(settings, window->Title(), window->Look(), 2208 window->Flags(), position, &dirty); 2209 } 2210 2211 window->SetLook(window->Look(), &dirty); 2212 fDesktop->RebuildAndRedrawAfterWindowChange(TopLayerStackWindow(), dirty); 2213 window->SetFocus(window->IsFocus()); 2214 return true; 2215 } 2216 2217 2218 Window* 2219 Window::StackedWindowAt(const BPoint& where) 2220 { 2221 ::Decorator* decorator = Decorator(); 2222 if (decorator == NULL) 2223 return this; 2224 2225 int tab = decorator->TabAt(where); 2226 // if we have a decorator we also have a stack 2227 Window* window = fCurrentStack->WindowAt(tab); 2228 if (window != NULL) 2229 return window; 2230 return this; 2231 } 2232 2233 2234 Window* 2235 Window::TopLayerStackWindow() 2236 { 2237 if (fCurrentStack.Get() == NULL) 2238 return this; 2239 return fCurrentStack->TopLayerWindow(); 2240 } 2241 2242 2243 WindowStack* 2244 Window::GetWindowStack() 2245 { 2246 if (fCurrentStack.Get() == NULL) 2247 return _InitWindowStack(); 2248 return fCurrentStack; 2249 } 2250 2251 2252 bool 2253 Window::MoveToTopStackLayer() 2254 { 2255 ::Decorator* decorator = Decorator(); 2256 if (decorator == NULL) 2257 return false; 2258 decorator->SetDrawingEngine(fDrawingEngine); 2259 SetLook(Look(), NULL); 2260 decorator->SetTopTab(PositionInStack()); 2261 return fCurrentStack->MoveToTopLayer(this); 2262 } 2263 2264 2265 bool 2266 Window::MoveToStackPosition(int32 to, bool isMoving) 2267 { 2268 if (fCurrentStack.Get() == NULL) 2269 return false; 2270 int32 index = PositionInStack(); 2271 if (fCurrentStack->Move(index, to) == false) 2272 return false; 2273 2274 BRegion dirty; 2275 ::Decorator* decorator = Decorator(); 2276 if (decorator && decorator->MoveTab(index, to, isMoving, &dirty) == false) 2277 return false; 2278 2279 fDesktop->RebuildAndRedrawAfterWindowChange(this, dirty); 2280 return true; 2281 } 2282 2283 2284 WindowStack* 2285 Window::_InitWindowStack() 2286 { 2287 fCurrentStack = NULL; 2288 ::Decorator* decorator = NULL; 2289 if (fLook != B_NO_BORDER_WINDOW_LOOK) 2290 decorator = gDecorManager.AllocateDecorator(this); 2291 2292 WindowStack* stack = new(std::nothrow) WindowStack(decorator); 2293 if (stack == NULL) 2294 return NULL; 2295 2296 if (stack->AddWindow(this) != true) { 2297 delete stack; 2298 return NULL; 2299 } 2300 fCurrentStack.SetTo(stack, true); 2301 return stack; 2302 } 2303 2304 2305 WindowStack::WindowStack(::Decorator* decorator) 2306 : 2307 fDecorator(decorator) 2308 { 2309 2310 } 2311 2312 2313 WindowStack::~WindowStack() 2314 { 2315 delete fDecorator; 2316 } 2317 2318 2319 void 2320 WindowStack::SetDecorator(::Decorator* decorator) 2321 { 2322 delete fDecorator; 2323 fDecorator = decorator; 2324 } 2325 2326 2327 ::Decorator* 2328 WindowStack::Decorator() 2329 { 2330 return fDecorator; 2331 } 2332 2333 2334 Window* 2335 WindowStack::TopLayerWindow() const 2336 { 2337 return fWindowLayerOrder.ItemAt(fWindowLayerOrder.CountItems() - 1); 2338 } 2339 2340 2341 int32 2342 WindowStack::CountWindows() 2343 { 2344 return fWindowList.CountItems(); 2345 } 2346 2347 2348 Window* 2349 WindowStack::WindowAt(int32 index) 2350 { 2351 return fWindowList.ItemAt(index); 2352 } 2353 2354 2355 bool 2356 WindowStack::AddWindow(Window* window, int32 position) 2357 { 2358 if (position >= 0) { 2359 if (fWindowList.AddItem(window, position) == false) 2360 return false; 2361 } else if (fWindowList.AddItem(window) == false) 2362 return false; 2363 2364 if (fWindowLayerOrder.AddItem(window) == false) { 2365 fWindowList.RemoveItem(window); 2366 return false; 2367 } 2368 return true; 2369 } 2370 2371 2372 bool 2373 WindowStack::RemoveWindow(Window* window) 2374 { 2375 if (fWindowList.RemoveItem(window) == false) 2376 return false; 2377 2378 fWindowLayerOrder.RemoveItem(window); 2379 return true; 2380 } 2381 2382 2383 bool 2384 WindowStack::MoveToTopLayer(Window* window) 2385 { 2386 int32 index = fWindowLayerOrder.IndexOf(window); 2387 return fWindowLayerOrder.MoveItem(index, 2388 fWindowLayerOrder.CountItems() - 1); 2389 } 2390 2391 2392 bool 2393 WindowStack::Move(int32 from, int32 to) 2394 { 2395 return fWindowList.MoveItem(from, to); 2396 } 2397