Various tweaks to the port matrix: open to full size; remove buttons and move their...
[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         PortGroup::BundleList const r = _matrix->rows()->bundles();
50         for (PortGroup::BundleList::const_iterator i = r.begin(); i != r.end(); ++i) {
51                 for (uint32_t j = 0; j < i->bundle->nchannels(); ++j) {
52                         cairo_text_extents_t ext;
53                         cairo_text_extents (cr, i->bundle->channel_name(j).c_str(), &ext);
54                         if (ext.width > _longest_port_name) {
55                                 _longest_port_name = ext.width;
56                         }
57                 }
58
59                 cairo_text_extents_t ext;
60                 cairo_text_extents (cr, i->bundle->name().c_str(), &ext);
61                 if (ext.width > _longest_bundle_name) {
62                         _longest_bundle_name = ext.width;
63                 }
64
65                 if (_matrix->show_only_bundles()) {
66                         _height += row_height ();
67                 } else {
68                         _height += i->bundle->nchannels() * row_height();
69                 }
70         }
71
72         _highest_group_name = 0;
73         for (PortGroupList::List::const_iterator i = _matrix->rows()->begin(); i != _matrix->rows()->end(); ++i) {
74                 if ((*i)->visible()) {
75                         cairo_text_extents_t ext;
76                         cairo_text_extents (cr, (*i)->name.c_str(), &ext);
77                         if (ext.height > _highest_group_name) {
78                                 _highest_group_name = ext.height;
79                         }
80                 } else {
81                         /* add another row_height for a tab for this hidden group */
82                         _height += row_height ();
83                 }
84         }
85                         
86         cairo_destroy (cr);
87         gdk_pixmap_unref (pm);
88
89         _width = _highest_group_name +
90                 _longest_bundle_name +
91                 name_pad() * 4;
92
93         if (!_matrix->show_only_bundles()) {
94                 _width += _longest_port_name;
95                 _width += name_pad() * 2;
96         }
97 }
98
99
100 void
101 PortMatrixRowLabels::render (cairo_t* cr)
102 {
103         /* BACKGROUND */
104         
105         set_source_rgb (cr, background_colour());
106         cairo_rectangle (cr, 0, 0, _width, _height);
107         cairo_fill (cr);
108
109         /* PORT GROUP NAMES */
110
111         double x = 0;
112         if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
113                 x = 0;
114         } else {
115                 x = _width - _highest_group_name - 2 * name_pad();
116         }
117
118         double y = 0;
119         int g = 0;
120         for (PortGroupList::List::const_iterator i = _matrix->rows()->begin(); i != _matrix->rows()->end(); ++i) {
121
122                 /* compute height of this group */
123                 double h = 0;
124                 if (!(*i)->visible() || (*i)->bundles().empty()) {
125                         h = row_height ();
126                 } else {
127                         if (_matrix->show_only_bundles()) {
128                                 h = (*i)->bundles().size() * row_height();
129                         } else {
130                                 h = (*i)->total_channels () * row_height();
131                         }
132                 }
133                 
134                 /* rectangle */
135                 set_source_rgb (cr, get_a_group_colour (g));
136                 double const rw = _highest_group_name + 2 * name_pad();
137                 cairo_rectangle (cr, x, y, rw, h);
138                 cairo_fill (cr);
139                 
140                 /* hence what abbreviation (or not) we need for the group name */
141                 string const upper = Glib::ustring ((*i)->name).uppercase ();
142                 pair<string, double> display = fit_to_pixels (cr, upper, h);
143                 
144                 /* plot it */
145                 set_source_rgb (cr, text_colour());
146                 cairo_move_to (cr, x + rw - name_pad(), y + (h + display.second) / 2);
147                 cairo_save (cr);
148                 cairo_rotate (cr, - M_PI / 2);
149                 cairo_show_text (cr, display.first.c_str());
150                 cairo_restore (cr);
151                 
152                 y += h;
153                 ++g;
154         }
155
156         /* BUNDLE AND PORT NAMES */
157
158         y = 0;
159         int N = 0;
160         int M = 0;
161         for (PortGroupList::List::const_iterator i = _matrix->rows()->begin(); i != _matrix->rows()->end(); ++i) {
162
163                 if ((*i)->visible ()) {
164                         
165                         PortGroup::BundleList const & bundles = (*i)->bundles ();
166                         for (PortGroup::BundleList::const_iterator j = bundles.begin(); j != bundles.end(); ++j) {
167                                 render_bundle_name (cr, j->has_colour ? j->colour : get_a_bundle_colour (N), 0, y, j->bundle);
168
169                                 if (!_matrix->show_only_bundles()) {
170                                         for (uint32_t k = 0; k < j->bundle->nchannels(); ++k) {
171                                                 Gdk::Color c = j->has_colour ? j->colour : get_a_bundle_colour (M);
172                                                 render_channel_name (cr, c, 0, y, ARDOUR::BundleChannel (j->bundle, k));
173                                                 y += row_height();
174                                                 ++M;
175                                         }
176                                 } else {
177                                         y += row_height();
178                                 }
179                                 
180                                 ++N;
181                         }
182                         
183                 } else {
184
185                         y += row_height ();
186                 }
187         }
188 }
189
190 void
191 PortMatrixRowLabels::button_press (double x, double y, int b, uint32_t t)
192 {
193         uint32_t const gw = (_highest_group_name + 2 * name_pad());
194
195         pair<boost::shared_ptr<PortGroup>, ARDOUR::BundleChannel> w = y_position_to_group_and_channel (y);
196
197         if (
198                 (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && x < gw) ||
199                 (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && x > (_width - gw))
200                 ) {
201
202                 w.second.bundle.reset ();
203         }
204         
205         if (b == 1) {
206
207                 if (w.second.bundle) {
208                         _body->highlight_associated_channels (_matrix->row_index(), w.second);
209                 } else {
210                         if (w.first) {
211                                 w.first->set_visible (!w.first->visible());
212                         }
213                 }
214
215         } else if (b == 3) {
216
217                 if (
218                         (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && x < (_longest_port_name + name_pad() * 2)) ||
219                         (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && x > (_longest_port_name + name_pad() * 2))
220                         
221                         ) {
222                         
223                         _matrix->popup_menu (
224                                 make_pair (boost::shared_ptr<PortGroup> (), ARDOUR::BundleChannel ()),
225                                 make_pair (w.first, ARDOUR::BundleChannel ()),
226                                 t
227                                 );
228                 } else {
229                         _matrix->popup_menu (
230                                 make_pair (boost::shared_ptr<PortGroup> (), ARDOUR::BundleChannel ()),
231                                 make_pair (w.first, ARDOUR::BundleChannel ()),
232                                 t
233                                 );
234                 }
235         }
236 }
237
238 double
239 PortMatrixRowLabels::component_to_parent_x (double x) const
240 {
241         return x + _parent_rectangle.get_x();
242 }
243
244 double
245 PortMatrixRowLabels::parent_to_component_x (double x) const
246 {
247         return x - _parent_rectangle.get_x();
248 }
249
250 double
251 PortMatrixRowLabels::component_to_parent_y (double y) const
252 {
253         return y - _body->yoffset() + _parent_rectangle.get_y();
254 }
255
256 double
257 PortMatrixRowLabels::parent_to_component_y (double y) const
258 {
259         return y + _body->yoffset() - _parent_rectangle.get_y();
260 }
261
262
263 double
264 PortMatrixRowLabels::bundle_name_x () const
265 {
266         double x = 0;
267         
268         if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
269                 x = _highest_group_name + 2 * name_pad();
270         } else {
271                 if (_matrix->show_only_bundles()) {
272                         x = 0;
273                 } else {
274                         x = _longest_port_name + name_pad() * 2;
275                 }
276         }
277
278         return x;
279 }
280
281 double
282 PortMatrixRowLabels::port_name_x () const
283 {
284         if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
285                 return _longest_bundle_name + _highest_group_name + name_pad() * 4;
286         } else {
287                 return 0;
288         }
289
290         return 0;
291 }
292
293 void
294 PortMatrixRowLabels::render_bundle_name (
295         cairo_t* cr, Gdk::Color colour, double xoff, double yoff, boost::shared_ptr<ARDOUR::Bundle> b
296         )
297 {
298         double const x = bundle_name_x ();
299         
300         int const n = _matrix->show_only_bundles() ? 1 : b->nchannels();
301         set_source_rgb (cr, colour);
302         cairo_rectangle (cr, xoff + x, yoff, _longest_bundle_name + name_pad() * 2, row_height() * n);
303         cairo_fill_preserve (cr);
304         set_source_rgb (cr, background_colour());
305         cairo_set_line_width (cr, label_border_width ());
306         cairo_stroke (cr);
307
308         double const off = row_height() / 2;
309
310 //      if ((*i)->nchannels () > 0 && !_matrix->show_only_bundles()) {
311 //              /* use the extent of our first channel name so that the bundle name is vertically aligned with it */
312 //              cairo_text_extents_t ext;
313 //              cairo_text_extents (cr, (*i)->channel_name(0).c_str(), &ext);
314 //              off = (row_height() - ext.height) / 2;
315 //      }
316
317         set_source_rgb (cr, text_colour());
318         cairo_move_to (cr, xoff + x + name_pad(), yoff + name_pad() + off);
319         cairo_show_text (cr, b->name().c_str());
320 }
321
322 void
323 PortMatrixRowLabels::render_channel_name (
324         cairo_t* cr, Gdk::Color colour, double xoff, double yoff, ARDOUR::BundleChannel const& bc
325         )
326 {
327         set_source_rgb (cr, colour);
328         cairo_rectangle (cr, port_name_x() + xoff, yoff, _longest_port_name + name_pad() * 2, row_height());
329         cairo_fill_preserve (cr);
330         set_source_rgb (cr, background_colour());
331         cairo_set_line_width (cr, label_border_width ());
332         cairo_stroke (cr);
333         
334         cairo_text_extents_t ext;
335         cairo_text_extents (cr, bc.bundle->channel_name(bc.channel).c_str(), &ext);
336         double const off = (row_height() - ext.height) / 2;
337         
338         set_source_rgb (cr, text_colour());
339         cairo_move_to (cr, port_name_x() + xoff + name_pad(), yoff + name_pad() + off);
340         cairo_show_text (cr, bc.bundle->channel_name(bc.channel).c_str());
341 }
342
343 double
344 PortMatrixRowLabels::channel_x (ARDOUR::BundleChannel const& bc) const
345 {
346         return 0;
347 }
348
349 double
350 PortMatrixRowLabels::channel_y (ARDOUR::BundleChannel const& bc) const
351 {
352         uint32_t n = 0;
353
354         PortGroup::BundleList const & bundles = _matrix->rows()->bundles();
355         PortGroup::BundleList::const_iterator i = bundles.begin ();
356         while (i != bundles.end() && i->bundle != bc.bundle) {
357                 if (_matrix->show_only_bundles()) {
358                         n += 1;
359                 } else {
360                         n += i->bundle->nchannels ();
361                 }
362                 ++i;
363         }
364
365         if (!_matrix->show_only_bundles()) {
366                 n += bc.channel;
367         }
368         
369         return n * row_height();
370 }
371
372 void
373 PortMatrixRowLabels::queue_draw_for (ARDOUR::BundleChannel const & bc)
374 {
375         if (bc.bundle) {
376
377                 if (_matrix->show_only_bundles()) {
378                         _body->queue_draw_area (
379                                 component_to_parent_x (bundle_name_x()),
380                                 component_to_parent_y (channel_y (bc)),
381                                 _longest_bundle_name + name_pad() * 2,
382                                 row_height()
383                                 );
384                 } else {
385                         _body->queue_draw_area (
386                                 component_to_parent_x (port_name_x()),
387                                 component_to_parent_y (channel_y (bc)),
388                                 _longest_port_name + name_pad() * 2,
389                                 row_height()
390                                 );
391                 }
392         }
393
394 }
395
396 void
397 PortMatrixRowLabels::mouseover_changed (PortMatrixNode const &)
398 {
399         clear_channel_highlights ();
400         if (_body->mouseover().row.bundle) {
401                 add_channel_highlight (_body->mouseover().row);
402         }
403 }