1/*
2 * Copyright 2011, Haiku, Inc.
3 * All rights reserved. Distributed under the terms of the MIT License.
4 */
5
6#include "CollapsingLayouter.h"
7
8#include "ComplexLayouter.h"
9#include "OneElementLayouter.h"
10#include "SimpleLayouter.h"
11
12#include <ObjectList.h>
13#include <Size.h>
14
15
16class CollapsingLayouter::ProxyLayoutInfo : public LayoutInfo {
17public:
18	ProxyLayoutInfo(LayoutInfo* target, int32 elementCount)
19		:
20		fTarget(target),
21		fElementCount(elementCount)
22	{
23		fElements = new int32[elementCount];
24	}
25
26	~ProxyLayoutInfo()
27	{
28		delete[] fElements;
29		delete fTarget;
30	}
31
32	void
33	LayoutTarget(Layouter* layouter, float size)
34	{
35		if (layouter)
36			layouter->Layout(fTarget, size);
37	}
38
39	void
40	SetElementPosition(int32 element, int32 position)
41	{
42		fElements[element] = position;
43	}
44
45	float
46	ElementLocation(int32 element)
47	{
48		if (element < 0 || element >= fElementCount || fElements[element] < 0)
49			return 0;
50		return fTarget->ElementLocation(fElements[element]);
51	}
52
53	float
54	ElementSize(int32 element)
55	{
56		if (element < 0 || element >= fElementCount || fElements[element] < 0)
57			return 0;
58		return fTarget->ElementSize(fElements[element]);
59	}
60
61	float
62	ElementRangeSize(int32 element, int32 length)
63	{
64		if (element < 0 || element >= fElementCount || fElements[element] < 0)
65			return 0;
66		return fTarget->ElementRangeSize(fElements[element], length);
67	}
68
69private:
70	int32*					fElements;
71	LayoutInfo*				fTarget;
72	int32					fElementCount;
73};
74
75
76struct CollapsingLayouter::Constraint {
77	int32 length;
78	float min;
79	float max;
80	float preferred;
81};
82
83
84struct CollapsingLayouter::ElementInfo {
85	float weight;
86	int32 position;
87	bool valid;
88	BObjectList<Constraint> constraints;
89
90	ElementInfo()
91		:
92		weight(0),
93		position(-1),
94		valid(false),
95		constraints(5, true)
96	{
97	}
98
99	~ElementInfo()
100	{
101	}
102
103	void SetTo(const ElementInfo& other)
104	{
105		weight = other.weight;
106		position = other.position;
107		valid = other.valid;
108		for (int32 i = other.constraints.CountItems() - 1; i >= 0; i--)
109			constraints.AddItem(new Constraint(*other.constraints.ItemAt(i)));
110	}
111};
112
113
114CollapsingLayouter::CollapsingLayouter(int32 elementCount, float spacing)
115	:
116	fElementCount(elementCount),
117	fElements(new ElementInfo[elementCount]),
118	fValidElementCount(0),
119	fHaveMultiElementConstraints(false),
120	fSpacing(spacing),
121	fLayouter(NULL)
122{
123}
124
125
126CollapsingLayouter::~CollapsingLayouter()
127{
128	delete[] fElements;
129	delete fLayouter;
130}
131
132
133void
134CollapsingLayouter::AddConstraints(int32 element, int32 length, float min,
135	float max, float preferred)
136{
137	if (min == B_SIZE_UNSET && max == B_SIZE_UNSET)
138		return;
139	if (element < 0 || length <= 0 || element + length > fElementCount)
140		return;
141
142	Constraint* constraint = new Constraint();
143	constraint->length = length;
144	constraint->min = min;
145	constraint->max = max;
146	constraint->preferred = preferred;
147
148	if (length > 1)
149		fHaveMultiElementConstraints = true;
150
151	int32 validElements = fValidElementCount;
152
153	for (int32 i = element; i < element + length; i++) {
154		if (fElements[i].valid == false) {
155			fElements[i].valid = true;
156			fValidElementCount++;
157		}
158	}
159
160	fElements[element].constraints.AddItem(constraint);
161	if (fValidElementCount > validElements) {
162		delete fLayouter;
163		fLayouter = NULL;
164	}
165
166	if (fLayouter)
167		_AddConstraints(element, constraint);
168
169}
170
171
172void
173CollapsingLayouter::SetWeight(int32 element, float weight)
174{
175	if (element < 0 || element >= fElementCount)
176		return;
177
178	ElementInfo& elementInfo = fElements[element];
179	elementInfo.weight = weight;
180
181	if (fLayouter && elementInfo.position >= 0)
182		fLayouter->SetWeight(elementInfo.position, weight);
183}
184
185
186float
187CollapsingLayouter::MinSize()
188{
189	_ValidateLayouter();
190	return fLayouter ? fLayouter->MinSize() : 0;
191}
192
193
194float
195CollapsingLayouter::MaxSize()
196{
197	_ValidateLayouter();
198	return fLayouter ? fLayouter->MaxSize() : B_SIZE_UNLIMITED;
199}
200
201
202float
203CollapsingLayouter::PreferredSize()
204{
205	_ValidateLayouter();
206	return fLayouter ? fLayouter->PreferredSize() : 0;
207}
208
209
210LayoutInfo*
211CollapsingLayouter::CreateLayoutInfo()
212{
213	_ValidateLayouter();
214
215	LayoutInfo* info = fLayouter ? fLayouter->CreateLayoutInfo() : NULL;
216	return new ProxyLayoutInfo(info, fElementCount);
217}
218
219
220void
221CollapsingLayouter::Layout(LayoutInfo* layoutInfo, float size)
222{
223	_ValidateLayouter();
224	ProxyLayoutInfo* info = static_cast<ProxyLayoutInfo*>(layoutInfo);
225	for (int32 i = 0; i < fElementCount; i++) {
226		info->SetElementPosition(i, fElements[i].position);
227	}
228
229	info->LayoutTarget(fLayouter, size);
230}
231
232
233Layouter*
234CollapsingLayouter::CloneLayouter()
235{
236	CollapsingLayouter* clone = new CollapsingLayouter(fElementCount, fSpacing);
237	for (int32 i = 0; i < fElementCount; i++)
238		clone->fElements[i].SetTo(fElements[i]);
239
240	clone->fValidElementCount = fValidElementCount;
241	clone->fHaveMultiElementConstraints = fHaveMultiElementConstraints;
242
243	if (fLayouter)
244		clone->fLayouter = fLayouter->CloneLayouter();
245	return clone;
246}
247
248
249void
250CollapsingLayouter::_ValidateLayouter()
251{
252	if (fLayouter)
253		return;
254
255	_CreateLayouter();
256	_DoCollapse();
257	_AddConstraints();
258	_SetWeights();
259}
260
261
262Layouter*
263CollapsingLayouter::_CreateLayouter()
264{
265	if (fLayouter)
266		return fLayouter;
267
268	if (fValidElementCount == 0) {
269		fLayouter =  NULL;
270	} else if (fValidElementCount == 1) {
271		fLayouter =  new OneElementLayouter();
272	} else if (fHaveMultiElementConstraints) {
273		fLayouter =  new ComplexLayouter(fValidElementCount, fSpacing);
274	} else {
275		fLayouter = new SimpleLayouter(fValidElementCount, fSpacing);
276	}
277
278	return fLayouter;
279}
280
281
282void
283CollapsingLayouter::_DoCollapse()
284{
285	int32 shift = 0;
286	for (int32 i = 0; i < fElementCount; i++) {
287		ElementInfo& element = fElements[i];
288		if (!element.valid) {
289			shift++;
290			element.position = -1;
291			continue;
292		} else {
293			element.position = i - shift;
294		}
295	}
296}
297
298
299void
300CollapsingLayouter::_AddConstraints()
301{
302	if (fLayouter == NULL)
303		return;
304
305	for (int32 i = 0; i < fElementCount; i++) {
306		ElementInfo& element = fElements[i];
307		for (int32 i = element.constraints.CountItems() - 1; i >= 0; i--)
308			_AddConstraints(element.position, element.constraints.ItemAt(i));
309	}
310}
311
312
313void
314CollapsingLayouter::_AddConstraints(int32 position, const Constraint* c)
315{
316	fLayouter->AddConstraints(position, c->length, c->min, c->max,
317		c->preferred);
318}
319
320
321void
322CollapsingLayouter::_SetWeights()
323{
324	if (!fLayouter)
325		return;
326
327	for (int32 i = 0; i < fElementCount; i++) {
328		fLayouter->SetWeight(fElements[i].position, fElements[i].weight);
329	}
330}
331