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