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