xref: /haiku/src/libs/alm/ALMLayout.cpp (revision c3e57dc36d5d18673ed9c49d9a3790091852fa0a)
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 #include <math.h>		// for floor
12 #include <new>
13 #include <iostream>
14 #include <vector>
15 
16 #include "RowColumnManager.h"
17 #include "ViewLayoutItem.h"
18 
19 
20 using namespace LinearProgramming;
21 
22 
23 const BSize kUnsetSize(B_SIZE_UNSET, B_SIZE_UNSET);
24 
25 
26 /*!
27  * Constructor.
28  * Creates new layout engine.
29  *
30  * If friendLayout is not NULL the solver of the friend layout is used.
31  */
32 BALMLayout::BALMLayout(float spacing, BALMLayout* friendLayout)
33 	:
34 	fInset(0.0f),
35 	fSpacing(spacing / 2),
36 	fCurrentArea(NULL)
37 {
38 	fSolver = friendLayout ? friendLayout->Solver() : &fOwnSolver;
39 	fRowColumnManager = new RowColumnManager(fSolver);
40 
41 	fLeft = AddXTab();
42 	fRight = AddXTab();
43 	fTop = AddYTab();
44 	fBottom = AddYTab();
45 
46 	// the Left tab is always at x-position 0, and the Top tab is always at
47 	// y-position 0
48 	fLeft->SetRange(0, 0);
49 	fTop->SetRange(0, 0);
50 
51 	// cached layout values
52 	// need to be invalidated whenever the layout specification is changed
53 	fMinSize = kUnsetSize;
54 	fMaxSize = kUnsetSize;
55 	fPreferredSize = kUnsetSize;
56 
57 	fPerformancePath = NULL;
58 }
59 
60 
61 BALMLayout::~BALMLayout()
62 {
63 	delete fRowColumnManager;
64 }
65 
66 
67 /**
68  * Adds a new x-tab to the specification.
69  *
70  * @return the new x-tab
71  */
72 BReference<XTab>
73 BALMLayout::AddXTab()
74 {
75 	BReference<XTab> tab(new(std::nothrow) XTab(this), true);
76 	if (!tab)
77 		return NULL;
78 	if (!fSolver->AddVariable(tab))
79 		return NULL;
80 
81 	fXTabList.AddItem(tab);
82 	return tab;
83 }
84 
85 
86 /**
87  * Adds a new y-tab to the specification.
88  *
89  * @return the new y-tab
90  */
91 BReference<YTab>
92 BALMLayout::AddYTab()
93 {
94 	BReference<YTab> tab(new(std::nothrow) YTab(this), true);
95 	if (tab.Get() == NULL)
96 		return NULL;
97 	if (!fSolver->AddVariable(tab))
98 		return NULL;
99 
100 	fYTabList.AddItem(tab);
101 	return tab;
102 }
103 
104 
105 int32
106 BALMLayout::CountXTabs() const
107 {
108 	return fXTabList.CountItems();
109 }
110 
111 
112 int32
113 BALMLayout::CountYTabs() const
114 {
115 	return fYTabList.CountItems();
116 }
117 
118 
119 XTab*
120 BALMLayout::XTabAt(int32 index) const
121 {
122 	return fXTabList.ItemAt(index);
123 }
124 
125 
126 YTab*
127 BALMLayout::YTabAt(int32 index) const
128 {
129 	return fYTabList.ItemAt(index);
130 }
131 
132 
133 static int
134 compare_x_tab_func(const XTab* tab1, const XTab* tab2)
135 {
136 	if (tab1->Value() < tab2->Value())
137 		return -1;
138 	else if (tab1->Value() == tab2->Value())
139 		return 0;
140 	return 1;
141 }
142 
143 
144 static int
145 compare_y_tab_func(const YTab* tab1, const YTab* tab2)
146 {
147 	if (tab1->Value() < tab2->Value())
148 		return -1;
149 	else if (tab1->Value() == tab2->Value())
150 		return 0;
151 	return 1;
152 }
153 
154 
155 const XTabList&
156 BALMLayout::OrderedXTabs()
157 {
158 	fXTabList.SortItems(compare_x_tab_func);
159 	return fXTabList;
160 }
161 
162 
163 const YTabList&
164 BALMLayout::OrderedYTabs()
165 {
166 	fYTabList.SortItems(compare_y_tab_func);
167 	return fYTabList;
168 }
169 
170 
171 /**
172  * Adds a new row to the specification that is glued to the given y-tabs.
173  *
174  * @param top
175  * @param bottom
176  * @return the new row
177  */
178 Row*
179 BALMLayout::AddRow(YTab* _top, YTab* _bottom)
180 {
181 	BReference<YTab> top = _top;
182 	BReference<YTab> bottom = _bottom;
183 	if (_top == NULL)
184 		top = AddYTab();
185 	if (_bottom == NULL)
186 		bottom = AddYTab();
187 	return new(std::nothrow) Row(fSolver, top, bottom);
188 }
189 
190 
191 /**
192  * Adds a new column to the specification that is glued to the given x-tabs.
193  *
194  * @param left
195  * @param right
196  * @return the new column
197  */
198 Column*
199 BALMLayout::AddColumn(XTab* _left, XTab* _right)
200 {
201 	BReference<XTab> left = _left;
202 	BReference<XTab> right = _right;
203 	if (_left == NULL)
204 		left = AddXTab();
205 	if (_right == NULL)
206 		right = AddXTab();
207 	return new(std::nothrow) Column(fSolver, left, right);
208 }
209 
210 
211 Area*
212 BALMLayout::AreaFor(int32 id) const
213 {
214 	int32 areaCount = CountAreas();
215 	for (int32 i = 0; i < areaCount; i++) {
216 		Area* area = AreaAt(i);
217 		if (area->ID() == id)
218 			return area;
219 	}
220 	return NULL;
221 }
222 
223 
224 /**
225  * Finds the area that contains the given control.
226  *
227  * @param control	the control to look for
228  * @return the area that contains the control
229  */
230 Area*
231 BALMLayout::AreaFor(const BView* control) const
232 {
233 	return AreaFor(ItemAt(IndexOfView(const_cast<BView*>(control))));
234 }
235 
236 
237 Area*
238 BALMLayout::AreaFor(const BLayoutItem* item) const
239 {
240 	if (!item)
241 		return NULL;
242 	return static_cast<Area*>(item->LayoutData());
243 }
244 
245 
246 int32
247 BALMLayout::CountAreas() const
248 {
249 	return CountItems();
250 }
251 
252 
253 Area*
254 BALMLayout::AreaAt(int32 index) const
255 {
256 	return AreaFor(ItemAt(index));
257 }
258 
259 
260 Area*
261 BALMLayout::CurrentArea() const
262 {
263 	return fCurrentArea;
264 }
265 
266 
267 bool
268 BALMLayout::SetCurrentArea(const Area* area)
269 {
270 	fCurrentArea = const_cast<Area*>(area);
271 	return true;
272 }
273 
274 
275 bool
276 BALMLayout::SetCurrentArea(const BView* view)
277 {
278 	Area* area = AreaFor(view);
279 	if (!area)
280 		return false;
281 	fCurrentArea = area;
282 	return true;
283 }
284 
285 
286 bool
287 BALMLayout::SetCurrentArea(const BLayoutItem* item)
288 {
289 	Area* area = AreaFor(item);
290 	if (!area)
291 		return false;
292 	fCurrentArea = area;
293 	return true;
294 }
295 
296 
297 XTab*
298 BALMLayout::LeftOf(const BView* view) const
299 {
300 	Area* area = AreaFor(view);
301 	if (!area)
302 		return NULL;
303 	return area->Left();
304 }
305 
306 
307 XTab*
308 BALMLayout::LeftOf(const BLayoutItem* item) const
309 {
310 	Area* area = AreaFor(item);
311 	if (!area)
312 		return NULL;
313 	return area->Left();
314 }
315 
316 
317 XTab*
318 BALMLayout::RightOf(const BView* view) const
319 {
320 	Area* area = AreaFor(view);
321 	if (!area)
322 		return NULL;
323 	return area->Right();
324 }
325 
326 
327 XTab*
328 BALMLayout::RightOf(const BLayoutItem* item) const
329 {
330 	Area* area = AreaFor(item);
331 	if (!area)
332 		return NULL;
333 	return area->Right();
334 }
335 
336 
337 YTab*
338 BALMLayout::TopOf(const BView* view) const
339 {
340 	Area* area = AreaFor(view);
341 	if (!area)
342 		return NULL;
343 	return area->Top();
344 }
345 
346 
347 YTab*
348 BALMLayout::TopOf(const BLayoutItem* item) const
349 {
350 	Area* area = AreaFor(item);
351 	if (!area)
352 		return NULL;
353 	return area->Top();
354 }
355 
356 
357 YTab*
358 BALMLayout::BottomOf(const BView* view) const
359 {
360 	Area* area = AreaFor(view);
361 	if (!area)
362 		return NULL;
363 	return area->Bottom();
364 }
365 
366 
367 YTab*
368 BALMLayout::BottomOf(const BLayoutItem* item) const
369 {
370 	Area* area = AreaFor(item);
371 	if (!area)
372 		return NULL;
373 	return area->Bottom();
374 }
375 
376 
377 void
378 BALMLayout::BuildLayout(GroupItem& item, XTab* left, YTab* top, XTab* right,
379 	YTab* bottom)
380 {
381 	if (left == NULL)
382 		left = Left();
383 	if (top == NULL)
384 		top = Top();
385 	if (right == NULL)
386 		right = Right();
387 	if (bottom == NULL)
388 		bottom = Bottom();
389 
390 	_ParseGroupItem(item, left, top, right, bottom);
391 }
392 
393 
394 void
395 BALMLayout::_ParseGroupItem(GroupItem& item, BReference<XTab> left,
396 	BReference<YTab> top, BReference<XTab> right, BReference<YTab> bottom)
397 {
398 	if (item.LayoutItem())
399 		AddItem(item.LayoutItem(), left, top, right, bottom);
400 	else if (item.View()) {
401 		AddView(item.View(), left, top, right, bottom);
402 	}
403 	else {
404 		for (unsigned int i = 0; i < item.GroupItems().size(); i++) {
405 			GroupItem& current = const_cast<GroupItem&>(
406 				item.GroupItems()[i]);
407 			if (item.Orientation() == B_HORIZONTAL) {
408 				BReference<XTab> r = (i == item.GroupItems().size() - 1) ? right
409 					: AddXTab();
410 				_ParseGroupItem(current, left, top, r, bottom);
411 				left = r;
412 			}
413 			else {
414 				BReference<YTab> b = (i == item.GroupItems().size() - 1)
415 					? bottom : AddYTab();
416 				_ParseGroupItem(current, left, top, right, b);
417 				top = b;
418 			}
419 		}
420 	}
421 }
422 
423 
424 BLayoutItem*
425 BALMLayout::AddView(BView* child)
426 {
427 	return AddView(-1, child);
428 }
429 
430 
431 BLayoutItem*
432 BALMLayout::AddView(int32 index, BView* child)
433 {
434 	return BAbstractLayout::AddView(index, child);
435 }
436 
437 
438 /**
439  * Adds a new area to the specification, automatically setting preferred size constraints.
440  *
441  * @param left			left border
442  * @param top			top border
443  * @param right		right border
444  * @param bottom		bottom border
445  * @param content		the control which is the area content
446  * @return the new area
447  */
448 Area*
449 BALMLayout::AddView(BView* view, XTab* left, YTab* top, XTab* right,
450 	YTab* bottom)
451 {
452 	BLayoutItem* item = _CreateLayoutItem(view);
453 	Area* area = AddItem(item, left, top, right, bottom);
454 	if (!area) {
455 		delete item;
456 		return NULL;
457 	}
458 	return area;
459 }
460 
461 
462 /**
463  * Adds a new area to the specification, automatically setting preferred size constraints.
464  *
465  * @param row			the row that defines the top and bottom border
466  * @param column		the column that defines the left and right border
467  * @param content		the control which is the area content
468  * @return the new area
469  */
470 Area*
471 BALMLayout::AddView(BView* view, Row* row, Column* column)
472 {
473 	BLayoutItem* item = _CreateLayoutItem(view);
474 	Area* area = AddItem(item, row, column);
475 	if (!area) {
476 		delete item;
477 		return NULL;
478 	}
479 	return area;
480 }
481 
482 
483 Area*
484 BALMLayout::AddViewToRight(BView* view, XTab* right, YTab* top, YTab* bottom)
485 {
486 	BLayoutItem* item = _CreateLayoutItem(view);
487 	Area* area = AddItemToRight(item, right, top, bottom);
488 	if (!area) {
489 		delete item;
490 		return NULL;
491 	}
492 	return area;
493 }
494 
495 
496 Area*
497 BALMLayout::AddViewToLeft(BView* view, XTab* left, YTab* top, YTab* bottom)
498 {
499 	BLayoutItem* item = _CreateLayoutItem(view);
500 	Area* area = AddItemToLeft(item, left, top, bottom);
501 	if (!area) {
502 		delete item;
503 		return NULL;
504 	}
505 	return area;
506 }
507 
508 
509 Area*
510 BALMLayout::AddViewToTop(BView* view, YTab* top, XTab* left, XTab* right)
511 {
512 	BLayoutItem* item = _CreateLayoutItem(view);
513 	Area* area = AddItemToTop(item, top, left, right);
514 	if (!area) {
515 		delete item;
516 		return NULL;
517 	}
518 	return area;
519 }
520 
521 
522 Area*
523 BALMLayout::AddViewToBottom(BView* view, YTab* bottom, XTab* left, XTab* right)
524 {
525 	BLayoutItem* item = _CreateLayoutItem(view);
526 	Area* area = AddItemToBottom(item, bottom, left, right);
527 	if (!area) {
528 		delete item;
529 		return NULL;
530 	}
531 	return area;
532 }
533 
534 
535 bool
536 BALMLayout::AddItem(BLayoutItem* item)
537 {
538 	return AddItem(-1, item);
539 }
540 
541 
542 bool
543 BALMLayout::AddItem(int32 index, BLayoutItem* item)
544 {
545 	if (!item)
546 		return false;
547 
548 	// simply add the item at the upper right corner of the previous item
549 	// TODO maybe find a more elegant solution
550 	XTab* left = Left();
551 	YTab* top = Top();
552 
553 	// check range
554 	if (index < 0 || index > CountItems())
555 		index = CountItems();
556 
557 	// for index = 0 we already have set the right tabs
558 	if (index != 0) {
559 		BLayoutItem* prevItem = ItemAt(index - 1);
560 		Area* area = AreaFor(prevItem);
561 		if (area) {
562 			left = area->Right();
563 			top = area->Top();
564 		}
565 	}
566 	Area* area = AddItem(item, left, top);
567 	return area ? true : false;
568 }
569 
570 
571 Area*
572 BALMLayout::AddItem(BLayoutItem* item, XTab* left, YTab* top, XTab* _right,
573 	YTab* _bottom)
574 {
575 	BReference<XTab> right = _right;
576 	if (right.Get() == NULL)
577 		right = AddXTab();
578 	BReference<YTab> bottom = _bottom;
579 	if (bottom.Get() == NULL)
580 		bottom = AddYTab();
581 
582 	// Area is added int ItemAdded
583 	if (!BAbstractLayout::AddItem(-1, item))
584 		return NULL;
585 	Area* area = AreaFor(item);
586 	if (!area)
587 		return NULL;
588 	fCurrentArea = area;
589 
590 	area->_Init(fSolver, left, top, right, bottom, fRowColumnManager);
591 
592 	fRowColumnManager->AddArea(area);
593 	return area;
594 }
595 
596 
597 Area*
598 BALMLayout::AddItem(BLayoutItem* item, Row* row, Column* column)
599 {
600 	if (!BAbstractLayout::AddItem(-1, item))
601 		return NULL;
602 	Area* area = AreaFor(item);
603 	if (!area)
604 		return NULL;
605 	fCurrentArea = area;
606 
607 	area->_Init(fSolver, row, column, fRowColumnManager);
608 
609 	fRowColumnManager->AddArea(area);
610 	return area;
611 }
612 
613 
614 Area*
615 BALMLayout::AddItemToRight(BLayoutItem* item, XTab* _right, YTab* top,
616 	YTab* bottom)
617 {
618 	if (fCurrentArea == NULL)
619 		return NULL;
620 
621 	XTab* left = fCurrentArea->Right();
622 	BReference<XTab> right = _right;
623 	if (_right == NULL)
624 		right = AddXTab();
625 	if (!top)
626 		top = fCurrentArea->Top();
627 	if (!bottom)
628 		bottom = fCurrentArea->Bottom();
629 
630 	return AddItem(item, left, top, right, bottom);
631 }
632 
633 
634 Area*
635 BALMLayout::AddItemToLeft(BLayoutItem* item, XTab* _left, YTab* top,
636 	YTab* bottom)
637 {
638 	if (fCurrentArea == NULL)
639 		return NULL;
640 
641 	BReference<XTab> left = _left;
642 	if (_left == NULL)
643 		left = AddXTab();
644 	XTab* right = fCurrentArea->Left();
645 	if (!top)
646 		top = fCurrentArea->Top();
647 	if (!bottom)
648 		bottom = fCurrentArea->Bottom();
649 
650 	return AddItem(item, left, top, right, bottom);
651 }
652 
653 
654 Area*
655 BALMLayout::AddItemToTop(BLayoutItem* item, YTab* _top, XTab* left, XTab* right)
656 {
657 	if (fCurrentArea == NULL)
658 		return NULL;
659 
660 	if (!left)
661 		left = fCurrentArea->Left();
662 	if (!right)
663 		right = fCurrentArea->Right();
664 	BReference<YTab> top = _top;
665 	if (_top == NULL)
666 		top = AddYTab();
667 	YTab* bottom = fCurrentArea->Top();
668 
669 	return AddItem(item, left, top, right, bottom);
670 }
671 
672 
673 Area*
674 BALMLayout::AddItemToBottom(BLayoutItem* item, YTab* _bottom, XTab* left,
675 	XTab* right)
676 {
677 	if (fCurrentArea == NULL)
678 		return NULL;
679 
680 	if (!left)
681 		left = fCurrentArea->Left();
682 	if (!right)
683 		right = fCurrentArea->Right();
684 	YTab* top = fCurrentArea->Bottom();
685 	BReference<YTab> bottom = _bottom;
686 	if (_bottom == NULL)
687 		bottom = AddYTab();
688 
689 	return AddItem(item, left, top, right, bottom);
690 }
691 
692 
693 enum {
694 	kLeftBorderIndex = -2,
695 	kTopBorderIndex = -3,
696 	kRightBorderIndex = -4,
697 	kBottomBorderIndex = -5,
698 };
699 
700 
701 bool
702 BALMLayout::SaveLayout(BMessage* archive) const
703 {
704 	archive->MakeEmpty();
705 
706 	archive->AddInt32("nXTabs", CountXTabs());
707 	archive->AddInt32("nYTabs", CountYTabs());
708 
709 	XTabList xTabs = fXTabList;
710 	xTabs.RemoveItem(fLeft);
711 	xTabs.RemoveItem(fRight);
712 	YTabList yTabs = fYTabList;
713 	yTabs.RemoveItem(fTop);
714 	yTabs.RemoveItem(fBottom);
715 
716 	int32 nAreas = CountAreas();
717 	for (int32 i = 0; i < nAreas; i++) {
718 		Area* area = AreaAt(i);
719 		if (area->Left() == fLeft)
720 			archive->AddInt32("left", kLeftBorderIndex);
721 		else
722 			archive->AddInt32("left", xTabs.IndexOf(area->Left()));
723 		if (area->Top() == fTop)
724 			archive->AddInt32("top", kTopBorderIndex);
725 		else
726 			archive->AddInt32("top", yTabs.IndexOf(area->Top()));
727 		if (area->Right() == fRight)
728 			archive->AddInt32("right", kRightBorderIndex);
729 		else
730 			archive->AddInt32("right", xTabs.IndexOf(area->Right()));
731 		if (area->Bottom() == fBottom)
732 			archive->AddInt32("bottom", kBottomBorderIndex);
733 		else
734 			archive->AddInt32("bottom", yTabs.IndexOf(area->Bottom()));
735 	}
736 	return true;
737 }
738 
739 
740 bool
741 BALMLayout::RestoreLayout(const BMessage* archive)
742 {
743 	int32 neededXTabs;
744 	int32 neededYTabs;
745 	if (archive->FindInt32("nXTabs", &neededXTabs) != B_OK)
746 		return false;
747 	if (archive->FindInt32("nYTabs", &neededYTabs) != B_OK)
748 		return false;
749 	// First store a reference to all needed tabs otherwise they might get lost
750 	// while editing the layout
751 	std::vector<BReference<XTab> > newXTabs;
752 	std::vector<BReference<YTab> > newYTabs;
753 	int32 existingXTabs = fXTabList.CountItems();
754 	for (int32 i = 0; i < neededXTabs; i++) {
755 		if (i < existingXTabs)
756 			newXTabs.push_back(BReference<XTab>(fXTabList.ItemAt(i)));
757 		else
758 			newXTabs.push_back(AddXTab());
759 	}
760 	int32 existingYTabs = fYTabList.CountItems();
761 	for (int32 i = 0; i < neededYTabs; i++) {
762 		if (i < existingYTabs)
763 			newYTabs.push_back(BReference<YTab>(fYTabList.ItemAt(i)));
764 		else
765 			newYTabs.push_back(AddYTab());
766 	}
767 
768 	XTabList xTabs = fXTabList;
769 	xTabs.RemoveItem(fLeft);
770 	xTabs.RemoveItem(fRight);
771 	YTabList yTabs = fYTabList;
772 	yTabs.RemoveItem(fTop);
773 	yTabs.RemoveItem(fBottom);
774 
775 	int32 nAreas = CountAreas();
776 	for (int32 i = 0; i < nAreas; i++) {
777 		Area* area = AreaAt(i);
778 		if (area == NULL)
779 			return false;
780 		int32 left = -1;
781 		if (archive->FindInt32("left", i, &left) != B_OK)
782 			break;
783 		int32 top = archive->FindInt32("top", i);
784 		int32 right = archive->FindInt32("right", i);
785 		int32 bottom = archive->FindInt32("bottom", i);
786 
787 		XTab* leftTab = NULL;
788 		YTab* topTab = NULL;
789 		XTab* rightTab = NULL;
790 		YTab* bottomTab = NULL;
791 
792 		if (left == kLeftBorderIndex)
793 			leftTab = fLeft;
794 		else
795 			leftTab = xTabs.ItemAt(left);
796 		if (top == kTopBorderIndex)
797 			topTab = fTop;
798 		else
799 			topTab = yTabs.ItemAt(top);
800 		if (right == kRightBorderIndex)
801 			rightTab = fRight;
802 		else
803 			rightTab = xTabs.ItemAt(right);
804 		if (bottom == kBottomBorderIndex)
805 			bottomTab = fBottom;
806 		else
807 			bottomTab = yTabs.ItemAt(bottom);
808 		if (leftTab == NULL || topTab == NULL || rightTab == NULL
809 			|| bottomTab == NULL)
810 			return false;
811 
812 		area->SetLeft(leftTab);
813 		area->SetTop(topTab);
814 		area->SetRight(rightTab);
815 		area->SetBottom(bottomTab);
816 	}
817 	return true;
818 }
819 
820 
821 /**
822  * Gets the left variable.
823  */
824 XTab*
825 BALMLayout::Left() const
826 {
827 	return fLeft;
828 }
829 
830 
831 /**
832  * Gets the right variable.
833  */
834 XTab*
835 BALMLayout::Right() const
836 {
837 	return fRight;
838 }
839 
840 
841 /**
842  * Gets the top variable.
843  */
844 YTab*
845 BALMLayout::Top() const
846 {
847 	return fTop;
848 }
849 
850 
851 /**
852  * Gets the bottom variable.
853  */
854 YTab*
855 BALMLayout::Bottom() const
856 {
857 	return fBottom;
858 }
859 
860 
861 /**
862  * Gets minimum size.
863  */
864 BSize
865 BALMLayout::BaseMinSize() {
866 	if (fMinSize == kUnsetSize)
867 		fMinSize = _CalculateMinSize();
868 	return fMinSize;
869 }
870 
871 
872 /**
873  * Gets maximum size.
874  */
875 BSize
876 BALMLayout::BaseMaxSize()
877 {
878 	if (fMaxSize == kUnsetSize)
879 		fMaxSize = _CalculateMaxSize();
880 	return fMaxSize;
881 }
882 
883 
884 /**
885  * Gets preferred size.
886  */
887 BSize
888 BALMLayout::BasePreferredSize()
889 {
890 	if (fPreferredSize == kUnsetSize)
891 		fPreferredSize = _CalculatePreferredSize();
892 	return fPreferredSize;
893 }
894 
895 
896 /**
897  * Gets the alignment.
898  */
899 BAlignment
900 BALMLayout::BaseAlignment()
901 {
902 	BAlignment alignment;
903 	alignment.SetHorizontal(B_ALIGN_HORIZONTAL_CENTER);
904 	alignment.SetVertical(B_ALIGN_VERTICAL_CENTER);
905 	return alignment;
906 }
907 
908 
909 /**
910  * Invalidates the layout.
911  * Resets minimum/maximum/preferred size.
912  */
913 void
914 BALMLayout::LayoutInvalidated(bool children)
915 {
916 	fMinSize = kUnsetSize;
917 	fMaxSize = kUnsetSize;
918 	fPreferredSize = kUnsetSize;
919 }
920 
921 
922 bool
923 BALMLayout::ItemAdded(BLayoutItem* item, int32 atIndex)
924 {
925 	item->SetLayoutData(new(std::nothrow) Area(item));
926 	return item->LayoutData() != NULL;
927 }
928 
929 
930 void
931 BALMLayout::ItemRemoved(BLayoutItem* item, int32 fromIndex)
932 {
933 	if (Area* area = AreaFor(item)) {
934 		fRowColumnManager->RemoveArea(area);
935 		item->SetLayoutData(NULL);
936 		delete area;
937 	}
938 }
939 
940 
941 /**
942  * Calculate and set the layout.
943  * If no layout specification is given, a specification is reverse engineered automatically.
944  */
945 void
946 BALMLayout::DoLayout()
947 {
948 	_UpdateAreaConstraints();
949 
950 	// Enforced absolute positions of Right and Bottom
951 	BRect area(LayoutArea());
952 	Right()->SetRange(area.right, area.right);
953 	Bottom()->SetRange(area.bottom, area.bottom);
954 
955 	fSolver->Solve();
956 
957 	// if new layout is infeasible, use previous layout
958 	if (fSolver->Result() == kInfeasible)
959 		return;
960 
961 	if (fSolver->Result() != kOptimal) {
962 		fSolver->Save("failed-layout.txt");
963 		printf("Could not solve the layout specification (%d). ",
964 			fSolver->Result());
965 		printf("Saved specification in file failed-layout.txt\n");
966 	}
967 
968 	// set the calculated positions and sizes for every area
969 	for (int32 i = 0; i < CountItems(); i++)
970 		AreaFor(ItemAt(i))->_DoLayout();
971 }
972 
973 
974 /**
975  * Gets the path of the performance log file.
976  *
977  * @return the path of the performance log file
978  */
979 char*
980 BALMLayout::PerformancePath() const
981 {
982 	return fPerformancePath;
983 }
984 
985 
986 /**
987  * Sets the path of the performance log file.
988  *
989  * @param path	the path of the performance log file
990  */
991 void
992 BALMLayout::SetPerformancePath(char* path)
993 {
994 	fPerformancePath = path;
995 }
996 
997 
998 LinearSpec*
999 BALMLayout::Solver() const
1000 {
1001 	return const_cast<LinearSpec*>(fSolver);
1002 }
1003 
1004 
1005 void
1006 BALMLayout::SetInset(float inset)
1007 {
1008 	fInset = inset;
1009 }
1010 
1011 
1012 float
1013 BALMLayout::Inset() const
1014 {
1015 	return fInset;
1016 }
1017 
1018 
1019 void
1020 BALMLayout::SetSpacing(float spacing)
1021 {
1022 	fSpacing = spacing / 2;
1023 }
1024 
1025 
1026 float
1027 BALMLayout::Spacing() const
1028 {
1029 	return fSpacing * 2;
1030 }
1031 
1032 
1033 BLayoutItem*
1034 BALMLayout::_CreateLayoutItem(BView* view)
1035 {
1036 	return new(std::nothrow) BViewLayoutItem(view);
1037 }
1038 
1039 
1040 /**
1041  * Caculates the miminum size.
1042  */
1043 BSize
1044 BALMLayout::_CalculateMinSize()
1045 {
1046 	_UpdateAreaConstraints();
1047 
1048 	Right()->SetRange(0, 20000);
1049 	Bottom()->SetRange(0, 20000);
1050 
1051 	return fSolver->MinSize(Right(), Bottom());
1052 }
1053 
1054 
1055 /**
1056  * Caculates the maximum size.
1057  */
1058 BSize
1059 BALMLayout::_CalculateMaxSize()
1060 {
1061 	_UpdateAreaConstraints();
1062 
1063 	Right()->SetRange(0, 20000);
1064 	Bottom()->SetRange(0, 20000);
1065 
1066 	return fSolver->MaxSize(Right(), Bottom());
1067 }
1068 
1069 
1070 /**
1071  * Caculates the preferred size.
1072  */
1073 BSize
1074 BALMLayout::_CalculatePreferredSize()
1075 {
1076 	_UpdateAreaConstraints();
1077 
1078 	Right()->SetRange(0, 20000);
1079 	Bottom()->SetRange(0, 20000);
1080 
1081 	fSolver->Solve();
1082 	if (fSolver->Result() != kOptimal) {
1083 		fSolver->Save("failed-layout.txt");
1084 		printf("Could not solve the layout specification (%d). "
1085 			"Saved specification in file failed-layout.txt", fSolver->Result());
1086 	}
1087 
1088 	return BSize(Right()->Value() - Left()->Value(),
1089 		Bottom()->Value() - Top()->Value());
1090 }
1091 
1092 
1093 void
1094 BALMLayout::_UpdateAreaConstraints()
1095 {
1096 	for (int i = 0; i < CountItems(); i++)
1097 		AreaFor(ItemAt(i))->InvalidateSizeConstraints();
1098 	fRowColumnManager->UpdateConstraints();
1099 }
1100