/*
* Copyright 2007-2008, Christof Lutteroth, lutteroth@cs.auckland.ac.nz
* Copyright 2007-2008, James Kim, jkim202@ec.auckland.ac.nz
* Copyright 2010, Clemens Zeidler <haiku@clemens-zeidler.de>
* Distributed under the terms of the MIT License.
*/
#include "ALMLayout.h"
#include <stdio.h>
#include <vector>
#include <AutoDeleter.h>
#include <ControlLook.h>
#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 <class T>
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<T>* 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<T>* _TabList();
T* fTab;
BALMLayout* fLayout;
int32 fIndex;
};
template <>
BObjectList<XTab>*
BALMLayout::TabAddTransaction<XTab>::_TabList()
{
return &fLayout->fXTabList;
}
template <>
BObjectList<YTab>*
BALMLayout::TabAddTransaction<YTab>::_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<XTab>
BALMLayout::AddXTab()
{
BReference<XTab> 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<XTab>* tabs, uint32 count)
{
for (uint32 i = 0; i < count; i++)
tabs[i] = AddXTab();
}
void
BALMLayout::AddYTabs(BReference<YTab>* 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<YTab>
BALMLayout::AddYTab()
{
BReference<YTab> tab(new(std::nothrow) YTab(this), true);
if (tab.Get() == NULL)
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<YTab> top = _top;
BReference<YTab> 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<XTab> left = _left;
BReference<XTab> 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<BView*>(control))));
}
Area*
BALMLayout::AreaFor(const BLayoutItem* item) const
{
if (!item)
return NULL;
return static_cast<Area*>(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<XTab> right = _right;
if (right.Get() == NULL)
right = AddXTab();
BReference<YTab> bottom = _bottom;
if (bottom.Get() == NULL)
bottom = AddYTab();
BReference<XTab> left = _left;
if (left.Get() == NULL)
left = AddXTab();
BReference<YTab> top = _top;
if (top.Get() == NULL)
top = AddYTab();
TabAddTransaction<XTab> leftTabAdd(this);
if (!leftTabAdd.AttempAdd(left))
return NULL;
TabAddTransaction<YTab> topTabAdd(this);
if (!topTabAdd.AttempAdd(top))
return NULL;
TabAddTransaction<XTab> rightTabAdd(this);
if (!rightTabAdd.AttempAdd(right))
return NULL;
TabAddTransaction<YTab> 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<XTab> 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<YTab> 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() {}
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fRowColumnManager, _reserved.
↑ V614 Potentially uninitialized pointer 'bottom' used. Consider checking the fifth actual argument of the '_Init' function.
↑ V614 Potentially uninitialized pointer 'top' used. Consider checking the third actual argument of the '_Init' function.
↑ V614 Potentially uninitialized pointer 'right' used. Consider checking the fourth actual argument of the '_Init' function.