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 "Area.h" 10 11 #include <algorithm> // for max 12 13 #include <Button.h> 14 #include <CheckBox.h> 15 #include <PictureButton.h> 16 #include <RadioButton.h> 17 #include <StatusBar.h> 18 #include <StringView.h> 19 20 #include "ALMLayout.h" 21 22 23 using namespace LinearProgramming; 24 using namespace std; 25 26 27 GroupItem::GroupItem(BLayoutItem* item) 28 { 29 _Init(item, NULL); 30 } 31 32 33 GroupItem::GroupItem(BView* view) 34 { 35 _Init(NULL, view); 36 } 37 38 39 BLayoutItem* 40 GroupItem::LayoutItem() 41 { 42 return fLayoutItem; 43 } 44 45 46 BView* 47 GroupItem::View() 48 { 49 return fView; 50 } 51 52 53 const std::vector<GroupItem>& 54 GroupItem::GroupItems() 55 { 56 return fGroupItems; 57 } 58 59 60 enum orientation 61 GroupItem::Orientation() 62 { 63 return fOrientation; 64 } 65 66 67 GroupItem& 68 GroupItem::operator|(const GroupItem& right) 69 { 70 return _AddItem(right, B_HORIZONTAL); 71 } 72 73 74 GroupItem& 75 GroupItem::operator/(const GroupItem& bottom) 76 { 77 return _AddItem(bottom, B_VERTICAL); 78 } 79 80 81 GroupItem::GroupItem() 82 { 83 _Init(NULL, NULL); 84 } 85 86 87 void 88 GroupItem::_Init(BLayoutItem* item, BView* view, enum orientation orien) 89 { 90 fLayoutItem = item; 91 fView = view; 92 fOrientation = orien; 93 } 94 95 96 GroupItem& 97 GroupItem::_AddItem(const GroupItem& item, enum orientation orien) 98 { 99 if (fGroupItems.size() == 0) 100 fGroupItems.push_back(*this); 101 else if (fOrientation != orien) { 102 GroupItem clone = *this; 103 fGroupItems.clear(); 104 _Init(NULL, NULL, orien); 105 fGroupItems.push_back(clone); 106 } 107 108 _Init(NULL, NULL, orien); 109 fGroupItems.push_back(item); 110 return *this; 111 } 112 113 114 BView* 115 Area::View() 116 { 117 return fLayoutItem->View(); 118 } 119 120 121 /** 122 * Gets the left tab of the area. 123 * 124 * @return the left tab of the area 125 */ 126 XTab* 127 Area::Left() const 128 { 129 return fLeft; 130 } 131 132 133 /** 134 * Gets the right tab of the area. 135 * 136 * @return the right tab of the area 137 */ 138 XTab* 139 Area::Right() const 140 { 141 return fRight; 142 } 143 144 145 /** 146 * Gets the top tab of the area. 147 */ 148 YTab* 149 Area::Top() const 150 { 151 return fTop; 152 } 153 154 155 /** 156 * Gets the bottom tab of the area. 157 */ 158 YTab* 159 Area::Bottom() const 160 { 161 return fBottom; 162 } 163 164 165 /** 166 * Sets the left tab of the area. 167 * 168 * @param left the left tab of the area 169 */ 170 void 171 Area::SetLeft(XTab* left) 172 { 173 fLeft = left; 174 175 fColumn = NULL; 176 177 fMinContentWidth->SetLeftSide(-1.0, fLeft, 1.0, fRight); 178 BSize preferredSize = fLayoutItem->PreferredSize(); 179 _UpdatePreferredWidthConstraint(preferredSize); 180 181 if (fMaxContentWidth != NULL) 182 fMaxContentWidth->SetLeftSide(-1.0, fLeft, 1.0, fRight); 183 184 fLayoutItem->Layout()->InvalidateLayout(); 185 } 186 187 188 /** 189 * Sets the right tab of the area. 190 * 191 * @param right the right tab of the area 192 */ 193 void 194 Area::SetRight(XTab* right) 195 { 196 fRight = right; 197 198 fColumn = NULL; 199 200 fMinContentWidth->SetLeftSide(-1.0, fLeft, 1.0, fRight); 201 BSize preferredSize = fLayoutItem->PreferredSize(); 202 _UpdatePreferredWidthConstraint(preferredSize); 203 if (fMaxContentWidth != NULL) 204 fMaxContentWidth->SetLeftSide(-1.0, fLeft, 1.0, fRight); 205 206 fLayoutItem->Layout()->InvalidateLayout(); 207 } 208 209 210 /** 211 * Sets the top tab of the area. 212 */ 213 void 214 Area::SetTop(YTab* top) 215 { 216 fTop = top; 217 218 fRow = NULL; 219 220 fMinContentHeight->SetLeftSide(-1.0, fTop, 1.0, fBottom); 221 BSize preferredSize = fLayoutItem->PreferredSize(); 222 _UpdatePreferredHeightConstraint(preferredSize); 223 if (fMaxContentHeight != NULL) 224 fMaxContentHeight->SetLeftSide(-1.0, fTop, 1.0, fBottom); 225 226 fLayoutItem->Layout()->InvalidateLayout(); 227 } 228 229 230 /** 231 * Sets the bottom tab of the area. 232 */ 233 void 234 Area::SetBottom(YTab* bottom) 235 { 236 fBottom = bottom; 237 238 fRow = NULL; 239 240 fMinContentHeight->SetLeftSide(-1.0, fTop, 1.0, fBottom); 241 BSize preferredSize = fLayoutItem->PreferredSize(); 242 _UpdatePreferredHeightConstraint(preferredSize); 243 if (fMaxContentHeight != NULL) 244 fMaxContentHeight->SetLeftSide(-1.0, fTop, 1.0, fBottom); 245 246 fLayoutItem->Layout()->InvalidateLayout(); 247 } 248 249 250 /** 251 * Gets the row that defines the top and bottom tabs. 252 */ 253 Row* 254 Area::GetRow() const 255 { 256 return fRow; 257 } 258 259 260 /** 261 * Gets the column that defines the left and right tabs. 262 */ 263 Column* 264 Area::GetColumn() const 265 { 266 return fColumn; 267 } 268 269 270 /** 271 * Sets the row that defines the top and bottom tabs. 272 * May be null. 273 */ 274 void 275 Area::SetRow(Row* row) 276 { 277 SetTop(row->Top()); 278 SetBottom(row->Bottom()); 279 fRow = row; 280 fLayoutItem->Layout()->InvalidateLayout(); 281 } 282 283 284 /** 285 * Sets the column that defines the left and right tabs. 286 * May be null. 287 */ 288 void 289 Area::SetColumn(Column* column) 290 { 291 SetLeft(column->Left()); 292 SetRight(column->Right()); 293 fColumn = column; 294 fLayoutItem->Layout()->InvalidateLayout(); 295 } 296 297 298 /** 299 * The reluctance with which the area's content shrinks below its preferred size. 300 * The bigger the less likely is such shrinking. 301 */ 302 BSize 303 Area::ShrinkPenalties() const 304 { 305 return fShrinkPenalties; 306 } 307 308 309 /** 310 * The reluctance with which the area's content grows over its preferred size. 311 * The bigger the less likely is such growth. 312 */ 313 BSize 314 Area::GrowPenalties() const 315 { 316 return fGrowPenalties; 317 } 318 319 320 void Area::SetShrinkPenalties(BSize shrink) { 321 fShrinkPenalties = shrink; 322 if (fPreferredContentWidth != NULL) { 323 fPreferredContentWidth->SetPenaltyNeg(shrink.Width()); 324 fPreferredContentHeight->SetPenaltyNeg(shrink.Height()); 325 } 326 fLayoutItem->Layout()->InvalidateLayout(); 327 } 328 329 330 void 331 Area::SetGrowPenalties(BSize grow) 332 { 333 fGrowPenalties = grow; 334 if (fPreferredContentWidth != NULL) { 335 fPreferredContentWidth->SetPenaltyPos(grow.Width()); 336 fPreferredContentHeight->SetPenaltyPos(grow.Height()); 337 } 338 fLayoutItem->Layout()->InvalidateLayout(); 339 } 340 341 342 /** 343 * Gets aspect ratio of the area's content. 344 */ 345 double 346 Area::ContentAspectRatio() const 347 { 348 return fContentAspectRatio; 349 } 350 351 352 /** 353 * Sets aspect ratio of the area's content. 354 * May be different from the aspect ratio of the area. 355 */ 356 void 357 Area::SetContentAspectRatio(double ratio) 358 { 359 fContentAspectRatio = ratio; 360 if (fContentAspectRatio <= 0) { 361 delete fContentAspectRatioC; 362 fContentAspectRatioC = NULL; 363 } else if (fContentAspectRatioC == NULL) { 364 fContentAspectRatioC = fLS->AddConstraint(-1.0, fLeft, 1.0, fRight, 365 ratio, fTop, -ratio, fBottom, kEQ, 0.0); 366 fConstraints.AddItem(fContentAspectRatioC); 367 } else { 368 fContentAspectRatioC->SetLeftSide(-1.0, fLeft, 1.0, fRight, ratio, 369 fTop, -ratio, fBottom); 370 } 371 fLayoutItem->Layout()->InvalidateLayout(); 372 } 373 374 375 /** 376 * Gets left inset between area and its content. 377 */ 378 float 379 Area::LeftInset() const 380 { 381 if (fTopLeftInset.IsWidthSet()) 382 return fTopLeftInset.Width(); 383 384 BALMLayout* layout = static_cast<BALMLayout*>(fLayoutItem->Layout()); 385 if (fLeft == layout->Left()) 386 return layout->Inset(); 387 return layout->Spacing() / 2; 388 } 389 390 391 /** 392 * Gets top inset between area and its content. 393 */ 394 float 395 Area::TopInset() const 396 { 397 if (fTopLeftInset.IsHeightSet()) 398 return fTopLeftInset.Height(); 399 400 BALMLayout* layout = static_cast<BALMLayout*>(fLayoutItem->Layout()); 401 if (fTop == layout->Top()) 402 return layout->Inset(); 403 return layout->Spacing() / 2; 404 } 405 406 407 /** 408 * Gets right inset between area and its content. 409 */ 410 float 411 Area::RightInset() const 412 { 413 if (fRightBottomInset.IsWidthSet()) 414 return fRightBottomInset.Width(); 415 416 BALMLayout* layout = static_cast<BALMLayout*>(fLayoutItem->Layout()); 417 if (fRight == layout->Right()) 418 return layout->Inset(); 419 return layout->Spacing() / 2; 420 } 421 422 423 /** 424 * Gets bottom inset between area and its content. 425 */ 426 float 427 Area::BottomInset() const 428 { 429 if (fRightBottomInset.IsHeightSet()) 430 return fRightBottomInset.Height(); 431 432 BALMLayout* layout = static_cast<BALMLayout*>(fLayoutItem->Layout()); 433 if (fBottom == layout->Bottom()) 434 return layout->Inset(); 435 return layout->Spacing() / 2; 436 } 437 438 439 /** 440 * Sets left inset between area and its content. 441 */ 442 void 443 Area::SetLeftInset(float left) 444 { 445 fTopLeftInset.width = left; 446 fLayoutItem->Layout()->InvalidateLayout(); 447 } 448 449 450 /** 451 * Sets top inset between area and its content. 452 */ 453 void 454 Area::SetTopInset(float top) 455 { 456 fTopLeftInset.height = top; 457 fLayoutItem->Layout()->InvalidateLayout(); 458 } 459 460 461 /** 462 * Sets right inset between area and its content. 463 */ 464 void 465 Area::SetRightInset(float right) 466 { 467 fRightBottomInset.width = right; 468 fLayoutItem->Layout()->InvalidateLayout(); 469 } 470 471 472 /** 473 * Sets bottom inset between area and its content. 474 */ 475 void 476 Area::SetBottomInset(float bottom) 477 { 478 fRightBottomInset.height = bottom; 479 fLayoutItem->Layout()->InvalidateLayout(); 480 } 481 482 483 Area::operator BString() const 484 { 485 BString string; 486 GetString(string); 487 return string; 488 } 489 490 491 void 492 Area::GetString(BString& string) const 493 { 494 string << "Area("; 495 fLeft->GetString(string); 496 string << ", "; 497 fTop->GetString(string); 498 string << ", "; 499 fRight->GetString(string); 500 string << ", "; 501 fBottom->GetString(string); 502 string << ")"; 503 } 504 505 506 /*! 507 * Sets the width of the area to be the same as the width of the given area 508 * times factor. 509 * 510 * @param area the area that should have the same width 511 * @return the same-width constraint 512 */ 513 Constraint* 514 Area::SetWidthAs(Area* area, float factor) 515 { 516 return fLS->AddConstraint(-1.0, fLeft, 1.0, fRight, factor, area->Left(), 517 -factor, area->Right(), kEQ, 0.0); 518 } 519 520 521 /*! 522 * Sets the height of the area to be the same as the height of the given area 523 * times factor. 524 * 525 * @param area the area that should have the same height 526 * @return the same-height constraint 527 */ 528 Constraint* 529 Area::SetHeightAs(Area* area, float factor) 530 { 531 return fLS->AddConstraint(-1.0, fTop, 1.0, fBottom, factor, area->Top(), 532 -factor, area->Bottom(), kEQ, 0.0); 533 } 534 535 536 void 537 Area::InvalidateSizeConstraints() 538 { 539 // check if if we are initialized 540 if (!fLeft) 541 return; 542 543 BSize minSize = fLayoutItem->MinSize(); 544 BSize maxSize = fLayoutItem->MaxSize(); 545 BSize prefSize = fLayoutItem->PreferredSize(); 546 547 _UpdateMinSizeConstraint(minSize); 548 _UpdateMaxSizeConstraint(maxSize); 549 _UpdatePreferredWidthConstraint(prefSize); 550 _UpdatePreferredHeightConstraint(prefSize); 551 } 552 553 554 /** 555 * Destructor. 556 * Removes the area from its specification. 557 */ 558 Area::~Area() 559 { 560 for (int32 i = 0; i < fConstraints.CountItems(); i++) 561 delete (Constraint*)fConstraints.ItemAt(i); 562 } 563 564 565 /** 566 * Constructor. 567 * Uses XTabs and YTabs. 568 */ 569 Area::Area(BLayoutItem* item) 570 : 571 fLayoutItem(item), 572 573 fLS(NULL), 574 fLeft(NULL), 575 fRight(NULL), 576 fTop(NULL), 577 fBottom(NULL), 578 579 fRow(NULL), 580 fColumn(NULL), 581 582 fShrinkPenalties(5, 5), 583 fGrowPenalties(5, 5), 584 585 fMinContentWidth(NULL), 586 fMaxContentWidth(NULL), 587 fMinContentHeight(NULL), 588 fMaxContentHeight(NULL), 589 fPreferredContentWidth(NULL), 590 fPreferredContentHeight(NULL), 591 592 fContentAspectRatio(-1), 593 fContentAspectRatioC(NULL) 594 { 595 596 } 597 598 599 /** 600 * Initialize variables. 601 */ 602 void 603 Area::_Init(LinearSpec* ls, XTab* left, YTab* top, XTab* right, YTab* bottom, 604 Variable* scaleWidth, Variable* scaleHeight) 605 { 606 fLS = ls; 607 fLeft = left; 608 fRight = right; 609 fTop = top; 610 fBottom = bottom; 611 612 fScaleWidth = scaleWidth; 613 fScaleHeight = scaleHeight; 614 615 // adds the two essential constraints of the area that make sure that the 616 // left x-tab is really to the left of the right x-tab, and the top y-tab 617 // really above the bottom y-tab 618 fMinContentWidth = ls->AddConstraint(-1.0, fLeft, 1.0, fRight, kGE, 0); 619 fMinContentHeight = ls->AddConstraint(-1.0, fTop, 1.0, fBottom, kGE, 0); 620 621 fConstraints.AddItem(fMinContentWidth); 622 fConstraints.AddItem(fMinContentHeight); 623 624 fPreferredContentWidth = fLS->AddConstraint(-1.0, fLeft, 1.0, fRight, -1.0, 625 fScaleWidth, kEQ, 0, fShrinkPenalties.Width(), 626 fGrowPenalties.Width()); 627 628 fPreferredContentHeight = fLS->AddConstraint(-1.0, fTop, 1.0, fBottom, -1.0, 629 fScaleHeight, kEQ, 0, fShrinkPenalties.Height(), 630 fGrowPenalties.Height()); 631 632 fConstraints.AddItem(fPreferredContentWidth); 633 fConstraints.AddItem(fPreferredContentHeight); 634 } 635 636 637 void 638 Area::_Init(LinearSpec* ls, Row* row, Column* column, Variable* scaleWidth, 639 Variable* scaleHeight) 640 { 641 _Init(ls, column->Left(), row->Top(), column->Right(), row->Bottom(), 642 scaleWidth, scaleHeight); 643 fRow = row; 644 fColumn = column; 645 } 646 647 648 /** 649 * Perform layout on the area. 650 */ 651 void 652 Area::_DoLayout() 653 { 654 // check if if we are initialized 655 if (!fLeft) 656 return; 657 658 BRect areaFrame(round(fLeft->Value()), round(fTop->Value()), 659 round(fRight->Value()), round(fBottom->Value())); 660 areaFrame.left += LeftInset(); 661 areaFrame.right -= RightInset(); 662 areaFrame.top += TopInset(); 663 areaFrame.bottom -= BottomInset(); 664 665 fLayoutItem->AlignInFrame(areaFrame); 666 } 667 668 669 void 670 Area::_UpdateMinSizeConstraint(BSize min) 671 { 672 float width = 0.; 673 float height = 0.; 674 if (min.width > 0) 675 width = min.Width() + LeftInset() + RightInset(); 676 if (min.height > 0) 677 height = min.Height() + TopInset() + BottomInset(); 678 679 fMinContentWidth->SetRightSide(width); 680 fMinContentHeight->SetRightSide(height); 681 } 682 683 684 void 685 Area::_UpdateMaxSizeConstraint(BSize max) 686 { 687 max.width += LeftInset() + RightInset(); 688 max.height += TopInset() + BottomInset(); 689 690 // we only need max constraints if the alignment is full height/width 691 // otherwise we can just align the item in the free space 692 BAlignment alignment = fLayoutItem->Alignment(); 693 if (alignment.Vertical() == B_ALIGN_USE_FULL_HEIGHT) { 694 if (fMaxContentHeight == NULL) { 695 fMaxContentHeight = fLS->AddConstraint(-1.0, fTop, 1.0, fBottom, 696 kLE, max.Height()); 697 fConstraints.AddItem(fMaxContentHeight); 698 } else 699 fMaxContentHeight->SetRightSide(max.Height()); 700 } 701 else { 702 fConstraints.RemoveItem(fMaxContentHeight); 703 delete fMaxContentHeight; 704 fMaxContentHeight = NULL; 705 } 706 707 if (alignment.Horizontal() == B_ALIGN_USE_FULL_WIDTH) { 708 if (fMaxContentWidth == NULL) { 709 fMaxContentWidth = fLS->AddConstraint(-1.0, fLeft, 1.0, fRight, kLE, 710 max.Width()); 711 fConstraints.AddItem(fMaxContentWidth); 712 } else 713 fMaxContentWidth->SetRightSide(max.Width()); 714 } 715 else { 716 fConstraints.RemoveItem(fMaxContentWidth); 717 delete fMaxContentWidth; 718 fMaxContentWidth = NULL; 719 } 720 } 721 722 723 void 724 Area::_UpdatePreferredWidthConstraint(BSize& preferred) 725 { 726 float width = 32000; 727 if (preferred.width > 0) 728 width = preferred.Width() + LeftInset() + RightInset(); 729 730 fPreferredContentWidth->SetLeftSide(-1.0, fLeft, 1.0, fRight, -width, 731 fScaleWidth); 732 } 733 734 735 void 736 Area::_UpdatePreferredHeightConstraint(BSize& preferred) 737 { 738 float height = 32000; 739 if (preferred.height > 0) 740 height = preferred.Height() + TopInset() + BottomInset(); 741 742 fPreferredContentHeight->SetLeftSide(-1.0, fTop, 1.0, fBottom, -height, 743 fScaleHeight); 744 } 745