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