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