Prefer const references for Pin/Channel maps
[ardour.git] / libs / canvas / grid.cc
index b3f8a031166ed97b42a369ac471d30fef94b53df..e46a184e42ce19dd3a5d6e4be29da8a849a36960 100644 (file)
 using namespace ArdourCanvas;
 using std::vector;
 using std::max;
+using std::cerr;
+using std::endl;
 
 Grid::Grid (Canvas* canvas)
        : Item (canvas)
-       , spacing (0)
+       , row_spacing (0)
+       , col_spacing (0)
        , top_padding (0), right_padding (0), bottom_padding (0), left_padding (0)
        , top_margin (0), right_margin (0), bottom_margin (0), left_margin (0)
        , homogenous (false)
 {
-       self = new Rectangle (this);
-       self->set_outline (false);
-       self->set_fill (false);
+       bg = new Rectangle (this);
+       bg->set_outline (false);
+       bg->set_fill (false);
+       bg->hide ();
 }
 
 Grid::Grid (Item* parent)
        : Item (parent)
-       , spacing (0)
+       , row_spacing (0)
+       , col_spacing (0)
        , top_padding (0), right_padding (0), bottom_padding (0), left_padding (0)
        , top_margin (0), right_margin (0), bottom_margin (0), left_margin (0)
        , homogenous (false)
 {
-       self = new Rectangle (this);
-       self->set_outline (false);
-       self->set_fill (false);
+       bg = new Rectangle (this);
+       bg->set_outline (false);
+       bg->set_fill (false);
+       bg->hide ();
 }
 
 Grid::Grid (Item* parent, Duple const & p)
        : Item (parent, p)
-       , spacing (0)
+       , row_spacing (0)
+       , col_spacing (0)
        , top_padding (0), right_padding (0), bottom_padding (0), left_padding (0)
        , top_margin (0), right_margin (0), bottom_margin (0), left_margin (0)
-       , homogenous (false)
+       , homogenous (true)
+{
+       bg = new Rectangle (this);
+       bg->set_outline (false);
+       bg->set_fill (false);
+       bg->hide ();
+}
+
+void
+Grid::set_homogenous (bool yn)
 {
-       self = new Rectangle (this);
-       self->set_outline (false);
-       self->set_fill (false);
+       homogenous = yn;
 }
 
 void
@@ -71,7 +85,7 @@ Grid::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
 void
 Grid::compute_bounding_box () const
 {
-       _bounding_box = boost::none;
+       _bounding_box = Rect();
 
        if (_items.empty()) {
                _bounding_box_dirty = false;
@@ -81,21 +95,27 @@ Grid::compute_bounding_box () const
        add_child_bounding_boxes (!collapse_on_hide);
 
        if (_bounding_box) {
-               Rect r = _bounding_box.get();
+               Rect r = _bounding_box;
 
-               _bounding_box = r.expand (top_padding + outline_width() + top_margin,
-                                         right_padding + outline_width() + right_margin,
-                                         bottom_padding + outline_width() + bottom_margin,
-                                         left_padding + outline_width() + left_margin);
+               _bounding_box = r.expand (outline_width() + top_margin + top_padding,
+                                         outline_width() + right_margin + right_padding,
+                                         outline_width() + bottom_margin + bottom_padding,
+                                         outline_width() + left_margin + left_padding);
        }
 
        _bounding_box_dirty = false;
 }
 
 void
-Grid::set_spacing (double s)
+Grid::set_row_spacing (double s)
 {
-       spacing = s;
+       row_spacing = s;
+}
+
+void
+Grid::set_col_spacing (double s)
+{
+       col_spacing = s;
 }
 
 void
@@ -139,22 +159,22 @@ Grid::set_margin (double t, double r, double b, double l)
 }
 
 void
