xref: /haiku/src/libs/alm/ALMLayout.cpp (revision 5440f6fee398073e8359b992a17b0943d39e6ff0)
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 
15 #include "ViewLayoutItem.h"
16 
17 #include "ResultType.h"
18 
19 
20 using namespace LinearProgramming;
21 
22 
23 const BSize kUnsetSize(B_SIZE_UNSET, B_SIZE_UNSET);
24 const BSize kMinSize(0, 0);
25 const BSize kMaxSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
26 
27 
28 /*!
29  * Constructor.
30  * Creates new layout engine.
31  *
32  * If friendLayout is not NULL the solver of the friend layout is used.
33  */
34 BALMLayout::BALMLayout(float spacing, BALMLayout* friendLayout)
35 	:
36 	fInset(0.0f),
37 	fSpacing(spacing),
38 	fCurrentArea(NULL)
39 {
40 	fSolver = friendLayout ? friendLayout->Solver() : &fOwnSolver;
41 
42 	fLeft = AddXTab();
43 	fRight = AddXTab();
44 	fTop = AddYTab();
45 	fBottom = AddYTab();
46 
47 	// the Left tab is always at x-position 0, and the Top tab is always at 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 	fScaleWidth = fSolver->AddVariable();
60 	fScaleHeight = fSolver->AddVariable();
61 }
62 
63 
64 BALMLayout::~BALMLayout()
65 {
66 	delete fScaleWidth;
67 	delete fScaleHeight;
68 }
69 
70 
71 /**
72  * Adds a new x-tab to the specification.
73  *
74  * @return the new x-tab
75  */
76 XTab*
77 BALMLayout::AddXTab()
78 {
79 	XTab* tab = new(std::nothrow) XTab(fSolver);
80 	if (!tab)
81 		return NULL;
82 	if (!fSolver->AddVariable(tab)) {
83 		delete tab;
84 		return NULL;
85 	}
86 
87 	return tab;
88 }
89 
90 
91 /**
92  * Adds a new y-tab to the specification.
93  *
94  * @return the new y-tab
95  */
96 YTab*
97 BALMLayout::AddYTab()
98 {
99 	YTab* tab = new(std::nothrow) YTab(fSolver);
100 	if (!tab)
101 		return NULL;
102 	if (!fSolver->AddVariable(tab)) {
103 		delete tab;
104 		return NULL;
105 	}
106 
107 	return tab;
108 }
109 
110 
111 /**
112  * Adds a new row to the specification.
113  *
114  * @return the new row
115  */
116 Row*
117 BALMLayout::AddRow()
118 {
119 	return new(std::nothrow) Row(this);
120 }
121 
122 
123 /**
124  * Adds a new row to the specification that is glued to the given y-tabs.
125  *
126  * @param top
127  * @param bottom
128  * @return the new row
129  */
130 Row*
131 BALMLayout::AddRow(YTab* top, YTab* bottom)
132 {
133 	Row* row = new(std::nothrow) Row(this);
134 	if (top != NULL)
135 		row->Constraints()->AddItem(row->Top()->IsEqual(top));
136 	if (bottom != NULL)
137 		row->Constraints()->AddItem(row->Bottom()->IsEqual(bottom));
138 	return row;
139 }
140 
141 
142 /**
143  * Adds a new column to the specification.
144  *
145  * @return the new column
146  */
147 Column*
148 BALMLayout::AddColumn()
149 {
150 	return new(std::nothrow) Column(this);
151 }
152 
153 
154 /**
155  * Adds a new column to the specification that is glued to the given x-tabs.
156  *
157  * @param left
158  * @param right
159  * @return the new column
160  */
161 Column*
162 BALMLayout::AddColumn(XTab* left, XTab* right)
163 {
164 	Column* column = new(std::nothrow) Column(this);
165 	if (left != NULL)
166 		column->Constraints()->AddItem(column->Left()->IsEqual(left));
167 	if (right != NULL)
168 		column->Constraints()->AddItem(column->Right()->IsEqual(right));
169 	return column;
170 }
171 
172 
173 /**
174  * Finds the area that contains the given control.
175  *
176  * @param control	the control to look for
177  * @return the area that contains the control
178  */
179 Area*
180 BALMLayout::AreaFor(const BView* control) const
181 {
182 	return AreaFor(ItemAt(IndexOfView(const_cast<BView*>(control))));
183 }
184 
185 
186 Area*
187 BALMLayout::AreaFor(const BLayoutItem* item) const
188 {
189 	if (!item)
190 		return NULL;
191 	return static_cast<Area*>(item->LayoutData());
192 }
193 
194 
195 Area*
196 BALMLayout::CurrentArea() const
197 {
198 	return fCurrentArea;
199 }
200 
201 
202 void
203 BALMLayout::SetCurrentArea(const Area* area)
204 {
205 	fCurrentArea = const_cast<Area*>(area);
206 }
207 
208 
209 void
210 BALMLayout::SetCurrentArea(const BView* view)
211 {
212 	fCurrentArea = AreaFor(view);
213 }
214 
215 
216 void
217 BALMLayout::SetCurrentArea(const BLayoutItem* item)
218 {
219 	fCurrentArea = AreaFor(item);
220 }
221 
222 
223 XTab*
224 BALMLayout::LeftOf(const BView* view) const
225 {
226 	return AreaFor(view)->Left();
227 }
228 
229 
230 XTab*
231 BALMLayout::LeftOf(const BLayoutItem* item) const
232 {
233 	return AreaFor(item)->Left();
234 }
235 
236 
237 XTab*
238 BALMLayout::RightOf(const BView* view) const
239 {
240 	return AreaFor(view)->Right();
241 }
242 
243 
244 XTab*
245 BALMLayout::RightOf(const BLayoutItem* item) const
246 {
247 	return AreaFor(item)->Right();
248 }
249 
250 
251 YTab*
252 BALMLayout::TopOf(const BView* view) const
253 {
254 	return AreaFor(view)->Top();
255 }
256 
257 
258 YTab*
259 BALMLayout::TopOf(const BLayoutItem* item) const
260 {
261 	return AreaFor(item)->Top();
262 }
263 
264 
265 YTab*
266 BALMLayout::BottomOf(const BView* view) const
267 {
268 	return AreaFor(view)->Bottom();
269 }
270 
271 
272 YTab*
273 BALMLayout::BottomOf(const BLayoutItem* item) const
274 {
275 	return AreaFor(item)->Bottom();
276 }
277 
278 
279 void
280 BALMLayout::BuildLayout(GroupItem& item, XTab* left, YTab* top, XTab* right,
281 	YTab* bottom)
282 {
283 	if (!left)
284 		left = Left();
285 	if (!top)
286 		top = Top();
287 	if (!right)
288 		right = Right();
289 	if (!bottom)
290 		bottom = Bottom();
291 
292 	_ParseGroupItem(item, left, top, right, bottom);
293 }
294 
295 
296 void
297 BALMLayout::_ParseGroupItem(GroupItem& item, XTab* left, YTab* top, XTab* right,
298 	YTab* bottom)
299 {
300 	if (item.LayoutItem())
301 		AddItem(item.LayoutItem(), left, top, right, bottom);
302 	else if (item.View()) {
303 		AddView(item.View(), left, top, right, bottom);
304 	}
305 	else {
306 		for (unsigned int i = 0; i < item.GroupItems().size(); i++) {
307 			GroupItem& current = const_cast<GroupItem&>(
308 				item.GroupItems()[i]);
309 			if (item.Orientation() == B_HORIZONTAL) {
310 				XTab* r = (i == item.GroupItems().size() - 1) ? right
311 					: AddXTab();
312 				_ParseGroupItem(current, left, top, r, bottom);
313 				left = r;
314 			}
315 			else {
316 				YTab* b = (i == item.GroupItems().size() - 1) ? bottom
317 					: AddYTab();
318 				_ParseGroupItem(current, left, top, right, b);
319 				top = b;
320 			}
321 		}
322 	}
323 }
324 
325 
326 BLayoutItem*
327 BALMLayout::AddView(BView* child)
328 {
329 	return AddView(-1, child);
330 }
331 
332 
333 BLayoutItem*
334 BALMLayout::AddView(int32 index, BView* child)
335 {
336 	return BAbstractLayout::AddView(index, child);
337 }
338 
339 
340 /**
341  * Adds a new area to the specification, automatically setting preferred size constraints.
342  *
343  * @param left			left border
344  * @param top			top border
345  * @param right		right border
346  * @param bottom		bottom border
347  * @param content		the control which is the area content
348  * @return the new area
349  */
350 Area*
351 BALMLayout::AddView(BView* view, XTab* left, YTab* top, XTab* right,
352 	YTab* bottom)
353 {
354 	BLayoutItem* item = _CreateLayoutItem(view);
355 	Area* area = AddItem(item, left, top, right, bottom);
356 	if (!area) {
357 		delete item;
358 		return NULL;
359 	}
360 	return area;
361 }
362 
363 
364 /**
365  * Adds a new area to the specification, automatically setting preferred size constraints.
366  *
367  * @param row			the row that defines the top and bottom border
368  * @param column		the column that defines the left and right border
369  * @param content		the control which is the area content
370  * @return the new area
371  */
372 Area*
373 BALMLayout::AddView(BView* view, Row* row, Column* column)
374 {
375 	BLayoutItem* item = _CreateLayoutItem(view);
376 	Area* area = AddItem(item, row, column);
377 	if (!area) {
378 		delete item;
379 		return NULL;
380 	}
381 	return area;
382 }
383 
384 
385 Area*
386 BALMLayout::AddViewToRight(BView* view, XTab* right, YTab* top, YTab* bottom)
387 {
388 	BLayoutItem* item = _CreateLayoutItem(view);
389 	Area* area = AddItemToRight(item, right, top, bottom);
390 	if (!area) {
391 		delete item;
392 		return NULL;
393 	}
394 	return area;
395 }
396 
397 
398 Area*
399 BALMLayout::AddViewToLeft(BView* view, XTab* left, YTab* top, YTab* bottom)
400 {
401 	BLayoutItem* item = _CreateLayoutItem(view);
402 	Area* area = AddItemToLeft(item, left, top, bottom);
403 	if (!area) {
404 		delete item;
405 		return NULL;
406 	}
407 	return area;
408 }
409 
410 
411 Area*
412 BALMLayout::AddViewToTop(BView* view, YTab* top, XTab* left, XTab* right)
413 {
414 	BLayoutItem* item = _CreateLayoutItem(view);
415 	Area* area = AddItemToTop(item, top, left, right);
416 	if (!area) {
417 		delete item;
418 		return NULL;
419 	}
420 	return area;
421 }
422 
423 
424 Area*
425 BALMLayout::AddViewToBottom(BView* view, YTab* bottom, XTab* left, XTab* right)
426 {
427 	BLayoutItem* item = _CreateLayoutItem(view);
428 	Area* area = AddItemToBottom(item, bottom, left, right);
429 	if (!area) {
430 		delete item;
431 		return NULL;
432 	}
433 	return area;
434 }
435 
436 
437 bool
438 BALMLayout::AddItem(BLayoutItem* item)
439 {
440 	return AddItem(-1, item);
441 }
442 
443 
444 bool
445 BALMLayout::AddItem(int32 index, BLayoutItem* item)
446 {
447 	if (!item)
448 		return false;
449 
450 	// simply add the item at the upper right corner of the previous item
451 	// TODO maybe find a more elegant solution
452 	XTab* left = Left();
453 	YTab* top = Top();
454 
455 	// check range
456 	if (index < 0 || index > CountItems())
457 		index = CountItems();
458 
459 	// for index = 0 we already have set the right tabs
460 	if (index != 0) {
461 		BLayoutItem* prevItem = ItemAt(index - 1);
462 		Area* area = AreaFor(prevItem);
463 		if (area) {
464 			left = area->Right();
465 			top = area->Top();
466 		}
467 	}
468 	Area* area = AddItem(item, left, top);
469 	return area ? true : false;
470 }
471 
472 
473 Area*
474 BALMLayout::AddItem(BLayoutItem* item, XTab* left, YTab* top, XTab* right,
475 	YTab* bottom)
476 {
477 	if (!right)
478 		right = AddXTab();
479 	if (!bottom)
480 		bottom = AddYTab();
481 
482 	if (!BAbstractLayout::AddItem(-1, item))
483 		return NULL;
484 	Area* area = AreaFor(item);
485 	if (!area)
486 		return NULL;
487 	fCurrentArea = area;
488 
489 	area->_Init(fSolver, left, top, right, bottom, fScaleWidth, fScaleHeight);
490 	return area;
491 }
492 
493 
494 Area*
495 BALMLayout::AddItem(BLayoutItem* item, Row* row, Column* column)
496 {
497 	if (!BAbstractLayout::AddItem(-1, item))
498 		return NULL;
499 	Area* area = AreaFor(item);
500 	if (!area)
501 		return NULL;
502 	fCurrentArea = area;
503 
504 	area->_Init(fSolver, row, column, fScaleWidth, fScaleHeight);
505 	return area;
506 }
507 
508 
509 Area*
510 BALMLayout::AddItemToRight(BLayoutItem* item, XTab* right, YTab* top,
511 	YTab* bottom)
512 {
513 	XTab* left = fCurrentArea->Right();
514 	if (!right)
515 		right = AddXTab();
516 	if (!top)
517 		top = fCurrentArea->Top();
518 	if (!bottom)
519 		bottom = fCurrentArea->Bottom();
520 
521 	return AddItem(item, left, top, right, bottom);
522 }
523 
524 
525 Area*
526 BALMLayout::AddItemToLeft(BLayoutItem* item, XTab* left, YTab* top,
527 	YTab* bottom)
528 {
529 	if (!left)
530 		left = AddXTab();
531 	XTab* right = fCurrentArea->Left();
532 	if (!top)
533 		top = fCurrentArea->Top();
534 	if (!bottom)
535 		bottom = fCurrentArea->Bottom();
536 
537 	return AddItem(item, left, top, right, bottom);
538 }
539 
540 
541 Area*
542 BALMLayout::AddItemToTop(BLayoutItem* item, YTab* top, XTab* left, XTab* right)
543 {
544 	if (!left)
545 		left = fCurrentArea->Left();
546 	if (!right)
547 		right = fCurrentArea->Right();
548 	if (!top)
549 		top = AddYTab();
550 	YTab* bottom = fCurrentArea->Top();
551 
552 	return AddItem(item, left, top, right, bottom);
553 }
554 
555 
556 Area*
557 BALMLayout::AddItemToBottom(BLayoutItem* item, YTab* bottom, XTab* left,
558 	XTab* right)
559 {
560 	if (!left)
561 		left = fCurrentArea->Left();
562 	if (!right)
563 		right = fCurrentArea->Right();
564 	YTab* top = fCurrentArea->Bottom();
565 	if (!bottom)
566 		bottom = AddYTab();
567 
568 	return AddItem(item, left, top, right, bottom);
569 }
570 
571 
572 /**
573  * Gets the left variable.
574  */
575 XTab*
576 BALMLayout::Left() const
577 {
578 	return fLeft;
579 }
580 
581 
582 /**
583  * Gets the right variable.
584  */
585 XTab*
586 BALMLayout::Right() const
587 {
588 	return fRight;
589 }
590 
591 
592 /**
593  * Gets the top variable.
594  */
595 YTab*
596 BALMLayout::Top() const
597 {
598 	return fTop;
599 }
600 
601 
602 /**
603  * Gets the bottom variable.
604  */
605 YTab*
606 BALMLayout::Bottom() const
607 {
608 	return fBottom;
609 }
610 
611 
612 /**
613  * Gets minimum size.
614  */
615 BSize
616 BALMLayout::BaseMinSize() {
617 	if (fMinSize == kUnsetSize)
618 		fMinSize = _CalculateMinSize();
619 	return fMinSize;
620 }
621 
622 
623 /**
624  * Gets maximum size.
625  */
626 BSize
627 BALMLayout::BaseMaxSize()
628 {
629 	if (fMaxSize == kUnsetSize)
630 		fMaxSize = _CalculateMaxSize();
631 	return fMaxSize;
632 }
633 
634 
635 /**
636  * Gets preferred size.
637  */
638 BSize
639 BALMLayout::BasePreferredSize()
640 {
641 	if (fPreferredSize == kUnsetSize)
642 		fPreferredSize = _CalculatePreferredSize();
643 	return fPreferredSize;
644 }
645 
646 
647 /**
648  * Gets the alignment.
649  */
650 BAlignment
651 BALMLayout::BaseAlignment()
652 {
653 	BAlignment alignment;
654 	alignment.SetHorizontal(B_ALIGN_HORIZONTAL_CENTER);
655 	alignment.SetVertical(B_ALIGN_VERTICAL_CENTER);
656 	return alignment;
657 }
658 
659 
660 /**
661  * Invalidates the layout.
662  * Resets minimum/maximum/preferred size.
663  */
664 void
665 BALMLayout::InvalidateLayout(bool children)
666 {
667 	BLayout::InvalidateLayout(children);
668 	fMinSize = kUnsetSize;
669 	fMaxSize = kUnsetSize;
670 	fPreferredSize = kUnsetSize;
671 }
672 
673 
674 bool
675 BALMLayout::ItemAdded(BLayoutItem* item, int32 atIndex)
676 {
677 	item->SetLayoutData(new(std::nothrow) Area(item));
678 	return item->LayoutData() != NULL;
679 }
680 
681 
682 void
683 BALMLayout::ItemRemoved(BLayoutItem* item, int32 fromIndex)
684 {
685 	if (Area* area = AreaFor(item)) {
686 		item->SetLayoutData(NULL);
687 		delete area;
688 	}
689 }
690 
691 
692 /**
693  * Calculate and set the layout.
694  * If no layout specification is given, a specification is reverse engineered automatically.
695  */
696 void
697 BALMLayout::DerivedLayoutItems()
698 {
699 	_UpdateAreaConstraints();
700 
701 	// Enforced absolute positions of Right and Bottom
702 	BRect area(LayoutArea());
703 	Right()->SetRange(area.right, area.right);
704 	Bottom()->SetRange(area.bottom, area.bottom);
705 
706 	_SolveLayout();
707 
708 	// if new layout is infeasible, use previous layout
709 	if (fSolver->Result() == INFEASIBLE)
710 		return;
711 
712 	if (fSolver->Result() != OPTIMAL) {
713 		fSolver->Save("failed-layout.txt");
714 		printf("Could not solve the layout specification (%d). ",
715 			fSolver->Result());
716 		printf("Saved specification in file failed-layout.txt\n");
717 	}
718 
719 	// set the calculated positions and sizes for every area
720 	for (int32 i = 0; i < CountItems(); i++)
721 		AreaFor(ItemAt(i))->_DoLayout();
722 }
723 
724 
725 /**
726  * Gets the path of the performance log file.
727  *
728  * @return the path of the performance log file
729  */
730 char*
731 BALMLayout::PerformancePath() const
732 {
733 	return fPerformancePath;
734 }
735 
736 
737 /**
738  * Sets the path of the performance log file.
739  *
740  * @param path	the path of the performance log file
741  */
742 void
743 BALMLayout::SetPerformancePath(char* path)
744 {
745 	fPerformancePath = path;
746 }
747 
748 
749 LinearSpec*
750 BALMLayout::Solver() const
751 {
752 	return const_cast<LinearSpec*>(fSolver);
753 }
754 
755 
756 void
757 BALMLayout::SetInset(float inset)
758 {
759 	fInset = inset;
760 }
761 
762 
763 float
764 BALMLayout::Inset() const
765 {
766 	return fInset;
767 }
768 
769 
770 void
771 BALMLayout::SetSpacing(float spacing)
772 {
773 	fSpacing = spacing;
774 }
775 
776 
777 float
778 BALMLayout::Spacing() const
779 {
780 	return fSpacing;
781 }
782 
783 
784 BLayoutItem*
785 BALMLayout::_CreateLayoutItem(BView* view)
786 {
787 	return new(std::nothrow) BViewLayoutItem(view);
788 }
789 
790 
791 void
792 BALMLayout::_SolveLayout()
793 {
794 	// Try to solve the layout until the result is OPTIMAL or INFEASIBLE,
795 	// maximally 15 tries sometimes the solving algorithm encounters numerical
796 	// problems (NUMFAILURE), and repeating the solving often helps to overcome
797 	// them.
798 	BFile* file = NULL;
799 	if (fPerformancePath != NULL) {
800 		file = new(std::nothrow) BFile(fPerformancePath,
801 			B_READ_WRITE | B_CREATE_FILE | B_OPEN_AT_END);
802 	}
803 
804 	ResultType result;
805 	for (int32 tries = 0; tries < 15; tries++) {
806 		result = fSolver->Solve();
807 		if (fPerformancePath != NULL) {
808 			/*char buffer [100];
809 			file->Write(buffer, sprintf(buffer, "%d\t%fms\t#vars=%ld\t"
810 				"#constraints=%ld\n", result, fSolver->SolvingTime(),
811 				fSolver->Variables()->CountItems(),
812 				fSolver->Constraints()->CountItems()));*/
813 		}
814 		if (result == OPTIMAL || result == INFEASIBLE)
815 			break;
816 	}
817 	delete file;
818 }
819 
820 
821 /**
822  * Caculates the miminum size.
823  */
824 BSize
825 BALMLayout::_CalculateMinSize()
826 {
827 	_UpdateAreaConstraints();
828 
829 	SummandList* newObjFunction = new(std::nothrow) SummandList(2);
830 	newObjFunction->AddItem(new(std::nothrow) Summand(1.0, fRight));
831 	newObjFunction->AddItem(new(std::nothrow) Summand(1.0, fBottom));
832 	SummandList* oldObjFunction = fSolver->SwapObjectiveFunction(
833 		newObjFunction);
834 	_SolveLayout();
835 	fSolver->SetObjectiveFunction(oldObjFunction);
836 
837 	if (fSolver->Result() == UNBOUNDED)
838 		return kMinSize;
839 	if (fSolver->Result() != OPTIMAL) {
840 		fSolver->Save("failed-layout.txt");
841 		printf("Could not solve the layout specification (%d). "
842 			"Saved specification in file failed-layout.txt", fSolver->Result());
843 	}
844 
845 	return BSize(Right()->Value() - Left()->Value(),
846 		Bottom()->Value() - Top()->Value());
847 }
848 
849 
850 /**
851  * Caculates the maximum size.
852  */
853 BSize
854 BALMLayout::_CalculateMaxSize()
855 {
856 	_UpdateAreaConstraints();
857 
858 	SummandList* newObjFunction = new(std::nothrow) SummandList(2);
859 	newObjFunction->AddItem(new(std::nothrow) Summand(-1.0, fRight));
860 	newObjFunction->AddItem(new(std::nothrow) Summand(-1.0, fBottom));
861 	SummandList* oldObjFunction = fSolver->SwapObjectiveFunction(
862 		newObjFunction);
863 	_SolveLayout();
864 	fSolver->SetObjectiveFunction(oldObjFunction);
865 
866 	if (fSolver->Result() == UNBOUNDED)
867 		return kMaxSize;
868 	if (fSolver->Result() != OPTIMAL) {
869 		fSolver->Save("failed-layout.txt");
870 		printf("Could not solve the layout specification (%d). "
871 			"Saved specification in file failed-layout.txt", fSolver->Result());
872 	}
873 
874 	return BSize(Right()->Value() - Left()->Value(),
875 		Bottom()->Value() - Top()->Value());
876 }
877 
878 
879 /**
880  * Caculates the preferred size.
881  */
882 BSize
883 BALMLayout::_CalculatePreferredSize()
884 {
885 	_UpdateAreaConstraints();
886 
887 	_SolveLayout();
888 	if (fSolver->Result() != OPTIMAL) {
889 		fSolver->Save("failed-layout.txt");
890 		printf("Could not solve the layout specification (%d). "
891 			"Saved specification in file failed-layout.txt", fSolver->Result());
892 	}
893 
894 	return BSize(Right()->Value() - Left()->Value(),
895 		Bottom()->Value() - Top()->Value());
896 }
897 
898 
899 void
900 BALMLayout::_UpdateAreaConstraints()
901 {
902 	for (int i = 0; i < CountItems(); i++)
903 		AreaFor(ItemAt(i))->InvalidateSizeConstraints();
904 }
905