Fix up menus (they broke when ardour.menus.in was copied over from 2.0-ongoing to...
[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 offer_inputs)
45         : PortMatrix (session, io->default_type(), offer_inputs,
46                       PortGroupList::Mask (PortGroupList::BUSS | 
47                                            PortGroupList::SYSTEM | 
48                                            PortGroupList::OTHER))
49         , _session (session)
50         , _io (io)
51 {
52         /* Listen for ports changing on the IO */
53         _io->PortCountChanged.connect (sigc::hide (mem_fun (*this, &IOSelector::ports_changed)));
54         
55         setup ();
56 }
57
58 void
59 IOSelector::setup ()
60 {
61         _our_bundle = boost::shared_ptr<ARDOUR::Bundle> (new ARDOUR::Bundle);
62         _our_bundle->set_name (_io->name());
63
64         if (offering_input ()) {
65                 const PortSet& ps (_io->outputs());
66
67                 int j = 0;
68                 for (PortSet::const_iterator i = ps.begin(); i != ps.end(); ++i) {
69                         char buf[32];
70                         snprintf (buf, sizeof(buf), _("out %d"), j + 1);
71                         _our_bundle->add_channel (buf);
72                         _our_bundle->add_port_to_channel (j, _session.engine().make_port_name_non_relative (i->name()));
73                         ++j;
74                 }
75                 
76         } else {
77                 
78                 const PortSet& ps (_io->inputs());
79
80                 int j = 0;
81                 for (PortSet::const_iterator i = ps.begin(); i != ps.end(); ++i) {
82                         char buf[32];
83                         snprintf (buf, sizeof(buf), _("in %d"), j + 1);
84                         _our_bundle->add_channel (buf);
85                         _our_bundle->add_port_to_channel (j, _session.engine().make_port_name_non_relative (i->name()));
86                         ++j;
87                 }
88
89         }
90
91         PortMatrix::setup ();
92 }
93
94 void
95 IOSelector::ports_changed ()
96 {
97         ENSURE_GUI_THREAD (mem_fun (*this, &IOSelector::ports_changed));
98
99         setup ();
100 }
101
102 void
103 IOSelector::set_state (
104         boost::shared_ptr<ARDOUR::Bundle> ab,
105         uint32_t ac,
106         boost::shared_ptr<ARDOUR::Bundle> bb,
107         uint32_t bc,
108         bool s,
109         uint32_t k
110         )
111 {
112         ARDOUR::Bundle::PortList const& our_ports = ab->channel_ports (ac);
113         ARDOUR::Bundle::PortList const& other_ports = bb->channel_ports (bc);
114
115         for (ARDOUR::Bundle::PortList::const_iterator i = our_ports.begin(); i != our_ports.end(); ++i) {
116                 for (ARDOUR::Bundle::PortList::const_iterator j = other_ports.begin(); j != other_ports.end(); ++j) {
117
118                         Port* f = _session.engine().get_port_by_name (*i);
119                         if (!f) {
120                                 return;
121                         }
122
123                         if (s) {
124                                 if (!offering_input()) {
125                                         _io->connect_input (f, *j, 0);
126                                 } else {
127                                         _io->connect_output (f, *j, 0);
128                                 }
129                         } else {
130                                 if (!offering_input()) {
131                                         _io->disconnect_input (f, *j, 0);
132                                 } else {
133                                         _io->disconnect_output (f, *j, 0);
134                                 }
135                         }
136                 }
137         }
138 }
139
140 bool
141 IOSelector::get_state (
142         boost::shared_ptr<ARDOUR::Bundle> ab,
143         uint32_t ac,
144         boost::shared_ptr<ARDOUR::Bundle> bb,
145         uint32_t bc
146         ) const
147 {
148         ARDOUR::Bundle::PortList const& our_ports = ab->channel_ports (ac);
149         ARDOUR::Bundle::PortList const& other_ports = bb->channel_ports (bc);
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 false;
163                         }
164                 }
165         }
166
167         return true;
168 }
169
170 uint32_t
171 IOSelector::n_rows () const
172 {
173         if (!offering_input()) {
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_rows () const
182 {
183         if (!offering_input()) {
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_rows () const
193 {
194         if (!offering_input()) {
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 (!offering_input()) {
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 (boost::shared_ptr<ARDOUR::Bundle> b, uint32_t c)
235 {
236         Port* f = _session.engine().get_port_by_name (b->channel_ports(c)[0]);
237         if (!f) {
238                 return;
239         }
240         
241         if (offering_input()) {
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_rows() > _selector.n_rows()) {
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         /* XXX: do we still need the ScrolledWindow? */
306         Gtk::ScrolledWindow* sel_scroll = Gtk::manage (new Gtk::ScrolledWindow);
307         sel_scroll->set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_NEVER);
308         sel_scroll->add (_selector);
309         get_vbox()->pack_start (*sel_scroll, true, true);
310
311         set_position (Gtk::WIN_POS_MOUSE);
312
313         io_name_changed (this);
314         ports_changed ();
315
316         show_all ();
317
318         signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), this));
319 }
320
321 IOSelectorWindow::~IOSelectorWindow()
322 {
323         
324 }
325
326 void
327 IOSelectorWindow::ports_changed ()
328 {
329         if (_selector.maximum_rows() > _selector.n_rows()) {
330                 add_button.set_sensitive (true);
331         } else {
332                 add_button.set_sensitive (false);
333         }
334 }
335
336 void
337 IOSelectorWindow::cancel ()
338 {
339         _selector.Finished (IOSelector::Cancelled);
340         hide ();
341 }
342
343 void
344 IOSelectorWindow::accept ()
345 {
346         _selector.Finished (IOSelector::Accepted);
347         hide ();
348 }
349
350 void
351 IOSelectorWindow::on_map ()
352 {
353         _selector.setup ();
354         Window::on_map ();
355 }
356
357 void
358 IOSelectorWindow::io_name_changed (void* src)
359 {
360         ENSURE_GUI_THREAD(bind (mem_fun(*this, &IOSelectorWindow::io_name_changed), src));
361         
362         string title;
363
364         if (!_selector.offering_input()) {
365                 title = string_compose(_("%1 input"), _selector.io()->name());
366         } else {
367                 title = string_compose(_("%1 output"), _selector.io()->name());
368         }
369
370         set_title (title);
371 }
372
373 PortInsertUI::PortInsertUI (ARDOUR::Session& sess, boost::shared_ptr<ARDOUR::PortInsert> pi)
374         : input_selector (sess, pi->io(), true),
375           output_selector (sess, pi->io(), false)
376 {
377         pack_start (output_selector, true, true);
378         pack_start (input_selector, true, true);
379 }
380
381 void
382 PortInsertUI::redisplay ()
383 {
384         input_selector.setup ();
385         output_selector.setup ();
386 }
387
388 void
389 PortInsertUI::finished (IOSelector::Result r)
390 {
391         input_selector.Finished (r);
392         output_selector.Finished (r);
393 }
394
395
396 PortInsertWindow::PortInsertWindow (ARDOUR::Session& sess, boost::shared_ptr<ARDOUR::PortInsert> pi, bool can_cancel)
397         : ArdourDialog ("port insert dialog"),
398           _portinsertui (sess, pi),
399           ok_button (can_cancel ? _("OK"): _("Close")),
400           cancel_button (_("Cancel")),
401           rescan_button (_("Rescan"))
402 {
403
404         set_name ("IOSelectorWindow");
405         string title = _("ardour: ");
406         title += pi->name();
407         set_title (title);
408         
409         ok_button.set_name ("IOSelectorButton");
410         if (!can_cancel) {
411                 ok_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::CLOSE, Gtk::ICON_SIZE_BUTTON)));
412         }
413         cancel_button.set_name ("IOSelectorButton");
414         rescan_button.set_name ("IOSelectorButton");
415         rescan_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::REFRESH, Gtk::ICON_SIZE_BUTTON)));
416
417         get_action_area()->pack_start (rescan_button, false, false);
418         if (can_cancel) {
419                 cancel_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::CANCEL, Gtk::ICON_SIZE_BUTTON)));
420                 get_action_area()->pack_start (cancel_button, false, false);
421         } else {
422                 cancel_button.hide();
423         }
424         get_action_area()->pack_start (ok_button, false, false);
425
426         get_vbox()->pack_start (_portinsertui);
427
428         ok_button.signal_clicked().connect (mem_fun (*this, &PortInsertWindow::accept));
429         cancel_button.signal_clicked().connect (mem_fun (*this, &PortInsertWindow::cancel));
430
431         signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), reinterpret_cast<Window *> (this))); 
432
433         going_away_connection = pi->GoingAway.connect (mem_fun (*this, &PortInsertWindow::plugin_going_away));
434 }
435
436 void
437 PortInsertWindow::plugin_going_away ()
438 {
439         ENSURE_GUI_THREAD (mem_fun (*this, &PortInsertWindow::plugin_going_away));
440         
441         going_away_connection.disconnect ();
442         delete_when_idle (this);
443 }
444
445 void
446 PortInsertWindow::on_map ()
447 {
448         _portinsertui.redisplay ();
449         Window::on_map ();
450 }
451
452
453 void
454 PortInsertWindow::cancel ()
455 {
456         _portinsertui.finished (IOSelector::Cancelled);
457         hide ();
458 }
459
460 void
461 PortInsertWindow::accept ()
462 {
463         _portinsertui.finished (IOSelector::Accepted);
464         hide ();
465 }