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