xref: /haiku/src/libs/alm/Area.cpp (revision bf243977ffd34197ad7c0820c2da5d21abea0402)
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