1/*
2 * Copyright 2007, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
3 * All rights reserved. Distributed under the terms of the MIT License.
4 */
5
6#include "View.h"
7
8#include <stdio.h>
9
10#include <Region.h>
11#include <View.h>
12
13
14View::View()
15	: fFrame(0, 0, 0, 0),
16	  fContainer(NULL),
17	  fParent(NULL),
18	  fChildren(),
19	  fViewColor(B_TRANSPARENT_32_BIT),
20	  fLayoutValid(false)
21{
22}
23
24
25View::View(BRect frame)
26	: fFrame(frame),
27	  fContainer(NULL),
28	  fParent(NULL),
29	  fChildren(),
30	  fViewColor(B_TRANSPARENT_32_BIT),
31	  fLayoutValid(false)
32{
33}
34
35
36View::~View()
37{
38	// delete children
39	for (int32 i = CountChildren() - 1; i >= 0; i--)
40		delete RemoveChild(i);
41}
42
43
44void
45View::SetFrame(BRect frame)
46{
47	if (frame != fFrame && frame.IsValid()) {
48		BRect oldFrame(frame);
49		Invalidate();
50		fFrame = frame;
51		Invalidate();
52
53		FrameChanged(oldFrame, frame);
54	}
55
56	// relayout if necessary
57	if (!fLayoutValid) {
58		Layout();
59		fLayoutValid = true;
60	}
61}
62
63
64BRect
65View::Frame() const
66{
67	return fFrame;
68}
69
70
71BRect
72View::Bounds() const
73{
74	return BRect(fFrame).OffsetToCopy(B_ORIGIN);
75}
76
77
78void
79View::SetLocation(BPoint location)
80{
81	SetFrame(fFrame.OffsetToCopy(location));
82}
83
84
85BPoint
86View::Location() const
87{
88	return fFrame.LeftTop();
89}
90
91
92void
93View::SetSize(BSize size)
94{
95	BRect frame(fFrame);
96	frame.right = frame.left + size.width;
97	frame.bottom = frame.top + size.height;
98	SetFrame(frame);
99}
100
101
102BSize
103View::Size() const
104{
105	return Frame().Size();
106}
107
108
109BSize
110View::MinSize()
111{
112	return BSize(-1, -1);
113}
114
115
116BSize
117View::MaxSize()
118{
119	return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
120}
121
122
123BSize
124View::PreferredSize()
125{
126	return MinSize();
127}
128
129
130BAlignment
131View::Alignment()
132{
133	return BAlignment(B_ALIGN_HORIZONTAL_CENTER, B_ALIGN_VERTICAL_CENTER);
134}
135
136
137BPoint
138View::LocationInContainer() const
139{
140	BPoint location = fFrame.LeftTop();
141	return (fParent ? fParent->LocationInContainer() + location : location);
142}
143
144
145BRect
146View::FrameInContainer() const
147{
148	BRect frame(fFrame);
149	return frame.OffsetToCopy(LocationInContainer());
150}
151
152
153BPoint
154View::ConvertFromContainer(BPoint point) const
155{
156	return point - LocationInContainer();
157}
158
159
160BRect
161View::ConvertFromContainer(BRect rect) const
162{
163	return rect.OffsetBySelf(-LocationInContainer());
164}
165
166
167BPoint
168View::ConvertToContainer(BPoint point) const
169{
170	return point + LocationInContainer();
171}
172
173
174BRect
175View::ConvertToContainer(BRect rect) const
176{
177	return rect.OffsetBySelf(LocationInContainer());
178}
179
180
181View*
182View::Parent() const
183{
184	return fParent;
185}
186
187
188BView*
189View::Container() const
190{
191	return fContainer;
192}
193
194
195bool
196View::AddChild(View* child)
197{
198	if (!child)
199		return false;
200
201	if (child->Parent() || child->Container()) {
202		fprintf(stderr, "View::AddChild(): view %p already has a parent "
203			"or is the container view\n", child);
204		return false;
205	}
206
207	if (!fChildren.AddItem(child))
208		return false;
209
210	child->_AddedToParent(this);
211
212	child->Invalidate();
213	InvalidateLayout();
214
215	return true;
216}
217
218
219bool
220View::RemoveChild(View* child)
221{
222	if (!child)
223		return false;
224
225	return RemoveChild(IndexOfChild(child));
226}
227
228
229View*
230View::RemoveChild(int32 index)
231{
232	if (index < 0 || index >= fChildren.CountItems())
233		return NULL;
234
235	View* child = ChildAt(index);
236	child->Invalidate();
237	child->_RemovingFromParent();
238	fChildren.RemoveItem(index);
239
240	InvalidateLayout();
241
242	return child;
243}
244
245
246int32
247View::CountChildren() const
248{
249	return fChildren.CountItems();
250}
251
252
253View*
254View::ChildAt(int32 index) const
255{
256	return (View*)fChildren.ItemAt(index);
257}
258
259
260View*
261View::ChildAt(BPoint point) const
262{
263	for (int32 i = 0; View* child = ChildAt(i); i++) {
264		if (child->Frame().Contains(point))
265			return child;
266	}
267
268	return NULL;
269}
270
271
272View*
273View::AncestorAt(BPoint point, BPoint* localPoint) const
274{
275	if (!Bounds().Contains(point))
276		return NULL;
277
278	View* view = const_cast<View*>(this);
279
280	// Iterate deeper down the hierarchy, until we reach a view that
281	// doesn't have a child at the location.
282	while (true) {
283		View* child = view->ChildAt(point);
284		if (!child) {
285			if (localPoint)
286				*localPoint = point;
287			return view;
288		}
289
290		view = child;
291		point -= view->Frame().LeftTop();
292	}
293}
294
295
296int32
297View::IndexOfChild(View* child) const
298{
299	return (child ? fChildren.IndexOf(child) : -1);
300}
301
302
303void
304View::Invalidate(BRect rect)
305{
306	if (fContainer) {
307		rect = rect & Bounds();
308		fContainer->Invalidate(rect.OffsetByCopy(LocationInContainer()));
309	}
310}
311
312
313void
314View::Invalidate()
315{
316	Invalidate(Bounds());
317}
318
319
320void
321View::InvalidateLayout()
322{
323//printf("%p->View::InvalidateLayout(): %d\n", this, fLayoutValid);
324	if (fLayoutValid) {
325		fLayoutValid = false;
326		if (fParent)
327			fParent->InvalidateLayout();
328	}
329}
330
331
332bool
333View::IsLayoutValid() const
334{
335	return fLayoutValid;
336}
337
338
339void
340View::SetViewColor(rgb_color color)
341{
342	fViewColor = color;
343}
344
345
346void
347View::Draw(BView* container, BRect updateRect)
348{
349}
350
351
352void
353View::MouseDown(BPoint where, uint32 buttons, int32 modifiers)
354{
355}
356
357
358void
359View::MouseUp(BPoint where, uint32 buttons, int32 modifiers)
360{
361}
362
363
364void
365View::MouseMoved(BPoint where, uint32 buttons, int32 modifiers)
366{
367}
368
369
370void
371View::AddedToContainer()
372{
373}
374
375
376void
377View::RemovingFromContainer()
378{
379}
380
381
382void
383View::FrameChanged(BRect oldFrame, BRect newFrame)
384{
385}
386
387
388void
389View::Layout()
390{
391	// simply trigger relayouting the children
392	for (int32 i = 0; View* child = ChildAt(i); i++)
393		child->SetFrame(child->Frame());
394}
395
396
397void
398View::_AddedToParent(View* parent)
399{
400	fParent = parent;
401
402	if (parent->Container()) {
403		Invalidate();
404		_AddedToContainer(parent->Container());
405	}
406}
407
408
409void
410View::_RemovingFromParent()
411{
412	if (fContainer)
413		_RemovingFromContainer();
414
415	fParent = NULL;
416}
417
418
419void
420View::_AddedToContainer(BView* container)
421{
422	fContainer = container;
423
424	AddedToContainer();
425
426	for (int32 i = 0; View* child = ChildAt(i); i++)
427		child->_AddedToContainer(fContainer);
428}
429
430
431void
432View::_RemovingFromContainer()
433{
434	for (int32 i = 0; View* child = ChildAt(i); i++)
435		child->_RemovingFromContainer();
436
437	RemovingFromContainer();
438
439	fContainer = NULL;
440}
441
442
443void
444View::_Draw(BView* container, BRect updateRect)
445{
446	// compute the clipping region
447	BRegion region(Bounds());
448	for (int32 i = 0; View* child = ChildAt(i); i++)
449		region.Exclude(child->Frame());
450
451	if (region.Frame().IsValid()) {
452		// set the clipping region
453		container->ConstrainClippingRegion(&region);
454
455		// draw the background, if it isn't transparent
456		if (fViewColor.alpha != 0) {
457			container->SetLowColor(fViewColor);
458			container->FillRect(updateRect, B_SOLID_LOW);
459		}
460
461		// draw this view
462		Draw(container, updateRect);
463
464		// revert the clipping region
465		region.Set(Bounds());
466		container->ConstrainClippingRegion(&region);
467	}
468
469	// draw the children
470	if (CountChildren() > 0) {
471		container->PushState();
472
473		for (int32 i = 0; View* child = ChildAt(i); i++) {
474			BRect childFrame = child->Frame();
475			BRect childUpdateRect = updateRect & childFrame;
476			if (childUpdateRect.IsValid()) {
477				// set origin
478				childUpdateRect.OffsetBy(-childFrame.LeftTop());
479				container->SetOrigin(childFrame.LeftTop());
480
481				// draw
482				child->_Draw(container, childUpdateRect);
483			}
484		}
485
486		container->PopState();
487	}
488}
489