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