enough with umpteen "i18n.h" files. Consolidate on pbd/i18n.h
[ardour.git] / gtk2_ardour / port_matrix_column_labels.cc
index fa3d3dd0307bfd78fb07e0986764d62152cbbe0e..4cf12b58b3fce1c89f4d7fc3ccc6ca1825c467ee 100644 (file)
 */
 
 #include <iostream>
+#include "gtkmm2ext/keyboard.h"
 #include "ardour/bundle.h"
-#include "ardour/types.h"
+#include "canvas/colors.h"
+#include "utils.h"
 #include "port_matrix_column_labels.h"
 #include "port_matrix.h"
 #include "port_matrix_body.h"
-#include "utils.h"
+
+#include "pbd/i18n.h"
 
 using namespace std;
 
@@ -37,63 +40,62 @@ PortMatrixColumnLabels::PortMatrixColumnLabels (PortMatrix* m, PortMatrixBody* b
 void
 PortMatrixColumnLabels::compute_dimensions ()
 {
-       GdkPixmap* pm = gdk_pixmap_new (NULL, 1, 1, 24);
-       gdk_drawable_set_colormap (pm, gdk_colormap_get_system());
-       cairo_t* cr = gdk_cairo_create (pm);
+       cairo_surface_t* surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 200, 200);
+       cairo_t* cr = cairo_create (surface);
 
        /* width of the longest bundle name */
        _longest_bundle_name = 0;
        /* width of the longest channel name */
        _longest_channel_name = 0;
-       /* height of highest bit of text (apart from group names) */
-       _highest_text = 0;
-       /* width of the whole thing */
-       _width = 0;
-       _highest_group_name = 0;
+
+       /* Compute dimensions using all port groups, so that we allow for the largest and hence
+          we can change between visible groups without the size of the labels jumping around.
+       */
 
        for (PortGroupList::List::const_iterator i = _matrix->columns()->begin(); i != _matrix->columns()->end(); ++i) {
                PortGroup::BundleList const c = _matrix->columns()->bundles();
                for (PortGroup::BundleList::const_iterator j = c.begin (); j != c.end(); ++j) {
 
                        cairo_text_extents_t ext;
-                       cairo_text_extents (cr, j->bundle->name().c_str(), &ext);
+                       cairo_text_extents (cr, (*j)->bundle->name().c_str(), &ext);
                        if (ext.width > _longest_bundle_name) {
                                _longest_bundle_name = ext.width;
                        }
 
-                       if (ext.height > _highest_text) {
-                               _highest_text = ext.height;
-                       }
+                       for (uint32_t k = 0; k < (*j)->bundle->nchannels().n_total(); ++k) {
 
-                       for (uint32_t k = 0; k < j->bundle->nchannels (); ++k) {
+                               if (!_matrix->should_show ((*j)->bundle->channel_type(k))) {
+                                       continue;
+                               }
 
                                cairo_text_extents (
                                        cr,
-                                       j->bundle->channel_name (k).c_str(),
+                                       (*j)->bundle->channel_name (k).c_str(),
                                        &ext
                                        );
 
                                if (ext.width > _longest_channel_name) {
                                        _longest_channel_name = ext.width;
                                }
-
-                               if (ext.height > _highest_text) {
-                                       _highest_text = ext.height;
-                               }
                        }
                }
+       }
 
-               _width += group_size (*i) * grid_spacing ();
+       /* height metrics */
+       cairo_text_extents_t ext;
+       cairo_text_extents (cr, X_("AQRjpy"), &ext);
+       _text_height = ext.height;
+       _descender_height = ext.height + ext.y_bearing;
 
-               cairo_text_extents_t ext;
-               cairo_text_extents (cr, (*i)->name.c_str(), &ext);
-               if (ext.height > _highest_group_name) {
-                       _highest_group_name = ext.height;
-               }
+       /* width of the whole thing */
+       if (_matrix->visible_columns()) {
+               _width = group_size (_matrix->visible_columns()) * grid_spacing ();
+       } else {
+               _width = 0;
        }
 
        cairo_destroy (cr);
-       gdk_pixmap_unref (pm);
+       cairo_surface_destroy (surface);
 
        /* height of the whole thing */
 
@@ -102,11 +104,8 @@ PortMatrixColumnLabels::compute_dimensions ()
                a += _longest_channel_name;
        }
 
-       double const parallelogram_height =  a * sin (angle()) + _highest_text * cos (angle());
-
-       _height = parallelogram_height + _highest_group_name + 2 * name_pad();
-
-       _overhang = parallelogram_height / tan (angle ());
+       _height =  a * sin (angle()) + _text_height * cos (angle());
+       _overhang = _height / tan (angle ());
        _width += _overhang;
 }
 
