11 class Layout::LinearProgram
17 LinearProgram &linprog;
21 Row(LinearProgram &, unsigned);
23 float &operator[](unsigned);
31 std::vector<float> values;
38 std::vector<Column> columns;
43 LinearProgram(unsigned);
46 Row operator[](unsigned);
49 float get_variable(unsigned);
51 unsigned find_minimal_ratio(unsigned);
52 void make_basic_column(unsigned, unsigned);
57 struct Layout::Pointers
60 unsigned Geometry::*dim;
61 Packing Slot::*packing;
62 unsigned Sides::*low_margin;
63 unsigned Sides::*high_margin;
64 unsigned Layout::*spacing;
67 Layout::Pointers Layout::pointers[2] =
69 &Geometry::x, &Geometry::w,
71 &Sides::left, &Sides::right,
74 &Geometry::y, &Geometry::h,
76 &Sides::bottom, &Sides::top,
90 for(list<Slot *>::iterator i=slots.begin(); i!=slots.end(); ++i)
94 void Layout::set_container(Container &c)
97 throw InvalidState("This layout is already assigned to a Container");
102 void Layout::set_margin(const Sides &m)
109 void Layout::add_widget(Widget &wdg)
112 throw InvalidState("Can't add Widgets without a Container");
114 Slot *slot = create_slot(wdg);
115 for(list<Constraint>::iterator i=slot->constraints.begin(); i!=slot->constraints.end(); ++i)
116 i->target.constraints.push_back(Constraint(complement(i->type), *slot));
117 slot->index = slots.size();
118 slots.push_back(slot);
123 void Layout::remove_widget(Widget &wdg)
125 for(list<Slot *>::iterator i=slots.begin(); i!=slots.end(); ++i)
126 if(&(*i)->widget==&wdg)
128 for(list<Slot *>::iterator j=slots.begin(); j!=slots.end(); ++j)
131 for(list<Constraint>::iterator k=(*j)->constraints.begin(); k!=(*j)->constraints.end(); )
134 (*j)->constraints.erase(k++);
144 for(i=slots.begin(); i!=slots.end(); ++i, ++n)
152 Layout::Slot *Layout::create_slot(Widget &wdg)
154 return new Slot(*this, wdg);
157 Layout::Slot &Layout::get_slot_for_widget(Widget &wdg)
159 for(list<Slot *>::iterator i=slots.begin(); i!=slots.end(); ++i)
160 if(&(*i)->widget==&wdg)
163 throw InvalidParameterValue("Widget is not in the Layout");
166 Layout::ConstraintType Layout::complement(ConstraintType type)
170 else if(type==LEFT_OF)
180 void Layout::add_constraint(Widget &src, ConstraintType type, Widget &tgt)
183 throw InvalidParameterValue("Can't add a self-referencing constraint");
185 Slot &src_slot = get_slot_for_widget(src);
186 Slot &tgt_slot = get_slot_for_widget(tgt);
188 for(list<Constraint>::iterator i=src_slot.constraints.begin(); i!=src_slot.constraints.end(); ++i)
189 if(i->type==type && &i->target==&tgt_slot)
192 src_slot.constraints.push_back(Constraint(type, tgt_slot));
193 tgt_slot.constraints.push_back(Constraint(complement(type), src_slot));
198 void Layout::set_gravity(Widget &wdg, int h, int v)
200 Slot &slot = get_slot_for_widget(wdg);
202 slot.horiz_pack.gravity = h;
203 slot.vert_pack.gravity = v;
208 void Layout::set_expand(Widget &wdg, bool h, bool v)
210 Slot &slot = get_slot_for_widget(wdg);
212 slot.horiz_pack.expand = h;
213 slot.vert_pack.expand = v;
218 void Layout::update()
220 for(list<Slot *>::iterator i=slots.begin(); i!=slots.end(); ++i)
222 (*i)->widget.autosize();
223 (*i)->geom = (*i)->widget.get_geometry();
226 solve_constraints(HORIZONTAL);
227 solve_constraints(VERTICAL);
229 for(list<Slot *>::iterator i=slots.begin(); i!=slots.end(); ++i)
230 (*i)->widget.set_geometry((*i)->geom);
234 void Layout::solve_constraints(int dir)
236 Pointers &ptrs = pointers[dir&VERTICAL];
238 LinearProgram linprog(slots.size()*5+1);
239 float weight = slots.size();
240 for(list<Slot *>::iterator i=slots.begin(); i!=slots.end(); ++i)
242 linprog.get_object_row()[(*i)->index*5] = ((*i)->*(ptrs.packing)).gravity/weight;
243 linprog.get_object_row()[(*i)->index*5+1] = (((*i)->*(ptrs.packing)).expand ? weight : -1);
246 LinearProgram::Row row = linprog.add_row();
247 row[(*i)->index*5] = 1;
248 row[(*i)->index*5+2] = -1;
249 row.back() = margin.*(ptrs.low_margin);
253 LinearProgram::Row row = linprog.add_row();
254 row[(*i)->index*5] = 1;
255 row[(*i)->index*5+1] = 1;
256 row[(*i)->index*5+3] = 1;
257 row.back() = container->get_geometry().*(ptrs.dim)-margin.*(ptrs.high_margin);
261 LinearProgram::Row row = linprog.add_row();
262 row[(*i)->index*5+1] = 1;
263 row[(*i)->index*5+4] = -1;
264 row.back() = (*i)->geom.*(ptrs.dim);
267 for(list<Constraint>::iterator j=(*i)->constraints.begin(); j!=(*i)->constraints.end(); ++j)
269 if((j->type&1)==dir && j->type!=BELOW && j->type!=LEFT_OF)
271 LinearProgram::Row row = linprog.add_row();
273 row[(*i)->index*5] = 1;
275 row[(*i)->index*5+1] = 1;
276 if(j->type&TARGET_POS)
277 row[j->target.index*5] = -1;
278 if(j->type&TARGET_DIM)
279 row[j->target.index*5+1] = -1;
281 row.back() = this->*(ptrs.spacing);
289 for(list<Slot *>::iterator i=slots.begin(); i!=slots.end(); ++i)
291 (*i)->geom.*(ptrs.pos) = linprog.get_variable((*i)->index*5);
292 (*i)->geom.*(ptrs.dim) = linprog.get_variable((*i)->index*5+1);
297 Layout::Constraint::Constraint(ConstraintType t, Slot &s):
303 Layout::Packing::Packing():
309 Layout::Slot::Slot(Layout &l, Widget &w):
314 vert_pack.gravity = 1;
315 widget.signal_autosize_changed.connect(sigc::mem_fun(this, &Slot::autosize_changed));
318 void Layout::Slot::autosize_changed()
324 Layout::LinearProgram::LinearProgram(unsigned s):
332 Layout::LinearProgram::Row Layout::LinearProgram::add_row()
334 return Row(*this, n_rows++);
337 Layout::LinearProgram::Row Layout::LinearProgram::operator[](unsigned r)
340 throw InvalidParameterValue("Row index out of range");
342 return Row(*this, r);
345 Layout::LinearProgram::Row Layout::LinearProgram::get_object_row()
347 return Row(*this, 0);
350 float Layout::LinearProgram::get_variable(unsigned i)
352 if(!solved || infeasible)
353 throw InvalidState("Not solved");
355 throw InvalidParameterValue("Variable index out of range");
357 unsigned r = columns[i].basic;
358 return columns.back().values[r];
361 bool Layout::LinearProgram::solve()
363 if(solved || infeasible)
366 // Force all columns fully into existence and relocate objective row to bottom
367 for(vector<Column>::iterator i=columns.begin(); i!=columns.end(); ++i)
369 float objective = i->values.front();
370 i->values.front() = 0.0f;
371 for(vector<float>::iterator j=i->values.begin(); j!=i->values.end(); ++j)
372 i->values.front() += *j;
373 i->values.resize(n_rows+1, 0.0f);
374 i->values.back() = objective;
377 // Create artificial variables for phase 1
378 columns.resize(n_columns+n_rows-1);
379 columns.back() = columns[n_columns-1];
380 columns[n_columns-1].values.clear();
381 for(unsigned i=1; i<n_rows; ++i)
383 Column &column = columns[n_columns+i-2];
387 // Solve the phase 1 problem
390 if(columns.back().values.front())
396 // Get rid of artificial columns and restore objective row
397 columns.erase(columns.begin()+(n_columns-1), columns.end()-1);
398 for(vector<Column>::iterator i=columns.begin(); i!=columns.end(); ++i)
401 i->values.front() = i->values.back();
402 i->values.pop_back();
412 unsigned Layout::LinearProgram::find_minimal_ratio(unsigned c)
414 float best = numeric_limits<float>::infinity();
416 /* Intentionally use n_rows since we need to ignore the relocated original
417 objective row in phase 1 */
418 for(unsigned i=1; i<n_rows; ++i)
419 if(columns[c].values[i]>0)
421 float ratio = columns.back().values[i]/columns[c].values[i];
432 void Layout::LinearProgram::make_basic_column(unsigned c, unsigned r)
434 for(unsigned i=0; i<columns.size(); ++i)
435 if(i!=c && (columns[i].basic==r || (!columns[i].basic && columns[i].values[r])))
439 columns[i].values.resize(columns.back().values.size(), 0.0f);
440 columns[i].values[columns[i].basic] = 1.0f;
441 columns[i].basic = 0;
444 float scale = columns[i].values[r]/columns[c].values[r];
446 for(unsigned j=0; j<columns[i].values.size(); ++j)
449 columns[i].values[j] = scale;
451 columns[i].values[j] -= scale*columns[c].values[j];
455 columns[c].basic = r;
456 columns[c].values.clear();
459 bool Layout::LinearProgram::pivot()
461 for(unsigned i=0; i+1<columns.size(); ++i)
462 if(!columns[i].basic && columns[i].values.front()>0)
463 if(unsigned row = find_minimal_ratio(i))
465 make_basic_column(i, row);
473 Layout::LinearProgram::Row::Row(LinearProgram &lp, unsigned i):
478 float &Layout::LinearProgram::Row::operator[](unsigned c)
480 if(c>=linprog.n_columns)
481 throw InvalidParameterValue("Column index out of range");
483 Column &column = linprog.columns[c];
484 if(column.values.size()<=index)
485 column.values.resize(index+1);
487 return column.values[index];
490 float &Layout::LinearProgram::Row::back()
492 return (*this)[linprog.n_columns-1];
496 Layout::LinearProgram::Column::Column():