Tidy up a bit in the case where the port matrix has nothing to display.
[ardour.git] / gtk2_ardour / port_group.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 <cstring>
21 #include <boost/shared_ptr.hpp>
22
23 #include "ardour/audio_track.h"
24 #include "ardour/audioengine.h"
25 #include "ardour/bundle.h"
26 #include "ardour/user_bundle.h"
27 #include "ardour/io_processor.h"
28 #include "ardour/midi_track.h"
29 #include "ardour/port.h"
30 #include "ardour/session.h"
31 #include "ardour/auditioner.h"
32
33 #include "port_group.h"
34 #include "port_matrix.h"
35 #include "time_axis_view.h"
36 #include "public_editor.h"
37
38 #include "i18n.h"
39
40 using namespace std;
41 using namespace Gtk;
42 using namespace ARDOUR;
43
44 /** PortGroup constructor.
45  * @param n Name.
46  */
47 PortGroup::PortGroup (std::string const & n)
48         : name (n), _visible (true)
49 {
50
51 }
52
53 /** Add a bundle to a group.
54  *  @param b Bundle.
55  *  @param allow_dups true to allow the group to contain more than one bundle with the same port, otherwise false.
56  */
57 void
58 PortGroup::add_bundle (boost::shared_ptr<Bundle> b, bool allow_dups)
59 {
60         add_bundle_internal (b, boost::shared_ptr<IO> (), false, Gdk::Color (), allow_dups);
61 }
62
63 /** Add a bundle to a group.
64  *  @param b Bundle.
65  *  @param io IO whose ports are in the bundle.
66  */
67 void
68 PortGroup::add_bundle (boost::shared_ptr<Bundle> b, boost::shared_ptr<IO> io)
69 {
70         add_bundle_internal (b, io, false, Gdk::Color (), false);
71 }
72
73 /** Add a bundle to a group.
74  *  @param b Bundle.
75  *  @param c Colour to represent the bundle with.
76  */
77 void
78 PortGroup::add_bundle (boost::shared_ptr<Bundle> b, boost::shared_ptr<IO> io, Gdk::Color c)
79 {
80         add_bundle_internal (b, io, true, c, false);
81 }
82
83 void
84 PortGroup::add_bundle_internal (boost::shared_ptr<Bundle> b, boost::shared_ptr<IO> io, bool has_colour, Gdk::Color colour, bool allow_dups)
85 {
86         assert (b.get());
87
88         if (!allow_dups) {
89                 
90                 /* don't add this bundle if we already have one with the same ports */
91                 
92                 BundleList::iterator i = _bundles.begin ();
93                 while (i != _bundles.end() && b->has_same_ports (i->bundle) == false) {
94                         ++i;
95                 }
96                 
97                 if (i != _bundles.end ()) {
98                         return;
99                 }
100         }
101
102         BundleRecord r;
103         r.bundle = b;
104         r.io = io;
105         r.colour = colour;
106         r.has_colour = has_colour;
107         r.changed_connection = b->Changed.connect (sigc::mem_fun (*this, &PortGroup::bundle_changed));
108
109         _bundles.push_back (r);
110
111         Changed ();     
112 }
113
114 void
115 PortGroup::remove_bundle (boost::shared_ptr<Bundle> b)
116 {
117         assert (b.get());
118
119         BundleList::iterator i = _bundles.begin ();
120         while (i != _bundles.end() && i->bundle != b) {
121                 ++i;
122         }
123
124         if (i == _bundles.end()) {
125                 return;
126         }
127
128         i->changed_connection.disconnect ();
129         _bundles.erase (i);
130
131         Changed ();
132 }
133
134 void
135 PortGroup::bundle_changed (Bundle::Change c)
136 {
137         BundleChanged (c);
138 }
139
140
141 void
142 PortGroup::clear ()
143 {
144         for (BundleList::iterator i = _bundles.begin(); i != _bundles.end(); ++i) {
145                 i->changed_connection.disconnect ();
146         }
147
148         _bundles.clear ();
149         Changed ();
150 }
151
152 bool
153 PortGroup::has_port (std::string const& p) const
154 {
155         for (BundleList::const_iterator i = _bundles.begin(); i != _bundles.end(); ++i) {
156                 if (i->bundle->offers_port_alone (p)) {
157                         return true;
158                 }
159         }
160
161         return false;
162 }
163
164 boost::shared_ptr<Bundle>
165 PortGroup::only_bundle ()
166 {
167         assert (_bundles.size() == 1);
168         return _bundles.front().bundle;
169 }
170
171
172 uint32_t
173 PortGroup::total_channels () const
174 {
175         uint32_t n = 0;
176         for (BundleList::const_iterator i = _bundles.begin(); i != _bundles.end(); ++i) {
177                 n += i->bundle->nchannels ();
178         }
179
180         return n;
181 }
182
183 boost::shared_ptr<IO>
184 PortGroup::io_from_bundle (boost::shared_ptr<ARDOUR::Bundle> b) const
185 {
186         BundleList::const_iterator i = _bundles.begin ();
187         while (i != _bundles.end() && i->bundle != b) {
188                 ++i;
189         }
190
191         if (i == _bundles.end()) {
192                 return boost::shared_ptr<IO> ();
193         }
194
195         return i->io;
196 }
197
198
199 /** PortGroupList constructor.
200  */
201 PortGroupList::PortGroupList ()
202         : _type (DataType::AUDIO), _signals_suspended (false), _pending_change (false), _pending_bundle_change ((Bundle::Change) 0)
203 {
204
205 }
206
207 void
208 PortGroupList::set_type (DataType t)
209 {
210         _type = t;
211         clear ();
212 }
213
214 void
215 PortGroupList::maybe_add_processor_to_bundle (boost::weak_ptr<Processor> wp, boost::shared_ptr<RouteBundle> rb, bool inputs, set<boost::shared_ptr<IO> >& used_io)
216 {
217         boost::shared_ptr<Processor> p (wp.lock());
218
219         if (!p) {
220                 return;
221         }
222
223         boost::shared_ptr<IOProcessor> iop = boost::dynamic_pointer_cast<IOProcessor> (p);
224
225         if (iop) {
226
227                 boost::shared_ptr<IO> io = inputs ? iop->input() : iop->output();
228
229                 if (io && used_io.find (io) == used_io.end()) {
230                         rb->add_processor_bundle (io->bundle ());
231                         used_io.insert (io);
232                 }
233         }
234 }
235
236
237 /** Gather bundles from around the system and put them in this PortGroupList */
238 void
239 PortGroupList::gather (ARDOUR::Session& session, bool inputs, bool allow_dups)
240 {
241         clear ();
242
243         boost::shared_ptr<PortGroup> bus (new PortGroup (_("Bus")));
244         boost::shared_ptr<PortGroup> track (new PortGroup (_("Track")));
245         boost::shared_ptr<PortGroup> system (new PortGroup (_("System")));
246         boost::shared_ptr<PortGroup> ardour (new PortGroup (_("Ardour")));
247         boost::shared_ptr<PortGroup> other (new PortGroup (_("Other")));
248
249         /* Find the bundles for routes.  We use the RouteBundle class to join
250            the route's input/output and processor bundles together so that they
251            are presented as one bundle in the matrix. */
252
253         boost::shared_ptr<RouteList> routes = session.get_routes ();
254
255         for (RouteList::const_iterator i = routes->begin(); i != routes->end(); ++i) {
256
257                 /* keep track of IOs that we have taken bundles from, so that maybe_add_processor... below
258                    can avoid taking the same IO from both Route::output() and the main_outs Delivery */
259
260                 set<boost::shared_ptr<IO> > used_io;
261                 boost::shared_ptr<IO> io = inputs ? (*i)->input() : (*i)->output();
262                 used_io.insert (io);
263
264                 boost::shared_ptr<RouteBundle> rb (new RouteBundle (io->bundle()));
265
266                 (*i)->foreach_processor (bind (mem_fun (*this, &PortGroupList::maybe_add_processor_to_bundle), rb, inputs, used_io));
267
268                 /* Work out which group to put this bundle in */
269                 boost::shared_ptr<PortGroup> g;
270                 if (_type == DataType::AUDIO) {
271
272                         if (boost::dynamic_pointer_cast<AudioTrack> (*i)) {
273                                 g = track;
274                         } else if (!boost::dynamic_pointer_cast<MidiTrack>(*i)) {
275                                 g = bus;
276                         }
277
278
279                 } else if (_type == DataType::MIDI) {
280
281                         if (boost::dynamic_pointer_cast<MidiTrack> (*i)) {
282                                 g = track;
283                         }
284
285                         /* No MIDI busses yet */
286                 }
287
288                 if (g) {
289
290                         TimeAxisView* tv = PublicEditor::instance().axis_view_from_route (i->get());
291                         if (tv) {
292                                 g->add_bundle (rb, io, tv->color ());
293                         } else {
294                                 g->add_bundle (rb, io);
295                         }
296                 }
297         }
298
299         /* Bundles owned by the session; add user bundles first, then normal ones, so
300            that UserBundles that offer the same ports as a normal bundle get priority
301         */
302
303         boost::shared_ptr<BundleList> b = session.bundles ();
304
305         for (BundleList::iterator i = b->begin(); i != b->end(); ++i) {
306                 if (boost::dynamic_pointer_cast<UserBundle> (*i) && (*i)->ports_are_inputs() == inputs && (*i)->type() == _type) {
307                         system->add_bundle (*i, allow_dups);
308                 }
309         }
310
311         for (BundleList::iterator i = b->begin(); i != b->end(); ++i) {
312                 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0 && (*i)->ports_are_inputs() == inputs && (*i)->type() == _type) {
313                         system->add_bundle (*i, allow_dups);
314                 }
315         }
316         
317         /* Ardour stuff */
318
319         if (!inputs && _type == DataType::AUDIO) {
320                 ardour->add_bundle (session.the_auditioner()->output()->bundle());
321                 ardour->add_bundle (session.click_io()->bundle());
322         }
323
324         /* Now find all other ports that we haven't thought of yet */
325
326         std::vector<std::string> extra_system;
327         std::vector<std::string> extra_other;
328
329         const char **ports = session.engine().get_ports ("", _type.to_jack_type(), inputs ?
330                                                          JackPortIsInput : JackPortIsOutput);
331         if (ports) {
332
333                 int n = 0;
334                 string client_matching_string;
335
336                 client_matching_string = session.engine().client_name();
337                 client_matching_string += ':';
338
339                 while (ports[n]) {
340
341                         std::string const p = ports[n];
342
343                         if (!system->has_port(p) && !bus->has_port(p) && !track->has_port(p) && !ardour->has_port(p) && !other->has_port(p)) {
344
345                                 if (port_has_prefix (p, "system:") ||
346                                     port_has_prefix (p, "alsa_pcm") ||
347                                     port_has_prefix (p, "ardour:")) {
348                                         extra_system.push_back (p);
349                                 } else {
350                                         extra_other.push_back (p);
351                                 }
352                         }
353
354                         ++n;
355                 }
356
357                 free (ports);
358         }
359
360         if (!extra_system.empty()) {
361                 system->add_bundle (make_bundle_from_ports (extra_system, inputs));
362         }
363
364         if (!extra_other.empty()) {
365                 other->add_bundle (make_bundle_from_ports (extra_other, inputs));
366         }
367
368         add_group (system);
369         add_group (bus);
370         add_group (track);
371         add_group (ardour);
372         add_group (other);
373
374         emit_changed ();
375 }
376
377 boost::shared_ptr<Bundle>
378 PortGroupList::make_bundle_from_ports (std::vector<std::string> const & p, bool inputs) const
379 {
380         boost::shared_ptr<Bundle> b (new Bundle ("", _type, inputs));
381
382         std::string const pre = common_prefix (p);
383         if (!pre.empty()) {
384                 b->set_name (pre.substr (0, pre.length() - 1));
385         }
386
387         for (uint32_t j = 0; j < p.size(); ++j) {
388                 b->add_channel (p[j].substr (pre.length()));
389                 b->set_port (j, p[j]);
390         }
391
392         return b;
393 }
394
395 bool
396 PortGroupList::port_has_prefix (const std::string& n, const std::string& p) const
397 {
398         return n.substr (0, p.length()) == p;
399 }
400
401 std::string
402 PortGroupList::common_prefix_before (std::vector<std::string> const & p, std::string const & s) const
403 {
404         /* we must have some strings and the first must contain the separator string */
405         if (p.empty() || p[0].find_first_of (s) == std::string::npos) {
406                 return "";
407         }
408
409         /* prefix of the first string */
410         std::string const fp = p[0].substr (0, p[0].find_first_of (s) + 1);
411
412         /* see if the other strings also start with fp */
413         uint32_t j = 1;
414         while (j < p.size()) {
415                 if (p[j].substr (0, fp.length()) != fp) {
416                         break;
417                 }
418                 ++j;
419         }
420
421         if (j != p.size()) {
422                 return "";
423         }
424
425         return fp;
426 }
427
428
429 std::string
430 PortGroupList::common_prefix (std::vector<std::string> const & p) const
431 {
432         /* common prefix before '/' ? */
433         std::string cp = common_prefix_before (p, "/");
434         if (!cp.empty()) {
435                 return cp;
436         }
437
438         cp = common_prefix_before (p, ":");
439         if (!cp.empty()) {
440                 return cp;
441         }
442
443         return "";
444 }
445
446 void
447 PortGroupList::clear ()
448 {
449         _groups.clear ();
450
451         for (std::vector<sigc::connection>::iterator i = _bundle_changed_connections.begin(); i != _bundle_changed_connections.end(); ++i) {
452                 i->disconnect ();
453         }
454
455         _bundle_changed_connections.clear ();
456
457         emit_changed ();
458 }
459
460
461 PortGroup::BundleList const &
462 PortGroupList::bundles () const
463 {
464         _bundles.clear ();
465
466         for (PortGroupList::List::const_iterator i = begin (); i != end (); ++i) {
467                 std::copy ((*i)->bundles().begin(), (*i)->bundles().end(), std::back_inserter (_bundles));
468         }
469
470         return _bundles;
471 }
472
473 uint32_t
474 PortGroupList::total_visible_channels () const
475 {
476         uint32_t n = 0;
477
478         for (PortGroupList::List::const_iterator i = begin(); i != end(); ++i) {
479                 if ((*i)->visible()) {
480                         n += (*i)->total_channels ();
481                 }
482         }
483
484         return n;
485 }
486
487
488 void
489 PortGroupList::add_group (boost::shared_ptr<PortGroup> g)
490 {
491         _groups.push_back (g);
492
493         g->Changed.connect (sigc::mem_fun (*this, &PortGroupList::emit_changed));
494
495         _bundle_changed_connections.push_back (
496                 g->BundleChanged.connect (sigc::mem_fun (*this, &PortGroupList::emit_bundle_changed))
497                 );
498
499         emit_changed ();
500 }
501
502 void
503 PortGroupList::remove_bundle (boost::shared_ptr<Bundle> b)
504 {
505         for (List::iterator i = _groups.begin(); i != _groups.end(); ++i) {
506                 (*i)->remove_bundle (b);
507         }
508
509         emit_changed ();
510 }
511
512 void
513 PortGroupList::emit_changed ()
514 {
515         if (_signals_suspended) {
516                 _pending_change = true;
517         } else {
518                 Changed ();
519         }
520 }
521
522 void
523 PortGroupList::emit_bundle_changed (Bundle::Change c)
524 {
525         if (_signals_suspended) {
526                 _pending_bundle_change = c;
527         } else {
528                 BundleChanged (c);
529         }
530 }
531 void
532 PortGroupList::suspend_signals ()
533 {
534         _signals_suspended = true;
535 }
536
537 void
538 PortGroupList::resume_signals ()
539 {
540         if (_pending_change) {
541                 Changed ();
542                 _pending_change = false;
543         }
544
545         if (_pending_bundle_change != 0) {
546                 BundleChanged (_pending_bundle_change);
547                 _pending_bundle_change = (ARDOUR::Bundle::Change) 0;
548         }
549
550         _signals_suspended = false;
551 }
552
553 boost::shared_ptr<IO>
554 PortGroupList::io_from_bundle (boost::shared_ptr<ARDOUR::Bundle> b) const
555 {
556         List::const_iterator i = _groups.begin ();
557         while (i != _groups.end()) {
558                 boost::shared_ptr<IO> io = (*i)->io_from_bundle (b);
559                 if (io) {
560                         return io;
561                 }
562                 ++i;
563         }
564
565         return boost::shared_ptr<IO> ();
566 }
567
568 bool
569 PortGroupList::empty () const
570 {
571         List::const_iterator i = _groups.begin ();
572         while (i != _groups.end() && (*i)->total_channels() == 0) {
573                 ++i;
574         }
575
576         return (i == _groups.end());
577 }
578
579
580 RouteBundle::RouteBundle (boost::shared_ptr<Bundle> r)
581         : _route (r)
582 {
583         _route->Changed.connect (sigc::hide (sigc::mem_fun (*this, &RouteBundle::reread_component_bundles)));
584         reread_component_bundles ();
585 }
586
587 void
588 RouteBundle::reread_component_bundles ()
589 {
590         suspend_signals ();
591
592         remove_channels ();
593
594         set_name (_route->name());
595
596         for (uint32_t i = 0; i < _route->nchannels(); ++i) {
597                 add_channel (_route->channel_name (i));
598                 PortList const & pl = _route->channel_ports (i);
599                 for (uint32_t j = 0; j < pl.size(); ++j) {
600                         add_port_to_channel (i, pl[j]);
601                 }
602         }
603
604         for (std::vector<boost::shared_ptr<Bundle> >::iterator i = _processor.begin(); i != _processor.end(); ++i) {
605                 add_channels_from_bundle (*i);
606         }
607
608         resume_signals ();
609 }
610
611 void
612 RouteBundle::add_processor_bundle (boost::shared_ptr<Bundle> p)
613 {
614         p->Changed.connect (sigc::hide (sigc::mem_fun (*this, &RouteBundle::reread_component_bundles)));
615         _processor.push_back (p);
616
617         reread_component_bundles ();
618 }
619