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 } 1297 1298 1299 void 1300 View::SetPicture(ServerPicture *picture) 1301 { 1302 fPicture = picture; 1303 } 1304 1305 1306 void 1307 View::Draw(DrawingEngine* drawingEngine, BRegion* effectiveClipping, 1308 BRegion* windowContentClipping, bool deep) 1309 { 1310 if (!fVisible) { 1311 // child views cannot be visible either 1312 return; 1313 } 1314 1315 if (fViewBitmap != NULL || fViewColor != B_TRANSPARENT_COLOR) { 1316 // we can only draw within our own area 1317 BRegion* redraw; 1318 if ((fFlags & B_DRAW_ON_CHILDREN) != 0) { 1319 // The client may actually want to prevent the background to 1320 // be painted outside the user clipping. 1321 redraw = fWindow->GetRegion( 1322 ScreenAndUserClipping(windowContentClipping)); 1323 } else { 1324 // Ignore user clipping as in BeOS for painting the background. 1325 redraw = fWindow->GetRegion( 1326 _ScreenClipping(windowContentClipping)); 1327 } 1328 if (!redraw) 1329 return; 1330 // add the current clipping 1331 redraw->IntersectWith(effectiveClipping); 1332 1333 Overlay* overlayCookie = _Overlay(); 1334 1335 if (fViewBitmap != NULL && overlayCookie == NULL) { 1336 // draw view bitmap 1337 // TODO: support other options! 1338 BRect rect = fBitmapDestination; 1339 ConvertToScreenForDrawing(&rect); 1340 1341 align_rect_to_pixels(&rect); 1342 1343 if (fBitmapOptions & B_TILE_BITMAP_Y) { 1344 // move rect up as much as needed 1345 while (rect.top > redraw->Frame().top) 1346 rect.OffsetBy(0.0, -(rect.Height() + 1)); 1347 } 1348 if (fBitmapOptions & B_TILE_BITMAP_X) { 1349 // move rect left as much as needed 1350 while (rect.left > redraw->Frame().left) 1351 rect.OffsetBy(-(rect.Width() + 1), 0.0); 1352 } 1353 1354 // XXX: locking removed because the Window keeps the engine locked 1355 // because it keeps track of syncing right now 1356 1357 // lock the drawing engine for as long as we need the clipping 1358 // to be valid 1359 if (rect.IsValid()/* && drawingEngine->Lock()*/) { 1360 drawingEngine->ConstrainClippingRegion(redraw); 1361 1362 drawing_mode oldMode; 1363 drawingEngine->SetDrawingMode(B_OP_COPY, oldMode); 1364 1365 if (fBitmapOptions & B_TILE_BITMAP) { 1366 // tile across entire view 1367 1368 float start = rect.left; 1369 while (rect.top < redraw->Frame().bottom) { 1370 while (rect.left < redraw->Frame().right) { 1371 drawingEngine->DrawBitmap(fViewBitmap, 1372 fBitmapSource, rect, fBitmapOptions); 1373 rect.OffsetBy(rect.Width() + 1, 0.0); 1374 } 1375 rect.OffsetBy(start - rect.left, rect.Height() + 1); 1376 } 1377 // nothing left to be drawn 1378 redraw->MakeEmpty(); 1379 } else if (fBitmapOptions & B_TILE_BITMAP_X) { 1380 // tile in x direction 1381 1382 while (rect.left < redraw->Frame().right) { 1383 drawingEngine->DrawBitmap(fViewBitmap, fBitmapSource, 1384 rect, fBitmapOptions); 1385 rect.OffsetBy(rect.Width() + 1, 0.0); 1386 } 1387 // remove horizontal stripe from clipping 1388 rect.left = redraw->Frame().left; 1389 rect.right = redraw->Frame().right; 1390 redraw->Exclude(rect); 1391 } else if (fBitmapOptions & B_TILE_BITMAP_Y) { 1392 // tile in y direction 1393 1394 while (rect.top < redraw->Frame().bottom) { 1395 drawingEngine->DrawBitmap(fViewBitmap, fBitmapSource, 1396 rect, fBitmapOptions); 1397 rect.OffsetBy(0.0, rect.Height() + 1); 1398 } 1399 // remove vertical stripe from clipping 1400 rect.top = redraw->Frame().top; 1401 rect.bottom = redraw->Frame().bottom; 1402 redraw->Exclude(rect); 1403 } else { 1404 // no tiling at all 1405 1406 drawingEngine->DrawBitmap(fViewBitmap, fBitmapSource, 1407 rect, fBitmapOptions); 1408 redraw->Exclude(rect); 1409 } 1410 1411 drawingEngine->SetDrawingMode(oldMode); 1412 1413 // NOTE: It is ok not to reset the clipping, that 1414 // would only waste time 1415 // drawingEngine->Unlock(); 1416 } 1417 1418 } 1419 1420 if (fViewColor != B_TRANSPARENT_COLOR) { 1421 // fill visible region with view color, 1422 // this version of FillRegion ignores any 1423 // clipping, that's why "redraw" needs to 1424 // be correct 1425 // see #634 1426 // if (redraw->Frame().left < 0 || redraw->Frame().top < 0) { 1427 // char message[1024]; 1428 // BRect c = effectiveClipping->Frame(); 1429 // BRect w = windowContentClipping->Frame(); 1430 // BRect r = redraw->Frame(); 1431 // sprintf(message, "invalid background: current clipping: (%d, %d)->(%d, %d), " 1432 // "window content: (%d, %d)->(%d, %d), redraw: (%d, %d)->(%d, %d)", 1433 // (int)c.left, (int)c.top, (int)c.right, (int)c.bottom, 1434 // (int)w.left, (int)w.top, (int)w.right, (int)w.bottom, 1435 // (int)r.left, (int)r.top, (int)r.right, (int)r.bottom); 1436 // debugger(message); 1437 // } 1438 1439 drawingEngine->FillRegion(*redraw, overlayCookie != NULL 1440 ? overlayCookie->Color() : fViewColor); 1441 } 1442 1443 fWindow->RecycleRegion(redraw); 1444 } 1445 1446 fBackgroundDirty = false; 1447 1448 // let children draw 1449 if (deep) { 1450 for (View* child = FirstChild(); child; child = child->NextSibling()) { 1451 child->Draw(drawingEngine, effectiveClipping, 1452 windowContentClipping, deep); 1453 } 1454 } 1455 } 1456 1457 1458 // #pragma mark - 1459 1460 1461 void 1462 View::MouseDown(BMessage* message, BPoint where) 1463 { 1464 // empty hook method 1465 } 1466 1467 1468 void 1469 View::MouseUp(BMessage* message, BPoint where) 1470 { 1471 // empty hook method 1472 } 1473 1474 1475 void 1476 View::MouseMoved(BMessage* message, BPoint where) 1477 { 1478 // empty hook method 1479 } 1480 1481 1482 // #pragma mark - 1483 1484 1485 void 1486 View::SetHidden(bool hidden) 1487 { 1488 if (fHidden != hidden) { 1489 fHidden = hidden; 1490 1491 // recurse into children and update their visible flag 1492 bool oldVisible = fVisible; 1493 UpdateVisibleDeep(fParent ? fParent->IsVisible() : !fHidden); 1494 if (oldVisible != fVisible) { 1495 // Include or exclude us from the parent area, and update the 1496 // children's clipping as well when the view will be visible 1497 if (fParent) 1498 fParent->RebuildClipping(fVisible); 1499 else 1500 RebuildClipping(fVisible); 1501 1502 if (fWindow) { 1503 // trigger a redraw 1504 IntRect clippedBounds = Bounds(); 1505 ConvertToVisibleInTopView(&clippedBounds); 1506 BRegion* dirty = fWindow->GetRegion(); 1507 if (!dirty) 1508 return; 1509 dirty->Set((clipping_rect)clippedBounds); 1510 fWindow->MarkContentDirty(*dirty); 1511 fWindow->RecycleRegion(dirty); 1512 } 1513 } 1514 } 1515 } 1516 1517 1518 bool 1519 View::IsHidden() const 1520 { 1521 return fHidden; 1522 } 1523 1524 1525 void 1526 View::UpdateVisibleDeep(bool parentVisible) 1527 { 1528 bool wasVisible = fVisible; 1529 1530 fVisible = parentVisible && !fHidden; 1531 for (View* child = FirstChild(); child; child = child->NextSibling()) 1532 child->UpdateVisibleDeep(fVisible); 1533 1534 // overlay handling 1535 1536 Overlay* overlay = _Overlay(); 1537 if (overlay == NULL) 1538 return; 1539 1540 if (fVisible && !wasVisible) 1541 _UpdateOverlayView(); 1542 else if (!fVisible && wasVisible) 1543 overlay->Hide(); 1544 } 1545 1546 1547 // #pragma mark - 1548 1549 1550 void 1551 View::MarkBackgroundDirty() 1552 { 1553 if (fBackgroundDirty) 1554 return; 1555 fBackgroundDirty = true; 1556 for (View* child = FirstChild(); child; child = child->NextSibling()) 1557 child->MarkBackgroundDirty(); 1558 } 1559 1560 1561 void 1562 View::AddTokensForViewsInRegion(BPrivate::PortLink& link, BRegion& region, 1563 BRegion* windowContentClipping) 1564 { 1565 if (!fVisible) 1566 return; 1567 1568 { 1569 // NOTE: use scope in order to reduce stack space requirements 1570 1571 // This check will prevent descending the view hierarchy 1572 // any further than necessary 1573 IntRect screenBounds(Bounds()); 1574 ConvertToScreen(&screenBounds); 1575 if (!region.Intersects((clipping_rect)screenBounds)) 1576 return; 1577 1578 // Unfortunately, we intersecting another region, but otherwise 1579 // we couldn't provide the exact update rect to the client 1580 BRegion localDirty = _ScreenClipping(windowContentClipping); 1581 localDirty.IntersectWith(®ion); 1582 if (localDirty.CountRects() > 0) { 1583 link.Attach<int32>(fToken); 1584 link.Attach<BRect>(localDirty.Frame()); 1585 } 1586 } 1587 1588 for (View* child = FirstChild(); child; child = child->NextSibling()) 1589 child->AddTokensForViewsInRegion(link, region, windowContentClipping); 1590 } 1591 1592 1593 void 1594 View::PrintToStream() const 1595 { 1596 printf("View: %s\n", Name()); 1597 printf(" fToken: %ld\n", fToken); 1598 printf(" fFrame: IntRect(%ld, %ld, %ld, %ld)\n", fFrame.left, fFrame.top, fFrame.right, fFrame.bottom); 1599 printf(" fScrollingOffset: IntPoint(%ld, %ld)\n", fScrollingOffset.x, fScrollingOffset.y); 1600 printf(" fHidden: %d\n", fHidden); 1601 printf(" fVisible: %d\n", fVisible); 1602 printf(" fWindow: %p\n", fWindow); 1603 printf(" fParent: %p\n", fParent); 1604 printf(" fLocalClipping:\n"); 1605 fLocalClipping.PrintToStream(); 1606 printf(" fScreenClipping:\n"); 1607 fScreenClipping.PrintToStream(); 1608 printf(" valid: %d\n", fScreenClippingValid); 1609 1610 printf(" fUserClipping:\n"); 1611 if (fUserClipping != NULL) 1612 fUserClipping->PrintToStream(); 1613 else 1614 printf(" none\n"); 1615 1616 printf(" fScreenAndUserClipping:\n"); 1617 if (fScreenAndUserClipping != NULL) 1618 fScreenAndUserClipping->PrintToStream(); 1619 else 1620 printf(" invalid\n"); 1621 1622 printf(" state:\n"); 1623 printf(" user clipping: %d\n", fDrawState->HasClipping()); 1624 BPoint origin = fDrawState->CombinedOrigin(); 1625 printf(" origin: BPoint(%.1f, %.1f)\n", origin.x, origin.y); 1626 printf(" scale: %.2f\n", fDrawState->CombinedScale()); 1627 printf("\n"); 1628 } 1629 1630 1631 void 1632 View::RebuildClipping(bool deep) 1633 { 1634 // the clipping spans over the bounds area 1635 fLocalClipping.Set((clipping_rect)Bounds()); 1636 1637 if (View* child = FirstChild()) { 1638 // if this view does not draw over children, 1639 // exclude all children from the clipping 1640 if ((fFlags & B_DRAW_ON_CHILDREN) == 0) { 1641 BRegion* childrenRegion = fWindow->GetRegion(); 1642 if (!childrenRegion) 1643 return; 1644 1645 for (; child; child = child->NextSibling()) { 1646 if (child->IsVisible()) 1647 childrenRegion->Include((clipping_rect)child->Frame()); 1648 } 1649 1650 fLocalClipping.Exclude(childrenRegion); 1651 fWindow->RecycleRegion(childrenRegion); 1652 } 1653 // if the operation is "deep", make children rebuild their 1654 // clipping too 1655 if (deep) { 1656 for (child = FirstChild(); child; child = child->NextSibling()) 1657 child->RebuildClipping(true); 1658 } 1659 } 1660 1661 // add the user clipping in case there is one 1662 if (fDrawState->HasClipping()) { 1663 // NOTE: in case the user sets a user defined clipping region, 1664 // rebuilding the clipping is a bit more expensive because there 1665 // is no separate "drawing region"... on the other 1666 // hand, views for which this feature is actually used will 1667 // probably not have any children, so it is not that expensive 1668 // after all 1669 if (fUserClipping == NULL) { 1670 fUserClipping = new (nothrow) BRegion; 1671 if (fUserClipping == NULL) 1672 return; 1673 } 1674 1675 fDrawState->GetCombinedClippingRegion(fUserClipping); 1676 } else { 1677 delete fUserClipping; 1678 fUserClipping = NULL; 1679 } 1680 1681 delete fScreenAndUserClipping; 1682 fScreenAndUserClipping = NULL; 1683 fScreenClippingValid = false; 1684 } 1685 1686 1687 BRegion& 1688 View::ScreenAndUserClipping(BRegion* windowContentClipping, bool force) const 1689 { 1690 // no user clipping - return screen clipping directly 1691 if (fUserClipping == NULL) 1692 return _ScreenClipping(windowContentClipping, force); 1693 1694 // combined screen and user clipping already valid 1695 if (fScreenAndUserClipping != NULL) 1696 return *fScreenAndUserClipping; 1697 1698 // build a new combined user and screen clipping 1699 fScreenAndUserClipping = new (nothrow) BRegion(*fUserClipping); 1700 if (fScreenAndUserClipping == NULL) 1701 return fScreenClipping; 1702 1703 ConvertToScreen(fScreenAndUserClipping); 1704 fScreenAndUserClipping->IntersectWith( 1705 &_ScreenClipping(windowContentClipping, force)); 1706 return *fScreenAndUserClipping; 1707 } 1708 1709 1710 void 1711 View::InvalidateScreenClipping() 1712 { 1713 // TODO: appearantly, we are calling ScreenClipping() on 1714 // views who's parents don't have a valid screen clipping yet, 1715 // this messes up the logic that for any given view with 1716 // fScreenClippingValid == false, all children have 1717 // fScreenClippingValid == false too. If this could be made the 1718 // case, we could save some performance here with the commented 1719 // out check, since InvalidateScreenClipping() might be called 1720 // frequently. 1721 // TODO: investigate, if InvalidateScreenClipping() could be 1722 // called in "deep" and "non-deep" mode, ie. see if there are 1723 // any cases where the children would still have valid screen 1724 // clipping, even though the parent's screen clipping becomes 1725 // invalid. 1726 // if (!fScreenClippingValid) 1727 // return; 1728 1729 delete fScreenAndUserClipping; 1730 fScreenAndUserClipping = NULL; 1731 fScreenClippingValid = false; 1732 // invalidate the childrens screen clipping as well 1733 for (View* child = FirstChild(); child; child = child->NextSibling()) { 1734 child->InvalidateScreenClipping(); 1735 } 1736 } 1737 1738 1739 BRegion& 1740 View::_ScreenClipping(BRegion* windowContentClipping, bool force) const 1741 { 1742 if (!fScreenClippingValid || force) { 1743 fScreenClipping = fLocalClipping; 1744 ConvertToScreen(&fScreenClipping); 1745 1746 // see if parts of our bounds are hidden underneath 1747 // the parent, the local clipping does not account for this 1748 IntRect clippedBounds = Bounds(); 1749 ConvertToVisibleInTopView(&clippedBounds); 1750 if (clippedBounds.Width() < fScreenClipping.Frame().Width() 1751 || clippedBounds.Height() < fScreenClipping.Frame().Height()) { 1752 BRegion* temp = fWindow->GetRegion(); 1753 if (temp) { 1754 temp->Set((clipping_rect)clippedBounds); 1755 fScreenClipping.IntersectWith(temp); 1756 fWindow->RecycleRegion(temp); 1757 } 1758 } 1759 1760 fScreenClipping.IntersectWith(windowContentClipping); 1761 fScreenClippingValid = true; 1762 } 1763 1764 return fScreenClipping; 1765 } 1766 1767 1768 void 1769 View::_MoveScreenClipping(int32 x, int32 y, bool deep) 1770 { 1771 if (fScreenClippingValid) { 1772 fScreenClipping.OffsetBy(x, y); 1773 delete fScreenAndUserClipping; 1774 fScreenAndUserClipping = NULL; 1775 } 1776 1777 if (deep) { 1778 // move the childrens screen clipping as well 1779 for (View* child = FirstChild(); child; child = child->NextSibling()) { 1780 child->_MoveScreenClipping(x, y, deep); 1781 } 1782 } 1783 } 1784 1785