xref: /haiku/src/libs/alm/Area.cpp (revision 5b6fca06392320a9c32d542b519d8f4fe876aef2)
1 /*
2  * Copyright 2007-2008, Christof Lutteroth, lutteroth@cs.auckland.ac.nz
3  * Copyright 2007-2008, James Kim, jkim202@ec.auckland.ac.nz
4  * Distributed under the terms of the MIT License.
5  */
6 
7 
8 #include "Area.h"
9 
10 #include <algorithm>	// for max
11 
12 #include <Button.h>
13 #include <CheckBox.h>
14 #include <PictureButton.h>
15 #include <RadioButton.h>
16 #include <StatusBar.h>
17 #include <StringView.h>
18 
19 #include "ALMLayout.h"
20 
21 
22 using namespace std;
23 
24 
25 BView*
26 Area::View()
27 {
28 	return fLayoutItem->View();
29 }
30 
31 
32 /**
33  * Gets the left tab of the area.
34  *
35  * @return the left tab of the area
36  */
37 XTab*
38 Area::Left() const
39 {
40 	return fLeft;
41 }
42 
43 
44 /**
45  * Gets the right tab of the area.
46  *
47  * @return the right tab of the area
48  */
49 XTab*
50 Area::Right() const
51 {
52 	return fRight;
53 }
54 
55 
56 /**
57  * Gets the top tab of the area.
58  */
59 YTab*
60 Area::Top() const
61 {
62 	return fTop;
63 }
64 
65 
66 /**
67  * Gets the bottom tab of the area.
68  */
69 YTab*
70 Area::Bottom() const
71 {
72 	return fBottom;
73 }
74 
75 
76 /**
77  * Sets the left tab of the area.
78  *
79  * @param left	the left tab of the area
80  */
81 void
82 Area::SetLeft(XTab* left)
83 {
84 	fLeft = left;
85 
86 	fColumn = NULL;
87 
88 	fMinContentWidth->SetLeftSide(-1.0, fLeft, 1.0, fRight);
89 	fPreferredContentWidth->SetLeftSide(-1.0, fLeft, 1.0, fRight);
90 	if (fMaxContentWidth != NULL)
91 		fMaxContentWidth->SetLeftSide(-1.0, fLeft, 1.0, fRight);
92 
93 	fLayoutItem->Layout()->InvalidateLayout();
94 }
95 
96 
97 /**
98  * Sets the right tab of the area.
99  *
100  * @param right	the right tab of the area
101  */
102 void
103 Area::SetRight(XTab* right)
104 {
105 	fRight = right;
106 
107 	fColumn = NULL;
108 
109 	fMinContentWidth->SetLeftSide(-1.0, fLeft, 1.0, fRight);
110 	fPreferredContentWidth->SetLeftSide(-1.0, fLeft, 1.0, fRight);
111 	if (fMaxContentWidth != NULL)
112 		fMaxContentWidth->SetLeftSide(-1.0, fLeft, 1.0, fRight);
113 
114 	fLayoutItem->Layout()->InvalidateLayout();
115 }
116 
117 
118 /**
119  * Sets the top tab of the area.
120  */
121 void
122 Area::SetTop(YTab* top)
123 {
124 	fTop = top;
125 
126 	fRow = NULL;
127 
128 	fMinContentHeight->SetLeftSide(-1.0, fTop, 1.0, fBottom);
129 	fPreferredContentHeight->SetLeftSide(-1.0, fTop, 1.0, fBottom);
130 	if (fMaxContentHeight != NULL)
131 		fMaxContentHeight->SetLeftSide(-1.0, fTop, 1.0, fBottom);
132 
133 	fLayoutItem->Layout()->InvalidateLayout();
134 }
135 
136 
137 /**
138  * Sets the bottom tab of the area.
139  */
140 void
141 Area::SetBottom(YTab* bottom)
142 {
143 	fBottom = bottom;
144 
145 	fRow = NULL;
146 
147 	fMinContentHeight->SetLeftSide(-1.0, fTop, 1.0, fBottom);
148 	fPreferredContentHeight->SetLeftSide(-1.0, fTop, 1.0, fBottom);
149 	if (fMaxContentHeight != NULL)
150 		fMaxContentHeight->SetLeftSide(-1.0, fTop, 1.0, fBottom);
151 
152 	fLayoutItem->Layout()->InvalidateLayout();
153 }
154 
155 
156 /**
157  * Gets the row that defines the top and bottom tabs.
158  */
159 Row*
160 Area::GetRow() const
161 {
162 	return fRow;
163 }
164 
165 
166 /**
167  * Gets the column that defines the left and right tabs.
168  */
169 Column*
170 Area::GetColumn() const
171 {
172 	return fColumn;
173 }
174 
175 
176 /**
177  * Sets the row that defines the top and bottom tabs.
178  * May be null.
179  */
180 void
181 Area::SetRow(Row* row)
182 {
183 	SetTop(row->Top());
184 	SetBottom(row->Bottom());
185 	fRow = row;
186 	fLayoutItem->Layout()->InvalidateLayout();
187 }
188 
189 
190 /**
191  * Sets the column that defines the left and right tabs.
192  * May be null.
193  */
194 void
195 Area::SetColumn(Column* column)
196 {
197 	SetLeft(column->Left());
198 	SetRight(column->Right());
199 	fColumn = column;
200 	fLayoutItem->Layout()->InvalidateLayout();
201 }
202 
203 
204 /**
205  * The reluctance with which the area's content shrinks below its preferred size.
206  * The bigger the less likely is such shrinking.
207  */
208 BSize
209 Area::ShrinkPenalties() const
210 {
211 	return fShrinkPenalties;
212 }
213 
214 
215 /**
216  * The reluctance with which the area's content grows over its preferred size.
217  * The bigger the less likely is such growth.
218  */
219 BSize
220 Area::GrowPenalties() const
221 {
222 	return fGrowPenalties;
223 }
224 
225 
226 void Area::SetShrinkPenalties(BSize shrink) {
227 	fShrinkPenalties = shrink;
228 	if (fPreferredContentWidth != NULL) {
229 		fPreferredContentWidth->SetPenaltyNeg(shrink.Width());
230 		fPreferredContentHeight->SetPenaltyNeg(shrink.Height());
231 	}
232 	fLayoutItem->Layout()->InvalidateLayout();
233 }
234 
235 
236 void
237 Area::SetGrowPenalties(BSize grow)
238 {
239 	fGrowPenalties = grow;
240 	if (fPreferredContentWidth != NULL) {
241 		fPreferredContentWidth->SetPenaltyPos(grow.Width());
242 		fPreferredContentHeight->SetPenaltyPos(grow.Height());
243 	}
244 	fLayoutItem->Layout()->InvalidateLayout();
245 }
246 
247 
248 /**
249  * Gets aspect ratio of the area's content.
250  */
251 double
252 Area::ContentAspectRatio() const
253 {
254 	return fContentAspectRatio;
255 }
256 
257 
258 /**
259  * Sets aspect ratio of the area's content.
260  * May be different from the aspect ratio of the area.
261  */
262 void
263 Area::SetContentAspectRatio(double ratio)
264 {
265 	fContentAspectRatio = ratio;
266 	if (fContentAspectRatio <= 0) {
267 		delete fContentAspectRatioC;
268 		fContentAspectRatioC = NULL;
269 	} else if (fContentAspectRatioC == NULL) {
270 		fContentAspectRatioC = fLS->AddConstraint(-1.0, fLeft, 1.0, fRight,
271 			ratio, fTop, -ratio, fBottom, OperatorType(EQ), 0.0);
272 		fConstraints.AddItem(fContentAspectRatioC);
273 	} else {
274 		fContentAspectRatioC->SetLeftSide(-1.0, fLeft, 1.0, fRight, ratio,
275 			fTop, -ratio, fBottom);
276 	}
277 	fLayoutItem->Layout()->InvalidateLayout();
278 }
279 
280 
281 /**
282  * Gets left inset between area and its content.
283  */
284 int32
285 Area::LeftInset() const
286 {
287 	if (fTopLeftInset.IsWidthSet())
288 		return fTopLeftInset.Width();
289 
290 	BALMLayout* layout = static_cast<BALMLayout*>(fLayoutItem->Layout());
291 	if (fLeft == layout->Left())
292 		return layout->Inset();
293 	return layout->Spacing() / 2;
294 }
295 
296 
297 /**
298  * Gets top inset between area and its content.
299  */
300 int32
301 Area::TopInset() const
302 {
303 	if (fTopLeftInset.IsHeightSet())
304 		return fTopLeftInset.Height();
305 
306 	BALMLayout* layout = static_cast<BALMLayout*>(fLayoutItem->Layout());
307 	if (fTop == layout->Top())
308 		return layout->Inset();
309 	return layout->Spacing() / 2;
310 }
311 
312 
313 /**
314  * Gets right inset between area and its content.
315  */
316 int32
317 Area::RightInset() const
318 {
319 	if (fRightBottomInset.IsWidthSet())
320 		return fRightBottomInset.Width();
321 
322 	BALMLayout* layout = static_cast<BALMLayout*>(fLayoutItem->Layout());
323 	if (fRight == layout->Right())
324 		return layout->Inset();
325 	return layout->Spacing() / 2;
326 }
327 
328 
329 /**
330  * Gets bottom inset between area and its content.
331  */
332 int32
333 Area::BottomInset() const
334 {
335 	if (fRightBottomInset.IsHeightSet())
336 		return fRightBottomInset.Height();
337 
338 	BALMLayout* layout = static_cast<BALMLayout*>(fLayoutItem->Layout());
339 	if (fBottom == layout->Bottom())
340 		return layout->Inset();
341 	return layout->Spacing() / 2;
342 }
343 
344 
345 /**
346  * Sets left inset between area and its content.
347  */
348 void
349 Area::SetLeftInset(int32 left)
350 {
351 	fTopLeftInset.width = left;
352 	fLayoutItem->Layout()->InvalidateLayout();
353 }
354 
355 
356 /**
357  * Sets top inset between area and its content.
358  */
359 void
360 Area::SetTopInset(int32 top)
361 {
362 	fTopLeftInset.height = top;
363 	fLayoutItem->Layout()->InvalidateLayout();
364 }
365 
366 
367 /**
368  * Sets right inset between area and its content.
369  */
370 void
371 Area::SetRightInset(int32 right)
372 {
373 	fRightBottomInset.width = right;
374 	fLayoutItem->Layout()->InvalidateLayout();
375 }
376 
377 
378 /**
379  * Sets bottom inset between area and its content.
380  */
381 void
382 Area::SetBottomInset(int32 bottom)
383 {
384 	fRightBottomInset.height = bottom;
385 	fLayoutItem->Layout()->InvalidateLayout();
386 }
387 
388 
389 Area::operator BString() const
390 {
391 	BString string;
392 	GetString(string);
393 	return string;
394 }
395 
396 
397 void
398 Area::GetString(BString& string) const
399 {
400 	string << "Area(";
401 	fLeft->GetString(string);
402 	string << ", ";
403 	fTop->GetString(string);
404 	string << ", ";
405 	fRight->GetString(string);
406 	string << ", ";
407 	fBottom->GetString(string);
408 	string << ")";
409 }
410 
411 
412 /*!
413  * Sets the width of the area times factor to be the same as the width of the
414  * given area.
415  *
416  * @param area	the area that should have the same width
417  * @return the same-width constraint
418  */
419 Constraint*
420 Area::SetWidthAs(Area* area, float factor)
421 {
422 	return fLS->AddConstraint(-factor, fLeft, factor, fRight, 1.0, area->Left(),
423 		-1.0, area->Right(), OperatorType(EQ), 0.0);
424 }
425 
426 
427 /*!
428  * Sets the height of the area times factor to be the same as the height of the
429  * given area.
430  *
431  * @param area	the area that should have the same height
432  * @return the same-height constraint
433  */
434 Constraint*
435 Area::SetHeightAs(Area* area, float factor)
436 {
437 	return fLS->AddConstraint(-factor, fTop, factor, fBottom, 1.0, area->Top(),
438 		-1.0, area->Bottom(), OperatorType(EQ), 0.0);
439 }
440 
441 
442 void
443 Area::InvalidateSizeConstraints()
444 {
445 	// check if if we are initialized
446 	if (!fLeft)
447 		return;
448 
449 	BSize minSize = fLayoutItem->MinSize();
450 	BSize maxSize = fLayoutItem->MaxSize();
451 	BSize prefSize = fLayoutItem->PreferredSize();
452 
453 	_UpdateMinSizeConstraint(minSize);
454 	_UpdateMaxSizeConstraint(maxSize);
455 	_UpdatePreferredConstraint(prefSize);
456 }
457 
458 
459 /**
460  * Destructor.
461  * Removes the area from its specification.
462  */
463 Area::~Area()
464 {
465 	for (int32 i = 0; i < fConstraints.CountItems(); i++)
466 		delete (Constraint*)fConstraints.ItemAt(i);
467 }
468 
469 
470 /**
471  * Constructor.
472  * Uses XTabs and YTabs.
473  */
474 Area::Area(BLayoutItem* item)
475 	:
476 	fLayoutItem(item),
477 
478 	fLS(NULL),
479 	fLeft(NULL),
480 	fRight(NULL),
481 	fTop(NULL),
482 	fBottom(NULL),
483 
484 	fRow(NULL),
485 	fColumn(NULL),
486 
487 	fShrinkPenalties(5, 5),
488 	fGrowPenalties(5, 5),
489 
490 	fMinContentWidth(NULL),
491 	fMaxContentWidth(NULL),
492 	fMinContentHeight(NULL),
493 	fMaxContentHeight(NULL),
494 	fPreferredContentWidth(NULL),
495 	fPreferredContentHeight(NULL),
496 
497 	fContentAspectRatio(-1),
498 	fContentAspectRatioC(NULL)
499 {
500 
501 }
502 
503 
504 /**
505  * Initialize variables.
506  */
507 void
508 Area::_Init(LinearSpec* ls, XTab* left, YTab* top, XTab* right, YTab* bottom)
509 {
510 	fLS = ls;
511 	fLeft = left;
512 	fRight = right;
513 	fTop = top;
514 	fBottom = bottom;
515 
516 	// adds the two essential constraints of the area that make sure that the
517 	// left x-tab is really to the left of the right x-tab, and the top y-tab
518 	// really above the bottom y-tab
519 	fMinContentWidth = ls->AddConstraint(-1.0, fLeft, 1.0, fRight,
520 		OperatorType(GE), 0);
521 	fMinContentHeight = ls->AddConstraint(-1.0, fTop, 1.0, fBottom,
522 		OperatorType(GE), 0);
523 
524 	fConstraints.AddItem(fMinContentWidth);
525 	fConstraints.AddItem(fMinContentHeight);
526 
527 	fPreferredContentWidth = fLS->AddConstraint(-1.0, fLeft, 1.0, fRight,
528 		OperatorType(EQ), 0, fShrinkPenalties.Height(), fGrowPenalties.Width());
529 
530 	fPreferredContentHeight = fLS->AddConstraint(-1.0, fTop, 1.0, fBottom,
531 		OperatorType(EQ), 0, fShrinkPenalties.Height(),
532 		fGrowPenalties.Height());
533 
534 	fConstraints.AddItem(fPreferredContentWidth);
535 	fConstraints.AddItem(fPreferredContentHeight);
536 }
537 
538 
539 void
540 Area::_Init(LinearSpec* ls, Row* row, Column* column)
541 {
542 	_Init(ls, column->Left(), row->Top(), column->Right(), row->Bottom());
543 	fRow = row;
544 	fColumn = column;
545 }
546 
547 
548 /**
549  * Perform layout on the area.
550  */
551 void
552 Area::_DoLayout()
553 {
554 	// check if if we are initialized
555 	if (!fLeft)
556 		return;
557 
558 	BRect areaFrame(round(fLeft->Value()), round(fTop->Value()),
559 		round(fRight->Value()), round(fBottom->Value()));
560 	areaFrame.left += LeftInset();
561 	areaFrame.right -= RightInset();
562 	areaFrame.top += TopInset();
563 	areaFrame.bottom -= BottomInset();
564 
565 	fLayoutItem->AlignInFrame(areaFrame);
566 }
567 
568 
569 void
570 Area::_UpdateMinSizeConstraint(BSize min)
571 {
572 	float width = 0.;
573 	float height = 0.;
574 	if (min.width > 0)
575 		width = min.Width() + LeftInset() + RightInset();
576 	if (min.height > 0)
577 		height = min.Height() + TopInset() + BottomInset();
578 
579 	fMinContentWidth->SetRightSide(width);
580 	fMinContentHeight->SetRightSide(height);
581 }
582 
583 
584 void
585 Area::_UpdateMaxSizeConstraint(BSize max)
586 {
587 	max.width += LeftInset() + RightInset();
588 	max.height += TopInset() + BottomInset();
589 
590 	// we only need max constraints if the alignment is full height/width
591 	// otherwise we can just align the item in the free space
592 	BAlignment alignment = fLayoutItem->Alignment();
593 	if (alignment.Vertical() == B_ALIGN_USE_FULL_HEIGHT) {
594 		if (fMaxContentHeight == NULL) {
595 			fMaxContentHeight = fLS->AddConstraint(-1.0, fTop, 1.0, fBottom,
596 				OperatorType(LE), max.Height());
597 			fConstraints.AddItem(fMaxContentHeight);
598 		} else
599 			fMaxContentHeight->SetRightSide(max.Height());
600 	}
601 	else {
602 		fConstraints.RemoveItem(fMaxContentHeight);
603 		delete fMaxContentHeight;
604 		fMaxContentHeight = NULL;
605 	}
606 
607 	if (alignment.Horizontal() == B_ALIGN_USE_FULL_WIDTH) {
608 		if (fMaxContentWidth == NULL) {
609 			fMaxContentWidth = fLS->AddConstraint(-1.0, fLeft, 1.0, fRight,
610 				OperatorType(LE), max.Width());
611 			fConstraints.AddItem(fMaxContentWidth);
612 		} else
613 			fMaxContentWidth->SetRightSide(max.Width());
614 	}
615 	else {
616 		fConstraints.RemoveItem(fMaxContentWidth);
617 		delete fMaxContentWidth;
618 		fMaxContentWidth = NULL;
619 	}
620 }
621 
622 
623 void
624 Area::_UpdatePreferredConstraint(BSize preferred)
625 {
626 	float width = 32000;
627 	float height = 32000;
628 	if (preferred.width > 0)
629 		width = preferred.Width() + LeftInset() + RightInset();
630 	if (preferred.height > 0)
631 		height = preferred.Height() + TopInset() + BottomInset();
632 
633 	fPreferredContentWidth->SetRightSide(width);
634 	fPreferredContentHeight->SetRightSide(height);
635 }
636