xref: /haiku/src/libs/alm/ALMLayout.cpp (revision 574533ef124f30d7710ce46ade1ca5f05935e083)
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 = _LayoutItemToAdd(view);
455 	Area* area = AddItem(item, left, top, right, bottom);
456 	if (!area) {
457 		if (item != view->GetLayout())
458 			delete item;
459 		return NULL;
460 	}
461 	return area;
462 }
463 
464 
465 /**
466  * Adds a new area to the specification, automatically setting preferred size constraints.
467  *
468  * @param row			the row that defines the top and bottom border
469  * @param column		the column that defines the left and right border
470  * @param content		the control which is the area content
471  * @return the new area
472  */
473 Area*
474 BALMLayout::AddView(BView* view, Row* row, Column* column)
475 {
476 	BLayoutItem* item = _LayoutItemToAdd(view);
477 	Area* area = AddItem(item, row, column);
478 	if (!area) {
479 		if (item != view->GetLayout())
480 			delete item;
481 		return NULL;
482 	}
483 	return area;
484 }
485 
486 
487 Area*
488 BALMLayout::AddViewToRight(BView* view, XTab* right, YTab* top, YTab* bottom)
489 {
490 	BLayoutItem* item = _LayoutItemToAdd(view);
491 	Area* area = AddItemToRight(item, right, top, bottom);
492 	if (!area) {
493 		if (item != view->GetLayout())
494 			delete item;
495 		return NULL;
496 	}
497 	return area;
498 }
499 
500 
501 Area*
502 BALMLayout::AddViewToLeft(BView* view, XTab* left, YTab* top, YTab* bottom)
503 {
504 	BLayoutItem* item = _LayoutItemToAdd(view);
505 	Area* area = AddItemToLeft(item, left, top, bottom);
506 	if (!area) {
507 		if (item != view->GetLayout())
508 			delete item;
509 		return NULL;
510 	}
511 	return area;
512 }
513 
514 
515 Area*
516 BALMLayout::AddViewToTop(BView* view, YTab* top, XTab* left, XTab* right)
517 {
518 	BLayoutItem* item = _LayoutItemToAdd(view);
519 	Area* area = AddItemToTop(item, top, left, right);
520 	if (!area) {
521 		if (item != view->GetLayout())
522 			delete item;
523 		return NULL;
524 	}
525 	return area;
526 }
527 
528 
529 Area*
530 BALMLayout::AddViewToBottom(BView* view, YTab* bottom, XTab* left, XTab* right)
531 {
532 	BLayoutItem* item = _LayoutItemToAdd(view);
533 	Area* area = AddItemToBottom(item, bottom, left, right);
534 	if (!area) {
535 		if (item != view->GetLayout())
536 			delete item;
537 		return NULL;
538 	}
539 	return area;
540 }
541 
542 
543 bool
544 BALMLayout::AddItem(BLayoutItem* item)
545 {
546 	return AddItem(-1, item);
547 }
548 
549 
550 bool
551 BALMLayout::AddItem(int32 index, BLayoutItem* item)
552 {
553 	if (!item)
554 		return false;
555 
556 	// simply add the item at the upper right corner of the previous item
557 	// TODO maybe find a more elegant solution
558 	XTab* left = Left();
559 	YTab* top = Top();
560 
561 	// check range
562 	if (index < 0 || index > CountItems())
563 		index = CountItems();
564 
565 	// for index = 0 we already have set the right tabs
566 	if (index != 0) {
567 		BLayoutItem* prevItem = ItemAt(index - 1);
568 		Area* area = AreaFor(prevItem);
569 		if (area) {
570 			left = area->Right();
571 			top = area->Top();
572 		}
573 	}
574 	Area* area = AddItem(item, left, top);
575 	return area ? true : false;
576 }
577 
578 
579 Area*
580 BALMLayout::AddItem(BLayoutItem* item, XTab* left, YTab* top, XTab* _right,
581 	YTab* _bottom)
582 {
583 	BReference<XTab> right = _right;
584 	if (right.Get() == NULL)
585 		right = AddXTab();
586 	BReference<YTab> bottom = _bottom;
587 	if (bottom.Get() == NULL)
588 		bottom = AddYTab();
589 
590 	// Area is added int ItemAdded
591 	if (!BAbstractLayout::AddItem(-1, item))
592 		return NULL;
593 	Area* area = AreaFor(item);
594 	if (!area)
595 		return NULL;
596 	fCurrentArea = area;
597 
598 	area->_Init(fSolver, left, top, right, bottom, fRowColumnManager);
599 
600 	fRowColumnManager->AddArea(area);
601 	return area;
602 }
603 
604 
605 Area*
606 BALMLayout::AddItem(BLayoutItem* item, Row* row, Column* column)
607 {
608 	if (!BAbstractLayout::AddItem(-1, item))
609 		return NULL;
610 	Area* area = AreaFor(item);
611 	if (!area)
612 		return NULL;
613 	fCurrentArea = area;
614 
615 	area->_Init(fSolver, row, column, fRowColumnManager);
616 
617 	fRowColumnManager->AddArea(area);
618 	return area;
619 }
620 
621 
622 Area*
623 BALMLayout::AddItemToRight(BLayoutItem* item, XTab* _right, YTab* top,
624 	YTab* bottom)
625 {
626 	if (fCurrentArea == NULL)
627 		return NULL;
628 
629 	XTab* left = fCurrentArea->Right();
630 	BReference<XTab> right = _right;
631 	if (_right == NULL)
632 		right = AddXTab();
633 	if (!top)
634 		top = fCurrentArea->Top();
635 	if (!bottom)
636 		bottom = fCurrentArea->Bottom();
637 
638 	return AddItem(item, left, top, right, bottom);
639 }
640 
641 
642 Area*
643 BALMLayout::AddItemToLeft(BLayoutItem* item, XTab* _left, YTab* top,
644 	YTab* bottom)
645 {
646 	if (fCurrentArea == NULL)
647 		return NULL;
648 
649 	BReference<XTab> left = _left;
650 	if (_left == NULL)
651 		left = AddXTab();
652 	XTab* right = fCurrentArea->Left();
653 	if (!top)
654 		top = fCurrentArea->Top();
655 	if (!bottom)
656 		bottom = fCurrentArea->Bottom();
657 
658 	return AddItem(item, left, top, right, bottom);
659 }
660 
661 
662 Area*
663 BALMLayout::AddItemToTop(BLayoutItem* item, YTab* _top, XTab* left, XTab* right)
664 {
665 	if (fCurrentArea == NULL)
666 		return NULL;
667 
668 	if (!left)
669 		left = fCurrentArea->Left();
670 	if (!right)
671 		right = fCurrentArea->Right();
672 	BReference<YTab> top = _top;
673 	if (_top == NULL)
674 		top = AddYTab();
675 	YTab* bottom = fCurrentArea->Top();
676 
677 	return AddItem(item, left, top, right, bottom);
678 }
679 
680 
681 Area*
682 BALMLayout::AddItemToBottom(BLayoutItem* item, YTab* _bottom, XTab* left,
683 	XTab* right)
684 {
685 	if (fCurrentArea == NULL)
686 		return NULL;
687 
688 	if (!left)
689 		left = fCurrentArea->Left();
690 	if (!right)
691 		right = fCurrentArea->Right();
692 	YTab* top = fCurrentArea->Bottom();
693 	BReference<YTab> bottom = _bottom;
694 	if (_bottom == NULL)
695 		bottom = AddYTab();
696 
697 	return AddItem(item, left, top, right, bottom);
698 }
699 
700 
701 enum {
702 	kLeftBorderIndex = -2,
703 	kTopBorderIndex = -3,
704 	kRightBorderIndex = -4,
705 	kBottomBorderIndex = -5,
706 };
707 
708 
709 bool
710 BALMLayout::SaveLayout(BMessage* archive) const
711 {
712 	archive->MakeEmpty();
713 
714 	archive->AddInt32("nXTabs", CountXTabs());
715 	archive->AddInt32("nYTabs", CountYTabs());
716 
717 	XTabList xTabs = fXTabList;
718 	xTabs.RemoveItem(fLeft);
719 	xTabs.RemoveItem(fRight);
720 	YTabList yTabs = fYTabList;
721 	yTabs.RemoveItem(fTop);
722 	yTabs.RemoveItem(fBottom);
723 
724 	int32 nAreas = CountAreas();
725 	for (int32 i = 0; i < nAreas; i++) {
726 		Area* area = AreaAt(i);
727 		if (area->Left() == fLeft)
728 			archive->AddInt32("left", kLeftBorderIndex);
729 		else
730 			archive->AddInt32("left", xTabs.IndexOf(area->Left()));
731 		if (area->Top() == fTop)
732 			archive->AddInt32("top", kTopBorderIndex);
733 		else
734 			archive->AddInt32("top", yTabs.IndexOf(area->Top()));
735 		if (area->Right() == fRight)
736 			archive->AddInt32("right", kRightBorderIndex);
737 		else
738 			archive->AddInt32("right", xTabs.IndexOf(area->Right()));
739 		if (area->Bottom() == fBottom)
740 			archive->AddInt32("bottom", kBottomBorderIndex);
741 		else
742 			archive->AddInt32("bottom", yTabs.IndexOf(area->Bottom()));
743 	}
744 	return true;
745 }
746 
747 
748 bool
749 BALMLayout::RestoreLayout(const BMessage* archive)
750 {
751 	int32 neededXTabs;
752 	int32 neededYTabs;
753 	if (archive->FindInt32("nXTabs", &neededXTabs) != B_OK)
754 		return false;
755 	if (archive->FindInt32("nYTabs", &neededYTabs) != B_OK)
756 		return false;
757 	// First store a reference to all needed tabs otherwise they might get lost
758 	// while editing the layout
759 	std::vector<BReference<XTab> > newXTabs;
760 	std::vector<BReference<YTab> > newYTabs;
761 	int32 existingXTabs = fXTabList.CountItems();
762 	for (int32 i = 0; i < neededXTabs; i++) {
763 		if (i < existingXTabs)
764 			newXTabs.push_back(BReference<XTab>(fXTabList.ItemAt(i)));
765 		else
766 			newXTabs.push_back(AddXTab());
767 	}
768 	int32 existingYTabs = fYTabList.CountItems();
769 	for (int32 i = 0; i < neededYTabs; i++) {
770 		if (i < existingYTabs)
771 			newYTabs.push_back(BReference<YTab>(fYTabList.ItemAt(i)));
772 		else
773 			newYTabs.push_back(AddYTab());
774 	}
775 
776 	XTabList xTabs = fXTabList;
777 	xTabs.RemoveItem(fLeft);
778 	xTabs.RemoveItem(fRight);
779 	YTabList yTabs = fYTabList;
780 	yTabs.RemoveItem(fTop);
781 	yTabs.RemoveItem(fBottom);
782 
783 	int32 nAreas = CountAreas();
784 	for (int32 i = 0; i < nAreas; i++) {
785 		Area* area = AreaAt(i);
786 		if (area == NULL)
787 			return false;
788 		int32 left = -1;
789 		if (archive->FindInt32("left", i, &left) != B_OK)
790 			break;
791 		int32 top = archive->FindInt32("top", i);
792 		int32 right = archive->FindInt32("right", i);
793 		int32 bottom = archive->FindInt32("bottom", i);
794 
795 		XTab* leftTab = NULL;
796 		YTab* topTab = NULL;
797 		XTab* rightTab = NULL;
798 		YTab* bottomTab = NULL;
799 
800 		if (left == kLeftBorderIndex)
801 			leftTab = fLeft;
802 		else
803 			leftTab = xTabs.ItemAt(left);
804 		if (top == kTopBorderIndex)
805 			topTab = fTop;
806 		else
807 			topTab = yTabs.ItemAt(top);
808 		if (right == kRightBorderIndex)
809 			rightTab = fRight;
810 		else
811 			rightTab = xTabs.ItemAt(right);
812 		if (bottom == kBottomBorderIndex)
813 			bottomTab = fBottom;
814 		else
815 			bottomTab = yTabs.ItemAt(bottom);
816 		if (leftTab == NULL || topTab == NULL || rightTab == NULL
817 			|| bottomTab == NULL)
818 			return false;
819 
820 		area->SetLeft(leftTab);
821 		area->SetTop(topTab);
822 		area->SetRight(rightTab);
823 		area->SetBottom(bottomTab);
824 	}
825 	return true;
826 }
827 
828 
829 /**
830  * Gets the left variable.
831  */
832 XTab*
833 BALMLayout::Left() const
834 {
835 	return fLeft;
836 }
837 
838 
839 /**
840  * Gets the right variable.
841  */
842 XTab*
843 BALMLayout::Right() const
844 {
845 	return fRight;
846 }
847 
848 
849 /**
850  * Gets the top variable.
851  */
852 YTab*
853 BALMLayout::Top() const
854 {
855 	return fTop;
856 }
857 
858 
859 /**
860  * Gets the bottom variable.
861  */
862 YTab*
863 BALMLayout::Bottom() const
864 {
865 	return fBottom;
866 }
867 
868 
869 /**
870  * Gets minimum size.
871  */
872 BSize
873 BALMLayout::BaseMinSize() {
874 	if (fMinSize == kUnsetSize)
875 		fMinSize = _CalculateMinSize();
876 	return fMinSize;
877 }
878 
879 
880 /**
881  * Gets maximum size.
882  */
883 BSize
884 BALMLayout::BaseMaxSize()
885 {
886 	if (fMaxSize == kUnsetSize)
887 		fMaxSize = _CalculateMaxSize();
888 	return fMaxSize;
889 }
890 
891 
892 /**
893  * Gets preferred size.
894  */
895 BSize
896 BALMLayout::BasePreferredSize()
897 {
898 	if (fPreferredSize == kUnsetSize)
899 		fPreferredSize = _CalculatePreferredSize();
900 	return fPreferredSize;
901 }
902 
903 
904 /**
905  * Gets the alignment.
906  */
907 BAlignment
908 BALMLayout::BaseAlignment()
909 {
910 	BAlignment alignment;
911 	alignment.SetHorizontal(B_ALIGN_HORIZONTAL_CENTER);
912 	alignment.SetVertical(B_ALIGN_VERTICAL_CENTER);
913 	return alignment;
914 }
915 
916 
917 /**
918  * Invalidates the layout.
919  * Resets minimum/maximum/preferred size.
920  */
921 void
922 BALMLayout::LayoutInvalidated(bool children)
923 {
924 	fMinSize = kUnsetSize;
925 	fMaxSize = kUnsetSize;
926 	fPreferredSize = kUnsetSize;
927 }
928 
929 
930 bool
931 BALMLayout::ItemAdded(BLayoutItem* item, int32 atIndex)
932 {
933 	item->SetLayoutData(new(std::nothrow) Area(item));
934 	return item->LayoutData() != NULL;
935 }
936 
937 
938 void
939 BALMLayout::ItemRemoved(BLayoutItem* item, int32 fromIndex)
940 {
941 	if (Area* area = AreaFor(item)) {
942 		fRowColumnManager->RemoveArea(area);
943 		item->SetLayoutData(NULL);
944 		delete area;
945 	}
946 }
947 
948 
949 /**
950  * Calculate and set the layout.
951  * If no layout specification is given, a specification is reverse engineered automatically.
952  */
953 void
954 BALMLayout::DoLayout()
955 {
956 	_UpdateAreaConstraints();
957 
958 	// Enforced absolute positions of Right and Bottom
959 	BRect area(LayoutArea());
960 	Right()->SetRange(area.right, area.right);
961 	Bottom()->SetRange(area.bottom, area.bottom);
962 
963 	fSolver->Solve();
964 
965 	// if new layout is infeasible, use previous layout
966 	if (fSolver->Result() == kInfeasible)
967 		return;
968 
969 	if (fSolver->Result() != kOptimal) {
970 		fSolver->Save("failed-layout.txt");
971 		printf("Could not solve the layout specification (%d). ",
972 			fSolver->Result());
973 		printf("Saved specification in file failed-layout.txt\n");
974 	}
975 
976 	// set the calculated positions and sizes for every area
977 	for (int32 i = 0; i < CountItems(); i++)
978 		AreaFor(ItemAt(i))->_DoLayout();
979 }
980 
981 
982 LinearSpec*
983 BALMLayout::Solver() const
984 {
985 	return const_cast<LinearSpec*>(fSolver);
986 }
987 
988 
989 void
990 BALMLayout::SetInsets(float left, float top, float right,
991 	float bottom)
992 {
993 	fLeftInset = BControlLook::ComposeSpacing(left);
994 	fTopInset = BControlLook::ComposeSpacing(top);
995 	fRightInset = BControlLook::ComposeSpacing(right);
996 	fBottomInset = BControlLook::ComposeSpacing(bottom);
997 
998 	InvalidateLayout();
999 }
1000 
1001 
1002 void
1003 BALMLayout::SetInsets(float horizontal, float vertical)
1004 {
1005 	fLeftInset = BControlLook::ComposeSpacing(horizontal);
1006 	fRightInset = fLeftInset;
1007 
1008 	fTopInset = BControlLook::ComposeSpacing(vertical);
1009 	fBottomInset = fTopInset;
1010 
1011 	InvalidateLayout();
1012 }
1013 
1014 
1015 void
1016 BALMLayout::SetInsets(float insets)
1017 {
1018 	fLeftInset = BControlLook::ComposeSpacing(insets);
1019 	fRightInset = fLeftInset;
1020 	fTopInset = fLeftInset;
1021 	fBottomInset = fLeftInset;
1022 
1023 	InvalidateLayout();
1024 }
1025 
1026 
1027 void
1028 BALMLayout::GetInsets(float* left, float* top, float* right,
1029 	float* bottom) const
1030 {
1031 	if (left)
1032 		*left = fLeftInset;
1033 	if (top)
1034 		*top = fTopInset;
1035 	if (right)
1036 		*right = fRightInset;
1037 	if (bottom)
1038 		*bottom = fBottomInset;
1039 }
1040 
1041 
1042 void
1043 BALMLayout::SetSpacing(float hSpacing, float vSpacing)
1044 {
1045 	fHSpacing = BControlLook::ComposeSpacing(hSpacing);
1046 	fVSpacing = BControlLook::ComposeSpacing(vSpacing);
1047 }
1048 
1049 
1050 void
1051 BALMLayout::GetSpacing(float *_hSpacing, float *_vSpacing) const
1052 {
1053 	if (_hSpacing)
1054 		*_hSpacing = fHSpacing;
1055 	if (_vSpacing)
1056 		*_vSpacing = fVSpacing;
1057 }
1058 
1059 
1060 float
1061 BALMLayout::InsetForTab(XTab* tab)
1062 {
1063 	if (tab == fLeft.Get())
1064 		return fLeftInset;
1065 	if (tab == fRight.Get())
1066 		return fRightInset;
1067 	return fHSpacing / 2;
1068 }
1069 
1070 
1071 float
1072 BALMLayout::InsetForTab(YTab* tab)
1073 {
1074 	if (tab == fTop.Get())
1075 		return fTopInset;
1076 	if (tab == fBottom.Get())
1077 		return fBottomInset;
1078 	return fVSpacing / 2;
1079 }
1080 
1081 
1082 BLayoutItem*
1083 BALMLayout::_LayoutItemToAdd(BView* view)
1084 {
1085 	if (view->GetLayout())
1086 		return view->GetLayout();
1087 	return new(std::nothrow) BViewLayoutItem(view);
1088 }
1089 
1090 
1091 /**
1092  * Caculates the miminum size.
1093  */
1094 BSize
1095 BALMLayout::_CalculateMinSize()
1096 {
1097 	_UpdateAreaConstraints();
1098 
1099 	Right()->SetRange(0, 20000);
1100 	Bottom()->SetRange(0, 20000);
1101 
1102 	return fSolver->MinSize(Right(), Bottom());
1103 }
1104 
1105 
1106 /**
1107  * Caculates the maximum size.
1108  */
1109 BSize
1110 BALMLayout::_CalculateMaxSize()
1111 {
1112 	_UpdateAreaConstraints();
1113 
1114 	Right()->SetRange(0, 20000);
1115 	Bottom()->SetRange(0, 20000);
1116 
1117 	return fSolver->MaxSize(Right(), Bottom());
1118 }
1119 
1120 
1121 /**
1122  * Caculates the preferred size.
1123  */
1124 BSize
1125 BALMLayout::_CalculatePreferredSize()
1126 {
1127 	_UpdateAreaConstraints();
1128 
1129 	Right()->SetRange(0, 20000);
1130 	Bottom()->SetRange(0, 20000);
1131 
1132 	fSolver->Solve();
1133 	if (fSolver->Result() != kOptimal) {
1134 		fSolver->Save("failed-layout.txt");
1135 		printf("Could not solve the layout specification (%d). "
1136 			"Saved specification in file failed-layout.txt", fSolver->Result());
1137 	}
1138 
1139 	return BSize(Right()->Value() - Left()->Value(),
1140 		Bottom()->Value() - Top()->Value());
1141 }
1142 
1143 
1144 void
1145 BALMLayout::_UpdateAreaConstraints()
1146 {
1147 	for (int i = 0; i < CountItems(); i++)
1148 		AreaFor(ItemAt(i))->InvalidateSizeConstraints();
1149 	fRowColumnManager->UpdateConstraints();
1150 }
1151