* 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 "Area.h"
#include <Alignment.h>
#include <ControlLook.h>
#include <View.h>
#include "ALMLayout.h"
#include "RowColumnManager.h"
#include "Row.h"
#include "Column.h"
using namespace LinearProgramming;
BLayoutItem*
Area::Item()
{
return fLayoutItem;
}
* Gets the left tab of the area.
*
* @return the left tab of the area
*/
XTab*
Area::Left() const
{
return fLeft;
}
* Gets the right tab of the area.
*
* @return the right tab of the area
*/
XTab*
Area::Right() const
{
return fRight;
}
* Gets the top tab of the area.
*/
YTab*
Area::Top() const
{
return fTop;
}
* Gets the bottom tab of the area.
*/
YTab*
Area::Bottom() const
{
return fBottom;
}
* Sets the left tab of the area.
*
* @param left the left tab of the area
*/
void
Area::SetLeft(BReference<XTab> left)
{
fLeft = left;
fMinContentWidth->SetLeftSide(-1.0, fLeft, 1.0, fRight);
if (fMaxContentWidth != NULL)
fMaxContentWidth->SetLeftSide(-1.0, fLeft, 1.0, fRight);
fRowColumnManager->TabsChanged(this);
fLayoutItem->Layout()->InvalidateLayout();
}
* Sets the right tab of the area.
*
* @param right the right tab of the area
*/
void
Area::SetRight(BReference<XTab> right)
{
fRight = right;
fMinContentWidth->SetLeftSide(-1.0, fLeft, 1.0, fRight);
if (fMaxContentWidth != NULL)
fMaxContentWidth->SetLeftSide(-1.0, fLeft, 1.0, fRight);
fRowColumnManager->TabsChanged(this);
fLayoutItem->Layout()->InvalidateLayout();
}
* Sets the top tab of the area.
*/
void
Area::SetTop(BReference<YTab> top)
{
fTop = top;
fMinContentHeight->SetLeftSide(-1.0, fTop, 1.0, fBottom);
if (fMaxContentHeight != NULL)
fMaxContentHeight->SetLeftSide(-1.0, fTop, 1.0, fBottom);
fRowColumnManager->TabsChanged(this);
fLayoutItem->Layout()->InvalidateLayout();
}
* Sets the bottom tab of the area.
*/
void
Area::SetBottom(BReference<YTab> bottom)
{
fBottom = bottom;
fMinContentHeight->SetLeftSide(-1.0, fTop, 1.0, fBottom);
if (fMaxContentHeight != NULL)
fMaxContentHeight->SetLeftSide(-1.0, fTop, 1.0, fBottom);
fRowColumnManager->TabsChanged(this);
fLayoutItem->Layout()->InvalidateLayout();
}
* Gets the row that defines the top and bottom tabs.
*/
Row*
Area::GetRow() const
{
return fRow;
}
* Gets the column that defines the left and right tabs.
*/
Column*
Area::GetColumn() const
{
return fColumn;
}
* The reluctance with which the area's content shrinks below its preferred size.
* The bigger the less likely is such shrinking.
*/
BSize
Area::ShrinkPenalties() const
{
return fShrinkPenalties;
}
* The reluctance with which the area's content grows over its preferred size.
* The bigger the less likely is such growth.
*/
BSize
Area::GrowPenalties() const
{
return fGrowPenalties;
}
void
Area::SetShrinkPenalties(BSize shrink) {
fShrinkPenalties = shrink;
fLayoutItem->Layout()->InvalidateLayout();
}
void
Area::SetGrowPenalties(BSize grow)
{
fGrowPenalties = grow;
fLayoutItem->Layout()->InvalidateLayout();
}
* Gets aspect ratio of the area's content.
*/
double
Area::ContentAspectRatio() const
{
return fContentAspectRatio;
}
* Sets aspect ratio of the area's content.
* May be different from the aspect ratio of the area.
*/
void
Area::SetContentAspectRatio(double ratio)
{
fContentAspectRatio = ratio;
if (fContentAspectRatio <= 0) {
delete fContentAspectRatioC;
fContentAspectRatioC = NULL;
} else if (fContentAspectRatioC == NULL) {
fContentAspectRatioC = fLS->AddConstraint(-1.0, fLeft, 1.0, fRight,
ratio, fTop, -ratio, fBottom, kEQ, 0.0);
} else {
fContentAspectRatioC->SetLeftSide(-1.0, fLeft, 1.0, fRight, ratio,
fTop, -ratio, fBottom);
}
if (BLayout* layout = fLayoutItem->Layout())
layout->InvalidateLayout();
}
void
Area::GetInsets(float* left, float* top, float* right, float* bottom) const
{
if (left)
*left = fLeftTopInset.Width();
if (top)
*top = fLeftTopInset.Height();
if (right)
*right = fRightBottomInset.Width();
if (bottom)
*bottom = fRightBottomInset.Height();
}
* Gets left inset between area and its content.
*/
float
Area::LeftInset() const
{
if (fLeftTopInset.IsWidthSet())
return fLeftTopInset.Width();
BALMLayout* layout = static_cast<BALMLayout*>(fLayoutItem->Layout());
return layout->InsetForTab(fLeft.Get());
}
* Gets top inset between area and its content.
*/
float
Area::TopInset() const
{
if (fLeftTopInset.IsHeightSet())
return fLeftTopInset.Height();
BALMLayout* layout = static_cast<BALMLayout*>(fLayoutItem->Layout());
return layout->InsetForTab(fTop.Get());
}
* Gets right inset between area and its content.
*/
float
Area::RightInset() const
{
if (fRightBottomInset.IsWidthSet())
return fRightBottomInset.Width();
BALMLayout* layout = static_cast<BALMLayout*>(fLayoutItem->Layout());
return layout->InsetForTab(fRight.Get());
}
* Gets bottom inset between area and its content.
*/
float
Area::BottomInset() const
{
if (fRightBottomInset.IsHeightSet())
return fRightBottomInset.Height();
BALMLayout* layout = static_cast<BALMLayout*>(fLayoutItem->Layout());
return layout->InsetForTab(fBottom.Get());
}
void
Area::SetInsets(float insets)
{
if (insets != B_SIZE_UNSET)
insets = BControlLook::ComposeSpacing(insets);
fLeftTopInset.Set(insets, insets);
fRightBottomInset.Set(insets, insets);
fLayoutItem->Layout()->InvalidateLayout();
}
void
Area::SetInsets(float horizontal, float vertical)
{
if (horizontal != B_SIZE_UNSET)
horizontal = BControlLook::ComposeSpacing(horizontal);
if (vertical != B_SIZE_UNSET)
vertical = BControlLook::ComposeSpacing(vertical);
fLeftTopInset.Set(horizontal, horizontal);
fRightBottomInset.Set(vertical, vertical);
fLayoutItem->Layout()->InvalidateLayout();
}
void
Area::SetInsets(float left, float top, float right, float bottom)
{
if (left != B_SIZE_UNSET)
left = BControlLook::ComposeSpacing(left);
if (right != B_SIZE_UNSET)
right = BControlLook::ComposeSpacing(right);
if (top != B_SIZE_UNSET)
top = BControlLook::ComposeSpacing(top);
if (bottom != B_SIZE_UNSET)
bottom = BControlLook::ComposeSpacing(bottom);
fLeftTopInset.Set(left, top);
fRightBottomInset.Set(right, bottom);
fLayoutItem->Layout()->InvalidateLayout();
}
* Sets left inset between area and its content.
*/
void
Area::SetLeftInset(float left)
{
fLeftTopInset.width = left;
fLayoutItem->Layout()->InvalidateLayout();
}
* Sets top inset between area and its content.
*/
void
Area::SetTopInset(float top)
{
fLeftTopInset.height = top;
fLayoutItem->Layout()->InvalidateLayout();
}
* Sets right inset between area and its content.
*/
void
Area::SetRightInset(float right)
{
fRightBottomInset.width = right;
fLayoutItem->Layout()->InvalidateLayout();
}
* Sets bottom inset between area and its content.
*/
void
Area::SetBottomInset(float bottom)
{
fRightBottomInset.height = bottom;
fLayoutItem->Layout()->InvalidateLayout();
}
BString
Area::ToString() const
{
BString string = "Area(";
string += fLeft->ToString();
string << ", ";
string += fTop->ToString();
string << ", ";
string += fRight->ToString();
string << ", ";
string += fBottom->ToString();
string << ")";
return string;
}
* Sets the width of the area to be the same as the width of the given area
* times factor.
*
* @param area the area that should have the same width
* @return the same-width constraint
*/
Constraint*
Area::SetWidthAs(Area* area, float factor)
{
return fLS->AddConstraint(-1.0, fLeft, 1.0, fRight, factor, area->Left(),
-factor, area->Right(), kEQ, 0.0);
}
* Sets the height of the area to be the same as the height of the given area
* times factor.
*
* @param area the area that should have the same height
* @return the same-height constraint
*/
Constraint*
Area::SetHeightAs(Area* area, float factor)
{
return fLS->AddConstraint(-1.0, fTop, 1.0, fBottom, factor, area->Top(),
-factor, area->Bottom(), kEQ, 0.0);
}
void
Area::InvalidateSizeConstraints()
{
if (!fLeft)
return;
BSize minSize = fLayoutItem->MinSize();
BSize maxSize = fLayoutItem->MaxSize();
_UpdateMinSizeConstraint(minSize);
_UpdateMaxSizeConstraint(maxSize);
}
BRect
Area::Frame() const
{
return BRect(round(fLeft->Value()), round(fTop->Value()),
round(fRight->Value()), round(fBottom->Value()));
}
* Destructor.
* Removes the area from its specification.
*/
Area::~Area()
{
delete fMinContentWidth;
delete fMaxContentWidth;
delete fMinContentHeight;
delete fMaxContentHeight;
delete fContentAspectRatioC;
}
static int32 sAreaID = 0;
static int32
new_area_id()
{
return sAreaID++;
}
* Constructor.
* Uses XTabs and YTabs.
*/
Area::Area(BLayoutItem* item)
:
fLayoutItem(item),
fLS(NULL),
fLeft(NULL),
fRight(NULL),
fTop(NULL),
fBottom(NULL),
fRow(NULL),
fColumn(NULL),
fShrinkPenalties(5, 5),
fGrowPenalties(5, 5),
fContentAspectRatio(-1),
fRowColumnManager(NULL),
fMinContentWidth(NULL),
fMaxContentWidth(NULL),
fMinContentHeight(NULL),
fMaxContentHeight(NULL),
fContentAspectRatioC(NULL)
{
fID = new_area_id();
}
int32
Area::ID() const
{
return fID;
}
void
Area::SetID(int32 id)
{
fID = id;
}
* Initialize variables.
*/
void
Area::_Init(LinearSpec* ls, XTab* left, YTab* top, XTab* right, YTab* bottom,
RowColumnManager* manager)
{
fLS = ls;
fLeft = left;
fRight = right;
fTop = top;
fBottom = bottom;
fRowColumnManager = manager;
fMinContentWidth = ls->AddConstraint(-1.0, fLeft, 1.0, fRight, kGE, 0);
fMinContentHeight = ls->AddConstraint(-1.0, fTop, 1.0, fBottom, kGE, 0);
InvalidateSizeConstraints();
}
void
Area::_Init(LinearSpec* ls, Row* row, Column* column, RowColumnManager* manager)
{
_Init(ls, column->Left(), row->Top(), column->Right(),
row->Bottom(), manager);
fRow = row;
fColumn = column;
}
* Perform layout on the area.
*/
void
Area::_DoLayout(const BPoint& offset)
{
if (!fLeft)
return;
if (!fLayoutItem->IsVisible())
fLayoutItem->AlignInFrame(BRect(0, 0, -1, -1));
BRect areaFrame(Frame());
areaFrame.left += LeftInset();
areaFrame.right -= RightInset();
areaFrame.top += TopInset();
areaFrame.bottom -= BottomInset();
fLayoutItem->AlignInFrame(areaFrame.OffsetBySelf(offset));
}
void
Area::_UpdateMinSizeConstraint(BSize min)
{
if (!fLayoutItem->IsVisible()) {
fMinContentHeight->SetRightSide(-1);
fMinContentWidth->SetRightSide(-1);
return;
}
float width = 0.;
float height = 0.;
if (min.width > 0)
width = min.Width() + LeftInset() + RightInset();
if (min.height > 0)
height = min.Height() + TopInset() + BottomInset();
fMinContentWidth->SetRightSide(width);
fMinContentHeight->SetRightSide(height);
}
void
Area::_UpdateMaxSizeConstraint(BSize max)
{
if (!fLayoutItem->IsVisible()) {
if (fMaxContentHeight != NULL)
fMaxContentHeight->SetRightSide(B_SIZE_UNLIMITED);
if (fMaxContentWidth != NULL)
fMaxContentWidth->SetRightSide(B_SIZE_UNLIMITED);
return;
}
max.width += LeftInset() + RightInset();
max.height += TopInset() + BottomInset();
const double kPriority = 100;
BAlignment alignment = fLayoutItem->Alignment();
double priority = kPriority;
if (alignment.Vertical() == B_ALIGN_USE_FULL_HEIGHT)
priority = -1;
if (max.Height() < 20000) {
if (fMaxContentHeight == NULL) {
fMaxContentHeight = fLS->AddConstraint(-1.0, fTop, 1.0, fBottom,
kLE, max.Height(), priority, priority);
} else {
fMaxContentHeight->SetRightSide(max.Height());
fMaxContentHeight->SetPenaltyNeg(priority);
fMaxContentHeight->SetPenaltyPos(priority);
}
} else {
delete fMaxContentHeight;
fMaxContentHeight = NULL;
}
priority = kPriority;
if (alignment.Horizontal() == B_ALIGN_USE_FULL_WIDTH)
priority = -1;
if (max.Width() < 20000) {
if (fMaxContentWidth == NULL) {
fMaxContentWidth = fLS->AddConstraint(-1.0, fLeft, 1.0, fRight, kLE,
max.Width(), priority, priority);
} else {
fMaxContentWidth->SetRightSide(max.Width());
fMaxContentWidth->SetPenaltyNeg(priority);
fMaxContentWidth->SetPenaltyPos(priority);
}
} else {
delete fMaxContentWidth;
fMaxContentWidth = NULL;
}
}