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