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