/* * Copyright 2007-2008, Christof Lutteroth, lutteroth@cs.auckland.ac.nz * Copyright 2007-2008, James Kim, jkim202@ec.auckland.ac.nz * Copyright 2010, Clemens Zeidler * Distributed under the terms of the MIT License. */ #include "ALMLayout.h" #include #include #include #include #include "RowColumnManager.h" #include "SharedSolver.h" #include "ViewLayoutItem.h" using BPrivate::AutoDeleter; using namespace LinearProgramming; const BSize kUnsetSize(B_SIZE_UNSET, B_SIZE_UNSET); namespace { const char* kSolverField = "BALMLayout:solver"; const char* kBadLayoutPolicyField = "BALMLayout:policy"; const char* kXTabsField = "BALMLayout:xtabs"; const char* kYTabsField = "BALMLayout:ytabs"; const char* kMyTabsField = "BALMLayout:tabs"; const char* kInsetsField = "BALMLayout:insets"; const char* kSpacingField = "BALMLayout:spacing"; const char* kTabsField = "BALMLayout:item:tabs"; const char* kItemAspectRatio = "BALMLayout:item:aspect"; const char* kItemPenalties = "BALMLayout:item:penalties"; const char* kItemInsets = "BALMLayout:item:insets"; int CompareXTabFunc(const XTab* tab1, const XTab* tab2); int CompareYTabFunc(const YTab* tab1, const YTab* tab2); }; namespace BALM { template struct BALMLayout::TabAddTransaction { ~TabAddTransaction() { if (fTab) fLayout->_RemoveSelfFromTab(fTab); if (fIndex > 0) _TabList()->RemoveItemAt(fIndex); } TabAddTransaction(BALMLayout* layout) : fTab(NULL), fLayout(layout), fIndex(-1) { } bool AttempAdd(T* tab) { if (fLayout->_HasTabInLayout(tab)) return true; if (!fLayout->_AddedTab(tab)) return false; fTab = tab; BObjectList* tabList = _TabList(); int32 index = tabList->CountItems(); if (!tabList->AddItem(tab, index)) return false; fIndex = index; return true; } void Commit() { fTab = NULL; fIndex = -1; } private: BObjectList* _TabList(); T* fTab; BALMLayout* fLayout; int32 fIndex; }; template <> BObjectList* BALMLayout::TabAddTransaction::_TabList() { return &fLayout->fXTabList; } template <> BObjectList* BALMLayout::TabAddTransaction::_TabList() { return &fLayout->fYTabList; } }; // end namespace BALM BALM::BALMLayout::BadLayoutPolicy::BadLayoutPolicy() { } BALM::BALMLayout::BadLayoutPolicy::BadLayoutPolicy(BMessage* archive) : BArchivable(archive) { } BALM::BALMLayout::BadLayoutPolicy::~BadLayoutPolicy() { } BALM::BALMLayout::DefaultPolicy::DefaultPolicy() { } BALM::BALMLayout::DefaultPolicy::DefaultPolicy(BMessage* archive) : BadLayoutPolicy(archive) { } BALM::BALMLayout::DefaultPolicy::~DefaultPolicy() { } bool BALM::BALMLayout::DefaultPolicy::OnBadLayout(BALMLayout* layout, ResultType result, BLayoutContext* context) { if (!context) return true; if (result == kInfeasible) { printf("BALMLayout failed to solve your layout!\n"); return false; } else return true; } status_t BALM::BALMLayout::DefaultPolicy::Archive(BMessage* archive, bool deep) const { return BadLayoutPolicy::Archive(archive, deep); } BArchivable* BALM::BALMLayout::DefaultPolicy::Instantiate(BMessage* archive) { if (validate_instantiation(archive, "BALM::BALMLayout::DefaultPolicy")) return new DefaultPolicy(archive); return NULL; } class BALMLayout::BALMLayoutSpecListener : public LinearProgramming::SpecificationListener { public: BALMLayoutSpecListener(BALMLayout* layout) : fLayout(layout) { } void ConstraintRemoved(Constraint* constraint) { fLayout->fConstraints.RemoveItem(constraint); } private: BALMLayout* fLayout; }; /*! * Constructor. * Creates new layout engine. * * If friendLayout is not NULL the solver of the friend layout is used. */ BALMLayout::BALMLayout(float hSpacing, float vSpacing, BALMLayout* friendLayout) : fLeftInset(0), fRightInset(0), fTopInset(0), fBottomInset(0), fHSpacing(BControlLook::ComposeSpacing(hSpacing)), fVSpacing(BControlLook::ComposeSpacing(vSpacing)), fXTabsSorted(false), fYTabsSorted(false), fBadLayoutPolicy(new DefaultPolicy()) { _SetSolver(friendLayout ? friendLayout->fSolver : new SharedSolver()); fSpecListener = new BALMLayoutSpecListener(this); Solver()->AddListener(fSpecListener); fLeft = AddXTab(); fRight = AddXTab(); fTop = AddYTab(); fBottom = AddYTab(); // the Left tab is always at x-position 0, and the Top tab is always at // y-position 0 fLeft->SetRange(0, 0); fTop->SetRange(0, 0); // cached layout values // need to be invalidated whenever the layout specification is changed fMinSize = kUnsetSize; fMaxSize = kUnsetSize; fPreferredSize = kUnsetSize; } BALMLayout::BALMLayout(BMessage* archive) : BAbstractLayout(BUnarchiver::PrepareArchive(archive)), fSolver(NULL), fLeft(NULL), fRight(NULL), fTop(NULL), fBottom(NULL), fMinSize(kUnsetSize), fMaxSize(kUnsetSize), fPreferredSize(kUnsetSize), fXTabsSorted(false), fYTabsSorted(false), fBadLayoutPolicy(new DefaultPolicy()) { BUnarchiver unarchiver(archive); BRect insets; status_t err = archive->FindRect(kInsetsField, &insets); if (err != B_OK) { unarchiver.Finish(err); return; } fLeftInset = insets.left; fRightInset = insets.right; fTopInset = insets.top; fBottomInset = insets.bottom; BSize spacing; err = archive->FindSize(kSpacingField, &spacing); if (err != B_OK) { unarchiver.Finish(err); return; } fHSpacing = spacing.width; fVSpacing = spacing.height; int32 tabCount = 0; archive->GetInfo(kXTabsField, NULL, &tabCount); for (int32 i = 0; i < tabCount && err == B_OK; i++) err = unarchiver.EnsureUnarchived(kXTabsField, i); archive->GetInfo(kYTabsField, NULL, &tabCount); for (int32 i = 0; i < tabCount && err == B_OK; i++) err = unarchiver.EnsureUnarchived(kYTabsField, i); if (err == B_OK && archive->GetInfo(kBadLayoutPolicyField, NULL) == B_OK) err = unarchiver.EnsureUnarchived(kBadLayoutPolicyField); if (err == B_OK) err = unarchiver.EnsureUnarchived(kSolverField); unarchiver.Finish(err); fSpecListener = new BALMLayoutSpecListener(this); Solver()->AddListener(fSpecListener); } BALMLayout::~BALMLayout() { Solver()->RemoveListener(fSpecListener); delete fSpecListener; delete fRowColumnManager; delete fBadLayoutPolicy; for (int32 i = 0; i < fConstraints.CountItems(); i++) Solver()->RemoveConstraint(fConstraints.ItemAt(i), true); if (fSolver) { fSolver->LayoutLeaving(this); fSolver->ReleaseReference(); } } /** * Adds a new x-tab to the specification. * * @return the new x-tab */ BReference BALMLayout::AddXTab() { BReference tab(new(std::nothrow) XTab(this), true); if (!tab) return NULL; if (!Solver()->AddVariable(tab)) return NULL; fXTabList.AddItem(tab); if (!tab->AddedToLayout(this)) { fXTabList.RemoveItem(tab); return NULL; } fXTabsSorted = false; return tab; } void BALMLayout::AddXTabs(BReference* tabs, uint32 count) { for (uint32 i = 0; i < count; i++) tabs[i] = AddXTab(); } void BALMLayout::AddYTabs(BReference* tabs, uint32 count) { for (uint32 i = 0; i < count; i++) tabs[i] = AddYTab(); } /** * Adds a new y-tab to the specification. * * @return the new y-tab */ BReference BALMLayout::AddYTab() { BReference tab(new(std::nothrow) YTab(this), true); if (!tab.IsSet()) return NULL; if (!Solver()->AddVariable(tab)) return NULL; fYTabList.AddItem(tab); if (!tab->AddedToLayout(this)) { fYTabList.RemoveItem(tab); return NULL; } fYTabsSorted = false; return tab; } int32 BALMLayout::CountXTabs() const { return fXTabList.CountItems(); } int32 BALMLayout::CountYTabs() const { return fYTabList.CountItems(); } XTab* BALMLayout::XTabAt(int32 index, bool ordered) { if (ordered && !fXTabsSorted) { Layout(); fXTabList.SortItems(CompareXTabFunc); fXTabsSorted = true; } return fXTabList.ItemAt(index); } XTab* BALMLayout::XTabAt(int32 index) const { return fXTabList.ItemAt(index); } YTab* BALMLayout::YTabAt(int32 index, bool ordered) { if (ordered && !fYTabsSorted) { Layout(); fYTabList.SortItems(CompareYTabFunc); fYTabsSorted = true; } return fYTabList.ItemAt(index); } YTab* BALMLayout::YTabAt(int32 index) const { return fYTabList.ItemAt(index); } const XTabList BALMLayout::GetXTabs() const { return fXTabList; } const YTabList BALMLayout::GetYTabs() const { return fYTabList; } int32 BALMLayout::IndexOf(XTab* tab, bool ordered) { if (ordered && !fXTabsSorted) { Layout(); fXTabList.SortItems(CompareXTabFunc); fXTabsSorted = true; } return fXTabList.IndexOf(tab); } int32 BALMLayout::IndexOf(YTab* tab, bool ordered) { if (ordered && !fYTabsSorted) { Layout(); fYTabList.SortItems(CompareYTabFunc); fYTabsSorted = true; } return fYTabList.IndexOf(tab); } int32 BALMLayout::CountConstraints() const { return fConstraints.CountItems(); } Constraint* BALMLayout::ConstraintAt(int32 index) const { return fConstraints.ItemAt(index); } bool BALMLayout::AddConstraint(Constraint* constraint) { fConstraints.AddItem(constraint); return Solver()->AddConstraint(constraint); } bool BALMLayout::RemoveConstraint(Constraint* constraint, bool deleteConstraint) { if (!fConstraints.RemoveItem(constraint)) return false; return Solver()->RemoveConstraint(constraint, deleteConstraint); } Constraint* BALMLayout::AddConstraint(double coeff1, Variable* var1, OperatorType op, double rightSide, double penaltyNeg, double penaltyPos) { Constraint* constraint = Solver()->AddConstraint(coeff1, var1, op, rightSide, penaltyNeg, penaltyPos); fConstraints.AddItem(constraint); return constraint; } Constraint* BALMLayout::AddConstraint(double coeff1, Variable* var1, double coeff2, Variable* var2, OperatorType op, double rightSide, double penaltyNeg, double penaltyPos) { Constraint* constraint = Solver()->AddConstraint(coeff1, var1, coeff2, var2, op, rightSide, penaltyNeg, penaltyPos); fConstraints.AddItem(constraint); return constraint; } Constraint* BALMLayout::AddConstraint(double coeff1, Variable* var1, double coeff2, Variable* var2, double coeff3, Variable* var3, OperatorType op, double rightSide, double penaltyNeg, double penaltyPos) { Constraint* constraint = Solver()->AddConstraint(coeff1, var1, coeff2, var2, coeff3, var3, op, rightSide, penaltyNeg, penaltyPos); fConstraints.AddItem(constraint); return constraint; } Constraint* BALMLayout::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) { Constraint* constraint = Solver()->AddConstraint(coeff1, var1, coeff2, var2, coeff3, var3, coeff4, var4, op, rightSide, penaltyNeg, penaltyPos); fConstraints.AddItem(constraint); return constraint; } namespace { int CompareXTabFunc(const XTab* tab1, const XTab* tab2) { if (tab1->Value() < tab2->Value()) return -1; else if (tab1->Value() == tab2->Value()) return 0; return 1; } int CompareYTabFunc(const YTab* tab1, const YTab* tab2) { if (tab1->Value() < tab2->Value()) return -1; else if (tab1->Value() == tab2->Value()) return 0; return 1; } }; // end anonymous namespace /** * Adds a new row to the specification that is glued to the given y-tabs. * * @param top * @param bottom * @return the new row */ Row* BALMLayout::AddRow(YTab* _top, YTab* _bottom) { BReference top = _top; BReference bottom = _bottom; if (_top == NULL) top = AddYTab(); if (_bottom == NULL) bottom = AddYTab(); return new(std::nothrow) Row(Solver(), top, bottom); } /** * Adds a new column to the specification that is glued to the given x-tabs. * * @param left * @param right * @return the new column */ Column* BALMLayout::AddColumn(XTab* _left, XTab* _right) { BReference left = _left; BReference right = _right; if (_left == NULL) left = AddXTab(); if (_right == NULL) right = AddXTab(); return new(std::nothrow) Column(Solver(), left, right); } Area* BALMLayout::AreaFor(int32 id) const { int32 areaCount = CountAreas(); for (int32 i = 0; i < areaCount; i++) { Area* area = AreaAt(i); if (area->ID() == id) return area; } return NULL; } /** * Finds the area that contains the given control. * * @param control the control to look for * @return the area that contains the control */ Area* BALMLayout::AreaFor(const BView* control) const { return AreaFor(ItemAt(IndexOfView(const_cast(control)))); } Area* BALMLayout::AreaFor(const BLayoutItem* item) const { if (!item) return NULL; return static_cast(item->LayoutData()); } int32 BALMLayout::CountAreas() const { return CountItems(); } Area* BALMLayout::AreaAt(int32 index) const { return AreaFor(ItemAt(index)); } XTab* BALMLayout::LeftOf(const BView* view) const { Area* area = AreaFor(view); if (!area) return NULL; return area->Left(); } XTab* BALMLayout::LeftOf(const BLayoutItem* item) const { Area* area = AreaFor(item); if (!area) return NULL; return area->Left(); } XTab* BALMLayout::RightOf(const BView* view) const { Area* area = AreaFor(view); if (!area) return NULL; return area->Right(); } XTab* BALMLayout::RightOf(const BLayoutItem* item) const { Area* area = AreaFor(item); if (!area) return NULL; return area->Right(); } YTab* BALMLayout::TopOf(const BView* view) const { Area* area = AreaFor(view); if (!area) return NULL; return area->Top(); } YTab* BALMLayout::TopOf(const BLayoutItem* item) const { Area* area = AreaFor(item); if (!area) return NULL; return area->Top(); } YTab* BALMLayout::BottomOf(const BView* view) const { Area* area = AreaFor(view); if (!area) return NULL; return area->Bottom(); } YTab* BALMLayout::BottomOf(const BLayoutItem* item) const { Area* area = AreaFor(item); if (!area) return NULL; return area->Bottom(); } BLayoutItem* BALMLayout::AddView(BView* child) { return AddView(-1, child); } BLayoutItem* BALMLayout::AddView(int32 index, BView* child) { return BAbstractLayout::AddView(index, child); } /** * Adds a new area to the specification, automatically setting preferred size constraints. * * @param left left border * @param top top border * @param right right border * @param bottom bottom border * @param content the control which is the area content * @return the new area */ Area* BALMLayout::AddView(BView* view, XTab* left, YTab* top, XTab* right, YTab* bottom) { BLayoutItem* item = _LayoutItemToAdd(view); Area* area = AddItem(item, left, top, right, bottom); if (!area) { if (item != view->GetLayout()) delete item; return NULL; } return area; } /** * Adds a new area to the specification, automatically setting preferred size constraints. * * @param row the row that defines the top and bottom border * @param column the column that defines the left and right border * @param content the control which is the area content * @return the new area */ Area* BALMLayout::AddView(BView* view, Row* row, Column* column) { BLayoutItem* item = _LayoutItemToAdd(view); Area* area = AddItem(item, row, column); if (!area) { if (item != view->GetLayout()) delete item; return NULL; } return area; } bool BALMLayout::AddItem(BLayoutItem* item) { return AddItem(-1, item); } bool BALMLayout::AddItem(int32 index, BLayoutItem* item) { if (!item) return false; // simply add the item at the upper right corner of the previous item // TODO maybe find a more elegant solution XTab* left = Left(); YTab* top = Top(); // check range if (index < 0 || index > CountItems()) index = CountItems(); // for index = 0 we already have set the right tabs if (index != 0) { BLayoutItem* prevItem = ItemAt(index - 1); Area* area = AreaFor(prevItem); if (area) { left = area->Right(); top = area->Top(); } } Area* area = AddItem(item, left, top); return area ? true : false; } Area* BALMLayout::AddItem(BLayoutItem* item, XTab* _left, YTab* _top, XTab* _right, YTab* _bottom) { if ((_left && !_left->IsSuitableFor(this)) || (_top && !_top->IsSuitableFor(this)) || (_right && !_right->IsSuitableFor(this)) || (_bottom && !_bottom->IsSuitableFor(this))) debugger("Tab added to unfriendly layout!"); BReference right = _right; if (!right.IsSet()) right = AddXTab(); BReference bottom = _bottom; if (!bottom.IsSet()) bottom = AddYTab(); BReference left = _left; if (!left.IsSet()) left = AddXTab(); BReference top = _top; if (!top.IsSet()) top = AddYTab(); TabAddTransaction leftTabAdd(this); if (!leftTabAdd.AttempAdd(left)) return NULL; TabAddTransaction topTabAdd(this); if (!topTabAdd.AttempAdd(top)) return NULL; TabAddTransaction rightTabAdd(this); if (!rightTabAdd.AttempAdd(right)) return NULL; TabAddTransaction bottomTabAdd(this); if (!bottomTabAdd.AttempAdd(bottom)) return NULL; // Area is added in ItemAdded if (!BAbstractLayout::AddItem(-1, item)) return NULL; Area* area = AreaFor(item); if (!area) { RemoveItem(item); return NULL; } fSolver->Invalidate(true); area->_Init(Solver(), left, top, right, bottom, fRowColumnManager); fRowColumnManager->AddArea(area); leftTabAdd.Commit(); topTabAdd.Commit(); rightTabAdd.Commit(); bottomTabAdd.Commit(); return area; } Area* BALMLayout::AddItem(BLayoutItem* item, Row* row, Column* column) { if (!BAbstractLayout::AddItem(-1, item)) return NULL; Area* area = AreaFor(item); if (!area) return NULL; fSolver->Invalidate(true); area->_Init(Solver(), row, column, fRowColumnManager); fRowColumnManager->AddArea(area); return area; } /** * Gets the left variable. */ XTab* BALMLayout::Left() const { return fLeft; } /** * Gets the right variable. */ XTab* BALMLayout::Right() const { return fRight; } /** * Gets the top variable. */ YTab* BALMLayout::Top() const { return fTop; } /** * Gets the bottom variable. */ YTab* BALMLayout::Bottom() const { return fBottom; } void BALMLayout::SetBadLayoutPolicy(BadLayoutPolicy* policy) { if (fBadLayoutPolicy != policy) delete fBadLayoutPolicy; if (policy == NULL) policy = new DefaultPolicy(); fBadLayoutPolicy = policy; } struct BALMLayout::BadLayoutPolicy* BALMLayout::GetBadLayoutPolicy() const { return fBadLayoutPolicy; } /** * Gets minimum size. */ BSize BALMLayout::BaseMinSize() { ResultType result = fSolver->ValidateMinSize(); if (result != kOptimal && result != kUnbounded) fBadLayoutPolicy->OnBadLayout(this, result, NULL); return fMinSize; } /** * Gets maximum size. */ BSize BALMLayout::BaseMaxSize() { ResultType result = fSolver->ValidateMaxSize(); if (result != kOptimal && result != kUnbounded) fBadLayoutPolicy->OnBadLayout(this, result, NULL); return fMaxSize; } /** * Gets preferred size. */ BSize BALMLayout::BasePreferredSize() { ResultType result = fSolver->ValidatePreferredSize(); if (result != kOptimal) fBadLayoutPolicy->OnBadLayout(this, result, NULL); return fPreferredSize; } /** * Gets the alignment. */ BAlignment BALMLayout::BaseAlignment() { BAlignment alignment; alignment.SetHorizontal(B_ALIGN_HORIZONTAL_CENTER); alignment.SetVertical(B_ALIGN_VERTICAL_CENTER); return alignment; } status_t BALMLayout::Archive(BMessage* into, bool deep) const { BArchiver archiver(into); status_t err = BAbstractLayout::Archive(into, deep); if (err != B_OK) return archiver.Finish(err); BRect insets(fLeftInset, fTopInset, fRightInset, fBottomInset); err = into->AddRect(kInsetsField, insets); if (err != B_OK) return archiver.Finish(err); BSize spacing(fHSpacing, fVSpacing); err = into->AddSize(kSpacingField, spacing); if (err != B_OK) return archiver.Finish(err); if (deep) { for (int32 i = CountXTabs() - 1; i >= 0 && err == B_OK; i--) err = archiver.AddArchivable(kXTabsField, XTabAt(i)); for (int32 i = CountYTabs() - 1; i >= 0 && err == B_OK; i--) err = archiver.AddArchivable(kYTabsField, YTabAt(i)); err = archiver.AddArchivable(kBadLayoutPolicyField, fBadLayoutPolicy); } if (err == B_OK) err = archiver.AddArchivable(kSolverField, fSolver); if (err == B_OK) err = archiver.AddArchivable(kMyTabsField, fLeft); if (err == B_OK) err = archiver.AddArchivable(kMyTabsField, fTop); if (err == B_OK) err = archiver.AddArchivable(kMyTabsField, fRight); if (err == B_OK) err = archiver.AddArchivable(kMyTabsField, fBottom); return archiver.Finish(err); } BArchivable* BALMLayout::Instantiate(BMessage* from) { if (validate_instantiation(from, "BALM::BALMLayout")) return new BALMLayout(from); return NULL; } status_t BALMLayout::ItemArchived(BMessage* into, BLayoutItem* item, int32 index) const { BArchiver archiver(into); status_t err = BAbstractLayout::ItemArchived(into, item, index); if (err != B_OK) return err; Area* area = AreaFor(item); err = into->AddSize(kItemPenalties, area->fShrinkPenalties); if (err == B_OK) err = into->AddSize(kItemPenalties, area->fGrowPenalties); if (err == B_OK) err = into->AddSize(kItemInsets, area->fLeftTopInset); if (err == B_OK) err = into->AddSize(kItemInsets, area->fRightBottomInset); if (err == B_OK) err = into->AddDouble(kItemAspectRatio, area->fContentAspectRatio); err = archiver.AddArchivable(kTabsField, area->Left()); if (err == B_OK) archiver.AddArchivable(kTabsField, area->Top()); if (err == B_OK) archiver.AddArchivable(kTabsField, area->Right()); if (err == B_OK) archiver.AddArchivable(kTabsField, area->Bottom()); return err; } status_t BALMLayout::ItemUnarchived(const BMessage* from, BLayoutItem* item, int32 index) { BUnarchiver unarchiver(from); status_t err = BAbstractLayout::ItemUnarchived(from, item, index); if (err != B_OK) return err; Area* area = AreaFor(item); XTab* left; XTab* right; YTab* bottom; YTab* top; err = unarchiver.FindObject(kTabsField, index * 4, left); if (err == B_OK) err = unarchiver.FindObject(kTabsField, index * 4 + 1, top); if (err == B_OK) err = unarchiver.FindObject(kTabsField, index * 4 + 2, right); if (err == B_OK) err = unarchiver.FindObject(kTabsField, index * 4 + 3, bottom); if (err != B_OK) return err; area->_Init(Solver(), left, top, right, bottom, fRowColumnManager); fRowColumnManager->AddArea(area); err = from->FindSize(kItemPenalties, index * 2, &area->fShrinkPenalties); if (err != B_OK) return err; err = from->FindSize(kItemPenalties, index * 2 + 1, &area->fGrowPenalties); if (err != B_OK) return err; err = from->FindSize(kItemInsets, index * 2, &area->fLeftTopInset); if (err != B_OK) return err; err = from->FindSize(kItemInsets, index * 2 + 1, &area->fRightBottomInset); if (err == B_OK) { double contentAspectRatio; err = from->FindDouble(kItemAspectRatio, index, &contentAspectRatio); if (err == B_OK) area->SetContentAspectRatio(contentAspectRatio); } return err; } status_t BALMLayout::AllUnarchived(const BMessage* archive) { BUnarchiver unarchiver(archive); SharedSolver* solver; status_t err = unarchiver.FindObject(kSolverField, solver); if (err != B_OK) return err; _SetSolver(solver); if (archive->GetInfo(kBadLayoutPolicyField, NULL) == B_OK) { BadLayoutPolicy* policy; err = unarchiver.FindObject(kBadLayoutPolicyField, policy); if (err == B_OK) SetBadLayoutPolicy(policy); } LinearSpec* spec = Solver(); int32 tabCount = 0; archive->GetInfo(kXTabsField, NULL, &tabCount); for (int32 i = 0; i < tabCount && err == B_OK; i++) { XTab* tab; err = unarchiver.FindObject(kXTabsField, i, BUnarchiver::B_DONT_ASSUME_OWNERSHIP, tab); spec->AddVariable(tab); TabAddTransaction adder(this); if (adder.AttempAdd(tab)) adder.Commit(); else err = B_NO_MEMORY; } archive->GetInfo(kYTabsField, NULL, &tabCount); for (int32 i = 0; i < tabCount; i++) { YTab* tab; unarchiver.FindObject(kYTabsField, i, BUnarchiver::B_DONT_ASSUME_OWNERSHIP, tab); spec->AddVariable(tab); TabAddTransaction adder(this); if (adder.AttempAdd(tab)) adder.Commit(); else err = B_NO_MEMORY; } if (err == B_OK) { XTab* leftTab = NULL; err = unarchiver.FindObject(kMyTabsField, 0, leftTab); fLeft = leftTab; } if (err == B_OK) { YTab* topTab = NULL; err = unarchiver.FindObject(kMyTabsField, 1, topTab); fTop = topTab; } if (err == B_OK) { XTab* rightTab = NULL; err = unarchiver.FindObject(kMyTabsField, 2, rightTab); fRight = rightTab; } if (err == B_OK) { YTab* bottomTab = NULL; err = unarchiver.FindObject(kMyTabsField, 3, bottomTab); fBottom = bottomTab; } if (err == B_OK) { fLeft->SetRange(0, 0); fTop->SetRange(0, 0); err = BAbstractLayout::AllUnarchived(archive); } return err; } status_t BALMLayout::AllArchived(BMessage* archive) const { status_t err = BAbstractLayout::AllArchived(archive); return err; } /** * Invalidates the layout. * Resets minimum/maximum/preferred size. */ void BALMLayout::LayoutInvalidated(bool children) { fMinSize = kUnsetSize; fMaxSize = kUnsetSize; fPreferredSize = kUnsetSize; fXTabsSorted = false; fYTabsSorted = false; if (fSolver) fSolver->Invalidate(children); } bool BALMLayout::ItemAdded(BLayoutItem* item, int32 atIndex) { item->SetLayoutData(new(std::nothrow) Area(item)); return item->LayoutData() != NULL; } void BALMLayout::ItemRemoved(BLayoutItem* item, int32 fromIndex) { if (Area* area = AreaFor(item)) { fRowColumnManager->RemoveArea(area); item->SetLayoutData(NULL); delete area; } } /** * Calculate and set the layout. * If no layout specification is given, a specification is reverse engineered automatically. */ void BALMLayout::DoLayout() { BLayoutContext* context = LayoutContext(); ResultType result = fSolver->ValidateLayout(context); if (result != kOptimal && !fBadLayoutPolicy->OnBadLayout(this, result, context)) { return; } // set the calculated positions and sizes for every area for (int32 i = 0; i < CountItems(); i++) AreaFor(ItemAt(i))->_DoLayout(LayoutArea().LeftTop()); fXTabsSorted = false; fYTabsSorted = false; } LinearSpec* BALMLayout::Solver() const { return fSolver->Solver(); } ResultType BALMLayout::ValidateLayout() { // we explicitly recaluclate the layout so set the invalidate flag first fSolver->Invalidate(true); BLayoutContext* context = LayoutContext(); return fSolver->ValidateLayout(context); } void BALMLayout::SetInsets(float left, float top, float right, float bottom) { fLeftInset = BControlLook::ComposeSpacing(left); fTopInset = BControlLook::ComposeSpacing(top); fRightInset = BControlLook::ComposeSpacing(right); fBottomInset = BControlLook::ComposeSpacing(bottom); InvalidateLayout(); } void BALMLayout::SetInsets(float horizontal, float vertical) { fLeftInset = BControlLook::ComposeSpacing(horizontal); fRightInset = fLeftInset; fTopInset = BControlLook::ComposeSpacing(vertical); fBottomInset = fTopInset; InvalidateLayout(); } void BALMLayout::SetInsets(float insets) { fLeftInset = BControlLook::ComposeSpacing(insets); fRightInset = fLeftInset; fTopInset = fLeftInset; fBottomInset = fLeftInset; InvalidateLayout(); } void BALMLayout::GetInsets(float* left, float* top, float* right, float* bottom) const { if (left) *left = fLeftInset; if (top) *top = fTopInset; if (right) *right = fRightInset; if (bottom) *bottom = fBottomInset; } void BALMLayout::SetSpacing(float hSpacing, float vSpacing) { fHSpacing = BControlLook::ComposeSpacing(hSpacing); fVSpacing = BControlLook::ComposeSpacing(vSpacing); } void BALMLayout::GetSpacing(float *_hSpacing, float *_vSpacing) const { if (_hSpacing) *_hSpacing = fHSpacing; if (_vSpacing) *_vSpacing = fVSpacing; } float BALMLayout::InsetForTab(XTab* tab) const { if (tab == fLeft.Get()) return fLeftInset; if (tab == fRight.Get()) return fRightInset; return fHSpacing / 2; } float BALMLayout::InsetForTab(YTab* tab) const { if (tab == fTop.Get()) return fTopInset; if (tab == fBottom.Get()) return fBottomInset; return fVSpacing / 2; } void BALMLayout::UpdateConstraints(BLayoutContext* context) { for (int i = 0; i < CountItems(); i++) AreaFor(ItemAt(i))->InvalidateSizeConstraints(); fRowColumnManager->UpdateConstraints(); } void BALMLayout::_RemoveSelfFromTab(XTab* tab) { tab->LayoutLeaving(this); } void BALMLayout::_RemoveSelfFromTab(YTab* tab) { tab->LayoutLeaving(this); } bool BALMLayout::_HasTabInLayout(XTab* tab) { return tab->IsInLayout(this); } bool BALMLayout::_HasTabInLayout(YTab* tab) { return tab->IsInLayout(this); } bool BALMLayout::_AddedTab(XTab* tab) { return tab->AddedToLayout(this); } bool BALMLayout::_AddedTab(YTab* tab) { return tab->AddedToLayout(this); } BLayoutItem* BALMLayout::_LayoutItemToAdd(BView* view) { if (view->GetLayout()) return view->GetLayout(); return new(std::nothrow) BViewLayoutItem(view); } void BALMLayout::_SetSolver(SharedSolver* solver) { fSolver = solver; fSolver->AcquireReference(); fSolver->RegisterLayout(this); fRowColumnManager = new RowColumnManager(Solver()); } status_t BALMLayout::Perform(perform_code d, void* arg) { return BAbstractLayout::Perform(d, arg); } void BALMLayout::_ReservedALMLayout1() {} void BALMLayout::_ReservedALMLayout2() {} void BALMLayout::_ReservedALMLayout3() {} void BALMLayout::_ReservedALMLayout4() {} void BALMLayout::_ReservedALMLayout5() {} void BALMLayout::_ReservedALMLayout6() {} void BALMLayout::_ReservedALMLayout7() {} void BALMLayout::_ReservedALMLayout8() {} void BALMLayout::_ReservedALMLayout9() {} void BALMLayout::_ReservedALMLayout10() {}