xref: /haiku/src/servers/app/View.cpp (revision 15fb7d88e971c4d6c787c6a3a5c159afb1ebf77b)
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 						|| child->fViewColor == B_TRANSPARENT_COLOR) {
725 						continue;
726 					}
727 					IntRect previousChildVisible(
728 						child->Frame() & oldBounds & Bounds());
729 					if (dirty->Frame().Intersects(previousChildVisible)) {
730 						dirty->Exclude((clipping_rect)previousChildVisible);
731 					}
732 				}
733 			}
734 
735 			LocalToScreenTransform().Apply(dirty);
736 			dirtyRegion->Include(dirty);
737 		}
738 		fWindow->RecycleRegion(dirty);
739 	}
740 
741 	// layout the children
742 	for (View* child = FirstChild(); child; child = child->NextSibling())
743 		child->ParentResized(x, y, dirtyRegion);
744 
745 	// view bitmap
746 	if (fViewBitmap != NULL)
747 		resize_frame(fBitmapDestination, fBitmapResizingMode, x, y);
748 
749 	// at this point, children are at their new locations,
750 	// so we can rebuild the clipping
751 	// TODO: when the implementation of Hide() and Show() is
752 	// complete, see if this should be avoided
753 	RebuildClipping(false);
754 }
755 
756 
757 void
758 View::ParentResized(int32 x, int32 y, BRegion* dirtyRegion)
759 {
760 	IntRect newFrame = fFrame;
761 	resize_frame(newFrame, fResizeMode & 0x0000ffff, x, y);
762 
763 	if (newFrame != fFrame) {
764 		// careful, MoveBy will change fFrame
765 		int32 widthDiff = (int32)(newFrame.Width() - fFrame.Width());
766 		int32 heightDiff = (int32)(newFrame.Height() - fFrame.Height());
767 
768 		MoveBy(newFrame.left - fFrame.left,
769 			newFrame.top - fFrame.top, dirtyRegion);
770 
771 		ResizeBy(widthDiff, heightDiff, dirtyRegion);
772 	} else {
773 		// TODO: this covers the fact that our screen clipping might change
774 		// when the parent changes its size, even though our frame stays
775 		// the same - there might be a way to test for this, but axeld doesn't
776 		// know, stippi should look into this when he's back :)
777 		InvalidateScreenClipping();
778 	}
779 }
780 
781 
782 void
783 View::ScrollBy(int32 x, int32 y, BRegion* dirtyRegion)
784 {
785 	if (!fVisible || !fWindow) {
786 		fScrollingOffset.x += x;
787 		fScrollingOffset.y += y;
788 		return;
789 	}
790 
791 	// blitting version, invalidates
792 	// old contents
793 
794 	// remember old bounds for tracking dirty region
795 	IntRect oldBounds(Bounds());
796 
797 	// NOTE: using ConvertToVisibleInTopView()
798 	// instead of ConvertToScreen(), this makes
799 	// sure we don't try to move or invalidate an
800 	// area hidden underneath the parent view
801 	ConvertToVisibleInTopView(&oldBounds);
802 
803 	// find the area of the view that can be scrolled,
804 	// contents are shifted in the opposite direction from scrolling
805 	IntRect stillVisibleBounds(oldBounds);
806 	stillVisibleBounds.OffsetBy(x, y);
807 	stillVisibleBounds = stillVisibleBounds & oldBounds;
808 
809 	fScrollingOffset.x += x;
810 	fScrollingOffset.y += y;
811 
812 	// do the blit, this will make sure
813 	// that other more complex dirty regions
814 	// are taken care of
815 	BRegion* copyRegion = fWindow->GetRegion();
816 	if (!copyRegion)
817 		return;
818 	copyRegion->Set((clipping_rect)stillVisibleBounds);
819 	fWindow->CopyContents(copyRegion, -x, -y);
820 
821 	// find the dirty region as far as we are
822 	// concerned
823 	BRegion* dirty = copyRegion;
824 		// reuse copyRegion and call it dirty
825 
826 	dirty->Set((clipping_rect)oldBounds);
827 	stillVisibleBounds.OffsetBy(-x, -y);
828 	dirty->Exclude((clipping_rect)stillVisibleBounds);
829 	dirtyRegion->Include(dirty);
830 
831 	fWindow->RecycleRegion(dirty);
832 
833 	// the screen clipping of this view and it's
834 	// childs is no longer valid
835 	InvalidateScreenClipping();
836 	RebuildClipping(false);
837 }
838 
839 
840 void
841 View::CopyBits(IntRect src, IntRect dst, BRegion& windowContentClipping)
842 {
843 	if (!fVisible || !fWindow)
844 		return;
845 
846 	// TODO: figure out what to do when we have a transform which is not
847 	// a dilation
848 	BAffineTransform transform = CurrentState()->CombinedTransform();
849 	if (!transform.IsIdentity() && transform.IsDilation()) {
850 		BPoint points[4] = { src.LeftTop(), src.RightBottom(),
851 							 dst.LeftTop(), dst.RightBottom() };
852 		transform.Apply(&points[0], 4);
853 		src.Set(points[0].x, points[0].y, points[1].x, points[1].y);
854 		dst.Set(points[2].x, points[2].y, points[3].x, points[3].y);
855 	}
856 
857 	// TODO: confirm that in R5 this call is affected by origin and scale
858 
859 	// blitting version
860 
861 	int32 xOffset = dst.left - src.left;
862 	int32 yOffset = dst.top - src.top;
863 
864 	// figure out which part can be blittet
865 	IntRect visibleSrc(src);
866 	ConvertToVisibleInTopView(&visibleSrc);
867 
868 	IntRect visibleSrcAtDest(src);
869 	visibleSrcAtDest.OffsetBy(xOffset, yOffset);
870 	ConvertToVisibleInTopView(&visibleSrcAtDest);
871 
872 	// clip src to visible at dest
873 	visibleSrcAtDest.OffsetBy(-xOffset, -yOffset);
874 	visibleSrc = visibleSrc & visibleSrcAtDest;
875 
876 	// do the blit, this will make sure
877 	// that other more complex dirty regions
878 	// are taken care of
879 	BRegion* copyRegion = fWindow->GetRegion();
880 	if (!copyRegion)
881 		return;
882 
883 	// move src rect to destination here for efficiency reasons
884 	visibleSrc.OffsetBy(xOffset, yOffset);
885 
886 	// we need to interstect the copyRegion two times, onces
887 	// at the source and once at the destination (here done
888 	// the other way arround but it doesn't matter)
889 	// the reason for this is that we are not supposed to visually
890 	// copy children in the source rect and neither to copy onto
891 	// children in the destination rect...
892 	copyRegion->Set((clipping_rect)visibleSrc);
893 	BRegion *screenAndUserClipping
894 		= &ScreenAndUserClipping(&windowContentClipping);
895 	copyRegion->IntersectWith(screenAndUserClipping);
896 	copyRegion->OffsetBy(-xOffset, -yOffset);
897 	copyRegion->IntersectWith(screenAndUserClipping);
898 
899 	// do the actual blit
900 	fWindow->CopyContents(copyRegion, xOffset, yOffset);
901 
902 	// find the dirty region as far as we are concerned
903 	IntRect dirtyDst(dst);
904 	ConvertToVisibleInTopView(&dirtyDst);
905 
906 	BRegion* dirty = fWindow->GetRegion();
907 	if (!dirty) {
908 		fWindow->RecycleRegion(copyRegion);
909 		return;
910 	}
911 
912 	// offset copyRegion to destination again
913 	copyRegion->OffsetBy(xOffset, yOffset);
914 	// start with destination given by user
915 	dirty->Set((clipping_rect)dirtyDst);
916 	// exclude the part that we could copy
917 	dirty->Exclude(copyRegion);
918 
919 	dirty->IntersectWith(screenAndUserClipping);
920 	fWindow->MarkContentDirty(*dirty);
921 
922 	fWindow->RecycleRegion(dirty);
923 	fWindow->RecycleRegion(copyRegion);
924 }
925 
926 
927 // #pragma mark -
928 
929 
930 void
931 View::SetViewColor(const rgb_color& color)
932 {
933 	rgb_color oldColor = fViewColor;
934 	fViewColor = color;
935 	// Child view with B_TRANSPARENT_COLOR view color change clipping of
936 	// parent view.
937 	if (fParent != NULL
938 		&& IsVisible()
939 		&& ((oldColor == B_TRANSPARENT_COLOR)
940 			!= (color == B_TRANSPARENT_COLOR))) {
941 		fParent->RebuildClipping(false);
942 	}
943 }
944 
945 
946 void
947 View::ColorUpdated(color_which which, rgb_color color)
948 {
949 	float tint = B_NO_TINT;
950 
951 	if (fWhichViewColor == which)
952 		SetViewColor(tint_color(color, fWhichViewColorTint));
953 
954 	if (CurrentState()->HighUIColor(&tint) == which)
955 		CurrentState()->SetHighColor(tint_color(color, tint));
956 
957 	if (CurrentState()->LowUIColor(&tint) == which)
958 		CurrentState()->SetLowColor(tint_color(color, tint));
959 
960 	for (View* child = FirstChild(); child != NULL;
961 			child = child->NextSibling()) {
962 
963 		child->ColorUpdated(which, color);
964 	}
965 }
966 
967 
968 void
969 View::SetViewUIColor(color_which which, float tint)
970 {
971 	if (which != B_NO_COLOR) {
972 		DesktopSettings settings(fWindow->Desktop());
973 		SetViewColor(tint_color(settings.UIColor(which), tint));
974 	}
975 
976 	fWhichViewColor = which;
977 	fWhichViewColorTint = tint;
978 }
979 
980 
981 color_which
982 View::ViewUIColor(float* tint)
983 {
984 	if (tint != NULL)
985 		*tint = fWhichViewColorTint;
986 
987 	return fWhichViewColor;
988 }
989 
990 
991 // #pragma mark -
992 
993 
994 void
995 View::PushState()
996 {
997 	DrawState* newState = fDrawState->PushState();
998 	if (newState) {
999 		fDrawState = newState;
1000 		// In BeAPI, B_SUBPIXEL_PRECISE is a view flag, and not affected by the
1001 		// view state. Our implementation moves it to the draw state, but let's
1002 		// be compatible with the API here and make it survive accross state
1003 		// changes.
1004 		fDrawState->SetSubPixelPrecise(fFlags & B_SUBPIXEL_PRECISE);
1005 	}
1006 }
1007 
1008 
1009 void
1010 View::PopState()
1011 {
1012 	if (fDrawState->PreviousState() == NULL) {
1013 		fprintf(stderr, "WARNING: User called BView(%s)::PopState(), "
1014 			"but there is NO state on stack!\n", Name());
1015 		return;
1016 	}
1017 
1018 	bool rebuildClipping = fDrawState->HasAdditionalClipping();
1019 
1020 	fDrawState = fDrawState->PopState();
1021 	fDrawState->SetSubPixelPrecise(fFlags & B_SUBPIXEL_PRECISE);
1022 
1023 	// rebuild clipping
1024 	// (the clipping from the popped state is not effective anymore)
1025 	if (rebuildClipping)
1026 		RebuildClipping(false);
1027 }
1028 
1029 
1030 // #pragma mark -
1031 
1032 
1033 void
1034 View::SetEventMask(uint32 eventMask, uint32 options)
1035 {
1036 	fEventMask = eventMask;
1037 	fEventOptions = options;
1038 }
1039 
1040 
1041 void
1042 View::SetCursor(ServerCursor* cursor)
1043 {
1044 	if (cursor == fCursor)
1045 		return;
1046 
1047 	if (fCursor)
1048 		fCursor->ReleaseReference();
1049 
1050 	fCursor = cursor;
1051 
1052 	if (fCursor)
1053 		fCursor->AcquireReference();
1054 }
1055 
1056 
1057 void
1058 View::SetPicture(ServerPicture* picture)
1059 {
1060 	if (picture == fPicture)
1061 		return;
1062 
1063 	if (fPicture != NULL)
1064 		fPicture->ReleaseReference();
1065 
1066 	fPicture = picture;
1067 
1068 	if (fPicture != NULL)
1069 		fPicture->AcquireReference();
1070 }
1071 
1072 
1073 void
1074 View::BlendAllLayers()
1075 {
1076 	if (fPicture == NULL)
1077 		return;
1078 	Layer* layer = dynamic_cast<Layer*>(fPicture);
1079 	if (layer == NULL)
1080 		return;
1081 	BlendLayer(layer);
1082 }
1083 
1084 
1085 void
1086 View::Draw(DrawingEngine* drawingEngine, BRegion* effectiveClipping,
1087 	BRegion* windowContentClipping, bool deep)
1088 {
1089 	if (!fVisible) {
1090 		// child views cannot be visible either
1091 		return;
1092 	}
1093 
1094 	if (fViewBitmap != NULL || fViewColor != B_TRANSPARENT_COLOR) {
1095 		// we can only draw within our own area
1096 		BRegion* redraw;
1097 		if ((fFlags & B_DRAW_ON_CHILDREN) != 0) {
1098 			// The client may actually want to prevent the background to
1099 			// be painted outside the user clipping.
1100 			redraw = fWindow->GetRegion(
1101 				ScreenAndUserClipping(windowContentClipping));
1102 		} else {
1103 			// Ignore user clipping as in BeOS for painting the background.
1104 			redraw = fWindow->GetRegion(
1105 				_ScreenClipping(windowContentClipping));
1106 		}
1107 		if (!redraw)
1108 			return;
1109 		// add the current clipping
1110 		redraw->IntersectWith(effectiveClipping);
1111 
1112 		Overlay* overlayCookie = _Overlay();
1113 
1114 		if (fViewBitmap != NULL && overlayCookie == NULL) {
1115 			// draw view bitmap
1116 			// TODO: support other options!
1117 			BRect rect = fBitmapDestination;
1118 			PenToScreenTransform().Apply(&rect);
1119 
1120 			align_rect_to_pixels(&rect);
1121 
1122 			if (fBitmapOptions & B_TILE_BITMAP_Y) {
1123 				// move rect up as much as needed
1124 				while (rect.top > redraw->Frame().top)
1125 					rect.OffsetBy(0.0, -(rect.Height() + 1));
1126 			}
1127 			if (fBitmapOptions & B_TILE_BITMAP_X) {
1128 				// move rect left as much as needed
1129 				while (rect.left > redraw->Frame().left)
1130 					rect.OffsetBy(-(rect.Width() + 1), 0.0);
1131 			}
1132 
1133 // XXX: locking removed because the Window keeps the engine locked
1134 // because it keeps track of syncing right now
1135 
1136 			// lock the drawing engine for as long as we need the clipping
1137 			// to be valid
1138 			if (rect.IsValid()/* && drawingEngine->Lock()*/) {
1139 				drawingEngine->ConstrainClippingRegion(redraw);
1140 
1141 				drawing_mode oldMode;
1142 				drawingEngine->SetDrawingMode(B_OP_COPY, oldMode);
1143 
1144 				if (fBitmapOptions & B_TILE_BITMAP) {
1145 					// tile across entire view
1146 
1147 					float start = rect.left;
1148 					while (rect.top < redraw->Frame().bottom) {
1149 						while (rect.left < redraw->Frame().right) {
1150 							drawingEngine->DrawBitmap(fViewBitmap,
1151 								fBitmapSource, rect, fBitmapOptions);
1152 							rect.OffsetBy(rect.Width() + 1, 0.0);
1153 						}
1154 						rect.OffsetBy(start - rect.left, rect.Height() + 1);
1155 					}
1156 					// nothing left to be drawn
1157 					redraw->MakeEmpty();
1158 				} else if (fBitmapOptions & B_TILE_BITMAP_X) {
1159 					// tile in x direction
1160 
1161 					while (rect.left < redraw->Frame().right) {
1162 						drawingEngine->DrawBitmap(fViewBitmap, fBitmapSource,
1163 							rect, fBitmapOptions);
1164 						rect.OffsetBy(rect.Width() + 1, 0.0);
1165 					}
1166 					// remove horizontal stripe from clipping
1167 					rect.left = redraw->Frame().left;
1168 					rect.right = redraw->Frame().right;
1169 					redraw->Exclude(rect);
1170 				} else if (fBitmapOptions & B_TILE_BITMAP_Y) {
1171 					// tile in y direction
1172 
1173 					while (rect.top < redraw->Frame().bottom) {
1174 						drawingEngine->DrawBitmap(fViewBitmap, fBitmapSource,
1175 							rect, fBitmapOptions);
1176 						rect.OffsetBy(0.0, rect.Height() + 1);
1177 					}
1178 					// remove vertical stripe from clipping
1179 					rect.top = redraw->Frame().top;
1180 					rect.bottom = redraw->Frame().bottom;
1181 					redraw->Exclude(rect);
1182 				} else {
1183 					// no tiling at all
1184 
1185 					drawingEngine->DrawBitmap(fViewBitmap, fBitmapSource,
1186 						rect, fBitmapOptions);
1187 					redraw->Exclude(rect);
1188 				}
1189 
1190 				drawingEngine->SetDrawingMode(oldMode);
1191 
1192 				// NOTE: It is ok not to reset the clipping, that
1193 				// would only waste time
1194 //				drawingEngine->Unlock();
1195 			}
1196 
1197 		}
1198 
1199 		if (fViewColor != B_TRANSPARENT_COLOR) {
1200 			// fill visible region with view color,
1201 			// this version of FillRegion ignores any
1202 			// clipping, that's why "redraw" needs to
1203 			// be correct
1204 // see #634
1205 //			if (redraw->Frame().left < 0 || redraw->Frame().top < 0) {
1206 //				char message[1024];
1207 //				BRect c = effectiveClipping->Frame();
1208 //				BRect w = windowContentClipping->Frame();
1209 //				BRect r = redraw->Frame();
1210 //				sprintf(message, "invalid background: current clipping: (%d, %d)->(%d, %d), "
1211 //					"window content: (%d, %d)->(%d, %d), redraw: (%d, %d)->(%d, %d)",
1212 //					(int)c.left, (int)c.top, (int)c.right, (int)c.bottom,
1213 //					(int)w.left, (int)w.top, (int)w.right, (int)w.bottom,
1214 //					(int)r.left, (int)r.top, (int)r.right, (int)r.bottom);
1215 //				debugger(message);
1216 //			}
1217 
1218 			drawingEngine->FillRegion(*redraw, overlayCookie != NULL
1219 				? overlayCookie->Color() : fViewColor);
1220 		}
1221 
1222 		fWindow->RecycleRegion(redraw);
1223 	}
1224 
1225 	fBackgroundDirty = false;
1226 
1227 	// let children draw
1228 	if (deep) {
1229 		for (View* child = FirstChild(); child; child = child->NextSibling()) {
1230 			child->Draw(drawingEngine, effectiveClipping,
1231 				windowContentClipping, deep);
1232 		}
1233 	}
1234 }
1235 
1236 
1237 // #pragma mark -
1238 
1239 
1240 void
1241 View::MouseDown(BMessage* message, BPoint where)
1242 {
1243 	// empty hook method
1244 }
1245 
1246 
1247 void
1248 View::MouseUp(BMessage* message, BPoint where)
1249 {
1250 	// empty hook method
1251 }
1252 
1253 
1254 void
1255 View::MouseMoved(BMessage* message, BPoint where)
1256 {
1257 	// empty hook method
1258 }
1259 
1260 
1261 // #pragma mark -
1262 
1263 
1264 void
1265 View::SetHidden(bool hidden)
1266 {
1267 	if (fHidden != hidden) {
1268 		fHidden = hidden;
1269 
1270 		// recurse into children and update their visible flag
1271 		bool oldVisible = fVisible;
1272 		UpdateVisibleDeep(fParent ? fParent->IsVisible() : !fHidden);
1273 		if (oldVisible != fVisible) {
1274 			// Include or exclude us from the parent area, and update the
1275 			// children's clipping as well when the view will be visible
1276 			if (fParent)
1277 				fParent->RebuildClipping(fVisible);
1278 			else
1279 				RebuildClipping(fVisible);
1280 
1281 			if (fWindow) {
1282 				// trigger a redraw
1283 				IntRect clippedBounds = Bounds();
1284 				ConvertToVisibleInTopView(&clippedBounds);
1285 				BRegion* dirty = fWindow->GetRegion();
1286 				if (!dirty)
1287 					return;
1288 				dirty->Set((clipping_rect)clippedBounds);
1289 				fWindow->MarkContentDirty(*dirty);
1290 				fWindow->RecycleRegion(dirty);
1291 			}
1292 		}
1293 	}
1294 }
1295 
1296 
1297 bool
1298 View::IsHidden() const
1299 {
1300 	return fHidden;
1301 }
1302 
1303 
1304 void
1305 View::UpdateVisibleDeep(bool parentVisible)
1306 {
1307 	bool wasVisible = fVisible;
1308 
1309 	fVisible = parentVisible && !fHidden;
1310 	for (View* child = FirstChild(); child; child = child->NextSibling())
1311 		child->UpdateVisibleDeep(fVisible);
1312 
1313 	// overlay handling
1314 
1315 	Overlay* overlay = _Overlay();
1316 	if (overlay == NULL)
1317 		return;
1318 
1319 	if (fVisible && !wasVisible)
1320 		_UpdateOverlayView();
1321 	else if (!fVisible && wasVisible)
1322 		overlay->Hide();
1323 }
1324 
1325 
1326 // #pragma mark -
1327 
1328 
1329 void
1330 View::MarkBackgroundDirty()
1331 {
1332 	if (fBackgroundDirty)
1333 		return;
1334 	fBackgroundDirty = true;
1335 	for (View* child = FirstChild(); child; child = child->NextSibling())
1336 		child->MarkBackgroundDirty();
1337 }
1338 
1339 
1340 void
1341 View::AddTokensForViewsInRegion(BPrivate::PortLink& link, BRegion& region,
1342 	BRegion* windowContentClipping)
1343 {
1344 	if (!fVisible)
1345 		return;
1346 
1347 	{
1348 		// NOTE: use scope in order to reduce stack space requirements
1349 
1350 		// This check will prevent descending the view hierarchy
1351 		// any further than necessary
1352 		IntRect screenBounds(Bounds());
1353 		LocalToScreenTransform().Apply(&screenBounds);
1354 		if (!region.Intersects((clipping_rect)screenBounds))
1355 			return;
1356 
1357 		// Unfortunately, we intersecting another region, but otherwise
1358 		// we couldn't provide the exact update rect to the client
1359 		BRegion localDirty = _ScreenClipping(windowContentClipping);
1360 		localDirty.IntersectWith(&region);
1361 		if (localDirty.CountRects() > 0) {
1362 			link.Attach<int32>(fToken);
1363 			link.Attach<BRect>(localDirty.Frame());
1364 		}
1365 	}
1366 
1367 	for (View* child = FirstChild(); child; child = child->NextSibling())
1368 		child->AddTokensForViewsInRegion(link, region, windowContentClipping);
1369 }
1370 
1371 
1372 void
1373 View::PrintToStream() const
1374 {
1375 	printf("View:          %s\n", Name());
1376 	printf("  fToken:           %" B_PRId32 "\n", fToken);
1377 	printf("  fFrame:           IntRect(%" B_PRId32 ", %" B_PRId32 ", %" B_PRId32 ", %" B_PRId32 ")\n",
1378 		fFrame.left, fFrame.top, fFrame.right, fFrame.bottom);
1379 	printf("  fScrollingOffset: IntPoint(%" B_PRId32 ", %" B_PRId32 ")\n",
1380 		fScrollingOffset.x, fScrollingOffset.y);
1381 	printf("  fHidden:          %d\n", fHidden);
1382 	printf("  fVisible:         %d\n", fVisible);
1383 	printf("  fWindow:          %p\n", fWindow);
1384 	printf("  fParent:          %p\n", fParent);
1385 	printf("  fLocalClipping:\n");
1386 	fLocalClipping.PrintToStream();
1387 	printf("  fScreenClipping:\n");
1388 	fScreenClipping.PrintToStream();
1389 	printf("  valid:            %d\n", fScreenClippingValid);
1390 
1391 	printf("  fUserClipping:\n");
1392 	if (fUserClipping != NULL)
1393 		fUserClipping->PrintToStream();
1394 	else
1395 		printf("  none\n");
1396 
1397 	printf("  fScreenAndUserClipping:\n");
1398 	if (fScreenAndUserClipping != NULL)
1399 		fScreenAndUserClipping->PrintToStream();
1400 	else
1401 		printf("  invalid\n");
1402 
1403 	printf("  state:\n");
1404 	printf("    user clipping:  %d\n", fDrawState->HasClipping());
1405 	BPoint origin = fDrawState->CombinedOrigin();
1406 	printf("    origin:         BPoint(%.1f, %.1f)\n", origin.x, origin.y);
1407 	printf("    scale:          %.2f\n", fDrawState->CombinedScale());
1408 	printf("\n");
1409 }
1410 
1411 
1412 void
1413 View::RebuildClipping(bool deep)
1414 {
1415 	// the clipping spans over the bounds area
1416 	fLocalClipping.Set((clipping_rect)Bounds());
1417 
1418 	if (View* child = FirstChild()) {
1419 		// if this view does not draw over children,
1420 		// exclude all children from the clipping
1421 		if ((fFlags & B_DRAW_ON_CHILDREN) == 0) {
1422 			BRegion* childrenRegion = fWindow->GetRegion();
1423 			if (!childrenRegion)
1424 				return;
1425 
1426 			for (; child; child = child->NextSibling()) {
1427 				if (child->IsVisible()
1428 					&& child->fViewColor != B_TRANSPARENT_COLOR) {
1429 					childrenRegion->Include((clipping_rect)child->Frame());
1430 				}
1431 			}
1432 
1433 			fLocalClipping.Exclude(childrenRegion);
1434 			fWindow->RecycleRegion(childrenRegion);
1435 		}
1436 		// if the operation is "deep", make children rebuild their
1437 		// clipping too
1438 		if (deep) {
1439 			for (child = FirstChild(); child; child = child->NextSibling())
1440 				child->RebuildClipping(true);
1441 		}
1442 	}
1443 
1444 	// add the user clipping in case there is one
1445 	if (fDrawState->HasClipping()) {
1446 		// NOTE: in case the user sets a user defined clipping region,
1447 		// rebuilding the clipping is a bit more expensive because there
1448 		// is no separate "drawing region"... on the other
1449 		// hand, views for which this feature is actually used will
1450 		// probably not have any children, so it is not that expensive
1451 		// after all
1452 		if (fUserClipping == NULL) {
1453 			fUserClipping = new (nothrow) BRegion;
1454 			if (fUserClipping == NULL)
1455 				return;
1456 		}
1457 
1458 		fDrawState->GetCombinedClippingRegion(fUserClipping);
1459 	} else {
1460 		delete fUserClipping;
1461 		fUserClipping = NULL;
1462 	}
1463 
1464 	delete fScreenAndUserClipping;
1465 	fScreenAndUserClipping = NULL;
1466 	fScreenClippingValid = false;
1467 }
1468 
1469 
1470 BRegion&
1471 View::ScreenAndUserClipping(BRegion* windowContentClipping, bool force) const
1472 {
1473 	// no user clipping - return screen clipping directly
1474 	if (fUserClipping == NULL)
1475 		return _ScreenClipping(windowContentClipping, force);
1476 
1477 	// combined screen and user clipping already valid
1478 	if (fScreenAndUserClipping != NULL)
1479 		return *fScreenAndUserClipping;
1480 
1481 	// build a new combined user and screen clipping
1482 	fScreenAndUserClipping = new (nothrow) BRegion(*fUserClipping);
1483 	if (fScreenAndUserClipping == NULL)
1484 		return fScreenClipping;
1485 
1486 	LocalToScreenTransform().Apply(fScreenAndUserClipping);
1487 	fScreenAndUserClipping->IntersectWith(
1488 		&_ScreenClipping(windowContentClipping, force));
1489 	return *fScreenAndUserClipping;
1490 }
1491 
1492 
1493 void
1494 View::InvalidateScreenClipping()
1495 {
1496 // TODO: appearantly, we are calling ScreenClipping() on
1497 // views who's parents don't have a valid screen clipping yet,
1498 // this messes up the logic that for any given view with
1499 // fScreenClippingValid == false, all children have
1500 // fScreenClippingValid == false too. If this could be made the
1501 // case, we could save some performance here with the commented
1502 // out check, since InvalidateScreenClipping() might be called
1503 // frequently.
1504 // TODO: investigate, if InvalidateScreenClipping() could be
1505 // called in "deep" and "non-deep" mode, ie. see if there are
1506 // any cases where the children would still have valid screen
1507 // clipping, even though the parent's screen clipping becomes
1508 // invalid.
1509 //	if (!fScreenClippingValid)
1510 //		return;
1511 
1512 	delete fScreenAndUserClipping;
1513 	fScreenAndUserClipping = NULL;
1514 	fScreenClippingValid = false;
1515 	// invalidate the childrens screen clipping as well
1516 	for (View* child = FirstChild(); child; child = child->NextSibling()) {
1517 		child->InvalidateScreenClipping();
1518 	}
1519 }
1520 
1521 
1522 BRegion&
1523 View::_ScreenClipping(BRegion* windowContentClipping, bool force) const
1524 {
1525 	if (!fScreenClippingValid || force) {
1526 		fScreenClipping = fLocalClipping;
1527 		LocalToScreenTransform().Apply(&fScreenClipping);
1528 
1529 		// see if parts of our bounds are hidden underneath
1530 		// the parent, the local clipping does not account for this
1531 		IntRect clippedBounds = Bounds();
1532 		ConvertToVisibleInTopView(&clippedBounds);
1533 		if (clippedBounds.Width() < fScreenClipping.Frame().Width()
1534 			|| clippedBounds.Height() < fScreenClipping.Frame().Height()) {
1535 			BRegion* temp = fWindow->GetRegion();
1536 			if (temp) {
1537 				temp->Set((clipping_rect)clippedBounds);
1538 				fScreenClipping.IntersectWith(temp);
1539 				fWindow->RecycleRegion(temp);
1540 			}
1541 		}
1542 
1543 		fScreenClipping.IntersectWith(windowContentClipping);
1544 		fScreenClippingValid = true;
1545 	}
1546 
1547 	return fScreenClipping;
1548 }
1549 
1550 
1551 void
1552 View::_MoveScreenClipping(int32 x, int32 y, bool deep)
1553 {
1554 	if (fScreenClippingValid) {
1555 		fScreenClipping.OffsetBy(x, y);
1556 		delete fScreenAndUserClipping;
1557 		fScreenAndUserClipping = NULL;
1558 	}
1559 
1560 	if (deep) {
1561 		// move the childrens screen clipping as well
1562 		for (View* child = FirstChild(); child; child = child->NextSibling()) {
1563 			child->_MoveScreenClipping(x, y, deep);
1564 		}
1565 	}
1566 }
1567 
1568