xref: /haiku/src/libs/alm/SharedSolver.cpp (revision 0a5e130725e2e8997bc0d9db9717f0a7874da8f3)
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 
18 using LinearProgramming::LinearSpec;
19 
20 
21 namespace {
22 
23 type_code kConstraintsTypeCode = 'cnst';
24 const char* kConstraintsField = "SharedSolver:constraints";
25 
26 };
27 
28 
29 struct 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(layout->Right()->Value(),
42 				layout->Bottom()->Value()));
43 		}
44 	}
45 };
46 
47 
48 struct 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(layout->Right()->Value(),
62 				layout->Bottom()->Value()));
63 		}
64 	}
65 };
66 
67 
68 struct 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->Top()->Value() - layout->Bottom()->Value();
79 		solver->SetPreferredSize(layout, BSize(width, height));
80 	}
81 };
82 
83 
84 SharedSolver::SharedSolver()
85 	:
86 	fConstraintsValid(false),
87 	fMinValid(false),
88 	fMaxValid(false),
89 	fPreferredValid(false),
90 	fLayoutValid(false),
91 	fLayoutContext(NULL)
92 {
93 }
94 
95 
96 SharedSolver::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 
109 SharedSolver::~SharedSolver()
110 {
111 	if (fLayouts.CountItems() > 0)
112 		debugger("SharedSolver deleted while still in use!");
113 
114 	_SetContext(NULL);
115 }
116 
117 
118 LinearSpec*
119 SharedSolver::Solver() const
120 {
121 	return const_cast<LinearSpec*>(&fLinearSpec);
122 }
123 
124 
125 void
126 SharedSolver::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 
144 void
145 SharedSolver::RegisterLayout(BALMLayout* layout)
146 {
147 	fLayouts.AddItem(layout);
148 	Invalidate(false);
149 }
150 
151 
152 void
153 SharedSolver::LayoutLeaving(const BALMLayout* layout)
154 {
155 	fLayouts.RemoveItem(const_cast<BALMLayout*>(layout));
156 	Invalidate(false);
157 }
158 
159 
160 ResultType
161 SharedSolver::ValidateMinSize()
162 {
163 	_Validate<MinSizeValidator>(fMinValid, fMinResult);
164 	return fMinResult;
165 }
166 
167 
168 ResultType
169 SharedSolver::ValidateMaxSize()
170 {
171 	_Validate<MaxSizeValidator>(fMaxValid, fMaxResult);
172 	return fMaxResult;
173 }
174 
175 
176 ResultType
177 SharedSolver::ValidatePreferredSize()
178 {
179 	_Validate<PreferredSizeValidator>(fPreferredValid, fPreferredResult);
180 	return fPreferredResult;
181 }
182 
183 
184 ResultType
185 SharedSolver::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 
206 status_t
207 SharedSolver::Archive(BMessage* archive, bool deep) const
208 {
209 	return BArchivable::Archive(archive, deep);
210 }
211 
212 
213 status_t
214 SharedSolver::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 	for each constraint in the linear spec:
221 		if it is not in the set above:
222 			archive it
223 	*/
224 	BArchiver archiver(archive);
225 	std::set<Constraint*> autoConstraints;
226 	for (int32 i = fLayouts.CountItems() - 1; i >= 0; i--) {
227 		BALMLayout* layout = fLayouts.ItemAt(i);
228 		if (!archiver.IsArchived(layout))
229 			continue;
230 
231 		for (int32 j = layout->CountItems() - 1; j >= 0; j--)
232 			_AddConstraintsToSet(layout->AreaAt(j), autoConstraints);
233 
234 		for (int32 j = layout->fRowColumnManager->fRows.CountItems() - 1;
235 				j >= 0; j--) {
236 			Row* row = layout->fRowColumnManager->fRows.ItemAt(i);
237 			autoConstraints.insert(row->fPrefSizeConstraint);
238 		}
239 
240 		for (int32 j = layout->fRowColumnManager->fColumns.CountItems() - 1;
241 				j >= 0; j--) {
242 			Column* column = layout->fRowColumnManager->fColumns.ItemAt(i);
243 			autoConstraints.insert(column->fPrefSizeConstraint);
244 		}
245 	}
246 
247 	status_t err = B_OK;
248 	const ConstraintList& constraints = fLinearSpec.Constraints();
249 	for (int32 i = constraints.CountItems() - 1; i >= 0 && err == B_OK; i--) {
250 		Constraint* constraint = constraints.ItemAt(i);
251 		if (autoConstraints.find(constraint) != autoConstraints.end())
252 			err = _AddConstraintToArchive(constraint, archive);
253 	}
254 	return err;
255 }
256 
257 
258 status_t
259 SharedSolver::AllUnarchived(const BMessage* archive)
260 {
261 	int32 count = 0;
262 	archive->GetInfo(kConstraintsField, NULL, &count);
263 
264 	status_t err = B_OK;
265 	BUnarchiver unarchiver(archive);
266 	for (int32 i = 0; i < count && err == B_OK; i++) {
267 		const void* constraintData;
268 		ssize_t numBytes;
269 		err = archive->FindData(kConstraintsField, kConstraintsTypeCode,
270 			i, &constraintData, &numBytes);
271 		if (err != B_OK)
272 			return err;
273 
274 		err = _InstantiateConstraint(constraintData, numBytes, unarchiver);
275 	}
276 	return err;
277 }
278 
279 
280 void
281 SharedSolver::_AddConstraintsToSet(Area* area,
282 	std::set<Constraint*>& constraints)
283 {
284 	if (area->fMinContentWidth)
285 		constraints.insert(area->fMinContentWidth);
286 	if (area->fMaxContentWidth)
287 		constraints.insert(area->fMaxContentWidth);
288 	if (area->fMinContentHeight)
289 		constraints.insert(area->fMinContentHeight);
290 	if (area->fMaxContentHeight)
291 		constraints.insert(area->fMaxContentHeight);
292 	if (area->fContentAspectRatioC)
293 		constraints.insert(area->fContentAspectRatioC);
294 }
295 
296 
297 status_t
298 SharedSolver::_AddConstraintToArchive(Constraint* constraint, BMessage* archive)
299 {
300 	// TODO: check Read/Write calls
301 	BArchiver archiver(archive);
302 	BMallocIO buffer;
303 	SummandList* leftSide = constraint->LeftSide();
304 	int32 summandCount = leftSide->CountItems();
305 	buffer.Write(&summandCount, sizeof(summandCount));
306 
307 	for (int32 i = 0; i < summandCount; i++) {
308 		Summand* summand = leftSide->ItemAt(i);
309 		Variable* var = summand->Var();
310 		BArchivable* varArchivable = dynamic_cast<BArchivable*>(var);
311 		if (!varArchivable || !archiver.IsArchived(varArchivable))
312 			return B_NAME_NOT_FOUND;
313 
314 		int32 token;
315 		archiver.GetTokenForArchivable(varArchivable, token);
316 		buffer.Write(&token, sizeof(token));
317 
318 		double coefficient = summand->Coeff();
319 		buffer.Write(&coefficient, sizeof(coefficient));
320 	}
321 
322 	int32 op = constraint->Op();
323 	buffer.Write(&op, sizeof(op));
324 
325 	double rightSide = constraint->RightSide();
326 	buffer.Write(&rightSide, sizeof(rightSide));
327 	double penaltyNeg = constraint->PenaltyNeg();
328 	buffer.Write(&penaltyNeg, sizeof(penaltyNeg));
329 	double penaltyPos = constraint->PenaltyPos();
330 	buffer.Write(&penaltyPos, sizeof(penaltyPos));
331 
332 	return archive->AddData(kConstraintsField, kConstraintsTypeCode,
333 		buffer.Buffer(), buffer.BufferLength(), false);
334 }
335 
336 
337 status_t
338 SharedSolver::_InstantiateConstraint(const void* rawData, ssize_t numBytes,
339 	BUnarchiver& unarchiver)
340 {
341 	// TODO: check Read/Write calls
342 	BMemoryIO buffer(rawData, numBytes);
343 	int32 summandCount;
344 	buffer.Read((void*)&summandCount, sizeof(summandCount));
345 
346 	SummandList* summandList = new SummandList(20, true);
347 	ObjectDeleter<SummandList> deleter(summandList);
348 	status_t err = B_OK;
349 	for (int32 i = 0; i < summandCount; i++) {
350 		int32 token;
351 		buffer.Read((void*)&token, sizeof(token));
352 
353 		BArchivable* varArchivable;
354 		err = unarchiver.GetObject(token, varArchivable);
355 		if (err != B_OK)
356 			break;
357 		Variable* var = dynamic_cast<Variable*>(varArchivable);
358 		if (!var)
359 			return B_BAD_TYPE;
360 
361 		fLinearSpec.AddVariable(var);
362 
363 		double coefficient;
364 		buffer.Read(&coefficient, sizeof(coefficient));
365 
366 		summandList->AddItem(new Summand(coefficient, var));
367 	}
368 
369 	int32 op;
370 	buffer.Read(&op, sizeof(op));
371 
372 	double rightSide;
373 	buffer.Read(&rightSide, sizeof(rightSide));
374 	double penaltyNeg;
375 	buffer.Read(&penaltyNeg, sizeof(penaltyNeg));
376 	double penaltyPos;
377 	buffer.Read(&penaltyPos, sizeof(penaltyPos));
378 
379 	if (err == B_OK) {
380 		fLinearSpec.AddConstraint(summandList, (OperatorType)op, rightSide,
381 			penaltyNeg, penaltyPos);
382 		deleter.Detach();
383 	}
384 
385 	return err;
386 }
387 
388 
389 void
390 SharedSolver::SetMaxSize(BALMLayout* layout, const BSize& max)
391 {
392 	layout->fMaxSize = max;
393 }
394 
395 
396 void
397 SharedSolver::SetMinSize(BALMLayout* layout, const BSize& min)
398 {
399 	layout->fMinSize = min;
400 }
401 
402 
403 void
404 SharedSolver::SetPreferredSize(BALMLayout* layout, const BSize& preferred)
405 {
406 	layout->fPreferredSize = preferred;
407 }
408 
409 
410 void
411 SharedSolver::LayoutContextLeft(BLayoutContext* context)
412 {
413 	fLayoutContext = NULL;
414 }
415 
416 
417 void
418 SharedSolver::_SetContext(BLayoutContext* context)
419 {
420 	if (fLayoutContext)
421 		fLayoutContext->RemoveListener(this);
422 	fLayoutContext = context;
423 	if (fLayoutContext)
424 		fLayoutContext->AddListener(this);
425 }
426 
427 
428 void
429 SharedSolver::_ValidateConstraints()
430 {
431 	if (!fConstraintsValid) {
432 		for (int32 i = fLayouts.CountItems() - 1; i >= 0; i--) {
433 			fLayouts.ItemAt(i)->UpdateConstraints(fLayoutContext);
434 		}
435 		fConstraintsValid = true;
436 	}
437 }
438 
439 
440 template <class Validator>
441 void
442 SharedSolver::_Validate(bool& isValid, ResultType& result)
443 {
444 	if (isValid)
445 		return;
446 
447 	_ValidateConstraints();
448 
449 	VariableList variables(fLayouts.CountItems() * 2);
450 	Validator validator;
451 
452 	for (int32 i = fLayouts.CountItems() - 1; i >= 0; i--) {
453 		BALMLayout* layout = fLayouts.ItemAt(i);
454 		layout->Right()->SetRange(0, 20000);
455 		layout->Bottom()->SetRange(0, 20000);
456 
457 		variables.AddItem(layout->Right());
458 		variables.AddItem(layout->Bottom());
459 	}
460 
461 	validator.CallSolverMethod(&fLinearSpec, &variables);
462 	result = fLinearSpec.Result();
463 
464 	for (int32 i = fLayouts.CountItems() - 1; i >= 0; i--)
465 		validator.Finalize(fLayouts.ItemAt(i), this, result);
466 
467 	isValid = true;
468 	fLayoutValid = false;
469 }
470 
471 
472 BArchivable*
473 SharedSolver::Instantiate(BMessage* archive)
474 {
475 	if (validate_instantiation(archive, "BPrivate::SharedSolver"))
476 		return new SharedSolver(archive);
477 	return NULL;
478 }
479