-Grid::reset_self ()
+Grid::reset_bg ()
 {
        if (_bounding_box_dirty) {
                compute_bounding_box ();
        }
 
        if (!_bounding_box) {
-               self->hide ();
+               bg->hide ();
                return;
        }
 
-       Rect r (_bounding_box.get());
+       Rect r (_bounding_box);
 
        /* XXX need to shrink by margin */
 
-       self->set (r);
+       bg->set (r);
 }
 
 void
@@ -164,83 +184,172 @@ Grid::reposition_children ()
        uint32_t max_col = 0;
 
        /* since we encourage dynamic and essentially random placement of
-        * children, begin by determining the maximum row and column given
+        * children, begin by determining the maximum row and column extents given
         * our current set of children and placements.
         */
 
        for (CoordsByItem::const_iterator c = coords_by_item.begin(); c != coords_by_item.end(); ++c) {
-               max_col = max (max_col, (uint32_t) c->second.x);
-               max_row = max (max_row, (uint32_t) c->second.y);
+               if (collapse_on_hide && !c->second.item->visible()) {
+                       continue;
+               }
+               max_col = max (max_col, (uint32_t) (c->second.x + c->second.col_span));
+               max_row = max (max_row, (uint32_t) (c->second.y + c->second.row_span));
        }
 
        /* Now compute the width of the widest child for each column, and the
-        * height of the tallest child for each row.
+        * height of the tallest child for each row. Store the results in
+        * row_dimens and col_dimens, making sure they are suitably sized first.
         */
 
        vector<double> row_dimens;
        vector<double> col_dimens;
 
-       row_dimens.assign (0, max_row);
-       col_dimens.assign (0, max_col);
+       row_dimens.assign (max_row + 1, 0);
+       col_dimens.assign (max_col + 1, 0);
 
