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