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