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