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