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