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