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