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