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