@@ -114,7 +113,7 @@ double
 PortMatrixColumnLabels::basic_text_x_pos (int) const
 {
        return grid_spacing() / 2 +
-               _highest_text / (2 * sin (angle ()));
+               _text_height / (2 * sin (angle ()));
 }
 
 void
@@ -126,119 +125,53 @@ PortMatrixColumnLabels::render (cairo_t* cr)
        cairo_rectangle (cr, 0, 0, _width, _height);
        cairo_fill (cr);
 
-       /* PORT GROUP NAME */
-
-       double x = 0;
-       double y = 0;
-
-       if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
-               x = slanted_height() / tan (angle());
-               y = _highest_group_name + name_pad();
-       } else {
-               x = 0;
-               y = _height - name_pad();
-       }
-
-       int g = 0;
-       for (PortGroupList::List::const_iterator i = _matrix->columns()->begin(); i != _matrix->columns()->end(); ++i) {
-
-               /* compute width of this group */
-               uint32_t w = 0;
-               if (!(*i)->visible()) {
-                       w = grid_spacing ();
-               } else {
-                       if (_matrix->show_only_bundles()) {
-                               w = (*i)->bundles().size() * grid_spacing();
-                       } else {
-                               w = (*i)->total_channels() * grid_spacing();
-                       }
-               }
-
-               if (w == 0) {
-                       continue;
-               }
-
-               /* rectangle */
-               set_source_rgb (cr, get_a_group_colour (g));
-               double const rh = _highest_group_name + 2 * name_pad();
-               if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
-                       cairo_rectangle (cr, x, 0, w, rh);
-               } else {
-                       cairo_rectangle (cr, x, _height - rh, w, rh);
-               }
-               cairo_fill (cr);
-
-               string const upper = Glib::ustring ((*i)->name).uppercase ();
-               pair<string, double> const display = fit_to_pixels (cr, upper, w);
-
-               /* plot it */
-               set_source_rgb (cr, text_colour());
-               cairo_move_to (cr, x + (w - display.second) / 2, y);
-               cairo_show_text (cr, display.first.c_str());
-
-               x += w;
-               ++g;
-
-       }
-
         /* BUNDLE PARALLELOGRAM-TYPE-THING AND NAME */
 
-       x = 0;
+       double x = 0;
        int N = 0;
 
