Fixes to bundle manager to make it vaguely usable.
[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
29 PortMatrixRowLabels::PortMatrixRowLabels (PortMatrix* m, PortMatrixBody* b)
30         : PortMatrixLabels (m, b)
31 {
32         
33 }
34
35 void
36 PortMatrixRowLabels::compute_dimensions ()
37 {
38         GdkPixmap* pm = gdk_pixmap_new (NULL, 1, 1, 24);
39         gdk_drawable_set_colormap (pm, gdk_colormap_get_system());
40         cairo_t* cr = gdk_cairo_create (pm);
41         
42         _longest_port_name = 0;
43         _longest_bundle_name = 0;
44         _height = 0;
45         ARDOUR::BundleList const r = _matrix->rows()->bundles();
46         for (ARDOUR::BundleList::const_iterator i = r.begin(); i != r.end(); ++i) {
47                 for (uint32_t j = 0; j < (*i)->nchannels(); ++j) {
48                         cairo_text_extents_t ext;
49                         cairo_text_extents (cr, (*i)->channel_name(j).c_str(), &ext);
50                         if (ext.width > _longest_port_name) {
51                                 _longest_port_name = ext.width;
52                         }
53                 }
54
55                 cairo_text_extents_t ext;
56                 cairo_text_extents (cr, (*i)->name().c_str(), &ext);
57                 if (ext.width > _longest_bundle_name) {
58                         _longest_bundle_name = ext.width;
59                 }
60
61                 _height += (*i)->nchannels() * row_height();
62         }
63
64         _highest_group_name = 0;
65         for (PortGroupList::List::const_iterator i = _matrix->rows()->begin(); i != _matrix->rows()->end(); ++i) {
66                 if ((*i)->visible()) {
67                         cairo_text_extents_t ext;
68                         cairo_text_extents (cr, (*i)->name.c_str(), &ext);
69                         if (ext.height > _highest_group_name) {
70                                 _highest_group_name = ext.height;
71                         }
72                 }
73         }
74                         
75         cairo_destroy (cr);
76         gdk_pixmap_unref (pm);
77
78         _width = _highest_group_name +
79                 _longest_port_name +
80                 _longest_bundle_name +
81                 name_pad() * 6;
82 }
83
84
85 void
86 PortMatrixRowLabels::render (cairo_t* cr)
87 {
88         /* BACKGROUND */
89         
90         set_source_rgb (cr, background_colour());
91         cairo_rectangle (cr, 0, 0, _width, _height);
92         cairo_fill (cr);
93
94         /* PORT GROUP NAMES */
95
96         double x = 0;
97         if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
98                 x = 0;
99         } else {
100                 x = _width - _highest_group_name - 2 * name_pad();
101         }
102
103         double y = 0;
104         int g = 0;
105         for (PortGroupList::List::const_iterator i = _matrix->rows()->begin(); i != _matrix->rows()->end(); ++i) {
106
107                 if (!(*i)->visible() || (*i)->bundles().empty()) {
108                         continue;
109                 }
110                         
111                 /* compute height of this group */
112                 double h = (*i)->total_channels () * row_height();
113
114                 /* rectangle */
115                 set_source_rgb (cr, get_a_group_colour (g));
116                 double const rw = _highest_group_name + 2 * name_pad();
117                 cairo_rectangle (cr, x, y, rw, h);
118                 cairo_fill (cr);
119                     
120                 /* hence what abbreviation (or not) we need for the group name */
121                 std::pair<std::string, double> display = display_port_name (cr, (*i)->name, h);
122
123                 /* plot it */
124                 set_source_rgb (cr, text_colour());
125                 cairo_move_to (cr, x + rw - name_pad(), y + (h + display.second) / 2);
126                 cairo_save (cr);
127                 cairo_rotate (cr, - M_PI / 2);
128                 cairo_show_text (cr, display.first.c_str());
129                 cairo_restore (cr);
130
131                 y += h;
132                 ++g;
133         }
134
135         /* BUNDLE NAMES */
136
137         x = 0;
138         if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
139                 x = _highest_group_name + 2 * name_pad();
140         } else {
141                 x = _longest_port_name + name_pad() * 2;
142         }
143
144         y = 0;
145         ARDOUR::BundleList const r = _matrix->rows()->bundles();
146         for (ARDOUR::BundleList::const_iterator i = r.begin(); i != r.end(); ++i) {
147
148                 Gdk::Color const colour = get_a_bundle_colour (i - r.begin ());
149                 set_source_rgb (cr, colour);
150                 cairo_rectangle (cr, x, y, _longest_bundle_name + name_pad() * 2, row_height() * (*i)->nchannels());
151                 cairo_fill_preserve (cr);
152                 set_source_rgb (cr, background_colour());
153                 cairo_set_line_width (cr, label_border_width ());
154                 cairo_stroke (cr);
155
156                 double off = 0;
157                 if ((*i)->nchannels () > 0) {
158                         /* use the extent of our first channel name so that the bundle name is vertically aligned with it */
159                         cairo_text_extents_t ext;
160                         cairo_text_extents (cr, (*i)->channel_name(0).c_str(), &ext);
161                         off = (row_height() - ext.height) / 2;
162                 } else {
163                         off = row_height() / 2;
164                 }
165
166                 set_source_rgb (cr, text_colour());
167                 cairo_move_to (cr, x + name_pad(), y + name_pad() + off);
168                 cairo_show_text (cr, (*i)->name().c_str());
169                 
170                 y += row_height() * (*i)->nchannels ();
171         }
172         
173
174         /* PORT NAMES */
175
176         y = 0;
177         for (ARDOUR::BundleList::const_iterator i = r.begin(); i != r.end(); ++i) {
178                 for (uint32_t j = 0; j < (*i)->nchannels(); ++j) {
179                         render_channel_name (cr, get_a_bundle_colour (i - r.begin()), 0, y, ARDOUR::BundleChannel (*i, j));
180                         y += row_height();
181                 }
182         }
183 }
184
185 void
186 PortMatrixRowLabels::button_press (double x, double y, int b, uint32_t t)
187 {
188         switch (b) {
189         case 1:
190                 _body->highlight_associated_channels (_matrix->row_index(), y / row_height ());
191                 break;
192         case 3:
193                 maybe_popup_context_menu (x, y, t);
194                 break;
195         }
196 }
197
198 void
199 PortMatrixRowLabels::maybe_popup_context_menu (double x, double y, uint32_t t)
200 {
201         if ( (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && x > (_longest_bundle_name + name_pad() * 2)) ||
202              (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && x < (_longest_port_name + name_pad() * 2))
203                 ) {
204
205                 _matrix->popup_channel_context_menu (_matrix->row_index(), y / row_height(), t);
206                 
207         }
208 }
209
210 double
211 PortMatrixRowLabels::component_to_parent_x (double x) const
212 {
213         return x + _parent_rectangle.get_x();
214 }
215
216 double
217 PortMatrixRowLabels::parent_to_component_x (double x) const
218 {
219         return x - _parent_rectangle.get_x();
220 }
221
222 double
223 PortMatrixRowLabels::component_to_parent_y (double y) const
224 {
225         return y - _body->yoffset() + _parent_rectangle.get_y();
226 }
227
228 double
229 PortMatrixRowLabels::parent_to_component_y (double y) const
230 {
231         return y + _body->yoffset() - _parent_rectangle.get_y();
232 }
233
234 double
235 PortMatrixRowLabels::port_name_x () const
236 {
237         if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
238                 return _longest_bundle_name + _highest_group_name + name_pad() * 4;
239         } else {
240                 return 0;
241         }
242
243         return 0;
244 }
245
246 void
247 PortMatrixRowLabels::render_channel_name (
248         cairo_t* cr, Gdk::Color colour, double xoff, double yoff, ARDOUR::BundleChannel const& bc
249         )
250 {
251         set_source_rgb (cr, colour);
252         cairo_rectangle (cr, port_name_x() + xoff, yoff, _longest_port_name + name_pad() * 2, row_height());
253         cairo_fill_preserve (cr);
254         set_source_rgb (cr, background_colour());
255         cairo_set_line_width (cr, label_border_width ());
256         cairo_stroke (cr);
257         
258         cairo_text_extents_t ext;
259         cairo_text_extents (cr, bc.bundle->channel_name(bc.channel).c_str(), &ext);
260         double const off = (row_height() - ext.height) / 2;
261         
262         set_source_rgb (cr, text_colour());
263         cairo_move_to (cr, port_name_x() + xoff + name_pad(), yoff + name_pad() + off);
264         cairo_show_text (cr, bc.bundle->channel_name(bc.channel).c_str());
265 }
266
267 double
268 PortMatrixRowLabels::channel_x (ARDOUR::BundleChannel const& bc) const
269 {
270         return 0;
271 }
272
273 double
274 PortMatrixRowLabels::channel_y (ARDOUR::BundleChannel const& bc) const
275 {
276         uint32_t n = 0;
277
278         ARDOUR::BundleList::const_iterator i = _matrix->rows()->bundles().begin();
279         while (i != _matrix->rows()->bundles().end() && *i != bc.bundle) {
280                 n += (*i)->nchannels ();
281                 ++i;
282         }
283         
284         n += bc.channel;
285         return n * row_height();
286 }
287
288 void
289 PortMatrixRowLabels::queue_draw_for (ARDOUR::BundleChannel const & bc)
290 {
291         if (bc.bundle) {
292
293                 _body->queue_draw_area (
294                         component_to_parent_x (port_name_x()),
295                         component_to_parent_y (channel_y (bc)),
296                         _longest_port_name + name_pad() * 2,
297                         row_height()
298                         );
299         }
300
301 }
302
303 void
304 PortMatrixRowLabels::mouseover_changed (PortMatrixNode const &)
305 {
306         clear_channel_highlights ();
307         if (_body->mouseover().row.bundle) {
308                 add_channel_highlight (_body->mouseover().row);
309         }
310 }