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