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 23using BPrivate::AutoDeleter; 24using namespace LinearProgramming; 25 26 27const BSize kUnsetSize(B_SIZE_UNSET, B_SIZE_UNSET); 28 29 30namespace { 31 32const char* kSolverField = "BALMLayout:solver"; 33const char* kBadLayoutPolicyField = "BALMLayout:policy"; 34const char* kXTabsField = "BALMLayout:xtabs"; 35const char* kYTabsField = "BALMLayout:ytabs"; 36const char* kMyTabsField = "BALMLayout:tabs"; 37const char* kInsetsField = "BALMLayout:insets"; 38const char* kSpacingField = "BALMLayout:spacing"; 39 40const char* kTabsField = "BALMLayout:item:tabs"; 41const char* kItemAspectRatio = "BALMLayout:item:aspect"; 42const char* kItemPenalties = "BALMLayout:item:penalties"; 43const char* kItemInsets = "BALMLayout:item:insets"; 44 45int CompareXTabFunc(const XTab* tab1, const XTab* tab2); 46int CompareYTabFunc(const YTab* tab1, const YTab* tab2); 47 48}; 49 50 51namespace BALM { 52 53 54template <class T> 55struct 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 94private: 95 BObjectList<T>* _TabList(); 96 97 T* fTab; 98 BALMLayout* fLayout; 99 int32 fIndex; 100}; 101 102 103template <> 104BObjectList<XTab>* 105BALMLayout::TabAddTransaction<XTab>::_TabList() 106{ 107 return &fLayout->fXTabList; 108} 109 110 111template <> 112BObjectList<YTab>* 113BALMLayout::TabAddTransaction<YTab>::_TabList() 114{ 115 return &fLayout->fYTabList; 116} 117 118 119}; // end namespace BALM 120 121 122BALM::BALMLayout::BadLayoutPolicy::BadLayoutPolicy() 123{ 124} 125 126 127BALM::BALMLayout::BadLayoutPolicy::BadLayoutPolicy(BMessage* archive) 128 : 129 BArchivable(archive) 130{ 131} 132 133 134BALM::BALMLayout::BadLayoutPolicy::~BadLayoutPolicy() 135{ 136} 137 138 139BALM::BALMLayout::DefaultPolicy::DefaultPolicy() 140{ 141} 142 143 144BALM::BALMLayout::DefaultPolicy::DefaultPolicy(BMessage* archive) 145 : 146 BadLayoutPolicy(archive) 147{ 148} 149 150 151BALM::BALMLayout::DefaultPolicy::~DefaultPolicy() 152{ 153} 154 155 156bool 157BALM::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 171status_t 172BALM::BALMLayout::DefaultPolicy::Archive(BMessage* archive, bool deep) const 173{ 174 return BadLayoutPolicy::Archive(archive, deep); 175} 176 177 178BArchivable* 179BALM::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 187class BALMLayout::BALMLayoutSpecListener 188 : public LinearProgramming::SpecificationListener { 189public: 190 BALMLayoutSpecListener(BALMLayout* layout) 191 : 192 fLayout(layout) 193 { 194 } 195 196 void ConstraintRemoved(Constraint* constraint) 197 { 198 fLayout->fConstraints.RemoveItem(constraint); 199 } 200 201private: 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 */ 212BALMLayout::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 247BALMLayout::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 309BALMLayout::~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 */ 332BReference<XTab> 333BALMLayout::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 351void 352BALMLayout::AddXTabs(BReference<XTab>* tabs, uint32 count) 353{ 354 for (uint32 i = 0; i < count; i++) 355 tabs[i] = AddXTab(); 356} 357 358 359void 360BALMLayout::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 */ 372BReference<YTab> 373BALMLayout::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 391int32 392BALMLayout::CountXTabs() const 393{ 394 return fXTabList.CountItems(); 395} 396 397 398int32 399BALMLayout::CountYTabs() const 400{ 401 return fYTabList.CountItems(); 402} 403 404 405XTab* 406BALMLayout::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 417XTab* 418BALMLayout::XTabAt(int32 index) const 419{ 420 return fXTabList.ItemAt(index); 421} 422 423 424YTab* 425BALMLayout::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 436YTab* 437BALMLayout::YTabAt(int32 index) const 438{ 439 return fYTabList.ItemAt(index); 440} 441 442 443const XTabList 444BALMLayout::GetXTabs() const 445{ 446 return fXTabList; 447} 448 449 450const YTabList 451BALMLayout::GetYTabs() const 452{ 453 return fYTabList; 454} 455 456 457int32 458BALMLayout::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 469int32 470BALMLayout::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 481int32 482BALMLayout::CountConstraints() const 483{ 484 return fConstraints.CountItems(); 485} 486 487 488Constraint* 489BALMLayout::ConstraintAt(int32 index) const 490{ 491 return fConstraints.ItemAt(index); 492} 493 494 495bool 496BALMLayout::AddConstraint(Constraint* constraint) 497{ 498 fConstraints.AddItem(constraint); 499 return Solver()->AddConstraint(constraint); 500} 501 502 503bool 504BALMLayout::RemoveConstraint(Constraint* constraint, 505 bool deleteConstraint) 506{ 507 if (!fConstraints.RemoveItem(constraint)) 508 return false; 509 return Solver()->RemoveConstraint(constraint, deleteConstraint); 510} 511 512 513Constraint* 514BALMLayout::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 524Constraint* 525BALMLayout::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 535Constraint* 536BALMLayout::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 547Constraint* 548BALMLayout::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 560namespace { 561 562 563int 564CompareXTabFunc(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 574int 575CompareYTabFunc(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 */ 595Row* 596BALMLayout::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 */ 615Column* 616BALMLayout::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 628Area* 629BALMLayout::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 */ 647Area* 648BALMLayout::AreaFor(const BView* control) const 649{ 650 return AreaFor(ItemAt(IndexOfView(const_cast<BView*>(control)))); 651} 652 653 654Area* 655BALMLayout::AreaFor(const BLayoutItem* item) const 656{ 657 if (!item) 658 return NULL; 659 return static_cast<Area*>(item->LayoutData()); 660} 661 662 663int32 664BALMLayout::CountAreas() const 665{ 666 return CountItems(); 667} 668 669 670Area* 671BALMLayout::AreaAt(int32 index) const 672{ 673 return AreaFor(ItemAt(index)); 674} 675 676 677XTab* 678BALMLayout::LeftOf(const BView* view) const 679{ 680 Area* area = AreaFor(view); 681 if (!area) 682 return NULL; 683 return area->Left(); 684} 685 686 687XTab* 688BALMLayout::LeftOf(const BLayoutItem* item) const 689{ 690 Area* area = AreaFor(item); 691 if (!area) 692 return NULL; 693 return area->Left(); 694} 695 696 697XTab* 698BALMLayout::RightOf(const BView* view) const 699{ 700 Area* area = AreaFor(view); 701 if (!area) 702 return NULL; 703 return area->Right(); 704} 705 706 707XTab* 708BALMLayout::RightOf(const BLayoutItem* item) const 709{ 710 Area* area = AreaFor(item); 711 if (!area) 712 return NULL; 713 return area->Right(); 714} 715 716 717YTab* 718BALMLayout::TopOf(const BView* view) const 719{ 720 Area* area = AreaFor(view); 721 if (!area) 722 return NULL; 723 return area->Top(); 724} 725 726 727YTab* 728BALMLayout::TopOf(const BLayoutItem* item) const 729{ 730 Area* area = AreaFor(item); 731 if (!area) 732 return NULL; 733 return area->Top(); 734} 735 736 737YTab* 738BALMLayout::BottomOf(const BView* view) const 739{ 740 Area* area = AreaFor(view); 741 if (!area) 742 return NULL; 743 return area->Bottom(); 744} 745 746 747YTab* 748BALMLayout::BottomOf(const BLayoutItem* item) const 749{ 750 Area* area = AreaFor(item); 751 if (!area) 752 return NULL; 753 return area->Bottom(); 754} 755 756 757BLayoutItem* 758BALMLayout::AddView(BView* child) 759{ 760 return AddView(-1, child); 761} 762 763 764BLayoutItem* 765BALMLayout::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 */ 781Area* 782BALMLayout::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 */ 804Area* 805BALMLayout::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 818bool 819BALMLayout::AddItem(BLayoutItem* item) 820{ 821 return AddItem(-1, item); 822} 823 824 825bool 826BALMLayout::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 854Area* 855BALMLayout::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 914Area* 915BALMLayout::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 */ 934XTab* 935BALMLayout::Left() const 936{ 937 return fLeft; 938} 939 940 941/** 942 * Gets the right variable. 943 */ 944XTab* 945BALMLayout::Right() const 946{ 947 return fRight; 948} 949 950 951/** 952 * Gets the top variable. 953 */ 954YTab* 955BALMLayout::Top() const 956{ 957 return fTop; 958} 959 960 961/** 962 * Gets the bottom variable. 963 */ 964YTab* 965BALMLayout::Bottom() const 966{ 967 return fBottom; 968} 969 970 971void 972BALMLayout::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 982struct BALMLayout::BadLayoutPolicy* 983BALMLayout::GetBadLayoutPolicy() const 984{ 985 return fBadLayoutPolicy; 986} 987 988 989/** 990 * Gets minimum size. 991 */ 992BSize 993BALMLayout::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 */ 1005BSize 1006BALMLayout::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 */ 1018BSize 1019BALMLayout::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 */ 1032BAlignment 1033BALMLayout::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 1042status_t 1043BALMLayout::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 1086BArchivable* 1087BALMLayout::Instantiate(BMessage* from) 1088{ 1089 if (validate_instantiation(from, "BALM::BALMLayout")) 1090 return new BALMLayout(from); 1091 return NULL; 1092} 1093 1094 1095status_t 1096BALMLayout::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 1126status_t 1127BALMLayout::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 1179status_t 1180BALMLayout::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 1262status_t 1263BALMLayout::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 */ 1275void 1276BALMLayout::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 1289bool 1290BALMLayout::ItemAdded(BLayoutItem* item, int32 atIndex) 1291{ 1292 item->SetLayoutData(new(std::nothrow) Area(item)); 1293 return item->LayoutData() != NULL; 1294} 1295 1296 1297void 1298BALMLayout::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 */ 1312void 1313BALMLayout::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 1331LinearSpec* 1332BALMLayout::Solver() const 1333{ 1334 return fSolver->Solver(); 1335} 1336 1337 1338ResultType 1339BALMLayout::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 1349void 1350BALMLayout::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 1362void 1363BALMLayout::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 1375void 1376BALMLayout::SetInsets(float insets) 1377{ 1378 fLeftInset = BControlLook::ComposeSpacing(insets); 1379 fRightInset = fLeftInset; 1380 fTopInset = fLeftInset; 1381 fBottomInset = fLeftInset; 1382 1383 InvalidateLayout(); 1384} 1385 1386 1387void 1388BALMLayout::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 1402void 1403BALMLayout::SetSpacing(float hSpacing, float vSpacing) 1404{ 1405 fHSpacing = BControlLook::ComposeSpacing(hSpacing); 1406 fVSpacing = BControlLook::ComposeSpacing(vSpacing); 1407} 1408 1409 1410void 1411BALMLayout::GetSpacing(float *_hSpacing, float *_vSpacing) const 1412{ 1413 if (_hSpacing) 1414 *_hSpacing = fHSpacing; 1415 if (_vSpacing) 1416 *_vSpacing = fVSpacing; 1417} 1418 1419 1420float 1421BALMLayout::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 1431float 1432BALMLayout::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 1442void 1443BALMLayout::UpdateConstraints(BLayoutContext* context) 1444{ 1445 for (int i = 0; i < CountItems(); i++) 1446 AreaFor(ItemAt(i))->InvalidateSizeConstraints(); 1447 fRowColumnManager->UpdateConstraints(); 1448} 1449 1450 1451void BALMLayout::_RemoveSelfFromTab(XTab* tab) { tab->LayoutLeaving(this); } 1452void BALMLayout::_RemoveSelfFromTab(YTab* tab) { tab->LayoutLeaving(this); } 1453 1454bool BALMLayout::_HasTabInLayout(XTab* tab) { return tab->IsInLayout(this); } 1455bool BALMLayout::_HasTabInLayout(YTab* tab) { return tab->IsInLayout(this); } 1456 1457bool BALMLayout::_AddedTab(XTab* tab) { return tab->AddedToLayout(this); } 1458bool BALMLayout::_AddedTab(YTab* tab) { return tab->AddedToLayout(this); } 1459 1460 1461BLayoutItem* 1462BALMLayout::_LayoutItemToAdd(BView* view) 1463{ 1464 if (view->GetLayout()) 1465 return view->GetLayout(); 1466 return new(std::nothrow) BViewLayoutItem(view); 1467} 1468 1469 1470void 1471BALMLayout::_SetSolver(SharedSolver* solver) 1472{ 1473 fSolver = solver; 1474 fSolver->AcquireReference(); 1475 fSolver->RegisterLayout(this); 1476 fRowColumnManager = new RowColumnManager(Solver()); 1477} 1478 1479 1480status_t 1481BALMLayout::Perform(perform_code d, void* arg) 1482{ 1483 return BAbstractLayout::Perform(d, arg); 1484} 1485 1486 1487void BALMLayout::_ReservedALMLayout1() {} 1488void BALMLayout::_ReservedALMLayout2() {} 1489void BALMLayout::_ReservedALMLayout3() {} 1490void BALMLayout::_ReservedALMLayout4() {} 1491void BALMLayout::_ReservedALMLayout5() {} 1492void BALMLayout::_ReservedALMLayout6() {} 1493void BALMLayout::_ReservedALMLayout7() {} 1494void BALMLayout::_ReservedALMLayout8() {} 1495void BALMLayout::_ReservedALMLayout9() {} 1496void BALMLayout::_ReservedALMLayout10() {} 1497 1498