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