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