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