1 /* 2 * Copyright 2007-2008, Christof Lutteroth, lutteroth@cs.auckland.ac.nz 3 * Copyright 2007-2008, James Kim, jkim202@ec.auckland.ac.nz 4 * Copyright 2010, Clemens Zeidler <haiku@clemens-zeidler.de> 5 * Distributed under the terms of the MIT License. 6 */ 7 8 9 #include "ALMLayout.h" 10 11 12 #include <stdio.h> 13 #include <vector> 14 15 #include <AutoDeleter.h> 16 #include <ControlLook.h> 17 18 #include "RowColumnManager.h" 19 #include "SharedSolver.h" 20 #include "ViewLayoutItem.h" 21 22 23 using BPrivate::AutoDeleter; 24 using namespace LinearProgramming; 25 26 27 const BSize kUnsetSize(B_SIZE_UNSET, B_SIZE_UNSET); 28 29 30 namespace { 31 32 const char* kSolverField = "BALMLayout:solver"; 33 const char* kBadLayoutPolicyField = "BALMLayout:policy"; 34 const char* kXTabsField = "BALMLayout:xtabs"; 35 const char* kYTabsField = "BALMLayout:ytabs"; 36 const char* kMyTabsField = "BALMLayout:tabs"; 37 const char* kInsetsField = "BALMLayout:insets"; 38 const char* kSpacingField = "BALMLayout:spacing"; 39 40 const char* kTabsField = "BALMLayout:item:tabs"; 41 const char* kItemAspectRatio = "BALMLayout:item:aspect"; 42 const char* kItemPenalties = "BALMLayout:item:penalties"; 43 const char* kItemInsets = "BALMLayout:item:insets"; 44 45 int CompareXTabFunc(const XTab* tab1, const XTab* tab2); 46 int CompareYTabFunc(const YTab* tab1, const YTab* tab2); 47 48 }; 49 50 51 namespace BALM { 52 53 54 template <class T> 55 struct BALMLayout::TabAddTransaction { 56 ~TabAddTransaction() 57 { 58 if (fTab) 59 fLayout->_RemoveSelfFromTab(fTab); 60 if (fIndex > 0) 61 _TabList()->RemoveItemAt(fIndex); 62 } 63 64 TabAddTransaction(BALMLayout* layout) 65 : 66 fTab(NULL), 67 fLayout(layout), 68 fIndex(-1) 69 { 70 } 71 72 bool AttempAdd(T* tab) 73 { 74 if (fLayout->_HasTabInLayout(tab)) 75 return true; 76 if (!fLayout->_AddedTab(tab)) 77 return false; 78 fTab = tab; 79 80 BObjectList<T>* tabList = _TabList(); 81 int32 index = tabList->CountItems(); 82 if (!tabList->AddItem(tab, index)) 83 return false; 84 fIndex = index; 85 return true; 86 } 87 88 void Commit() 89 { 90 fTab = NULL; 91 fIndex = -1; 92 } 93 94 private: 95 BObjectList<T>* _TabList(); 96 97 T* fTab; 98 BALMLayout* fLayout; 99 int32 fIndex; 100 }; 101 102 103 template <> 104 BObjectList<XTab>* 105 BALMLayout::TabAddTransaction<XTab>::_TabList() 106 { 107 return &fLayout->fXTabList; 108 } 109 110 111 template <> 112 BObjectList<YTab>* 113 BALMLayout::TabAddTransaction<YTab>::_TabList() 114 { 115 return &fLayout->fYTabList; 116 } 117 118 119 }; // end namespace BALM 120 121 122 BALM::BALMLayout::BadLayoutPolicy::BadLayoutPolicy() 123 { 124 } 125 126 127 BALM::BALMLayout::BadLayoutPolicy::BadLayoutPolicy(BMessage* archive) 128 : 129 BArchivable(archive) 130 { 131 } 132 133 134 BALM::BALMLayout::BadLayoutPolicy::~BadLayoutPolicy() 135 { 136 } 137 138 139 BALM::BALMLayout::DefaultPolicy::DefaultPolicy() 140 { 141 } 142 143 144 BALM::BALMLayout::DefaultPolicy::DefaultPolicy(BMessage* archive) 145 : 146 BadLayoutPolicy(archive) 147 { 148 } 149 150 151 BALM::BALMLayout::DefaultPolicy::~DefaultPolicy() 152 { 153 } 154 155 156 bool 157 BALM::BALMLayout::DefaultPolicy::OnBadLayout(BALMLayout* layout, 158 ResultType result, BLayoutContext* context) 159 { 160 if (!context) 161 return true; 162 163 if (result == kInfeasible) { 164 printf("BALMLayout failed to solve your layout!\n"); 165 return false; 166 } else 167 return true; 168 } 169 170 171 status_t 172 BALM::BALMLayout::DefaultPolicy::Archive(BMessage* archive, bool deep) const 173 { 174 return BadLayoutPolicy::Archive(archive, deep); 175 } 176 177 178 BArchivable* 179 BALM::BALMLayout::DefaultPolicy::Instantiate(BMessage* archive) 180 { 181 if (validate_instantiation(archive, "BALM::BALMLayout::DefaultPolicy")) 182 return new DefaultPolicy(archive); 183 return NULL; 184 } 185 186 187 class BALMLayout::BALMLayoutSpecListener 188 : public LinearProgramming::SpecificationListener { 189 public: 190 BALMLayoutSpecListener(BALMLayout* layout) 191 : 192 fLayout(layout) 193 { 194 } 195 196 void ConstraintRemoved(Constraint* constraint) 197 { 198 fLayout->fConstraints.RemoveItem(constraint); 199 } 200 201 private: 202 BALMLayout* fLayout; 203 }; 204 205 206 /*! 207 * Constructor. 208 * Creates new layout engine. 209 * 210 * If friendLayout is not NULL the solver of the friend layout is used. 211 */ 212 BALMLayout::BALMLayout(float hSpacing, float vSpacing, BALMLayout* friendLayout) 213 : 214 fLeftInset(0), 215 fRightInset(0), 216 fTopInset(0), 217 fBottomInset(0), 218 fHSpacing(BControlLook::ComposeSpacing(hSpacing)), 219 fVSpacing(BControlLook::ComposeSpacing(vSpacing)), 220 fXTabsSorted(false), 221 fYTabsSorted(false), 222 fBadLayoutPolicy(new DefaultPolicy()) 223 { 224 _SetSolver(friendLayout ? friendLayout->fSolver : new SharedSolver()); 225 226 fSpecListener = new BALMLayoutSpecListener(this); 227 Solver()->AddListener(fSpecListener); 228 229 fLeft = AddXTab(); 230 fRight = AddXTab(); 231 fTop = AddYTab(); 232 fBottom = AddYTab(); 233 234 // the Left tab is always at x-position 0, and the Top tab is always at 235 // y-position 0 236 fLeft->SetRange(0, 0); 237 fTop->SetRange(0, 0); 238 239 // cached layout values 240 // need to be invalidated whenever the layout specification is changed 241 fMinSize = kUnsetSize; 242 fMaxSize = kUnsetSize; 243 fPreferredSize = kUnsetSize; 244 } 245 246 247 BALMLayout::BALMLayout(BMessage* archive) 248 : 249 BAbstractLayout(BUnarchiver::PrepareArchive(archive)), 250 fSolver(NULL), 251 fLeft(NULL), 252 fRight(NULL), 253 fTop(NULL), 254 fBottom(NULL), 255 fMinSize(kUnsetSize), 256 fMaxSize(kUnsetSize), 257 fPreferredSize(kUnsetSize), 258 fXTabsSorted(false), 259 fYTabsSorted(false), 260 fBadLayoutPolicy(new DefaultPolicy()) 261 { 262 BUnarchiver unarchiver(archive); 263 264 BRect insets; 265 status_t err = archive->FindRect(kInsetsField, &insets); 266 if (err != B_OK) { 267 unarchiver.Finish(err); 268 return; 269 } 270 271 fLeftInset = insets.left; 272 fRightInset = insets.right; 273 fTopInset = insets.top; 274 fBottomInset = insets.bottom; 275 276 277 BSize spacing; 278 err = archive->FindSize(kSpacingField, &spacing); 279 if (err != B_OK) { 280 unarchiver.Finish(err); 281 return; 282 } 283 284 fHSpacing = spacing.width; 285 fVSpacing = spacing.height; 286 287 int32 tabCount = 0; 288 archive->GetInfo(kXTabsField, NULL, &tabCount); 289 for (int32 i = 0; i < tabCount && err == B_OK; i++) 290 err = unarchiver.EnsureUnarchived(kXTabsField, i); 291 292 archive->GetInfo(kYTabsField, NULL, &tabCount); 293 for (int32 i = 0; i < tabCount && err == B_OK; i++) 294 err = unarchiver.EnsureUnarchived(kYTabsField, i); 295 296 if (err == B_OK && archive->GetInfo(kBadLayoutPolicyField, NULL) == B_OK) 297 err = unarchiver.EnsureUnarchived(kBadLayoutPolicyField); 298 299 if (err == B_OK) 300 err = unarchiver.EnsureUnarchived(kSolverField); 301 302 unarchiver.Finish(err); 303 304 fSpecListener = new BALMLayoutSpecListener(this); 305 Solver()->AddListener(fSpecListener); 306 } 307 308 309 BALMLayout::~BALMLayout() 310 { 311 Solver()->RemoveListener(fSpecListener); 312 delete fSpecListener; 313 314 delete fRowColumnManager; 315 delete fBadLayoutPolicy; 316 317 for (int32 i = 0; i < fConstraints.CountItems(); i++) 318 Solver()->RemoveConstraint(fConstraints.ItemAt(i), true); 319 320 if (fSolver) { 321 fSolver->LayoutLeaving(this); 322 fSolver->ReleaseReference(); 323 } 324 } 325 326 327 /** 328 * Adds a new x-tab to the specification. 329 * 330 * @return the new x-tab 331 */ 332 BReference<XTab> 333 BALMLayout::AddXTab() 334 { 335 BReference<XTab> tab(new(std::nothrow) XTab(this), true); 336 if (!tab) 337 return NULL; 338 if (!Solver()->AddVariable(tab)) 339 return NULL; 340 341 fXTabList.AddItem(tab); 342 if (!tab->AddedToLayout(this)) { 343 fXTabList.RemoveItem(tab); 344 return NULL; 345 } 346 fXTabsSorted = false; 347 return tab; 348 } 349 350 351 void 352 BALMLayout::AddXTabs(BReference<XTab>* tabs, uint32 count) 353 { 354 for (uint32 i = 0; i < count; i++) 355 tabs[i] = AddXTab(); 356 } 357 358 359 void 360 BALMLayout::AddYTabs(BReference<YTab>* tabs, uint32 count) 361 { 362 for (uint32 i = 0; i < count; i++) 363 tabs[i] = AddYTab(); 364 } 365 366 367 /** 368 * Adds a new y-tab to the specification. 369 * 370 * @return the new y-tab 371 */ 372 BReference<YTab> 373 BALMLayout::AddYTab() 374 { 375 BReference<YTab> tab(new(std::nothrow) YTab(this), true); 376 if (!tab.IsSet()) 377 return NULL; 378 if (!Solver()->AddVariable(tab)) 379 return NULL; 380 381 fYTabList.AddItem(tab); 382 if (!tab->AddedToLayout(this)) { 383 fYTabList.RemoveItem(tab); 384 return NULL; 385 } 386 fYTabsSorted = false; 387 return tab; 388 } 389 390 391 int32 392 BALMLayout::CountXTabs() const 393 { 394 return fXTabList.CountItems(); 395 } 396 397 398 int32 399 BALMLayout::CountYTabs() const 400 { 401 return fYTabList.CountItems(); 402 } 403 404 405 XTab* 406 BALMLayout::XTabAt(int32 index, bool ordered) 407 { 408 if (ordered && !fXTabsSorted) { 409 Layout(); 410 fXTabList.SortItems(CompareXTabFunc); 411 fXTabsSorted = true; 412 } 413 return fXTabList.ItemAt(index); 414 } 415 416 417 XTab* 418 BALMLayout::XTabAt(int32 index) const 419 { 420 return fXTabList.ItemAt(index); 421 } 422 423 424 YTab* 425 BALMLayout::YTabAt(int32 index, bool ordered) 426 { 427 if (ordered && !fYTabsSorted) { 428 Layout(); 429 fYTabList.SortItems(CompareYTabFunc); 430 fYTabsSorted = true; 431 } 432 return fYTabList.ItemAt(index); 433 } 434 435 436 YTab* 437 BALMLayout::YTabAt(int32 index) const 438 { 439 return fYTabList.ItemAt(index); 440 } 441 442 443 const XTabList 444 BALMLayout::GetXTabs() const 445 { 446 return fXTabList; 447 } 448 449 450 const YTabList 451 BALMLayout::GetYTabs() const 452 { 453 return fYTabList; 454 } 455 456 457 int32 458 BALMLayout::IndexOf(XTab* tab, bool ordered) 459 { 460 if (ordered && !fXTabsSorted) { 461 Layout(); 462 fXTabList.SortItems(CompareXTabFunc); 463 fXTabsSorted = true; 464 } 465 return fXTabList.IndexOf(tab); 466 } 467 468 469 int32 470 BALMLayout::IndexOf(YTab* tab, bool ordered) 471 { 472 if (ordered && !fYTabsSorted) { 473 Layout(); 474 fYTabList.SortItems(CompareYTabFunc); 475 fYTabsSorted = true; 476 } 477 return fYTabList.IndexOf(tab); 478 } 479 480 481 int32 482 BALMLayout::CountConstraints() const 483 { 484 return fConstraints.CountItems(); 485 } 486 487 488 Constraint* 489 BALMLayout::ConstraintAt(int32 index) const 490 { 491 return fConstraints.ItemAt(index); 492 } 493 494 495 bool 496 BALMLayout::AddConstraint(Constraint* constraint) 497 { 498 fConstraints.AddItem(constraint); 499 return Solver()->AddConstraint(constraint); 500 } 501 502 503 bool 504 BALMLayout::RemoveConstraint(Constraint* constraint, 505 bool deleteConstraint) 506 { 507 if (!fConstraints.RemoveItem(constraint)) 508 return false; 509 return Solver()->RemoveConstraint(constraint, deleteConstraint); 510 } 511 512 513 Constraint* 514 BALMLayout::AddConstraint(double coeff1, Variable* var1, OperatorType op, 515 double rightSide, double penaltyNeg, double penaltyPos) 516 { 517 Constraint* constraint = Solver()->AddConstraint(coeff1, var1, op, 518 rightSide, penaltyNeg, penaltyPos); 519 fConstraints.AddItem(constraint); 520 return constraint; 521 } 522 523 524 Constraint* 525 BALMLayout::AddConstraint(double coeff1, Variable* var1, double coeff2, 526 Variable* var2, OperatorType op, double rightSide, double penaltyNeg, 527 double penaltyPos) 528 { 529 Constraint* constraint = Solver()->AddConstraint(coeff1, var1, coeff2, var2, 530 op, rightSide, penaltyNeg, penaltyPos); 531 fConstraints.AddItem(constraint); 532 return constraint; 533 } 534 535 Constraint* 536 BALMLayout::AddConstraint(double coeff1, Variable* var1, double coeff2, 537 Variable* var2, double coeff3, Variable* var3, OperatorType op, 538 double rightSide, double penaltyNeg, double penaltyPos) 539 { 540 Constraint* constraint = Solver()->AddConstraint(coeff1, var1, coeff2, var2, 541 coeff3, var3, op, rightSide, penaltyNeg, penaltyPos); 542 fConstraints.AddItem(constraint); 543 return constraint; 544 } 545 546 547 Constraint* 548 BALMLayout::AddConstraint(double coeff1, Variable* var1, double coeff2, 549 Variable* var2, double coeff3, Variable* var3, double coeff4, 550 Variable* var4, OperatorType op, double rightSide, double penaltyNeg, 551 double penaltyPos) 552 { 553 Constraint* constraint = Solver()->AddConstraint(coeff1, var1, coeff2, var2, 554 coeff3, var3, coeff4, var4, op, rightSide, penaltyNeg, penaltyPos); 555 fConstraints.AddItem(constraint); 556 return constraint; 557 } 558 559 560 namespace { 561 562 563 int 564 CompareXTabFunc(const XTab* tab1, const XTab* tab2) 565 { 566 if (tab1->Value() < tab2->Value()) 567 return -1; 568 else if (tab1->Value() == tab2->Value()) 569 return 0; 570 return 1; 571 } 572 573 574 int 575 CompareYTabFunc(const YTab* tab1, const YTab* tab2) 576 { 577 if (tab1->Value() < tab2->Value()) 578 return -1; 579 else if (tab1->Value() == tab2->Value()) 580 return 0; 581 return 1; 582 } 583 584 585 }; // end anonymous namespace 586 587 588 /** 589 * Adds a new row to the specification that is glued to the given y-tabs. 590 * 591 * @param top 592 * @param bottom 593 * @return the new row 594 */ 595 Row* 596 BALMLayout::AddRow(YTab* _top, YTab* _bottom) 597 { 598 BReference<YTab> top = _top; 599 BReference<YTab> bottom = _bottom; 600 if (_top == NULL) 601 top = AddYTab(); 602 if (_bottom == NULL) 603 bottom = AddYTab(); 604 return new(std::nothrow) Row(Solver(), top, bottom); 605 } 606 607 608 /** 609 * Adds a new column to the specification that is glued to the given x-tabs. 610 * 611 * @param left 612 * @param right 613 * @return the new column 614 */ 615 Column* 616 BALMLayout::AddColumn(XTab* _left, XTab* _right) 617 { 618 BReference<XTab> left = _left; 619 BReference<XTab> right = _right; 620 if (_left == NULL) 621 left = AddXTab(); 622 if (_right == NULL) 623 right = AddXTab(); 624 return new(std::nothrow) Column(Solver(), left, right); 625 } 626 627 628 Area* 629 BALMLayout::AreaFor(int32 id) const 630 { 631 int32 areaCount = CountAreas(); 632 for (int32 i = 0; i < areaCount; i++) { 633 Area* area = AreaAt(i); 634 if (area->ID() == id) 635 return area; 636 } 637 return NULL; 638 } 639 640 641 /** 642 * Finds the area that contains the given control. 643 * 644 * @param control the control to look for 645 * @return the area that contains the control 646 */ 647 Area* 648 BALMLayout::AreaFor(const BView* control) const 649 { 650 return AreaFor(ItemAt(IndexOfView(const_cast<BView*>(control)))); 651 } 652 653 654 Area* 655 BALMLayout::AreaFor(const BLayoutItem* item) const 656 { 657 if (!item) 658 return NULL; 659 return static_cast<Area*>(item->LayoutData()); 660 } 661 662 663 int32 664 BALMLayout::CountAreas() const 665 { 666 return CountItems(); 667 } 668 669 670 Area* 671 BALMLayout::AreaAt(int32 index) const 672 { 673 return AreaFor(ItemAt(index)); 674 } 675 676 677 XTab* 678 BALMLayout::LeftOf(const BView* view) const 679 { 680 Area* area = AreaFor(view); 681 if (!area) 682 return NULL; 683 return area->Left(); 684 } 685 686 687 XTab* 688 BALMLayout::LeftOf(const BLayoutItem* item) const 689 { 690 Area* area = AreaFor(item); 691 if (!area) 692 return NULL; 693 return area->Left(); 694 } 695 696 697 XTab* 698 BALMLayout::RightOf(const BView* view) const 699 { 700 Area* area = AreaFor(view); 701 if (!area) 702 return NULL; 703 return area->Right(); 704 } 705 706 707 XTab* 708 BALMLayout::RightOf(const BLayoutItem* item) const 709 { 710 Area* area = AreaFor(item); 711 if (!area) 712 return NULL; 713 return area->Right(); 714 } 715 716 717 YTab* 718 BALMLayout::TopOf(const BView* view) const 719 { 720 Area* area = AreaFor(view); 721 if (!area) 722 return NULL; 723 return area->Top(); 724 } 725 726 727 YTab* 728 BALMLayout::TopOf(const BLayoutItem* item) const 729 { 730 Area* area = AreaFor(item); 731 if (!area) 732 return NULL; 733 return area->Top(); 734 } 735 736 737 YTab* 738 BALMLayout::BottomOf(const BView* view) const 739 { 740 Area* area = AreaFor(view); 741 if (!area) 742 return NULL; 743 return area->Bottom(); 744 } 745 746 747 YTab* 748 BALMLayout::BottomOf(const BLayoutItem* item) const 749 { 750 Area* area = AreaFor(item); 751 if (!area) 752 return NULL; 753 return area->Bottom(); 754 } 755 756 757 BLayoutItem* 758 BALMLayout::AddView(BView* child) 759 { 760 return AddView(-1, child); 761 } 762 763 764 BLayoutItem* 765 BALMLayout::AddView(int32 index, BView* child) 766 { 767 return BAbstractLayout::AddView(index, child); 768 } 769 770 771 /** 772 * Adds a new area to the specification, automatically setting preferred size constraints. 773 * 774 * @param left left border 775 * @param top top border 776 * @param right right border 777 * @param bottom bottom border 778 * @param content the control which is the area content 779 * @return the new area 780 */ 781 Area* 782 BALMLayout::AddView(BView* view, XTab* left, YTab* top, XTab* right, 783 YTab* bottom) 784 { 785 BLayoutItem* item = _LayoutItemToAdd(view); 786 Area* area = AddItem(item, left, top, right, bottom); 787 if (!area) { 788 if (item != view->GetLayout()) 789 delete item; 790 return NULL; 791 } 792 return area; 793 } 794 795 796 /** 797 * Adds a new area to the specification, automatically setting preferred size constraints. 798 * 799 * @param row the row that defines the top and bottom border 800 * @param column the column that defines the left and right border 801 * @param content the control which is the area content 802 * @return the new area 803 */ 804 Area* 805 BALMLayout::AddView(BView* view, Row* row, Column* column) 806 { 807 BLayoutItem* item = _LayoutItemToAdd(view); 808 Area* area = AddItem(item, row, column); 809 if (!area) { 810 if (item != view->GetLayout()) 811 delete item; 812 return NULL; 813 } 814 return area; 815 } 816 817 818 bool 819 BALMLayout::AddItem(BLayoutItem* item) 820 { 821 return AddItem(-1, item); 822 } 823 824 825 bool 826 BALMLayout::AddItem(int32 index, BLayoutItem* item) 827 { 828 if (!item) 829 return false; 830 831 // simply add the item at the upper right corner of the previous item 832 // TODO maybe find a more elegant solution 833 XTab* left = Left(); 834 YTab* top = Top(); 835 836 // check range 837 if (index < 0 || index > CountItems()) 838 index = CountItems(); 839 840 // for index = 0 we already have set the right tabs 841 if (index != 0) { 842 BLayoutItem* prevItem = ItemAt(index - 1); 843 Area* area = AreaFor(prevItem); 844 if (area) { 845 left = area->Right(); 846 top = area->Top(); 847 } 848 } 849 Area* area = AddItem(item, left, top); 850 return area ? true : false; 851 } 852 853 854 Area* 855 BALMLayout::AddItem(BLayoutItem* item, XTab* _left, YTab* _top, XTab* _right, 856 YTab* _bottom) 857 { 858 if ((_left && !_left->IsSuitableFor(this)) 859 || (_top && !_top->IsSuitableFor(this)) 860 || (_right && !_right->IsSuitableFor(this)) 861 || (_bottom && !_bottom->IsSuitableFor(this))) 862 debugger("Tab added to unfriendly layout!"); 863 864 BReference<XTab> right = _right; 865 if (!right.IsSet()) 866 right = AddXTab(); 867 BReference<YTab> bottom = _bottom; 868 if (!bottom.IsSet()) 869 bottom = AddYTab(); 870 BReference<XTab> left = _left; 871 if (!left.IsSet()) 872 left = AddXTab(); 873 BReference<YTab> top = _top; 874 if (!top.IsSet()) 875 top = AddYTab(); 876 877 TabAddTransaction<XTab> leftTabAdd(this); 878 if (!leftTabAdd.AttempAdd(left)) 879 return NULL; 880 881 TabAddTransaction<YTab> topTabAdd(this); 882 if (!topTabAdd.AttempAdd(top)) 883 return NULL; 884 885 TabAddTransaction<XTab> rightTabAdd(this); 886 if (!rightTabAdd.AttempAdd(right)) 887 return NULL; 888 889 TabAddTransaction<YTab> bottomTabAdd(this); 890 if (!bottomTabAdd.AttempAdd(bottom)) 891 return NULL; 892 893 // Area is added in ItemAdded 894 if (!BAbstractLayout::AddItem(-1, item)) 895 return NULL; 896 Area* area = AreaFor(item); 897 if (!area) { 898 RemoveItem(item); 899 return NULL; 900 } 901 902 fSolver->Invalidate(true); 903 area->_Init(Solver(), left, top, right, bottom, fRowColumnManager); 904 fRowColumnManager->AddArea(area); 905 906 leftTabAdd.Commit(); 907 topTabAdd.Commit(); 908 rightTabAdd.Commit(); 909 bottomTabAdd.Commit(); 910 return area; 911 } 912 913 914 Area* 915 BALMLayout::AddItem(BLayoutItem* item, Row* row, Column* column) 916 { 917 if (!BAbstractLayout::AddItem(-1, item)) 918 return NULL; 919 Area* area = AreaFor(item); 920 if (!area) 921 return NULL; 922 923 fSolver->Invalidate(true); 924 area->_Init(Solver(), row, column, fRowColumnManager); 925 926 fRowColumnManager->AddArea(area); 927 return area; 928 } 929 930 931 /** 932 * Gets the left variable. 933 */ 934 XTab* 935 BALMLayout::Left() const 936 { 937 return fLeft; 938 } 939 940 941 /** 942 * Gets the right variable. 943 */ 944 XTab* 945 BALMLayout::Right() const 946 { 947 return fRight; 948 } 949 950 951 /** 952 * Gets the top variable. 953 */ 954 YTab* 955 BALMLayout::Top() const 956 { 957 return fTop; 958 } 959 960 961 /** 962 * Gets the bottom variable. 963 */ 964 YTab* 965 BALMLayout::Bottom() const 966 { 967 return fBottom; 968 } 969 970 971 void 972 BALMLayout::SetBadLayoutPolicy(BadLayoutPolicy* policy) 973 { 974 if (fBadLayoutPolicy != policy) 975 delete fBadLayoutPolicy; 976 if (policy == NULL) 977 policy = new DefaultPolicy(); 978 fBadLayoutPolicy = policy; 979 } 980 981 982 struct BALMLayout::BadLayoutPolicy* 983 BALMLayout::GetBadLayoutPolicy() const 984 { 985 return fBadLayoutPolicy; 986 } 987 988 989 /** 990 * Gets minimum size. 991 */ 992 BSize 993 BALMLayout::BaseMinSize() 994 { 995 ResultType result = fSolver->ValidateMinSize(); 996 if (result != kOptimal && result != kUnbounded) 997 fBadLayoutPolicy->OnBadLayout(this, result, NULL); 998 return fMinSize; 999 } 1000 1001 1002 /** 1003 * Gets maximum size. 1004 */ 1005 BSize 1006 BALMLayout::BaseMaxSize() 1007 { 1008 ResultType result = fSolver->ValidateMaxSize(); 1009 if (result != kOptimal && result != kUnbounded) 1010 fBadLayoutPolicy->OnBadLayout(this, result, NULL); 1011 return fMaxSize; 1012 } 1013 1014 1015 /** 1016 * Gets preferred size. 1017 */ 1018 BSize 1019 BALMLayout::BasePreferredSize() 1020 { 1021 ResultType result = fSolver->ValidatePreferredSize(); 1022 if (result != kOptimal) 1023 fBadLayoutPolicy->OnBadLayout(this, result, NULL); 1024 1025 return fPreferredSize; 1026 } 1027 1028 1029 /** 1030 * Gets the alignment. 1031 */ 1032 BAlignment 1033 BALMLayout::BaseAlignment() 1034 { 1035 BAlignment alignment; 1036 alignment.SetHorizontal(B_ALIGN_HORIZONTAL_CENTER); 1037 alignment.SetVertical(B_ALIGN_VERTICAL_CENTER); 1038 return alignment; 1039 } 1040 1041 1042 status_t 1043 BALMLayout::Archive(BMessage* into, bool deep) const 1044 { 1045 BArchiver archiver(into); 1046 status_t err = BAbstractLayout::Archive(into, deep); 1047 if (err != B_OK) 1048 return archiver.Finish(err); 1049 1050 BRect insets(fLeftInset, fTopInset, fRightInset, fBottomInset); 1051 err = into->AddRect(kInsetsField, insets); 1052 if (err != B_OK) 1053 return archiver.Finish(err); 1054 1055 BSize spacing(fHSpacing, fVSpacing); 1056 err = into->AddSize(kSpacingField, spacing); 1057 if (err != B_OK) 1058 return archiver.Finish(err); 1059 1060 if (deep) { 1061 for (int32 i = CountXTabs() - 1; i >= 0 && err == B_OK; i--) 1062 err = archiver.AddArchivable(kXTabsField, XTabAt(i)); 1063 1064 for (int32 i = CountYTabs() - 1; i >= 0 && err == B_OK; i--) 1065 err = archiver.AddArchivable(kYTabsField, YTabAt(i)); 1066 1067 err = archiver.AddArchivable(kBadLayoutPolicyField, fBadLayoutPolicy); 1068 } 1069 1070 if (err == B_OK) 1071 err = archiver.AddArchivable(kSolverField, fSolver); 1072 1073 if (err == B_OK) 1074 err = archiver.AddArchivable(kMyTabsField, fLeft); 1075 if (err == B_OK) 1076 err = archiver.AddArchivable(kMyTabsField, fTop); 1077 if (err == B_OK) 1078 err = archiver.AddArchivable(kMyTabsField, fRight); 1079 if (err == B_OK) 1080 err = archiver.AddArchivable(kMyTabsField, fBottom); 1081 1082 return archiver.Finish(err); 1083 } 1084 1085 1086 BArchivable* 1087 BALMLayout::Instantiate(BMessage* from) 1088 { 1089 if (validate_instantiation(from, "BALM::BALMLayout")) 1090 return new BALMLayout(from); 1091 return NULL; 1092 } 1093 1094 1095 status_t 1096 BALMLayout::ItemArchived(BMessage* into, BLayoutItem* item, int32 index) const 1097 { 1098 BArchiver archiver(into); 1099 status_t err = BAbstractLayout::ItemArchived(into, item, index); 1100 if (err != B_OK) 1101 return err; 1102 1103 Area* area = AreaFor(item); 1104 err = into->AddSize(kItemPenalties, area->fShrinkPenalties); 1105 if (err == B_OK) 1106 err = into->AddSize(kItemPenalties, area->fGrowPenalties); 1107 if (err == B_OK) 1108 err = into->AddSize(kItemInsets, area->fLeftTopInset); 1109 if (err == B_OK) 1110 err = into->AddSize(kItemInsets, area->fRightBottomInset); 1111 if (err == B_OK) 1112 err = into->AddDouble(kItemAspectRatio, area->fContentAspectRatio); 1113 1114 err = archiver.AddArchivable(kTabsField, area->Left()); 1115 if (err == B_OK) 1116 archiver.AddArchivable(kTabsField, area->Top()); 1117 if (err == B_OK) 1118 archiver.AddArchivable(kTabsField, area->Right()); 1119 if (err == B_OK) 1120 archiver.AddArchivable(kTabsField, area->Bottom()); 1121 1122 return err; 1123 } 1124 1125 1126 status_t 1127 BALMLayout::ItemUnarchived(const BMessage* from, BLayoutItem* item, 1128 int32 index) 1129 { 1130 BUnarchiver unarchiver(from); 1131 status_t err = BAbstractLayout::ItemUnarchived(from, item, index); 1132 if (err != B_OK) 1133 return err; 1134 1135 Area* area = AreaFor(item); 1136 XTab* left; 1137 XTab* right; 1138 YTab* bottom; 1139 YTab* top; 1140 err = unarchiver.FindObject(kTabsField, index * 4, left); 1141 if (err == B_OK) 1142 err = unarchiver.FindObject(kTabsField, index * 4 + 1, top); 1143 if (err == B_OK) 1144 err = unarchiver.FindObject(kTabsField, index * 4 + 2, right); 1145 if (err == B_OK) 1146 err = unarchiver.FindObject(kTabsField, index * 4 + 3, bottom); 1147 1148 if (err != B_OK) 1149 return err; 1150 1151 area->_Init(Solver(), left, top, right, bottom, fRowColumnManager); 1152 fRowColumnManager->AddArea(area); 1153 1154 err = from->FindSize(kItemPenalties, index * 2, &area->fShrinkPenalties); 1155 if (err != B_OK) 1156 return err; 1157 1158 err = from->FindSize(kItemPenalties, index * 2 + 1, &area->fGrowPenalties); 1159 if (err != B_OK) 1160 return err; 1161 1162 err = from->FindSize(kItemInsets, index * 2, &area->fLeftTopInset); 1163 if (err != B_OK) 1164 return err; 1165 1166 err = from->FindSize(kItemInsets, index * 2 + 1, &area->fRightBottomInset); 1167 1168 if (err == B_OK) { 1169 double contentAspectRatio; 1170 err = from->FindDouble(kItemAspectRatio, index, &contentAspectRatio); 1171 if (err == B_OK) 1172 area->SetContentAspectRatio(contentAspectRatio); 1173 } 1174 1175 return err; 1176 } 1177 1178 1179 status_t 1180 BALMLayout::AllUnarchived(const BMessage* archive) 1181 { 1182 BUnarchiver unarchiver(archive); 1183 1184 SharedSolver* solver; 1185 status_t err = unarchiver.FindObject(kSolverField, solver); 1186 1187 if (err != B_OK) 1188 return err; 1189 1190 _SetSolver(solver); 1191 1192 if (archive->GetInfo(kBadLayoutPolicyField, NULL) == B_OK) { 1193 BadLayoutPolicy* policy; 1194 err = unarchiver.FindObject(kBadLayoutPolicyField, policy); 1195 if (err == B_OK) 1196 SetBadLayoutPolicy(policy); 1197 } 1198 1199 LinearSpec* spec = Solver(); 1200 int32 tabCount = 0; 1201 archive->GetInfo(kXTabsField, NULL, &tabCount); 1202 for (int32 i = 0; i < tabCount && err == B_OK; i++) { 1203 XTab* tab; 1204 err = unarchiver.FindObject(kXTabsField, i, 1205 BUnarchiver::B_DONT_ASSUME_OWNERSHIP, tab); 1206 spec->AddVariable(tab); 1207 TabAddTransaction<XTab> adder(this); 1208 if (adder.AttempAdd(tab)) 1209 adder.Commit(); 1210 else 1211 err = B_NO_MEMORY; 1212 } 1213 1214 archive->GetInfo(kYTabsField, NULL, &tabCount); 1215 for (int32 i = 0; i < tabCount; i++) { 1216 YTab* tab; 1217 unarchiver.FindObject(kYTabsField, i, 1218 BUnarchiver::B_DONT_ASSUME_OWNERSHIP, tab); 1219 spec->AddVariable(tab); 1220 TabAddTransaction<YTab> adder(this); 1221 if (adder.AttempAdd(tab)) 1222 adder.Commit(); 1223 else 1224 err = B_NO_MEMORY; 1225 } 1226 1227 1228 if (err == B_OK) { 1229 XTab* leftTab = NULL; 1230 err = unarchiver.FindObject(kMyTabsField, 0, leftTab); 1231 fLeft = leftTab; 1232 } 1233 1234 if (err == B_OK) { 1235 YTab* topTab = NULL; 1236 err = unarchiver.FindObject(kMyTabsField, 1, topTab); 1237 fTop = topTab; 1238 } 1239 1240 if (err == B_OK) { 1241 XTab* rightTab = NULL; 1242 err = unarchiver.FindObject(kMyTabsField, 2, rightTab); 1243 fRight = rightTab; 1244 } 1245 1246 if (err == B_OK) { 1247 YTab* bottomTab = NULL; 1248 err = unarchiver.FindObject(kMyTabsField, 3, bottomTab); 1249 fBottom = bottomTab; 1250 } 1251 1252 if (err == B_OK) { 1253 fLeft->SetRange(0, 0); 1254 fTop->SetRange(0, 0); 1255 1256 err = BAbstractLayout::AllUnarchived(archive); 1257 } 1258 return err; 1259 } 1260 1261 1262 status_t 1263 BALMLayout::AllArchived(BMessage* archive) const 1264 { 1265 status_t err = BAbstractLayout::AllArchived(archive); 1266 1267 return err; 1268 } 1269 1270 1271 /** 1272 * Invalidates the layout. 1273 * Resets minimum/maximum/preferred size. 1274 */ 1275 void 1276 BALMLayout::LayoutInvalidated(bool children) 1277 { 1278 fMinSize = kUnsetSize; 1279 fMaxSize = kUnsetSize; 1280 fPreferredSize = kUnsetSize; 1281 fXTabsSorted = false; 1282 fYTabsSorted = false; 1283 1284 if (fSolver) 1285 fSolver->Invalidate(children); 1286 } 1287 1288 1289 bool 1290 BALMLayout::ItemAdded(BLayoutItem* item, int32 atIndex) 1291 { 1292 item->SetLayoutData(new(std::nothrow) Area(item)); 1293 return item->LayoutData() != NULL; 1294 } 1295 1296 1297 void 1298 BALMLayout::ItemRemoved(BLayoutItem* item, int32 fromIndex) 1299 { 1300 if (Area* area = AreaFor(item)) { 1301 fRowColumnManager->RemoveArea(area); 1302 item->SetLayoutData(NULL); 1303 delete area; 1304 } 1305 } 1306 1307 1308 /** 1309 * Calculate and set the layout. 1310 * If no layout specification is given, a specification is reverse engineered automatically. 1311 */ 1312 void 1313 BALMLayout::DoLayout() 1314 { 1315 BLayoutContext* context = LayoutContext(); 1316 ResultType result = fSolver->ValidateLayout(context); 1317 if (result != kOptimal 1318 && !fBadLayoutPolicy->OnBadLayout(this, result, context)) { 1319 return; 1320 } 1321 1322 // set the calculated positions and sizes for every area 1323 for (int32 i = 0; i < CountItems(); i++) 1324 AreaFor(ItemAt(i))->_DoLayout(LayoutArea().LeftTop()); 1325 1326 fXTabsSorted = false; 1327 fYTabsSorted = false; 1328 } 1329 1330 1331 LinearSpec* 1332 BALMLayout::Solver() const 1333 { 1334 return fSolver->Solver(); 1335 } 1336 1337 1338 ResultType 1339 BALMLayout::ValidateLayout() 1340 { 1341 // we explicitly recaluclate the layout so set the invalidate flag first 1342 fSolver->Invalidate(true); 1343 1344 BLayoutContext* context = LayoutContext(); 1345 return fSolver->ValidateLayout(context); 1346 } 1347 1348 1349 void 1350 BALMLayout::SetInsets(float left, float top, float right, 1351 float bottom) 1352 { 1353 fLeftInset = BControlLook::ComposeSpacing(left); 1354 fTopInset = BControlLook::ComposeSpacing(top); 1355 fRightInset = BControlLook::ComposeSpacing(right); 1356 fBottomInset = BControlLook::ComposeSpacing(bottom); 1357 1358 InvalidateLayout(); 1359 } 1360 1361 1362 void 1363 BALMLayout::SetInsets(float horizontal, float vertical) 1364 { 1365 fLeftInset = BControlLook::ComposeSpacing(horizontal); 1366 fRightInset = fLeftInset; 1367 1368 fTopInset = BControlLook::ComposeSpacing(vertical); 1369 fBottomInset = fTopInset; 1370 1371 InvalidateLayout(); 1372 } 1373 1374 1375 void 1376 BALMLayout::SetInsets(float insets) 1377 { 1378 fLeftInset = BControlLook::ComposeSpacing(insets); 1379 fRightInset = fLeftInset; 1380 fTopInset = fLeftInset; 1381 fBottomInset = fLeftInset; 1382 1383 InvalidateLayout(); 1384 } 1385 1386 1387 void 1388 BALMLayout::GetInsets(float* left, float* top, float* right, 1389 float* bottom) const 1390 { 1391 if (left) 1392 *left = fLeftInset; 1393 if (top) 1394 *top = fTopInset; 1395 if (right) 1396 *right = fRightInset; 1397 if (bottom) 1398 *bottom = fBottomInset; 1399 } 1400 1401 1402 void 1403 BALMLayout::SetSpacing(float hSpacing, float vSpacing) 1404 { 1405 fHSpacing = BControlLook::ComposeSpacing(hSpacing); 1406 fVSpacing = BControlLook::ComposeSpacing(vSpacing); 1407 } 1408 1409 1410 void 1411 BALMLayout::GetSpacing(float *_hSpacing, float *_vSpacing) const 1412 { 1413 if (_hSpacing) 1414 *_hSpacing = fHSpacing; 1415 if (_vSpacing) 1416 *_vSpacing = fVSpacing; 1417 } 1418 1419 1420 float 1421 BALMLayout::InsetForTab(XTab* tab) const 1422 { 1423 if (tab == fLeft.Get()) 1424 return fLeftInset; 1425 if (tab == fRight.Get()) 1426 return fRightInset; 1427 return fHSpacing / 2; 1428 } 1429 1430 1431 float 1432 BALMLayout::InsetForTab(YTab* tab) const 1433 { 1434 if (tab == fTop.Get()) 1435 return fTopInset; 1436 if (tab == fBottom.Get()) 1437 return fBottomInset; 1438 return fVSpacing / 2; 1439 } 1440 1441 1442 void 1443 BALMLayout::UpdateConstraints(BLayoutContext* context) 1444 { 1445 for (int i = 0; i < CountItems(); i++) 1446 AreaFor(ItemAt(i))->InvalidateSizeConstraints(); 1447 fRowColumnManager->UpdateConstraints(); 1448 } 1449 1450 1451 void BALMLayout::_RemoveSelfFromTab(XTab* tab) { tab->LayoutLeaving(this); } 1452 void BALMLayout::_RemoveSelfFromTab(YTab* tab) { tab->LayoutLeaving(this); } 1453 1454 bool BALMLayout::_HasTabInLayout(XTab* tab) { return tab->IsInLayout(this); } 1455 bool BALMLayout::_HasTabInLayout(YTab* tab) { return tab->IsInLayout(this); } 1456 1457 bool BALMLayout::_AddedTab(XTab* tab) { return tab->AddedToLayout(this); } 1458 bool BALMLayout::_AddedTab(YTab* tab) { return tab->AddedToLayout(this); } 1459 1460 1461 BLayoutItem* 1462 BALMLayout::_LayoutItemToAdd(BView* view) 1463 { 1464 if (view->GetLayout()) 1465 return view->GetLayout(); 1466 return new(std::nothrow) BViewLayoutItem(view); 1467 } 1468 1469 1470 void 1471 BALMLayout::_SetSolver(SharedSolver* solver) 1472 { 1473 fSolver = solver; 1474 fSolver->AcquireReference(); 1475 fSolver->RegisterLayout(this); 1476 fRowColumnManager = new RowColumnManager(Solver()); 1477 } 1478 1479 1480 status_t 1481 BALMLayout::Perform(perform_code d, void* arg) 1482 { 1483 return BAbstractLayout::Perform(d, arg); 1484 } 1485 1486 1487 void BALMLayout::_ReservedALMLayout1() {} 1488 void BALMLayout::_ReservedALMLayout2() {} 1489 void BALMLayout::_ReservedALMLayout3() {} 1490 void BALMLayout::_ReservedALMLayout4() {} 1491 void BALMLayout::_ReservedALMLayout5() {} 1492 void BALMLayout::_ReservedALMLayout6() {} 1493 void BALMLayout::_ReservedALMLayout7() {} 1494 void BALMLayout::_ReservedALMLayout8() {} 1495 void BALMLayout::_ReservedALMLayout9() {} 1496 void BALMLayout::_ReservedALMLayout10() {} 1497 1498