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