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 <stdio.h>
13#include <vector>
14
15#include <AutoDeleter.h>
16#include <ControlLook.h>
17
18#include "RowColumnManager.h"
19#include "SharedSolver.h"
20#include "ViewLayoutItem.h"
21
22
23using BPrivate::AutoDeleter;
24using namespace LinearProgramming;
25
26
27const BSize kUnsetSize(B_SIZE_UNSET, B_SIZE_UNSET);
28
29
30namespace {
31
32const char* kSolverField = "BALMLayout:solver";
33const char* kBadLayoutPolicyField = "BALMLayout:policy";
34const char* kXTabsField = "BALMLayout:xtabs";
35const char* kYTabsField = "BALMLayout:ytabs";
36const char* kMyTabsField = "BALMLayout:tabs";
37const char* kInsetsField = "BALMLayout:insets";
38const char* kSpacingField = "BALMLayout:spacing";
39
40const char* kTabsField = "BALMLayout:item:tabs";
41const char* kItemAspectRatio = "BALMLayout:item:aspect";
42const char* kItemPenalties = "BALMLayout:item:penalties";
43const char* kItemInsets = "BALMLayout:item:insets";
44
45int CompareXTabFunc(const XTab* tab1, const XTab* tab2);
46int CompareYTabFunc(const YTab* tab1, const YTab* tab2);
47
48};
49
50
51namespace BALM {
52
53
54template <class T>
55struct BALMLayout::TabAddTransaction {
56	~TabAddTransaction()
57	{
58		if (fTab)
59			fLayout->_RemoveSelfFromTab(fTab);
60		if (fIndex > 0)
61			_TabList()->RemoveItemAt(fIndex);
62	}
63
64	TabAddTransaction(BALMLayout* layout)
65		:
66		fTab(NULL),
67		fLayout(layout),
68		fIndex(-1)
69	{
70	}
71
72	bool AttempAdd(T* tab)
73	{
74		if (fLayout->_HasTabInLayout(tab))
75			return true;
76		if (!fLayout->_AddedTab(tab))
77			return false;
78		fTab = tab;
79
80		BObjectList<T>* tabList = _TabList();
81		int32 index = tabList->CountItems();
82		if (!tabList->AddItem(tab, index))
83			return false;
84		fIndex = index;
85		return true;
86	}
87
88	void Commit()
89	{
90		fTab = NULL;
91		fIndex = -1;
92	}
93
94private:
95	BObjectList<T>* _TabList();
96
97	T*				fTab;
98	BALMLayout*		fLayout;
99	int32			fIndex;
100};
101
102
103template <>
104BObjectList<XTab>*
105BALMLayout::TabAddTransaction<XTab>::_TabList()
106{
107	return &fLayout->fXTabList;
108}
109
110
111template <>
112BObjectList<YTab>*
113BALMLayout::TabAddTransaction<YTab>::_TabList()
114{
115	return &fLayout->fYTabList;
116}
117
118
119}; // end namespace BALM
120
121
122BALM::BALMLayout::BadLayoutPolicy::BadLayoutPolicy()
123{
124}
125
126
127BALM::BALMLayout::BadLayoutPolicy::BadLayoutPolicy(BMessage* archive)
128	:
129	BArchivable(archive)
130{
131}
132
133
134BALM::BALMLayout::BadLayoutPolicy::~BadLayoutPolicy()
135{
136}
137
138
139BALM::BALMLayout::DefaultPolicy::DefaultPolicy()
140{
141}
142
143
144BALM::BALMLayout::DefaultPolicy::DefaultPolicy(BMessage* archive)
145	:
146	BadLayoutPolicy(archive)
147{
148}
149
150
151BALM::BALMLayout::DefaultPolicy::~DefaultPolicy()
152{
153}
154
155
156bool
157BALM::BALMLayout::DefaultPolicy::OnBadLayout(BALMLayout* layout,
158	ResultType result, BLayoutContext* context)
159{
160	if (!context)
161		return true;
162
163	if (result == kInfeasible) {
164		printf("BALMLayout failed to solve your layout!\n");
165		return false;
166	} else
167		return true;
168}
169
170
171status_t
172BALM::BALMLayout::DefaultPolicy::Archive(BMessage* archive, bool deep) const
173{
174	return BadLayoutPolicy::Archive(archive, deep);
175}
176
177
178BArchivable*
179BALM::BALMLayout::DefaultPolicy::Instantiate(BMessage* archive)
180{
181	if (validate_instantiation(archive, "BALM::BALMLayout::DefaultPolicy"))
182		return new DefaultPolicy(archive);
183	return NULL;
184}
185
186
187class BALMLayout::BALMLayoutSpecListener
188	: public LinearProgramming::SpecificationListener {
189public:
190	BALMLayoutSpecListener(BALMLayout* layout)
191		:
192		fLayout(layout)
193	{
194	}
195
196	void ConstraintRemoved(Constraint* constraint)
197	{
198		fLayout->fConstraints.RemoveItem(constraint);
199	}
200
201private:
202			BALMLayout*			fLayout;
203};
204
205
206/*!
207 * Constructor.
208 * Creates new layout engine.
209 *
210 * If friendLayout is not NULL the solver of the friend layout is used.
211 */
212BALMLayout::BALMLayout(float hSpacing, float vSpacing, BALMLayout* friendLayout)
213	:
214	fLeftInset(0),
215	fRightInset(0),
216	fTopInset(0),
217	fBottomInset(0),
218	fHSpacing(BControlLook::ComposeSpacing(hSpacing)),
219	fVSpacing(BControlLook::ComposeSpacing(vSpacing)),
220	fXTabsSorted(false),
221	fYTabsSorted(false),
222	fBadLayoutPolicy(new DefaultPolicy())
223{
224	_SetSolver(friendLayout ? friendLayout->fSolver : new SharedSolver());
225
226	fSpecListener = new BALMLayoutSpecListener(this);
227	Solver()->AddListener(fSpecListener);
228
229	fLeft = AddXTab();
230	fRight = AddXTab();
231	fTop = AddYTab();
232	fBottom = AddYTab();
233
234	// the Left tab is always at x-position 0, and the Top tab is always at
235	// y-position 0
236	fLeft->SetRange(0, 0);
237	fTop->SetRange(0, 0);
238
239	// cached layout values
240	// need to be invalidated whenever the layout specification is changed
241	fMinSize = kUnsetSize;
242	fMaxSize = kUnsetSize;
243	fPreferredSize = kUnsetSize;
244}
245
246
247BALMLayout::BALMLayout(BMessage* archive)
248	:
249	BAbstractLayout(BUnarchiver::PrepareArchive(archive)),
250	fSolver(NULL),
251	fLeft(NULL),
252	fRight(NULL),
253	fTop(NULL),
254	fBottom(NULL),
255	fMinSize(kUnsetSize),
256	fMaxSize(kUnsetSize),
257	fPreferredSize(kUnsetSize),
258	fXTabsSorted(false),
259	fYTabsSorted(false),
260	fBadLayoutPolicy(new DefaultPolicy())
261{
262	BUnarchiver unarchiver(archive);
263
264	BRect insets;
265	status_t err = archive->FindRect(kInsetsField, &insets);
266	if (err != B_OK) {
267		unarchiver.Finish(err);
268		return;
269	}
270
271	fLeftInset = insets.left;
272	fRightInset = insets.right;
273	fTopInset = insets.top;
274	fBottomInset = insets.bottom;
275
276
277	BSize spacing;
278	err = archive->FindSize(kSpacingField, &spacing);
279	if (err != B_OK) {
280		unarchiver.Finish(err);
281		return;
282	}
283
284	fHSpacing = spacing.width;
285	fVSpacing = spacing.height;
286
287	int32 tabCount = 0;
288	archive->GetInfo(kXTabsField, NULL, &tabCount);
289	for (int32 i = 0; i < tabCount && err == B_OK; i++)
290		err = unarchiver.EnsureUnarchived(kXTabsField, i);
291
292	archive->GetInfo(kYTabsField, NULL, &tabCount);
293	for (int32 i = 0; i < tabCount && err == B_OK; i++)
294		err = unarchiver.EnsureUnarchived(kYTabsField, i);
295
296	if (err == B_OK && archive->GetInfo(kBadLayoutPolicyField, NULL) == B_OK)
297		err = unarchiver.EnsureUnarchived(kBadLayoutPolicyField);
298
299	if (err == B_OK)
300		err = unarchiver.EnsureUnarchived(kSolverField);
301
302	unarchiver.Finish(err);
303
304	fSpecListener = new BALMLayoutSpecListener(this);
305	Solver()->AddListener(fSpecListener);
306}
307
308
309BALMLayout::~BALMLayout()
310{
311	Solver()->RemoveListener(fSpecListener);
312	delete fSpecListener;
313
314	delete fRowColumnManager;
315	delete fBadLayoutPolicy;
316
317	for (int32 i = 0; i < fConstraints.CountItems(); i++)
318		Solver()->RemoveConstraint(fConstraints.ItemAt(i), true);
319
320	if (fSolver) {
321		fSolver->LayoutLeaving(this);
322		fSolver->ReleaseReference();
323	}
324}
325
326
327/**
328 * Adds a new x-tab to the specification.
329 *
330 * @return the new x-tab
331 */
332BReference<XTab>
333BALMLayout::AddXTab()
334{
335	BReference<XTab> tab(new(std::nothrow) XTab(this), true);
336	if (!tab)
337		return NULL;
338	if (!Solver()->AddVariable(tab))
339		return NULL;
340
341	fXTabList.AddItem(tab);
342	if (!tab->AddedToLayout(this)) {
343		fXTabList.RemoveItem(tab);
344		return NULL;
345	}
346	fXTabsSorted = false;
347	return tab;
348}
349
350
351void
352BALMLayout::AddXTabs(BReference<XTab>* tabs, uint32 count)
353{
354	for (uint32 i = 0; i < count; i++)
355		tabs[i] = AddXTab();
356}
357
358
359void
360BALMLayout::AddYTabs(BReference<YTab>* tabs, uint32 count)
361{
362	for (uint32 i = 0; i < count; i++)
363		tabs[i] = AddYTab();
364}
365
366
367/**
368 * Adds a new y-tab to the specification.
369 *
370 * @return the new y-tab
371 */
372BReference<YTab>
373BALMLayout::AddYTab()
374{
375	BReference<YTab> tab(new(std::nothrow) YTab(this), true);
376	if (!tab.IsSet())
377		return NULL;
378	if (!Solver()->AddVariable(tab))
379		return NULL;
380
381	fYTabList.AddItem(tab);
382	if (!tab->AddedToLayout(this)) {
383		fYTabList.RemoveItem(tab);
384		return NULL;
385	}
386	fYTabsSorted = false;
387	return tab;
388}
389
390
391int32
392BALMLayout::CountXTabs() const
393{
394	return fXTabList.CountItems();
395}
396
397
398int32
399BALMLayout::CountYTabs() const
400{
401	return fYTabList.CountItems();
402}
403
404
405XTab*
406BALMLayout::XTabAt(int32 index, bool ordered)
407{
408	if (ordered && !fXTabsSorted) {
409		Layout();
410		fXTabList.SortItems(CompareXTabFunc);
411		fXTabsSorted = true;
412	}
413	return fXTabList.ItemAt(index);
414}
415
416
417XTab*
418BALMLayout::XTabAt(int32 index) const
419{
420	return fXTabList.ItemAt(index);
421}
422
423
424YTab*
425BALMLayout::YTabAt(int32 index, bool ordered)
426{
427	if (ordered && !fYTabsSorted) {
428		Layout();
429		fYTabList.SortItems(CompareYTabFunc);
430		fYTabsSorted = true;
431	}
432	return fYTabList.ItemAt(index);
433}
434
435
436YTab*
437BALMLayout::YTabAt(int32 index) const
438{
439	return fYTabList.ItemAt(index);
440}
441
442
443const XTabList
444BALMLayout::GetXTabs() const
445{
446	return fXTabList;
447}
448
449
450const YTabList
451BALMLayout::GetYTabs() const
452{
453	return fYTabList;
454}
455
456
457int32
458BALMLayout::IndexOf(XTab* tab, bool ordered)
459{
460	if (ordered && !fXTabsSorted) {
461		Layout();
462		fXTabList.SortItems(CompareXTabFunc);
463		fXTabsSorted = true;
464	}
465	return fXTabList.IndexOf(tab);
466}
467
468
469int32
470BALMLayout::IndexOf(YTab* tab, bool ordered)
471{
472	if (ordered && !fYTabsSorted) {
473		Layout();
474		fYTabList.SortItems(CompareYTabFunc);
475		fYTabsSorted = true;
476	}
477	return fYTabList.IndexOf(tab);
478}
479
480
481int32
482BALMLayout::CountConstraints() const
483{
484	return fConstraints.CountItems();
485}
486
487
488Constraint*
489BALMLayout::ConstraintAt(int32 index) const
490{
491	return fConstraints.ItemAt(index);
492}
493
494
495bool
496BALMLayout::AddConstraint(Constraint* constraint)
497{
498	fConstraints.AddItem(constraint);
499	return Solver()->AddConstraint(constraint);
500}
501
502
503bool
504BALMLayout::RemoveConstraint(Constraint* constraint,
505	bool deleteConstraint)
506{
507	if (!fConstraints.RemoveItem(constraint))
508		return false;
509	return Solver()->RemoveConstraint(constraint, deleteConstraint);
510}
511
512
513Constraint*
514BALMLayout::AddConstraint(double coeff1, Variable* var1, OperatorType op,
515	double rightSide, double penaltyNeg, double penaltyPos)
516{
517	Constraint* constraint = Solver()->AddConstraint(coeff1, var1, op,
518		rightSide, penaltyNeg, penaltyPos);
519	fConstraints.AddItem(constraint);
520	return constraint;
521}
522
523
524Constraint*
525BALMLayout::AddConstraint(double coeff1, Variable* var1, double coeff2,
526	Variable* var2,	OperatorType op, double rightSide, double penaltyNeg,
527	double penaltyPos)
528{
529	Constraint* constraint = Solver()->AddConstraint(coeff1, var1, coeff2, var2,
530		op, rightSide, penaltyNeg, penaltyPos);
531	fConstraints.AddItem(constraint);
532	return constraint;
533}
534
535Constraint*
536BALMLayout::AddConstraint(double coeff1, Variable* var1, double coeff2,
537	Variable* var2, double coeff3, Variable* var3, OperatorType op,
538	double rightSide, double penaltyNeg, double penaltyPos)
539{
540	Constraint* constraint = Solver()->AddConstraint(coeff1, var1, coeff2, var2,
541		coeff3, var3, op, rightSide, penaltyNeg, penaltyPos);
542	fConstraints.AddItem(constraint);
543	return constraint;
544}
545
546
547Constraint*
548BALMLayout::AddConstraint(double coeff1, Variable* var1, double coeff2,
549	Variable* var2, double coeff3, Variable* var3, double coeff4,
550	Variable* var4, OperatorType op, double rightSide, double penaltyNeg,
551	double penaltyPos)
552{
553	Constraint* constraint = Solver()->AddConstraint(coeff1, var1, coeff2, var2,
554		coeff3, var3, coeff4, var4, op, rightSide, penaltyNeg, penaltyPos);
555	fConstraints.AddItem(constraint);
556	return constraint;
557}
558
559
560namespace {
561
562
563int
564CompareXTabFunc(const XTab* tab1, const XTab* tab2)
565{
566	if (tab1->Value() < tab2->Value())
567		return -1;
568	else if (tab1->Value() == tab2->Value())
569		return 0;
570	return 1;
571}
572
573
574int
575CompareYTabFunc(const YTab* tab1, const YTab* tab2)
576{
577	if (tab1->Value() < tab2->Value())
578		return -1;
579	else if (tab1->Value() == tab2->Value())
580		return 0;
581	return 1;
582}
583
584
585}; // end anonymous namespace
586
587
588/**
589 * Adds a new row to the specification that is glued to the given y-tabs.
590 *
591 * @param top
592 * @param bottom
593 * @return the new row
594 */
595Row*
596BALMLayout::AddRow(YTab* _top, YTab* _bottom)
597{
598	BReference<YTab> top = _top;
599	BReference<YTab> bottom = _bottom;
600	if (_top == NULL)
601		top = AddYTab();
602	if (_bottom == NULL)
603		bottom = AddYTab();
604	return new(std::nothrow) Row(Solver(), top, bottom);
605}
606
607
608/**
609 * Adds a new column to the specification that is glued to the given x-tabs.
610 *
611 * @param left
612 * @param right
613 * @return the new column
614 */
615Column*
616BALMLayout::AddColumn(XTab* _left, XTab* _right)
617{
618	BReference<XTab> left = _left;
619	BReference<XTab> right = _right;
620	if (_left == NULL)
621		left = AddXTab();
622	if (_right == NULL)
623		right = AddXTab();
624	return new(std::nothrow) Column(Solver(), left, right);
625}
626
627
628Area*
629BALMLayout::AreaFor(int32 id) const
630{
631	int32 areaCount = CountAreas();
632	for (int32 i = 0; i < areaCount; i++) {
633		Area* area = AreaAt(i);
634		if (area->ID() == id)
635			return area;
636	}
637	return NULL;
638}
639
640
641/**
642 * Finds the area that contains the given control.
643 *
644 * @param control	the control to look for
645 * @return the area that contains the control
646 */
647Area*
648BALMLayout::AreaFor(const BView* control) const
649{
650	return AreaFor(ItemAt(IndexOfView(const_cast<BView*>(control))));
651}
652
653
654Area*
655BALMLayout::AreaFor(const BLayoutItem* item) const
656{
657	if (!item)
658		return NULL;
659	return static_cast<Area*>(item->LayoutData());
660}
661
662
663int32
664BALMLayout::CountAreas() const
665{
666	return CountItems();
667}
668
669
670Area*
671BALMLayout::AreaAt(int32 index) const
672{
673	return AreaFor(ItemAt(index));
674}
675
676
677XTab*
678BALMLayout::LeftOf(const BView* view) const
679{
680	Area* area = AreaFor(view);
681	if (!area)
682		return NULL;
683	return area->Left();
684}
685
686
687XTab*
688BALMLayout::LeftOf(const BLayoutItem* item) const
689{
690	Area* area = AreaFor(item);
691	if (!area)
692		return NULL;
693	return area->Left();
694}
695
696
697XTab*
698BALMLayout::RightOf(const BView* view) const
699{
700	Area* area = AreaFor(view);
701	if (!area)
702		return NULL;
703	return area->Right();
704}
705
706
707XTab*
708BALMLayout::RightOf(const BLayoutItem* item) const
709{
710	Area* area = AreaFor(item);
711	if (!area)
712		return NULL;
713	return area->Right();
714}
715
716
717YTab*
718BALMLayout::TopOf(const BView* view) const
719{
720	Area* area = AreaFor(view);
721	if (!area)
722		return NULL;
723	return area->Top();
724}
725
726
727YTab*
728BALMLayout::TopOf(const BLayoutItem* item) const
729{
730	Area* area = AreaFor(item);
731	if (!area)
732		return NULL;
733	return area->Top();
734}
735
736
737YTab*
738BALMLayout::BottomOf(const BView* view) const
739{
740	Area* area = AreaFor(view);
741	if (!area)
742		return NULL;
743	return area->Bottom();
744}
745
746
747YTab*
748BALMLayout::BottomOf(const BLayoutItem* item) const
749{
750	Area* area = AreaFor(item);
751	if (!area)
752		return NULL;
753	return area->Bottom();
754}
755
756
757BLayoutItem*
758BALMLayout::AddView(BView* child)
759{
760	return AddView(-1, child);
761}
762
763
764BLayoutItem*
765BALMLayout::AddView(int32 index, BView* child)
766{
767	return BAbstractLayout::AddView(index, child);
768}
769
770
771/**
772 * Adds a new area to the specification, automatically setting preferred size constraints.
773 *
774 * @param left			left border
775 * @param top			top border
776 * @param right		right border
777 * @param bottom		bottom border
778 * @param content		the control which is the area content
779 * @return the new area
780 */
781Area*
782BALMLayout::AddView(BView* view, XTab* left, YTab* top, XTab* right,
783	YTab* bottom)
784{
785	BLayoutItem* item = _LayoutItemToAdd(view);
786	Area* area = AddItem(item, left, top, right, bottom);
787	if (!area) {
788		if (item != view->GetLayout())
789			delete item;
790		return NULL;
791	}
792	return area;
793}
794
795
796/**
797 * Adds a new area to the specification, automatically setting preferred size constraints.
798 *
799 * @param row			the row that defines the top and bottom border
800 * @param column		the column that defines the left and right border
801 * @param content		the control which is the area content
802 * @return the new area
803 */
804Area*
805BALMLayout::AddView(BView* view, Row* row, Column* column)
806{
807	BLayoutItem* item = _LayoutItemToAdd(view);
808	Area* area = AddItem(item, row, column);
809	if (!area) {
810		if (item != view->GetLayout())
811			delete item;
812		return NULL;
813	}
814	return area;
815}
816
817
818bool
819BALMLayout::AddItem(BLayoutItem* item)
820{
821	return AddItem(-1, item);
822}
823
824
825bool
826BALMLayout::AddItem(int32 index, BLayoutItem* item)
827{
828	if (!item)
829		return false;
830
831	// simply add the item at the upper right corner of the previous item
832	// TODO maybe find a more elegant solution
833	XTab* left = Left();
834	YTab* top = Top();
835
836	// check range
837	if (index < 0 || index > CountItems())
838		index = CountItems();
839
840	// for index = 0 we already have set the right tabs
841	if (index != 0) {
842		BLayoutItem* prevItem = ItemAt(index - 1);
843		Area* area = AreaFor(prevItem);
844		if (area) {
845			left = area->Right();
846			top = area->Top();
847		}
848	}
849	Area* area = AddItem(item, left, top);
850	return area ? true : false;
851}
852
853
854Area*
855BALMLayout::AddItem(BLayoutItem* item, XTab* _left, YTab* _top, XTab* _right,
856	YTab* _bottom)
857{
858	if ((_left && !_left->IsSuitableFor(this))
859			|| (_top && !_top->IsSuitableFor(this))
860			|| (_right && !_right->IsSuitableFor(this))
861			|| (_bottom && !_bottom->IsSuitableFor(this)))
862		debugger("Tab added to unfriendly layout!");
863
864	BReference<XTab> right = _right;
865	if (!right.IsSet())
866		right = AddXTab();
867	BReference<YTab> bottom = _bottom;
868	if (!bottom.IsSet())
869		bottom = AddYTab();
870	BReference<XTab> left = _left;
871	if (!left.IsSet())
872		left = AddXTab();
873	BReference<YTab> top = _top;
874	if (!top.IsSet())
875		top = AddYTab();
876
877	TabAddTransaction<XTab> leftTabAdd(this);
878	if (!leftTabAdd.AttempAdd(left))
879		return NULL;
880
881	TabAddTransaction<YTab> topTabAdd(this);
882	if (!topTabAdd.AttempAdd(top))
883		return NULL;
884
885	TabAddTransaction<XTab> rightTabAdd(this);
886	if (!rightTabAdd.AttempAdd(right))
887		return NULL;
888
889	TabAddTransaction<YTab> bottomTabAdd(this);
890	if (!bottomTabAdd.AttempAdd(bottom))
891		return NULL;
892
893	// Area is added in ItemAdded
894	if (!BAbstractLayout::AddItem(-1, item))
895		return NULL;
896	Area* area = AreaFor(item);
897	if (!area) {
898		RemoveItem(item);
899		return NULL;
900	}
901
902	fSolver->Invalidate(true);
903	area->_Init(Solver(), left, top, right, bottom, fRowColumnManager);
904	fRowColumnManager->AddArea(area);
905
906	leftTabAdd.Commit();
907	topTabAdd.Commit();
908	rightTabAdd.Commit();
909	bottomTabAdd.Commit();
910	return area;
911}
912
913
914Area*
915BALMLayout::AddItem(BLayoutItem* item, Row* row, Column* column)
916{
917	if (!BAbstractLayout::AddItem(-1, item))
918		return NULL;
919	Area* area = AreaFor(item);
920	if (!area)
921		return NULL;
922
923	fSolver->Invalidate(true);
924	area->_Init(Solver(), row, column, fRowColumnManager);
925
926	fRowColumnManager->AddArea(area);
927	return area;
928}
929
930
931/**
932 * Gets the left variable.
933 */
934XTab*
935BALMLayout::Left() const
936{
937	return fLeft;
938}
939
940
941/**
942 * Gets the right variable.
943 */
944XTab*
945BALMLayout::Right() const
946{
947	return fRight;
948}
949
950
951/**
952 * Gets the top variable.
953 */
954YTab*
955BALMLayout::Top() const
956{
957	return fTop;
958}
959
960
961/**
962 * Gets the bottom variable.
963 */
964YTab*
965BALMLayout::Bottom() const
966{
967	return fBottom;
968}
969
970
971void
972BALMLayout::SetBadLayoutPolicy(BadLayoutPolicy* policy)
973{
974	if (fBadLayoutPolicy != policy)
975		delete fBadLayoutPolicy;
976	if (policy == NULL)
977		policy = new DefaultPolicy();
978	fBadLayoutPolicy = policy;
979}
980
981
982struct BALMLayout::BadLayoutPolicy*
983BALMLayout::GetBadLayoutPolicy() const
984{
985	return fBadLayoutPolicy;
986}
987
988
989/**
990 * Gets minimum size.
991 */
992BSize
993BALMLayout::BaseMinSize()
994{
995	ResultType result = fSolver->ValidateMinSize();
996	if (result != kOptimal && result != kUnbounded)
997		fBadLayoutPolicy->OnBadLayout(this, result, NULL);
998	return fMinSize;
999}
1000
1001
1002/**
1003 * Gets maximum size.
1004 */
1005BSize
1006BALMLayout::BaseMaxSize()
1007{
1008	ResultType result = fSolver->ValidateMaxSize();
1009	if (result != kOptimal && result != kUnbounded)
1010		fBadLayoutPolicy->OnBadLayout(this, result, NULL);
1011	return fMaxSize;
1012}
1013
1014
1015/**
1016 * Gets preferred size.
1017 */
1018BSize
1019BALMLayout::BasePreferredSize()
1020{
1021	ResultType result = fSolver->ValidatePreferredSize();
1022	if (result != kOptimal)
1023		fBadLayoutPolicy->OnBadLayout(this, result, NULL);
1024
1025	return fPreferredSize;
1026}
1027
1028
1029/**
1030 * Gets the alignment.
1031 */
1032BAlignment
1033BALMLayout::BaseAlignment()
1034{
1035	BAlignment alignment;
1036	alignment.SetHorizontal(B_ALIGN_HORIZONTAL_CENTER);
1037	alignment.SetVertical(B_ALIGN_VERTICAL_CENTER);
1038	return alignment;
1039}
1040
1041
1042status_t
1043BALMLayout::Archive(BMessage* into, bool deep) const
1044{
1045	BArchiver archiver(into);
1046	status_t err = BAbstractLayout::Archive(into, deep);
1047	if (err != B_OK)
1048		return archiver.Finish(err);
1049
1050	BRect insets(fLeftInset, fTopInset, fRightInset, fBottomInset);
1051	err = into->AddRect(kInsetsField, insets);
1052	if (err != B_OK)
1053		return archiver.Finish(err);
1054
1055	BSize spacing(fHSpacing, fVSpacing);
1056	err = into->AddSize(kSpacingField, spacing);
1057	if (err != B_OK)
1058		return archiver.Finish(err);
1059
1060	if (deep) {
1061		for (int32 i = CountXTabs() - 1; i >= 0 && err == B_OK; i--)
1062			err = archiver.AddArchivable(kXTabsField, XTabAt(i));
1063
1064		for (int32 i = CountYTabs() - 1; i >= 0 && err == B_OK; i--)
1065			err = archiver.AddArchivable(kYTabsField, YTabAt(i));
1066
1067		err = archiver.AddArchivable(kBadLayoutPolicyField, fBadLayoutPolicy);
1068	}
1069
1070	if (err == B_OK)
1071		err = archiver.AddArchivable(kSolverField, fSolver);
1072
1073	if (err == B_OK)
1074		err = archiver.AddArchivable(kMyTabsField, fLeft);
1075	if (err == B_OK)
1076		err = archiver.AddArchivable(kMyTabsField, fTop);
1077	if (err == B_OK)
1078		err = archiver.AddArchivable(kMyTabsField, fRight);
1079	if (err == B_OK)
1080		err = archiver.AddArchivable(kMyTabsField, fBottom);
1081
1082	return archiver.Finish(err);
1083}
1084
1085
1086BArchivable*
1087BALMLayout::Instantiate(BMessage* from)
1088{
1089	if (validate_instantiation(from, "BALM::BALMLayout"))
1090		return new BALMLayout(from);
1091	return NULL;
1092}
1093
1094
1095status_t
1096BALMLayout::ItemArchived(BMessage* into, BLayoutItem* item, int32 index) const
1097{
1098	BArchiver archiver(into);
1099	status_t err = BAbstractLayout::ItemArchived(into, item, index);
1100	if (err != B_OK)
1101		return err;
1102
1103	Area* area = AreaFor(item);
1104	err = into->AddSize(kItemPenalties, area->fShrinkPenalties);
1105	if (err == B_OK)
1106		err = into->AddSize(kItemPenalties, area->fGrowPenalties);
1107	if (err == B_OK)
1108		err = into->AddSize(kItemInsets, area->fLeftTopInset);
1109	if (err == B_OK)
1110		err = into->AddSize(kItemInsets, area->fRightBottomInset);
1111	if (err == B_OK)
1112		err = into->AddDouble(kItemAspectRatio, area->fContentAspectRatio);
1113
1114	err = archiver.AddArchivable(kTabsField, area->Left());
1115	if (err == B_OK)
1116		archiver.AddArchivable(kTabsField, area->Top());
1117	if (err == B_OK)
1118		archiver.AddArchivable(kTabsField, area->Right());
1119	if (err == B_OK)
1120		archiver.AddArchivable(kTabsField, area->Bottom());
1121
1122	return err;
1123}
1124
1125
1126status_t
1127BALMLayout::ItemUnarchived(const BMessage* from, BLayoutItem* item,
1128	int32 index)
1129{
1130	BUnarchiver unarchiver(from);
1131	status_t err = BAbstractLayout::ItemUnarchived(from, item, index);
1132	if (err != B_OK)
1133		return err;
1134
1135	Area* area = AreaFor(item);
1136	XTab* left;
1137	XTab* right;
1138	YTab* bottom;
1139	YTab* top;
1140	err = unarchiver.FindObject(kTabsField, index * 4, left);
1141	if (err == B_OK)
1142		err = unarchiver.FindObject(kTabsField, index * 4 + 1, top);
1143	if (err == B_OK)
1144		err = unarchiver.FindObject(kTabsField, index * 4 + 2, right);
1145	if (err == B_OK)
1146		err = unarchiver.FindObject(kTabsField, index * 4 + 3, bottom);
1147
1148	if (err != B_OK)
1149		return err;
1150
1151	area->_Init(Solver(), left, top, right, bottom, fRowColumnManager);
1152	fRowColumnManager->AddArea(area);
1153
1154	err = from->FindSize(kItemPenalties, index * 2, &area->fShrinkPenalties);
1155	if (err != B_OK)
1156		return err;
1157
1158	err = from->FindSize(kItemPenalties, index * 2 + 1, &area->fGrowPenalties);
1159	if (err != B_OK)
1160		return err;
1161
1162	err = from->FindSize(kItemInsets, index * 2, &area->fLeftTopInset);
1163	if (err != B_OK)
1164		return err;
1165
1166	err = from->FindSize(kItemInsets, index * 2 + 1, &area->fRightBottomInset);
1167
1168	if (err == B_OK) {
1169		double contentAspectRatio;
1170		err = from->FindDouble(kItemAspectRatio, index, &contentAspectRatio);
1171		if (err == B_OK)
1172			area->SetContentAspectRatio(contentAspectRatio);
1173	}
1174
1175	return err;
1176}
1177
1178
1179status_t
1180BALMLayout::AllUnarchived(const BMessage* archive)
1181{
1182	BUnarchiver unarchiver(archive);
1183
1184	SharedSolver* solver;
1185	status_t err = unarchiver.FindObject(kSolverField, solver);
1186
1187	if (err != B_OK)
1188		return err;
1189
1190	_SetSolver(solver);
1191
1192	if (archive->GetInfo(kBadLayoutPolicyField, NULL) == B_OK) {
1193		BadLayoutPolicy* policy;
1194		err = unarchiver.FindObject(kBadLayoutPolicyField, policy);
1195		if (err == B_OK)
1196			SetBadLayoutPolicy(policy);
1197	}
1198
1199	LinearSpec* spec = Solver();
1200	int32 tabCount = 0;
1201	archive->GetInfo(kXTabsField, NULL, &tabCount);
1202	for (int32 i = 0; i < tabCount && err == B_OK; i++) {
1203		XTab* tab;
1204		err = unarchiver.FindObject(kXTabsField, i,
1205			BUnarchiver::B_DONT_ASSUME_OWNERSHIP, tab);
1206		spec->AddVariable(tab);
1207		TabAddTransaction<XTab> adder(this);
1208		if (adder.AttempAdd(tab))
1209			adder.Commit();
1210		else
1211			err = B_NO_MEMORY;
1212	}
1213
1214	archive->GetInfo(kYTabsField, NULL, &tabCount);
1215	for (int32 i = 0; i < tabCount; i++) {
1216		YTab* tab;
1217		unarchiver.FindObject(kYTabsField, i,
1218			BUnarchiver::B_DONT_ASSUME_OWNERSHIP, tab);
1219		spec->AddVariable(tab);
1220		TabAddTransaction<YTab> adder(this);
1221		if (adder.AttempAdd(tab))
1222			adder.Commit();
1223		else
1224			err = B_NO_MEMORY;
1225	}
1226
1227
1228	if (err == B_OK) {
1229		XTab* leftTab = NULL;
1230		err = unarchiver.FindObject(kMyTabsField, 0, leftTab);
1231		fLeft = leftTab;
1232	}
1233
1234	if (err == B_OK) {
1235		YTab* topTab = NULL;
1236		err = unarchiver.FindObject(kMyTabsField, 1, topTab);
1237		fTop = topTab;
1238	}
1239
1240	if (err == B_OK) {
1241		XTab* rightTab = NULL;
1242		err = unarchiver.FindObject(kMyTabsField, 2, rightTab);
1243		fRight = rightTab;
1244	}
1245
1246	if (err == B_OK) {
1247		YTab* bottomTab = NULL;
1248		err = unarchiver.FindObject(kMyTabsField, 3, bottomTab);
1249		fBottom = bottomTab;
1250	}
1251
1252	if (err == B_OK) {
1253		fLeft->SetRange(0, 0);
1254		fTop->SetRange(0, 0);
1255
1256   		err = BAbstractLayout::AllUnarchived(archive);
1257	}
1258	return err;
1259}
1260
1261
1262status_t
1263BALMLayout::AllArchived(BMessage* archive) const
1264{
1265	status_t err = BAbstractLayout::AllArchived(archive);
1266
1267	return err;
1268}
1269
1270
1271/**
1272 * Invalidates the layout.
1273 * Resets minimum/maximum/preferred size.
1274 */
1275void
1276BALMLayout::LayoutInvalidated(bool children)
1277{
1278	fMinSize = kUnsetSize;
1279	fMaxSize = kUnsetSize;
1280	fPreferredSize = kUnsetSize;
1281	fXTabsSorted = false;
1282	fYTabsSorted = false;
1283
1284	if (fSolver)
1285		fSolver->Invalidate(children);
1286}
1287
1288
1289bool
1290BALMLayout::ItemAdded(BLayoutItem* item, int32 atIndex)
1291{
1292	item->SetLayoutData(new(std::nothrow) Area(item));
1293	return item->LayoutData() != NULL;
1294}
1295
1296
1297void
1298BALMLayout::ItemRemoved(BLayoutItem* item, int32 fromIndex)
1299{
1300	if (Area* area = AreaFor(item)) {
1301		fRowColumnManager->RemoveArea(area);
1302		item->SetLayoutData(NULL);
1303		delete area;
1304	}
1305}
1306
1307
1308/**
1309 * Calculate and set the layout.
1310 * If no layout specification is given, a specification is reverse engineered automatically.
1311 */
1312void
1313BALMLayout::DoLayout()
1314{
1315	BLayoutContext* context = LayoutContext();
1316	ResultType result = fSolver->ValidateLayout(context);
1317	if (result != kOptimal
1318			&& !fBadLayoutPolicy->OnBadLayout(this, result, context)) {
1319		return;
1320	}
1321
1322	// set the calculated positions and sizes for every area
1323	for (int32 i = 0; i < CountItems(); i++)
1324		AreaFor(ItemAt(i))->_DoLayout(LayoutArea().LeftTop());
1325
1326	fXTabsSorted = false;
1327	fYTabsSorted = false;
1328}
1329
1330
1331LinearSpec*
1332BALMLayout::Solver() const
1333{
1334	return fSolver->Solver();
1335}
1336
1337
1338ResultType
1339BALMLayout::ValidateLayout()
1340{
1341	// we explicitly recaluclate the layout so set the invalidate flag first
1342	fSolver->Invalidate(true);
1343
1344	BLayoutContext* context = LayoutContext();
1345	return fSolver->ValidateLayout(context);
1346}
1347
1348
1349void
1350BALMLayout::SetInsets(float left, float top, float right,
1351	float bottom)
1352{
1353	fLeftInset = BControlLook::ComposeSpacing(left);
1354	fTopInset = BControlLook::ComposeSpacing(top);
1355	fRightInset = BControlLook::ComposeSpacing(right);
1356	fBottomInset = BControlLook::ComposeSpacing(bottom);
1357
1358	InvalidateLayout();
1359}
1360
1361
1362void
1363BALMLayout::SetInsets(float horizontal, float vertical)
1364{
1365	fLeftInset = BControlLook::ComposeSpacing(horizontal);
1366	fRightInset = fLeftInset;
1367
1368	fTopInset = BControlLook::ComposeSpacing(vertical);
1369	fBottomInset = fTopInset;
1370
1371	InvalidateLayout();
1372}
1373
1374
1375void
1376BALMLayout::SetInsets(float insets)
1377{
1378	fLeftInset = BControlLook::ComposeSpacing(insets);
1379	fRightInset = fLeftInset;
1380	fTopInset = fLeftInset;
1381	fBottomInset = fLeftInset;
1382
1383	InvalidateLayout();
1384}
1385
1386
1387void
1388BALMLayout::GetInsets(float* left, float* top, float* right,
1389	float* bottom) const
1390{
1391	if (left)
1392		*left = fLeftInset;
1393	if (top)
1394		*top = fTopInset;
1395	if (right)
1396		*right = fRightInset;
1397	if (bottom)
1398		*bottom = fBottomInset;
1399}
1400
1401
1402void
1403BALMLayout::SetSpacing(float hSpacing, float vSpacing)
1404{
1405	fHSpacing = BControlLook::ComposeSpacing(hSpacing);
1406	fVSpacing = BControlLook::ComposeSpacing(vSpacing);
1407}
1408
1409
1410void
1411BALMLayout::GetSpacing(float *_hSpacing, float *_vSpacing) const
1412{
1413	if (_hSpacing)
1414		*_hSpacing = fHSpacing;
1415	if (_vSpacing)
1416		*_vSpacing = fVSpacing;
1417}
1418
1419
1420float
1421BALMLayout::InsetForTab(XTab* tab) const
1422{
1423	if (tab == fLeft.Get())
1424		return fLeftInset;
1425	if (tab == fRight.Get())
1426		return fRightInset;
1427	return fHSpacing / 2;
1428}
1429
1430
1431float
1432BALMLayout::InsetForTab(YTab* tab) const
1433{
1434	if (tab == fTop.Get())
1435		return fTopInset;
1436	if (tab == fBottom.Get())
1437		return fBottomInset;
1438	return fVSpacing / 2;
1439}
1440
1441
1442void
1443BALMLayout::UpdateConstraints(BLayoutContext* context)
1444{
1445	for (int i = 0; i < CountItems(); i++)
1446		AreaFor(ItemAt(i))->InvalidateSizeConstraints();
1447	fRowColumnManager->UpdateConstraints();
1448}
1449
1450
1451void BALMLayout::_RemoveSelfFromTab(XTab* tab) { tab->LayoutLeaving(this); }
1452void BALMLayout::_RemoveSelfFromTab(YTab* tab) { tab->LayoutLeaving(this); }
1453
1454bool BALMLayout::_HasTabInLayout(XTab* tab) { return tab->IsInLayout(this); }
1455bool BALMLayout::_HasTabInLayout(YTab* tab) { return tab->IsInLayout(this); }
1456
1457bool BALMLayout::_AddedTab(XTab* tab) { return tab->AddedToLayout(this); }
1458bool BALMLayout::_AddedTab(YTab* tab) { return tab->AddedToLayout(this); }
1459
1460
1461BLayoutItem*
1462BALMLayout::_LayoutItemToAdd(BView* view)
1463{
1464	if (view->GetLayout())
1465		return view->GetLayout();
1466	return new(std::nothrow) BViewLayoutItem(view);
1467}
1468
1469
1470void
1471BALMLayout::_SetSolver(SharedSolver* solver)
1472{
1473	fSolver = solver;
1474	fSolver->AcquireReference();
1475	fSolver->RegisterLayout(this);
1476	fRowColumnManager = new RowColumnManager(Solver());
1477}
1478
1479
1480status_t
1481BALMLayout::Perform(perform_code d, void* arg)
1482{
1483	return BAbstractLayout::Perform(d, arg);
1484}
1485
1486
1487void BALMLayout::_ReservedALMLayout1() {}
1488void BALMLayout::_ReservedALMLayout2() {}
1489void BALMLayout::_ReservedALMLayout3() {}
1490void BALMLayout::_ReservedALMLayout4() {}
1491void BALMLayout::_ReservedALMLayout5() {}
1492void BALMLayout::_ReservedALMLayout6() {}
1493void BALMLayout::_ReservedALMLayout7() {}
1494void BALMLayout::_ReservedALMLayout8() {}
1495void BALMLayout::_ReservedALMLayout9() {}
1496void BALMLayout::_ReservedALMLayout10() {}
1497
1498