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