usb_ecm: Use the current configuration instead of a fixed one.
[haiku.git] / src / servers / app / View.cpp
blob554308d4756531d22bfec2e7474655ce01aabcd3
1 /*
2 * Copyright (c) 2001-2015, Haiku, Inc.
3 * Distributed under the terms of the MIT license.
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>
15 #include "View.h"
17 #include <new>
18 #include <stdio.h>
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"
33 #include "BitmapHWInterface.h"
34 #include "drawing_support.h"
36 #include <List.h>
37 #include <Message.h>
38 #include <PortLink.h>
39 #include <View.h> // for resize modes
40 #include <WindowPrivate.h>
42 #include <GradientLinear.h>
43 #include <GradientRadial.h>
44 #include <GradientRadialFocus.h>
45 #include <GradientDiamond.h>
46 #include <GradientConic.h>
49 using std::nothrow;
52 void
53 resize_frame(IntRect& frame, uint32 resizingMode, int32 x, int32 y)
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;
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;
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;
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;
81 // #pragma mark -
84 View::View(IntRect frame, IntPoint scrollingOffset, const char* name,
85 int32 token, uint32 resizeMode, uint32 flags)
87 fName(name),
88 fToken(token),
90 fFrame(frame),
91 fScrollingOffset(scrollingOffset),
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),
100 fResizeMode(resizeMode),
101 fFlags(flags),
103 // Views start visible by default
104 fHidden(false),
105 fVisible(true),
106 fBackgroundDirty(true),
107 fIsDesktopBackground(false),
109 fEventMask(0),
110 fEventOptions(0),
112 fWindow(NULL),
113 fParent(NULL),
115 fFirstChild(NULL),
116 fPreviousSibling(NULL),
117 fNextSibling(NULL),
118 fLastChild(NULL),
120 fCursor(NULL),
121 fPicture(NULL),
123 fLocalClipping((BRect)Bounds()),
124 fScreenClipping(),
125 fScreenClippingValid(false),
126 fUserClipping(NULL),
127 fScreenAndUserClipping(NULL)
129 if (fDrawState)
130 fDrawState->SetSubPixelPrecise(fFlags & B_SUBPIXEL_PRECISE);
134 View::~View()
136 if (fViewBitmap != NULL)
137 fViewBitmap->ReleaseReference();
139 delete fScreenAndUserClipping;
140 delete fUserClipping;
141 delete fDrawState;
143 // if (fWindow && this == fWindow->TopView())
144 // fWindow->SetTopView(NULL);
146 if (fCursor)
147 fCursor->ReleaseReference();
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;
159 IntRect
160 View::Bounds() const
162 IntRect bounds(fScrollingOffset.x, fScrollingOffset.y,
163 fScrollingOffset.x + fFrame.Width(),
164 fScrollingOffset.y + fFrame.Height());
165 return bounds;
169 void
170 View::ConvertToVisibleInTopView(IntRect* bounds) const
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);
177 if (fParent)
178 fParent->ConvertToVisibleInTopView(bounds);
182 void
183 View::AttachedToWindow(::Window* window)
185 fWindow = window;
187 // an ugly hack to detect the desktop background
188 if (window->Feel() == kDesktopWindowFeel && Parent() == TopView())
189 fIsDesktopBackground = true;
191 // insert view into local token space
192 if (fWindow != NULL) {
193 fWindow->ServerWindow()->App()->ViewTokens().SetToken(fToken,
194 B_HANDLER_TOKEN, this);
197 // attach child views as well
198 for (View* child = FirstChild(); child; child = child->NextSibling())
199 child->AttachedToWindow(window);
203 void
204 View::DetachedFromWindow()
206 // remove view from local token space
207 if (fWindow != NULL && fWindow->ServerWindow()->App() != NULL)
208 fWindow->ServerWindow()->App()->ViewTokens().RemoveToken(fToken);
210 fWindow = NULL;
211 // detach child views as well
212 for (View* child = FirstChild(); child; child = child->NextSibling())
213 child->DetachedFromWindow();
217 // #pragma mark -
220 DrawingEngine*
221 View::GetDrawingEngine() const
223 return Window()->GetDrawingEngine();
227 ServerPicture*
228 View::GetPicture(int32 token) const
230 return Window()->ServerWindow()->App()->GetPicture(token);
234 void
235 View::ResyncDrawState()
237 return Window()->ServerWindow()->ResyncDrawState();
241 void
242 View::UpdateCurrentDrawingRegion()
244 return Window()->ServerWindow()->UpdateCurrentDrawingRegion();
248 void
249 View::AddChild(View* view)
251 if (view->fParent) {
252 printf("View::AddChild() - View already has a parent\n");
253 return;
256 view->fParent = this;
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;
266 fLastChild = view;
268 view->UpdateVisibleDeep(fVisible);
270 if (view->IsVisible())
271 RebuildClipping(false);
273 if (fWindow) {
274 view->AttachedToWindow(fWindow);
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);
291 bool
292 View::RemoveChild(View* view)
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;
300 view->fParent = NULL;
302 if (fLastChild == view)
303 fLastChild = view->fPreviousSibling;
304 // view->fNextSibling would be NULL
306 if (fFirstChild == view )
307 fFirstChild = view->fNextSibling;
308 // view->fPreviousSibling would be NULL
310 // connect child before and after view
311 if (view->fPreviousSibling)
312 view->fPreviousSibling->fNextSibling = view->fNextSibling;
314 if (view->fNextSibling)
315 view->fNextSibling->fPreviousSibling = view->fPreviousSibling;
317 // view has no siblings anymore
318 view->fPreviousSibling = NULL;
319 view->fNextSibling = NULL;
321 if (view->IsVisible()) {
322 Overlay* overlay = view->_Overlay();
323 if (overlay != NULL)
324 overlay->Hide();
326 RebuildClipping(false);
329 if (fWindow) {
330 view->DetachedFromWindow();
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);
345 return true;
349 View*
350 View::TopView()
352 // returns the top level view of the hirarchy,
353 // it doesn't have to be the top level of a window
355 if (fParent)
356 return fParent->TopView();
358 return this;
362 uint32
363 View::CountChildren(bool deep) const
365 uint32 count = 0;
366 for (View* child = FirstChild(); child; child = child->NextSibling()) {
367 count++;
368 if (deep) {
369 count += child->CountChildren(deep);
372 return count;
376 void
377 View::CollectTokensForChildren(BList* tokenMap) const
379 for (View* child = FirstChild(); child; child = child->NextSibling()) {
380 tokenMap->AddItem((void*)child);
381 child->CollectTokensForChildren(tokenMap);
386 #if 0
387 bool
388 View::MarkAt(DrawingEngine* engine, const BPoint& where, int32 level)
390 BRect rect(fFrame.left, fFrame.top, fFrame.right, fFrame.bottom);
392 if (Parent() != NULL) {
393 Parent()->ConvertToScreen(&rect);
394 if (!rect.Contains(where))
395 return false;
397 engine->StrokeRect(rect, (rgb_color){level * 30, level * 30, level * 30});
401 bool found = false;
402 for (View* child = FirstChild(); child; child = child->NextSibling()) {
403 found |= child->MarkAt(engine, where, level + 1);
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;
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);
420 return true;
422 #endif
425 void
426 View::FindViews(uint32 flags, BObjectList<View>& list, int32& left)
428 if ((Flags() & flags) == flags) {
429 list.AddItem(this);
430 left--;
431 return;
434 for (View* child = FirstChild(); child; child = child->NextSibling()) {
435 child->FindViews(flags, list, left);
436 if (left == 0)
437 break;
442 bool
443 View::HasView(View* view)
445 if (view == this)
446 return true;
448 for (View* child = FirstChild(); child; child = child->NextSibling()) {
449 if (child->HasView(view))
450 return true;
453 return false;
457 View*
458 View::ViewAt(const BPoint& where)
460 if (!fVisible)
461 return NULL;
463 IntRect frame = Frame();
464 if (Parent() != NULL)
465 Parent()->LocalToScreenTransform().Apply(&frame);
467 if (!frame.Contains(where))
468 return NULL;
470 for (View* child = FirstChild(); child; child = child->NextSibling()) {
471 View* view = child->ViewAt(where);
472 if (view != NULL)
473 return view;
476 return this;
480 // #pragma mark -
483 void
484 View::SetName(const char* string)
486 fName.SetTo(string);
490 void
491 View::SetFlags(uint32 flags)
493 fFlags = flags;
494 fDrawState->SetSubPixelPrecise(fFlags & B_SUBPIXEL_PRECISE);
498 void
499 View::SetViewBitmap(ServerBitmap* bitmap, IntRect sourceRect,
500 IntRect destRect, int32 resizingMode, int32 options)
502 if (fViewBitmap != NULL) {
503 Overlay* overlay = _Overlay();
505 if (bitmap != NULL) {
506 // take over overlay token from current overlay (if it has any)
507 Overlay* newOverlay = bitmap->Overlay();
509 if (overlay != NULL && newOverlay != NULL)
510 newOverlay->TakeOverToken(overlay);
511 } else if (overlay != NULL)
512 overlay->Hide();
514 fViewBitmap->ReleaseReference();
517 // the caller is allowed to delete the bitmap after setting the background
518 if (bitmap != NULL)
519 bitmap->AcquireReference();
521 fViewBitmap = bitmap;
522 fBitmapSource = sourceRect;
523 fBitmapDestination = destRect;
524 fBitmapResizingMode = resizingMode;
525 fBitmapOptions = options;
527 _UpdateOverlayView();
531 ::Overlay*
532 View::_Overlay() const
534 if (fViewBitmap == NULL)
535 return NULL;
537 return fViewBitmap->Overlay();
541 void
542 View::_UpdateOverlayView() const
544 Overlay* overlay = _Overlay();
545 if (overlay == NULL)
546 return;
548 IntRect destination = fBitmapDestination;
549 LocalToScreenTransform().Apply(&destination);
551 overlay->Configure(fBitmapSource, destination);
556 This method is called whenever the window is resized or moved - would
557 be nice to have a better solution for this, though.
559 void
560 View::UpdateOverlay()
562 if (!IsVisible())
563 return;
565 if (_Overlay() != NULL) {
566 _UpdateOverlayView();
567 } else {
568 // recursively ask children of this view
570 for (View* child = FirstChild(); child; child = child->NextSibling()) {
571 child->UpdateOverlay();
577 // #pragma mark -
580 void
581 View::_LocalToScreenTransform(SimpleTransform& transform) const
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);
592 transform.AddOffset(offsetX, offsetY);
596 void
597 View::_ScreenToLocalTransform(SimpleTransform& transform) const
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);
608 transform.AddOffset(offsetX, offsetY);
612 // #pragma mark -
615 void
616 View::MoveBy(int32 x, int32 y, BRegion* dirtyRegion)
618 if (x == 0 && y == 0)
619 return;
621 fFrame.OffsetBy(x, y);
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);
635 ConvertToVisibleInTopView(&newVisibleBounds);
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);
648 // NOTE: using ConvertToVisibleInTopView()
649 // instead of ConvertToScreen()! see below
650 ConvertToVisibleInTopView(&newVisibleBounds);
652 newVisibleBounds.OffsetBy(-x, -y);
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);
662 region->Set(oldVisibleBounds);
663 newVisibleBounds.OffsetBy(x, y);
664 region->Exclude((clipping_rect)newVisibleBounds);
665 dirtyRegion->Include(dirty);
667 fWindow->RecycleRegion(region);
670 #endif
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();
688 void
689 View::ResizeBy(int32 x, int32 y, BRegion* dirtyRegion)
691 if (x == 0 && y == 0)
692 return;
694 fFrame.right += x;
695 fFrame.bottom += y;
697 if (fVisible && dirtyRegion) {
698 IntRect oldBounds(Bounds());
699 oldBounds.right -= x;
700 oldBounds.bottom -= y;
702 BRegion* dirty = fWindow->GetRegion();
703 if (!dirty)
704 return;
706 dirty->Set((clipping_rect)Bounds());
707 dirty->Include((clipping_rect)oldBounds);
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()));
715 InvalidateScreenClipping();
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);
733 LocalToScreenTransform().Apply(dirty);
734 dirtyRegion->Include(dirty);
736 fWindow->RecycleRegion(dirty);
739 // layout the children
740 for (View* child = FirstChild(); child; child = child->NextSibling())
741 child->ParentResized(x, y, dirtyRegion);
743 // view bitmap
744 if (fViewBitmap != NULL)
745 resize_frame(fBitmapDestination, fBitmapResizingMode, x, y);
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);
755 void
756 View::ParentResized(int32 x, int32 y, BRegion* dirtyRegion)
758 IntRect newFrame = fFrame;
759 resize_frame(newFrame, fResizeMode & 0x0000ffff, x, y);
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());
766 MoveBy(newFrame.left - fFrame.left,
767 newFrame.top - fFrame.top, dirtyRegion);
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();
780 void
781 View::ScrollBy(int32 x, int32 y, BRegion* dirtyRegion)
783 if (!fVisible || !fWindow) {
784 fScrollingOffset.x += x;
785 fScrollingOffset.y += y;
786 return;
789 // blitting version, invalidates
790 // old contents
792 // remember old bounds for tracking dirty region
793 IntRect oldBounds(Bounds());
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);
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;
807 fScrollingOffset.x += x;
808 fScrollingOffset.y += y;
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);
819 // find the dirty region as far as we are
820 // concerned
821 BRegion* dirty = copyRegion;
822 // reuse copyRegion and call it dirty
824 dirty->Set((clipping_rect)oldBounds);
825 stillVisibleBounds.OffsetBy(-x, -y);
826 dirty->Exclude((clipping_rect)stillVisibleBounds);
827 dirtyRegion->Include(dirty);
829 fWindow->RecycleRegion(dirty);
831 // the screen clipping of this view and it's
832 // childs is no longer valid
833 InvalidateScreenClipping();
834 RebuildClipping(false);
838 void
839 View::CopyBits(IntRect src, IntRect dst, BRegion& windowContentClipping)
841 if (!fVisible || !fWindow)
842 return;
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);
855 // TODO: confirm that in R5 this call is affected by origin and scale
857 // blitting version
859 int32 xOffset = dst.left - src.left;
860 int32 yOffset = dst.top - src.top;
862 // figure out which part can be blittet
863 IntRect visibleSrc(src);
864 ConvertToVisibleInTopView(&visibleSrc);
866 IntRect visibleSrcAtDest(src);
867 visibleSrcAtDest.OffsetBy(xOffset, yOffset);
868 ConvertToVisibleInTopView(&visibleSrcAtDest);
870 // clip src to visible at dest
871 visibleSrcAtDest.OffsetBy(-xOffset, -yOffset);
872 visibleSrc = visibleSrc & visibleSrcAtDest;
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;
881 // move src rect to destination here for efficiency reasons
882 visibleSrc.OffsetBy(xOffset, yOffset);
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);
897 // do the actual blit
898 fWindow->CopyContents(copyRegion, xOffset, yOffset);
900 // find the dirty region as far as we are concerned
901 IntRect dirtyDst(dst);
902 ConvertToVisibleInTopView(&dirtyDst);
904 BRegion* dirty = fWindow->GetRegion();
905 if (!dirty) {
906 fWindow->RecycleRegion(copyRegion);
907 return;
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);
917 dirty->IntersectWith(screenAndUserClipping);
918 fWindow->MarkContentDirty(*dirty);
920 fWindow->RecycleRegion(dirty);
921 fWindow->RecycleRegion(copyRegion);
925 // #pragma mark -
928 void
929 View::ColorUpdated(color_which which, rgb_color color)
931 float tint = B_NO_TINT;
933 if (fWhichViewColor == which)
934 SetViewColor(tint_color(color, fWhichViewColorTint));
936 if (CurrentState()->HighUIColor(&tint) == which)
937 CurrentState()->SetHighColor(tint_color(color, tint));
939 if (CurrentState()->LowUIColor(&tint) == which)
940 CurrentState()->SetLowColor(tint_color(color, tint));
942 for (View* child = FirstChild(); child != NULL;
943 child = child->NextSibling()) {
945 child->ColorUpdated(which, color);
950 void
951 View::SetViewUIColor(color_which which, float tint)
953 if (which != B_NO_COLOR) {
954 DesktopSettings settings(fWindow->Desktop());
955 SetViewColor(tint_color(settings.UIColor(which), tint));
958 fWhichViewColor = which;
959 fWhichViewColorTint = tint;
963 color_which
964 View::ViewUIColor(float* tint)
966 if (tint != NULL)
967 *tint = fWhichViewColorTint;
969 return fWhichViewColor;
973 // #pragma mark -
976 void
977 View::PushState()
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);
991 void
992 View::PopState()
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;
1000 bool rebuildClipping = fDrawState->HasAdditionalClipping();
1002 fDrawState = fDrawState->PopState();
1003 fDrawState->SetSubPixelPrecise(fFlags & B_SUBPIXEL_PRECISE);
1005 // rebuild clipping
1006 // (the clipping from the popped state is not effective anymore)
1007 if (rebuildClipping)
1008 RebuildClipping(false);
1012 // #pragma mark -
1015 void
1016 View::SetEventMask(uint32 eventMask, uint32 options)
1018 fEventMask = eventMask;
1019 fEventOptions = options;
1023 void
1024 View::SetCursor(ServerCursor* cursor)
1026 if (cursor == fCursor)
1027 return;
1029 if (fCursor)
1030 fCursor->ReleaseReference();
1032 fCursor = cursor;
1034 if (fCursor)
1035 fCursor->AcquireReference();
1039 void
1040 View::SetPicture(ServerPicture* picture)
1042 if (picture == fPicture)
1043 return;
1045 if (fPicture != NULL)
1046 fPicture->ReleaseReference();
1048 fPicture = picture;
1050 if (fPicture != NULL)
1051 fPicture->AcquireReference();
1055 void
1056 View::BlendAllLayers()
1058 if (fPicture == NULL)
1059 return;
1060 Layer* layer = dynamic_cast<Layer*>(fPicture);
1061 if (layer == NULL)
1062 return;
1063 BlendLayer(layer);
1067 void
1068 View::Draw(DrawingEngine* drawingEngine, BRegion* effectiveClipping,
1069 BRegion* windowContentClipping, bool deep)
1071 if (!fVisible) {
1072 // child views cannot be visible either
1073 return;
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));
1089 if (!redraw)
1090 return;
1091 // add the current clipping
1092 redraw->IntersectWith(effectiveClipping);
1094 Overlay* overlayCookie = _Overlay();
1096 if (fViewBitmap != NULL && overlayCookie == NULL) {
1097 // draw view bitmap
1098 // TODO: support other options!
1099 BRect rect = fBitmapDestination;
1100 PenToScreenTransform().Apply(&rect);
1102 align_rect_to_pixels(&rect);
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));
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);
1115 // XXX: locking removed because the Window keeps the engine locked
1116 // because it keeps track of syncing right now
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);
1123 drawing_mode oldMode;
1124 drawingEngine->SetDrawingMode(B_OP_COPY, oldMode);
1126 if (fBitmapOptions & B_TILE_BITMAP) {
1127 // tile across entire view
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);
1136 rect.OffsetBy(start - rect.left, rect.Height() + 1);
1138 // nothing left to be drawn
1139 redraw->MakeEmpty();
1140 } else if (fBitmapOptions & B_TILE_BITMAP_X) {
1141 // tile in x direction
1143 while (rect.left < redraw->Frame().right) {
1144 drawingEngine->DrawBitmap(fViewBitmap, fBitmapSource,
1145 rect, fBitmapOptions);
1146 rect.OffsetBy(rect.Width() + 1, 0.0);
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
1155 while (rect.top < redraw->Frame().bottom) {
1156 drawingEngine->DrawBitmap(fViewBitmap, fBitmapSource,
1157 rect, fBitmapOptions);
1158 rect.OffsetBy(0.0, rect.Height() + 1);
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
1167 drawingEngine->DrawBitmap(fViewBitmap, fBitmapSource,
1168 rect, fBitmapOptions);
1169 redraw->Exclude(rect);
1172 drawingEngine->SetDrawingMode(oldMode);
1174 // NOTE: It is ok not to reset the clipping, that
1175 // would only waste time
1176 // drawingEngine->Unlock();
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 // }
1200 drawingEngine->FillRegion(*redraw, overlayCookie != NULL
1201 ? overlayCookie->Color() : fViewColor);
1204 fWindow->RecycleRegion(redraw);
1207 fBackgroundDirty = false;
1209 // let children draw
1210 if (deep) {
1211 for (View* child = FirstChild(); child; child = child->NextSibling()) {
1212 child->Draw(drawingEngine, effectiveClipping,
1213 windowContentClipping, deep);
1219 // #pragma mark -
1222 void
1223 View::MouseDown(BMessage* message, BPoint where)
1225 // empty hook method
1229 void
1230 View::MouseUp(BMessage* message, BPoint where)
1232 // empty hook method
1236 void
1237 View::MouseMoved(BMessage* message, BPoint where)
1239 // empty hook method
1243 // #pragma mark -
1246 void
1247 View::SetHidden(bool hidden)
1249 if (fHidden != hidden) {
1250 fHidden = hidden;
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);
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);
1279 bool
1280 View::IsHidden() const
1282 return fHidden;
1286 void
1287 View::UpdateVisibleDeep(bool parentVisible)
1289 bool wasVisible = fVisible;
1291 fVisible = parentVisible && !fHidden;
1292 for (View* child = FirstChild(); child; child = child->NextSibling())
1293 child->UpdateVisibleDeep(fVisible);
1295 // overlay handling
1297 Overlay* overlay = _Overlay();
1298 if (overlay == NULL)
1299 return;
1301 if (fVisible && !wasVisible)
1302 _UpdateOverlayView();
1303 else if (!fVisible && wasVisible)
1304 overlay->Hide();
1308 // #pragma mark -
1311 void
1312 View::MarkBackgroundDirty()
1314 if (fBackgroundDirty)
1315 return;
1316 fBackgroundDirty = true;
1317 for (View* child = FirstChild(); child; child = child->NextSibling())
1318 child->MarkBackgroundDirty();
1322 void
1323 View::AddTokensForViewsInRegion(BPrivate::PortLink& link, BRegion& region,
1324 BRegion* windowContentClipping)
1326 if (!fVisible)
1327 return;
1330 // NOTE: use scope in order to reduce stack space requirements
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;
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(&region);
1343 if (localDirty.CountRects() > 0) {
1344 link.Attach<int32>(fToken);
1345 link.Attach<BRect>(localDirty.Frame());
1349 for (View* child = FirstChild(); child; child = child->NextSibling())
1350 child->AddTokensForViewsInRegion(link, region, windowContentClipping);
1354 void
1355 View::PrintToStream() const
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);
1373 printf(" fUserClipping:\n");
1374 if (fUserClipping != NULL)
1375 fUserClipping->PrintToStream();
1376 else
1377 printf(" none\n");
1379 printf(" fScreenAndUserClipping:\n");
1380 if (fScreenAndUserClipping != NULL)
1381 fScreenAndUserClipping->PrintToStream();
1382 else
1383 printf(" invalid\n");
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");
1394 void
1395 View::RebuildClipping(bool deep)
1397 // the clipping spans over the bounds area
1398 fLocalClipping.Set((clipping_rect)Bounds());
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;
1408 for (; child; child = child->NextSibling()) {
1409 if (child->IsVisible())
1410 childrenRegion->Include((clipping_rect)child->Frame());
1413 fLocalClipping.Exclude(childrenRegion);
1414 fWindow->RecycleRegion(childrenRegion);
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);
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;
1438 fDrawState->GetCombinedClippingRegion(fUserClipping);
1439 } else {
1440 delete fUserClipping;
1441 fUserClipping = NULL;
1444 delete fScreenAndUserClipping;
1445 fScreenAndUserClipping = NULL;
1446 fScreenClippingValid = false;
1450 BRegion&
1451 View::ScreenAndUserClipping(BRegion* windowContentClipping, bool force) const
1453 // no user clipping - return screen clipping directly
1454 if (fUserClipping == NULL)
1455 return _ScreenClipping(windowContentClipping, force);
1457 // combined screen and user clipping already valid
1458 if (fScreenAndUserClipping != NULL)
1459 return *fScreenAndUserClipping;
1461 // build a new combined user and screen clipping
1462 fScreenAndUserClipping = new (nothrow) BRegion(*fUserClipping);
1463 if (fScreenAndUserClipping == NULL)
1464 return fScreenClipping;
1466 LocalToScreenTransform().Apply(fScreenAndUserClipping);
1467 fScreenAndUserClipping->IntersectWith(
1468 &_ScreenClipping(windowContentClipping, force));
1469 return *fScreenAndUserClipping;
1473 void
1474 View::InvalidateScreenClipping()
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;
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();
1502 BRegion&
1503 View::_ScreenClipping(BRegion* windowContentClipping, bool force) const
1505 if (!fScreenClippingValid || force) {
1506 fScreenClipping = fLocalClipping;
1507 LocalToScreenTransform().Apply(&fScreenClipping);
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);
1523 fScreenClipping.IntersectWith(windowContentClipping);
1524 fScreenClippingValid = true;
1527 return fScreenClipping;
1531 void
1532 View::_MoveScreenClipping(int32 x, int32 y, bool deep)
1534 if (fScreenClippingValid) {
1535 fScreenClipping.OffsetBy(x, y);
1536 delete fScreenAndUserClipping;
1537 fScreenAndUserClipping = NULL;
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);