-       for (std::list<Item*>::iterator i = _items.begin(); ++i != _items.end(); ++i) {
-               boost::optional<Rect> bb = (*i)->bounding_box();
+       Rect uniform_cell_size;
 
-               if (!bb) {
-                       continue;
+       if (homogenous) {
+               for (std::list<Item*>::iterator i = _items.begin(); i != _items.end(); ++i) {
+
+                       if (*i == bg || (collapse_on_hide && !(*i)->visible())) {
+                               continue;
+                       }
+
+                       Rect bb = (*i)->bounding_box();
+
+                       if (!bb) {
+                               continue;
+                       }
+
+                       CoordsByItem::const_iterator c = coords_by_item.find (*i);
+
+                       uniform_cell_size.x1 = max (uniform_cell_size.x1, (bb.width()/c->second.col_span));
+                       uniform_cell_size.y1 = max (uniform_cell_size.y1, (bb.height()/c->second.row_span));
                }
 
-               CoordsByItem::const_iterator c = coords_by_item.find (*i);
+               for (uint32_t n = 0; n < max_col; ++n) {
+                       col_dimens[n] = uniform_cell_size.width();
+               }
+
+               for (uint32_t n = 0; n < max_row; ++n) {
+                       row_dimens[n] = uniform_cell_size.height();
+               }
 
-               row_dimens[c->second.x] = max (row_dimens[c->second.x], bb.get().width());
-               col_dimens[c->second.y] = max (col_dimens[c->second.y], bb.get().height());
+               for (std::list<Item*>::iterator i = _items.begin(); i != _items.end(); ++i) {
+
+                       if (*i == bg || (collapse_on_hide && !(*i)->visible())) {
+                               /* bg rect is not a normal child */
+                               continue;
+                       }
+
+                       CoordsByItem::const_iterator c = coords_by_item.find (*i);
+
+                       Rect r = uniform_cell_size;
+                       r.x1 *= c->second.col_span;
+                       r.y1 *= c->second.row_span;
+
+                       (*i)->size_allocate (r);
+               }
+
+       } else {
+               for (std::list<Item*>::iterator i = _items.begin(); i != _items.end(); ++i) {
+
+                       if (*i == bg || (collapse_on_hide && !(*i)->visible())) {
+                               /* bg rect is not a normal child */
+                               continue;
+                       }
+
+                       Rect bb = (*i)->bounding_box();
+
+                       if (!bb) {
+                               continue;
+                       }
+
+                       CoordsByItem::const_iterator c = coords_by_item.find (*i);
+
+                       const double per_col_width = bb.width() / c->second.col_span;
+                       const double per_row_height = bb.height() / c->second.row_span;
+
+                       /* set the width of each column spanned by this item
+                        */
+
+                       for (int n = 0; n < (int) c->second.col_span; ++n) {
+                               col_dimens[c->second.x + n] = max (col_dimens[c->second.x + n], per_col_width);
+                       }
+                       for (int n = 0; n < (int) c->second.row_span; ++n) {
+                               row_dimens[c->second.y + n] = max (row_dimens[c->second.y + n], per_row_height);
+                       }
+               }
        }
 
-       /* now sum the row and column widths, so that row_dimens is transformed
-        * into the y coordinate of the upper left of each row, and col_dimens
-        * is transformed into the x coordinate of the left edge of each
-        * column.
+       /* now progressively sum the row and column widths, once we're done:
+        *
+        * col_dimens: transformed into the x coordinate of the left edge of each column.
+        *
+        * row_dimens: transformed into the y coordinate of the upper left of each row,
+        *
         */
 
-       double prev = row_dimens[0];
-       row_dimens[0] = 0;
+       double current_right_edge = left_margin + left_padding;
 
-       for (uint32_t n = 1; n < max_row; ++n) {
-               row_dimens[n] = row_dimens[n-1] + prev;
-               prev = row_dimens[n];
+       for (uint32_t n = 0; n < max_col; ++n) {
+               if (col_dimens[n]) {
+                       /* a width was defined for this column */
+                       const double w = col_dimens[n]; /* save width of this column */
+                       col_dimens[n] = current_right_edge;
+                       current_right_edge = current_right_edge + w + col_spacing;
+               }
        }
 
-       prev = col_dimens[0];
-       col_dimens[0] = 0;
+       double current_top_edge = top_margin + top_padding;
 
-       for (uint32_t n = 1; n < max_col; ++n) {
-               col_dimens[n] = col_dimens[n-1] + prev;
-               prev = col_dimens[n];
+       for (uint32_t n = 0; n < max_row; ++n) {
+               if (row_dimens[n]) {
+                       /* height defined for this row */
+                       const double h = row_dimens[n]; /* save height */
+                       row_dimens[n] = current_top_edge;
+                       current_top_edge = current_top_edge + h + row_spacing;
+               }
        }
 
        /* position each item at the upper left of its (row, col) coordinate,
         * given the width of all rows or columns before it.
         */
 
-       for (std::list<Item*>::iterator i = _items.begin(); ++i != _items.end(); ++i) {
+       for (std::list<Item*>::iterator i = _items.begin(); i != _items.end(); ++i) {
                CoordsByItem::const_iterator c = coords_by_item.find (*i);
 
                if (c == coords_by_item.end()) {
                        continue;
                }
 
+               /* do this even for hidden items - it will be corrected when
+                * they become visible again.
+                */
+
                (*i)->set_position (Duple (col_dimens[c->second.x], row_dimens[c->second.y]));
        }
 
        _bounding_box_dirty = true;
-       reset_self ();
+       reset_bg ();
 }
 
 void
-Grid::place (Item* i, Duple at)
+Grid::place (Item* i, double x, double y, double col_span, double row_span)
 {
+       ChildInfo ci;
+
        add (i);
-       coords_by_item.insert (std::make_pair (i, at));
+
+       ci.item = i;
+       ci.x = x;
+       ci.y = y;
+       ci.col_span = max (1.0, col_span);
+       ci.row_span = max (1.0, row_span);
+
+       coords_by_item.insert (std::make_pair (i, ci));
+       reposition_children ();
 }
 
 void