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