Do not display orphaned Show: labels when they are not required.
[ardour.git] / gtk2_ardour / port_matrix.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 <gtkmm/scrolledwindow.h>
22 #include <gtkmm/adjustment.h>
23 #include <gtkmm/label.h>
24 #include <gtkmm/menu.h>
25 #include <gtkmm/menushell.h>
26 #include <gtkmm/menu_elems.h>
27 #include "ardour/bundle.h"
28 #include "ardour/types.h"
29 #include "ardour/session.h"
30 #include "ardour/route.h"
31 #include "port_matrix.h"
32 #include "i18n.h"
33
34 /** PortMatrix constructor.
35  *  @param session Our session.
36  *  @param type Port type that we are handling.
37  */
38 PortMatrix::PortMatrix (ARDOUR::Session& session, ARDOUR::DataType type)
39         : _session (session),
40           _type (type),
41           _body (this),
42           _column_visibility_box_added (false),
43           _row_visibility_box_added (false),
44           _menu (0),
45           _setup_once (false),
46           _arrangement (TOP_TO_RIGHT),
47           _row_index (0),
48           _column_index (1)
49 {
50         _ports[0].set_type (type);
51         _ports[1].set_type (type);
52
53         _row_visibility_box.pack_start (_row_visibility_label, Gtk::PACK_SHRINK);
54         _column_visibility_box.pack_start (_column_visibility_label, Gtk::PACK_SHRINK);
55
56         _hscroll.signal_value_changed().connect (sigc::mem_fun (*this, &PortMatrix::hscroll_changed));
57         _vscroll.signal_value_changed().connect (sigc::mem_fun (*this, &PortMatrix::vscroll_changed));
58
59         /* watch for routes being added or removed */
60         _session.RouteAdded.connect (sigc::hide (sigc::mem_fun (*this, &PortMatrix::routes_changed)));
61         
62         reconnect_to_routes ();
63
64         show_all ();
65 }
66
67 PortMatrix::~PortMatrix ()
68 {
69         for (std::vector<Gtk::CheckButton*>::iterator i = _column_visibility_buttons.begin(); i != _column_visibility_buttons.end(); ++i) {
70                 delete *i;
71         }
72
73         for (std::vector<Gtk::CheckButton*>::iterator i = _row_visibility_buttons.begin(); i != _row_visibility_buttons.end(); ++i) {
74                 delete *i;
75         }
76
77         delete _menu;
78 }
79
80 /** Disconnect from and reconnect to routes' signals that we need to watch for things that affect the matrix */
81 void
82 PortMatrix::reconnect_to_routes ()
83 {
84         for (std::vector<sigc::connection>::iterator i = _route_connections.begin(); i != _route_connections.end(); ++i) {
85                 i->disconnect ();
86         }
87
88         boost::shared_ptr<ARDOUR::RouteList> routes = _session.get_routes ();
89         for (ARDOUR::RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
90                 _route_connections.push_back (
91                         (*i)->processors_changed.connect (sigc::mem_fun (*this, &PortMatrix::setup))
92                         );
93         }
94 }
95
96 /** A route has been added to or removed from the session */
97 void
98 PortMatrix::routes_changed ()
99 {
100         reconnect_to_routes ();
101         setup ();
102 }
103
104 /** Set up everything that changes about the matrix */
105 void
106 PortMatrix::setup ()
107 {
108         select_arrangement ();
109         _body.setup ();
110         setup_scrollbars ();
111         queue_draw ();
112
113         if (_setup_once) {
114
115                 /* we've set up before, so we need to clean up before re-setting-up */
116                 /* XXX: we ought to be able to do this by just getting a list of children
117                    from each container widget, but I couldn't make that work */
118
119                
120                 for (std::vector<Gtk::CheckButton*>::iterator i = _column_visibility_buttons.begin(); i != _column_visibility_buttons.end(); ++i) {
121                         _column_visibility_box.remove (**i);
122                         delete *i;
123                 }
124
125                 _column_visibility_buttons.clear ();
126
127                 for (std::vector<Gtk::CheckButton*>::iterator i = _row_visibility_buttons.begin(); i != _row_visibility_buttons.end(); ++i) {
128                         _row_visibility_box.remove (**i);
129                         delete *i;
130                 }
131
132                 _row_visibility_buttons.clear ();
133                 
134                 _scroller_table.remove (_vscroll);
135                 _scroller_table.remove (_body);
136                 _scroller_table.remove (_hscroll);
137
138                 _main_hbox.remove (_scroller_table);
139                 if (_row_visibility_box_added) {
140                         _main_hbox.remove (_row_visibility_box);
141                 }
142                 
143                 if (_column_visibility_box_added) {
144                         remove (_column_visibility_box);
145                 }
146                 remove (_main_hbox);
147         }
148
149         if (_column_index == 0) {
150                 _column_visibility_label.set_text (_("Show Outputs"));
151                 _row_visibility_label.set_text (_("Show Inputs"));
152         } else {
153                 _column_visibility_label.set_text (_("Show Inputs"));
154                 _row_visibility_label.set_text (_("Show Outputs"));
155         }
156
157         for (PortGroupList::List::const_iterator i = columns()->begin(); i != columns()->end(); ++i) {
158                 Gtk::CheckButton* b = new Gtk::CheckButton ((*i)->name);
159                 b->set_active ((*i)->visible());
160                 boost::weak_ptr<PortGroup> w (*i);
161                 b->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &PortMatrix::visibility_toggled), w, b));
162                 _column_visibility_buttons.push_back (b);
163                 _column_visibility_box.pack_start (*b, Gtk::PACK_SHRINK);
164         }
165
166         for (PortGroupList::List::const_iterator i = rows()->begin(); i != rows()->end(); ++i) {
167                 Gtk::CheckButton* b = new Gtk::CheckButton ((*i)->name);
168                 b->set_active ((*i)->visible());
169                 boost::weak_ptr<PortGroup> w (*i);
170                 b->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &PortMatrix::visibility_toggled), w, b));
171                 _row_visibility_buttons.push_back (b);
172                 _row_visibility_box.pack_start (*b, Gtk::PACK_SHRINK);
173         }
174
175         if (_arrangement == TOP_TO_RIGHT) {
176           
177                 _scroller_table.attach (_hscroll, 0, 1, 0, 1, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
178                 _scroller_table.attach (_body, 0, 1, 1, 2);
179                 _scroller_table.attach (_vscroll, 1, 2, 1, 2, Gtk::SHRINK);
180
181                 _main_hbox.pack_start (_scroller_table);
182
183                 if (rows()->size() > 1) {
184                         _main_hbox.pack_start (_row_visibility_box, Gtk::PACK_SHRINK);
185                         _row_visibility_box_added = true;
186                 } else {
187                         _row_visibility_box_added = false;
188                 }
189
190                 if (columns()->size() > 1) {
191                         pack_start (_column_visibility_box, Gtk::PACK_SHRINK);
192                         _column_visibility_box_added = true;
193                 } else {
194                         _column_visibility_box_added = false;
195                 }
196                 
197                 pack_start (_main_hbox);
198                 
199         } else {
200                 _scroller_table.attach (_vscroll, 0, 1, 0, 1, Gtk::SHRINK);
201                 _scroller_table.attach (_body, 1, 2, 0, 1);
202                 _scroller_table.attach (_hscroll, 1, 2, 1, 2, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
203
204                 if (rows()->size() > 1) {
205                         _main_hbox.pack_start (_row_visibility_box, Gtk::PACK_SHRINK);
206                         _row_visibility_box_added = true;
207                 } else {
208                         _row_visibility_box_added = false;
209                 }
210                 
211                 _main_hbox.pack_start (_scroller_table);
212
213                 pack_start (_main_hbox);
214                 
215                 if (columns()->size() > 1) {
216                         pack_start (_column_visibility_box, Gtk::PACK_SHRINK);
217                         _column_visibility_box_added = true;
218                 } else {
219                         _column_visibility_box_added = false;
220                 }
221         }
222
223         _setup_once = true;
224
225         show_all ();
226 }
227
228 void
229 PortMatrix::set_type (ARDOUR::DataType t)
230 {
231         _type = t;
232         _ports[0].set_type (_type);
233         _ports[1].set_type (_type);
234         setup ();
235 }
236
237 void
238 PortMatrix::hscroll_changed ()
239 {
240         _body.set_xoffset (_hscroll.get_adjustment()->get_value());
241 }
242
243 void
244 PortMatrix::vscroll_changed ()
245 {
246         _body.set_yoffset (_vscroll.get_adjustment()->get_value());
247 }
248
249 void
250 PortMatrix::setup_scrollbars ()
251 {
252         Gtk::Adjustment* a = _hscroll.get_adjustment ();
253         a->set_lower (0);
254         a->set_upper (_body.full_scroll_width());
255         a->set_page_size (_body.alloc_scroll_width());
256         a->set_step_increment (32);
257         a->set_page_increment (128);
258
259         a = _vscroll.get_adjustment ();
260         a->set_lower (0);
261         a->set_upper (_body.full_scroll_height());
262         a->set_page_size (_body.alloc_scroll_height());
263         a->set_step_increment (32);
264         a->set_page_increment (128);
265 }
266
267 /** Disassociate all of our ports from each other */
268 void
269 PortMatrix::disassociate_all ()
270 {
271         ARDOUR::BundleList a = _ports[0].bundles ();
272         ARDOUR::BundleList b = _ports[1].bundles ();
273         
274         for (ARDOUR::BundleList::iterator i = a.begin(); i != a.end(); ++i) {
275                 for (uint32_t j = 0; j < (*i)->nchannels(); ++j) {
276                         for (ARDOUR::BundleList::iterator k = b.begin(); k != b.end(); ++k) {
277                                 for (uint32_t l = 0; l < (*k)->nchannels(); ++l) {
278                                                 
279                                         ARDOUR::BundleChannel c[2] = {
280                                                 ARDOUR::BundleChannel (*i, j),
281                                                 ARDOUR::BundleChannel (*k, l)
282                                                         };
283                                         
284                                         set_state (c, false);
285
286                                 }
287                         }
288                 }
289         }
290
291         _body.rebuild_and_draw_grid ();
292 }
293
294 /* Decide how to arrange the components of the matrix */
295 void
296 PortMatrix::select_arrangement ()
297 {
298         uint32_t const N[2] = {
299                 _ports[0].total_visible_ports (),
300                 _ports[1].total_visible_ports ()
301         };
302
303         /* The list with the most ports goes on left or right, so that the most port
304            names are printed horizontally and hence more readable.  However we also
305            maintain notional `signal flow' vaguely from left to right.  Subclasses
306            should choose where to put ports based on signal flowing from _ports[0]
307            to _ports[1] */
308         
309         if (N[0] > N[1]) {
310
311                 _row_index = 0;
312                 _column_index = 1;
313                 _arrangement = LEFT_TO_BOTTOM;
314
315         } else {
316
317                 _row_index = 1;
318                 _column_index = 0;
319                 _arrangement = TOP_TO_RIGHT;
320         }
321 }
322
323 /** @return columns list */
324 PortGroupList const *
325 PortMatrix::columns () const
326 {
327         return &_ports[_column_index];
328 }
329
330 /* @return rows list */
331 PortGroupList const *
332 PortMatrix::rows () const
333 {
334         return &_ports[_row_index];
335 }
336
337 /** A group visibility checkbutton has been toggled.
338  * @param w Group.
339  * @param b Button.
340  */
341 void
342 PortMatrix::visibility_toggled (boost::weak_ptr<PortGroup> w, Gtk::CheckButton* b)
343 {
344         boost::shared_ptr<PortGroup> g = w.lock ();
345         if (!g) {
346                 return;
347         }
348         
349         g->set_visible (b->get_active());
350         _body.setup ();
351         setup_scrollbars ();
352         queue_draw ();
353 }
354
355 void
356 PortMatrix::popup_channel_context_menu (int dim, uint32_t N, uint32_t t)
357 {
358         delete _menu;
359
360         _menu = new Gtk::Menu;
361         _menu->set_name ("ArdourContextMenu");
362         
363         Gtk::Menu_Helpers::MenuList& items = _menu->items ();
364
365         ARDOUR::BundleChannel bc;
366
367         ARDOUR::BundleList const r = _ports[dim].bundles();
368         for (ARDOUR::BundleList::const_iterator i = r.begin(); i != r.end(); ++i) {
369                 if (N < (*i)->nchannels ()) {
370                         bc = ARDOUR::BundleChannel (*i, N);
371                         break;
372                 } else {
373                         N -= (*i)->nchannels ();
374                 }
375         }
376
377         if (bc.bundle) {
378                 char buf [64];
379
380                 if (can_rename_channels (dim)) {
381                         snprintf (buf, sizeof (buf), _("Rename '%s'..."), bc.bundle->channel_name (bc.channel).c_str());
382                         boost::weak_ptr<ARDOUR::Bundle> w (bc.bundle);
383                         items.push_back (
384                                 Gtk::Menu_Helpers::MenuElem (
385                                         buf,
386                                         sigc::bind (sigc::mem_fun (*this, &PortMatrix::rename_channel_proxy), w, bc.channel)
387                                         )
388                                 );
389                 }
390
391                 if (can_remove_channels (dim)) {
392                         snprintf (buf, sizeof (buf), _("Remove '%s'"), bc.bundle->channel_name (bc.channel).c_str());
393                         boost::weak_ptr<ARDOUR::Bundle> w (bc.bundle);
394                         items.push_back (
395                                 Gtk::Menu_Helpers::MenuElem (
396                                         buf,
397                                         sigc::bind (sigc::mem_fun (*this, &PortMatrix::remove_channel_proxy), w, bc.channel)
398                                         )
399                                 );
400                 }
401                         
402                 _menu->popup (1, t);
403         }
404         
405 }
406
407
408 void
409 PortMatrix::remove_channel_proxy (boost::weak_ptr<ARDOUR::Bundle> b, uint32_t c)
410 {
411         boost::shared_ptr<ARDOUR::Bundle> sb = b.lock ();
412         if (!sb) {
413                 return;
414         }
415
416         remove_channel (ARDOUR::BundleChannel (sb, c));
417
418 }
419
420 void
421 PortMatrix::rename_channel_proxy (boost::weak_ptr<ARDOUR::Bundle> b, uint32_t c)
422 {
423         boost::shared_ptr<ARDOUR::Bundle> sb = b.lock ();
424         if (!sb) {
425                 return;
426         }
427
428         rename_channel (ARDOUR::BundleChannel (sb, c));
429 }