-       for (PortGroupList::List::const_iterator i = _matrix->columns()->begin(); i != _matrix->columns()->end(); ++i) {
-
-               if ((*i)->visible ()) {
+       PortGroup::BundleList const & bundles = _matrix->visible_columns()->bundles ();
+       for (PortGroup::BundleList::const_iterator i = bundles.begin (); i != bundles.end(); ++i) {
 
-                       PortGroup::BundleList const & bundles = (*i)->bundles ();
-                       for (PortGroup::BundleList::const_iterator j = bundles.begin (); j != bundles.end(); ++j) {
-
-                               Gdk::Color c = j->has_colour ? j->colour : get_a_bundle_colour (N);
-                               render_bundle_name (cr, background_colour (), c, x, 0, j->bundle);
-
-                               if (_matrix->show_only_bundles()) {
-                                       x += grid_spacing();
-                               } else {
-                                       x += j->bundle->nchannels () * grid_spacing();
-                               }
-
-                               ++N;
-                       }
+               Gdk::Color c = (*i)->has_colour ? (*i)->colour : get_a_bundle_colour (N);
+               render_bundle_name (cr, background_colour (), c, x, 0, (*i)->bundle);
 
+               if (_matrix->show_only_bundles()) {
+                       x += grid_spacing();
                } else {
-
-                       x += grid_spacing ();
-
+                       x += _matrix->count_of_our_type_min_1 ((*i)->bundle->nchannels()) * grid_spacing();
                }
-       }
 
+               ++N;
+       }
 
        /* PORT NAMES */
 
        if (!_matrix->show_only_bundles()) {
                x = 0;
                N = 0;
-               for (PortGroupList::List::const_iterator i = _matrix->columns()->begin(); i != _matrix->columns()->end(); ++i) {
 
-                       if ((*i)->visible ()) {
+               for (PortGroup::BundleList::const_iterator i = bundles.begin (); i != bundles.end(); ++i) {
 
-                               PortGroup::BundleList const & bundles = (*i)->bundles ();
-                               for (PortGroup::BundleList::const_iterator j = bundles.begin (); j != bundles.end(); ++j) {
+                       uint32_t const C = _matrix->count_of_our_type ((*i)->bundle->nchannels ());
 
-                                       for (uint32_t k = 0; k < j->bundle->nchannels(); ++k) {
-                                               Gdk::Color c = j->has_colour ? j->colour : get_a_bundle_colour (N);
-                                               render_channel_name (cr, background_colour (), c, x, 0, ARDOUR::BundleChannel (j->bundle, k));
-                                               x += grid_spacing();
-                                       }
+                       for (uint32_t j = 0; j < C; ++j) {
+                               Gdk::Color c = (*i)->has_colour ? (*i)->colour : get_a_bundle_colour (N);
 
-                                       ++N;
-                               }
+                               ARDOUR::BundleChannel bc (
+                                       (*i)->bundle,
+                                       (*i)->bundle->type_channel_to_overall (_matrix->type (), j)
+                                       );
 
-                       } else {
+                               render_channel_name (cr, background_colour (), c, x, 0, bc);
+                               x += grid_spacing();
+                       }
 
+                       if (C == 0) {
                                x += grid_spacing ();
-
                        }
+
+                       ++N;
                }
        }
 }
@@ -258,21 +191,31 @@ PortMatrixColumnLabels::parent_to_component_x (double x) const
 double
 PortMatrixColumnLabels::component_to_parent_y (double y) const
 {
+       /* Column labels don't scroll vertically, so y conversion does not depend on yoffset */
        return y + _parent_rectangle.get_y();
 }
 
 double
 PortMatrixColumnLabels::parent_to_component_y (double y) const
 {
+       /* Column labels don't scroll vertically, so y conversion does not depend on yoffset */
        return y - _parent_rectangle.get_y();
 }
 
 void
-PortMatrixColumnLabels::mouseover_changed (PortMatrixNode const &)
+PortMatrixColumnLabels::mouseover_changed (list<PortMatrixNode> const &)
 {
-       clear_channel_highlights ();
-       if (_body->mouseover().column.bundle && _body->mouseover().row.bundle) {
-               add_channel_highlight (_body->mouseover().column);
+       list<PortMatrixNode> const m = _body->mouseover ();
+       for (list<PortMatrixNode>::const_iterator i = m.begin(); i != m.end(); ++i) {
+
+               ARDOUR::BundleChannel c = i->column;
+               ARDOUR::BundleChannel r = i->row;
+
+               if (PortMatrix::bundle_with_channels (c.bundle) && PortMatrix::bundle_with_channels (r.bundle)) {
+                       add_channel_highlight (c);
+               } else if (c.bundle) {
+                       _body->highlight_associated_channels (_matrix->column_index(), c);
+               }
        }
 }
 
@@ -286,7 +229,7 @@ PortMatrixColumnLabels::port_name_shape (double xoff, double yoff) const
 
        if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
 
-               double x_ = xoff + slanted_height() / tan (angle()) + w;
+               double x_ = xoff + _height / tan (angle()) + w;
                double y_ = yoff;
                shape.push_back (make_pair (x_, y_));
                x_ -= w;
@@ -327,24 +270,20 @@ PortMatrixColumnLabels::render_bundle_name (
        if (_matrix->show_only_bundles()) {
                w = grid_spacing ();
        } else {
-               w = b->nchannels() * grid_spacing();
+               w = _matrix->count_of_our_type_min_1 (b->nchannels()) * grid_spacing();
        }
 
        double x_ = xoff;
 
        uint32_t y = yoff;
-       if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
-               y += _height;
-       } else {
-               y += slanted_height();
-       }
+       y += _height;
 
        double y_ = y;
        cairo_move_to (cr, x_, y_);
        x_ += w;
        cairo_line_to (cr, x_, y_);
-       x_ += slanted_height() / tan (angle ());
-       y_ -= slanted_height();
+       x_ += _height / tan (angle ());
+       y_ -= _height;
        cairo_line_to (cr, x_, y_);
        x_ -= w;
        cairo_line_to (cr, x_, y_);
@@ -354,7 +293,11 @@ PortMatrixColumnLabels::render_bundle_name (
        cairo_set_line_width (cr, label_border_width());
        cairo_stroke (cr);
 
-       set_source_rgb (cr, text_colour());
+       Gdk::Color textcolor;
+       ARDOUR_UI_UTILS::set_color_from_rgba(textcolor, ArdourCanvas::contrasting_text_color(ARDOUR_UI_UTILS::gdk_color_to_rgba(bg_colour)));
+       set_source_rgb (cr, textcolor);
+
+       double const q = ((grid_spacing() * sin (angle())) - _text_height) / 2 + _descender_height;
 
        if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
 
@@ -366,16 +309,16 @@ PortMatrixColumnLabels::render_bundle_name (
                }
                cairo_move_to (
                        cr,
-                       xoff + basic_text_x_pos (0) + rl * cos (angle()),
-                       yoff + _height - rl * sin (angle())
+                       xoff + grid_spacing() - q * sin (angle ()) + rl * cos (angle()),
+                       yoff + _height - q * cos (angle ()) - rl * sin (angle())
                        );
 
        } else {
 
                cairo_move_to (
                        cr,
-                       xoff + basic_text_x_pos (0),
-                       yoff + slanted_height() - name_pad() * sin (angle())
+                       xoff + grid_spacing() - q * sin (angle ()),
+                       yoff + _height - q * cos (angle ())
                        );
        }
 
@@ -404,41 +347,52 @@ PortMatrixColumnLabels::render_channel_name (
        cairo_set_line_width (cr, label_border_width());
        cairo_stroke (cr);
 
-       set_source_rgb (cr, text_colour());
+       Gdk::Color textcolor;
+       ARDOUR_UI_UTILS::set_color_from_rgba(textcolor, ArdourCanvas::contrasting_text_color(ARDOUR_UI_UTILS::gdk_color_to_rgba(bg_colour)));
+       set_source_rgb (cr, textcolor);
+
+       double const q = ((grid_spacing() * sin (angle())) - _text_height) / 2 + _descender_height;
 
        if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
 
                cairo_move_to (
                        cr,
-                       xoff + basic_text_x_pos(bc.channel),
-                       yoff + _height - name_pad() * sin (angle())
+                       xoff + grid_spacing() - q * sin (angle ()),
+                       yoff + _height - q * cos (angle ())
                        );
 
+
        } else {
 
                double const rl = 3 * name_pad() + _longest_bundle_name;
                cairo_move_to (
                        cr,
-                       xoff + basic_text_x_pos(bc.channel) + rl * cos (angle ()),
-                       yoff + slanted_height() - rl * sin (angle())
+                       xoff + grid_spacing() - q * sin (angle ()) + rl * cos (angle ()),
+                       yoff + _height - q * cos (angle ()) - rl * sin (angle())
                        );
        }
 
-       cairo_save (cr);
-       cairo_rotate (cr, -angle());
+       if (_matrix->count_of_our_type (bc.bundle->nchannels()) > 1) {
 
-       cairo_show_text (
-               cr,
-               bc.bundle->channel_name(bc.channel).c_str()
-               );
+               /* only plot the name if the bundle has more than one channel;
+                  the name of a single channel is assumed to be redundant */
 
-       cairo_restore (cr);
+               cairo_save (cr);
+               cairo_rotate (cr, -angle());
+
+               cairo_show_text (
+                       cr,
+                       bc.bundle->channel_name(bc.channel).c_str()
+                       );
+
+               cairo_restore (cr);
+       }
 }
 
 double
 PortMatrixColumnLabels::channel_x (ARDOUR::BundleChannel const &bc) const
 {
-       return channel_to_position (bc, _matrix->columns()) * grid_spacing ();
+       return channel_to_position (bc, _matrix->visible_columns()) * grid_spacing ();
 }
 
 double
@@ -480,7 +434,7 @@ PortMatrixColumnLabels::queue_draw_for (ARDOUR::BundleChannel const & bc)
 
                } else if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
 
-                       double const x_ = x + slanted_height() / tan (angle()) - lc * cos (angle());
+                       double const x_ = x + _height / tan (angle()) - lc * cos (angle());
 
                        _body->queue_draw_area (
                                component_to_parent_x (x_) - 1,
@@ -494,46 +448,71 @@ PortMatrixColumnLabels::queue_draw_for (ARDOUR::BundleChannel const & bc)
        }
 }
 
