1/*
2 * Copyright 2012, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "SharedSolver.h"
8
9#include <set>
10
11#include <ALMLayout.h>
12#include <AutoDeleter.h>
13#include <ObjectList.h>
14
15#include "RowColumnManager.h"
16
17
18using LinearProgramming::LinearSpec;
19
20
21namespace {
22
23type_code kConstraintsTypeCode = 'cnst';
24const char* kConstraintsField = "SharedSolver:constraints";
25
26};
27
28
29struct SharedSolver::MinSizeValidator {
30	inline void CallSolverMethod(LinearSpec* spec, VariableList* vars)
31	{
32		spec->FindMins(vars);
33	}
34
35	inline void Finalize(BALMLayout* layout, SharedSolver* solver,
36		ResultType solveResult)
37	{
38		if (solveResult == LinearProgramming::kUnbounded) {
39			solver->SetMinSize(layout, BSize(0, 0));
40		} else {
41			solver->SetMinSize(layout, BSize(ceilf(layout->Right()->Value()),
42				ceilf(layout->Bottom()->Value())));
43		}
44	}
45};
46
47
48struct SharedSolver::MaxSizeValidator {
49	inline void CallSolverMethod(LinearSpec* spec, VariableList* vars)
50	{
51		spec->FindMaxs(vars);
52	}
53
54	inline void Finalize(BALMLayout* layout, SharedSolver* solver,
55		ResultType solveResult)
56	{
57		if (solveResult == LinearProgramming::kUnbounded) {
58			solver->SetMaxSize(layout,
59				BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED));
60		} else {
61			solver->SetMaxSize(layout, BSize(floorf(layout->Right()->Value()),
62				floorf(layout->Bottom()->Value())));
63		}
64	}
65};
66
67
68struct SharedSolver::PreferredSizeValidator {
69	inline void CallSolverMethod(LinearSpec* spec, VariableList* vars)
70	{
71		spec->Solve();
72	}
73
74	inline void Finalize(BALMLayout* layout, SharedSolver* solver,
75		ResultType solveResult)
76	{
77		float width = layout->Right()->Value() - layout->Left()->Value();
78		float height = layout->Bottom()->Value() - layout->Top()->Value();
79		solver->SetPreferredSize(layout, BSize(width, height));
80	}
81};
82
83
84SharedSolver::SharedSolver()
85	:
86	fConstraintsValid(false),
87	fMinValid(false),
88	fMaxValid(false),
89	fPreferredValid(false),
90	fLayoutValid(false),
91	fLayoutContext(NULL)
92{
93}
94
95
96SharedSolver::SharedSolver(BMessage* archive)
97	:
98	BArchivable(archive),
99	fConstraintsValid(false),
100	fMinValid(false),
101	fMaxValid(false),
102	fPreferredValid(false),
103	fLayoutValid(false),
104	fLayoutContext(NULL)
105{
106}
107
108
109SharedSolver::~SharedSolver()
110{
111	if (fLayouts.CountItems() > 0)
112		debugger("SharedSolver deleted while still in use!");
113
114	_SetContext(NULL);
115}
116
117
118LinearSpec*
119SharedSolver::Solver() const
120{
121	return const_cast<LinearSpec*>(&fLinearSpec);
122}
123
124
125void
126SharedSolver::Invalidate(bool children)
127{
128	if (!fConstraintsValid && !fMaxValid && !fMinValid && !fLayoutValid)
129		return;
130
131	fConstraintsValid = false;
132	fMinValid = false;
133	fMaxValid = false;
134	fPreferredValid = false;
135	fLayoutValid = false;
136
137	_SetContext(NULL);
138
139	for (int32 i = fLayouts.CountItems() - 1; i >= 0; i--)
140		fLayouts.ItemAt(i)->InvalidateLayout(children);
141}
142
143
144void
145SharedSolver::RegisterLayout(BALMLayout* layout)
146{
147	fLayouts.AddItem(layout);
148	Invalidate(false);
149}
150
151
152void
153SharedSolver::LayoutLeaving(const BALMLayout* layout)
154{
155	fLayouts.RemoveItem(const_cast<BALMLayout*>(layout));
156	Invalidate(false);
157}
158
159
160ResultType
161SharedSolver::ValidateMinSize()
162{
163	_Validate<MinSizeValidator>(fMinValid, fMinResult);
164	return fMinResult;
165}
166
167
168ResultType
169SharedSolver::ValidateMaxSize()
170{
171	_Validate<MaxSizeValidator>(fMaxValid, fMaxResult);
172	return fMaxResult;
173}
174
175
176ResultType
177SharedSolver::ValidatePreferredSize()
178{
179	_Validate<PreferredSizeValidator>(fPreferredValid, fPreferredResult);
180	return fPreferredResult;
181}
182
183
184ResultType
185SharedSolver::ValidateLayout(BLayoutContext* context)
186{
187	if (fLayoutValid && fLayoutContext == context)
188		return fLayoutResult;
189
190	_SetContext(context);
191	_ValidateConstraints();
192
193	for (int32 i = fLayouts.CountItems() - 1; i >= 0; i--) {
194		BALMLayout* layout = fLayouts.ItemAt(i);
195		BSize size(layout->LayoutArea().Size());
196		layout->Right()->SetRange(size.width, size.width);
197		layout->Bottom()->SetRange(size.height, size.height);
198	}
199
200	fLayoutResult = fLinearSpec.Solve();
201	fLayoutValid = true;
202	return fLayoutResult;
203}
204
205
206status_t
207SharedSolver::Archive(BMessage* archive, bool deep) const
208{
209	return BArchivable::Archive(archive, deep);
210}
211
212
213status_t
214SharedSolver::AllArchived(BMessage* archive) const
215{
216	/*
217	for each archived layout:
218		add it to our archive
219		add constraints created by areas and column manager to a set
220		add range constraints on the left/top/right/bottom tabs to the same set
221	for each constraint in the linear spec:
222		if it is not in the set above:
223			archive it
224	*/
225	BArchiver archiver(archive);
226	std::set<const Constraint*> autoConstraints;
227	for (int32 i = fLayouts.CountItems() - 1; i >= 0; i--) {
228		BALMLayout* layout = fLayouts.ItemAt(i);
229		if (!archiver.IsArchived(layout))
230			continue;
231
232		for (int32 j = layout->CountItems() - 1; j >= 0; j--)
233			_AddConstraintsToSet(layout->AreaAt(j), autoConstraints);
234
235		for (int32 j = layout->fRowColumnManager->fRows.CountItems() - 1;
236				j >= 0; j--) {
237			Row* row = layout->fRowColumnManager->fRows.ItemAt(j);
238			autoConstraints.insert(row->fPrefSizeConstraint);
239		}
240
241		for (int32 j = layout->fRowColumnManager->fColumns.CountItems() - 1;
242				j >= 0; j--) {
243			Column* column = layout->fRowColumnManager->fColumns.ItemAt(j);
244			autoConstraints.insert(column->fPrefSizeConstraint);
245		}
246
247		Variable* corners[] = {layout->fLeft, layout->fTop, layout->fRight,
248			layout->fBottom};
249
250		for (int32 j = 0; j < 4; j++) {
251			const Constraint* min;
252			const Constraint* max;
253			fLinearSpec.GetRangeConstraints(corners[j], &min, &max);
254			if (min)
255				autoConstraints.insert(min);
256			if (max)
257				autoConstraints.insert(max);
258		}
259	}
260
261	status_t err = B_OK;
262	const ConstraintList& constraints = fLinearSpec.Constraints();
263	for (int32 i = constraints.CountItems() - 1; i >= 0 && err == B_OK; i--) {
264		Constraint* constraint = constraints.ItemAt(i);
265		if (autoConstraints.find(constraint) == autoConstraints.end())
266			err = _AddConstraintToArchive(constraint, archive);
267	}
268	return err;
269}
270
271
272status_t
273SharedSolver::AllUnarchived(const BMessage* archive)
274{
275	int32 count = 0;
276	archive->GetInfo(kConstraintsField, NULL, &count);
277
278	status_t err = B_OK;
279	BUnarchiver unarchiver(archive);
280	for (int32 i = 0; i < count && err == B_OK; i++) {
281		const void* constraintData;
282		ssize_t numBytes;
283		err = archive->FindData(kConstraintsField, kConstraintsTypeCode,
284			i, &constraintData, &numBytes);
285		if (err != B_OK)
286			return err;
287
288		err = _InstantiateConstraint(constraintData, numBytes, unarchiver);
289	}
290	return err;
291}
292
293
294void
295SharedSolver::_AddConstraintsToSet(Area* area,
296	std::set<const Constraint*>& constraints)
297{
298	if (area->fMinContentWidth)
299		constraints.insert(area->fMinContentWidth);
300	if (area->fMaxContentWidth)
301		constraints.insert(area->fMaxContentWidth);
302	if (area->fMinContentHeight)
303		constraints.insert(area->fMinContentHeight);
304	if (area->fMaxContentHeight)
305		constraints.insert(area->fMaxContentHeight);
306	if (area->fContentAspectRatioC)
307		constraints.insert(area->fContentAspectRatioC);
308}
309
310
311status_t
312SharedSolver::_AddConstraintToArchive(Constraint* constraint,
313	BMessage* archive)
314{
315	/* Format:
316	 * int32: summandCount
317	 *  { int32 : token
318	 *    double: coefficient
319	 *  } [summandCount]
320	 *  int32: operator
321	 *  double: rightSide
322	 *  double: penaltyNeg
323	 *  double: penaltyPos
324	 */
325
326	// TODO: check Read/Write calls
327	BArchiver archiver(archive);
328	BMallocIO buffer;
329	SummandList* leftSide = constraint->LeftSide();
330	int32 summandCount = leftSide->CountItems();
331	buffer.Write(&summandCount, sizeof(summandCount));
332
333	for (int32 i = 0; i < summandCount; i++) {
334		Summand* summand = leftSide->ItemAt(i);
335		Variable* var = summand->Var();
336		BArchivable* varArchivable = dynamic_cast<BArchivable*>(var);
337		if (!varArchivable || !archiver.IsArchived(varArchivable))
338			return B_NAME_NOT_FOUND;
339
340		int32 token;
341		archiver.GetTokenForArchivable(varArchivable, token);
342		buffer.Write(&token, sizeof(token));
343
344		double coefficient = summand->Coeff();
345		buffer.Write(&coefficient, sizeof(coefficient));
346	}
347
348	int32 op = constraint->Op();
349	buffer.Write(&op, sizeof(op));
350
351	double rightSide = constraint->RightSide();
352	buffer.Write(&rightSide, sizeof(rightSide));
353	double penaltyNeg = constraint->PenaltyNeg();
354	buffer.Write(&penaltyNeg, sizeof(penaltyNeg));
355	double penaltyPos = constraint->PenaltyPos();
356	buffer.Write(&penaltyPos, sizeof(penaltyPos));
357
358	return archive->AddData(kConstraintsField, kConstraintsTypeCode,
359		buffer.Buffer(), buffer.BufferLength(), false);
360}
361
362
363status_t
364SharedSolver::_InstantiateConstraint(const void* rawData, ssize_t numBytes,
365	BUnarchiver& unarchiver)
366{
367	/* Format:
368	 * int32: summandCount
369	 *  { int32 : token
370	 *    double: coefficient
371	 *  } [summandCount]
372	 *  int32: operator
373	 *  double: rightSide
374	 *  double: penaltyNeg
375	 *  double: penaltyPos
376	 */
377
378	// TODO: check Read/Write calls
379	BMemoryIO buffer(rawData, numBytes);
380	int32 summandCount;
381	buffer.Read((void*)&summandCount, sizeof(summandCount));
382
383	SummandList* summandList = new SummandList(20, true);
384	ObjectDeleter<SummandList> deleter(summandList);
385	status_t err = B_OK;
386	for (int32 i = 0; i < summandCount; i++) {
387		int32 token;
388		buffer.Read((void*)&token, sizeof(token));
389
390		BArchivable* varArchivable;
391		err = unarchiver.GetObject(token, varArchivable);
392		if (err != B_OK)
393			break;
394		Variable* var = dynamic_cast<Variable*>(varArchivable);
395		if (!var)
396			return B_BAD_TYPE;
397
398		fLinearSpec.AddVariable(var);
399
400		double coefficient;
401		buffer.Read(&coefficient, sizeof(coefficient));
402
403		summandList->AddItem(new Summand(coefficient, var));
404	}
405
406	int32 op;
407	buffer.Read(&op, sizeof(op));
408
409	double rightSide;
410	buffer.Read(&rightSide, sizeof(rightSide));
411	double penaltyNeg;
412	buffer.Read(&penaltyNeg, sizeof(penaltyNeg));
413	double penaltyPos;
414	buffer.Read(&penaltyPos, sizeof(penaltyPos));
415
416	if (err != B_OK)
417		return err;
418
419	if (fLinearSpec.AddConstraint(summandList, (OperatorType)op, rightSide,
420			penaltyNeg, penaltyPos) != NULL) {
421		deleter.Detach();
422		return B_OK;
423	}
424
425	return B_NO_MEMORY;
426}
427
428
429void
430SharedSolver::SetMaxSize(BALMLayout* layout, const BSize& max)
431{
432	layout->fMaxSize = max;
433}
434
435
436void
437SharedSolver::SetMinSize(BALMLayout* layout, const BSize& min)
438{
439	layout->fMinSize = min;
440}
441
442
443void
444SharedSolver::SetPreferredSize(BALMLayout* layout, const BSize& preferred)
445{
446	layout->fPreferredSize = preferred;
447}
448
449
450void
451SharedSolver::LayoutContextLeft(BLayoutContext* context)
452{
453	fLayoutContext = NULL;
454}
455
456
457void
458SharedSolver::_SetContext(BLayoutContext* context)
459{
460	if (fLayoutContext)
461		fLayoutContext->RemoveListener(this);
462	fLayoutContext = context;
463	if (fLayoutContext)
464		fLayoutContext->AddListener(this);
465}
466
467
468void
469SharedSolver::_ValidateConstraints()
470{
471	if (!fConstraintsValid) {
472		for (int32 i = fLayouts.CountItems() - 1; i >= 0; i--) {
473			fLayouts.ItemAt(i)->UpdateConstraints(fLayoutContext);
474		}
475		fConstraintsValid = true;
476	}
477}
478
479
480template <class Validator>
481void
482SharedSolver::_Validate(bool& isValid, ResultType& result)
483{
484	if (isValid)
485		return;
486
487	_ValidateConstraints();
488
489	VariableList variables(fLayouts.CountItems() * 2);
490	Validator validator;
491
492	for (int32 i = fLayouts.CountItems() - 1; i >= 0; i--) {
493		BALMLayout* layout = fLayouts.ItemAt(i);
494		layout->Right()->SetRange(0, 20000);
495		layout->Bottom()->SetRange(0, 20000);
496
497		variables.AddItem(layout->Right());
498		variables.AddItem(layout->Bottom());
499	}
500
501	validator.CallSolverMethod(&fLinearSpec, &variables);
502	result = fLinearSpec.Result();
503
504	for (int32 i = fLayouts.CountItems() - 1; i >= 0; i--)
505		validator.Finalize(fLayouts.ItemAt(i), this, result);
506
507	isValid = true;
508	fLayoutValid = false;
509}
510
511
512BArchivable*
513SharedSolver::Instantiate(BMessage* archive)
514{
515	if (validate_instantiation(archive, "BPrivate::SharedSolver"))
516		return new SharedSolver(archive);
517	return NULL;
518}
519