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