Better fix for non-rearrangement after open for the port matrix.
[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           _realized (false)
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 (!_realized) {
119                 select_arrangement ();
120         }
121
122         _body->setup ();
123         setup_scrollbars ();
124         queue_draw ();
125
126         show_all ();
127 }
128
129 void
130 PortMatrix::set_type (ARDOUR::DataType t)
131 {
132         _type = t;
133         _ports[0].set_type (_type);
134         _ports[1].set_type (_type);
135         
136         setup_all_ports ();
137 }
138
139 void
140 PortMatrix::hscroll_changed ()
141 {
142         _body->set_xoffset (_hscroll.get_adjustment()->get_value());
143 }
144
145 void
146 PortMatrix::vscroll_changed ()
147 {
148         _body->set_yoffset (_vscroll.get_adjustment()->get_value());
149 }
150
151 void
152 PortMatrix::setup_scrollbars ()
153 {
154         Adjustment* a = _hscroll.get_adjustment ();
155         a->set_lower (0);
156         a->set_upper (_body->full_scroll_width());
157         a->set_page_size (_body->alloc_scroll_width());
158         a->set_step_increment (32);
159         a->set_page_increment (128);
160
161         a = _vscroll.get_adjustment ();
162         a->set_lower (0);
163         a->set_upper (_body->full_scroll_height());
164         a->set_page_size (_body->alloc_scroll_height());
165         a->set_step_increment (32);
166         a->set_page_increment (128);
167 }
168
169 /** Disassociate all of our ports from each other */
170 void
171 PortMatrix::disassociate_all ()
172 {
173         PortGroup::BundleList a = _ports[0].bundles ();
174         PortGroup::BundleList b = _ports[1].bundles ();
175
176         for (PortGroup::BundleList::iterator i = a.begin(); i != a.end(); ++i) {
177                 for (uint32_t j = 0; j < i->bundle->nchannels(); ++j) {
178                         for (PortGroup::BundleList::iterator k = b.begin(); k != b.end(); ++k) {
179                                 for (uint32_t l = 0; l < k->bundle->nchannels(); ++l) {
180                                                 
181                                         ARDOUR::BundleChannel c[2] = {
182                                                 ARDOUR::BundleChannel (i->bundle, j),
183                                                 ARDOUR::BundleChannel (k->bundle, l)
184                                                         };
185
186                                         if (get_state (c) == PortMatrixNode::ASSOCIATED) {
187                                                 set_state (c, false);
188                                         }
189
190                                 }
191                         }
192                 }
193         }
194
195         _body->rebuild_and_draw_grid ();
196 }
197
198 /* Decide how to arrange the components of the matrix */
199 void
200 PortMatrix::select_arrangement ()
201 {
202         uint32_t const N[2] = {
203                 _ports[0].total_visible_channels (),
204                 _ports[1].total_visible_channels ()
205         };
206
207         /* The list with the most channels goes on left or right, so that the most channel
208            names are printed horizontally and hence more readable.  However we also
209            maintain notional `signal flow' vaguely from left to right.  Subclasses
210            should choose where to put ports based on signal flowing from _ports[0]
211            to _ports[1] */
212         
213         if (N[0] > N[1]) {
214
215                 _row_index = 0;
216                 _column_index = 1;
217                 _arrangement = LEFT_TO_BOTTOM;
218
219         } else {
220
221                 _row_index = 1;
222                 _column_index = 0;
223                 _arrangement = TOP_TO_RIGHT;
224         }
225 }
226
227 /** @return columns list */
228 PortGroupList const *
229 PortMatrix::columns () const
230 {
231         return &_ports[_column_index];
232 }
233
234 /* @return rows list */
235 PortGroupList const *
236 PortMatrix::rows () const
237 {
238         return &_ports[_row_index];
239 }
240
241 void
242 PortMatrix::popup_menu (
243         pair<boost::shared_ptr<PortGroup>, ARDOUR::BundleChannel> column,
244         pair<boost::shared_ptr<PortGroup>, ARDOUR::BundleChannel> row,
245         uint32_t t
246         )
247 {
248         using namespace Menu_Helpers;
249         
250         delete _menu;
251
252         _menu = new Menu;
253         _menu->set_name ("ArdourContextMenu");
254         
255         MenuList& items = _menu->items ();
256
257         boost::shared_ptr<PortGroup> pg[2];
258         pg[_column_index] = column.first;
259         pg[_row_index] = row.first;
260
261         ARDOUR::BundleChannel bc[2];
262         bc[_column_index] = column.second;
263         bc[_row_index] = row.second;
264
265         char buf [64];
266
267         for (int dim = 0; dim < 2; ++dim) {
268
269                 if (pg[dim]) {
270
271                         boost::weak_ptr<PortGroup> wp (pg[dim]);
272                         
273                         if (pg[dim]->visible()) {
274                                 if (dim == 0) {
275                                         snprintf (buf, sizeof (buf), _("Hide '%s' sources"), pg[dim]->name.c_str());
276                                 } else {
277                                         snprintf (buf, sizeof (buf), _("Hide '%s' destinations"), pg[dim]->name.c_str());
278                                 }
279
280                                 items.push_back (MenuElem (buf, bind (mem_fun (*this, &PortMatrix::hide_group), wp)));
281                         } else {
282                                 if (dim == 0) {
283                                         snprintf (buf, sizeof (buf), _("Show '%s' sources"), pg[dim]->name.c_str());
284                                 } else {
285                                         snprintf (buf, sizeof (buf), _("Show '%s' destinations"), pg[dim]->name.c_str());
286                                 }
287                                 items.push_back (MenuElem (buf, bind (mem_fun (*this, &PortMatrix::show_group), wp)));
288                         }
289                 }
290
291                 if (bc[dim].bundle) {
292                         bool have_one = false;
293                         
294                         if (can_rename_channels (dim)) {
295                                 snprintf (buf, sizeof (buf), _("Rename '%s'..."), bc[dim].bundle->channel_name (bc[dim].channel).c_str());
296                                 boost::weak_ptr<ARDOUR::Bundle> w (bc[dim].bundle);
297                                 items.push_back (
298                                         MenuElem (
299                                                 buf,
300                                                 bind (mem_fun (*this, &PortMatrix::rename_channel_proxy), w, bc[dim].channel)
301                                                 )
302                                         );
303                                 
304                                 have_one = true;
305                         }
306                         
307                         if (can_remove_channels (dim)) {
308                                 snprintf (buf, sizeof (buf), _("Remove '%s'"), bc[dim].bundle->channel_name (bc[dim].channel).c_str());
309                                 boost::weak_ptr<ARDOUR::Bundle> w (bc[dim].bundle);
310                                 items.push_back (
311                                         MenuElem (
312                                                 buf,
313                                                 bind (mem_fun (*this, &PortMatrix::remove_channel_proxy), w, bc[dim].channel)
314                                                 )
315                                         );
316                                 
317                                 have_one = true;
318                         }
319                         
320                         boost::weak_ptr<ARDOUR::Bundle> w (bc[dim].bundle);
321
322                         if (_show_only_bundles) {
323                                 snprintf (buf, sizeof (buf), _("Disassociate all from '%s'"), bc[dim].bundle->name().c_str());
324                         } else {
325                                 snprintf (
326                                         buf, sizeof (buf), _("Disassociate all from '%s/%s'"),
327                                         bc[dim].bundle->name().c_str(), bc[dim].bundle->channel_name (bc[dim].channel).c_str()
328                                         );
329                         }
330                         
331                         items.push_back (
332                                 MenuElem (buf, bind (mem_fun (*this, &PortMatrix::disassociate_all_on_channel), w, bc[dim].channel, dim))
333                                 );
334                         
335                 }
336         }
337
338         items.push_back (SeparatorElem ());
339
340         items.push_back (MenuElem (_("Rescan"), mem_fun (*this, &PortMatrix::setup_all_ports)));
341         items.push_back (CheckMenuElem (_("Show individual ports"), mem_fun (*this, &PortMatrix::toggle_show_only_bundles)));
342         CheckMenuItem* i = dynamic_cast<CheckMenuItem*> (&items.back());
343         _inhibit_toggle_show_only_bundles = true;
344         i->set_active (!_show_only_bundles);
345         _inhibit_toggle_show_only_bundles = false;
346         
347         _menu->popup (1, t);
348 }
349
350
351 void
352 PortMatrix::remove_channel_proxy (boost::weak_ptr<ARDOUR::Bundle> b, uint32_t c)
353 {
354         boost::shared_ptr<ARDOUR::Bundle> sb = b.lock ();
355         if (!sb) {
356                 return;
357         }
358
359         remove_channel (ARDOUR::BundleChannel (sb, c));
360
361 }
362
363 void
364 PortMatrix::rename_channel_proxy (boost::weak_ptr<ARDOUR::Bundle> b, uint32_t c)
365 {
366         boost::shared_ptr<ARDOUR::Bundle> sb = b.lock ();
367         if (!sb) {
368                 return;
369         }
370
371         rename_channel (ARDOUR::BundleChannel (sb, c));
372 }
373
374 void
375 PortMatrix::disassociate_all_on_channel (boost::weak_ptr<ARDOUR::Bundle> bundle, uint32_t channel, int dim)
376 {
377         boost::shared_ptr<ARDOUR::Bundle> sb = bundle.lock ();
378         if (!sb) {
379                 return;
380         }
381
382         PortGroup::BundleList a = _ports[1-dim].bundles ();
383
384         for (PortGroup::BundleList::iterator i = a.begin(); i != a.end(); ++i) {
385                 for (uint32_t j = 0; j < i->bundle->nchannels(); ++j) {
386
387                         ARDOUR::BundleChannel c[2];
388                         c[dim] = ARDOUR::BundleChannel (sb, channel);
389                         c[1-dim] = ARDOUR::BundleChannel (i->bundle, j);
390
391                         if (get_state (c) == PortMatrixNode::ASSOCIATED) {
392                                 set_state (c, false);
393                         }
394                 }
395         }
396
397         _body->rebuild_and_draw_grid ();
398 }
399
400 void
401 PortMatrix::setup_global_ports ()
402 {
403         for (int i = 0; i < 2; ++i) {
404                 if (list_is_global (i)) {
405                         setup_ports (i);
406                 }
407         }
408 }
409
410 void
411 PortMatrix::setup_all_ports ()
412 {
413         setup_ports (0);
414         setup_ports (1);
415 }
416
417 void
418 PortMatrix::toggle_show_only_bundles ()
419 {
420         if (_inhibit_toggle_show_only_bundles) {
421                 return;
422         }
423         
424         _show_only_bundles = !_show_only_bundles;
425         _body->setup ();
426         setup_scrollbars ();
427         queue_draw ();
428 }
429
430 void
431 PortMatrix::hide_group (boost::weak_ptr<PortGroup> w)
432 {
433         boost::shared_ptr<PortGroup> g = w.lock ();
434         if (!g) {
435                 return;
436         }
437
438         g->set_visible (false);
439 }
440
441 void
442 PortMatrix::show_group (boost::weak_ptr<PortGroup> w)
443 {
444         boost::shared_ptr<PortGroup> g = w.lock ();
445         if (!g) {
446                 return;
447         }
448
449         g->set_visible (true);
450 }
451
452 pair<uint32_t, uint32_t>
453 PortMatrix::max_size () const
454 {
455         pair<uint32_t, uint32_t> m = _body->max_size ();
456
457         m.first += _vscroll.get_width ();
458         m.second += _hscroll.get_height ();
459
460         return m;
461 }
462
463 void
464 PortMatrix::on_realize ()
465 {
466         Widget::on_realize ();
467         _realized = true;
468 }
469
470 void
471 PortMatrix::on_unrealize ()
472 {
473         Widget::on_unrealize ();
474         _realized = false;
475 }