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