xref: /haiku/src/libs/alm/ALMLayout.cpp (revision d7568eabd2d523e465efeb763e7644ea81af4ae7)
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 int32
283 BALMLayout::IndexOf(XTab* tab, bool ordered)
284 {
285 	if (ordered && !fXTabsSorted) {
286 		Layout();
287 		fXTabList.SortItems(CompareXTabFunc);
288 		fXTabsSorted = true;
289 	}
290 	return fXTabList.IndexOf(tab);
291 }
292 
293 
294 int32
295 BALMLayout::IndexOf(YTab* tab, bool ordered)
296 {
297 	if (ordered && !fYTabsSorted) {
298 		Layout();
299 		fYTabList.SortItems(CompareYTabFunc);
300 		fYTabsSorted = true;
301 	}
302 	return fYTabList.IndexOf(tab);
303 }
304 
305 
306 int
307 CompareXTabFunc(const XTab* tab1, const XTab* tab2)
308 {
309 	if (tab1->Value() < tab2->Value())
310 		return -1;
311 	else if (tab1->Value() == tab2->Value())
312 		return 0;
313 	return 1;
314 }
315 
316 
317 int
318 CompareYTabFunc(const YTab* tab1, const YTab* tab2)
319 {
320 	if (tab1->Value() < tab2->Value())
321 		return -1;
322 	else if (tab1->Value() == tab2->Value())
323 		return 0;
324 	return 1;
325 }
326 
327 
328 /**
329  * Adds a new row to the specification that is glued to the given y-tabs.
330  *
331  * @param top
332  * @param bottom
333  * @return the new row
334  */
335 Row*
336 BALMLayout::AddRow(YTab* _top, YTab* _bottom)
337 {
338 	BReference<YTab> top = _top;
339 	BReference<YTab> bottom = _bottom;
340 	if (_top == NULL)
341 		top = AddYTab();
342 	if (_bottom == NULL)
343 		bottom = AddYTab();
344 	return new(std::nothrow) Row(Solver(), top, bottom);
345 }
346 
347 
348 /**
349  * Adds a new column to the specification that is glued to the given x-tabs.
350  *
351  * @param left
352  * @param right
353  * @return the new column
354  */
355 Column*
356 BALMLayout::AddColumn(XTab* _left, XTab* _right)
357 {
358 	BReference<XTab> left = _left;
359 	BReference<XTab> right = _right;
360 	if (_left == NULL)
361 		left = AddXTab();
362 	if (_right == NULL)
363 		right = AddXTab();
364 	return new(std::nothrow) Column(Solver(), left, right);
365 }
366 
367 
368 Area*
369 BALMLayout::AreaFor(int32 id) const
370 {
371 	int32 areaCount = CountAreas();
372 	for (int32 i = 0; i < areaCount; i++) {
373 		Area* area = AreaAt(i);
374 		if (area->ID() == id)
375 			return area;
376 	}
377 	return NULL;
378 }
379 
380 
381 /**
382  * Finds the area that contains the given control.
383  *
384  * @param control	the control to look for
385  * @return the area that contains the control
386  */
387 Area*
388 BALMLayout::AreaFor(const BView* control) const
389 {
390 	return AreaFor(ItemAt(IndexOfView(const_cast<BView*>(control))));
391 }
392 
393 
394 Area*
395 BALMLayout::AreaFor(const BLayoutItem* item) const
396 {
397 	if (!item)
398 		return NULL;
399 	return static_cast<Area*>(item->LayoutData());
400 }
401 
402 
403 int32
404 BALMLayout::CountAreas() const
405 {
406 	return CountItems();
407 }
408 
409 
410 Area*
411 BALMLayout::AreaAt(int32 index) const
412 {
413 	return AreaFor(ItemAt(index));
414 }
415 
416 
417 XTab*
418 BALMLayout::LeftOf(const BView* view) const
419 {
420 	Area* area = AreaFor(view);
421 	if (!area)
422 		return NULL;
423 	return area->Left();
424 }
425 
426 
427 XTab*
428 BALMLayout::LeftOf(const BLayoutItem* item) const
429 {
430 	Area* area = AreaFor(item);
431 	if (!area)
432 		return NULL;
433 	return area->Left();
434 }
435 
436 
437 XTab*
438 BALMLayout::RightOf(const BView* view) const
439 {
440 	Area* area = AreaFor(view);
441 	if (!area)
442 		return NULL;
443 	return area->Right();
444 }
445 
446 
447 XTab*
448 BALMLayout::RightOf(const BLayoutItem* item) const
449 {
450 	Area* area = AreaFor(item);
451 	if (!area)
452 		return NULL;
453 	return area->Right();
454 }
455 
456 
457 YTab*
458 BALMLayout::TopOf(const BView* view) const
459 {
460 	Area* area = AreaFor(view);
461 	if (!area)
462 		return NULL;
463 	return area->Top();
464 }
465 
466 
467 YTab*
468 BALMLayout::TopOf(const BLayoutItem* item) const
469 {
470 	Area* area = AreaFor(item);
471 	if (!area)
472 		return NULL;
473 	return area->Top();
474 }
475 
476 
477 YTab*
478 BALMLayout::BottomOf(const BView* view) const
479 {
480 	Area* area = AreaFor(view);
481 	if (!area)
482 		return NULL;
483 	return area->Bottom();
484 }
485 
486 
487 YTab*
488 BALMLayout::BottomOf(const BLayoutItem* item) const
489 {
490 	Area* area = AreaFor(item);
491 	if (!area)
492 		return NULL;
493 	return area->Bottom();
494 }
495 
496 
497 BLayoutItem*
498 BALMLayout::AddView(BView* child)
499 {
500 	return AddView(-1, child);
501 }
502 
503 
504 BLayoutItem*
505 BALMLayout::AddView(int32 index, BView* child)
506 {
507 	return BAbstractLayout::AddView(index, child);
508 }
509 
510 
511 /**
512  * Adds a new area to the specification, automatically setting preferred size constraints.
513  *
514  * @param left			left border
515  * @param top			top border
516  * @param right		right border
517  * @param bottom		bottom border
518  * @param content		the control which is the area content
519  * @return the new area
520  */
521 Area*
522 BALMLayout::AddView(BView* view, XTab* left, YTab* top, XTab* right,
523 	YTab* bottom)
524 {
525 	BLayoutItem* item = _LayoutItemToAdd(view);
526 	Area* area = AddItem(item, left, top, right, bottom);
527 	if (!area) {
528 		if (item != view->GetLayout())
529 			delete item;
530 		return NULL;
531 	}
532 	return area;
533 }
534 
535 
536 /**
537  * Adds a new area to the specification, automatically setting preferred size constraints.
538  *
539  * @param row			the row that defines the top and bottom border
540  * @param column		the column that defines the left and right border
541  * @param content		the control which is the area content
542  * @return the new area
543  */
544 Area*
545 BALMLayout::AddView(BView* view, Row* row, Column* column)
546 {
547 	BLayoutItem* item = _LayoutItemToAdd(view);
548 	Area* area = AddItem(item, row, column);
549 	if (!area) {
550 		if (item != view->GetLayout())
551 			delete item;
552 		return NULL;
553 	}
554 	return area;
555 }
556 
557 
558 bool
559 BALMLayout::AddItem(BLayoutItem* item)
560 {
561 	return AddItem(-1, item);
562 }
563 
564 
565 bool
566 BALMLayout::AddItem(int32 index, BLayoutItem* item)
567 {
568 	if (!item)
569 		return false;
570 
571 	// simply add the item at the upper right corner of the previous item
572 	// TODO maybe find a more elegant solution
573 	XTab* left = Left();
574 	YTab* top = Top();
575 
576 	// check range
577 	if (index < 0 || index > CountItems())
578 		index = CountItems();
579 
580 	// for index = 0 we already have set the right tabs
581 	if (index != 0) {
582 		BLayoutItem* prevItem = ItemAt(index - 1);
583 		Area* area = AreaFor(prevItem);
584 		if (area) {
585 			left = area->Right();
586 			top = area->Top();
587 		}
588 	}
589 	Area* area = AddItem(item, left, top);
590 	return area ? true : false;
591 }
592 
593 
594 Area*
595 BALMLayout::AddItem(BLayoutItem* item, XTab* _left, YTab* _top, XTab* _right,
596 	YTab* _bottom)
597 {
598 	if ((_left && !_left->IsSuitableFor(this))
599 			|| (_top && !_top->IsSuitableFor(this))
600 			|| (_right && !_right->IsSuitableFor(this))
601 			|| (_bottom && !_bottom->IsSuitableFor(this)))
602 		debugger("Tab added to unfriendly layout!");
603 
604 	BReference<XTab> right = _right;
605 	if (right.Get() == NULL)
606 		right = AddXTab();
607 	BReference<YTab> bottom = _bottom;
608 	if (bottom.Get() == NULL)
609 		bottom = AddYTab();
610 	BReference<XTab> left = _left;
611 	if (left.Get() == NULL)
612 		left = AddXTab();
613 	BReference<YTab> top = _top;
614 	if (top.Get() == NULL)
615 		top = AddYTab();
616 
617 	TabAddTransaction<XTab> leftTabAdd(this);
618 	if (!leftTabAdd.AttempAdd(left))
619 		return NULL;
620 
621 	TabAddTransaction<YTab> topTabAdd(this);
622 	if (!topTabAdd.AttempAdd(top))
623 		return NULL;
624 
625 	TabAddTransaction<XTab> rightTabAdd(this);
626 	if (!rightTabAdd.AttempAdd(right))
627 		return NULL;
628 
629 	TabAddTransaction<YTab> bottomTabAdd(this);
630 	if (!bottomTabAdd.AttempAdd(bottom))
631 		return NULL;
632 
633 	// Area is added in ItemAdded
634 	if (!BAbstractLayout::AddItem(-1, item))
635 		return NULL;
636 	Area* area = AreaFor(item);
637 	if (!area) {
638 		RemoveItem(item);
639 		return NULL;
640 	}
641 
642 	area->_Init(Solver(), left, top, right, bottom, fRowColumnManager);
643 	fRowColumnManager->AddArea(area);
644 
645 	leftTabAdd.Commit();
646 	topTabAdd.Commit();
647 	rightTabAdd.Commit();
648 	bottomTabAdd.Commit();
649 	return area;
650 }
651 
652 
653 Area*
654 BALMLayout::AddItem(BLayoutItem* item, Row* row, Column* column)
655 {
656 	if (!BAbstractLayout::AddItem(-1, item))
657 		return NULL;
658 	Area* area = AreaFor(item);
659 	if (!area)
660 		return NULL;
661 
662 	area->_Init(Solver(), row, column, fRowColumnManager);
663 
664 	fRowColumnManager->AddArea(area);
665 	return area;
666 }
667 
668 
669 enum {
670 	kLeftBorderIndex = -2,
671 	kTopBorderIndex = -3,
672 	kRightBorderIndex = -4,
673 	kBottomBorderIndex = -5,
674 };
675 
676 
677 bool
678 BALMLayout::SaveLayout(BMessage* archive) const
679 {
680 	archive->MakeEmpty();
681 
682 	archive->AddInt32("nXTabs", CountXTabs());
683 	archive->AddInt32("nYTabs", CountYTabs());
684 
685 	XTabList xTabs = fXTabList;
686 	xTabs.RemoveItem(fLeft);
687 	xTabs.RemoveItem(fRight);
688 	YTabList yTabs = fYTabList;
689 	yTabs.RemoveItem(fTop);
690 	yTabs.RemoveItem(fBottom);
691 
692 	int32 nAreas = CountAreas();
693 	for (int32 i = 0; i < nAreas; i++) {
694 		Area* area = AreaAt(i);
695 		if (area->Left() == fLeft)
696 			archive->AddInt32("left", kLeftBorderIndex);
697 		else
698 			archive->AddInt32("left", xTabs.IndexOf(area->Left()));
699 		if (area->Top() == fTop)
700 			archive->AddInt32("top", kTopBorderIndex);
701 		else
702 			archive->AddInt32("top", yTabs.IndexOf(area->Top()));
703 		if (area->Right() == fRight)
704 			archive->AddInt32("right", kRightBorderIndex);
705 		else
706 			archive->AddInt32("right", xTabs.IndexOf(area->Right()));
707 		if (area->Bottom() == fBottom)
708 			archive->AddInt32("bottom", kBottomBorderIndex);
709 		else
710 			archive->AddInt32("bottom", yTabs.IndexOf(area->Bottom()));
711 	}
712 	return true;
713 }
714 
715 
716 bool
717 BALMLayout::RestoreLayout(const BMessage* archive)
718 {
719 	int32 neededXTabs;
720 	int32 neededYTabs;
721 	if (archive->FindInt32("nXTabs", &neededXTabs) != B_OK)
722 		return false;
723 	if (archive->FindInt32("nYTabs", &neededYTabs) != B_OK)
724 		return false;
725 	// First store a reference to all needed tabs otherwise they might get lost
726 	// while editing the layout
727 	std::vector<BReference<XTab> > newXTabs;
728 	std::vector<BReference<YTab> > newYTabs;
729 	int32 existingXTabs = fXTabList.CountItems();
730 	for (int32 i = 0; i < neededXTabs; i++) {
731 		if (i < existingXTabs)
732 			newXTabs.push_back(BReference<XTab>(fXTabList.ItemAt(i)));
733 		else
734 			newXTabs.push_back(AddXTab());
735 	}
736 	int32 existingYTabs = fYTabList.CountItems();
737 	for (int32 i = 0; i < neededYTabs; i++) {
738 		if (i < existingYTabs)
739 			newYTabs.push_back(BReference<YTab>(fYTabList.ItemAt(i)));
740 		else
741 			newYTabs.push_back(AddYTab());
742 	}
743 
744 	XTabList xTabs = fXTabList;
745 	xTabs.RemoveItem(fLeft);
746 	xTabs.RemoveItem(fRight);
747 	YTabList yTabs = fYTabList;
748 	yTabs.RemoveItem(fTop);
749 	yTabs.RemoveItem(fBottom);
750 
751 	int32 nAreas = CountAreas();
752 	for (int32 i = 0; i < nAreas; i++) {
753 		Area* area = AreaAt(i);
754 		if (area == NULL)
755 			return false;
756 		int32 left = -1;
757 		if (archive->FindInt32("left", i, &left) != B_OK)
758 			break;
759 		int32 top = archive->FindInt32("top", i);
760 		int32 right = archive->FindInt32("right", i);
761 		int32 bottom = archive->FindInt32("bottom", i);
762 
763 		XTab* leftTab = NULL;
764 		YTab* topTab = NULL;
765 		XTab* rightTab = NULL;
766 		YTab* bottomTab = NULL;
767 
768 		if (left == kLeftBorderIndex)
769 			leftTab = fLeft;
770 		else
771 			leftTab = xTabs.ItemAt(left);
772 		if (top == kTopBorderIndex)
773 			topTab = fTop;
774 		else
775 			topTab = yTabs.ItemAt(top);
776 		if (right == kRightBorderIndex)
777 			rightTab = fRight;
778 		else
779 			rightTab = xTabs.ItemAt(right);
780 		if (bottom == kBottomBorderIndex)
781 			bottomTab = fBottom;
782 		else
783 			bottomTab = yTabs.ItemAt(bottom);
784 		if (leftTab == NULL || topTab == NULL || rightTab == NULL
785 			|| bottomTab == NULL)
786 			return false;
787 
788 		area->SetLeft(leftTab);
789 		area->SetTop(topTab);
790 		area->SetRight(rightTab);
791 		area->SetBottom(bottomTab);
792 	}
793 	return true;
794 }
795 
796 
797 /**
798  * Gets the left variable.
799  */
800 XTab*
801 BALMLayout::Left() const
802 {
803 	return fLeft;
804 }
805 
806 
807 /**
808  * Gets the right variable.
809  */
810 XTab*
811 BALMLayout::Right() const
812 {
813 	return fRight;
814 }
815 
816 
817 /**
818  * Gets the top variable.
819  */
820 YTab*
821 BALMLayout::Top() const
822 {
823 	return fTop;
824 }
825 
826 
827 /**
828  * Gets the bottom variable.
829  */
830 YTab*
831 BALMLayout::Bottom() const
832 {
833 	return fBottom;
834 }
835 
836 
837 void
838 BALMLayout::SetBadLayoutPolicy(BadLayoutPolicy* policy)
839 {
840 	if (fBadLayoutPolicy != policy)
841 		delete fBadLayoutPolicy;
842 	if (policy == NULL)
843 		policy = new DefaultPolicy();
844 	fBadLayoutPolicy = policy;
845 }
846 
847 
848 struct BALMLayout::BadLayoutPolicy*
849 BALMLayout::GetBadLayoutPolicy() const
850 {
851 	return fBadLayoutPolicy;
852 }
853 
854 
855 /**
856  * Gets minimum size.
857  */
858 BSize
859 BALMLayout::BaseMinSize()
860 {
861 	ResultType result = fSolver->ValidateMinSize();
862 	if (result != kOptimal && result != kUnbounded)
863 		fBadLayoutPolicy->OnBadLayout(this, result, NULL);
864 	return fMinSize;
865 }
866 
867 
868 /**
869  * Gets maximum size.
870  */
871 BSize
872 BALMLayout::BaseMaxSize()
873 {
874 	ResultType result = fSolver->ValidateMaxSize();
875 	if (result != kOptimal && result != kUnbounded)
876 		fBadLayoutPolicy->OnBadLayout(this, result, NULL);
877 	return fMaxSize;
878 }
879 
880 
881 /**
882  * Gets preferred size.
883  */
884 BSize
885 BALMLayout::BasePreferredSize()
886 {
887 	ResultType result = fSolver->ValidatePreferredSize();
888 	if (result != kOptimal)
889 		fBadLayoutPolicy->OnBadLayout(this, result, NULL);
890 
891 	return fPreferredSize;
892 }
893 
894 
895 /**
896  * Gets the alignment.
897  */
898 BAlignment
899 BALMLayout::BaseAlignment()
900 {
901 	BAlignment alignment;
902 	alignment.SetHorizontal(B_ALIGN_HORIZONTAL_CENTER);
903 	alignment.SetVertical(B_ALIGN_VERTICAL_CENTER);
904 	return alignment;
905 }
906 
907 
908 /**
909  * Invalidates the layout.
910  * Resets minimum/maximum/preferred size.
911  */
912 void
913 BALMLayout::LayoutInvalidated(bool children)
914 {
915 	fMinSize = kUnsetSize;
916 	fMaxSize = kUnsetSize;
917 	fPreferredSize = kUnsetSize;
918 	fXTabsSorted = false;
919 	fYTabsSorted = false;
920 
921 	fSolver->Invalidate(children);
922 }
923 
924 
925 bool
926 BALMLayout::ItemAdded(BLayoutItem* item, int32 atIndex)
927 {
928 	item->SetLayoutData(new(std::nothrow) Area(item));
929 	return item->LayoutData() != NULL;
930 }
931 
932 
933 void
934 BALMLayout::ItemRemoved(BLayoutItem* item, int32 fromIndex)
935 {
936 	if (Area* area = AreaFor(item)) {
937 		fRowColumnManager->RemoveArea(area);
938 		item->SetLayoutData(NULL);
939 		delete area;
940 	}
941 }
942 
943 
944 /**
945  * Calculate and set the layout.
946  * If no layout specification is given, a specification is reverse engineered automatically.
947  */
948 void
949 BALMLayout::DoLayout()
950 {
951 	BLayoutContext* context = LayoutContext();
952 	ResultType result = fSolver->ValidateLayout(context);
953 	if (result != kOptimal
954 			&& !fBadLayoutPolicy->OnBadLayout(this, result, context)) {
955 		return;
956 	}
957 
958 	// set the calculated positions and sizes for every area
959 	for (int32 i = 0; i < CountItems(); i++)
960 		AreaFor(ItemAt(i))->_DoLayout(LayoutArea().LeftTop());
961 
962 	fXTabsSorted = false;
963 	fYTabsSorted = false;
964 }
965 
966 
967 LinearSpec*
968 BALMLayout::Solver() const
969 {
970 	return fSolver->Solver();
971 }
972 
973 
974 void
975 BALMLayout::SetInsets(float left, float top, float right,
976 	float bottom)
977 {
978 	fLeftInset = BControlLook::ComposeSpacing(left);
979 	fTopInset = BControlLook::ComposeSpacing(top);
980 	fRightInset = BControlLook::ComposeSpacing(right);
981 	fBottomInset = BControlLook::ComposeSpacing(bottom);
982 
983 	InvalidateLayout();
984 }
985 
986 
987 void
988 BALMLayout::SetInsets(float horizontal, float vertical)
989 {
990 	fLeftInset = BControlLook::ComposeSpacing(horizontal);
991 	fRightInset = fLeftInset;
992 
993 	fTopInset = BControlLook::ComposeSpacing(vertical);
994 	fBottomInset = fTopInset;
995 
996 	InvalidateLayout();
997 }
998 
999 
1000 void
1001 BALMLayout::SetInsets(float insets)
1002 {
1003 	fLeftInset = BControlLook::ComposeSpacing(insets);
1004 	fRightInset = fLeftInset;
1005 	fTopInset = fLeftInset;
1006 	fBottomInset = fLeftInset;
1007 
1008 	InvalidateLayout();
1009 }
1010 
1011 
1012 void
1013 BALMLayout::GetInsets(float* left, float* top, float* right,
1014 	float* bottom) const
1015 {
1016 	if (left)
1017 		*left = fLeftInset;
1018 	if (top)
1019 		*top = fTopInset;
1020 	if (right)
1021 		*right = fRightInset;
1022 	if (bottom)
1023 		*bottom = fBottomInset;
1024 }
1025 
1026 
1027 void
1028 BALMLayout::SetSpacing(float hSpacing, float vSpacing)
1029 {
1030 	fHSpacing = BControlLook::ComposeSpacing(hSpacing);
1031 	fVSpacing = BControlLook::ComposeSpacing(vSpacing);
1032 }
1033 
1034 
1035 void
1036 BALMLayout::GetSpacing(float *_hSpacing, float *_vSpacing) const
1037 {
1038 	if (_hSpacing)
1039 		*_hSpacing = fHSpacing;
1040 	if (_vSpacing)
1041 		*_vSpacing = fVSpacing;
1042 }
1043 
1044 
1045 float
1046 BALMLayout::InsetForTab(XTab* tab) const
1047 {
1048 	if (tab == fLeft.Get())
1049 		return fLeftInset;
1050 	if (tab == fRight.Get())
1051 		return fRightInset;
1052 	return fHSpacing / 2;
1053 }
1054 
1055 
1056 float
1057 BALMLayout::InsetForTab(YTab* tab) const
1058 {
1059 	if (tab == fTop.Get())
1060 		return fTopInset;
1061 	if (tab == fBottom.Get())
1062 		return fBottomInset;
1063 	return fVSpacing / 2;
1064 }
1065 
1066 
1067 void
1068 BALMLayout::UpdateConstraints(BLayoutContext* context)
1069 {
1070 	for (int i = 0; i < CountItems(); i++)
1071 		AreaFor(ItemAt(i))->InvalidateSizeConstraints();
1072 	fRowColumnManager->UpdateConstraints();
1073 }
1074 
1075 
1076 void BALMLayout::_RemoveSelfFromTab(XTab* tab) { tab->LayoutLeaving(this); }
1077 void BALMLayout::_RemoveSelfFromTab(YTab* tab) { tab->LayoutLeaving(this); }
1078 
1079 bool BALMLayout::_HasTabInLayout(XTab* tab) { return tab->IsInLayout(this); }
1080 bool BALMLayout::_HasTabInLayout(YTab* tab) { return tab->IsInLayout(this); }
1081 
1082 bool BALMLayout::_AddedTab(XTab* tab) { return tab->AddedToLayout(this); }
1083 bool BALMLayout::_AddedTab(YTab* tab) { return tab->AddedToLayout(this); }
1084 
1085 
1086 BLayoutItem*
1087 BALMLayout::_LayoutItemToAdd(BView* view)
1088 {
1089 	if (view->GetLayout())
1090 		return view->GetLayout();
1091 	return new(std::nothrow) BViewLayoutItem(view);
1092 }
1093 
1094 
1095 status_t
1096 BALMLayout::Perform(perform_code d, void* arg)
1097 {
1098 	return BAbstractLayout::Perform(d, arg);
1099 }
1100 
1101 
1102 void BALMLayout::_ReservedALMLayout1() {}
1103 void BALMLayout::_ReservedALMLayout2() {}
1104 void BALMLayout::_ReservedALMLayout3() {}
1105 void BALMLayout::_ReservedALMLayout4() {}
1106 void BALMLayout::_ReservedALMLayout5() {}
1107 void BALMLayout::_ReservedALMLayout6() {}
1108 void BALMLayout::_ReservedALMLayout7() {}
1109 void BALMLayout::_ReservedALMLayout8() {}
1110 void BALMLayout::_ReservedALMLayout9() {}
1111 void BALMLayout::_ReservedALMLayout10() {}
1112 
1113