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