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(ceilf(layout->Right()->Value()), 42 ceilf(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(floorf(layout->Right()->Value()), 62 floorf(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->Bottom()->Value() - layout->Top()->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 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 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 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 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 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 430 SharedSolver::SetMaxSize(BALMLayout* layout, const BSize& max) 431 { 432 layout->fMaxSize = max; 433 } 434 435 436 void 437 SharedSolver::SetMinSize(BALMLayout* layout, const BSize& min) 438 { 439 layout->fMinSize = min; 440 } 441 442 443 void 444 SharedSolver::SetPreferredSize(BALMLayout* layout, const BSize& preferred) 445 { 446 layout->fPreferredSize = preferred; 447 } 448 449 450 void 451 SharedSolver::LayoutContextLeft(BLayoutContext* context) 452 { 453 fLayoutContext = NULL; 454 } 455 456 457 void 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 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 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* 513 SharedSolver::Instantiate(BMessage* archive) 514 { 515 if (validate_instantiation(archive, "BPrivate::SharedSolver")) 516 return new SharedSolver(archive); 517 return NULL; 518 } 519