More port matrix re-working. Global matrix now has separate visibility buttons
[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 "port_group.h"
21 #include "port_matrix.h"
22 #include "i18n.h"
23 #include "ardour/session.h"
24 #include "ardour/audio_track.h"
25 #include "ardour/midi_track.h"
26 #include "ardour/audioengine.h"
27 #include "ardour/port.h"
28 #include "ardour/bundle.h"
29 #include <boost/shared_ptr.hpp>
30 #include <cstring>
31
32 using namespace std;
33 using namespace Gtk;
34
35 /** Add a bundle to a group.
36  *  @param b Bundle.
37  */
38 void
39 PortGroup::add_bundle (boost::shared_ptr<ARDOUR::Bundle> b)
40 {
41         assert (b.get());
42         _bundles.push_back (b);
43
44         Modified ();
45 }
46
47 /** Add a port to a group.
48  *  @param p Port.
49  */
50 void
51 PortGroup::add_port (std::string const &p)
52 {
53         ports.push_back (p);
54
55         Modified ();
56 }
57
58 void
59 PortGroup::clear ()
60 {
61         _bundles.clear ();
62         ports.clear ();
63
64         Modified ();
65 }
66
67 bool
68 PortGroup::has_port (std::string const& p) const
69 {
70         for (ARDOUR::BundleList::const_iterator i = _bundles.begin(); i != _bundles.end(); ++i) {
71                 if ((*i)->offers_port_alone (p)) {
72                         return true;
73                 }
74         }
75
76         for (vector<std::string>::const_iterator i = ports.begin(); i != ports.end(); ++i) {
77                 if (*i == p) {
78                         return true;
79                 }
80         }
81
82         return false;
83 }
84
85 boost::shared_ptr<ARDOUR::Bundle>
86 PortGroup::only_bundle ()
87 {
88         assert (_bundles.size() == 1);
89         return _bundles.front();
90 }
91
92
93 uint32_t
94 PortGroup::total_ports () const
95 {
96         uint32_t n = 0;
97         for (ARDOUR::BundleList::const_iterator i = _bundles.begin(); i != _bundles.end(); ++i) {
98                 n += (*i)->nchannels ();
99         }
100
101         n += ports.size();
102
103         return n;
104 }
105
106         
107 /** PortGroupList constructor.
108  */
109
110 PortGroupList::PortGroupList ()
111         : _type (ARDOUR::DataType::AUDIO), _bundles_dirty (true)
112 {
113         
114 }
115
116 void
117 PortGroupList::set_type (ARDOUR::DataType t)
118 {
119         _type = t;
120         clear ();
121 }
122
123 /** Gather bundles from around the system and put them in this PortGroupList */
124 void
125 PortGroupList::gather (ARDOUR::Session& session, bool inputs)
126 {
127         clear ();
128
129         boost::shared_ptr<PortGroup> buss (new PortGroup (_("Buss")));
130         boost::shared_ptr<PortGroup> track (new PortGroup (_("Track")));
131         boost::shared_ptr<PortGroup> system (new PortGroup (_("System")));
132         boost::shared_ptr<PortGroup> other (new PortGroup (_("Other")));
133
134         /* Find the bundles for routes.  We take their bundles, copy them,
135            and add ports from the route's processors */
136
137         boost::shared_ptr<ARDOUR::RouteList> routes = session.get_routes ();
138
139         for (ARDOUR::RouteList::const_iterator i = routes->begin(); i != routes->end(); ++i) {
140                 /* Copy the appropriate bundle from the route */
141                 boost::shared_ptr<ARDOUR::Bundle> bundle (
142                         new ARDOUR::Bundle (
143                                 inputs ? (*i)->bundle_for_inputs() : (*i)->bundle_for_outputs ()
144                                 )
145                         );
146
147                 /* Add ports from the route's processors */
148                 uint32_t n = 0;
149                 while (1) {
150                         boost::shared_ptr<ARDOUR::Processor> p = (*i)->nth_processor (n);
151                         if (p == 0) {
152                                 break;
153                         }
154
155                         boost::shared_ptr<ARDOUR::IOProcessor> iop = boost::dynamic_pointer_cast<ARDOUR::IOProcessor> (p);
156
157                         if (iop) {
158                                 boost::shared_ptr<ARDOUR::Bundle> pb = inputs ?
159                                         iop->io()->bundle_for_inputs() : iop->io()->bundle_for_outputs();
160                                 bundle->add_channels_from_bundle (pb);
161                         }
162
163                         ++n;
164                 }
165                         
166                 /* Work out which group to put this bundle in */
167                 boost::shared_ptr<PortGroup> g;
168                 if (_type == ARDOUR::DataType::AUDIO) {
169
170                         if (boost::dynamic_pointer_cast<ARDOUR::AudioTrack> (*i)) {
171                                 g = track;
172                         } else if (!boost::dynamic_pointer_cast<ARDOUR::MidiTrack>(*i)) {
173                                 g = buss;
174                         } 
175
176
177                 } else if (_type == ARDOUR::DataType::MIDI) {
178
179                         if (boost::dynamic_pointer_cast<ARDOUR::MidiTrack> (*i)) {
180                                 g = track;
181                         }
182
183                         /* No MIDI busses yet */
184                 } 
185                         
186                 if (g) {
187                         g->add_bundle (bundle);
188                 }
189         }
190
191         /* Bundles created by the session */
192         
193         boost::shared_ptr<ARDOUR::BundleList> b = session.bundles ();
194         for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
195                 if ((*i)->ports_are_inputs() == inputs && (*i)->type() == _type) {
196                         system->add_bundle (*i);
197                 }
198         }
199
200         /* Now find all other ports that we haven't thought of yet */
201
202         const char **ports = session.engine().get_ports ("", _type.to_jack_type(), inputs ? 
203                                                           JackPortIsInput : JackPortIsOutput);
204         if (ports) {
205
206                 int n = 0;
207                 string client_matching_string;
208
209                 client_matching_string = session.engine().client_name();
210                 client_matching_string += ':';
211
212                 while (ports[n]) {
213                         
214                         std::string const p = ports[n];
215
216                         if (!system->has_port(p) && !buss->has_port(p) && !track->has_port(p) && !other->has_port(p)) {
217                                 
218                                 if (port_has_prefix (p, "system:") ||
219                                     port_has_prefix (p, "alsa_pcm") ||
220                                     port_has_prefix (p, "ardour:")) {
221                                         system->add_port (p);
222                                 } else {
223                                         other->add_port (p);
224                                 }
225                         }
226                         
227                         ++n;
228                 }
229
230                 free (ports);
231         }
232
233         add_group (system);
234         add_group (buss);
235         add_group (track);
236         add_group (other);
237
238         _bundles_dirty = true;
239 }
240
241 bool
242 PortGroupList::port_has_prefix (const std::string& n, const std::string& p) const
243 {
244         return n.substr (0, p.length()) == p;
245 }
246         
247
248 void
249 PortGroupList::update_bundles () const
250 {
251         _bundles.clear ();
252                 
253         for (PortGroupList::List::const_iterator i = begin (); i != end (); ++i) {
254                 if ((*i)->visible()) {
255                         
256                         std::copy ((*i)->bundles().begin(), (*i)->bundles().end(), std::back_inserter (_bundles));
257
258                         /* make a bundle for the ports, if there are any */
259                         if (!(*i)->ports.empty()) {
260
261                                 boost::shared_ptr<ARDOUR::Bundle> b (new ARDOUR::Bundle ("", _type, !_offer_inputs));
262                                 
263                                 std::string const pre = common_prefix ((*i)->ports);
264                                 if (!pre.empty()) {
265                                         b->set_name (pre.substr (0, pre.length() - 1));
266                                 }
267
268                                 for (uint32_t j = 0; j < (*i)->ports.size(); ++j) {
269                                         std::string const p = (*i)->ports[j];
270                                         b->add_channel (p.substr (pre.length()));
271                                         b->set_port (j, p);
272                                 }
273                                         
274                                 _bundles.push_back (b);
275                         }
276                 }
277         }
278
279         _bundles_dirty = false;
280 }
281
282 std::string
283 PortGroupList::common_prefix (std::vector<std::string> const & p) const
284 {
285         /* common prefix before '/' ? */
286         if (p[0].find_first_of ("/") != std::string::npos) {
287                 std::string const fp = p[0].substr (0, (p[0].find_first_of ("/") + 1));
288                 uint32_t j = 1;
289                 while (j < p.size()) {
290                         if (p[j].substr (0, fp.length()) != fp) {
291                                 break;
292                         }
293                         ++j;
294                 }
295                 
296                 if (j == p.size()) {
297                         return fp;
298                 }
299         }
300         
301         /* or before ':' ? */
302         if (p[0].find_first_of (":") != std::string::npos) {
303                 std::string const fp = p[0].substr (0, (p[0].find_first_of (":") + 1));
304                 uint32_t j = 1;
305                 while (j < p.size()) {
306                         if (p[j].substr (0, fp.length()) != fp) {
307                                 break;
308                         }
309                         ++j;
310                 }
311                 
312                 if (j == p.size()) {
313                         return fp;
314                 }
315         }
316
317         return "";
318 }
319
320 void
321 PortGroupList::clear ()
322 {
323         _groups.clear ();
324         _bundles_dirty = true;
325 }
326
327 ARDOUR::BundleList const &
328 PortGroupList::bundles () const
329 {
330         if (_bundles_dirty) {
331                 update_bundles ();
332         }
333
334         return _bundles;
335 }
336
337 uint32_t
338 PortGroupList::total_visible_ports () const
339 {
340         uint32_t n = 0;
341         
342         for (PortGroupList::List::const_iterator i = begin(); i != end(); ++i) {
343                 if ((*i)->visible()) {
344                         n += (*i)->total_ports ();
345                 }
346         }
347
348         return n;
349 }
350
351 void
352 PortGroupList::group_modified ()
353 {
354         _bundles_dirty = true;
355 }
356
357 void
358 PortGroupList::add_group (boost::shared_ptr<PortGroup> g)
359 {
360         _groups.push_back (g);
361         g->Modified.connect (sigc::mem_fun (*this, &PortGroupList::group_modified));
362         _bundles_dirty = true;
363 }