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