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