1/*
2 * Copyright 2006-2009, Ingo Weinhold <ingo_weinhold@gmx.de>.
3 * All rights reserved. Distributed under the terms of the MIT License.
4 */
5
6#include <CardLayout.h>
7
8#include <LayoutItem.h>
9#include <Message.h>
10#include <View.h>
11
12
13namespace {
14	const char* kVisibleItemField = "BCardLayout:visibleItem";
15}
16
17
18BCardLayout::BCardLayout()
19	:
20	BAbstractLayout(),
21	fMin(0, 0),
22	fMax(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED),
23	fPreferred(0, 0),
24	fVisibleItem(NULL),
25	fMinMaxValid(false)
26{
27}
28
29
30BCardLayout::BCardLayout(BMessage* from)
31	:
32	BAbstractLayout(BUnarchiver::PrepareArchive(from)),
33	fMin(0, 0),
34	fMax(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED),
35	fPreferred(0, 0),
36	fVisibleItem(NULL),
37	fMinMaxValid(false)
38{
39	BUnarchiver(from).Finish();
40}
41
42
43BCardLayout::~BCardLayout()
44{
45}
46
47
48BLayoutItem*
49BCardLayout::VisibleItem() const
50{
51	return fVisibleItem;
52}
53
54
55int32
56BCardLayout::VisibleIndex() const
57{
58	return IndexOfItem(fVisibleItem);
59}
60
61
62void
63BCardLayout::SetVisibleItem(int32 index)
64{
65	SetVisibleItem(ItemAt(index));
66}
67
68
69void
70BCardLayout::SetVisibleItem(BLayoutItem* item)
71{
72	if (item == fVisibleItem)
73		return;
74
75	if (item != NULL && IndexOfItem(item) < 0) {
76		debugger("BCardLayout::SetVisibleItem(BLayoutItem*): this item is not "
77			"part of this layout, or the item does not exist.");
78		return;
79	}
80
81	// Changing an item's visibility will invalidate its parent's layout (us),
82	// which would normally cause the min-max to be re-computed. But in this
83	// case, that is unnecessary, and so we can skip it.
84	const bool minMaxValid = fMinMaxValid;
85
86	if (fVisibleItem != NULL)
87		fVisibleItem->SetVisible(false);
88
89	fVisibleItem = item;
90
91	if (fVisibleItem != NULL)
92		fVisibleItem->SetVisible(true);
93
94	fMinMaxValid = minMaxValid;
95}
96
97
98BSize
99BCardLayout::BaseMinSize()
100{
101	_ValidateMinMax();
102	return fMin;
103}
104
105
106BSize
107BCardLayout::BaseMaxSize()
108{
109	_ValidateMinMax();
110	return fMax;
111}
112
113
114BSize
115BCardLayout::BasePreferredSize()
116{
117	_ValidateMinMax();
118	return fPreferred;
119}
120
121
122BAlignment
123BCardLayout::BaseAlignment()
124{
125	return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT);
126}
127
128
129bool
130BCardLayout::HasHeightForWidth()
131{
132	int32 count = CountItems();
133	for (int32 i = 0; i < count; i++) {
134		if (ItemAt(i)->HasHeightForWidth())
135			return true;
136	}
137
138	return false;
139}
140
141
142void
143BCardLayout::GetHeightForWidth(float width, float* min, float* max,
144	float* preferred)
145{
146	_ValidateMinMax();
147
148	// init with useful values
149	float minHeight = fMin.height;
150	float maxHeight = fMax.height;
151	float preferredHeight = fPreferred.height;
152
153	// apply the items' constraints
154	int32 count = CountItems();
155	for (int32 i = 0; i < count; i++) {
156		BLayoutItem* item = ItemAt(i);
157		if (item->HasHeightForWidth()) {
158			float itemMinHeight;
159			float itemMaxHeight;
160			float itemPreferredHeight;
161			item->GetHeightForWidth(width, &itemMinHeight, &itemMaxHeight,
162				&itemPreferredHeight);
163			minHeight = max_c(minHeight, itemMinHeight);
164			maxHeight = min_c(maxHeight, itemMaxHeight);
165			preferredHeight = min_c(preferredHeight, itemPreferredHeight);
166		}
167	}
168
169	// adjust max and preferred, if necessary
170	maxHeight = max_c(maxHeight, minHeight);
171	preferredHeight = max_c(preferredHeight, minHeight);
172	preferredHeight = min_c(preferredHeight, maxHeight);
173
174	if (min)
175		*min = minHeight;
176	if (max)
177		*max = maxHeight;
178	if (preferred)
179		*preferred = preferredHeight;
180}
181
182
183void
184BCardLayout::LayoutInvalidated(bool children)
185{
186	fMinMaxValid = false;
187}
188
189
190void
191BCardLayout::DoLayout()
192{
193	_ValidateMinMax();
194
195	BSize size(LayoutArea().Size());
196
197	// this cannot be done when we are viewless, as our children
198	// would not get cut off in the right place.
199	if (Owner()) {
200		size.width = max_c(size.width, fMin.width);
201		size.height = max_c(size.height, fMin.height);
202	}
203
204	if (fVisibleItem != NULL)
205		fVisibleItem->AlignInFrame(BRect(LayoutArea().LeftTop(), size));
206}
207
208
209status_t
210BCardLayout::Archive(BMessage* into, bool deep) const
211{
212	BArchiver archiver(into);
213	status_t err = BAbstractLayout::Archive(into, deep);
214
215	if (err == B_OK && deep)
216		err = into->AddInt32(kVisibleItemField, IndexOfItem(fVisibleItem));
217
218	return archiver.Finish(err);
219}
220
221
222status_t
223BCardLayout::AllArchived(BMessage* archive) const
224{
225	return BAbstractLayout::AllArchived(archive);
226}
227
228
229status_t
230BCardLayout::AllUnarchived(const BMessage* from)
231{
232	status_t err = BLayout::AllUnarchived(from);
233	if (err != B_OK)
234		return err;
235
236	int32 visibleIndex;
237	err = from->FindInt32(kVisibleItemField, &visibleIndex);
238	if (err == B_OK)
239		SetVisibleItem(visibleIndex);
240
241	return err;
242}
243
244
245status_t
246BCardLayout::ItemArchived(BMessage* into, BLayoutItem* item, int32 index) const
247{
248	return BAbstractLayout::ItemArchived(into, item, index);
249}
250
251
252status_t
253BCardLayout::ItemUnarchived(const BMessage* from, BLayoutItem* item,
254	int32 index)
255{
256	return BAbstractLayout::ItemUnarchived(from, item, index);
257}
258
259
260
261BArchivable*
262BCardLayout::Instantiate(BMessage* from)
263{
264	if (validate_instantiation(from, "BCardLayout"))
265		return new BCardLayout(from);
266	return NULL;
267}
268
269
270bool
271BCardLayout::ItemAdded(BLayoutItem* item, int32 atIndex)
272{
273	if (CountItems() <= 1)
274		SetVisibleItem(item);
275	else
276		item->SetVisible(false);
277	return true;
278}
279
280
281void
282BCardLayout::ItemRemoved(BLayoutItem* item, int32 fromIndex)
283{
284	fMinMaxValid = false;
285
286	if (fVisibleItem == item) {
287		BLayoutItem* newVisibleItem = NULL;
288		SetVisibleItem(newVisibleItem);
289	}
290}
291
292
293void
294BCardLayout::_ValidateMinMax()
295{
296	if (fMinMaxValid)
297		return;
298
299	fMin.width = 0;
300	fMin.height = 0;
301	fMax.width = B_SIZE_UNLIMITED;
302	fMax.height = B_SIZE_UNLIMITED;
303	fPreferred.width = 0;
304	fPreferred.height = 0;
305
306	int32 itemCount = CountItems();
307	for (int32 i = 0; i < itemCount; i++) {
308		BLayoutItem* item = ItemAt(i);
309
310		BSize min = item->MinSize();
311		BSize max = item->MaxSize();
312		BSize preferred = item->PreferredSize();
313
314		fMin.width = max_c(fMin.width, min.width);
315		fMin.height = max_c(fMin.height, min.height);
316
317		fMax.width = min_c(fMax.width, max.width);
318		fMax.height = min_c(fMax.height, max.height);
319
320		fPreferred.width = max_c(fPreferred.width, preferred.width);
321		fPreferred.height = max_c(fPreferred.height, preferred.height);
322	}
323
324	fMax.width = max_c(fMax.width, fMin.width);
325	fMax.height = max_c(fMax.height, fMin.height);
326
327	fPreferred.width = max_c(fPreferred.width, fMin.width);
328	fPreferred.height = max_c(fPreferred.height, fMin.height);
329	fPreferred.width = min_c(fPreferred.width, fMax.width);
330	fPreferred.height = min_c(fPreferred.height, fMax.height);
331
332	fMinMaxValid = true;
333	ResetLayoutInvalidation();
334}
335
336
337status_t
338BCardLayout::Perform(perform_code d, void* arg)
339{
340	return BAbstractLayout::Perform(d, arg);
341}
342
343
344void BCardLayout::_ReservedCardLayout1() {}
345void BCardLayout::_ReservedCardLayout2() {}
346void BCardLayout::_ReservedCardLayout3() {}
347void BCardLayout::_ReservedCardLayout4() {}
348void BCardLayout::_ReservedCardLayout5() {}
349void BCardLayout::_ReservedCardLayout6() {}
350void BCardLayout::_ReservedCardLayout7() {}
351void BCardLayout::_ReservedCardLayout8() {}
352void BCardLayout::_ReservedCardLayout9() {}
353void BCardLayout::_ReservedCardLayout10() {}
354
355