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