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