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 21 using namespace LinearProgramming; 22 23 24 BLayoutItem* 25 Area::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 */ 36 XTab* 37 Area::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 */ 48 XTab* 49 Area::Right() const 50 { 51 return fRight; 52 } 53 54 55 /** 56 * Gets the top tab of the area. 57 */ 58 YTab* 59 Area::Top() const 60 { 61 return fTop; 62 } 63 64 65 /** 66 * Gets the bottom tab of the area. 67 */ 68 YTab* 69 Area::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 */ 80 void 81 Area::SetLeft(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 */ 99 void 100 Area::SetRight(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 */ 116 void 117 Area::SetTop(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 */ 133 void 134 Area::SetBottom(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 */ 150 Row* 151 Area::GetRow() const 152 { 153 return fRow; 154 } 155 156 157 /** 158 * Gets the column that defines the left and right tabs. 159 */ 160 Column* 161 Area::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 */ 171 BSize 172 Area::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 */ 182 BSize 183 Area::GrowPenalties() const 184 { 185 return fGrowPenalties; 186 } 187 188 189 void 190 Area::SetShrinkPenalties(BSize shrink) { 191 fShrinkPenalties = shrink; 192 193 fLayoutItem->Layout()->InvalidateLayout(); 194 } 195 196 197 void 198 Area::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 */ 209 double 210 Area::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 */ 220 void 221 Area::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 fConstraints.AddItem(fContentAspectRatioC); 231 } else { 232 fContentAspectRatioC->SetLeftSide(-1.0, fLeft, 1.0, fRight, ratio, 233 fTop, -ratio, fBottom); 234 } 235 fLayoutItem->Layout()->InvalidateLayout(); 236 } 237 238 239 void 240 Area::GetInsets(float* left, float* top, float* right, float* bottom) const 241 { 242 if (left) 243 *left = fTopLeftInset.Width(); 244 if (top) 245 *top = fTopLeftInset.Height(); 246 if (right) 247 *right = fRightBottomInset.Width(); 248 if (bottom) 249 *bottom = fRightBottomInset.Height(); 250 } 251 252 253 /** 254 * Gets left inset between area and its content. 255 */ 256 float 257 Area::LeftInset() const 258 { 259 if (fTopLeftInset.IsWidthSet()) 260 return fTopLeftInset.Width(); 261 262 BALMLayout* layout = static_cast<BALMLayout*>(fLayoutItem->Layout()); 263 return layout->InsetForTab(fLeft.Get()); 264 } 265 266 267 /** 268 * Gets top inset between area and its content. 269 */ 270 float 271 Area::TopInset() const 272 { 273 if (fTopLeftInset.IsHeightSet()) 274 return fTopLeftInset.Height(); 275 276 BALMLayout* layout = static_cast<BALMLayout*>(fLayoutItem->Layout()); 277 return layout->InsetForTab(fTop.Get()); 278 } 279 280 281 /** 282 * Gets right inset between area and its content. 283 */ 284 float 285 Area::RightInset() const 286 { 287 if (fRightBottomInset.IsWidthSet()) 288 return fRightBottomInset.Width(); 289 290 BALMLayout* layout = static_cast<BALMLayout*>(fLayoutItem->Layout()); 291 return layout->InsetForTab(fRight.Get()); 292 } 293 294 295 /** 296 * Gets bottom inset between area and its content. 297 */ 298 float 299 Area::BottomInset() const 300 { 301 if (fRightBottomInset.IsHeightSet()) 302 return fRightBottomInset.Height(); 303 304 BALMLayout* layout = static_cast<BALMLayout*>(fLayoutItem->Layout()); 305 return layout->InsetForTab(fBottom.Get()); 306 } 307 308 309 void 310 Area::SetInsets(float insets) 311 { 312 if (insets != B_SIZE_UNSET) 313 insets = BControlLook::ComposeSpacing(insets); 314 315 fTopLeftInset.Set(insets, insets); 316 fRightBottomInset.Set(insets, insets); 317 fLayoutItem->Layout()->InvalidateLayout(); 318 } 319 320 321 void 322 Area::SetInsets(float horizontal, float vertical) 323 { 324 if (horizontal != B_SIZE_UNSET) 325 horizontal = BControlLook::ComposeSpacing(horizontal); 326 if (vertical != B_SIZE_UNSET) 327 vertical = BControlLook::ComposeSpacing(vertical); 328 329 fTopLeftInset.Set(horizontal, horizontal); 330 fRightBottomInset.Set(vertical, vertical); 331 fLayoutItem->Layout()->InvalidateLayout(); 332 } 333 334 335 void 336 Area::SetInsets(float left, float top, float right, float bottom) 337 { 338 if (left != B_SIZE_UNSET) 339 left = BControlLook::ComposeSpacing(left); 340 if (right != B_SIZE_UNSET) 341 right = BControlLook::ComposeSpacing(right); 342 if (top != B_SIZE_UNSET) 343 top = BControlLook::ComposeSpacing(top); 344 if (bottom != B_SIZE_UNSET) 345 bottom = BControlLook::ComposeSpacing(bottom); 346 347 fTopLeftInset.Set(left, top); 348 fRightBottomInset.Set(right, bottom); 349 fLayoutItem->Layout()->InvalidateLayout(); 350 } 351 352 353 /** 354 * Sets left inset between area and its content. 355 */ 356 void 357 Area::SetLeftInset(float left) 358 { 359 fTopLeftInset.width = left; 360 fLayoutItem->Layout()->InvalidateLayout(); 361 } 362 363 364 /** 365 * Sets top inset between area and its content. 366 */ 367 void 368 Area::SetTopInset(float top) 369 { 370 fTopLeftInset.height = top; 371 fLayoutItem->Layout()->InvalidateLayout(); 372 } 373 374 375 /** 376 * Sets right inset between area and its content. 377 */ 378 void 379 Area::SetRightInset(float right) 380 { 381 fRightBottomInset.width = right; 382 fLayoutItem->Layout()->InvalidateLayout(); 383 } 384 385 386 /** 387 * Sets bottom inset between area and its content. 388 */ 389 void 390 Area::SetBottomInset(float bottom) 391 { 392 fRightBottomInset.height = bottom; 393 fLayoutItem->Layout()->InvalidateLayout(); 394 } 395 396 397 BString 398 Area::ToString() const 399 { 400 BString string = "Area("; 401 string += fLeft->ToString(); 402 string << ", "; 403 string += fTop->ToString(); 404 string << ", "; 405 string += fRight->ToString(); 406 string << ", "; 407 string += fBottom->ToString(); 408 string << ")"; 409 return string; 410 } 411 412 413 /*! 414 * Sets the width of the area to be the same as the width of the given area 415 * times factor. 416 * 417 * @param area the area that should have the same width 418 * @return the same-width constraint 419 */ 420 Constraint* 421 Area::SetWidthAs(Area* area, float factor) 422 { 423 return fLS->AddConstraint(-1.0, fLeft, 1.0, fRight, factor, area->Left(), 424 -factor, area->Right(), kEQ, 0.0); 425 } 426 427 428 /*! 429 * Sets the height of the area to be the same as the height of the given area 430 * times factor. 431 * 432 * @param area the area that should have the same height 433 * @return the same-height constraint 434 */ 435 Constraint* 436 Area::SetHeightAs(Area* area, float factor) 437 { 438 return fLS->AddConstraint(-1.0, fTop, 1.0, fBottom, factor, area->Top(), 439 -factor, area->Bottom(), kEQ, 0.0); 440 } 441 442 443 void 444 Area::InvalidateSizeConstraints() 445 { 446 // check if if we are initialized 447 if (!fLeft) 448 return; 449 450 BSize minSize = fLayoutItem->MinSize(); 451 BSize maxSize = fLayoutItem->MaxSize(); 452 453 _UpdateMinSizeConstraint(minSize); 454 _UpdateMaxSizeConstraint(maxSize); 455 } 456 457 458 BRect 459 Area::Frame() const 460 { 461 return BRect(round(fLeft->Value()), round(fTop->Value()), 462 round(fRight->Value()), round(fBottom->Value())); 463 } 464 465 466 /** 467 * Destructor. 468 * Removes the area from its specification. 469 */ 470 Area::~Area() 471 { 472 for (int32 i = 0; i < fConstraints.CountItems(); i++) 473 delete fConstraints.ItemAt(i); 474 } 475 476 477 static int32 sAreaID = 0; 478 479 static int32 480 new_area_id() 481 { 482 return sAreaID++; 483 } 484 485 486 /** 487 * Constructor. 488 * Uses XTabs and YTabs. 489 */ 490 Area::Area(BLayoutItem* item) 491 : 492 fLayoutItem(item), 493 494 fLS(NULL), 495 fLeft(NULL), 496 fRight(NULL), 497 fTop(NULL), 498 fBottom(NULL), 499 500 fRow(NULL), 501 fColumn(NULL), 502 503 fShrinkPenalties(5, 5), 504 fGrowPenalties(5, 5), 505 506 fContentAspectRatio(-1), 507 fRowColumnManager(NULL), 508 509 fMinContentWidth(NULL), 510 fMaxContentWidth(NULL), 511 fMinContentHeight(NULL), 512 fMaxContentHeight(NULL), 513 514 fContentAspectRatioC(NULL) 515 { 516 fID = new_area_id(); 517 } 518 519 520 int32 521 Area::ID() const 522 { 523 return fID; 524 } 525 526 527 void 528 Area::SetID(int32 id) 529 { 530 fID = id; 531 } 532 533 534 /** 535 * Initialize variables. 536 */ 537 void 538 Area::_Init(LinearSpec* ls, XTab* left, YTab* top, XTab* right, YTab* bottom, 539 RowColumnManager* manager) 540 { 541 fLS = ls; 542 fLeft = left; 543 fRight = right; 544 fTop = top; 545 fBottom = bottom; 546 547 fRowColumnManager = manager; 548 549 // adds the two essential constraints of the area that make sure that the 550 // left x-tab is really to the left of the right x-tab, and the top y-tab 551 // really above the bottom y-tab 552 fMinContentWidth = ls->AddConstraint(-1.0, fLeft, 1.0, fRight, kGE, 0); 553 fMinContentHeight = ls->AddConstraint(-1.0, fTop, 1.0, fBottom, kGE, 0); 554 555 fConstraints.AddItem(fMinContentWidth); 556 fConstraints.AddItem(fMinContentHeight); 557 } 558 559 560 void 561 Area::_Init(LinearSpec* ls, Row* row, Column* column, RowColumnManager* manager) 562 { 563 _Init(ls, column->Left(), row->Top(), column->Right(), row->Bottom(), manager); 564 565 fRow = row; 566 fColumn = column; 567 } 568 569 570 /** 571 * Perform layout on the area. 572 */ 573 void 574 Area::_DoLayout() 575 { 576 // check if if we are initialized 577 if (!fLeft) 578 return; 579 580 BRect areaFrame(Frame()); 581 areaFrame.left += LeftInset(); 582 areaFrame.right -= RightInset(); 583 areaFrame.top += TopInset(); 584 areaFrame.bottom -= BottomInset(); 585 586 fLayoutItem->AlignInFrame(areaFrame); 587 } 588 589 590 void 591 Area::_UpdateMinSizeConstraint(BSize min) 592 { 593 float width = 0.; 594 float height = 0.; 595 if (min.width > 0) 596 width = min.Width() + LeftInset() + RightInset(); 597 if (min.height > 0) 598 height = min.Height() + TopInset() + BottomInset(); 599 600 fMinContentWidth->SetRightSide(width); 601 fMinContentHeight->SetRightSide(height); 602 } 603 604 605 void 606 Area::_UpdateMaxSizeConstraint(BSize max) 607 { 608 max.width += LeftInset() + RightInset(); 609 max.height += TopInset() + BottomInset(); 610 611 // we only need max constraints if the alignment is full height/width 612 // otherwise we can just align the item in the free space 613 BAlignment alignment = fLayoutItem->Alignment(); 614 if (alignment.Vertical() == B_ALIGN_USE_FULL_HEIGHT) { 615 if (fMaxContentHeight == NULL) { 616 fMaxContentHeight = fLS->AddConstraint(-1.0, fTop, 1.0, fBottom, 617 kLE, max.Height()); 618 fConstraints.AddItem(fMaxContentHeight); 619 } else 620 fMaxContentHeight->SetRightSide(max.Height()); 621 } 622 else { 623 fConstraints.RemoveItem(fMaxContentHeight); 624 delete fMaxContentHeight; 625 fMaxContentHeight = NULL; 626 } 627 628 if (alignment.Horizontal() == B_ALIGN_USE_FULL_WIDTH) { 629 if (fMaxContentWidth == NULL) { 630 fMaxContentWidth = fLS->AddConstraint(-1.0, fLeft, 1.0, fRight, kLE, 631 max.Width()); 632 fConstraints.AddItem(fMaxContentWidth); 633 } else 634 fMaxContentWidth->SetRightSide(max.Width()); 635 } 636 else { 637 fConstraints.RemoveItem(fMaxContentWidth); 638 delete fMaxContentWidth; 639 fMaxContentWidth = NULL; 640 } 641 } 642