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