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