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