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