xref: /haiku/src/servers/app/View.cpp (revision 9642f7705b27e5c270c15fa526d14e1848c2c27d)
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(&region);
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