xref: /haiku/src/libs/alm/ALMLayout.cpp (revision 7e021d45dad8e78bce48a81a5524ed6497715ba1)
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 #include "ALMLayout.h"
8 
9 
10 #include <vector>
11 
12 #include <ControlLook.h>
13 
14 #include "ALMGroup.h"
15 #include "RowColumnManager.h"
16 #include "ViewLayoutItem.h"
17 
18 
19 using namespace LinearProgramming;
20 
21 
22 const BSize kUnsetSize(B_SIZE_UNSET, B_SIZE_UNSET);
23 
24 
25 /*!
26  * Constructor.
27  * Creates new layout engine.
28  *
29  * If friendLayout is not NULL the solver of the friend layout is used.
30  */
31 BALMLayout::BALMLayout(float hSpacing, float vSpacing, BALMLayout* friendLayout)
32 	:
33 	fLeftInset(0),
34 	fRightInset(0),
35 	fTopInset(0),
36 	fBottomInset(0),
37 	fHSpacing(hSpacing),
38 	fVSpacing(vSpacing)
39 {
40 	fSolver = friendLayout ? friendLayout->Solver() : &fOwnSolver;
41 	fRowColumnManager = new RowColumnManager(fSolver);
42 
43 	fLeft = AddXTab();
44 	fRight = AddXTab();
45 	fTop = AddYTab();
46 	fBottom = AddYTab();
47 
48 	// the Left tab is always at x-position 0, and the Top tab is always at
49 	// y-position 0
50 	fLeft->SetRange(0, 0);
51 	fTop->SetRange(0, 0);
52 
53 	// cached layout values
54 	// need to be invalidated whenever the layout specification is changed
55 	fMinSize = kUnsetSize;
56 	fMaxSize = kUnsetSize;
57 	fPreferredSize = kUnsetSize;
58 }
59 
60 
61 BALMLayout::~BALMLayout()
62 {
63 	delete fRowColumnManager;
64 }
65 
66 
67 /**
68  * Adds a new x-tab to the specification.
69  *
70  * @return the new x-tab
71  */
72 BReference<XTab>
73 BALMLayout::AddXTab()
74 {
75 	BReference<XTab> tab(new(std::nothrow) XTab(this), true);
76 	if (!tab)
77 		return NULL;
78 	if (!fSolver->AddVariable(tab))
79 		return NULL;
80 
81 	fXTabList.AddItem(tab);
82 	return tab;
83 }
84 
85 
86 void
87 BALMLayout::AddXTabs(BReference<XTab>* tabs, uint32 count)
88 {
89 	for (uint32 i = 0; i < count; i++)
90 		tabs[i] = AddXTab();
91 }
92 
93 
94 void
95 BALMLayout::AddYTabs(BReference<YTab>* tabs, uint32 count)
96 {
97 	for (uint32 i = 0; i < count; i++)
98 		tabs[i] = AddYTab();
99 }
100 
101 
102 /**
103  * Adds a new y-tab to the specification.
104  *
105  * @return the new y-tab
106  */
107 BReference<YTab>
108 BALMLayout::AddYTab()
109 {
110 	BReference<YTab> tab(new(std::nothrow) YTab(this), true);
111 	if (tab.Get() == NULL)
112 		return NULL;
113 	if (!fSolver->AddVariable(tab))
114 		return NULL;
115 
116 	fYTabList.AddItem(tab);
117 	return tab;
118 }
119 
120 
121 int32
122 BALMLayout::CountXTabs() const
123 {
124 	return fXTabList.CountItems();
125 }
126 
127 
128 int32
129 BALMLayout::CountYTabs() const
130 {
131 	return fYTabList.CountItems();
132 }
133 
134 
135 XTab*
136 BALMLayout::XTabAt(int32 index) const
137 {
138 	return fXTabList.ItemAt(index);
139 }
140 
141 
142 YTab*
143 BALMLayout::YTabAt(int32 index) const
144 {
145 	return fYTabList.ItemAt(index);
146 }
147 
148 
149 static int
150 compare_x_tab_func(const XTab* tab1, const XTab* tab2)
151 {
152 	if (tab1->Value() < tab2->Value())
153 		return -1;
154 	else if (tab1->Value() == tab2->Value())
155 		return 0;
156 	return 1;
157 }
158 
159 
160 static int
161 compare_y_tab_func(const YTab* tab1, const YTab* tab2)
162 {
163 	if (tab1->Value() < tab2->Value())
164 		return -1;
165 	else if (tab1->Value() == tab2->Value())
166 		return 0;
167 	return 1;
168 }
169 
170 
171 const XTabList&
172 BALMLayout::OrderedXTabs()
173 {
174 	fXTabList.SortItems(compare_x_tab_func);
175 	return fXTabList;
176 }
177 
178 
179 const YTabList&
180 BALMLayout::OrderedYTabs()
181 {
182 	fYTabList.SortItems(compare_y_tab_func);
183 	return fYTabList;
184 }
185 
186 
187 /**
188  * Adds a new row to the specification that is glued to the given y-tabs.
189  *
190  * @param top
191  * @param bottom
192  * @return the new row
193  */
194 Row*
195 BALMLayout::AddRow(YTab* _top, YTab* _bottom)
196 {
197 	BReference<YTab> top = _top;
198 	BReference<YTab> bottom = _bottom;
199 	if (_top == NULL)
200 		top = AddYTab();
201 	if (_bottom == NULL)
202 		bottom = AddYTab();
203 	return new(std::nothrow) Row(fSolver, top, bottom);
204 }
205 
206 
207 /**
208  * Adds a new column to the specification that is glued to the given x-tabs.
209  *
210  * @param left
211  * @param right
212  * @return the new column
213  */
214 Column*
215 BALMLayout::AddColumn(XTab* _left, XTab* _right)
216 {
217 	BReference<XTab> left = _left;
218 	BReference<XTab> right = _right;
219 	if (_left == NULL)
220 		left = AddXTab();
221 	if (_right == NULL)
222 		right = AddXTab();
223 	return new(std::nothrow) Column(fSolver, left, right);
224 }
225 
226 
227 Area*
228 BALMLayout::AreaFor(int32 id) const
229 {
230 	int32 areaCount = CountAreas();
231 	for (int32 i = 0; i < areaCount; i++) {
232 		Area* area = AreaAt(i);
233 		if (area->ID() == id)
234 			return area;
235 	}
236 	return NULL;
237 }
238 
239 
240 /**
241  * Finds the area that contains the given control.
242  *
243  * @param control	the control to look for
244  * @return the area that contains the control
245  */
246 Area*
247 BALMLayout::AreaFor(const BView* control) const
248 {
249 	return AreaFor(ItemAt(IndexOfView(const_cast<BView*>(control))));
250 }
251 
252 
253 Area*
254 BALMLayout::AreaFor(const BLayoutItem* item) const
255 {
256 	if (!item)
257 		return NULL;
258 	return static_cast<Area*>(item->LayoutData());
259 }
260 
261 
262 int32
263 BALMLayout::CountAreas() const
264 {
265 	return CountItems();
266 }
267 
268 
269 Area*
270 BALMLayout::AreaAt(int32 index) const
271 {
272 	return AreaFor(ItemAt(index));
273 }
274 
275 
276 XTab*
277 BALMLayout::LeftOf(const BView* view) const
278 {
279 	Area* area = AreaFor(view);
280 	if (!area)
281 		return NULL;
282 	return area->Left();
283 }
284 
285 
286 XTab*
287 BALMLayout::LeftOf(const BLayoutItem* item) const
288 {
289 	Area* area = AreaFor(item);
290 	if (!area)
291 		return NULL;
292 	return area->Left();
293 }
294 
295 
296 XTab*
297 BALMLayout::RightOf(const BView* view) const
298 {
299 	Area* area = AreaFor(view);
300 	if (!area)
301 		return NULL;
302 	return area->Right();
303 }
304 
305 
306 XTab*
307 BALMLayout::RightOf(const BLayoutItem* item) const
308 {
309 	Area* area = AreaFor(item);
310 	if (!area)
311 		return NULL;
312 	return area->Right();
313 }
314 
315 
316 YTab*
317 BALMLayout::TopOf(const BView* view) const
318 {
319 	Area* area = AreaFor(view);
320 	if (!area)
321 		return NULL;
322 	return area->Top();
323 }
324 
325 
326 YTab*
327 BALMLayout::TopOf(const BLayoutItem* item) const
328 {
329 	Area* area = AreaFor(item);
330 	if (!area)
331 		return NULL;
332 	return area->Top();
333 }
334 
335 
336 YTab*
337 BALMLayout::BottomOf(const BView* view) const
338 {
339 	Area* area = AreaFor(view);
340 	if (!area)
341 		return NULL;
342 	return area->Bottom();
343 }
344 
345 
346 YTab*
347 BALMLayout::BottomOf(const BLayoutItem* item) const
348 {
349 	Area* area = AreaFor(item);
350 	if (!area)
351 		return NULL;
352 	return area->Bottom();
353 }
354 
355 
356 BLayoutItem*
357 BALMLayout::AddView(BView* child)
358 {
359 	return AddView(-1, child);
360 }
361 
362 
363 BLayoutItem*
364 BALMLayout::AddView(int32 index, BView* child)
365 {
366 	return BAbstractLayout::AddView(index, child);
367 }
368 
369 
370 /**
371  * Adds a new area to the specification, automatically setting preferred size constraints.
372  *
373  * @param left			left border
374  * @param top			top border
375  * @param right		right border
376  * @param bottom		bottom border
377  * @param content		the control which is the area content
378  * @return the new area
379  */
380 Area*
381 BALMLayout::AddView(BView* view, XTab* left, YTab* top, XTab* right,
382 	YTab* bottom)
383 {
384 	BLayoutItem* item = _LayoutItemToAdd(view);
385 	Area* area = AddItem(item, left, top, right, bottom);
386 	if (!area) {
387 		if (item != view->GetLayout())
388 			delete item;
389 		return NULL;
390 	}
391 	return area;
392 }
393 
394 
395 /**
396  * Adds a new area to the specification, automatically setting preferred size constraints.
397  *
398  * @param row			the row that defines the top and bottom border
399  * @param column		the column that defines the left and right border
400  * @param content		the control which is the area content
401  * @return the new area
402  */
403 Area*
404 BALMLayout::AddView(BView* view, Row* row, Column* column)
405 {
406 	BLayoutItem* item = _LayoutItemToAdd(view);
407 	Area* area = AddItem(item, row, column);
408 	if (!area) {
409 		if (item != view->GetLayout())
410 			delete item;
411 		return NULL;
412 	}
413 	return area;
414 }
415 
416 
417 bool
418 BALMLayout::AddItem(BLayoutItem* item)
419 {
420 	return AddItem(-1, item);
421 }
422 
423 
424 bool
425 BALMLayout::AddItem(int32 index, BLayoutItem* item)
426 {
427 	if (!item)
428 		return false;
429 
430 	// simply add the item at the upper right corner of the previous item
431 	// TODO maybe find a more elegant solution
432 	XTab* left = Left();
433 	YTab* top = Top();
434 
435 	// check range
436 	if (index < 0 || index > CountItems())
437 		index = CountItems();
438 
439 	// for index = 0 we already have set the right tabs
440 	if (index != 0) {
441 		BLayoutItem* prevItem = ItemAt(index - 1);
442 		Area* area = AreaFor(prevItem);
443 		if (area) {
444 			left = area->Right();
445 			top = area->Top();
446 		}
447 	}
448 	Area* area = AddItem(item, left, top);
449 	return area ? true : false;
450 }
451 
452 
453 Area*
454 BALMLayout::AddItem(BLayoutItem* item, XTab* left, YTab* top, XTab* _right,
455 	YTab* _bottom)
456 {
457 	BReference<XTab> right = _right;
458 	if (right.Get() == NULL)
459 		right = AddXTab();
460 	BReference<YTab> bottom = _bottom;
461 	if (bottom.Get() == NULL)
462 		bottom = AddYTab();
463 
464 	// Area is added int ItemAdded
465 	if (!BAbstractLayout::AddItem(-1, item))
466 		return NULL;
467 	Area* area = AreaFor(item);
468 	if (!area)
469 		return NULL;
470 
471 	area->_Init(fSolver, left, top, right, bottom, fRowColumnManager);
472 
473 	fRowColumnManager->AddArea(area);
474 	return area;
475 }
476 
477 
478 Area*
479 BALMLayout::AddItem(BLayoutItem* item, Row* row, Column* column)
480 {
481 	if (!BAbstractLayout::AddItem(-1, item))
482 		return NULL;
483 	Area* area = AreaFor(item);
484 	if (!area)
485 		return NULL;
486 
487 	area->_Init(fSolver, row, column, fRowColumnManager);
488 
489 	fRowColumnManager->AddArea(area);
490 	return area;
491 }
492 
493 
494 enum {
495 	kLeftBorderIndex = -2,
496 	kTopBorderIndex = -3,
497 	kRightBorderIndex = -4,
498 	kBottomBorderIndex = -5,
499 };
500 
501 
502 bool
503 BALMLayout::SaveLayout(BMessage* archive) const
504 {
505 	archive->MakeEmpty();
506 
507 	archive->AddInt32("nXTabs", CountXTabs());
508 	archive->AddInt32("nYTabs", CountYTabs());
509 
510 	XTabList xTabs = fXTabList;
511 	xTabs.RemoveItem(fLeft);
512 	xTabs.RemoveItem(fRight);
513 	YTabList yTabs = fYTabList;
514 	yTabs.RemoveItem(fTop);
515 	yTabs.RemoveItem(fBottom);
516 
517 	int32 nAreas = CountAreas();
518 	for (int32 i = 0; i < nAreas; i++) {
519 		Area* area = AreaAt(i);
520 		if (area->Left() == fLeft)
521 			archive->AddInt32("left", kLeftBorderIndex);
522 		else
523 			archive->AddInt32("left", xTabs.IndexOf(area->Left()));
524 		if (area->Top() == fTop)
525 			archive->AddInt32("top", kTopBorderIndex);
526 		else
527 			archive->AddInt32("top", yTabs.IndexOf(area->Top()));
528 		if (area->Right() == fRight)
529 			archive->AddInt32("right", kRightBorderIndex);
530 		else
531 			archive->AddInt32("right", xTabs.IndexOf(area->Right()));
532 		if (area->Bottom() == fBottom)
533 			archive->AddInt32("bottom", kBottomBorderIndex);
534 		else
535 			archive->AddInt32("bottom", yTabs.IndexOf(area->Bottom()));
536 	}
537 	return true;
538 }
539 
540 
541 bool
542 BALMLayout::RestoreLayout(const BMessage* archive)
543 {
544 	int32 neededXTabs;
545 	int32 neededYTabs;
546 	if (archive->FindInt32("nXTabs", &neededXTabs) != B_OK)
547 		return false;
548 	if (archive->FindInt32("nYTabs", &neededYTabs) != B_OK)
549 		return false;
550 	// First store a reference to all needed tabs otherwise they might get lost
551 	// while editing the layout
552 	std::vector<BReference<XTab> > newXTabs;
553 	std::vector<BReference<YTab> > newYTabs;
554 	int32 existingXTabs = fXTabList.CountItems();
555 	for (int32 i = 0; i < neededXTabs; i++) {
556 		if (i < existingXTabs)
557 			newXTabs.push_back(BReference<XTab>(fXTabList.ItemAt(i)));
558 		else
559 			newXTabs.push_back(AddXTab());
560 	}
561 	int32 existingYTabs = fYTabList.CountItems();
562 	for (int32 i = 0; i < neededYTabs; i++) {
563 		if (i < existingYTabs)
564 			newYTabs.push_back(BReference<YTab>(fYTabList.ItemAt(i)));
565 		else
566 			newYTabs.push_back(AddYTab());
567 	}
568 
569 	XTabList xTabs = fXTabList;
570 	xTabs.RemoveItem(fLeft);
571 	xTabs.RemoveItem(fRight);
572 	YTabList yTabs = fYTabList;
573 	yTabs.RemoveItem(fTop);
574 	yTabs.RemoveItem(fBottom);
575 
576 	int32 nAreas = CountAreas();
577 	for (int32 i = 0; i < nAreas; i++) {
578 		Area* area = AreaAt(i);
579 		if (area == NULL)
580 			return false;
581 		int32 left = -1;
582 		if (archive->FindInt32("left", i, &left) != B_OK)
583 			break;
584 		int32 top = archive->FindInt32("top", i);
585 		int32 right = archive->FindInt32("right", i);
586 		int32 bottom = archive->FindInt32("bottom", i);
587 
588 		XTab* leftTab = NULL;
589 		YTab* topTab = NULL;
590 		XTab* rightTab = NULL;
591 		YTab* bottomTab = NULL;
592 
593 		if (left == kLeftBorderIndex)
594 			leftTab = fLeft;
595 		else
596 			leftTab = xTabs.ItemAt(left);
597 		if (top == kTopBorderIndex)
598 			topTab = fTop;
599 		else
600 			topTab = yTabs.ItemAt(top);
601 		if (right == kRightBorderIndex)
602 			rightTab = fRight;
603 		else
604 			rightTab = xTabs.ItemAt(right);
605 		if (bottom == kBottomBorderIndex)
606 			bottomTab = fBottom;
607 		else
608 			bottomTab = yTabs.ItemAt(bottom);
609 		if (leftTab == NULL || topTab == NULL || rightTab == NULL
610 			|| bottomTab == NULL)
611 			return false;
612 
613 		area->SetLeft(leftTab);
614 		area->SetTop(topTab);
615 		area->SetRight(rightTab);
616 		area->SetBottom(bottomTab);
617 	}
618 	return true;
619 }
620 
621 
622 /**
623  * Gets the left variable.
624  */
625 XTab*
626 BALMLayout::Left() const
627 {
628 	return fLeft;
629 }
630 
631 
632 /**
633  * Gets the right variable.
634  */
635 XTab*
636 BALMLayout::Right() const
637 {
638 	return fRight;
639 }
640 
641 
642 /**
643  * Gets the top variable.
644  */
645 YTab*
646 BALMLayout::Top() const
647 {
648 	return fTop;
649 }
650 
651 
652 /**
653  * Gets the bottom variable.
654  */
655 YTab*
656 BALMLayout::Bottom() const
657 {
658 	return fBottom;
659 }
660 
661 
662 /**
663  * Gets minimum size.
664  */
665 BSize
666 BALMLayout::BaseMinSize() {
667 	if (fMinSize == kUnsetSize)
668 		fMinSize = _CalculateMinSize();
669 	return fMinSize;
670 }
671 
672 
673 /**
674  * Gets maximum size.
675  */
676 BSize
677 BALMLayout::BaseMaxSize()
678 {
679 	if (fMaxSize == kUnsetSize)
680 		fMaxSize = _CalculateMaxSize();
681 	return fMaxSize;
682 }
683 
684 
685 /**
686  * Gets preferred size.
687  */
688 BSize
689 BALMLayout::BasePreferredSize()
690 {
691 	if (fPreferredSize == kUnsetSize)
692 		fPreferredSize = _CalculatePreferredSize();
693 	return fPreferredSize;
694 }
695 
696 
697 /**
698  * Gets the alignment.
699  */
700 BAlignment
701 BALMLayout::BaseAlignment()
702 {
703 	BAlignment alignment;
704 	alignment.SetHorizontal(B_ALIGN_HORIZONTAL_CENTER);
705 	alignment.SetVertical(B_ALIGN_VERTICAL_CENTER);
706 	return alignment;
707 }
708 
709 
710 /**
711  * Invalidates the layout.
712  * Resets minimum/maximum/preferred size.
713  */
714 void
715 BALMLayout::LayoutInvalidated(bool children)
716 {
717 	fMinSize = kUnsetSize;
718 	fMaxSize = kUnsetSize;
719 	fPreferredSize = kUnsetSize;
720 }
721 
722 
723 bool
724 BALMLayout::ItemAdded(BLayoutItem* item, int32 atIndex)
725 {
726 	item->SetLayoutData(new(std::nothrow) Area(item));
727 	return item->LayoutData() != NULL;
728 }
729 
730 
731 void
732 BALMLayout::ItemRemoved(BLayoutItem* item, int32 fromIndex)
733 {
734 	if (Area* area = AreaFor(item)) {
735 		fRowColumnManager->RemoveArea(area);
736 		item->SetLayoutData(NULL);
737 		delete area;
738 	}
739 }
740 
741 
742 /**
743  * Calculate and set the layout.
744  * If no layout specification is given, a specification is reverse engineered automatically.
745  */
746 void
747 BALMLayout::DoLayout()
748 {
749 	_UpdateAreaConstraints();
750 
751 	// Enforced absolute positions of Right and Bottom
752 	BRect area(LayoutArea());
753 	Right()->SetRange(area.right, area.right);
754 	Bottom()->SetRange(area.bottom, area.bottom);
755 
756 	fSolver->Solve();
757 
758 	// if new layout is infeasible, use previous layout
759 	if (fSolver->Result() == kInfeasible)
760 		return;
761 
762 	if (fSolver->Result() != kOptimal) {
763 		fSolver->Save("failed-layout.txt");
764 		printf("Could not solve the layout specification (%d). ",
765 			fSolver->Result());
766 		printf("Saved specification in file failed-layout.txt\n");
767 	}
768 
769 	// set the calculated positions and sizes for every area
770 	for (int32 i = 0; i < CountItems(); i++)
771 		AreaFor(ItemAt(i))->_DoLayout();
772 }
773 
774 
775 LinearSpec*
776 BALMLayout::Solver() const
777 {
778 	return const_cast<LinearSpec*>(fSolver);
779 }
780 
781 
782 void
783 BALMLayout::SetInsets(float left, float top, float right,
784 	float bottom)
785 {
786 	fLeftInset = BControlLook::ComposeSpacing(left);
787 	fTopInset = BControlLook::ComposeSpacing(top);
788 	fRightInset = BControlLook::ComposeSpacing(right);
789 	fBottomInset = BControlLook::ComposeSpacing(bottom);
790 
791 	InvalidateLayout();
792 }
793 
794 
795 void
796 BALMLayout::SetInsets(float horizontal, float vertical)
797 {
798 	fLeftInset = BControlLook::ComposeSpacing(horizontal);
799 	fRightInset = fLeftInset;
800 
801 	fTopInset = BControlLook::ComposeSpacing(vertical);
802 	fBottomInset = fTopInset;
803 
804 	InvalidateLayout();
805 }
806 
807 
808 void
809 BALMLayout::SetInsets(float insets)
810 {
811 	fLeftInset = BControlLook::ComposeSpacing(insets);
812 	fRightInset = fLeftInset;
813 	fTopInset = fLeftInset;
814 	fBottomInset = fLeftInset;
815 
816 	InvalidateLayout();
817 }
818 
819 
820 void
821 BALMLayout::GetInsets(float* left, float* top, float* right,
822 	float* bottom) const
823 {
824 	if (left)
825 		*left = fLeftInset;
826 	if (top)
827 		*top = fTopInset;
828 	if (right)
829 		*right = fRightInset;
830 	if (bottom)
831 		*bottom = fBottomInset;
832 }
833 
834 
835 void
836 BALMLayout::SetSpacing(float hSpacing, float vSpacing)
837 {
838 	fHSpacing = BControlLook::ComposeSpacing(hSpacing);
839 	fVSpacing = BControlLook::ComposeSpacing(vSpacing);
840 }
841 
842 
843 void
844 BALMLayout::GetSpacing(float *_hSpacing, float *_vSpacing) const
845 {
846 	if (_hSpacing)
847 		*_hSpacing = fHSpacing;
848 	if (_vSpacing)
849 		*_vSpacing = fVSpacing;
850 }
851 
852 
853 float
854 BALMLayout::InsetForTab(XTab* tab) const
855 {
856 	if (tab == fLeft.Get())
857 		return fLeftInset;
858 	if (tab == fRight.Get())
859 		return fRightInset;
860 	return fHSpacing / 2;
861 }
862 
863 
864 float
865 BALMLayout::InsetForTab(YTab* tab) const
866 {
867 	if (tab == fTop.Get())
868 		return fTopInset;
869 	if (tab == fBottom.Get())
870 		return fBottomInset;
871 	return fVSpacing / 2;
872 }
873 
874 
875 BLayoutItem*
876 BALMLayout::_LayoutItemToAdd(BView* view)
877 {
878 	if (view->GetLayout())
879 		return view->GetLayout();
880 	return new(std::nothrow) BViewLayoutItem(view);
881 }
882 
883 
884 /**
885  * Caculates the miminum size.
886  */
887 BSize
888 BALMLayout::_CalculateMinSize()
889 {
890 	_UpdateAreaConstraints();
891 
892 	Right()->SetRange(0, 20000);
893 	Bottom()->SetRange(0, 20000);
894 
895 	return fSolver->MinSize(Right(), Bottom());
896 }
897 
898 
899 /**
900  * Caculates the maximum size.
901  */
902 BSize
903 BALMLayout::_CalculateMaxSize()
904 {
905 	_UpdateAreaConstraints();
906 
907 	Right()->SetRange(0, 20000);
908 	Bottom()->SetRange(0, 20000);
909 
910 	return fSolver->MaxSize(Right(), Bottom());
911 }
912 
913 
914 /**
915  * Caculates the preferred size.
916  */
917 BSize
918 BALMLayout::_CalculatePreferredSize()
919 {
920 	_UpdateAreaConstraints();
921 
922 	Right()->SetRange(0, 20000);
923 	Bottom()->SetRange(0, 20000);
924 
925 	fSolver->Solve();
926 	if (fSolver->Result() != kOptimal) {
927 		fSolver->Save("failed-layout.txt");
928 		printf("Could not solve the layout specification (%d). "
929 			"Saved specification in file failed-layout.txt", fSolver->Result());
930 	}
931 
932 	return BSize(Right()->Value() - Left()->Value(),
933 		Bottom()->Value() - Top()->Value());
934 }
935 
936 
937 void
938 BALMLayout::_UpdateAreaConstraints()
939 {
940 	for (int i = 0; i < CountItems(); i++)
941 		AreaFor(ItemAt(i))->InvalidateSizeConstraints();
942 	fRowColumnManager->UpdateConstraints();
943 }
944