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