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