+ARDOUR::BundleChannel
+PortMatrixColumnLabels::position_to_channel (double p, double o, boost::shared_ptr<const PortGroup> group) const
+{
+       uint32_t const cx = p - (_height - o) * tan (angle ());
+       return PortMatrixComponent::position_to_channel (cx, o, group);
+}
+
 void
-PortMatrixColumnLabels::button_press (double x, double y, int b, uint32_t t)
+PortMatrixColumnLabels::button_press (double x, double y, GdkEventButton* ev)
 {
-       uint32_t cx = 0;
-       uint32_t const gh = _highest_group_name + 2 * name_pad();
+       ARDOUR::BundleChannel w = position_to_channel (x, y, _matrix->visible_columns());
 
-       bool group_name = false;
-       if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
-               if (y > (_height - gh)) {
-                       group_name = true;
-                       cx = x;
-               } else {
-                       cx = x - (_height - gh - y) * tan (angle ());
-               }
-       } else {
-               if (y < gh) {
-                       group_name = true;
-                       cx = x - _overhang;
-               } else {
-                       cx = x - (_height - y) * tan (angle ());
-               }
+       if (
+               (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && y > (_height - _longest_bundle_name * sin (angle ()))) ||
+               (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && y < (_longest_bundle_name * sin (angle ())))
+               ) {
+
+               w.channel = -1;
        }
 
-       pair<boost::shared_ptr<PortGroup>, ARDOUR::BundleChannel> gc = position_to_group_and_channel (cx / grid_spacing(), _matrix->columns());
+       if (Gtkmm2ext::Keyboard::is_delete_event (ev) && w.channel != -1) {
+               _matrix->remove_channel (w);
+       } else if (ev->button == 3) {
+               _matrix->popup_menu (
+                       w,
+                       ARDOUR::BundleChannel (),
+                       ev->time
+                       );
+       }
+}
 
