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