1
2#include <stdio.h>
3
4#include <List.h>
5#include <View.h> // for resize modes
6
7#include "Desktop.h"
8#include "DrawingEngine.h"
9#include "WindowLayer.h"
10
11#include "ViewLayer.h"
12
13extern BWindow* wind;
14
15// constructor
16ViewLayer::ViewLayer(BRect frame, const char* name,
17			 uint32 resizeMode, uint32 flags,
18			 rgb_color viewColor)
19	: fName(name),
20
21	  fFrame(frame),
22	  fScrollingOffset(0.0, 0.0),
23
24	  fViewColor(viewColor),
25
26	  fResizeMode(resizeMode),
27	  fFlags(flags),
28
29	  // ViewLayers start visible by default
30	  fHidden(false),
31	  fVisible(true),
32
33	  fWindow(NULL),
34	  fParent(NULL),
35
36	  fFirstChild(NULL),
37	  fPreviousSibling(NULL),
38	  fNextSibling(NULL),
39	  fLastChild(NULL),
40
41	  fCurrentChild(NULL),
42
43	  fLocalClipping(Bounds()),
44	  fScreenClipping(),
45	  fScreenClippingValid(false)
46{
47	fFrame.left = float((int32)fFrame.left);
48	fFrame.top = float((int32)fFrame.top);
49	fFrame.right = float((int32)fFrame.right);
50	fFrame.bottom = float((int32)fFrame.bottom);
51}
52
53// destructor
54ViewLayer::~ViewLayer()
55{
56	// iterate over children and delete each one
57	ViewLayer* layer = fFirstChild;
58	while (layer) {
59		ViewLayer* toast = layer;
60		layer = layer->fNextSibling;
61		delete toast;
62	}
63}
64
65// Bounds
66BRect
67ViewLayer::Bounds() const
68{
69	BRect bounds(fScrollingOffset.x, fScrollingOffset.y,
70				 fScrollingOffset.x + fFrame.Width(),
71				 fScrollingOffset.y + fFrame.Height());
72	return bounds;
73}
74
75// ConvertToVisibleInTopView
76void
77ViewLayer::ConvertToVisibleInTopView(BRect* bounds) const
78{
79	*bounds = *bounds & Bounds();
80	// NOTE: this step is necessary even if we don't have a parent!
81	ConvertToParent(bounds);
82
83	if (fParent)
84		fParent->ConvertToVisibleInTopView(bounds);
85}
86
87// AttachedToWindow
88void
89ViewLayer::AttachedToWindow(WindowLayer* window)
90{
91	fWindow = window;
92
93	for (ViewLayer* child = FirstChild(); child; child = NextChild())
94		child->AttachedToWindow(window);
95}
96
97
98// DetachedFromWindow
99void
100ViewLayer::DetachedFromWindow()
101{
102	fWindow = NULL;
103	for (ViewLayer* child = FirstChild(); child; child = NextChild())
104		child->DetachedFromWindow();
105}
106
107// AddChild
108void
109ViewLayer::AddChild(ViewLayer* layer)
110{
111	if (layer->fParent) {
112		printf("ViewLayer::AddChild() - ViewLayer already has a parent\n");
113		return;
114	}
115
116	layer->fParent = this;
117
118	if (!fLastChild) {
119		// no children yet
120		fFirstChild = layer;
121	} else {
122		// append layer to formerly last child
123		fLastChild->fNextSibling = layer;
124		layer->fPreviousSibling = fLastChild;
125	}
126	fLastChild = layer;
127
128	if (layer->IsVisible())
129		RebuildClipping(false);
130
131	if (fWindow) {
132		layer->AttachedToWindow(fWindow);
133
134		if (fVisible && layer->IsVisible()) {
135			// trigger redraw
136			BRect clippedFrame = layer->Frame();
137			ConvertToVisibleInTopView(&clippedFrame);
138			BRegion dirty(clippedFrame);
139			fWindow->MarkContentDirty(&dirty);
140		}
141	}
142}
143
144// RemoveChild
145bool
146ViewLayer::RemoveChild(ViewLayer* layer)
147{
148	if (layer->fParent != this) {
149		printf("ViewLayer::RemoveChild(%p - %s) - ViewLayer is not child of this (%p) layer!\n", layer, layer ? layer->Name() : NULL, this);
150		return false;
151	}
152
153	layer->fParent = NULL;
154
155	if (fLastChild == layer)
156		fLastChild = layer->fPreviousSibling;
157		// layer->fNextSibling would be NULL
158
159	if (fFirstChild == layer )
160		fFirstChild = layer->fNextSibling;
161		// layer->fPreviousSibling would be NULL
162
163	// connect child before and after layer
164	if (layer->fPreviousSibling)
165		layer->fPreviousSibling->fNextSibling = layer->fNextSibling;
166
167	if (layer->fNextSibling)
168		layer->fNextSibling->fPreviousSibling = layer->fPreviousSibling;
169
170	// layer has no siblings anymore
171	layer->fPreviousSibling = NULL;
172	layer->fNextSibling = NULL;
173
174	if (layer->IsVisible())
175		RebuildClipping(false);
176
177	if (fWindow) {
178		layer->DetachedFromWindow();
179
180		if (fVisible && layer->IsVisible()) {
181			// trigger redraw
182			BRect clippedFrame = layer->Frame();
183			ConvertToVisibleInTopView(&clippedFrame);
184			BRegion dirty(clippedFrame);
185			fWindow->MarkContentDirty(&dirty);
186		}
187	}
188
189	return true;
190}
191
192// FirstChild
193ViewLayer*
194ViewLayer::FirstChild() const
195{
196	fCurrentChild = fFirstChild;
197	return fCurrentChild;
198}
199
200// PreviousChild
201ViewLayer*
202ViewLayer::PreviousChild() const
203{
204	fCurrentChild = fCurrentChild->fPreviousSibling;
205	return fCurrentChild;
206}
207
208// NextChild
209ViewLayer*
210ViewLayer::NextChild() const
211{
212	fCurrentChild = fCurrentChild->fNextSibling;
213	return fCurrentChild;
214}
215
216// LastChild
217ViewLayer*
218ViewLayer::LastChild() const
219{
220	fCurrentChild = fLastChild;
221	return fCurrentChild;
222}
223
224// TopLayer
225ViewLayer*
226ViewLayer::TopLayer()
227{
228	// returns the top level view of the hirarchy,
229	// it doesn't have to be the top level of a window
230
231	if (fParent)
232		return fParent->TopLayer();
233
234	return this;
235}
236
237// CountChildren
238uint32
239ViewLayer::CountChildren(bool deep) const
240{
241	uint32 count = 0;
242	for (ViewLayer* child = FirstChild(); child; child = NextChild()) {
243		count++;
244		if (deep) {
245			count += child->CountChildren(deep);
246		}
247	}
248	return count;
249}
250
251// CollectTokensForChildren
252void
253ViewLayer::CollectTokensForChildren(BList* tokenMap) const
254{
255	for (ViewLayer* child = FirstChild(); child; child = NextChild()) {
256		tokenMap->AddItem((void*)child);
257		child->CollectTokensForChildren(tokenMap);
258	}
259}
260
261// ViewAt
262ViewLayer*
263ViewLayer::ViewAt(const BPoint& where, BRegion* windowContentClipping)
264{
265	if (!fVisible)
266		return NULL;
267
268	if (ScreenClipping(windowContentClipping).Contains(where))
269		return this;
270
271	for (ViewLayer* child = FirstChild(); child; child = NextChild()) {
272		ViewLayer* layer = child->ViewAt(where, windowContentClipping);
273		if (layer)
274			return layer;
275	}
276	return NULL;
277}
278
279// ConvertToParent
280void
281ViewLayer::ConvertToParent(BPoint* point) const
282{
283	// remove scrolling offset and convert to parent coordinate space
284	point->x += fFrame.left - fScrollingOffset.x;
285	point->y += fFrame.top - fScrollingOffset.y;
286}
287
288// ConvertToParent
289void
290ViewLayer::ConvertToParent(BRect* rect) const
291{
292	// remove scrolling offset and convert to parent coordinate space
293	rect->OffsetBy(fFrame.left - fScrollingOffset.x,
294				   fFrame.top - fScrollingOffset.y);
295}
296
297// ConvertToParent
298void
299ViewLayer::ConvertToParent(BRegion* region) const
300{
301	// remove scrolling offset and convert to parent coordinate space
302	region->OffsetBy(fFrame.left - fScrollingOffset.x,
303					 fFrame.top - fScrollingOffset.y);
304}
305
306// ConvertFromParent
307void
308ViewLayer::ConvertFromParent(BPoint* point) const
309{
310	// remove scrolling offset and convert to parent coordinate space
311	point->x += fScrollingOffset.x - fFrame.left;
312	point->y += fScrollingOffset.y - fFrame.top;
313}
314
315// ConvertFromParent
316void
317ViewLayer::ConvertFromParent(BRect* rect) const
318{
319	// remove scrolling offset and convert to parent coordinate space
320	rect->OffsetBy(fScrollingOffset.x - fFrame.left,
321				   fScrollingOffset.y - fFrame.top);
322}
323
324// ConvertFromParent
325void
326ViewLayer::ConvertFromParent(BRegion* region) const
327{
328	// remove scrolling offset and convert to parent coordinate space
329	region->OffsetBy(fScrollingOffset.x - fFrame.left,
330					 fScrollingOffset.y - fFrame.top);
331}
332
333// ConvertToTop
334void
335ViewLayer::ConvertToTop(BPoint* point) const
336{
337	ConvertToParent(point);
338
339	if (fParent)
340		fParent->ConvertToTop(point);
341}
342
343// ConvertToTop
344void
345ViewLayer::ConvertToTop(BRect* rect) const
346{
347	ConvertToParent(rect);
348
349	if (fParent)
350		fParent->ConvertToTop(rect);
351}
352
353// ConvertToTop
354void
355ViewLayer::ConvertToTop(BRegion* region) const
356{
357	ConvertToParent(region);
358
359	if (fParent)
360		fParent->ConvertToTop(region);
361}
362
363// ConvertFromTop
364void
365ViewLayer::ConvertFromTop(BPoint* point) const
366{
367	ConvertFromParent(point);
368
369	if (fParent)
370		fParent->ConvertFromTop(point);
371}
372
373// ConvertFromTop
374void
375ViewLayer::ConvertFromTop(BRect* rect) const
376{
377	ConvertFromParent(rect);
378
379	if (fParent)
380		fParent->ConvertFromTop(rect);
381}
382
383// ConvertFromTop
384void
385ViewLayer::ConvertFromTop(BRegion* region) const
386{
387	ConvertFromParent(region);
388
389	if (fParent)
390		fParent->ConvertFromTop(region);
391}
392
393// SetName
394void
395ViewLayer::SetName(const char* string)
396{
397	fName.SetTo(string);
398}
399
400// #pragma mark -
401
402// MoveBy
403void
404ViewLayer::MoveBy(int32 x, int32 y, BRegion* dirtyRegion)
405{
406	if (x == 0 && y == 0)
407		return;
408
409	fFrame.OffsetBy(x, y);
410
411	// to move on screen, we must not be hidden and we must have a parent
412	if (fVisible && fParent && dirtyRegion) {
413#if 0
414// based on redraw on new location
415		// the place were we are now visible
416		BRect newVisibleBounds = Bounds();
417		// we can use the frame of the old
418		// local clipping to see which parts need invalidation
419		BRect oldVisibleBounds(Bounds());
420		oldVisibleBounds.OffsetBy(-x, -y);
421		ConvertToTop(&oldVisibleBounds);
422
423		ConvertToVisibleInTopView(&newVisibleBounds);
424
425		dirtyRegion->Include(oldVisibleBounds);
426		// newVisibleBounds already is in screen coords
427		dirtyRegion->Include(newVisibleBounds);
428#else
429// blitting version, invalidates
430// old contents
431		BRect oldVisibleBounds(Bounds());
432		oldVisibleBounds.OffsetBy(-x, -y);
433		ConvertToTop(&oldVisibleBounds);
434
435		BRect newVisibleBounds(Bounds());
436		// NOTE: using ConvertToVisibleInTopView()
437		// instead of ConvertToTop()! see below
438		ConvertToVisibleInTopView(&newVisibleBounds);
439
440		newVisibleBounds.OffsetBy(-x, -y);
441
442		// clipping oldVisibleBounds to newVisibleBounds
443		// makes sure we don't copy parts hidden under
444		// parent views
445		BRegion copyRegion(oldVisibleBounds & newVisibleBounds);
446		fWindow->CopyContents(&copyRegion, x, y);
447
448		BRegion dirty(oldVisibleBounds);
449		newVisibleBounds.OffsetBy(x, y);
450		dirty.Exclude(newVisibleBounds);
451		dirtyRegion->Include(&dirty);
452#endif
453	}
454
455	if (!fParent) {
456		// the top view's screen clipping does not change,
457		// because no parts are clipped away from parent
458		// views
459		_MoveScreenClipping(x, y, true);
460	} else {
461		// parts might have been revealed from underneath
462		// the parent, or might now be hidden underneath
463		// the parent, this is taken care of when building
464		// the screen clipping
465		InvalidateScreenClipping(true);
466	}
467}
468
469// ResizeBy
470void
471ViewLayer::ResizeBy(int32 x, int32 y, BRegion* dirtyRegion)
472{
473	if (x == 0 && y == 0)
474		return;
475
476	fFrame.right += x;
477	fFrame.bottom += y;
478
479	if (fVisible && dirtyRegion) {
480		BRect oldBounds(Bounds());
481		oldBounds.right -= x;
482		oldBounds.bottom -= y;
483
484		BRegion dirty(Bounds());
485		dirty.Include(oldBounds);
486
487		if (!(fFlags & B_FULL_UPDATE_ON_RESIZE)) {
488			// the dirty region is just the difference of
489			// old and new bounds
490			dirty.Exclude(oldBounds & Bounds());
491		}
492
493		InvalidateScreenClipping(true);
494
495		if (dirty.CountRects() > 0) {
496			// exclude children, they are expected to
497			// include their own dirty regions in ParentResized()
498			for (ViewLayer* child = FirstChild(); child; child = NextChild()) {
499				if (child->IsVisible()) {
500					BRect previousChildVisible(child->Frame() & oldBounds & Bounds());
501					if (dirty.Frame().Intersects(previousChildVisible)) {
502						dirty.Exclude(previousChildVisible);
503					}
504				}
505			}
506
507			ConvertToTop(&dirty);
508			dirtyRegion->Include(&dirty);
509		}
510	}
511
512	// layout the children
513	for (ViewLayer* child = FirstChild(); child; child = NextChild())
514		child->ParentResized(x, y, dirtyRegion);
515
516	// at this point, children are at their new locations,
517	// so we can rebuild the clipping
518	// TODO: when the implementation of Hide() and Show() is
519	// complete, see if this should be avoided
520	RebuildClipping(false);
521}
522
523// ParentResized
524void
525ViewLayer::ParentResized(int32 x, int32 y, BRegion* dirtyRegion)
526{
527	uint16 rm = fResizeMode & 0x0000FFFF;
528	BRect newFrame = fFrame;
529
530	// follow with left side
531	if ((rm & 0x0F00U) == _VIEW_RIGHT_ << 8)
532		newFrame.left += x;
533	else if ((rm & 0x0F00U) == _VIEW_CENTER_ << 8)
534		newFrame.left += x / 2;
535
536	// follow with right side
537	if ((rm & 0x000FU) == _VIEW_RIGHT_)
538		newFrame.right += x;
539	else if ((rm & 0x000FU) == _VIEW_CENTER_)
540		newFrame.right += x / 2;
541
542	// follow with top side
543	if ((rm & 0xF000U) == _VIEW_BOTTOM_ << 12)
544		newFrame.top += y;
545	else if ((rm & 0xF000U) == _VIEW_CENTER_ << 12)
546		newFrame.top += y / 2;
547
548	// follow with bottom side
549	if ((rm & 0x00F0U) == _VIEW_BOTTOM_ << 4)
550		newFrame.bottom += y;
551	else if ((rm & 0x00F0U) == _VIEW_CENTER_ << 4)
552		newFrame.bottom += y / 2;
553
554	if (newFrame != fFrame) {
555		// careful, MoveBy will change fFrame
556		int32 widthDiff = (int32)(newFrame.Width() - fFrame.Width());
557		int32 heightDiff = (int32)(newFrame.Height() - fFrame.Height());
558
559		MoveBy(newFrame.left - fFrame.left,
560			   newFrame.top - fFrame.top, dirtyRegion);
561
562		ResizeBy(widthDiff, heightDiff, dirtyRegion);
563	}
564}
565
566// ScrollBy
567void
568ViewLayer::ScrollBy(int32 x, int32 y, BRegion* dirtyRegion)
569{
570	// blitting version, invalidates
571	// old contents
572
573	// remember old bounds for tracking dirty region
574	BRect oldBounds(Bounds());
575	// find the area of the view that can be scrolled,
576	// contents are shifted in the opposite direction from scrolling
577	BRect stillVisibleBounds(oldBounds);
578	stillVisibleBounds.OffsetBy(x, y);
579
580	// NOTE: using ConvertToVisibleInTopView()
581	// instead of ConvertToTop(), this makes
582	// sure we don't try to move or invalidate an
583	// area hidden underneath the parent view
584	ConvertToVisibleInTopView(&oldBounds);
585	ConvertToVisibleInTopView(&stillVisibleBounds);
586
587	fScrollingOffset.x += x;
588	fScrollingOffset.y += y;
589
590	// do the blit, this will make sure
591	// that other more complex dirty regions
592	// are taken care of
593	BRegion copyRegion(stillVisibleBounds);
594	fWindow->CopyContents(&copyRegion, -x, -y);
595
596	// find the dirty region as far as we are
597	// concerned
598	BRegion dirty(oldBounds);
599	stillVisibleBounds.OffsetBy(-x, -y);
600	dirty.Exclude(stillVisibleBounds);
601	dirtyRegion->Include(&dirty);
602
603	// the screen clipping of this view and it's
604	// childs is no longer valid
605	InvalidateScreenClipping(true);
606	RebuildClipping(false);
607}
608
609// #pragma mark -
610
611// Draw
612void
613ViewLayer::Draw(DrawingEngine* drawingEngine, BRegion* effectiveClipping,
614				BRegion* windowContentClipping, bool deep)
615{
616	// we can only draw within our own area
617	BRegion redraw(ScreenClipping(windowContentClipping));
618	// add the current clipping
619	redraw.IntersectWith(effectiveClipping);
620
621	// fill visible region with white
622	drawingEngine->FillRegion(&redraw, (rgb_color){ 255, 255, 255, 255 });
623
624	// let children draw
625	if (deep) {
626		// before passing the clipping on to children, exclude our
627		// own region from the available clipping
628		effectiveClipping->Exclude(&ScreenClipping(windowContentClipping));
629
630		for (ViewLayer* child = FirstChild(); child; child = NextChild()) {
631			child->Draw(drawingEngine, effectiveClipping,
632						windowContentClipping, deep);
633		}
634	}
635}
636
637// ClientDraw
638void
639ViewLayer::ClientDraw(DrawingEngine* drawingEngine, BRegion* effectiveClipping)
640{
641	BRect b(Bounds());
642	b.OffsetTo(0.0, 0.0);
643	ConvertToTop(&b);
644
645	if (drawingEngine->Lock()) {
646		drawingEngine->ConstrainClipping(effectiveClipping);
647
648		// draw a frame with the view color
649		drawingEngine->StrokeRect(b, fViewColor);
650		b.InsetBy(1, 1);
651		drawingEngine->StrokeRect(b, fViewColor);
652		b.InsetBy(1, 1);
653		drawingEngine->StrokeRect(b, fViewColor);
654		b.InsetBy(1, 1);
655		drawingEngine->StrokeLine(b.LeftTop(), b.RightBottom(), fViewColor);
656
657		drawingEngine->Unlock();
658	}
659}
660
661// #pragma mark -
662
663// SetHidden
664void
665ViewLayer::SetHidden(bool hidden)
666{
667	// TODO: test...
668
669	if (fHidden != hidden) {
670		fHidden = hidden;
671
672		// recurse into children and update their visible flag
673		if (fParent) {
674			bool olfVisible = fVisible;
675			UpdateVisibleDeep(fParent->IsVisible());
676			if (olfVisible != fVisible) {
677				// include or exclude us from the parent area
678				fParent->RebuildClipping(false);
679				if (fWindow) {
680					// trigger a redraw
681					BRect clippedBounds = Bounds();
682					ConvertToVisibleInTopView(&clippedBounds);
683					BRegion dirty(clippedBounds);
684					fWindow->MarkContentDirty(&dirty);
685				}
686			}
687		} else {
688			UpdateVisibleDeep(true);
689		}
690	}
691}
692
693// IsHidden
694bool
695ViewLayer::IsHidden() const
696{
697	return fHidden;
698}
699
700// UpdateVisibleDeep
701void
702ViewLayer::UpdateVisibleDeep(bool parentVisible)
703{
704	fVisible = parentVisible && !fHidden;
705	for (ViewLayer* child = FirstChild(); child; child = NextChild())
706		child->UpdateVisibleDeep(fVisible);
707}
708
709// PrintToStream
710void
711ViewLayer::PrintToStream() const
712{
713}
714
715// RebuildClipping
716void
717ViewLayer::RebuildClipping(bool deep)
718{
719	// the clipping spans over the bounds area
720	fLocalClipping.Set(Bounds());
721
722	// exclude all childs from the clipping
723	for (ViewLayer* child = FirstChild(); child; child = NextChild()) {
724		if (child->IsVisible())
725			fLocalClipping.Exclude(child->Frame());
726
727		if (deep)
728			child->RebuildClipping(deep);
729	}
730
731	fScreenClippingValid = false;
732}
733
734// ScreenClipping
735BRegion&
736ViewLayer::ScreenClipping(BRegion* windowContentClipping, bool force) const
737{
738	if (!fScreenClippingValid || force) {
739		fScreenClipping = fLocalClipping;
740		ConvertToTop(&fScreenClipping);
741
742		// see if we parts of our bounds are hidden underneath
743		// the parent, the local clipping does not account for this
744		BRect clippedBounds = Bounds();
745		ConvertToVisibleInTopView(&clippedBounds);
746		if (clippedBounds.Width() < fScreenClipping.Frame().Width() ||
747			clippedBounds.Height() < fScreenClipping.Frame().Height()) {
748			BRegion temp(clippedBounds);
749			fScreenClipping.IntersectWith(&temp);
750		}
751
752		fScreenClipping.IntersectWith(windowContentClipping);
753		fScreenClippingValid = true;
754	}
755	return fScreenClipping;
756}
757
758// InvalidateScreenClipping
759void
760ViewLayer::InvalidateScreenClipping(bool deep)
761{
762	fScreenClippingValid = false;
763	if (deep) {
764		// invalidate the childrens screen clipping as well
765		for (ViewLayer* child = FirstChild(); child; child = NextChild()) {
766			child->InvalidateScreenClipping(deep);
767		}
768	}
769}
770
771// _MoveScreenClipping
772void
773ViewLayer::_MoveScreenClipping(int32 x, int32 y, bool deep)
774{
775	if (fScreenClippingValid)
776		fScreenClipping.OffsetBy(x, y);
777
778	if (deep) {
779		// move the childrens screen clipping as well
780		for (ViewLayer* child = FirstChild(); child; child = NextChild()) {
781			child->_MoveScreenClipping(x, y, deep);
782		}
783	}
784}
785
786