xref: /haiku/src/libs/alm/SharedSolver.cpp (revision 25a7b01d15612846f332751841da3579db313082)
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 {
CallSolverMethodSharedSolver::MinSizeValidator30 	inline void CallSolverMethod(LinearSpec* spec, VariableList* vars)
31 	{
32 		spec->FindMins(vars);
33 	}
34 
FinalizeSharedSolver::MinSizeValidator35 	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 
48 struct SharedSolver::MaxSizeValidator {
CallSolverMethodSharedSolver::MaxSizeValidator49 	inline void CallSolverMethod(LinearSpec* spec, VariableList* vars)
50 	{
51 		spec->FindMaxs(vars);
52 	}
53 
FinalizeSharedSolver::MaxSizeValidator54 	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 
68 struct SharedSolver::PreferredSizeValidator {
CallSolverMethodSharedSolver::PreferredSizeValidator69 	inline void CallSolverMethod(LinearSpec* spec, VariableList* vars)
70 	{
71 		spec->Solve();
72 	}
73 
FinalizeSharedSolver::PreferredSizeValidator74 	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 
SharedSolver()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 
SharedSolver(BMessage * archive)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 
~SharedSolver()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*
Solver() const119 SharedSolver::Solver() const
120 {
121 	return const_cast<LinearSpec*>(&fLinearSpec);
122 }
123 
124 
125 void
Invalidate(bool children)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
RegisterLayout(BALMLayout * layout)145 SharedSolver::RegisterLayout(BALMLayout* layout)
146 {
147 	fLayouts.AddItem(layout);
148 	Invalidate(false);
149 }
150 
151 
152 void
LayoutLeaving(const BALMLayout * layout)153 SharedSolver::LayoutLeaving(const BALMLayout* layout)
154 {
155 	fLayouts.RemoveItem(const_cast<BALMLayout*>(layout));
156 	Invalidate(false);
157 }
158 
159 
160 ResultType
ValidateMinSize()161 SharedSolver::ValidateMinSize()
162 {
163 	_Validate<MinSizeValidator>(fMinValid, fMinResult);
164 	return fMinResult;
165 }
166 
167 
168 ResultType
ValidateMaxSize()169 SharedSolver::ValidateMaxSize()
170 {
171 	_Validate<MaxSizeValidator>(fMaxValid, fMaxResult);
172 	return fMaxResult;
173 }
174 
175 
176 ResultType
ValidatePreferredSize()177 SharedSolver::ValidatePreferredSize()
178 {
179 	_Validate<PreferredSizeValidator>(fPreferredValid, fPreferredResult);
180 	return fPreferredResult;
181 }
182 
183 
184 ResultType
ValidateLayout(BLayoutContext * context)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
Archive(BMessage * archive,bool deep) const207 SharedSolver::Archive(BMessage* archive, bool deep) const
208 {
209 	return BArchivable::Archive(archive, deep);
210 }
211 
212 
213 status_t
AllArchived(BMessage * archive) const214 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 		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 
272 status_t
AllUnarchived(const BMessage * archive)273 SharedSolver::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 
294 void
_AddConstraintsToSet(Area * area,std::set<const Constraint * > & constraints)295 SharedSolver::_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 
311 status_t
_AddConstraintToArchive(Constraint * constraint,BMessage * archive)312 SharedSolver::_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 
363 status_t
_InstantiateConstraint(const void * rawData,ssize_t numBytes,BUnarchiver & unarchiver)364 SharedSolver::_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 
429 void
SetMaxSize(BALMLayout * layout,const BSize & max)430 SharedSolver::SetMaxSize(BALMLayout* layout, const BSize& max)
431 {
432 	layout->fMaxSize = max;
433 }
434 
435 
436 void
SetMinSize(BALMLayout * layout,const BSize & min)437 SharedSolver::SetMinSize(BALMLayout* layout, const BSize& min)
438 {
439 	layout->fMinSize = min;
440 }
441 
442 
443 void
SetPreferredSize(BALMLayout * layout,const BSize & preferred)444 SharedSolver::SetPreferredSize(BALMLayout* layout, const BSize& preferred)
445 {
446 	layout->fPreferredSize = preferred;
447 }
448 
449 
450 void
LayoutContextLeft(BLayoutContext * context)451 SharedSolver::LayoutContextLeft(BLayoutContext* context)
452 {
453 	fLayoutContext = NULL;
454 }
455 
456 
457 void
_SetContext(BLayoutContext * context)458 SharedSolver::_SetContext(BLayoutContext* context)
459 {
460 	if (fLayoutContext)
461 		fLayoutContext->RemoveListener(this);
462 	fLayoutContext = context;
463 	if (fLayoutContext)
464 		fLayoutContext->AddListener(this);
465 }
466 
467 
468 void
_ValidateConstraints()469 SharedSolver::_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 
480 template <class Validator>
481 void
_Validate(bool & isValid,ResultType & result)482 SharedSolver::_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 
512 BArchivable*
Instantiate(BMessage * archive)513 SharedSolver::Instantiate(BMessage* archive)
514 {
515 	if (validate_instantiation(archive, "BPrivate::SharedSolver"))
516 		return new SharedSolver(archive);
517 	return NULL;
518 }
519