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