xref: /haiku/src/libs/alm/ALMLayout.cpp (revision 899e0ef82b5624ace2ccfa5f5a58c8ebee54aaef)
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 
23 using BPrivate::AutoDeleter;
24 using namespace LinearProgramming;
25 
26 
27 const BSize kUnsetSize(B_SIZE_UNSET, B_SIZE_UNSET);
28 
29 
30 namespace {
31 
32 const char* kSolverField = "BALMLayout:solver";
33 const char* kBadLayoutPolicyField = "BALMLayout:policy";
34 const char* kXTabsField = "BALMLayout:xtabs";
35 const char* kYTabsField = "BALMLayout:ytabs";
36 const char* kMyTabsField = "BALMLayout:tabs";
37 const char* kInsetsField = "BALMLayout:insets";
38 const char* kSpacingField = "BALMLayout:spacing";
39 
40 const char* kTabsField = "BALMLayout:item:tabs";
41 const char* kItemAspectRatio = "BALMLayout:item:aspect";
42 const char* kItemPenalties = "BALMLayout:item:penalties";
43 const char* kItemInsets = "BALMLayout:item:insets";
44 
45 int CompareXTabFunc(const XTab* tab1, const XTab* tab2);
46 int CompareYTabFunc(const YTab* tab1, const YTab* tab2);
47 
48 };
49 
50 
51 namespace BALM {
52 
53 
54 template <class T>
55 struct 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 
94 private:
95 	BObjectList<T>* _TabList();
96 
97 	T*				fTab;
98 	BALMLayout*		fLayout;
99 	int32			fIndex;
100 };
101 
102 
103 template <>
104 BObjectList<XTab>*
105 BALMLayout::TabAddTransaction<XTab>::_TabList()
106 {
107 	return &fLayout->fXTabList;
108 }
109 
110 
111 template <>
112 BObjectList<YTab>*
113 BALMLayout::TabAddTransaction<YTab>::_TabList()
114 {
115 	return &fLayout->fYTabList;
116 }
117 
118 
119 }; // end namespace BALM
120 
121 
122 BALM::BALMLayout::BadLayoutPolicy::BadLayoutPolicy()
123 {
124 }
125 
126 
127 BALM::BALMLayout::BadLayoutPolicy::BadLayoutPolicy(BMessage* archive)
128 	:
129 	BArchivable(archive)
130 {
131 }
132 
133 
134 BALM::BALMLayout::BadLayoutPolicy::~BadLayoutPolicy()
135 {
136 }
137 
138 
139 BALM::BALMLayout::DefaultPolicy::DefaultPolicy()
140 {
141 }
142 
143 
144 BALM::BALMLayout::DefaultPolicy::DefaultPolicy(BMessage* archive)
145 	:
146 	BadLayoutPolicy(archive)
147 {
148 }
149 
150 
151 BALM::BALMLayout::DefaultPolicy::~DefaultPolicy()
152 {
153 }
154 
155 
156 bool
157 BALM::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 
171 status_t
172 BALM::BALMLayout::DefaultPolicy::Archive(BMessage* archive, bool deep) const
173 {
174 	return BadLayoutPolicy::Archive(archive, deep);
175 }
176 
177 
178 BArchivable*
179 BALM::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 
187 class BALMLayout::BALMLayoutSpecListener
188 	: public LinearProgramming::SpecificationListener {
189 public:
190 	BALMLayoutSpecListener(BALMLayout* layout)
191 		:
192 		fLayout(layout)
193 	{
194 	}
195 
196 	void ConstraintRemoved(Constraint* constraint)
197 	{
198 		fLayout->fConstraints.RemoveItem(constraint);
199 	}
200 
201 private:
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  */
212 BALMLayout::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 
247 BALMLayout::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 
309 BALMLayout::~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  */
332 BReference<XTab>
333 BALMLayout::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 
351 void
352 BALMLayout::AddXTabs(BReference<XTab>* tabs, uint32 count)
353 {
354 	for (uint32 i = 0; i < count; i++)
355 		tabs[i] = AddXTab();
356 }
357 
358 
359 void
360 BALMLayout::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  */
372 BReference<YTab>
373 BALMLayout::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 
391 int32
392 BALMLayout::CountXTabs() const
393 {
394 	return fXTabList.CountItems();
395 }
396 
397 
398 int32
399 BALMLayout::CountYTabs() const
400 {
401 	return fYTabList.CountItems();
402 }
403 
404 
405 XTab*
406 BALMLayout::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 
417 XTab*
418 BALMLayout::XTabAt(int32 index) const
419 {
420 	return fXTabList.ItemAt(index);
421 }
422 
423 
424 YTab*
425 BALMLayout::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 
436 YTab*
437 BALMLayout::YTabAt(int32 index) const
438 {
439 	return fYTabList.ItemAt(index);
440 }
441 
442 
443 const XTabList
444 BALMLayout::GetXTabs() const
445 {
446 	return fXTabList;
447 }
448 
449 
450 const YTabList
451 BALMLayout::GetYTabs() const
452 {
453 	return fYTabList;
454 }
455 
456 
457 int32
458 BALMLayout::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 
469 int32
470 BALMLayout::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 
481 int32
482 BALMLayout::CountConstraints() const
483 {
484 	return fConstraints.CountItems();
485 }
486 
487 
488 Constraint*
489 BALMLayout::ConstraintAt(int32 index) const
490 {
491 	return fConstraints.ItemAt(index);
492 }
493 
494 
495 bool
496 BALMLayout::AddConstraint(Constraint* constraint)
497 {
498 	fConstraints.AddItem(constraint);
499 	return Solver()->AddConstraint(constraint);
500 }
501 
502 
503 bool
504 BALMLayout::RemoveConstraint(Constraint* constraint,
505 	bool deleteConstraint)
506 {
507 	if (!fConstraints.RemoveItem(constraint))
508 		return false;
509 	return Solver()->RemoveConstraint(constraint, deleteConstraint);
510 }
511 
512 
513 Constraint*
514 BALMLayout::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 
524 Constraint*
525 BALMLayout::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 
535 Constraint*
536 BALMLayout::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 
547 Constraint*
548 BALMLayout::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 
560 namespace {
561 
562 
563 int
564 CompareXTabFunc(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 
574 int
575 CompareYTabFunc(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  */
595 Row*
596 BALMLayout::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  */
615 Column*
616 BALMLayout::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 
628 Area*
629 BALMLayout::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  */
647 Area*
648 BALMLayout::AreaFor(const BView* control) const
649 {
650 	return AreaFor(ItemAt(IndexOfView(const_cast<BView*>(control))));
651 }
652 
653 
654 Area*
655 BALMLayout::AreaFor(const BLayoutItem* item) const
656 {
657 	if (!item)
658 		return NULL;
659 	return static_cast<Area*>(item->LayoutData());
660 }
661 
662 
663 int32
664 BALMLayout::CountAreas() const
665 {
666 	return CountItems();
667 }
668 
669 
670 Area*
671 BALMLayout::AreaAt(int32 index) const
672 {
673 	return AreaFor(ItemAt(index));
674 }
675 
676 
677 XTab*
678 BALMLayout::LeftOf(const BView* view) const
679 {
680 	Area* area = AreaFor(view);
681 	if (!area)
682 		return NULL;
683 	return area->Left();
684 }
685 
686 
687 XTab*
688 BALMLayout::LeftOf(const BLayoutItem* item) const
689 {
690 	Area* area = AreaFor(item);
691 	if (!area)
692 		return NULL;
693 	return area->Left();
694 }
695 
696 
697 XTab*
698 BALMLayout::RightOf(const BView* view) const
699 {
700 	Area* area = AreaFor(view);
701 	if (!area)
702 		return NULL;
703 	return area->Right();
704 }
705 
706 
707 XTab*
708 BALMLayout::RightOf(const BLayoutItem* item) const
709 {
710 	Area* area = AreaFor(item);
711 	if (!area)
712 		return NULL;
713 	return area->Right();
714 }
715 
716 
717 YTab*
718 BALMLayout::TopOf(const BView* view) const
719 {
720 	Area* area = AreaFor(view);
721 	if (!area)
722 		return NULL;
723 	return area->Top();
724 }
725 
726 
727 YTab*
728 BALMLayout::TopOf(const BLayoutItem* item) const
729 {
730 	Area* area = AreaFor(item);
731 	if (!area)
732 		return NULL;
733 	return area->Top();
734 }
735 
736 
737 YTab*
738 BALMLayout::BottomOf(const BView* view) const
739 {
740 	Area* area = AreaFor(view);
741 	if (!area)
742 		return NULL;
743 	return area->Bottom();
744 }
745 
746 
747 YTab*
748 BALMLayout::BottomOf(const BLayoutItem* item) const
749 {
750 	Area* area = AreaFor(item);
751 	if (!area)
752 		return NULL;
753 	return area->Bottom();
754 }
755 
756 
757 BLayoutItem*
758 BALMLayout::AddView(BView* child)
759 {
760 	return AddView(-1, child);
761 }
762 
763 
764 BLayoutItem*
765 BALMLayout::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  */
781 Area*
782 BALMLayout::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  */
804 Area*
805 BALMLayout::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 
818 bool
819 BALMLayout::AddItem(BLayoutItem* item)
820 {
821 	return AddItem(-1, item);
822 }
823 
824 
825 bool
826 BALMLayout::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 
854 Area*
855 BALMLayout::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 
914 Area*
915 BALMLayout::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  */
934 XTab*
935 BALMLayout::Left() const
936 {
937 	return fLeft;
938 }
939 
940 
941 /**
942  * Gets the right variable.
943  */
944 XTab*
945 BALMLayout::Right() const
946 {
947 	return fRight;
948 }
949 
950 
951 /**
952  * Gets the top variable.
953  */
954 YTab*
955 BALMLayout::Top() const
956 {
957 	return fTop;
958 }
959 
960 
961 /**
962  * Gets the bottom variable.
963  */
964 YTab*
965 BALMLayout::Bottom() const
966 {
967 	return fBottom;
968 }
969 
970 
971 void
972 BALMLayout::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 
982 struct BALMLayout::BadLayoutPolicy*
983 BALMLayout::GetBadLayoutPolicy() const
984 {
985 	return fBadLayoutPolicy;
986 }
987 
988 
989 /**
990  * Gets minimum size.
991  */
992 BSize
993 BALMLayout::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  */
1005 BSize
1006 BALMLayout::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  */
1018 BSize
1019 BALMLayout::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  */
1032 BAlignment
1033 BALMLayout::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 
1042 status_t
1043 BALMLayout::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 
1086 BArchivable*
1087 BALMLayout::Instantiate(BMessage* from)
1088 {
1089 	if (validate_instantiation(from, "BALM::BALMLayout"))
1090 		return new BALMLayout(from);
1091 	return NULL;
1092 }
1093 
1094 
1095 status_t
1096 BALMLayout::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 
1126 status_t
1127 BALMLayout::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 
1179 status_t
1180 BALMLayout::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 
1262 status_t
1263 BALMLayout::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  */
1275 void
1276 BALMLayout::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 
1289 bool
1290 BALMLayout::ItemAdded(BLayoutItem* item, int32 atIndex)
1291 {
1292 	item->SetLayoutData(new(std::nothrow) Area(item));
1293 	return item->LayoutData() != NULL;
1294 }
1295 
1296 
1297 void
1298 BALMLayout::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  */
1312 void
1313 BALMLayout::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 
1331 LinearSpec*
1332 BALMLayout::Solver() const
1333 {
1334 	return fSolver->Solver();
1335 }
1336 
1337 
1338 ResultType
1339 BALMLayout::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 
1349 void
1350 BALMLayout::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 
1362 void
1363 BALMLayout::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 
1375 void
1376 BALMLayout::SetInsets(float insets)
1377 {
1378 	fLeftInset = BControlLook::ComposeSpacing(insets);
1379 	fRightInset = fLeftInset;
1380 	fTopInset = fLeftInset;
1381 	fBottomInset = fLeftInset;
1382 
1383 	InvalidateLayout();
1384 }
1385 
1386 
1387 void
1388 BALMLayout::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 
1402 void
1403 BALMLayout::SetSpacing(float hSpacing, float vSpacing)
1404 {
1405 	fHSpacing = BControlLook::ComposeSpacing(hSpacing);
1406 	fVSpacing = BControlLook::ComposeSpacing(vSpacing);
1407 }
1408 
1409 
1410 void
1411 BALMLayout::GetSpacing(float *_hSpacing, float *_vSpacing) const
1412 {
1413 	if (_hSpacing)
1414 		*_hSpacing = fHSpacing;
1415 	if (_vSpacing)
1416 		*_vSpacing = fVSpacing;
1417 }
1418 
1419 
1420 float
1421 BALMLayout::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 
1431 float
1432 BALMLayout::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 
1442 void
1443 BALMLayout::UpdateConstraints(BLayoutContext* context)
1444 {
1445 	for (int i = 0; i < CountItems(); i++)
1446 		AreaFor(ItemAt(i))->InvalidateSizeConstraints();
1447 	fRowColumnManager->UpdateConstraints();
1448 }
1449 
1450 
1451 void BALMLayout::_RemoveSelfFromTab(XTab* tab) { tab->LayoutLeaving(this); }
1452 void BALMLayout::_RemoveSelfFromTab(YTab* tab) { tab->LayoutLeaving(this); }
1453 
1454 bool BALMLayout::_HasTabInLayout(XTab* tab) { return tab->IsInLayout(this); }
1455 bool BALMLayout::_HasTabInLayout(YTab* tab) { return tab->IsInLayout(this); }
1456 
1457 bool BALMLayout::_AddedTab(XTab* tab) { return tab->AddedToLayout(this); }
1458 bool BALMLayout::_AddedTab(YTab* tab) { return tab->AddedToLayout(this); }
1459 
1460 
1461 BLayoutItem*
1462 BALMLayout::_LayoutItemToAdd(BView* view)
1463 {
1464 	if (view->GetLayout())
1465 		return view->GetLayout();
1466 	return new(std::nothrow) BViewLayoutItem(view);
1467 }
1468 
1469 
1470 void
1471 BALMLayout::_SetSolver(SharedSolver* solver)
1472 {
1473 	fSolver = solver;
1474 	fSolver->AcquireReference();
1475 	fSolver->RegisterLayout(this);
1476 	fRowColumnManager = new RowColumnManager(Solver());
1477 }
1478 
1479 
1480 status_t
1481 BALMLayout::Perform(perform_code d, void* arg)
1482 {
1483 	return BAbstractLayout::Perform(d, arg);
1484 }
1485 
1486 
1487 void BALMLayout::_ReservedALMLayout1() {}
1488 void BALMLayout::_ReservedALMLayout2() {}
1489 void BALMLayout::_ReservedALMLayout3() {}
1490 void BALMLayout::_ReservedALMLayout4() {}
1491 void BALMLayout::_ReservedALMLayout5() {}
1492 void BALMLayout::_ReservedALMLayout6() {}
1493 void BALMLayout::_ReservedALMLayout7() {}
1494 void BALMLayout::_ReservedALMLayout8() {}
1495 void BALMLayout::_ReservedALMLayout9() {}
1496 void BALMLayout::_ReservedALMLayout10() {}
1497 
1498