More port matrix re-working. Global matrix now has separate visibility buttons
[ardour.git] / gtk2_ardour / io_selector.cc
1 /*
2     Copyright (C) 2002-2007 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 <gtkmm/messagedialog.h>
21 #include <glibmm/objectbase.h>
22
23 #include <gtkmm2ext/doi.h>
24
25 #include <ardour/port_insert.h>
26 #include "ardour/session.h"
27 #include "ardour/io.h"
28 #include "ardour/audioengine.h"
29 #include "ardour/track.h"
30 #include "ardour/audio_track.h"
31 #include "ardour/midi_track.h"
32 #include "ardour/data_type.h"
33 #include "ardour/port.h"
34 #include "ardour/bundle.h"
35
36 #include "io_selector.h"
37 #include "utils.h"
38 #include "gui_thread.h"
39 #include "i18n.h"
40
41 using namespace ARDOUR;
42 using namespace Gtk;
43
44 IOSelector::IOSelector (ARDOUR::Session& session, boost::shared_ptr<ARDOUR::IO> io, bool in)
45         : PortMatrix (session, io->default_type())
46         , _io (io)
47         , _find_inputs_for_io_outputs (in)
48 {
49         /* Listen for ports changing on the IO */
50         _io->PortCountChanged.connect (sigc::hide (mem_fun (*this, &IOSelector::ports_changed)));
51
52         /* signal flow from 0 to 1 */
53         if (_find_inputs_for_io_outputs) {
54                 _other = 1;
55                 _ours = 0;
56         } else {
57                 _other = 0;
58                 _ours = 1;
59         }
60
61         _port_group = boost::shared_ptr<PortGroup> (new PortGroup (""));
62         _ports[_ours].add_group (_port_group);
63         
64         setup ();
65 }
66
67 void
68 IOSelector::setup ()
69 {
70         _ports[_other].gather (_session, _find_inputs_for_io_outputs);
71                 
72         _port_group->clear ();
73         _port_group->add_bundle (boost::shared_ptr<ARDOUR::Bundle> (new ARDOUR::Bundle));
74         _port_group->only_bundle()->set_name (_io->name());
75
76         if (_find_inputs_for_io_outputs) {
77                 const PortSet& ps (_io->outputs());
78
79                 int j = 0;
80                 for (PortSet::const_iterator i = ps.begin(); i != ps.end(); ++i) {
81                         char buf[32];
82                         snprintf (buf, sizeof(buf), _("out %d"), j + 1);
83                         _port_group->only_bundle()->add_channel (buf);
84                         _port_group->only_bundle()->add_port_to_channel (j, _session.engine().make_port_name_non_relative (i->name()));
85                         ++j;
86                 }
87                 
88         } else {
89                 
90                 const PortSet& ps (_io->inputs());
91
92                 int j = 0;
93                 for (PortSet::const_iterator i = ps.begin(); i != ps.end(); ++i) {
94                         char buf[32];
95                         snprintf (buf, sizeof(buf), _("in %d"), j + 1);
96                         _port_group->only_bundle()->add_channel (buf);
97                         _port_group->only_bundle()->add_port_to_channel (j, _session.engine().make_port_name_non_relative (i->name()));
98                         ++j;
99                 }
100
101         }
102
103         PortMatrix::setup ();
104 }
105
106 void
107 IOSelector::ports_changed ()
108 {
109         ENSURE_GUI_THREAD (mem_fun (*this, &IOSelector::ports_changed));
110
111         setup ();
112 }
113
114 void
115 IOSelector::set_state (ARDOUR::BundleChannel c[2], bool s)
116 {
117         ARDOUR::Bundle::PortList const & our_ports = c[_ours].bundle->channel_ports (c[_ours].channel);
118         ARDOUR::Bundle::PortList const & other_ports = c[_other].bundle->channel_ports (c[_other].channel);
119
120         for (ARDOUR::Bundle::PortList::const_iterator i = our_ports.begin(); i != our_ports.end(); ++i) {
121                 for (ARDOUR::Bundle::PortList::const_iterator j = other_ports.begin(); j != other_ports.end(); ++j) {
122
123                         Port* f = _session.engine().get_port_by_name (*i);
124                         if (!f) {
125                                 return;
126                         }
127
128                         if (s) {
129                                 if (!_find_inputs_for_io_outputs) {
130                                         _io->connect_input (f, *j, 0);
131                                 } else {
132                                         _io->connect_output (f, *j, 0);
133                                 }
134                         } else {
135                                 if (!_find_inputs_for_io_outputs) {
136                                         _io->disconnect_input (f, *j, 0);
137                                 } else {
138                                         _io->disconnect_output (f, *j, 0);
139                                 }
140                         }
141                 }
142         }
143 }
144
145 PortMatrix::State
146 IOSelector::get_state (ARDOUR::BundleChannel c[2]) const
147 {
148         ARDOUR::Bundle::PortList const & our_ports = c[_ours].bundle->channel_ports (c[_ours].channel);
149         ARDOUR::Bundle::PortList const & other_ports = c[_other].bundle->channel_ports (c[_other].channel);
150
151         for (ARDOUR::Bundle::PortList::const_iterator i = our_ports.begin(); i != our_ports.end(); ++i) {
152                 for (ARDOUR::Bundle::PortList::const_iterator j = other_ports.begin(); j != other_ports.end(); ++j) {
153
154                         Port* f = _session.engine().get_port_by_name (*i);
155
156                         /* since we are talking about an IO, our ports should all have an associated Port *,
157                            so the above call should never fail */
158                         assert (f);
159                         
160                         if (!f->connected_to (*j)) {
161                                 /* if any one thing is not connected, all bets are off */
162                                 return NOT_ASSOCIATED;
163                         }
164                 }
165         }
166
167         return ASSOCIATED;
168 }
169
170 uint32_t
171 IOSelector::n_io_ports () const
172 {
173         if (!_find_inputs_for_io_outputs) {
174                 return _io->inputs().num_ports (_io->default_type());
175         } else {
176                 return _io->outputs().num_ports (_io->default_type());
177         }
178 }
179
180 uint32_t
181 IOSelector::maximum_io_ports () const
182 {
183         if (!_find_inputs_for_io_outputs) {
184                 return _io->input_maximum ().get (_io->default_type());
185         } else {
186                 return _io->output_maximum ().get (_io->default_type());
187         }
188 }
189
190
191 uint32_t
192 IOSelector::minimum_io_ports () const
193 {
194         if (!_find_inputs_for_io_outputs) {
195                 return _io->input_minimum ().get (_io->default_type());
196         } else {
197                 return _io->output_minimum ().get (_io->default_type());
198         }
199 }
200
201 void
202 IOSelector::add_channel (boost::shared_ptr<ARDOUR::Bundle> b)
203 {
204         /* we ignore the bundle parameter, as we know what it is that we're adding to */
205         
206         // The IO selector only works for single typed IOs
207         const ARDOUR::DataType t = _io->default_type ();
208
209         if (!_find_inputs_for_io_outputs) {
210
211                 try {
212                         _io->add_input_port ("", this);
213                 }
214
215                 catch (AudioEngine::PortRegistrationFailure& err) {
216                         MessageDialog msg (_("There are no more JACK ports available."));
217                         msg.run ();
218                 }
219
220         } else {
221
222                 try {
223                         _io->add_output_port ("", this);
224                 }
225
226                 catch (AudioEngine::PortRegistrationFailure& err) {
227                         MessageDialog msg (_("There are no more JACK ports available."));
228                         msg.run ();
229                 }
230         }
231 }
232
233 void
234 IOSelector::remove_channel (ARDOUR::BundleChannel bc)
235 {
236         Port* f = _session.engine().get_port_by_name (bc.bundle->channel_ports(bc.channel)[0]);
237         if (!f) {
238                 return;
239         }
240         
241         if (_find_inputs_for_io_outputs) {
242                 _io->remove_output_port (f, this);
243         } else {
244                 _io->remove_input_port (f, this);
245         }
246 }
247
248 IOSelectorWindow::IOSelectorWindow (ARDOUR::Session& session, boost::shared_ptr<ARDOUR::IO> io, bool for_input, bool can_cancel)
249         : ArdourDialog ("I/O selector")
250         , _selector (session, io, !for_input)
251         , add_button (_("Add Port"))
252         , disconnect_button (_("Disconnect All"))
253         , ok_button (can_cancel ? _("OK"): _("Close"))
254         , cancel_button (_("Cancel"))
255         , rescan_button (_("Rescan"))
256
257 {
258         /* XXX: what's this for? */
259         add_events (Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK);
260         
261         set_name ("IOSelectorWindow2");
262
263         /* Disconnect All button */
264         disconnect_button.set_name ("IOSelectorButton");
265         disconnect_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::DISCONNECT, Gtk::ICON_SIZE_BUTTON)));
266         disconnect_button.signal_clicked().connect (sigc::mem_fun (_selector, &IOSelector::disassociate_all));
267         get_action_area()->pack_start (disconnect_button, false, false);
268
269         /* Add Port button */
270         if (_selector.maximum_io_ports() > _selector.n_io_ports()) {
271                 add_button.set_name ("IOSelectorButton");
272                 add_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::ADD, Gtk::ICON_SIZE_BUTTON)));
273                 get_action_area()->pack_start (add_button, false, false);
274                 add_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (_selector, &IOSelector::add_channel), boost::shared_ptr<Bundle> ()));
275         } 
276
277         /* Rescan button */
278         rescan_button.set_name ("IOSelectorButton");
279         rescan_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::REFRESH, Gtk::ICON_SIZE_BUTTON)));
280         rescan_button.signal_clicked().connect (sigc::mem_fun (_selector, &IOSelector::setup));
281         get_action_area()->pack_start (rescan_button, false, false);
282
283         io->PortCountChanged.connect (sigc::hide (mem_fun (*this, &IOSelectorWindow::ports_changed)));
284
285         /* Cancel button */
286         if (can_cancel) {
287                 cancel_button.set_name ("IOSelectorButton");
288                 cancel_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::CANCEL, Gtk::ICON_SIZE_BUTTON)));
289                 get_action_area()->pack_start (cancel_button, false, false);
290         } else {
291                 cancel_button.hide();
292         }
293         cancel_button.signal_clicked().connect (mem_fun(*this, &IOSelectorWindow::cancel));
294
295         /* OK button */
296         ok_button.set_name ("IOSelectorButton");
297         if (!can_cancel) {
298                 ok_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::CLOSE, Gtk::ICON_SIZE_BUTTON)));
299         }
300         ok_button.signal_clicked().connect (mem_fun(*this, &IOSelectorWindow::accept));
301         get_action_area()->pack_start (ok_button, false, false);
302
303         get_vbox()->set_spacing (8);
304
305         get_vbox()->pack_start (_selector, true, true);
306
307         set_position (Gtk::WIN_POS_MOUSE);
308
309         io_name_changed (this);
310         ports_changed ();
311
312         show_all ();
313
314         signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), this));
315 }
316
317 void
318 IOSelectorWindow::ports_changed ()
319 {
320         if (_selector.maximum_io_ports() > _selector.n_io_ports()) {
321                 add_button.set_sensitive (true);
322         } else {
323                 add_button.set_sensitive (false);
324         }
325 }
326
327 void
328 IOSelectorWindow::cancel ()
329 {
330         _selector.Finished (IOSelector::Cancelled);
331         hide ();
332 }
333
334 void
335 IOSelectorWindow::accept ()
336 {
337         _selector.Finished (IOSelector::Accepted);
338         hide ();
339 }
340
341 void
342 IOSelectorWindow::on_map ()
343 {
344         _selector.setup ();
345         Window::on_map ();
346 }
347
348 void
349 IOSelectorWindow::io_name_changed (void* src)
350 {
351         ENSURE_GUI_THREAD(bind (mem_fun(*this, &IOSelectorWindow::io_name_changed), src));
352         
353         string title;
354
355         if (!_selector.find_inputs_for_io_outputs()) {
356                 title = string_compose(_("%1 input"), _selector.io()->name());
357         } else {
358                 title = string_compose(_("%1 output"), _selector.io()->name());
359         }
360
361         set_title (title);
362 }
363
364 PortInsertUI::PortInsertUI (ARDOUR::Session& sess, boost::shared_ptr<ARDOUR::PortInsert> pi)
365         : input_selector (sess, pi->io(), true),
366           output_selector (sess, pi->io(), false)
367 {
368         pack_start (output_selector, true, true);
369         pack_start (input_selector, true, true);
370 }
371
372 void
373 PortInsertUI::redisplay ()
374 {
375         input_selector.setup ();
376         output_selector.setup ();
377 }
378
379 void
380 PortInsertUI::finished (IOSelector::Result r)
381 {
382         input_selector.Finished (r);
383         output_selector.Finished (r);
384 }
385
386
387 PortInsertWindow::PortInsertWindow (ARDOUR::Session& sess, boost::shared_ptr<ARDOUR::PortInsert> pi, bool can_cancel)
388         : ArdourDialog ("port insert dialog"),
389           _portinsertui (sess, pi),
390           ok_button (can_cancel ? _("OK"): _("Close")),
391           cancel_button (_("Cancel")),
392           rescan_button (_("Rescan"))
393 {
394
395         set_name ("IOSelectorWindow");
396         string title = _("ardour: ");
397         title += pi->name();
398         set_title (title);
399         
400         ok_button.set_name ("IOSelectorButton");
401         if (!can_cancel) {
402                 ok_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::CLOSE, Gtk::ICON_SIZE_BUTTON)));
403         }
404         cancel_button.set_name ("IOSelectorButton");
405         rescan_button.set_name ("IOSelectorButton");
406         rescan_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::REFRESH, Gtk::ICON_SIZE_BUTTON)));
407
408         get_action_area()->pack_start (rescan_button, false, false);
409         if (can_cancel) {
410                 cancel_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::CANCEL, Gtk::ICON_SIZE_BUTTON)));
411                 get_action_area()->pack_start (cancel_button, false, false);
412         } else {
413                 cancel_button.hide();
414         }
415         get_action_area()->pack_start (ok_button, false, false);
416
417         get_vbox()->pack_start (_portinsertui);
418
419         ok_button.signal_clicked().connect (mem_fun (*this, &PortInsertWindow::accept));
420         cancel_button.signal_clicked().connect (mem_fun (*this, &PortInsertWindow::cancel));
421
422         signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), reinterpret_cast<Window *> (this))); 
423
424         going_away_connection = pi->GoingAway.connect (mem_fun (*this, &PortInsertWindow::plugin_going_away));
425 }
426
427 void
428 PortInsertWindow::plugin_going_away ()
429 {
430         ENSURE_GUI_THREAD (mem_fun (*this, &PortInsertWindow::plugin_going_away));
431         
432         going_away_connection.disconnect ();
433         delete_when_idle (this);
434 }
435
436 void
437 PortInsertWindow::on_map ()
438 {
439         _portinsertui.redisplay ();
440         Window::on_map ();
441 }
442
443
444 void
445 PortInsertWindow::cancel ()
446 {
447         _portinsertui.finished (IOSelector::Cancelled);
448         hide ();
449 }
450
451 void
452 PortInsertWindow::accept ()
453 {
454         _portinsertui.finished (IOSelector::Accepted);
455         hide ();
456 }