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 #include "ALMLayout.h" 8 9 #include <math.h> // for floor 10 #include <new> 11 #include <iostream> 12 #include <vector> 13 14 #include <ControlLook.h> 15 16 #include "RowColumnManager.h" 17 #include "ViewLayoutItem.h" 18 19 20 using namespace LinearProgramming; 21 22 23 const BSize kUnsetSize(B_SIZE_UNSET, B_SIZE_UNSET); 24 25 26 /*! 27 * Constructor. 28 * Creates new layout engine. 29 * 30 * If friendLayout is not NULL the solver of the friend layout is used. 31 */ 32 BALMLayout::BALMLayout(float hSpacing, float vSpacing, BALMLayout* friendLayout) 33 : 34 fLeftInset(0), 35 fRightInset(0), 36 fTopInset(0), 37 fBottomInset(0), 38 fHSpacing(hSpacing), 39 fVSpacing(vSpacing), 40 fCurrentArea(NULL) 41 { 42 fSolver = friendLayout ? friendLayout->Solver() : &fOwnSolver; 43 fRowColumnManager = new RowColumnManager(fSolver); 44 45 fLeft = AddXTab(); 46 fRight = AddXTab(); 47 fTop = AddYTab(); 48 fBottom = AddYTab(); 49 50 // the Left tab is always at x-position 0, and the Top tab is always at 51 // y-position 0 52 fLeft->SetRange(0, 0); 53 fTop->SetRange(0, 0); 54 55 // cached layout values 56 // need to be invalidated whenever the layout specification is changed 57 fMinSize = kUnsetSize; 58 fMaxSize = kUnsetSize; 59 fPreferredSize = kUnsetSize; 60 } 61 62 63 BALMLayout::~BALMLayout() 64 { 65 delete fRowColumnManager; 66 } 67 68 69 /** 70 * Adds a new x-tab to the specification. 71 * 72 * @return the new x-tab 73 */ 74 BReference<XTab> 75 BALMLayout::AddXTab() 76 { 77 BReference<XTab> tab(new(std::nothrow) XTab(this), true); 78 if (!tab) 79 return NULL; 80 if (!fSolver->AddVariable(tab)) 81 return NULL; 82 83 fXTabList.AddItem(tab); 84 return tab; 85 } 86 87 88 void 89 BALMLayout::AddXTabs(BReference<XTab>* tabs, uint32 count) 90 { 91 for (uint32 i = 0; i < count; i++) 92 tabs[i] = AddXTab(); 93 } 94 95 96 void 97 BALMLayout::AddYTabs(BReference<YTab>* tabs, uint32 count) 98 { 99 for (uint32 i = 0; i < count; i++) 100 tabs[i] = AddYTab(); 101 } 102 103 104 /** 105 * Adds a new y-tab to the specification. 106 * 107 * @return the new y-tab 108 */ 109 BReference<YTab> 110 BALMLayout::AddYTab() 111 { 112 BReference<YTab> tab(new(std::nothrow) YTab(this), true); 113 if (tab.Get() == NULL) 114 return NULL; 115 if (!fSolver->AddVariable(tab)) 116 return NULL; 117 118 fYTabList.AddItem(tab); 119 return tab; 120 } 121 122 123 int32 124 BALMLayout::CountXTabs() const 125 { 126 return fXTabList.CountItems(); 127 } 128 129 130 int32 131 BALMLayout::CountYTabs() const 132 { 133 return fYTabList.CountItems(); 134 } 135 136 137 XTab* 138 BALMLayout::XTabAt(int32 index) const 139 { 140 return fXTabList.ItemAt(index); 141 } 142 143 144 YTab* 145 BALMLayout::YTabAt(int32 index) const 146 { 147 return fYTabList.ItemAt(index); 148 } 149 150 151 static int 152 compare_x_tab_func(const XTab* tab1, const XTab* tab2) 153 { 154 if (tab1->Value() < tab2->Value()) 155 return -1; 156 else if (tab1->Value() == tab2->Value()) 157 return 0; 158 return 1; 159 } 160 161 162 static int 163 compare_y_tab_func(const YTab* tab1, const YTab* tab2) 164 { 165 if (tab1->Value() < tab2->Value()) 166 return -1; 167 else if (tab1->Value() == tab2->Value()) 168 return 0; 169 return 1; 170 } 171 172 173 const XTabList& 174 BALMLayout::OrderedXTabs() 175 { 176 fXTabList.SortItems(compare_x_tab_func); 177 return fXTabList; 178 } 179 180 181 const YTabList& 182 BALMLayout::OrderedYTabs() 183 { 184 fYTabList.SortItems(compare_y_tab_func); 185 return fYTabList; 186 } 187 188 189 /** 190 * Adds a new row to the specification that is glued to the given y-tabs. 191 * 192 * @param top 193 * @param bottom 194 * @return the new row 195 */ 196 Row* 197 BALMLayout::AddRow(YTab* _top, YTab* _bottom) 198 { 199 BReference<YTab> top = _top; 200 BReference<YTab> bottom = _bottom; 201 if (_top == NULL) 202 top = AddYTab(); 203 if (_bottom == NULL) 204 bottom = AddYTab(); 205 return new(std::nothrow) Row(fSolver, top, bottom); 206 } 207 208 209 /** 210 * Adds a new column to the specification that is glued to the given x-tabs. 211 * 212 * @param left 213 * @param right 214 * @return the new column 215 */ 216 Column* 217 BALMLayout::AddColumn(XTab* _left, XTab* _right) 218 { 219 BReference<XTab> left = _left; 220 BReference<XTab> right = _right; 221 if (_left == NULL) 222 left = AddXTab(); 223 if (_right == NULL) 224 right = AddXTab(); 225 return new(std::nothrow) Column(fSolver, left, right); 226 } 227 228 229 Area* 230 BALMLayout::AreaFor(int32 id) const 231 { 232 int32 areaCount = CountAreas(); 233 for (int32 i = 0; i < areaCount; i++) { 234 Area* area = AreaAt(i); 235 if (area->ID() == id) 236 return area; 237 } 238 return NULL; 239 } 240 241 242 /** 243 * Finds the area that contains the given control. 244 * 245 * @param control the control to look for 246 * @return the area that contains the control 247 */ 248 Area* 249 BALMLayout::AreaFor(const BView* control) const 250 { 251 return AreaFor(ItemAt(IndexOfView(const_cast<BView*>(control)))); 252 } 253 254 255 Area* 256 BALMLayout::AreaFor(const BLayoutItem* item) const 257 { 258 if (!item) 259 return NULL; 260 return static_cast<Area*>(item->LayoutData()); 261 } 262 263 264 int32 265 BALMLayout::CountAreas() const 266 { 267 return CountItems(); 268 } 269 270 271 Area* 272 BALMLayout::AreaAt(int32 index) const 273 { 274 return AreaFor(ItemAt(index)); 275 } 276 277 278 Area* 279 BALMLayout::CurrentArea() const 280 { 281 return fCurrentArea; 282 } 283 284 285 bool 286 BALMLayout::SetCurrentArea(const Area* area) 287 { 288 fCurrentArea = const_cast<Area*>(area); 289 return true; 290 } 291 292 293 bool 294 BALMLayout::SetCurrentArea(const BView* view) 295 { 296 Area* area = AreaFor(view); 297 if (!area) 298 return false; 299 fCurrentArea = area; 300 return true; 301 } 302 303 304 bool 305 BALMLayout::SetCurrentArea(const BLayoutItem* item) 306 { 307 Area* area = AreaFor(item); 308 if (!area) 309 return false; 310 fCurrentArea = area; 311 return true; 312 } 313 314 315 XTab* 316 BALMLayout::LeftOf(const BView* view) const 317 { 318 Area* area = AreaFor(view); 319 if (!area) 320 return NULL; 321 return area->Left(); 322 } 323 324 325 XTab* 326 BALMLayout::LeftOf(const BLayoutItem* item) const 327 { 328 Area* area = AreaFor(item); 329 if (!area) 330 return NULL; 331 return area->Left(); 332 } 333 334 335 XTab* 336 BALMLayout::RightOf(const BView* view) const 337 { 338 Area* area = AreaFor(view); 339 if (!area) 340 return NULL; 341 return area->Right(); 342 } 343 344 345 XTab* 346 BALMLayout::RightOf(const BLayoutItem* item) const 347 { 348 Area* area = AreaFor(item); 349 if (!area) 350 return NULL; 351 return area->Right(); 352 } 353 354 355 YTab* 356 BALMLayout::TopOf(const BView* view) const 357 { 358 Area* area = AreaFor(view); 359 if (!area) 360 return NULL; 361 return area->Top(); 362 } 363 364 365 YTab* 366 BALMLayout::TopOf(const BLayoutItem* item) const 367 { 368 Area* area = AreaFor(item); 369 if (!area) 370 return NULL; 371 return area->Top(); 372 } 373 374 375 YTab* 376 BALMLayout::BottomOf(const BView* view) const 377 { 378 Area* area = AreaFor(view); 379 if (!area) 380 return NULL; 381 return area->Bottom(); 382 } 383 384 385 YTab* 386 BALMLayout::BottomOf(const BLayoutItem* item) const 387 { 388 Area* area = AreaFor(item); 389 if (!area) 390 return NULL; 391 return area->Bottom(); 392 } 393 394 395 void 396 BALMLayout::BuildLayout(GroupItem& item, XTab* left, YTab* top, XTab* right, 397 YTab* bottom) 398 { 399 if (left == NULL) 400 left = Left(); 401 if (top == NULL) 402 top = Top(); 403 if (right == NULL) 404 right = Right(); 405 if (bottom == NULL) 406 bottom = Bottom(); 407 408 _ParseGroupItem(item, left, top, right, bottom); 409 } 410 411 412 void 413 BALMLayout::_ParseGroupItem(GroupItem& item, BReference<XTab> left, 414 BReference<YTab> top, BReference<XTab> right, BReference<YTab> bottom) 415 { 416 if (item.LayoutItem()) 417 AddItem(item.LayoutItem(), left, top, right, bottom); 418 else if (item.View()) { 419 AddView(item.View(), left, top, right, bottom); 420 } 421 else { 422 for (unsigned int i = 0; i < item.GroupItems().size(); i++) { 423 GroupItem& current = const_cast<GroupItem&>( 424 item.GroupItems()[i]); 425 if (item.Orientation() == B_HORIZONTAL) { 426 BReference<XTab> r = (i == item.GroupItems().size() - 1) ? right 427 : AddXTab(); 428 _ParseGroupItem(current, left, top, r, bottom); 429 left = r; 430 } 431 else { 432 BReference<YTab> b = (i == item.GroupItems().size() - 1) 433 ? bottom : AddYTab(); 434 _ParseGroupItem(current, left, top, right, b); 435 top = b; 436 } 437 } 438 } 439 } 440 441 442 BLayoutItem* 443 BALMLayout::AddView(BView* child) 444 { 445 return AddView(-1, child); 446 } 447 448 449 BLayoutItem* 450 BALMLayout::AddView(int32 index, BView* child) 451 { 452 return BAbstractLayout::AddView(index, child); 453 } 454 455 456 /** 457 * Adds a new area to the specification, automatically setting preferred size constraints. 458 * 459 * @param left left border 460 * @param top top border 461 * @param right right border 462 * @param bottom bottom border 463 * @param content the control which is the area content 464 * @return the new area 465 */ 466 Area* 467 BALMLayout::AddView(BView* view, XTab* left, YTab* top, XTab* right, 468 YTab* bottom) 469 { 470 BLayoutItem* item = _LayoutItemToAdd(view); 471 Area* area = AddItem(item, left, top, right, bottom); 472 if (!area) { 473 if (item != view->GetLayout()) 474 delete item; 475 return NULL; 476 } 477 return area; 478 } 479 480 481 /** 482 * Adds a new area to the specification, automatically setting preferred size constraints. 483 * 484 * @param row the row that defines the top and bottom border 485 * @param column the column that defines the left and right border 486 * @param content the control which is the area content 487 * @return the new area 488 */ 489 Area* 490 BALMLayout::AddView(BView* view, Row* row, Column* column) 491 { 492 BLayoutItem* item = _LayoutItemToAdd(view); 493 Area* area = AddItem(item, row, column); 494 if (!area) { 495 if (item != view->GetLayout()) 496 delete item; 497 return NULL; 498 } 499 return area; 500 } 501 502 503 Area* 504 BALMLayout::AddViewToRight(BView* view, XTab* right, YTab* top, YTab* bottom) 505 { 506 BLayoutItem* item = _LayoutItemToAdd(view); 507 Area* area = AddItemToRight(item, right, top, bottom); 508 if (!area) { 509 if (item != view->GetLayout()) 510 delete item; 511 return NULL; 512 } 513 return area; 514 } 515 516 517 Area* 518 BALMLayout::AddViewToLeft(BView* view, XTab* left, YTab* top, YTab* bottom) 519 { 520 BLayoutItem* item = _LayoutItemToAdd(view); 521 Area* area = AddItemToLeft(item, left, top, bottom); 522 if (!area) { 523 if (item != view->GetLayout()) 524 delete item; 525 return NULL; 526 } 527 return area; 528 } 529 530 531 Area* 532 BALMLayout::AddViewToTop(BView* view, YTab* top, XTab* left, XTab* right) 533 { 534 BLayoutItem* item = _LayoutItemToAdd(view); 535 Area* area = AddItemToTop(item, top, left, right); 536 if (!area) { 537 if (item != view->GetLayout()) 538 delete item; 539 return NULL; 540 } 541 return area; 542 } 543 544 545 Area* 546 BALMLayout::AddViewToBottom(BView* view, YTab* bottom, XTab* left, XTab* right) 547 { 548 BLayoutItem* item = _LayoutItemToAdd(view); 549 Area* area = AddItemToBottom(item, bottom, left, right); 550 if (!area) { 551 if (item != view->GetLayout()) 552 delete item; 553 return NULL; 554 } 555 return area; 556 } 557 558 559 bool 560 BALMLayout::AddItem(BLayoutItem* item) 561 { 562 return AddItem(-1, item); 563 } 564 565 566 bool 567 BALMLayout::AddItem(int32 index, BLayoutItem* item) 568 { 569 if (!item) 570 return false; 571 572 // simply add the item at the upper right corner of the previous item 573 // TODO maybe find a more elegant solution 574 XTab* left = Left(); 575 YTab* top = Top(); 576 577 // check range 578 if (index < 0 || index > CountItems()) 579 index = CountItems(); 580 581 // for index = 0 we already have set the right tabs 582 if (index != 0) { 583 BLayoutItem* prevItem = ItemAt(index - 1); 584 Area* area = AreaFor(prevItem); 585 if (area) { 586 left = area->Right(); 587 top = area->Top(); 588 } 589 } 590 Area* area = AddItem(item, left, top); 591 return area ? true : false; 592 } 593 594 595 Area* 596 BALMLayout::AddItem(BLayoutItem* item, XTab* left, YTab* top, XTab* _right, 597 YTab* _bottom) 598 { 599 BReference<XTab> right = _right; 600 if (right.Get() == NULL) 601 right = AddXTab(); 602 BReference<YTab> bottom = _bottom; 603 if (bottom.Get() == NULL) 604 bottom = AddYTab(); 605 606 // Area is added int ItemAdded 607 if (!BAbstractLayout::AddItem(-1, item)) 608 return NULL; 609 Area* area = AreaFor(item); 610 if (!area) 611 return NULL; 612 fCurrentArea = area; 613 614 area->_Init(fSolver, left, top, right, bottom, fRowColumnManager); 615 616 fRowColumnManager->AddArea(area); 617 return area; 618 } 619 620 621 Area* 622 BALMLayout::AddItem(BLayoutItem* item, Row* row, Column* column) 623 { 624 if (!BAbstractLayout::AddItem(-1, item)) 625 return NULL; 626 Area* area = AreaFor(item); 627 if (!area) 628 return NULL; 629 fCurrentArea = area; 630 631 area->_Init(fSolver, row, column, fRowColumnManager); 632 633 fRowColumnManager->AddArea(area); 634 return area; 635 } 636 637 638 Area* 639 BALMLayout::AddItemToRight(BLayoutItem* item, XTab* _right, YTab* top, 640 YTab* bottom) 641 { 642 if (fCurrentArea == NULL) 643 return NULL; 644 645 XTab* left = fCurrentArea->Right(); 646 BReference<XTab> right = _right; 647 if (_right == NULL) 648 right = AddXTab(); 649 if (!top) 650 top = fCurrentArea->Top(); 651 if (!bottom) 652 bottom = fCurrentArea->Bottom(); 653 654 return AddItem(item, left, top, right, bottom); 655 } 656 657 658 Area* 659 BALMLayout::AddItemToLeft(BLayoutItem* item, XTab* _left, YTab* top, 660 YTab* bottom) 661 { 662 if (fCurrentArea == NULL) 663 return NULL; 664 665 BReference<XTab> left = _left; 666 if (_left == NULL) 667 left = AddXTab(); 668 XTab* right = fCurrentArea->Left(); 669 if (!top) 670 top = fCurrentArea->Top(); 671 if (!bottom) 672 bottom = fCurrentArea->Bottom(); 673 674 return AddItem(item, left, top, right, bottom); 675 } 676 677 678 Area* 679 BALMLayout::AddItemToTop(BLayoutItem* item, YTab* _top, XTab* left, XTab* right) 680 { 681 if (fCurrentArea == NULL) 682 return NULL; 683 684 if (!left) 685 left = fCurrentArea->Left(); 686 if (!right) 687 right = fCurrentArea->Right(); 688 BReference<YTab> top = _top; 689 if (_top == NULL) 690 top = AddYTab(); 691 YTab* bottom = fCurrentArea->Top(); 692 693 return AddItem(item, left, top, right, bottom); 694 } 695 696 697 Area* 698 BALMLayout::AddItemToBottom(BLayoutItem* item, YTab* _bottom, XTab* left, 699 XTab* right) 700 { 701 if (fCurrentArea == NULL) 702 return NULL; 703 704 if (!left) 705 left = fCurrentArea->Left(); 706 if (!right) 707 right = fCurrentArea->Right(); 708 YTab* top = fCurrentArea->Bottom(); 709 BReference<YTab> bottom = _bottom; 710 if (_bottom == NULL) 711 bottom = AddYTab(); 712 713 return AddItem(item, left, top, right, bottom); 714 } 715 716 717 enum { 718 kLeftBorderIndex = -2, 719 kTopBorderIndex = -3, 720 kRightBorderIndex = -4, 721 kBottomBorderIndex = -5, 722 }; 723 724 725 bool 726 BALMLayout::SaveLayout(BMessage* archive) const 727 { 728 archive->MakeEmpty(); 729 730 archive->AddInt32("nXTabs", CountXTabs()); 731 archive->AddInt32("nYTabs", CountYTabs()); 732 733 XTabList xTabs = fXTabList; 734 xTabs.RemoveItem(fLeft); 735 xTabs.RemoveItem(fRight); 736 YTabList yTabs = fYTabList; 737 yTabs.RemoveItem(fTop); 738 yTabs.RemoveItem(fBottom); 739 740 int32 nAreas = CountAreas(); 741 for (int32 i = 0; i < nAreas; i++) { 742 Area* area = AreaAt(i); 743 if (area->Left() == fLeft) 744 archive->AddInt32("left", kLeftBorderIndex); 745 else 746 archive->AddInt32("left", xTabs.IndexOf(area->Left())); 747 if (area->Top() == fTop) 748 archive->AddInt32("top", kTopBorderIndex); 749 else 750 archive->AddInt32("top", yTabs.IndexOf(area->Top())); 751 if (area->Right() == fRight) 752 archive->AddInt32("right", kRightBorderIndex); 753 else 754 archive->AddInt32("right", xTabs.IndexOf(area->Right())); 755 if (area->Bottom() == fBottom) 756 archive->AddInt32("bottom", kBottomBorderIndex); 757 else 758 archive->AddInt32("bottom", yTabs.IndexOf(area->Bottom())); 759 } 760 return true; 761 } 762 763 764 bool 765 BALMLayout::RestoreLayout(const BMessage* archive) 766 { 767 int32 neededXTabs; 768 int32 neededYTabs; 769 if (archive->FindInt32("nXTabs", &neededXTabs) != B_OK) 770 return false; 771 if (archive->FindInt32("nYTabs", &neededYTabs) != B_OK) 772 return false; 773 // First store a reference to all needed tabs otherwise they might get lost 774 // while editing the layout 775 std::vector<BReference<XTab> > newXTabs; 776 std::vector<BReference<YTab> > newYTabs; 777 int32 existingXTabs = fXTabList.CountItems(); 778 for (int32 i = 0; i < neededXTabs; i++) { 779 if (i < existingXTabs) 780 newXTabs.push_back(BReference<XTab>(fXTabList.ItemAt(i))); 781 else 782 newXTabs.push_back(AddXTab()); 783 } 784 int32 existingYTabs = fYTabList.CountItems(); 785 for (int32 i = 0; i < neededYTabs; i++) { 786 if (i < existingYTabs) 787 newYTabs.push_back(BReference<YTab>(fYTabList.ItemAt(i))); 788 else 789 newYTabs.push_back(AddYTab()); 790 } 791 792 XTabList xTabs = fXTabList; 793 xTabs.RemoveItem(fLeft); 794 xTabs.RemoveItem(fRight); 795 YTabList yTabs = fYTabList; 796 yTabs.RemoveItem(fTop); 797 yTabs.RemoveItem(fBottom); 798 799 int32 nAreas = CountAreas(); 800 for (int32 i = 0; i < nAreas; i++) { 801 Area* area = AreaAt(i); 802 if (area == NULL) 803 return false; 804 int32 left = -1; 805 if (archive->FindInt32("left", i, &left) != B_OK) 806 break; 807 int32 top = archive->FindInt32("top", i); 808 int32 right = archive->FindInt32("right", i); 809 int32 bottom = archive->FindInt32("bottom", i); 810 811 XTab* leftTab = NULL; 812 YTab* topTab = NULL; 813 XTab* rightTab = NULL; 814 YTab* bottomTab = NULL; 815 816 if (left == kLeftBorderIndex) 817 leftTab = fLeft; 818 else 819 leftTab = xTabs.ItemAt(left); 820 if (top == kTopBorderIndex) 821 topTab = fTop; 822 else 823 topTab = yTabs.ItemAt(top); 824 if (right == kRightBorderIndex) 825 rightTab = fRight; 826 else 827 rightTab = xTabs.ItemAt(right); 828 if (bottom == kBottomBorderIndex) 829 bottomTab = fBottom; 830 else 831 bottomTab = yTabs.ItemAt(bottom); 832 if (leftTab == NULL || topTab == NULL || rightTab == NULL 833 || bottomTab == NULL) 834 return false; 835 836 area->SetLeft(leftTab); 837 area->SetTop(topTab); 838 area->SetRight(rightTab); 839 area->SetBottom(bottomTab); 840 } 841 return true; 842 } 843 844 845 /** 846 * Gets the left variable. 847 */ 848 XTab* 849 BALMLayout::Left() const 850 { 851 return fLeft; 852 } 853 854 855 /** 856 * Gets the right variable. 857 */ 858 XTab* 859 BALMLayout::Right() const 860 { 861 return fRight; 862 } 863 864 865 /** 866 * Gets the top variable. 867 */ 868 YTab* 869 BALMLayout::Top() const 870 { 871 return fTop; 872 } 873 874 875 /** 876 * Gets the bottom variable. 877 */ 878 YTab* 879 BALMLayout::Bottom() const 880 { 881 return fBottom; 882 } 883 884 885 /** 886 * Gets minimum size. 887 */ 888 BSize 889 BALMLayout::BaseMinSize() { 890 if (fMinSize == kUnsetSize) 891 fMinSize = _CalculateMinSize(); 892 return fMinSize; 893 } 894 895 896 /** 897 * Gets maximum size. 898 */ 899 BSize 900 BALMLayout::BaseMaxSize() 901 { 902 if (fMaxSize == kUnsetSize) 903 fMaxSize = _CalculateMaxSize(); 904 return fMaxSize; 905 } 906 907 908 /** 909 * Gets preferred size. 910 */ 911 BSize 912 BALMLayout::BasePreferredSize() 913 { 914 if (fPreferredSize == kUnsetSize) 915 fPreferredSize = _CalculatePreferredSize(); 916 return fPreferredSize; 917 } 918 919 920 /** 921 * Gets the alignment. 922 */ 923 BAlignment 924 BALMLayout::BaseAlignment() 925 { 926 BAlignment alignment; 927 alignment.SetHorizontal(B_ALIGN_HORIZONTAL_CENTER); 928 alignment.SetVertical(B_ALIGN_VERTICAL_CENTER); 929 return alignment; 930 } 931 932 933 /** 934 * Invalidates the layout. 935 * Resets minimum/maximum/preferred size. 936 */ 937 void 938 BALMLayout::LayoutInvalidated(bool children) 939 { 940 fMinSize = kUnsetSize; 941 fMaxSize = kUnsetSize; 942 fPreferredSize = kUnsetSize; 943 } 944 945 946 bool 947 BALMLayout::ItemAdded(BLayoutItem* item, int32 atIndex) 948 { 949 item->SetLayoutData(new(std::nothrow) Area(item)); 950 return item->LayoutData() != NULL; 951 } 952 953 954 void 955 BALMLayout::ItemRemoved(BLayoutItem* item, int32 fromIndex) 956 { 957 if (Area* area = AreaFor(item)) { 958 fRowColumnManager->RemoveArea(area); 959 item->SetLayoutData(NULL); 960 delete area; 961 } 962 } 963 964 965 /** 966 * Calculate and set the layout. 967 * If no layout specification is given, a specification is reverse engineered automatically. 968 */ 969 void 970 BALMLayout::DoLayout() 971 { 972 _UpdateAreaConstraints(); 973 974 // Enforced absolute positions of Right and Bottom 975 BRect area(LayoutArea()); 976 Right()->SetRange(area.right, area.right); 977 Bottom()->SetRange(area.bottom, area.bottom); 978 979 fSolver->Solve(); 980 981 // if new layout is infeasible, use previous layout 982 if (fSolver->Result() == kInfeasible) 983 return; 984 985 if (fSolver->Result() != kOptimal) { 986 fSolver->Save("failed-layout.txt"); 987 printf("Could not solve the layout specification (%d). ", 988 fSolver->Result()); 989 printf("Saved specification in file failed-layout.txt\n"); 990 } 991 992 // set the calculated positions and sizes for every area 993 for (int32 i = 0; i < CountItems(); i++) 994 AreaFor(ItemAt(i))->_DoLayout(); 995 } 996 997 998 LinearSpec* 999 BALMLayout::Solver() const 1000 { 1001 return const_cast<LinearSpec*>(fSolver); 1002 } 1003 1004 1005 void 1006 BALMLayout::SetInsets(float left, float top, float right, 1007 float bottom) 1008 { 1009 fLeftInset = BControlLook::ComposeSpacing(left); 1010 fTopInset = BControlLook::ComposeSpacing(top); 1011 fRightInset = BControlLook::ComposeSpacing(right); 1012 fBottomInset = BControlLook::ComposeSpacing(bottom); 1013 1014 InvalidateLayout(); 1015 } 1016 1017 1018 void 1019 BALMLayout::SetInsets(float horizontal, float vertical) 1020 { 1021 fLeftInset = BControlLook::ComposeSpacing(horizontal); 1022 fRightInset = fLeftInset; 1023 1024 fTopInset = BControlLook::ComposeSpacing(vertical); 1025 fBottomInset = fTopInset; 1026 1027 InvalidateLayout(); 1028 } 1029 1030 1031 void 1032 BALMLayout::SetInsets(float insets) 1033 { 1034 fLeftInset = BControlLook::ComposeSpacing(insets); 1035 fRightInset = fLeftInset; 1036 fTopInset = fLeftInset; 1037 fBottomInset = fLeftInset; 1038 1039 InvalidateLayout(); 1040 } 1041 1042 1043 void 1044 BALMLayout::GetInsets(float* left, float* top, float* right, 1045 float* bottom) const 1046 { 1047 if (left) 1048 *left = fLeftInset; 1049 if (top) 1050 *top = fTopInset; 1051 if (right) 1052 *right = fRightInset; 1053 if (bottom) 1054 *bottom = fBottomInset; 1055 } 1056 1057 1058 void 1059 BALMLayout::SetSpacing(float hSpacing, float vSpacing) 1060 { 1061 fHSpacing = BControlLook::ComposeSpacing(hSpacing); 1062 fVSpacing = BControlLook::ComposeSpacing(vSpacing); 1063 } 1064 1065 1066 void 1067 BALMLayout::GetSpacing(float *_hSpacing, float *_vSpacing) const 1068 { 1069 if (_hSpacing) 1070 *_hSpacing = fHSpacing; 1071 if (_vSpacing) 1072 *_vSpacing = fVSpacing; 1073 } 1074 1075 1076 float 1077 BALMLayout::InsetForTab(XTab* tab) 1078 { 1079 if (tab == fLeft.Get()) 1080 return fLeftInset; 1081 if (tab == fRight.Get()) 1082 return fRightInset; 1083 return fHSpacing / 2; 1084 } 1085 1086 1087 float 1088 BALMLayout::InsetForTab(YTab* tab) 1089 { 1090 if (tab == fTop.Get()) 1091 return fTopInset; 1092 if (tab == fBottom.Get()) 1093 return fBottomInset; 1094 return fVSpacing / 2; 1095 } 1096 1097 1098 BLayoutItem* 1099 BALMLayout::_LayoutItemToAdd(BView* view) 1100 { 1101 if (view->GetLayout()) 1102 return view->GetLayout(); 1103 return new(std::nothrow) BViewLayoutItem(view); 1104 } 1105 1106 1107 /** 1108 * Caculates the miminum size. 1109 */ 1110 BSize 1111 BALMLayout::_CalculateMinSize() 1112 { 1113 _UpdateAreaConstraints(); 1114 1115 Right()->SetRange(0, 20000); 1116 Bottom()->SetRange(0, 20000); 1117 1118 return fSolver->MinSize(Right(), Bottom()); 1119 } 1120 1121 1122 /** 1123 * Caculates the maximum size. 1124 */ 1125 BSize 1126 BALMLayout::_CalculateMaxSize() 1127 { 1128 _UpdateAreaConstraints(); 1129 1130 Right()->SetRange(0, 20000); 1131 Bottom()->SetRange(0, 20000); 1132 1133 return fSolver->MaxSize(Right(), Bottom()); 1134 } 1135 1136 1137 /** 1138 * Caculates the preferred size. 1139 */ 1140 BSize 1141 BALMLayout::_CalculatePreferredSize() 1142 { 1143 _UpdateAreaConstraints(); 1144 1145 Right()->SetRange(0, 20000); 1146 Bottom()->SetRange(0, 20000); 1147 1148 fSolver->Solve(); 1149 if (fSolver->Result() != kOptimal) { 1150 fSolver->Save("failed-layout.txt"); 1151 printf("Could not solve the layout specification (%d). " 1152 "Saved specification in file failed-layout.txt", fSolver->Result()); 1153 } 1154 1155 return BSize(Right()->Value() - Left()->Value(), 1156 Bottom()->Value() - Top()->Value()); 1157 } 1158 1159 1160 void 1161 BALMLayout::_UpdateAreaConstraints() 1162 { 1163 for (int i = 0; i < CountItems(); i++) 1164 AreaFor(ItemAt(i))->InvalidateSizeConstraints(); 1165 fRowColumnManager->UpdateConstraints(); 1166 } 1167