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