Primary-modifier click on a node in the port matrix toggles association for everythin...
[ardour.git] / gtk2_ardour / port_matrix_row_labels.cc
1 /*
2     Copyright (C) 2002-2009 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <iostream>
21 #include <boost/weak_ptr.hpp>
22 #include <cairo/cairo.h>
23 #include "ardour/bundle.h"
24 #include "port_matrix_row_labels.h"
25 #include "port_matrix.h"
26 #include "port_matrix_body.h"
27 #include "i18n.h"
28 #include "utils.h"
29
30 using namespace std;
31
32 PortMatrixRowLabels::PortMatrixRowLabels (PortMatrix* m, PortMatrixBody* b)
33         : PortMatrixLabels (m, b)
34 {
35
36 }
37
38 void
39 PortMatrixRowLabels::compute_dimensions ()
40 {
41         GdkPixmap* pm = gdk_pixmap_new (NULL, 1, 1, 24);
42         gdk_drawable_set_colormap (pm, gdk_colormap_get_system());
43         cairo_t* cr = gdk_cairo_create (pm);
44
45         _longest_port_name = 0;
46         _longest_bundle_name = 0;
47         _height = 0;
48
49         /* Compute maximum dimensions using all port groups, so that we allow for the largest and hence
50            we can change between visible groups without the size of the labels jumping around.
51         */
52
53         for (PortGroupList::List::const_iterator i = _matrix->rows()->begin(); i != _matrix->rows()->end(); ++i) {
54
55                 PortGroup::BundleList const r = (*i)->bundles ();
56                 for (PortGroup::BundleList::const_iterator j = r.begin(); j != r.end(); ++j) {
57
58                         for (uint32_t k = 0; k < j->bundle->nchannels(); ++k) {
59                                 cairo_text_extents_t ext;
60                                 cairo_text_extents (cr, j->bundle->channel_name(k).c_str(), &ext);
61                                 if (ext.width > _longest_port_name) {
62                                         _longest_port_name = ext.width;
63                                 }
64                         }
65
66                         cairo_text_extents_t ext;
67                         cairo_text_extents (cr, j->bundle->name().c_str(), &ext);
68                         if (ext.width > _longest_bundle_name) {
69                                 _longest_bundle_name = ext.width;
70                         }
71                 }
72         }
73
74         
75         _height += group_size (_matrix->visible_rows()) * grid_spacing ();
76
77         cairo_destroy (cr);
78         gdk_pixmap_unref (pm);
79
80         _width = _longest_bundle_name +
81                 name_pad() * 2;
82
83         if (!_matrix->show_only_bundles()) {
84                 _width += _longest_port_name;
85                 _width += name_pad() * 2;
86         }
87 }
88
89
90 void
91 PortMatrixRowLabels::render (cairo_t* cr)
92 {
93         /* BACKGROUND */
94
95         set_source_rgb (cr, background_colour());
96         cairo_rectangle (cr, 0, 0, _width, _height);
97         cairo_fill (cr);
98
99         /* BUNDLE AND PORT NAMES */
100
101         double y = 0;
102         int N = 0;
103         int M = 0;
104         
105         PortGroup::BundleList const & bundles = _matrix->visible_rows()->bundles ();
106         for (PortGroup::BundleList::const_iterator i = bundles.begin(); i != bundles.end(); ++i) {
107                 render_bundle_name (cr, background_colour (), i->has_colour ? i->colour : get_a_bundle_colour (N), 0, y, i->bundle);
108
109                 if (!_matrix->show_only_bundles()) {
110                         for (uint32_t j = 0; j < i->bundle->nchannels(); ++j) {
111                                 Gdk::Color c = i->has_colour ? i->colour : get_a_bundle_colour (M);
112                                 render_channel_name (cr, background_colour (), c, 0, y, ARDOUR::BundleChannel (i->bundle, j));
113                                 y += grid_spacing();
114                                 ++M;
115                         }
116                 } else {
117                         y += grid_spacing();
118                 }
119                 
120                 ++N;
121         }
122 }
123
124 void
125 PortMatrixRowLabels::button_press (double x, double y, int b, uint32_t t, guint)
126 {
127         ARDOUR::BundleChannel const w = position_to_channel (y, x, _matrix->visible_rows());
128
129         if (b == 3) {
130
131                 _matrix->popup_menu (
132                         ARDOUR::BundleChannel (),
133                         w,
134                         t
135                         );
136         }
137 }
138
139 double
140 PortMatrixRowLabels::component_to_parent_x (double x) const
141 {
142         /* Row labels don't scroll horizontally, so x conversion does not depend on xoffset */
143         return x + _parent_rectangle.get_x();
144 }
145
146 double
147 PortMatrixRowLabels::parent_to_component_x (double x) const
148 {
149         /* Row labels don't scroll horizontally, so x conversion does not depend on xoffset */
150         return x - _parent_rectangle.get_x();
151 }
152
153 double
154 PortMatrixRowLabels::component_to_parent_y (double y) const
155 {
156         return y - _body->yoffset() + _parent_rectangle.get_y();
157 }
158
159 double
160 PortMatrixRowLabels::parent_to_component_y (double y) const
161 {
162         return y + _body->yoffset() - _parent_rectangle.get_y();
163 }
164
165
166 double
167 PortMatrixRowLabels::bundle_name_x () const
168 {
169         double x = 0;
170
171         if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && !_matrix->show_only_bundles ()) {
172                 x = _longest_port_name + name_pad() * 2;
173         }
174
175         return x;
176 }
177
178 double
179 PortMatrixRowLabels::port_name_x () const
180 {
181         if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
182                 return _longest_bundle_name + name_pad() * 2;
183         } else {
184                 return 0;
185         }
186
187         return 0;
188 }
189
190 void
191 PortMatrixRowLabels::render_bundle_name (
192         cairo_t* cr, Gdk::Color fg_colour, Gdk::Color bg_colour, double xoff, double yoff, boost::shared_ptr<ARDOUR::Bundle> b
193         )
194 {
195         double const x = bundle_name_x ();
196
197         int const n = _matrix->show_only_bundles() ? 1 : b->nchannels();
198         set_source_rgb (cr, bg_colour);
199         cairo_rectangle (cr, xoff + x, yoff, _longest_bundle_name + name_pad() * 2, grid_spacing() * n);
200         cairo_fill_preserve (cr);
201         set_source_rgb (cr, fg_colour);
202         cairo_set_line_width (cr, label_border_width ());
203         cairo_stroke (cr);
204
205         cairo_text_extents_t ext;
206         cairo_text_extents (cr, b->name().c_str(), &ext);
207         double const off = (grid_spacing() - ext.height) / 2;
208
209         set_source_rgb (cr, text_colour());
210         cairo_move_to (cr, xoff + x + name_pad(), yoff + name_pad() + off);
211         cairo_show_text (cr, b->name().c_str());
212 }
213
214 void
215 PortMatrixRowLabels::render_channel_name (
216         cairo_t* cr, Gdk::Color fg_colour, Gdk::Color bg_colour, double xoff, double yoff, ARDOUR::BundleChannel const& bc
217         )
218 {
219         set_source_rgb (cr, bg_colour);
220         cairo_rectangle (cr, port_name_x() + xoff, yoff, _longest_port_name + name_pad() * 2, grid_spacing());
221         cairo_fill_preserve (cr);
222         set_source_rgb (cr, fg_colour);
223         cairo_set_line_width (cr, label_border_width ());
224         cairo_stroke (cr);
225
226         cairo_text_extents_t ext;
227         cairo_text_extents (cr, bc.bundle->channel_name(bc.channel).c_str(), &ext);
228         double const off = (grid_spacing() - ext.height) / 2;
229
230         if (bc.bundle->nchannels() > 1) {
231
232                 /* only plot the name if the bundle has more than one channel;
233                    the name of a single channel is assumed to be redundant */
234                 
235                 set_source_rgb (cr, text_colour());
236                 cairo_move_to (cr, port_name_x() + xoff + name_pad(), yoff + name_pad() + off);
237                 cairo_show_text (cr, bc.bundle->channel_name(bc.channel).c_str());
238         }
239 }
240
241 double
242 PortMatrixRowLabels::channel_x (ARDOUR::BundleChannel const &) const
243 {
244         return 0;
245 }
246
247 double
248 PortMatrixRowLabels::channel_y (ARDOUR::BundleChannel const& bc) const
249 {
250         return channel_to_position (bc, _matrix->visible_rows()) * grid_spacing ();
251 }
252
253 void
254 PortMatrixRowLabels::queue_draw_for (ARDOUR::BundleChannel const & bc)
255 {
256         if (bc.bundle) {
257
258                 if (_matrix->show_only_bundles()) {
259                         _body->queue_draw_area (
260                                 component_to_parent_x (bundle_name_x()) - 1,
261                                 component_to_parent_y (channel_y (bc)) - 1,
262                                 _longest_bundle_name + name_pad() * 2 + 2,
263                                 grid_spacing() + 2
264                                 );
265                 } else {
266                         _body->queue_draw_area (
267                                 component_to_parent_x (port_name_x()) - 1,
268                                 component_to_parent_y (channel_y (bc)) - 1,
269                                 _longest_port_name + name_pad() * 2 + 2,
270                                 grid_spacing() + 2
271                                 );
272                 }
273         }
274
275 }
276
277 void
278 PortMatrixRowLabels::mouseover_changed (list<PortMatrixNode> const &)
279 {
280         list<PortMatrixNode> const m = _body->mouseover ();
281         for (list<PortMatrixNode>::const_iterator i = m.begin(); i != m.end(); ++i) {
282                 
283                 ARDOUR::BundleChannel c = i->column;
284                 ARDOUR::BundleChannel r = i->row;
285                 
286                 if (c.bundle && r.bundle) {
287                         add_channel_highlight (r);
288                 } else if (r.bundle) {
289                         _body->highlight_associated_channels (_matrix->row_index(), r);
290                 }
291         }
292 }
293
294 void
295 PortMatrixRowLabels::motion (double x, double y)
296 {
297         ARDOUR::BundleChannel const w = position_to_channel (y, x, _matrix->visible_rows());
298
299         if (w.bundle == 0) {
300                 /* not over any bundle */
301                 _body->set_mouseover (PortMatrixNode ());
302                 return;
303         }
304
305         uint32_t const bw = _longest_bundle_name + 2 * name_pad();
306
307         if (
308                 (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && x < bw) ||
309                 (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && x > (_width - bw))
310                  
311                         ) {
312
313                 /* if the mouse is over a bundle name, highlight all channels in the bundle */
314                 
315                 list<PortMatrixNode> n;
316
317                 for (uint32_t i = 0; i < w.bundle->nchannels(); ++i) {
318                         ARDOUR::BundleChannel const bc (w.bundle, i);
319                         n.push_back (PortMatrixNode (bc, ARDOUR::BundleChannel ()));
320                 }
321
322                 _body->set_mouseover (n);
323
324         } else {
325         
326                 _body->set_mouseover (PortMatrixNode (w, ARDOUR::BundleChannel ()));
327
328         }
329 }