Reduce header dependencies.
[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() && (*i)->ports.empty()) ) {
108                         continue;
109                 }
110                         
111                 /* compute height of this group */
112                 double h = 0;
113                 for (ARDOUR::BundleList::const_iterator j = (*i)->bundles().begin(); j != (*i)->bundles().end(); ++j) {
114                         h += (*j)->nchannels() * row_height();
115                 }
116                 h += (*i)->ports.size() * row_height();
117
118                 /* rectangle */
119                 set_source_rgb (cr, get_a_group_colour (g));
120                 double const rw = _highest_group_name + 2 * name_pad();
121                 cairo_rectangle (cr, x, y, rw, h);
122                 cairo_fill (cr);
123                     
124                 /* hence what abbreviation (or not) we need for the group name */
125                 std::pair<std::string, double> display = display_port_name (cr, (*i)->name, h);
126
127                 /* plot it */
128                 set_source_rgb (cr, text_colour());
129                 cairo_move_to (cr, x + rw - name_pad(), y + (h + display.second) / 2);
130                 cairo_save (cr);
131                 cairo_rotate (cr, - M_PI / 2);
132                 cairo_show_text (cr, display.first.c_str());
133                 cairo_restore (cr);
134
135                 y += h;
136                 ++g;
137         }
138
139         /* BUNDLE NAMES */
140
141         x = 0;
142         if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
143                 x = _highest_group_name + 2 * name_pad();
144         } else {
145                 x = _longest_port_name + name_pad() * 2;
146         }
147
148         y = 0;
149         ARDOUR::BundleList const r = _matrix->rows()->bundles();
150         for (ARDOUR::BundleList::const_iterator i = r.begin(); i != r.end(); ++i) {
151
152                 Gdk::Color const colour = get_a_bundle_colour (i - r.begin ());
153                 set_source_rgb (cr, colour);
154                 cairo_rectangle (cr, x, y, _longest_bundle_name + name_pad() * 2, row_height() * (*i)->nchannels());
155                 cairo_fill_preserve (cr);
156                 set_source_rgb (cr, background_colour());
157                 cairo_set_line_width (cr, label_border_width ());
158                 cairo_stroke (cr);
159
160                 double off = 0;
161                 if ((*i)->nchannels () > 0) {
162                         /* use the extent of our first channel name so that the bundle name is vertically aligned with it */
163                         cairo_text_extents_t ext;
164                         cairo_text_extents (cr, (*i)->channel_name(0).c_str(), &ext);
165                         off = (row_height() - ext.height) / 2;
166                 } else {
167                         off = row_height() / 2;
168                 }
169
170                 set_source_rgb (cr, text_colour());
171                 cairo_move_to (cr, x + name_pad(), y + name_pad() + off);
172                 cairo_show_text (cr, (*i)->name().c_str());
173                 
174                 y += row_height() * (*i)->nchannels ();
175         }
176         
177
178         /* PORT NAMES */
179
180         y = 0;
181         for (ARDOUR::BundleList::const_iterator i = r.begin(); i != r.end(); ++i) {
182                 for (uint32_t j = 0; j < (*i)->nchannels(); ++j) {
183                         render_channel_name (cr, get_a_bundle_colour (i - r.begin()), 0, y, ARDOUR::BundleChannel (*i, j));
184                         y += row_height();
185                 }
186         }
187 }
188
189 void
190 PortMatrixRowLabels::button_press (double x, double y, int b, uint32_t t)
191 {
192         switch (b) {
193         case 1:
194                 _body->highlight_associated_channels (_matrix->row_index(), y / row_height ());
195                 break;
196         case 3:
197                 maybe_popup_context_menu (x, y, t);
198                 break;
199         }
200 }
201
202 void
203 PortMatrixRowLabels::maybe_popup_context_menu (double x, double y, uint32_t t)
204 {
205         if (!_matrix->can_rename_channels (_matrix->row_index()) &&
206             !_matrix->can_remove_channels (_matrix->row_index())) {
207                 return;
208         }
209
210         if ( (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && x > (_longest_bundle_name + name_pad() * 2)) ||
211              (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && x < (_longest_port_name + name_pad() * 2))
212                 ) {
213
214                 _matrix->popup_channel_context_menu (_matrix->row_index(), y / row_height(), t);
215                 
216         }
217 }
218
219 double
220 PortMatrixRowLabels::component_to_parent_x (double x) const
221 {
222         return x + _parent_rectangle.get_x();
223 }
224
225 double
226 PortMatrixRowLabels::parent_to_component_x (double x) const
227 {
228         return x - _parent_rectangle.get_x();
229 }
230
231 double
232 PortMatrixRowLabels::component_to_parent_y (double y) const
233 {
234         return y - _body->yoffset() + _parent_rectangle.get_y();
235 }
236
237 double
238 PortMatrixRowLabels::parent_to_component_y (double y) const
239 {
240         return y + _body->yoffset() - _parent_rectangle.get_y();
241 }
242
243 double
244 PortMatrixRowLabels::port_name_x () const
245 {
246         if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
247                 return _longest_bundle_name + _highest_group_name + name_pad() * 4;
248         } else {
249                 return 0;
250         }
251
252         return 0;
253 }
254
255 void
256 PortMatrixRowLabels::render_channel_name (
257         cairo_t* cr, Gdk::Color colour, double xoff, double yoff, ARDOUR::BundleChannel const& bc
258         )
259 {
260         set_source_rgb (cr, colour);
261         cairo_rectangle (cr, port_name_x() + xoff, yoff, _longest_port_name + name_pad() * 2, row_height());
262         cairo_fill_preserve (cr);
263         set_source_rgb (cr, background_colour());
264         cairo_set_line_width (cr, label_border_width ());
265         cairo_stroke (cr);
266         
267         cairo_text_extents_t ext;
268         cairo_text_extents (cr, bc.bundle->channel_name(bc.channel).c_str(), &ext);
269         double const off = (row_height() - ext.height) / 2;
270         
271         set_source_rgb (cr, text_colour());
272         cairo_move_to (cr, port_name_x() + xoff + name_pad(), yoff + name_pad() + off);
273         cairo_show_text (cr, bc.bundle->channel_name(bc.channel).c_str());
274 }
275
276 double
277 PortMatrixRowLabels::channel_x (ARDOUR::BundleChannel const& bc) const
278 {
279         return 0;
280 }
281
282 double
283 PortMatrixRowLabels::channel_y (ARDOUR::BundleChannel const& bc) const
284 {
285         uint32_t n = 0;
286
287         ARDOUR::BundleList::const_iterator i = _matrix->rows()->bundles().begin();
288         while (i != _matrix->rows()->bundles().end() && *i != bc.bundle) {
289                 n += (*i)->nchannels ();
290                 ++i;
291         }
292         
293         n += bc.channel;
294         return n * row_height();
295 }
296
297 void
298 PortMatrixRowLabels::queue_draw_for (ARDOUR::BundleChannel const & bc)
299 {
300         if (bc.bundle) {
301
302                 _body->queue_draw_area (
303                         component_to_parent_x (port_name_x()),
304                         component_to_parent_y (channel_y (bc)),
305                         _longest_port_name + name_pad() * 2,
306                         row_height()
307                         );
308         }
309
310 }
311
312 void
313 PortMatrixRowLabels::mouseover_changed (PortMatrixNode const &)
314 {
315         clear_channel_highlights ();
316         if (_body->mouseover().row.bundle) {
317                 add_channel_highlight (_body->mouseover().row);
318         }
319 }