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