1 /* 2 * Copyright (c) 2001-2008, 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 * Axel Dörfler, axeld@pinc-software.de 9 * Stephan Aßmus <superstippi@gmx.de> 10 * Marcus Overhagen <marcus@overhagen.de> 11 */ 12 13 14 #include "View.h" 15 16 #include "BitmapManager.h" 17 #include "Desktop.h" 18 #include "DrawingEngine.h" 19 #include "Overlay.h" 20 #include "ServerApp.h" 21 #include "ServerBitmap.h" 22 #include "ServerCursor.h" 23 #include "ServerPicture.h" 24 #include "ServerWindow.h" 25 #include "Window.h" 26 27 #include "drawing_support.h" 28 29 #include <List.h> 30 #include <Message.h> 31 #include <PortLink.h> 32 #include <View.h> // for resize modes 33 #include <WindowPrivate.h> 34 35 #include <GradientLinear.h> 36 #include <GradientRadial.h> 37 #include <GradientRadialFocus.h> 38 #include <GradientDiamond.h> 39 #include <GradientConic.h> 40 41 #include <stdio.h> 42 43 #include <new> 44 45 using std::nothrow; 46 47 48 void 49 resize_frame(IntRect& frame, uint32 resizingMode, int32 x, int32 y) 50 { 51 // follow with left side 52 if ((resizingMode & 0x0F00U) == _VIEW_RIGHT_ << 8) 53 frame.left += x; 54 else if ((resizingMode & 0x0F00U) == _VIEW_CENTER_ << 8) 55 frame.left += x / 2; 56 57 // follow with right side 58 if ((resizingMode & 0x000FU) == _VIEW_RIGHT_) 59 frame.right += x; 60 else if ((resizingMode & 0x000FU) == _VIEW_CENTER_) 61 frame.right += x / 2; 62 63 // follow with top side 64 if ((resizingMode & 0xF000U) == _VIEW_BOTTOM_ << 12) 65 frame.top += y; 66 else if ((resizingMode & 0xF000U) == _VIEW_CENTER_ << 12) 67 frame.top += y / 2; 68 69 // follow with bottom side 70 if ((resizingMode & 0x00F0U) == _VIEW_BOTTOM_ << 4) 71 frame.bottom += y; 72 else if ((resizingMode & 0x00F0U) == _VIEW_CENTER_ << 4) 73 frame.bottom += y / 2; 74 } 75 76 77 // #pragma mark - 78 79 80 View::View(IntRect frame, IntPoint scrollingOffset, const char* name, 81 int32 token, uint32 resizeMode, uint32 flags) 82 : 83 fName(name), 84 fToken(token), 85 86 fFrame(frame), 87 fScrollingOffset(scrollingOffset), 88 89 fViewColor((rgb_color){ 255, 255, 255, 255 }), 90 fDrawState(new (nothrow) DrawState), 91 fViewBitmap(NULL), 92 93 fResizeMode(resizeMode), 94 fFlags(flags), 95 96 // Views start visible by default 97 fHidden(false), 98 fVisible(true), 99 fBackgroundDirty(true), 100 fIsDesktopBackground(false), 101 102 fEventMask(0), 103 fEventOptions(0), 104 105 fWindow(NULL), 106 fParent(NULL), 107 108 fFirstChild(NULL), 109 fPreviousSibling(NULL), 110 fNextSibling(NULL), 111 fLastChild(NULL), 112 113 fCursor(NULL), 114 fPicture(NULL), 115 116 fLocalClipping((BRect)Bounds()), 117 fScreenClipping(), 118 fScreenClippingValid(false), 119 fUserClipping(NULL), 120 fScreenAndUserClipping(NULL) 121 { 122 if (fDrawState) 123 fDrawState->SetSubPixelPrecise(fFlags & B_SUBPIXEL_PRECISE); 124 } 125 126 127 View::~View() 128 { 129 if (fViewBitmap != NULL) 130 gBitmapManager->DeleteBitmap(fViewBitmap); 131 132 delete fScreenAndUserClipping; 133 delete fUserClipping; 134 delete fDrawState; 135 136 // if (fWindow && this == fWindow->TopView()) 137 // fWindow->SetTopView(NULL); 138 139 if (fCursor) 140 fCursor->Release(); 141 142 // iterate over children and delete each one 143 View* view = fFirstChild; 144 while (view) { 145 View* toast = view; 146 view = view->fNextSibling; 147 delete toast; 148 } 149 } 150 151 152 IntRect 153 View::Bounds() const 154 { 155 IntRect bounds(fScrollingOffset.x, fScrollingOffset.y, 156 fScrollingOffset.x + fFrame.Width(), 157 fScrollingOffset.y + fFrame.Height()); 158 return bounds; 159 } 160 161 162 void 163 View::ConvertToVisibleInTopView(IntRect* bounds) const 164 { 165 *bounds = *bounds & Bounds(); 166 // NOTE: this step is necessary even if we don't have a parent! 167 ConvertToParent(bounds); 168 169 if (fParent) 170 fParent->ConvertToVisibleInTopView(bounds); 171 } 172 173 174 void 175 View::AttachedToWindow(::Window* window) 176 { 177 fWindow = window; 178 179 // an ugly hack to detect the desktop background 180 if (window->Feel() == kDesktopWindowFeel && Parent() == TopView()) 181 fIsDesktopBackground = true; 182 183 // insert view into local token space 184 if (fWindow != NULL) { 185 fWindow->ServerWindow()->App()->ViewTokens().SetToken(fToken, 186 B_HANDLER_TOKEN, this); 187 } 188 189 // attach child views as well 190 for (View* child = FirstChild(); child; child = child->NextSibling()) 191 child->AttachedToWindow(window); 192 } 193 194 195 void 196 View::DetachedFromWindow() 197 { 198 // remove view from local token space 199 if (fWindow != NULL) 200 fWindow->ServerWindow()->App()->ViewTokens().RemoveToken(fToken); 201 202 fWindow = NULL; 203 // detach child views as well 204 for (View* child = FirstChild(); child; child = child->NextSibling()) 205 child->DetachedFromWindow(); 206 } 207 208 209 // #pragma mark - 210 211 212 void 213 View::AddChild(View* view) 214 { 215 if (view->fParent) { 216 printf("View::AddChild() - View already has a parent\n"); 217 return; 218 } 219 220 view->fParent = this; 221 222 if (!fLastChild) { 223 // no children yet 224 fFirstChild = view; 225 } else { 226 // append view to formerly last child 227 fLastChild->fNextSibling = view; 228 view->fPreviousSibling = fLastChild; 229 } 230 fLastChild = view; 231 232 view->UpdateVisibleDeep(fVisible); 233 234 if (view->IsVisible()) 235 RebuildClipping(false); 236 237 if (fWindow) { 238 view->AttachedToWindow(fWindow); 239 240 if (view->IsVisible()) { 241 // trigger redraw 242 IntRect clippedFrame = view->Frame(); 243 ConvertToVisibleInTopView(&clippedFrame); 244 BRegion* dirty = fWindow->GetRegion(); 245 if (dirty) { 246 dirty->Set((clipping_rect)clippedFrame); 247 fWindow->MarkContentDirtyAsync(*dirty); 248 fWindow->RecycleRegion(dirty); 249 } 250 } 251 } 252 } 253 254 255 bool 256 View::RemoveChild(View* view) 257 { 258 if (view == NULL || view->fParent != this) { 259 printf("View::RemoveChild(%p - %s) - View is not child of " 260 "this (%p) view!\n", view, view ? view->Name() : NULL, this); 261 return false; 262 } 263 264 view->fParent = NULL; 265 266 if (fLastChild == view) 267 fLastChild = view->fPreviousSibling; 268 // view->fNextSibling would be NULL 269 270 if (fFirstChild == view ) 271 fFirstChild = view->fNextSibling; 272 // view->fPreviousSibling would be NULL 273 274 // connect child before and after view 275 if (view->fPreviousSibling) 276 view->fPreviousSibling->fNextSibling = view->fNextSibling; 277 278 if (view->fNextSibling) 279 view->fNextSibling->fPreviousSibling = view->fPreviousSibling; 280 281 // view has no siblings anymore 282 view->fPreviousSibling = NULL; 283 view->fNextSibling = NULL; 284 285 if (view->IsVisible()) { 286 Overlay* overlay = view->_Overlay(); 287 if (overlay != NULL) 288 overlay->Hide(); 289 290 RebuildClipping(false); 291 } 292 293 if (fWindow) { 294 view->DetachedFromWindow(); 295 296 if (fVisible && view->IsVisible()) { 297 // trigger redraw 298 IntRect clippedFrame = view->Frame(); 299 ConvertToVisibleInTopView(&clippedFrame); 300 BRegion* dirty = fWindow->GetRegion(); 301 if (dirty) { 302 dirty->Set((clipping_rect)clippedFrame); 303 fWindow->MarkContentDirtyAsync(*dirty); 304 fWindow->RecycleRegion(dirty); 305 } 306 } 307 } 308 309 return true; 310 } 311 312 313 View* 314 View::TopView() 315 { 316 // returns the top level view of the hirarchy, 317 // it doesn't have to be the top level of a window 318 319 if (fParent) 320 return fParent->TopView(); 321 322 return this; 323 } 324 325 326 uint32 327 View::CountChildren(bool deep) const 328 { 329 uint32 count = 0; 330 for (View* child = FirstChild(); child; child = child->NextSibling()) { 331 count++; 332 if (deep) { 333 count += child->CountChildren(deep); 334 } 335 } 336 return count; 337 } 338 339 340 void 341 View::CollectTokensForChildren(BList* tokenMap) const 342 { 343 for (View* child = FirstChild(); child; child = child->NextSibling()) { 344 tokenMap->AddItem((void*)child); 345 child->CollectTokensForChildren(tokenMap); 346 } 347 } 348 349 350 #if 0 351 bool 352 View::MarkAt(DrawingEngine* engine, const BPoint& where, int32 level) 353 { 354 BRect rect(fFrame.left, fFrame.top, fFrame.right, fFrame.bottom); 355 356 if (Parent() != NULL) { 357 Parent()->ConvertToScreen(&rect); 358 if (!rect.Contains(where)) 359 return false; 360 361 engine->StrokeRect(rect, (rgb_color){level * 30, level * 30, level * 30}); 362 } 363 364 365 bool found = false; 366 for (View* child = FirstChild(); child; child = child->NextSibling()) { 367 found |= child->MarkAt(engine, where, level + 1); 368 } 369 370 if (!found) { 371 rgb_color color = {0}; 372 switch (level % 2) { 373 case 0: color.green = rand() % 256; break; 374 case 1: color.blue = rand() % 256; break; 375 } 376 377 rect.InsetBy(1, 1); 378 //engine->FillRegion(fLocalClipping, (rgb_color){255, 255, 0, 10}); 379 engine->StrokeRect(rect, color); 380 rect.InsetBy(1, 1); 381 engine->StrokeRect(rect, color); 382 } 383 384 return true; 385 } 386 #endif 387 388 389 void 390 View::FindViews(uint32 flags, BObjectList<View>& list, int32& left) 391 { 392 if ((Flags() & flags) == flags) { 393 list.AddItem(this); 394 left--; 395 return; 396 } 397 398 for (View* child = FirstChild(); child; child = child->NextSibling()) { 399 child->FindViews(flags, list, left); 400 if (left == 0) 401 break; 402 } 403 } 404 405 406 View* 407 View::ViewAt(const BPoint& where) 408 { 409 if (!fVisible) 410 return NULL; 411 412 IntRect frame = Frame(); 413 if (Parent() != NULL) 414 Parent()->ConvertToScreen(&frame); 415 416 if (!frame.Contains(where)) 417 return NULL; 418 419 for (View* child = FirstChild(); child; child = child->NextSibling()) { 420 View* view = child->ViewAt(where); 421 if (view != NULL) 422 return view; 423 } 424 425 return this; 426 } 427 428 429 // #pragma mark - 430 431 432 void 433 View::SetName(const char* string) 434 { 435 fName.SetTo(string); 436 } 437 438 439 void 440 View::SetFlags(uint32 flags) 441 { 442 fFlags = flags; 443 fDrawState->SetSubPixelPrecise(fFlags & B_SUBPIXEL_PRECISE); 444 } 445 446 447 void 448 View::SetDrawingOrigin(BPoint origin) 449 { 450 fDrawState->SetOrigin(origin); 451 452 // rebuild clipping 453 if (fDrawState->HasClipping()) 454 RebuildClipping(false); 455 } 456 457 458 BPoint 459 View::DrawingOrigin() const 460 { 461 BPoint origin(fDrawState->Origin()); 462 float scale = Scale(); 463 464 origin.x *= scale; 465 origin.y *= scale; 466 467 return origin; 468 } 469 470 471 void 472 View::SetScale(float scale) 473 { 474 fDrawState->SetScale(scale); 475 476 // rebuild clipping 477 if (fDrawState->HasClipping()) 478 RebuildClipping(false); 479 } 480 481 482 float 483 View::Scale() const 484 { 485 return CurrentState()->Scale(); 486 } 487 488 489 void 490 View::SetUserClipping(const BRegion* region) 491 { 492 fDrawState->SetClippingRegion(region); 493 494 // rebuild clipping (for just this view) 495 RebuildClipping(false); 496 } 497 498 499 void 500 View::SetViewBitmap(ServerBitmap* bitmap, IntRect sourceRect, 501 IntRect destRect, int32 resizingMode, int32 options) 502 { 503 if (fViewBitmap != NULL) { 504 Overlay* overlay = _Overlay(); 505 506 if (bitmap != NULL) { 507 // take over overlay token from current overlay (if it has any) 508 Overlay* newOverlay = bitmap->Overlay(); 509 510 if (overlay != NULL && newOverlay != NULL) 511 newOverlay->TakeOverToken(overlay); 512 } else if (overlay != NULL) 513 overlay->Hide(); 514 515 gBitmapManager->DeleteBitmap(fViewBitmap); 516 } 517 518 // the caller is allowed to delete the bitmap after setting the background 519 if (bitmap != NULL) 520 bitmap->Acquire(); 521 522 fViewBitmap = bitmap; 523 fBitmapSource = sourceRect; 524 fBitmapDestination = destRect; 525 fBitmapResizingMode = resizingMode; 526 fBitmapOptions = options; 527 528 _UpdateOverlayView(); 529 } 530 531 532 ::Overlay* 533 View::_Overlay() const 534 { 535 if (fViewBitmap == NULL) 536 return NULL; 537 538 return fViewBitmap->Overlay(); 539 } 540 541 542 void 543 View::_UpdateOverlayView() const 544 { 545 Overlay* overlay = _Overlay(); 546 if (overlay == NULL) 547 return; 548 549 IntRect destination = fBitmapDestination; 550 ConvertToScreen(&destination); 551 552 overlay->Configure(fBitmapSource, destination); 553 } 554 555 556 /*! 557 This method is called whenever the window is resized or moved - would 558 be nice to have a better solution for this, though. 559 */ 560 void 561 View::UpdateOverlay() 562 { 563 if (!IsVisible()) 564 return; 565 566 if (_Overlay() != NULL) { 567 _UpdateOverlayView(); 568 } else { 569 // recursively ask children of this view 570 571 for (View* child = FirstChild(); child; child = child->NextSibling()) { 572 child->UpdateOverlay(); 573 } 574 } 575 } 576 577 578 // #pragma mark - 579 580 581 void 582 View::ConvertToParent(BPoint* point) const 583 { 584 // remove scrolling offset and convert to parent coordinate space 585 point->x += fFrame.left - fScrollingOffset.x; 586 point->y += fFrame.top - fScrollingOffset.y; 587 } 588 589 590 void 591 View::ConvertToParent(IntPoint* point) const 592 { 593 // remove scrolling offset and convert to parent coordinate space 594 point->x += fFrame.left - fScrollingOffset.x; 595 point->y += fFrame.top - fScrollingOffset.y; 596 } 597 598 599 void 600 View::ConvertToParent(BRect* rect) const 601 { 602 // remove scrolling offset and convert to parent coordinate space 603 rect->OffsetBy(fFrame.left - fScrollingOffset.x, 604 fFrame.top - fScrollingOffset.y); 605 } 606 607 608 void 609 View::ConvertToParent(IntRect* rect) const 610 { 611 // remove scrolling offset and convert to parent coordinate space 612 rect->OffsetBy(fFrame.left - fScrollingOffset.x, 613 fFrame.top - fScrollingOffset.y); 614 } 615 616 617 void 618 View::ConvertToParent(BRegion* region) const 619 { 620 // remove scrolling offset and convert to parent coordinate space 621 region->OffsetBy(fFrame.left - fScrollingOffset.x, 622 fFrame.top - fScrollingOffset.y); 623 } 624 625 626 void 627 View::ConvertFromParent(BPoint* point) const 628 { 629 // convert from parent coordinate space amd add scrolling offset 630 point->x += fScrollingOffset.x - fFrame.left; 631 point->y += fScrollingOffset.y - fFrame.top; 632 } 633 634 635 void 636 View::ConvertFromParent(IntPoint* point) const 637 { 638 // convert from parent coordinate space amd add scrolling offset 639 point->x += fScrollingOffset.x - fFrame.left; 640 point->y += fScrollingOffset.y - fFrame.top; 641 } 642 643 644 void 645 View::ConvertFromParent(BRect* rect) const 646 { 647 // convert from parent coordinate space amd add scrolling offset 648 rect->OffsetBy(fScrollingOffset.x - fFrame.left, 649 fScrollingOffset.y - fFrame.top); 650 } 651 652 653 void 654 View::ConvertFromParent(IntRect* rect) const 655 { 656 // convert from parent coordinate space amd add scrolling offset 657 rect->OffsetBy(fScrollingOffset.x - fFrame.left, 658 fScrollingOffset.y - fFrame.top); 659 } 660 661 662 void 663 View::ConvertFromParent(BRegion* region) const 664 { 665 // convert from parent coordinate space amd add scrolling offset 666 region->OffsetBy(fScrollingOffset.x - fFrame.left, 667 fScrollingOffset.y - fFrame.top); 668 } 669 670 //! converts a point from local to screen coordinate system 671 void 672 View::ConvertToScreen(BPoint* pt) const 673 { 674 ConvertToParent(pt); 675 676 if (fParent) 677 fParent->ConvertToScreen(pt); 678 } 679 680 681 //! converts a point from local to screen coordinate system 682 void 683 View::ConvertToScreen(IntPoint* pt) const 684 { 685 ConvertToParent(pt); 686 687 if (fParent) 688 fParent->ConvertToScreen(pt); 689 } 690 691 692 //! converts a rect from local to screen coordinate system 693 void 694 View::ConvertToScreen(BRect* rect) const 695 { 696 BPoint offset(0.0, 0.0); 697 ConvertToScreen(&offset); 698 699 rect->OffsetBy(offset); 700 } 701 702 703 //! converts a rect from local to screen coordinate system 704 void 705 View::ConvertToScreen(IntRect* rect) const 706 { 707 BPoint offset(0.0, 0.0); 708 ConvertToScreen(&offset); 709 710 rect->OffsetBy(offset); 711 } 712 713 714 //! converts a region from local to screen coordinate system 715 void 716 View::ConvertToScreen(BRegion* region) const 717 { 718 BPoint offset(0.0, 0.0); 719 ConvertToScreen(&offset); 720 721 region->OffsetBy((int)offset.x, (int)offset.y); 722 } 723 724 725 //! converts a point from screen to local coordinate system 726 void 727 View::ConvertFromScreen(BPoint* pt) const 728 { 729 ConvertFromParent(pt); 730 731 if (fParent) 732 fParent->ConvertFromScreen(pt); 733 } 734 735 736 //! converts a point from screen to local coordinate system 737 void 738 View::ConvertFromScreen(IntPoint* pt) const 739 { 740 ConvertFromParent(pt); 741 742 if (fParent) 743 fParent->ConvertFromScreen(pt); 744 } 745 746 747 //! converts a rect from screen to local coordinate system 748 void 749 View::ConvertFromScreen(BRect* rect) const 750 { 751 BPoint offset(0.0, 0.0); 752 ConvertFromScreen(&offset); 753 754 rect->OffsetBy(offset.x, offset.y); 755 } 756 757 758 //! converts a rect from screen to local coordinate system 759 void 760 View::ConvertFromScreen(IntRect* rect) const 761 { 762 BPoint offset(0.0, 0.0); 763 ConvertFromScreen(&offset); 764 765 rect->OffsetBy((int)offset.x, (int)offset.y); 766 } 767 768 769 //! converts a region from screen to local coordinate system 770 void 771 View::ConvertFromScreen(BRegion* region) const 772 { 773 BPoint offset(0.0, 0.0); 774 ConvertFromScreen(&offset); 775 776 region->OffsetBy((int)offset.x, (int)offset.y); 777 } 778 779 780 //! converts a point from local *drawing* to screen coordinate system 781 void 782 View::ConvertToScreenForDrawing(BPoint* point) const 783 { 784 fDrawState->Transform(point); 785 // NOTE: from here on, don't use the 786 // "*ForDrawing()" versions of the parent! 787 ConvertToScreen(point); 788 } 789 790 791 //! converts a rect from local *drawing* to screen coordinate system 792 void 793 View::ConvertToScreenForDrawing(BRect* rect) const 794 { 795 fDrawState->Transform(rect); 796 // NOTE: from here on, don't use the 797 // "*ForDrawing()" versions of the parent! 798 ConvertToScreen(rect); 799 } 800 801 802 //! converts a region from local *drawing* to screen coordinate system 803 void 804 View::ConvertToScreenForDrawing(BRegion* region) const 805 { 806 fDrawState->Transform(region); 807 // NOTE: from here on, don't use the 808 // "*ForDrawing()" versions of the parent! 809 ConvertToScreen(region); 810 } 811 812 813 //! converts a gradient from local *drawing* to screen coordinate system 814 void 815 View::ConvertToScreenForDrawing(BGradient* gradient) const 816 { 817 switch(gradient->GetType()) { 818 case BGradient::TYPE_LINEAR: { 819 BGradientLinear* linear = (BGradientLinear*) gradient; 820 BPoint start = linear->Start(); 821 BPoint end = linear->End(); 822 fDrawState->Transform(&start); 823 ConvertToScreen(&start); 824 fDrawState->Transform(&end); 825 ConvertToScreen(&end); 826 linear->SetStart(start); 827 linear->SetEnd(end); 828 linear->SortColorStopsByOffset(); 829 break; 830 } 831 case BGradient::TYPE_RADIAL: { 832 BGradientRadial* radial = (BGradientRadial*) gradient; 833 BPoint center = radial->Center(); 834 fDrawState->Transform(¢er); 835 ConvertToScreen(¢er); 836 radial->SetCenter(center); 837 radial->SortColorStopsByOffset(); 838 break; 839 } 840 case BGradient::TYPE_RADIAL_FOCUS: { 841 BGradientRadialFocus* radialFocus = (BGradientRadialFocus*) gradient; 842 BPoint center = radialFocus->Center(); 843 BPoint focal = radialFocus->Focal(); 844 fDrawState->Transform(¢er); 845 ConvertToScreen(¢er); 846 fDrawState->Transform(&focal); 847 ConvertToScreen(&focal); 848 radialFocus->SetCenter(center); 849 radialFocus->SetFocal(focal); 850 radialFocus->SortColorStopsByOffset(); 851 break; 852 } 853 case BGradient::TYPE_DIAMOND: { 854 BGradientDiamond* diamond = (BGradientDiamond*) gradient; 855 BPoint center = diamond->Center(); 856 fDrawState->Transform(¢er); 857 ConvertToScreen(¢er); 858 diamond->SetCenter(center); 859 diamond->SortColorStopsByOffset(); 860 break; 861 } 862 case BGradient::TYPE_CONIC: { 863 BGradientConic* conic = (BGradientConic*) gradient; 864 BPoint center = conic->Center(); 865 fDrawState->Transform(¢er); 866 ConvertToScreen(¢er); 867 conic->SetCenter(center); 868 conic->SortColorStopsByOffset(); 869 break; 870 } 871 case BGradient::TYPE_NONE: { 872 break; 873 } 874 } 875 } 876 877 878 //! converts points from local *drawing* to screen coordinate system 879 void 880 View::ConvertToScreenForDrawing(BPoint* dst, const BPoint* src, int32 num) const 881 { 882 // TODO: optimize this, it should be smarter 883 while (num--) { 884 *dst = *src; 885 fDrawState->Transform(dst); 886 // NOTE: from here on, don't use the 887 // "*ForDrawing()" versions of the parent! 888 ConvertToScreen(dst); 889 src++; 890 dst++; 891 } 892 } 893 894 895 //! converts rects from local *drawing* to screen coordinate system 896 void 897 View::ConvertToScreenForDrawing(BRect* dst, const BRect* src, int32 num) const 898 { 899 // TODO: optimize this, it should be smarter 900 while (num--) { 901 *dst = *src; 902 fDrawState->Transform(dst); 903 // NOTE: from here on, don't use the 904 // "*ForDrawing()" versions of the parent! 905 ConvertToScreen(dst); 906 src++; 907 dst++; 908 } 909 } 910 911 912 //! converts regions from local *drawing* to screen coordinate system 913 void 914 View::ConvertToScreenForDrawing(BRegion* dst, const BRegion* src, int32 num) const 915 { 916 // TODO: optimize this, it should be smarter 917 while (num--) { 918 *dst = *src; 919 fDrawState->Transform(dst); 920 // NOTE: from here on, don't use the 921 // "*ForDrawing()" versions of the parent! 922 ConvertToScreen(dst); 923 src++; 924 dst++; 925 } 926 } 927 928 929 //! converts a point from screen to local coordinate system 930 void 931 View::ConvertFromScreenForDrawing(BPoint* point) const 932 { 933 ConvertFromScreen(point); 934 fDrawState->InverseTransform(point); 935 } 936 937 938 // #pragma mark - 939 940 941 void 942 View::MoveBy(int32 x, int32 y, BRegion* dirtyRegion) 943 { 944 if (x == 0 && y == 0) 945 return; 946 947 fFrame.OffsetBy(x, y); 948 949 // to move on screen, we must not be hidden and we must have a parent 950 if (fVisible && fParent && dirtyRegion) { 951 #if 1 952 // based on redraw on new location 953 // the place were we are now visible 954 IntRect newVisibleBounds(Bounds()); 955 // we can use the frame of the old 956 // local clipping to see which parts need invalidation 957 IntRect oldVisibleBounds(newVisibleBounds); 958 oldVisibleBounds.OffsetBy(-x, -y); 959 ConvertToScreen(&oldVisibleBounds); 960 961 ConvertToVisibleInTopView(&newVisibleBounds); 962 963 dirtyRegion->Include((clipping_rect)oldVisibleBounds); 964 // newVisibleBounds already is in screen coords 965 dirtyRegion->Include((clipping_rect)newVisibleBounds); 966 #else 967 // blitting version, invalidates 968 // old contents 969 IntRect oldVisibleBounds(Bounds()); 970 IntRect newVisibleBounds(oldVisibleBounds); 971 oldVisibleBounds.OffsetBy(-x, -y); 972 ConvertToScreen(&oldVisibleBounds); 973 974 // NOTE: using ConvertToVisibleInTopView() 975 // instead of ConvertToScreen()! see below 976 ConvertToVisibleInTopView(&newVisibleBounds); 977 978 newVisibleBounds.OffsetBy(-x, -y); 979 980 // clipping oldVisibleBounds to newVisibleBounds 981 // makes sure we don't copy parts hidden under 982 // parent views 983 BRegion* region = fWindow->GetRegion(); 984 if (region) { 985 region->Set(oldVisibleBounds & newVisibleBounds); 986 fWindow->CopyContents(region, x, y); 987 988 region->Set(oldVisibleBounds); 989 newVisibleBounds.OffsetBy(x, y); 990 region->Exclude((clipping_rect)newVisibleBounds); 991 dirtyRegion->Include(dirty); 992 993 fWindow->RecycleRegion(region); 994 } 995 996 #endif 997 } 998 999 if (!fParent) { 1000 // the top view's screen clipping does not change, 1001 // because no parts are clipped away from parent 1002 // views 1003 _MoveScreenClipping(x, y, true); 1004 } else { 1005 // parts might have been revealed from underneath 1006 // the parent, or might now be hidden underneath 1007 // the parent, this is taken care of when building 1008 // the screen clipping 1009 InvalidateScreenClipping(); 1010 } 1011 } 1012 1013 1014 void 1015 View::ResizeBy(int32 x, int32 y, BRegion* dirtyRegion) 1016 { 1017 if (x == 0 && y == 0) 1018 return; 1019 1020 fFrame.right += x; 1021 fFrame.bottom += y; 1022 1023 if (fVisible && dirtyRegion) { 1024 IntRect oldBounds(Bounds()); 1025 oldBounds.right -= x; 1026 oldBounds.bottom -= y; 1027 1028 BRegion* dirty = fWindow->GetRegion(); 1029 if (!dirty) 1030 return; 1031 1032 dirty->Set((clipping_rect)Bounds()); 1033 dirty->Include((clipping_rect)oldBounds); 1034 1035 if (!(fFlags & B_FULL_UPDATE_ON_RESIZE)) { 1036 // the dirty region is just the difference of 1037 // old and new bounds 1038 dirty->Exclude((clipping_rect)(oldBounds & Bounds())); 1039 } 1040 1041 InvalidateScreenClipping(); 1042 1043 if (dirty->CountRects() > 0) { 1044 if ((fFlags & B_DRAW_ON_CHILDREN) == 0) { 1045 // exclude children, they are expected to 1046 // include their own dirty regions in ParentResized() 1047 for (View* child = FirstChild(); child; 1048 child = child->NextSibling()) { 1049 if (!child->IsVisible()) 1050 continue; 1051 IntRect previousChildVisible( 1052 child->Frame() & oldBounds & Bounds()); 1053 if (dirty->Frame().Intersects(previousChildVisible)) { 1054 dirty->Exclude((clipping_rect)previousChildVisible); 1055 } 1056 } 1057 } 1058 1059 ConvertToScreen(dirty); 1060 dirtyRegion->Include(dirty); 1061 } 1062 fWindow->RecycleRegion(dirty); 1063 } 1064 1065 // layout the children 1066 for (View* child = FirstChild(); child; child = child->NextSibling()) 1067 child->ParentResized(x, y, dirtyRegion); 1068 1069 // view bitmap 1070 1071 resize_frame(fBitmapDestination, fBitmapResizingMode, x, y); 1072 1073 // at this point, children are at their new locations, 1074 // so we can rebuild the clipping 1075 // TODO: when the implementation of Hide() and Show() is 1076 // complete, see if this should be avoided 1077 RebuildClipping(false); 1078 } 1079 1080 1081 void 1082 View::ParentResized(int32 x, int32 y, BRegion* dirtyRegion) 1083 { 1084 IntRect newFrame = fFrame; 1085 resize_frame(newFrame, fResizeMode & 0x0000ffff, x, y); 1086 1087 if (newFrame != fFrame) { 1088 // careful, MoveBy will change fFrame 1089 int32 widthDiff = (int32)(newFrame.Width() - fFrame.Width()); 1090 int32 heightDiff = (int32)(newFrame.Height() - fFrame.Height()); 1091 1092 MoveBy(newFrame.left - fFrame.left, 1093 newFrame.top - fFrame.top, dirtyRegion); 1094 1095 ResizeBy(widthDiff, heightDiff, dirtyRegion); 1096 } else { 1097 // TODO: this covers the fact that our screen clipping might change 1098 // when the parent changes its size, even though our frame stays 1099 // the same - there might be a way to test for this, but axeld doesn't 1100 // know, stippi should look into this when he's back :) 1101 InvalidateScreenClipping(); 1102 } 1103 } 1104 1105 1106 void 1107 View::ScrollBy(int32 x, int32 y, BRegion* dirtyRegion) 1108 { 1109 if (!fVisible || !fWindow) { 1110 fScrollingOffset.x += x; 1111 fScrollingOffset.y += y; 1112 return; 1113 } 1114 1115 // blitting version, invalidates 1116 // old contents 1117 1118 // remember old bounds for tracking dirty region 1119 IntRect oldBounds(Bounds()); 1120 1121 // NOTE: using ConvertToVisibleInTopView() 1122 // instead of ConvertToScreen(), this makes 1123 // sure we don't try to move or invalidate an 1124 // area hidden underneath the parent view 1125 ConvertToVisibleInTopView(&oldBounds); 1126 1127 // find the area of the view that can be scrolled, 1128 // contents are shifted in the opposite direction from scrolling 1129 IntRect stillVisibleBounds(oldBounds); 1130 stillVisibleBounds.OffsetBy(x, y); 1131 stillVisibleBounds = stillVisibleBounds & oldBounds; 1132 1133 fScrollingOffset.x += x; 1134 fScrollingOffset.y += y; 1135 1136 // do the blit, this will make sure 1137 // that other more complex dirty regions 1138 // are taken care of 1139 BRegion* copyRegion = fWindow->GetRegion(); 1140 if (!copyRegion) 1141 return; 1142 copyRegion->Set((clipping_rect)stillVisibleBounds); 1143 fWindow->CopyContents(copyRegion, -x, -y); 1144 1145 // find the dirty region as far as we are 1146 // concerned 1147 BRegion* dirty = copyRegion; 1148 // reuse copyRegion and call it dirty 1149 1150 dirty->Set((clipping_rect)oldBounds); 1151 stillVisibleBounds.OffsetBy(-x, -y); 1152 dirty->Exclude((clipping_rect)stillVisibleBounds); 1153 dirtyRegion->Include(dirty); 1154 1155 fWindow->RecycleRegion(dirty); 1156 1157 // the screen clipping of this view and it's 1158 // childs is no longer valid 1159 InvalidateScreenClipping(); 1160 RebuildClipping(false); 1161 } 1162 1163 1164 void 1165 View::CopyBits(IntRect src, IntRect dst, BRegion& windowContentClipping) 1166 { 1167 if (!fVisible || !fWindow) 1168 return; 1169 1170 // TODO: confirm that in R5 this call is affected by origin and scale 1171 1172 // blitting version 1173 1174 int32 xOffset = dst.left - src.left; 1175 int32 yOffset = dst.top - src.top; 1176 1177 // figure out which part can be blittet 1178 IntRect visibleSrc(src); 1179 ConvertToVisibleInTopView(&visibleSrc); 1180 1181 IntRect visibleSrcAtDest(src); 1182 visibleSrcAtDest.OffsetBy(xOffset, yOffset); 1183 ConvertToVisibleInTopView(&visibleSrcAtDest); 1184 1185 // clip src to visible at dest 1186 visibleSrcAtDest.OffsetBy(-xOffset, -yOffset); 1187 visibleSrc = visibleSrc & visibleSrcAtDest; 1188 1189 // do the blit, this will make sure 1190 // that other more complex dirty regions 1191 // are taken care of 1192 BRegion* copyRegion = fWindow->GetRegion(); 1193 if (!copyRegion) 1194 return; 1195 1196 // move src rect to destination here for efficiency reasons 1197 visibleSrc.OffsetBy(xOffset, yOffset); 1198 1199 // we need to interstect the copyRegion two times, onces 1200 // at the source and once at the destination (here done 1201 // the other way arround but it doesn't matter) 1202 // the reason for this is that we are not supposed to visually 1203 // copy children in the source rect and neither to copy onto 1204 // children in the destination rect... 1205 copyRegion->Set((clipping_rect)visibleSrc); 1206 BRegion *screenAndUserClipping 1207 = &ScreenAndUserClipping(&windowContentClipping); 1208 copyRegion->IntersectWith(screenAndUserClipping); 1209 copyRegion->OffsetBy(-xOffset, -yOffset); 1210 copyRegion->IntersectWith(screenAndUserClipping); 1211 1212 // do the actual blit 1213 fWindow->CopyContents(copyRegion, xOffset, yOffset); 1214 1215 // find the dirty region as far as we are concerned 1216 IntRect dirtyDst(dst); 1217 ConvertToVisibleInTopView(&dirtyDst); 1218 1219 BRegion* dirty = fWindow->GetRegion(); 1220 if (!dirty) { 1221 fWindow->RecycleRegion(copyRegion); 1222 return; 1223 } 1224 1225 // offset copyRegion to destination again 1226 copyRegion->OffsetBy(xOffset, yOffset); 1227 // start with destination given by user 1228 dirty->Set((clipping_rect)dirtyDst); 1229 // exclude the part that we could copy 1230 dirty->Exclude(copyRegion); 1231 1232 dirty->IntersectWith(screenAndUserClipping); 1233 fWindow->MarkContentDirty(*dirty); 1234 1235 fWindow->RecycleRegion(dirty); 1236 fWindow->RecycleRegion(copyRegion); 1237 } 1238 1239 1240 // #pragma mark - 1241 1242 1243 void 1244 View::PushState() 1245 { 1246 DrawState* newState = fDrawState->PushState(); 1247 if (newState) { 1248 fDrawState = newState; 1249 fDrawState->SetSubPixelPrecise(fFlags & B_SUBPIXEL_PRECISE); 1250 } 1251 } 1252 1253 1254 void 1255 View::PopState() 1256 { 1257 if (fDrawState->PreviousState() == NULL) { 1258 fprintf(stderr, "WARNING: User called BView(%s)::PopState(), " 1259 "but there is NO state on stack!\n", Name()); 1260 return; 1261 } 1262 1263 bool rebuildClipping = fDrawState->HasAdditionalClipping(); 1264 1265 fDrawState = fDrawState->PopState(); 1266 fDrawState->SetSubPixelPrecise(fFlags & B_SUBPIXEL_PRECISE); 1267 1268 // rebuild clipping 1269 // (the clipping from the popped state is not effective anymore) 1270 if (rebuildClipping) 1271 RebuildClipping(false); 1272 } 1273 1274 1275 void 1276 View::SetEventMask(uint32 eventMask, uint32 options) 1277 { 1278 fEventMask = eventMask; 1279 fEventOptions = options; 1280 } 1281 1282 1283 void 1284 View::SetCursor(ServerCursor *cursor) 1285 { 1286 if (cursor == fCursor) 1287 return; 1288 1289 if (fCursor) 1290 fCursor->Release(); 1291 1292 fCursor = cursor; 1293 1294 if (fCursor) { 1295 fCursor->Acquire(); 1296 fCursor->SetPendingViewCursor(false); 1297 } 1298 } 1299 1300 1301 void 1302 View::SetPicture(ServerPicture *picture) 1303 { 1304 fPicture = picture; 1305 } 1306 1307 1308 void 1309 View::Draw(DrawingEngine* drawingEngine, BRegion* effectiveClipping, 1310 BRegion* windowContentClipping, bool deep) 1311 { 1312 if (!fVisible) { 1313 // child views cannot be visible either 1314 return; 1315 } 1316 1317 if (fViewBitmap != NULL || fViewColor != B_TRANSPARENT_COLOR) { 1318 // we can only draw within our own area 1319 BRegion* redraw; 1320 if ((fFlags & B_DRAW_ON_CHILDREN) != 0) { 1321 // The client may actually want to prevent the background to 1322 // be painted outside the user clipping. 1323 redraw = fWindow->GetRegion( 1324 ScreenAndUserClipping(windowContentClipping)); 1325 } else { 1326 // Ignore user clipping as in BeOS for painting the background. 1327 redraw = fWindow->GetRegion( 1328 _ScreenClipping(windowContentClipping)); 1329 } 1330 if (!redraw) 1331 return; 1332 // add the current clipping 1333 redraw->IntersectWith(effectiveClipping); 1334 1335 Overlay* overlayCookie = _Overlay(); 1336 1337 if (fViewBitmap != NULL && overlayCookie == NULL) { 1338 // draw view bitmap 1339 // TODO: support other options! 1340 BRect rect = fBitmapDestination; 1341 ConvertToScreenForDrawing(&rect); 1342 1343 align_rect_to_pixels(&rect); 1344 1345 if (fBitmapOptions & B_TILE_BITMAP_Y) { 1346 // move rect up as much as needed 1347 while (rect.top > redraw->Frame().top) 1348 rect.OffsetBy(0.0, -(rect.Height() + 1)); 1349 } 1350 if (fBitmapOptions & B_TILE_BITMAP_X) { 1351 // move rect left as much as needed 1352 while (rect.left > redraw->Frame().left) 1353 rect.OffsetBy(-(rect.Width() + 1), 0.0); 1354 } 1355 1356 // XXX: locking removed because the Window keeps the engine locked 1357 // because it keeps track of syncing right now 1358 1359 // lock the drawing engine for as long as we need the clipping 1360 // to be valid 1361 if (rect.IsValid()/* && drawingEngine->Lock()*/) { 1362 drawingEngine->ConstrainClippingRegion(redraw); 1363 1364 drawing_mode oldMode; 1365 drawingEngine->SetDrawingMode(B_OP_COPY, oldMode); 1366 1367 if (fBitmapOptions & B_TILE_BITMAP) { 1368 // tile across entire view 1369 1370 float start = rect.left; 1371 while (rect.top < redraw->Frame().bottom) { 1372 while (rect.left < redraw->Frame().right) { 1373 drawingEngine->DrawBitmap(fViewBitmap, 1374 fBitmapSource, rect, fBitmapOptions); 1375 rect.OffsetBy(rect.Width() + 1, 0.0); 1376 } 1377 rect.OffsetBy(start - rect.left, rect.Height() + 1); 1378 } 1379 // nothing left to be drawn 1380 redraw->MakeEmpty(); 1381 } else if (fBitmapOptions & B_TILE_BITMAP_X) { 1382 // tile in x direction 1383 1384 while (rect.left < redraw->Frame().right) { 1385 drawingEngine->DrawBitmap(fViewBitmap, fBitmapSource, 1386 rect, fBitmapOptions); 1387 rect.OffsetBy(rect.Width() + 1, 0.0); 1388 } 1389 // remove horizontal stripe from clipping 1390 rect.left = redraw->Frame().left; 1391 rect.right = redraw->Frame().right; 1392 redraw->Exclude(rect); 1393 } else if (fBitmapOptions & B_TILE_BITMAP_Y) { 1394 // tile in y direction 1395 1396 while (rect.top < redraw->Frame().bottom) { 1397 drawingEngine->DrawBitmap(fViewBitmap, fBitmapSource, 1398 rect, fBitmapOptions); 1399 rect.OffsetBy(0.0, rect.Height() + 1); 1400 } 1401 // remove vertical stripe from clipping 1402 rect.top = redraw->Frame().top; 1403 rect.bottom = redraw->Frame().bottom; 1404 redraw->Exclude(rect); 1405 } else { 1406 // no tiling at all 1407 1408 drawingEngine->DrawBitmap(fViewBitmap, fBitmapSource, 1409 rect, fBitmapOptions); 1410 redraw->Exclude(rect); 1411 } 1412 1413 drawingEngine->SetDrawingMode(oldMode); 1414 1415 // NOTE: It is ok not to reset the clipping, that 1416 // would only waste time 1417 // drawingEngine->Unlock(); 1418 } 1419 1420 } 1421 1422 if (fViewColor != B_TRANSPARENT_COLOR) { 1423 // fill visible region with view color, 1424 // this version of FillRegion ignores any 1425 // clipping, that's why "redraw" needs to 1426 // be correct 1427 // see #634 1428 // if (redraw->Frame().left < 0 || redraw->Frame().top < 0) { 1429 // char message[1024]; 1430 // BRect c = effectiveClipping->Frame(); 1431 // BRect w = windowContentClipping->Frame(); 1432 // BRect r = redraw->Frame(); 1433 // sprintf(message, "invalid background: current clipping: (%d, %d)->(%d, %d), " 1434 // "window content: (%d, %d)->(%d, %d), redraw: (%d, %d)->(%d, %d)", 1435 // (int)c.left, (int)c.top, (int)c.right, (int)c.bottom, 1436 // (int)w.left, (int)w.top, (int)w.right, (int)w.bottom, 1437 // (int)r.left, (int)r.top, (int)r.right, (int)r.bottom); 1438 // debugger(message); 1439 // } 1440 1441 drawingEngine->FillRegion(*redraw, overlayCookie != NULL 1442 ? overlayCookie->Color() : fViewColor); 1443 } 1444 1445 fWindow->RecycleRegion(redraw); 1446 } 1447 1448 fBackgroundDirty = false; 1449 1450 // let children draw 1451 if (deep) { 1452 for (View* child = FirstChild(); child; child = child->NextSibling()) { 1453 child->Draw(drawingEngine, effectiveClipping, 1454 windowContentClipping, deep); 1455 } 1456 } 1457 } 1458 1459 1460 // #pragma mark - 1461 1462 1463 void 1464 View::MouseDown(BMessage* message, BPoint where) 1465 { 1466 // empty hook method 1467 } 1468 1469 1470 void 1471 View::MouseUp(BMessage* message, BPoint where) 1472 { 1473 // empty hook method 1474 } 1475 1476 1477 void 1478 View::MouseMoved(BMessage* message, BPoint where) 1479 { 1480 // empty hook method 1481 } 1482 1483 1484 // #pragma mark - 1485 1486 1487 void 1488 View::SetHidden(bool hidden) 1489 { 1490 if (fHidden != hidden) { 1491 fHidden = hidden; 1492 1493 // recurse into children and update their visible flag 1494 bool oldVisible = fVisible; 1495 UpdateVisibleDeep(fParent ? fParent->IsVisible() : !fHidden); 1496 if (oldVisible != fVisible) { 1497 // Include or exclude us from the parent area, and update the 1498 // children's clipping as well when the view will be visible 1499 if (fParent) 1500 fParent->RebuildClipping(fVisible); 1501 else 1502 RebuildClipping(fVisible); 1503 1504 if (fWindow) { 1505 // trigger a redraw 1506 IntRect clippedBounds = Bounds(); 1507 ConvertToVisibleInTopView(&clippedBounds); 1508 BRegion* dirty = fWindow->GetRegion(); 1509 if (!dirty) 1510 return; 1511 dirty->Set((clipping_rect)clippedBounds); 1512 fWindow->MarkContentDirty(*dirty); 1513 fWindow->RecycleRegion(dirty); 1514 } 1515 } 1516 } 1517 } 1518 1519 1520 bool 1521 View::IsHidden() const 1522 { 1523 return fHidden; 1524 } 1525 1526 1527 void 1528 View::UpdateVisibleDeep(bool parentVisible) 1529 { 1530 bool wasVisible = fVisible; 1531 1532 fVisible = parentVisible && !fHidden; 1533 for (View* child = FirstChild(); child; child = child->NextSibling()) 1534 child->UpdateVisibleDeep(fVisible); 1535 1536 // overlay handling 1537 1538 Overlay* overlay = _Overlay(); 1539 if (overlay == NULL) 1540 return; 1541 1542 if (fVisible && !wasVisible) 1543 _UpdateOverlayView(); 1544 else if (!fVisible && wasVisible) 1545 overlay->Hide(); 1546 } 1547 1548 1549 // #pragma mark - 1550 1551 1552 void 1553 View::MarkBackgroundDirty() 1554 { 1555 if (fBackgroundDirty) 1556 return; 1557 fBackgroundDirty = true; 1558 for (View* child = FirstChild(); child; child = child->NextSibling()) 1559 child->MarkBackgroundDirty(); 1560 } 1561 1562 1563 void 1564 View::AddTokensForViewsInRegion(BPrivate::PortLink& link, BRegion& region, 1565 BRegion* windowContentClipping) 1566 { 1567 if (!fVisible) 1568 return; 1569 1570 { 1571 // NOTE: use scope in order to reduce stack space requirements 1572 1573 // This check will prevent descending the view hierarchy 1574 // any further than necessary 1575 IntRect screenBounds(Bounds()); 1576 ConvertToScreen(&screenBounds); 1577 if (!region.Intersects((clipping_rect)screenBounds)) 1578 return; 1579 1580 // Unfortunately, we intersecting another region, but otherwise 1581 // we couldn't provide the exact update rect to the client 1582 BRegion localDirty = _ScreenClipping(windowContentClipping); 1583 localDirty.IntersectWith(®ion); 1584 if (localDirty.CountRects() > 0) { 1585 link.Attach<int32>(fToken); 1586 link.Attach<BRect>(localDirty.Frame()); 1587 } 1588 } 1589 1590 for (View* child = FirstChild(); child; child = child->NextSibling()) 1591 child->AddTokensForViewsInRegion(link, region, windowContentClipping); 1592 } 1593 1594 1595 void 1596 View::PrintToStream() const 1597 { 1598 printf("View: %s\n", Name()); 1599 printf(" fToken: %ld\n", fToken); 1600 printf(" fFrame: IntRect(%ld, %ld, %ld, %ld)\n", fFrame.left, fFrame.top, fFrame.right, fFrame.bottom); 1601 printf(" fScrollingOffset: IntPoint(%ld, %ld)\n", fScrollingOffset.x, fScrollingOffset.y); 1602 printf(" fHidden: %d\n", fHidden); 1603 printf(" fVisible: %d\n", fVisible); 1604 printf(" fWindow: %p\n", fWindow); 1605 printf(" fParent: %p\n", fParent); 1606 printf(" fLocalClipping:\n"); 1607 fLocalClipping.PrintToStream(); 1608 printf(" fScreenClipping:\n"); 1609 fScreenClipping.PrintToStream(); 1610 printf(" valid: %d\n", fScreenClippingValid); 1611 1612 printf(" fUserClipping:\n"); 1613 if (fUserClipping != NULL) 1614 fUserClipping->PrintToStream(); 1615 else 1616 printf(" none\n"); 1617 1618 printf(" fScreenAndUserClipping:\n"); 1619 if (fScreenAndUserClipping != NULL) 1620 fScreenAndUserClipping->PrintToStream(); 1621 else 1622 printf(" invalid\n"); 1623 1624 printf(" state:\n"); 1625 printf(" user clipping: %d\n", fDrawState->HasClipping()); 1626 BPoint origin = fDrawState->CombinedOrigin(); 1627 printf(" origin: BPoint(%.1f, %.1f)\n", origin.x, origin.y); 1628 printf(" scale: %.2f\n", fDrawState->CombinedScale()); 1629 printf("\n"); 1630 } 1631 1632 1633 void 1634 View::RebuildClipping(bool deep) 1635 { 1636 // the clipping spans over the bounds area 1637 fLocalClipping.Set((clipping_rect)Bounds()); 1638 1639 if (View* child = FirstChild()) { 1640 // if this view does not draw over children, 1641 // exclude all children from the clipping 1642 if ((fFlags & B_DRAW_ON_CHILDREN) == 0) { 1643 BRegion* childrenRegion = fWindow->GetRegion(); 1644 if (!childrenRegion) 1645 return; 1646 1647 for (; child; child = child->NextSibling()) { 1648 if (child->IsVisible()) 1649 childrenRegion->Include((clipping_rect)child->Frame()); 1650 } 1651 1652 fLocalClipping.Exclude(childrenRegion); 1653 fWindow->RecycleRegion(childrenRegion); 1654 } 1655 // if the operation is "deep", make children rebuild their 1656 // clipping too 1657 if (deep) { 1658 for (child = FirstChild(); child; child = child->NextSibling()) 1659 child->RebuildClipping(true); 1660 } 1661 } 1662 1663 // add the user clipping in case there is one 1664 if (fDrawState->HasClipping()) { 1665 // NOTE: in case the user sets a user defined clipping region, 1666 // rebuilding the clipping is a bit more expensive because there 1667 // is no separate "drawing region"... on the other 1668 // hand, views for which this feature is actually used will 1669 // probably not have any children, so it is not that expensive 1670 // after all 1671 if (fUserClipping == NULL) { 1672 fUserClipping = new (nothrow) BRegion; 1673 if (fUserClipping == NULL) 1674 return; 1675 } 1676 1677 fDrawState->GetCombinedClippingRegion(fUserClipping); 1678 } else { 1679 delete fUserClipping; 1680 fUserClipping = NULL; 1681 } 1682 1683 delete fScreenAndUserClipping; 1684 fScreenAndUserClipping = NULL; 1685 fScreenClippingValid = false; 1686 } 1687 1688 1689 BRegion& 1690 View::ScreenAndUserClipping(BRegion* windowContentClipping, bool force) const 1691 { 1692 // no user clipping - return screen clipping directly 1693 if (fUserClipping == NULL) 1694 return _ScreenClipping(windowContentClipping, force); 1695 1696 // combined screen and user clipping already valid 1697 if (fScreenAndUserClipping != NULL) 1698 return *fScreenAndUserClipping; 1699 1700 // build a new combined user and screen clipping 1701 fScreenAndUserClipping = new (nothrow) BRegion(*fUserClipping); 1702 if (fScreenAndUserClipping == NULL) 1703 return fScreenClipping; 1704 1705 ConvertToScreen(fScreenAndUserClipping); 1706 fScreenAndUserClipping->IntersectWith( 1707 &_ScreenClipping(windowContentClipping, force)); 1708 return *fScreenAndUserClipping; 1709 } 1710 1711 1712 void 1713 View::InvalidateScreenClipping() 1714 { 1715 // TODO: appearantly, we are calling ScreenClipping() on 1716 // views who's parents don't have a valid screen clipping yet, 1717 // this messes up the logic that for any given view with 1718 // fScreenClippingValid == false, all children have 1719 // fScreenClippingValid == false too. If this could be made the 1720 // case, we could save some performance here with the commented 1721 // out check, since InvalidateScreenClipping() might be called 1722 // frequently. 1723 // TODO: investigate, if InvalidateScreenClipping() could be 1724 // called in "deep" and "non-deep" mode, ie. see if there are 1725 // any cases where the children would still have valid screen 1726 // clipping, even though the parent's screen clipping becomes 1727 // invalid. 1728 // if (!fScreenClippingValid) 1729 // return; 1730 1731 delete fScreenAndUserClipping; 1732 fScreenAndUserClipping = NULL; 1733 fScreenClippingValid = false; 1734 // invalidate the childrens screen clipping as well 1735 for (View* child = FirstChild(); child; child = child->NextSibling()) { 1736 child->InvalidateScreenClipping(); 1737 } 1738 } 1739 1740 1741 BRegion& 1742 View::_ScreenClipping(BRegion* windowContentClipping, bool force) const 1743 { 1744 if (!fScreenClippingValid || force) { 1745 fScreenClipping = fLocalClipping; 1746 ConvertToScreen(&fScreenClipping); 1747 1748 // see if parts of our bounds are hidden underneath 1749 // the parent, the local clipping does not account for this 1750 IntRect clippedBounds = Bounds(); 1751 ConvertToVisibleInTopView(&clippedBounds); 1752 if (clippedBounds.Width() < fScreenClipping.Frame().Width() 1753 || clippedBounds.Height() < fScreenClipping.Frame().Height()) { 1754 BRegion* temp = fWindow->GetRegion(); 1755 if (temp) { 1756 temp->Set((clipping_rect)clippedBounds); 1757 fScreenClipping.IntersectWith(temp); 1758 fWindow->RecycleRegion(temp); 1759 } 1760 } 1761 1762 fScreenClipping.IntersectWith(windowContentClipping); 1763 fScreenClippingValid = true; 1764 } 1765 1766 return fScreenClipping; 1767 } 1768 1769 1770 void 1771 View::_MoveScreenClipping(int32 x, int32 y, bool deep) 1772 { 1773 if (fScreenClippingValid) { 1774 fScreenClipping.OffsetBy(x, y); 1775 delete fScreenAndUserClipping; 1776 fScreenAndUserClipping = NULL; 1777 } 1778 1779 if (deep) { 1780 // move the childrens screen clipping as well 1781 for (View* child = FirstChild(); child; child = child->NextSibling()) { 1782 child->_MoveScreenClipping(x, y, deep); 1783 } 1784 } 1785 } 1786 1787