Remove now-deprecated non-zero page size in Adjustments used for SpinButtons;
[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, 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, 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                         if (!f) {
156                                 return false;
157                         }
158
159                         if (!f->connected_to (*j)) {
160                                 /* if any one thing is not connected, all bets are off */
161                                 return false;
162                         }
163                 }
164         }
165
166         return true;
167 }
168
169 uint32_t
170 IOSelector::n_rows () const
171 {
172         if (!offering_input()) {
173                 return _io->inputs().num_ports (_io->default_type());
174         } else {
175                 return _io->outputs().num_ports (_io->default_type());
176         }
177 }
178
179 uint32_t
180 IOSelector::maximum_rows () const
181 {
182         if (!offering_input()) {
183                 return _io->input_maximum ().get (_io->default_type());
184         } else {
185                 return _io->output_maximum ().get (_io->default_type());
186         }
187 }
188
189
190 uint32_t
191 IOSelector::minimum_rows () const
192 {
193         if (!offering_input()) {
194                 return _io->input_minimum ().get (_io->default_type());
195         } else {
196                 return _io->output_minimum ().get (_io->default_type());
197         }
198 }
199
200 void
201 IOSelector::add_channel (boost::shared_ptr<ARDOUR::Bundle> b)
202 {
203         /* we ignore the bundle parameter, as we know what it is that we're adding to */
204         
205         // The IO selector only works for single typed IOs
206         const ARDOUR::DataType t = _io->default_type ();
207
208         if (!offering_input()) {
209
210                 try {
211                         _io->add_input_port ("", this);
212                 }
213
214                 catch (AudioEngine::PortRegistrationFailure& err) {
215                         MessageDialog msg (_("There are no more JACK ports available."));
216                         msg.run ();
217                 }
218
219         } else {
220
221                 try {
222                         _io->add_output_port ("", this);
223                 }
224
225                 catch (AudioEngine::PortRegistrationFailure& err) {
226                         MessageDialog msg (_("There are no more JACK ports available."));
227                         msg.run ();
228                 }
229         }
230 }
231
232 void
233 IOSelector::remove_channel (boost::shared_ptr<ARDOUR::Bundle> b, uint32_t c)
234 {
235         Port* f = _session.engine().get_port_by_name (b->channel_ports(c)[0]);
236         if (!f) {
237                 return;
238         }
239         
240         if (offering_input()) {
241                 _io->remove_output_port (f, this);
242         } else {
243                 _io->remove_input_port (f, this);
244         }
245 }
246
247 IOSelectorWindow::IOSelectorWindow (ARDOUR::Session& session, boost::shared_ptr<ARDOUR::IO> io, bool for_input, bool can_cancel)
248         : ArdourDialog ("I/O selector")
249         , _selector (session, io, !for_input)
250         , add_button (_("Add Port"))
251         , disconnect_button (_("Disconnect All"))
252         , ok_button (can_cancel ? _("OK"): _("Close"))
253         , cancel_button (_("Cancel"))
254         , rescan_button (_("Rescan"))
255
256 {
257         /* XXX: what's this for? */
258         add_events (Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK);
259         
260         set_name ("IOSelectorWindow2");
261
262         /* Disconnect All button */
263         disconnect_button.set_name ("IOSelectorButton");
264         disconnect_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::DISCONNECT, Gtk::ICON_SIZE_BUTTON)));
265         disconnect_button.signal_clicked().connect (sigc::mem_fun (_selector, &IOSelector::disassociate_all));
266         get_action_area()->pack_start (disconnect_button, false, false);
267
268         /* Add Port button */
269         if (_selector.maximum_rows() > _selector.n_rows()) {
270                 add_button.set_name ("IOSelectorButton");
271                 add_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::ADD, Gtk::ICON_SIZE_BUTTON)));
272                 get_action_area()->pack_start (add_button, false, false);
273                 add_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (_selector, &IOSelector::add_channel), boost::shared_ptr<Bundle> ()));
274         } 
275
276         /* Rescan button */
277         rescan_button.set_name ("IOSelectorButton");
278         rescan_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::REFRESH, Gtk::ICON_SIZE_BUTTON)));
279         rescan_button.signal_clicked().connect (sigc::mem_fun (_selector, &IOSelector::setup));
280         get_action_area()->pack_start (rescan_button, false, false);
281
282         io->PortCountChanged.connect (sigc::hide (mem_fun (*this, &IOSelectorWindow::ports_changed)));
283
284         /* Cancel button */
285         if (can_cancel) {
286                 cancel_button.set_name ("IOSelectorButton");
287                 cancel_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::CANCEL, Gtk::ICON_SIZE_BUTTON)));
288                 get_action_area()->pack_start (cancel_button, false, false);
289         } else {
290                 cancel_button.hide();
291         }
292         cancel_button.signal_clicked().connect (mem_fun(*this, &IOSelectorWindow::cancel));
293
294         /* OK button */
295         ok_button.set_name ("IOSelectorButton");
296         if (!can_cancel) {
297                 ok_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::CLOSE, Gtk::ICON_SIZE_BUTTON)));
298         }
299         ok_button.signal_clicked().connect (mem_fun(*this, &IOSelectorWindow::accept));
300         get_action_area()->pack_start (ok_button, false, false);
301
302         get_vbox()->set_spacing (8);
303
304         /* XXX: do we still need the ScrolledWindow? */
305         Gtk::ScrolledWindow* sel_scroll = Gtk::manage (new Gtk::ScrolledWindow);
306         sel_scroll->set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_NEVER);
307         sel_scroll->add (_selector);
308         get_vbox()->pack_start (*sel_scroll, true, true);
309
310         set_position (Gtk::WIN_POS_MOUSE);
311
312         io_name_changed (this);
313         ports_changed ();
314
315         show_all ();
316
317         signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), this));
318 }
319
320 IOSelectorWindow::~IOSelectorWindow()
321 {
322         
323 }
324
325 void
326 IOSelectorWindow::ports_changed ()
327 {
328         if (_selector.maximum_rows() > _selector.n_rows()) {
329                 add_button.set_sensitive (true);
330         } else {
331                 add_button.set_sensitive (false);
332         }
333 }
334
335 void
336 IOSelectorWindow::cancel ()
337 {
338         _selector.Finished (IOSelector::Cancelled);
339         hide ();
340 }
341
342 void
343 IOSelectorWindow::accept ()
344 {
345         _selector.Finished (IOSelector::Accepted);
346         hide ();
347 }
348
349 void
350 IOSelectorWindow::on_map ()
351 {
352         _selector.setup ();
353         Window::on_map ();
354 }
355
356 void
357 IOSelectorWindow::io_name_changed (void* src)
358 {
359         ENSURE_GUI_THREAD(bind (mem_fun(*this, &IOSelectorWindow::io_name_changed), src));
360         
361         string title;
362
363         if (!_selector.offering_input()) {
364                 title = string_compose(_("%1 input"), _selector.io()->name());
365         } else {
366                 title = string_compose(_("%1 output"), _selector.io()->name());
367         }
368
369         set_title (title);
370 }
371
372 PortInsertUI::PortInsertUI (ARDOUR::Session& sess, boost::shared_ptr<ARDOUR::PortInsert> pi)
373         : input_selector (sess, pi->io(), true),
374           output_selector (sess, pi->io(), false)
375 {
376         pack_start (output_selector, true, true);
377         pack_start (input_selector, true, true);
378 }
379
380 void
381 PortInsertUI::redisplay ()
382 {
383         input_selector.setup ();
384         output_selector.setup ();
385 }
386
387 void
388 PortInsertUI::finished (IOSelector::Result r)
389 {
390         input_selector.Finished (r);
391         output_selector.Finished (r);
392 }
393
394
395 PortInsertWindow::PortInsertWindow (ARDOUR::Session& sess, boost::shared_ptr<ARDOUR::PortInsert> pi, bool can_cancel)
396         : ArdourDialog ("port insert dialog"),
397           _portinsertui (sess, pi),
398           ok_button (can_cancel ? _("OK"): _("Close")),
399           cancel_button (_("Cancel")),
400           rescan_button (_("Rescan"))
401 {
402
403         set_name ("IOSelectorWindow");
404         string title = _("ardour: ");
405         title += pi->name();
406         set_title (title);
407         
408         ok_button.set_name ("IOSelectorButton");
409         if (!can_cancel) {
410                 ok_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::CLOSE, Gtk::ICON_SIZE_BUTTON)));
411         }
412         cancel_button.set_name ("IOSelectorButton");
413         rescan_button.set_name ("IOSelectorButton");
414         rescan_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::REFRESH, Gtk::ICON_SIZE_BUTTON)));
415
416         get_action_area()->pack_start (rescan_button, false, false);
417         if (can_cancel) {
418                 cancel_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::CANCEL, Gtk::ICON_SIZE_BUTTON)));
419                 get_action_area()->pack_start (cancel_button, false, false);
420         } else {
421                 cancel_button.hide();
422         }
423         get_action_area()->pack_start (ok_button, false, false);
424
425         get_vbox()->pack_start (_portinsertui);
426
427         ok_button.signal_clicked().connect (mem_fun (*this, &PortInsertWindow::accept));
428         cancel_button.signal_clicked().connect (mem_fun (*this, &PortInsertWindow::cancel));
429
430         signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), reinterpret_cast<Window *> (this))); 
431
432         going_away_connection = pi->GoingAway.connect (mem_fun (*this, &PortInsertWindow::plugin_going_away));
433 }
434
435 void
436 PortInsertWindow::plugin_going_away ()
437 {
438         ENSURE_GUI_THREAD (mem_fun (*this, &PortInsertWindow::plugin_going_away));
439         
440         going_away_connection.disconnect ();
441         delete_when_idle (this);
442 }
443
444 void
445 PortInsertWindow::on_map ()
446 {
447         _portinsertui.redisplay ();
448         Window::on_map ();
449 }
450
451
452 void
453 PortInsertWindow::cancel ()
454 {
455         _portinsertui.finished (IOSelector::Cancelled);
456         hide ();
457 }
458
459 void
460 PortInsertWindow::accept ()
461 {
462         _portinsertui.finished (IOSelector::Accepted);
463         hide ();
464 }