-       if (b == 1) {
+void
+PortMatrixColumnLabels::motion (double x, double y)
+{
+       ARDOUR::BundleChannel const w = position_to_channel (x, y, _matrix->visible_columns());
 
-               if (group_name && gc.first) {
-                       gc.first->set_visible (!gc.first->visible ());
-               } else if (gc.second.bundle) {
-                       _body->highlight_associated_channels (_matrix->column_index(), gc.second);
+       if (w.bundle == 0) {
+               _body->set_mouseover (PortMatrixNode ());
+               return;
+       }
+
+       uint32_t const bh = _longest_channel_name * sin (angle ()) + _text_height / cos (angle ());
+
+       if (
+               (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && y > bh) ||
+               (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && y < (_height - bh))
+               ) {
+
+               /* if the mouse is over a bundle name, highlight all channels in the bundle */
+
+               list<PortMatrixNode> n;
+
+               for (uint32_t i = 0; i < w.bundle->nchannels().n_total(); ++i) {
+                       if (!_matrix->should_show (w.bundle->channel_type (i))) {
+                               continue;
+                       }
+
+                       ARDOUR::BundleChannel const bc (w.bundle, i);
+                       n.push_back (PortMatrixNode (ARDOUR::BundleChannel (), bc));
                }
 
-       } else if (b == 3) {
+               _body->set_mouseover (n);
 
-                       _matrix->popup_menu (
-                               gc,
-                               make_pair (boost::shared_ptr<PortGroup> (), ARDOUR::BundleChannel ()),
-                               t
-                               );
+       } else {
+
+               _body->set_mouseover (PortMatrixNode (ARDOUR::BundleChannel (), w));
        }
 }
-