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