-#include <algorithm>
#include <limits>
+#include <msp/core/algorithm.h>
#include <msp/core/maputils.h>
#include <msp/strings/format.h>
#include "arrangement.h"
Column();
};
- unsigned n_columns;
- unsigned n_rows;
+ unsigned n_columns = 1;
+ unsigned n_rows = 1;
std::vector<Column> columns;
- bool solved;
- bool infeasible;
+ bool solved = false;
+ bool infeasible = false;
public:
LinearProgram(unsigned);
} };
-Layout::Layout():
- container(0),
- n_active_slots(0),
- margin(8),
- row_spacing(5),
- col_spacing(4)
-{
- n_slack_vars[0] = 0;
- n_slack_vars[1] = 0;
-}
-
Layout::~Layout()
{
- for(list<Slot *>::iterator i=slots.begin(); i!=slots.end(); ++i)
- delete *i;
+ for(Slot *s: slots)
+ delete s;
}
void Layout::set_container(Container &c)
void Layout::pop_arrangement(Arrangement &arr)
{
- list<Arrangement *>::iterator begin = find(arrangement_stack.begin(), arrangement_stack.end(), &arr);
+ auto begin = find(arrangement_stack, &arr);
if(begin==arrangement_stack.end())
return;
void Layout::remove_widget(Widget &wdg)
{
- for(list<Slot *>::iterator i=slots.begin(); i!=slots.end(); ++i)
- if(&(*i)->widget==&wdg)
+ auto i = find_if(slots, [&wdg](Slot *s){ return &s->widget==&wdg; });
+ if(i==slots.end())
+ return;
+
+ for(Slot *s: slots)
+ if(s!=*i)
{
- for(list<Slot *>::iterator j=slots.begin(); j!=slots.end(); ++j)
- if(j!=i)
- {
- for(list<Constraint>::iterator k=(*j)->constraints.begin(); k!=(*j)->constraints.end(); )
- {
- if(&k->target==*i)
- (*j)->constraints.erase(k++);
- else
- ++k;
- }
- }
+ for(auto k=s->constraints.begin(); k!=s->constraints.end(); )
+ {
+ if(&k->target==*i)
+ s->constraints.erase(k++);
+ else
+ ++k;
+ }
+ }
- delete *i;
- slots.erase(i);
+ delete *i;
+ slots.erase(i);
- update_slot_indices();
- update();
- return;
- }
+ update_slot_indices();
+ update();
}
void Layout::update_slot_indices()
{
n_active_slots = 0;
unsigned n_floating = 0;
- for(list<Slot *>::iterator i=slots.begin(); i!=slots.end(); ++i)
+ for(Slot *s: slots)
{
- if((*i)->widget.is_visible() || (*i)->ghost)
+ if(s->widget.is_visible() || s->ghost)
{
- (*i)->index = n_active_slots++;
- if((*i)->floating)
+ s->index = n_active_slots++;
+ if(s->floating)
++n_floating;
}
else
- (*i)->index = -1;
+ s->index = -1;
}
n_slack_vars[0] = n_floating*2;
n_slack_vars[1] = n_floating*2;
- for(list<Slot *>::iterator i=slots.begin(); i!=slots.end(); ++i)
- if((*i)->index>=0)
+ for(const Slot *s: slots)
+ if(s->index>=0)
{
- if(!(*i)->floating)
+ if(!s->floating)
{
for(unsigned j=0; j<2; ++j)
- if(((*i)->*(pointers[j].packing)).gravity==0)
+ if((s->*(pointers[j].packing)).gravity==0)
n_slack_vars[j] += 2;
}
- for(list<Constraint>::iterator j=(*i)->constraints.begin(); j!=(*i)->constraints.end(); ++j)
- if(j->target.index>(*i)->index && (j->type&SLACK))
- ++n_slack_vars[j->type&1];
+ for(const Constraint &c: s->constraints)
+ if(c.target.index>s->index && (c.type&SLACK))
+ ++n_slack_vars[c.type&1];
}
}
Layout::Slot &Layout::get_slot_for_widget(Widget &wdg)
{
- for(list<Slot *>::iterator i=slots.begin(); i!=slots.end(); ++i)
- if(&(*i)->widget==&wdg)
- return **i;
+ auto i = find_if(slots, [&wdg](const Slot *s){ return &s->widget==&wdg; });
+ if(i==slots.end())
+ throw hierarchy_error("widget not in layout");
- throw hierarchy_error("widget not in layout");
+ return **i;
}
Layout::ConstraintType Layout::complement(ConstraintType type)
Slot &src_slot = get_slot_for_widget(src);
Slot &tgt_slot = get_slot_for_widget(tgt);
- for(list<Constraint>::iterator i=src_slot.constraints.begin(); i!=src_slot.constraints.end(); ++i)
- if(i->type==type && &i->target==&tgt_slot)
+ for(const Constraint &c: src_slot.constraints)
+ if(c.type==type && &c.target==&tgt_slot)
return;
src_slot.constraints.push_back(Constraint(type, tgt_slot));
solve_constraints(HORIZONTAL, UPDATE);
solve_constraints(VERTICAL, UPDATE);
- for(list<Slot *>::iterator i=slots.begin(); i!=slots.end(); ++i)
- (*i)->widget.set_geometry((*i)->geom);
+ for(const Slot *s: slots)
+ s->widget.set_geometry(s->geom);
}
void Layout::autosize(Geometry &geom)
columns of a widget are its position and dimension, respectively. The
remaining three are slack columns; see below for their purposes. */
LinearProgram linprog(n_active_slots*5+n_slack_vars[dir]+1);
- float weight = slots.size();
+ float weight = slots.size()+1;
unsigned k = n_active_slots*5;
- for(list<Slot *>::iterator i=slots.begin(); i!=slots.end(); ++i)
+ for(const Slot *s: slots)
{
- if((*i)->index<0)
+ if(s->index<0)
continue;
LinearProgram::Row objective = linprog.get_objective_row();
if(mode==AUTOSIZE)
{
- objective[(*i)->index*5] = -1;
- objective[(*i)->index*5+1] = -1;
+ objective[s->index*5] = -1;
+ objective[s->index*5+1] = -1;
}
else
{
- if(!(*i)->floating)
- objective[(*i)->index*5] = ((*i)->*(ptrs.packing)).gravity/weight;
- objective[(*i)->index*5+1] = (((*i)->*(ptrs.packing)).expand ? weight : -1);
+ if(!s->floating)
+ objective[s->index*5] = (s->*(ptrs.packing)).gravity/weight;
+ objective[s->index*5+1] = ((s->*(ptrs.packing)).expand ? weight : -1);
}
{
// Prevent the widget from going past the container's low edge.
LinearProgram::Row row = linprog.add_row();
- row[(*i)->index*5] = 1;
- row[(*i)->index*5+2] = -1;
+ row[s->index*5] = 1;
+ row[s->index*5+2] = -1;
row.back() = margin.*(ptrs.low_margin);
}
{
// Prevent the widget from going past the container's high edge.
LinearProgram::Row row = linprog.add_row();
- row[(*i)->index*5] = 1;
- row[(*i)->index*5+1] = 1;
- row[(*i)->index*5+3] = 1;
+ row[s->index*5] = 1;
+ row[s->index*5+1] = 1;
+ row[s->index*5+3] = 1;
row.back() = geom.*(ptrs.dim)-margin.*(ptrs.high_margin);
}
- if((*i)->floating || ((*i)->*(ptrs.packing)).gravity==0)
+ if(s->floating || (s->*(ptrs.packing)).gravity==0)
{
/* Try to keep the widget as close to a target position as possible.
Since linear programs can't express absolute values directly, use two
opposing slack variables that are optimized for a low value. */
- float a = ((*i)->*(ptrs.packing)).gravity*0.5+0.5;
+ float a = (s->*(ptrs.packing)).gravity*0.5+0.5;
LinearProgram::Row row = linprog.add_row();
- row[(*i)->index*5] = 1;
- row[(*i)->index*5+1] = a;
+ row[s->index*5] = 1;
+ row[s->index*5+1] = a;
row[k] = 1;
row[k+1] = -1;
- if((*i)->floating)
+ if(s->floating)
{
- const Geometry &cgeom = (*i)->widget.get_geometry();
+ const Geometry &cgeom = s->widget.get_geometry();
row.back() = cgeom.*(ptrs.pos)+cgeom.*(ptrs.dim)*a;
}
else
/* Don't allow the widget's dimension to get below that determined
by autosizing. */
LinearProgram::Row row = linprog.add_row();
- row[(*i)->index*5+1] = 1;
- row[(*i)->index*5+4] = -1;
- row.back() = (*i)->autosize_geom.*(ptrs.dim);
+ row[s->index*5+1] = 1;
+ row[s->index*5+4] = -1;
+ row.back() = s->autosize_geom.*(ptrs.dim);
}
/* Add rows for user-defined constraints. Constraints are always added
in pairs, so it's only necessary to create a row for one half. */
- for(list<Constraint>::iterator j=(*i)->constraints.begin(); j!=(*i)->constraints.end(); ++j)
- if(j->target.index>(*i)->index && (j->type&1)==dir)
+ for(const Constraint &c: s->constraints)
+ if(c.target.index>s->index && (c.type&1)==dir)
{
LinearProgram::Row row = linprog.add_row();
- float polarity = ((j->type&SELF_DIM) ? -1 : 1);
- if(j->type&SELF_POS)
- row[(*i)->index*5] = polarity;
- if(j->type&SELF_DIM)
- row[(*i)->index*5+1] = polarity;
- if(j->type&TARGET_POS)
- row[j->target.index*5] = -polarity;
- if(j->type&TARGET_DIM)
- row[j->target.index*5+1] = -polarity;
- if(j->type&SPACING)
- row.back() = (j->spacing>=0 ? j->spacing : this->*(ptrs.spacing));
- if(j->type&SLACK)
+ float polarity = ((c.type&SELF_DIM) ? -1 : 1);
+ float dim_weight = ((c.type&HALF_DIM) ? 0.5f : 1);
+ if(c.type&SELF_POS)
+ row[s->index*5] = polarity;
+ if(c.type&SELF_DIM)
+ row[s->index*5+1] = polarity*dim_weight;
+ if(c.type&TARGET_POS)
+ row[c.target.index*5] = -polarity;
+ if(c.type&TARGET_DIM)
+ row[c.target.index*5+1] = -polarity*dim_weight;
+ if(c.type&SPACING)
+ row.back() = (c.spacing>=0 ? c.spacing : this->*(ptrs.spacing));
+ if(c.type&SLACK)
row[k++] = -1;
}
}
if(mode==AUTOSIZE)
{
autosize_geom.*(ptrs.dim) = 0;
- for(list<Slot *>::iterator i=slots.begin(); i!=slots.end(); ++i)
- if((*i)->index>=0)
+ for(const Slot *s: slots)
+ if(s->index>=0)
{
- int high_edge = linprog.get_variable((*i)->index*5)+linprog.get_variable((*i)->index*5+1);
+ int high_edge = linprog.get_variable(s->index*5)+linprog.get_variable(s->index*5+1);
autosize_geom.*(ptrs.dim) = max(autosize_geom.*(ptrs.dim), high_edge+margin.*(ptrs.high_margin));
}
}
else
{
- for(list<Slot *>::iterator i=slots.begin(); i!=slots.end(); ++i)
- if((*i)->index>=0)
+ for(Slot *s: slots)
+ if(s->index>=0)
{
- (*i)->geom.*(ptrs.pos) = linprog.get_variable((*i)->index*5);
- (*i)->geom.*(ptrs.dim) = linprog.get_variable((*i)->index*5+1);
+ s->geom.*(ptrs.pos) = linprog.get_variable(s->index*5);
+ s->geom.*(ptrs.dim) = linprog.get_variable(s->index*5+1);
}
}
}
Layout::Constraint::Constraint(ConstraintType t, Slot &s):
type(t),
- target(s),
- spacing(-1)
-{ }
-
-
-Layout::Packing::Packing():
- gravity(-1),
- expand(false)
+ target(s)
{ }
Layout::Slot::Slot(Layout &l, Widget &w):
layout(l),
- index(0),
- widget(w),
- ghost(false),
- floating(false)
+ widget(w)
{
vert_pack.gravity = 1;
widget.signal_autosize_changed.connect(sigc::mem_fun(this, &Slot::autosize_changed));
ctype = Layout::FAR_LEFT_OF;
else if(str=="ALIGN_TOP")
ctype = Layout::ALIGN_TOP;
+ else if(str=="ALIGN_VCENTER")
+ ctype = Layout::ALIGN_VCENTER;
else if(str=="ALIGN_BOTTOM")
ctype = Layout::ALIGN_BOTTOM;
else if(str=="ALIGN_RIGHT")
ctype = Layout::ALIGN_RIGHT;
+ else if(str=="ALIGN_HCENTER")
+ ctype = Layout::ALIGN_HCENTER;
else if(str=="ALIGN_LEFT")
ctype = Layout::ALIGN_LEFT;
else if(str=="COPY_WIDTH")
Layout::LinearProgram::LinearProgram(unsigned s):
n_columns(s),
- n_rows(1),
- columns(n_columns),
- solved(false),
- infeasible(false)
+ columns(n_columns)
{ }
Layout::LinearProgram::Row Layout::LinearProgram::add_row()
vector<float> obj_coeff(n_rows, 0.0f);
vector<float> row_coeff(n_rows, 1.0f);
const vector<float> &constants = columns.back().values;
- for(vector<Column>::iterator i=columns.begin(); i!=columns.end(); ++i)
- {
- if(i->values.size()>=2 && i->values.back()!=0.0f && (constants.size()<i->values.size() || i->values.back()*constants[i->values.size()-1]>=0.0f) && obj_coeff[i->values.size()-1]==0.0f)
+ for(Column &c: columns)
+ if(c.values.size()>=2 && c.values.back()!=0.0f && (constants.size()<c.values.size() || c.values.back()*constants[c.values.size()-1]>=0.0f) && obj_coeff[c.values.size()-1]==0.0f)
{
bool basic = true;
- for(unsigned j=1; (basic && j+1<i->values.size()); ++j)
- basic = (i->values[j]==0.0f);
+ for(unsigned j=1; (basic && j+1<c.values.size()); ++j)
+ basic = (c.values[j]==0.0f);
if(basic)
{
- i->basic = i->values.size()-1;
- row_coeff[i->basic] = 1.0f/i->values.back();
- obj_coeff[i->basic] = -i->values.front();
- i->values.clear();
+ c.basic = c.values.size()-1;
+ row_coeff[c.basic] = 1.0f/c.values.back();
+ obj_coeff[c.basic] = -c.values.front();
+ c.values.clear();
}
}
- }
// Price out the newly-created basic variables.
- for(vector<Column>::iterator i=columns.begin(); i!=columns.end(); ++i)
- if(!i->values.empty())
+ for(Column &c: columns)
+ if(!c.values.empty())
{
- for(unsigned j=0; j<i->values.size(); ++j)
+ for(unsigned j=0; j<c.values.size(); ++j)
{
- i->values[j] *= row_coeff[j];
- i->values.front() += obj_coeff[j]*i->values[j];
+ c.values[j] *= row_coeff[j];
+ c.values.front() += obj_coeff[j]*c.values[j];
}
}
}
for(unsigned i=0; i<artificial_rows.size(); ++i)
artificial_rows[i] = i+1;
- for(vector<Column>::iterator i=columns.begin(); i!=columns.end(); ++i)
- if(i->basic)
- artificial_rows[i->basic-1] = 0;
+ for(const Column &c: columns)
+ if(c.basic)
+ artificial_rows[c.basic-1] = 0;
artificial_rows.erase(std::remove(artificial_rows.begin(), artificial_rows.end(), 0), artificial_rows.end());
/* Force all non-basic columns fully into existence and relocate objective
row to bottom in preparation of phase 1. A new objective row is calculated
by pricing out the constraint rows. */
- for(vector<Column>::iterator i=columns.begin(); i!=columns.end(); ++i)
- if(!i->basic)
+ for(Column &c: columns)
+ if(!c.basic)
{
float objective = 0.0f;
- if(!i->values.empty())
+ if(!c.values.empty())
{
- objective = i->values.front();
- i->values.front() = 0.0f;
- for(vector<unsigned>::iterator j=artificial_rows.begin(); j!=artificial_rows.end(); ++j)
- if(*j<i->values.size())
- i->values.front() += i->values[*j];
+ objective = c.values.front();
+ c.values.front() = 0.0f;
+ for(unsigned r: artificial_rows)
+ if(r<c.values.size())
+ c.values.front() += c.values[r];
}
- i->values.resize(n_rows+1, 0.0f);
- i->values.back() = objective;
+ c.values.resize(n_rows+1, 0.0f);
+ c.values.back() = objective;
}
if(artificial_rows.empty())
/* Get rid of the artificial variables and restore the original objective
row to form the phase 2 problem. */
columns.erase(columns.begin()+(n_columns-1), columns.end()-1);
- for(vector<Column>::iterator i=columns.begin(); i!=columns.end(); ++i)
- if(!i->basic)
+ for(Column &c: columns)
+ if(!c.basic)
{
- i->values.front() = i->values.back();
- i->values.pop_back();
+ c.values.front() = c.values.back();
+ c.values.pop_back();
}
}