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 fMinContentWidth->SetLeftSide(-1.0, fLeft, 1.0, fRight); 177 if (fMaxContentWidth != NULL) 178 fMaxContentWidth->SetLeftSide(-1.0, fLeft, 1.0, fRight); 179 fRowColumnManager->TabsChanged(this); 180 181 fLayoutItem->Layout()->InvalidateLayout(); 182 } 183 184 185 /** 186 * Sets the right tab of the area. 187 * 188 * @param right the right tab of the area 189 */ 190 void 191 Area::SetRight(XTab* right) 192 { 193 fRight = right; 194 195 fMinContentWidth->SetLeftSide(-1.0, fLeft, 1.0, fRight); 196 if (fMaxContentWidth != NULL) 197 fMaxContentWidth->SetLeftSide(-1.0, fLeft, 1.0, fRight); 198 fRowColumnManager->TabsChanged(this); 199 200 fLayoutItem->Layout()->InvalidateLayout(); 201 } 202 203 204 /** 205 * Sets the top tab of the area. 206 */ 207 void 208 Area::SetTop(YTab* top) 209 { 210 fTop = top; 211 212 fMinContentHeight->SetLeftSide(-1.0, fTop, 1.0, fBottom); 213 if (fMaxContentHeight != NULL) 214 fMaxContentHeight->SetLeftSide(-1.0, fTop, 1.0, fBottom); 215 fRowColumnManager->TabsChanged(this); 216 217 fLayoutItem->Layout()->InvalidateLayout(); 218 } 219 220 221 /** 222 * Sets the bottom tab of the area. 223 */ 224 void 225 Area::SetBottom(YTab* bottom) 226 { 227 fBottom = bottom; 228 229 fMinContentHeight->SetLeftSide(-1.0, fTop, 1.0, fBottom); 230 if (fMaxContentHeight != NULL) 231 fMaxContentHeight->SetLeftSide(-1.0, fTop, 1.0, fBottom); 232 fRowColumnManager->TabsChanged(this); 233 234 fLayoutItem->Layout()->InvalidateLayout(); 235 } 236 237 238 /** 239 * Gets the row that defines the top and bottom tabs. 240 */ 241 Row* 242 Area::GetRow() const 243 { 244 return fRow; 245 } 246 247 248 /** 249 * Gets the column that defines the left and right tabs. 250 */ 251 Column* 252 Area::GetColumn() const 253 { 254 return fColumn; 255 } 256 257 258 /** 259 * The reluctance with which the area's content shrinks below its preferred size. 260 * The bigger the less likely is such shrinking. 261 */ 262 BSize 263 Area::ShrinkPenalties() const 264 { 265 return fShrinkPenalties; 266 } 267 268 269 /** 270 * The reluctance with which the area's content grows over its preferred size. 271 * The bigger the less likely is such growth. 272 */ 273 BSize 274 Area::GrowPenalties() const 275 { 276 return fGrowPenalties; 277 } 278 279 280 void 281 Area::SetShrinkPenalties(BSize shrink) { 282 fShrinkPenalties = shrink; 283 284 fLayoutItem->Layout()->InvalidateLayout(); 285 } 286 287 288 void 289 Area::SetGrowPenalties(BSize grow) 290 { 291 fGrowPenalties = grow; 292 293 fLayoutItem->Layout()->InvalidateLayout(); 294 } 295 296 297 /** 298 * Gets aspect ratio of the area's content. 299 */ 300 double 301 Area::ContentAspectRatio() const 302 { 303 return fContentAspectRatio; 304 } 305 306 307 /** 308 * Sets aspect ratio of the area's content. 309 * May be different from the aspect ratio of the area. 310 */ 311 void 312 Area::SetContentAspectRatio(double ratio) 313 { 314 fContentAspectRatio = ratio; 315 if (fContentAspectRatio <= 0) { 316 delete fContentAspectRatioC; 317 fContentAspectRatioC = NULL; 318 } else if (fContentAspectRatioC == NULL) { 319 fContentAspectRatioC = fLS->AddConstraint(-1.0, fLeft, 1.0, fRight, 320 ratio, fTop, -ratio, fBottom, kEQ, 0.0); 321 fConstraints.AddItem(fContentAspectRatioC); 322 } else { 323 fContentAspectRatioC->SetLeftSide(-1.0, fLeft, 1.0, fRight, ratio, 324 fTop, -ratio, fBottom); 325 } 326 fLayoutItem->Layout()->InvalidateLayout(); 327 } 328 329 330 /** 331 * Gets left inset between area and its content. 332 */ 333 float 334 Area::LeftInset() const 335 { 336 if (fTopLeftInset.IsWidthSet()) 337 return fTopLeftInset.Width(); 338 339 BALMLayout* layout = static_cast<BALMLayout*>(fLayoutItem->Layout()); 340 if (fLeft == layout->Left()) 341 return layout->Inset(); 342 return layout->Spacing() / 2; 343 } 344 345 346 /** 347 * Gets top inset between area and its content. 348 */ 349 float 350 Area::TopInset() const 351 { 352 if (fTopLeftInset.IsHeightSet()) 353 return fTopLeftInset.Height(); 354 355 BALMLayout* layout = static_cast<BALMLayout*>(fLayoutItem->Layout()); 356 if (fTop == layout->Top()) 357 return layout->Inset(); 358 return layout->Spacing() / 2; 359 } 360 361 362 /** 363 * Gets right inset between area and its content. 364 */ 365 float 366 Area::RightInset() const 367 { 368 if (fRightBottomInset.IsWidthSet()) 369 return fRightBottomInset.Width(); 370 371 BALMLayout* layout = static_cast<BALMLayout*>(fLayoutItem->Layout()); 372 if (fRight == layout->Right()) 373 return layout->Inset(); 374 return layout->Spacing() / 2; 375 } 376 377 378 /** 379 * Gets bottom inset between area and its content. 380 */ 381 float 382 Area::BottomInset() const 383 { 384 if (fRightBottomInset.IsHeightSet()) 385 return fRightBottomInset.Height(); 386 387 BALMLayout* layout = static_cast<BALMLayout*>(fLayoutItem->Layout()); 388 if (fBottom == layout->Bottom()) 389 return layout->Inset(); 390 return layout->Spacing() / 2; 391 } 392 393 394 /** 395 * Sets left inset between area and its content. 396 */ 397 void 398 Area::SetLeftInset(float left) 399 { 400 fTopLeftInset.width = left; 401 fLayoutItem->Layout()->InvalidateLayout(); 402 } 403 404 405 /** 406 * Sets top inset between area and its content. 407 */ 408 void 409 Area::SetTopInset(float top) 410 { 411 fTopLeftInset.height = top; 412 fLayoutItem->Layout()->InvalidateLayout(); 413 } 414 415 416 /** 417 * Sets right inset between area and its content. 418 */ 419 void 420 Area::SetRightInset(float right) 421 { 422 fRightBottomInset.width = right; 423 fLayoutItem->Layout()->InvalidateLayout(); 424 } 425 426 427 /** 428 * Sets bottom inset between area and its content. 429 */ 430 void 431 Area::SetBottomInset(float bottom) 432 { 433 fRightBottomInset.height = bottom; 434 fLayoutItem->Layout()->InvalidateLayout(); 435 } 436 437 438 BString 439 Area::ToString() const 440 { 441 BString string = "Area("; 442 string += fLeft->ToString(); 443 string << ", "; 444 string += fTop->ToString(); 445 string << ", "; 446 string += fRight->ToString(); 447 string << ", "; 448 string += fBottom->ToString(); 449 string << ")"; 450 return string; 451 } 452 453 454 /*! 455 * Sets the width of the area to be the same as the width of the given area 456 * times factor. 457 * 458 * @param area the area that should have the same width 459 * @return the same-width constraint 460 */ 461 Constraint* 462 Area::SetWidthAs(Area* area, float factor) 463 { 464 return fLS->AddConstraint(-1.0, fLeft, 1.0, fRight, factor, area->Left(), 465 -factor, area->Right(), kEQ, 0.0); 466 } 467 468 469 /*! 470 * Sets the height of the area to be the same as the height of the given area 471 * times factor. 472 * 473 * @param area the area that should have the same height 474 * @return the same-height constraint 475 */ 476 Constraint* 477 Area::SetHeightAs(Area* area, float factor) 478 { 479 return fLS->AddConstraint(-1.0, fTop, 1.0, fBottom, factor, area->Top(), 480 -factor, area->Bottom(), kEQ, 0.0); 481 } 482 483 484 void 485 Area::InvalidateSizeConstraints() 486 { 487 // check if if we are initialized 488 if (!fLeft) 489 return; 490 491 BSize minSize = fLayoutItem->MinSize(); 492 BSize maxSize = fLayoutItem->MaxSize(); 493 494 _UpdateMinSizeConstraint(minSize); 495 _UpdateMaxSizeConstraint(maxSize); 496 } 497 498 499 BRect 500 Area::Frame() 501 { 502 return BRect(fLeft->Value(), fTop->Value(), fRight->Value(), 503 fBottom->Value()); 504 } 505 506 507 BRect 508 Area::ItemFrame() 509 { 510 return fLayoutItem->Frame(); 511 } 512 513 514 /** 515 * Destructor. 516 * Removes the area from its specification. 517 */ 518 Area::~Area() 519 { 520 for (int32 i = 0; i < fConstraints.CountItems(); i++) 521 delete fConstraints.ItemAt(i); 522 } 523 524 525 /** 526 * Constructor. 527 * Uses XTabs and YTabs. 528 */ 529 Area::Area(BLayoutItem* item) 530 : 531 fLayoutItem(item), 532 533 fLS(NULL), 534 fLeft(NULL), 535 fRight(NULL), 536 fTop(NULL), 537 fBottom(NULL), 538 539 fRow(NULL), 540 fColumn(NULL), 541 542 fShrinkPenalties(5, 5), 543 fGrowPenalties(5, 5), 544 545 fMinContentWidth(NULL), 546 fMaxContentWidth(NULL), 547 fMinContentHeight(NULL), 548 fMaxContentHeight(NULL), 549 550 fContentAspectRatio(-1), 551 fContentAspectRatioC(NULL) 552 { 553 554 } 555 556 557 /** 558 * Initialize variables. 559 */ 560 void 561 Area::_Init(LinearSpec* ls, XTab* left, YTab* top, XTab* right, YTab* bottom, 562 RowColumnManager* manager) 563 { 564 fLS = ls; 565 fLeft = left; 566 fRight = right; 567 fTop = top; 568 fBottom = bottom; 569 570 fRowColumnManager = manager; 571 572 // adds the two essential constraints of the area that make sure that the 573 // left x-tab is really to the left of the right x-tab, and the top y-tab 574 // really above the bottom y-tab 575 fMinContentWidth = ls->AddConstraint(-1.0, fLeft, 1.0, fRight, kGE, 0); 576 fMinContentHeight = ls->AddConstraint(-1.0, fTop, 1.0, fBottom, kGE, 0); 577 578 fConstraints.AddItem(fMinContentWidth); 579 fConstraints.AddItem(fMinContentHeight); 580 } 581 582 583 void 584 Area::_Init(LinearSpec* ls, Row* row, Column* column, RowColumnManager* manager) 585 { 586 _Init(ls, column->Left(), row->Top(), column->Right(), row->Bottom(), manager); 587 588 fRow = row; 589 fColumn = column; 590 } 591 592 593 /** 594 * Perform layout on the area. 595 */ 596 void 597 Area::_DoLayout() 598 { 599 // check if if we are initialized 600 if (!fLeft) 601 return; 602 603 BRect areaFrame(round(fLeft->Value()), round(fTop->Value()), 604 round(fRight->Value()), round(fBottom->Value())); 605 areaFrame.left += LeftInset(); 606 areaFrame.right -= RightInset(); 607 areaFrame.top += TopInset(); 608 areaFrame.bottom -= BottomInset(); 609 610 fLayoutItem->AlignInFrame(areaFrame); 611 } 612 613 614 void 615 Area::_UpdateMinSizeConstraint(BSize min) 616 { 617 float width = 0.; 618 float height = 0.; 619 if (min.width > 0) 620 width = min.Width() + LeftInset() + RightInset(); 621 if (min.height > 0) 622 height = min.Height() + TopInset() + BottomInset(); 623 624 fMinContentWidth->SetRightSide(width); 625 fMinContentHeight->SetRightSide(height); 626 } 627 628 629 void 630 Area::_UpdateMaxSizeConstraint(BSize max) 631 { 632 max.width += LeftInset() + RightInset(); 633 max.height += TopInset() + BottomInset(); 634 635 // we only need max constraints if the alignment is full height/width 636 // otherwise we can just align the item in the free space 637 BAlignment alignment = fLayoutItem->Alignment(); 638 if (alignment.Vertical() == B_ALIGN_USE_FULL_HEIGHT) { 639 if (fMaxContentHeight == NULL) { 640 fMaxContentHeight = fLS->AddConstraint(-1.0, fTop, 1.0, fBottom, 641 kLE, max.Height()); 642 fConstraints.AddItem(fMaxContentHeight); 643 } else 644 fMaxContentHeight->SetRightSide(max.Height()); 645 } 646 else { 647 fConstraints.RemoveItem(fMaxContentHeight); 648 delete fMaxContentHeight; 649 fMaxContentHeight = NULL; 650 } 651 652 if (alignment.Horizontal() == B_ALIGN_USE_FULL_WIDTH) { 653 if (fMaxContentWidth == NULL) { 654 fMaxContentWidth = fLS->AddConstraint(-1.0, fLeft, 1.0, fRight, kLE, 655 max.Width()); 656 fConstraints.AddItem(fMaxContentWidth); 657 } else 658 fMaxContentWidth->SetRightSide(max.Width()); 659 } 660 else { 661 fConstraints.RemoveItem(fMaxContentWidth); 662 delete fMaxContentWidth; 663 fMaxContentWidth = NULL; 664 } 665 } 666