bump required version of GTK, SLV2; basic fixups so that the new port-matrix io-selec...
[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 (session, io->default_type(), offer_inputs,
44                       PortGroupList::Mask (PortGroupList::BUSS | 
45                                            PortGroupList::SYSTEM | 
46                                            PortGroupList::OTHER))
47         , _io (io)
48 {
49         list<string> our_ports;
50
51         /* Listen for ports changing on the IO */
52         if (_offer_inputs) {
53                 _io->output_changed.connect (mem_fun(*this, &IOSelector::ports_changed));
54
55                 const PortSet& ps (_io->outputs());
56
57                 for (PortSet::const_iterator i = ps.begin(); i != ps.end(); ++i) {
58                         our_ports.push_back ((*i).name());
59                 }
60
61         } else {
62                 _io->input_changed.connect (mem_fun(*this, &IOSelector::ports_changed));
63
64                 const PortSet& ps (_io->inputs());
65
66                 for (PortSet::const_iterator i = ps.begin(); i != ps.end(); ++i) {
67                         our_ports.push_back ((*i).name());
68                 }
69
70         }
71         
72         set_ports (our_ports);
73 }
74
75 void
76 IOSelector::ports_changed (ARDOUR::IOChange change, void *src)
77 {
78         ENSURE_GUI_THREAD (bind (mem_fun (*this, &IOSelector::ports_changed), change, src));
79
80         redisplay ();
81 }
82
83 void
84 IOSelector::set_state (int r, std::string const & p, bool s, uint32_t keymod)
85 {
86         if (s) {
87                 if (!_offer_inputs) {
88                         _io->connect_input (_io->input(r), p, 0);
89                 } else {
90                         _io->connect_output (_io->output(r), p, 0);
91                 }
92         } else {
93                 if (!_offer_inputs) {
94                         _io->disconnect_input (_io->input(r), p, 0);
95                 } else {
96                         _io->disconnect_output (_io->output(r), p, 0);
97                 }
98         }
99 }
100
101 bool
102 IOSelector::get_state (int r, std::string const & p) const
103 {
104         vector<string> connections;
105
106         if (_offer_inputs) {
107                 _io->output(r)->get_connections (connections);
108         } else {
109                 _io->input(r)->get_connections (connections);
110         }
111
112         int k = 0;
113         for (vector<string>::iterator i = connections.begin(); i != connections.end(); ++i) {
114
115                 if ((*i)== p) {
116                         return true;
117                 }
118                 
119                 ++k;
120         }
121
122         return false;
123 }
124
125 uint32_t
126 IOSelector::n_rows () const
127 {
128         if (!_offer_inputs) {
129                 return _io->inputs().num_ports (_io->default_type());
130         } else {
131                 return _io->outputs().num_ports (_io->default_type());
132         }
133 }
134
135 uint32_t
136 IOSelector::maximum_rows () const
137 {
138         if (!_offer_inputs) {
139                 return _io->input_maximum ().get (_io->default_type());
140         } else {
141                 return _io->output_maximum ().get (_io->default_type());
142         }
143 }
144
145
146 uint32_t
147 IOSelector::minimum_rows () const
148 {
149         if (!_offer_inputs) {
150                 return _io->input_minimum ().get (_io->default_type());
151         } else {
152                 return _io->output_minimum ().get (_io->default_type());
153         }
154 }
155
156 std::string
157 IOSelector::row_name (int r) const
158 {
159         string n;
160         string::size_type pos;
161
162         if (!_offer_inputs) {
163                 n =  _io->input(r)->short_name();
164         } else {
165                 n = _io->output(r)->short_name();
166         }
167         
168         if ((pos = n.find ('/')) != string::npos) {
169                 return n.substr (pos+1);
170         } else {
171                 return n;
172         }
173 }
174
175 void
176 IOSelector::add_row ()
177 {
178         // The IO selector only works for single typed IOs
179         const ARDOUR::DataType t = _io->default_type ();
180
181         if (!_offer_inputs) {
182
183                 try {
184                         _io->add_input_port ("", this);
185                 }
186
187                 catch (AudioEngine::PortRegistrationFailure& err) {
188                         MessageDialog msg (_("There are no more JACK ports available."));
189                         msg.run ();
190                 }
191
192         } else {
193
194                 try {
195                         _io->add_output_port ("", this);
196                 }
197
198                 catch (AudioEngine::PortRegistrationFailure& err) {
199                         MessageDialog msg (_("There are no more JACK ports available."));
200                         msg.run ();
201                 }
202         }
203 }
204
205 void
206 IOSelector::remove_row (int r)
207 {
208         // The IO selector only works for single typed IOs
209         const ARDOUR::DataType t = _io->default_type ();
210         
211         if (!_offer_inputs) {
212                 _io->remove_input_port (_io->input (r), this);
213         } else {
214                 _io->remove_output_port (_io->output (r), this);
215         }
216 }
217
218 std::string
219 IOSelector::row_descriptor () const
220 {
221         return _("port");
222 }
223
224 IOSelectorWindow::IOSelectorWindow (ARDOUR::Session& session, boost::shared_ptr<ARDOUR::IO> io, bool for_input, bool can_cancel)
225         : ArdourDialog ("I/O selector")
226         , _selector (session, io, !for_input)
227         , add_button (_("Add Port"))
228         , disconnect_button (_("Disconnect All"))
229         , ok_button (can_cancel ? _("OK"): _("Close"))
230         , cancel_button (_("Cancel"))
231         , rescan_button (_("Rescan"))
232
233 {
234         add_events (Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK);
235         set_name ("IOSelectorWindow2");
236
237         disconnect_button.set_name ("IOSelectorButton");
238         disconnect_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::DISCONNECT, Gtk::ICON_SIZE_BUTTON)));
239         get_action_area()->pack_start (disconnect_button, false, false);
240
241         if (_selector.maximum_rows() > _selector.n_rows()) {
242                 add_button.set_name ("IOSelectorButton");
243                 add_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::ADD, Gtk::ICON_SIZE_BUTTON)));
244                 get_action_area()->pack_start (add_button, false, false);
245                 add_button.signal_clicked().connect (sigc::mem_fun (_selector, &IOSelector::add_row));
246         } 
247
248         if (!for_input) {
249                 io->output_changed.connect (mem_fun(*this, &IOSelectorWindow::ports_changed));
250         } else {
251                 io->input_changed.connect (mem_fun(*this, &IOSelectorWindow::ports_changed));
252         }
253
254         rescan_button.set_name ("IOSelectorButton");
255         rescan_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::REFRESH, Gtk::ICON_SIZE_BUTTON)));
256         get_action_area()->pack_start (rescan_button, false, false);
257
258         if (can_cancel) {
259                 cancel_button.set_name ("IOSelectorButton");
260                 cancel_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::CANCEL, Gtk::ICON_SIZE_BUTTON)));
261                 get_action_area()->pack_start (cancel_button, false, false);
262         } else {
263                 cancel_button.hide();
264         }
265                 
266         ok_button.set_name ("IOSelectorButton");
267         if (!can_cancel) {
268                 ok_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::CLOSE, Gtk::ICON_SIZE_BUTTON)));
269         }
270         get_action_area()->pack_start (ok_button, false, false);
271
272         get_vbox()->set_spacing (8);
273         get_vbox()->pack_start (_selector);
274
275         suggestion.set_alignment (0.5, 0.5);
276         suggestion_box.pack_start (suggestion, true, true);
277         get_vbox()->pack_start (suggestion_box);
278
279         ok_button.signal_clicked().connect (mem_fun(*this, &IOSelectorWindow::accept));
280         cancel_button.signal_clicked().connect (mem_fun(*this, &IOSelectorWindow::cancel));
281         rescan_button.signal_clicked().connect (mem_fun(*this, &IOSelectorWindow::rescan));
282
283         set_position (Gtk::WIN_POS_MOUSE);
284
285         io_name_changed (this);
286         ports_changed (IOChange (0), this);
287         leave_scroller ((GdkEventCrossing*) 0);
288
289         show_all ();
290
291         signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), this));
292
293         _selector.scrolled_window().add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
294         _selector.scrolled_window().signal_enter_notify_event().connect (mem_fun (*this, &IOSelectorWindow::enter_scroller), false);
295         _selector.scrolled_window().signal_leave_notify_event().connect (mem_fun (*this, &IOSelectorWindow::leave_scroller), false);
296 }
297
298 IOSelectorWindow::~IOSelectorWindow()
299 {
300         
301 }
302
303 bool
304 IOSelectorWindow::enter_scroller (GdkEventCrossing* ignored)
305 {
306         cerr << "IN\n";
307         suggestion.set_text (_("Click to connect. Ctrl-click to disconnect. Shift-click for cross-connect"));
308         return false;
309 }
310
311 bool
312 IOSelectorWindow::leave_scroller (GdkEventCrossing* ignored)
313 {
314         cerr << "OUT, ev = " << ignored << "\n";
315         suggestion.set_text (_("Right-click on individual port names for per-port operations"));
316         return false;
317 }
318
319 void
320 IOSelectorWindow::ports_changed (ARDOUR::IOChange change, void *src)
321 {
322         if (_selector.maximum_rows() > _selector.n_rows()) {
323                 add_button.set_sensitive (true);
324         } else {
325                 add_button.set_sensitive (false);
326         }
327 }
328
329 void
330 IOSelectorWindow::rescan ()
331 {
332         _selector.redisplay ();
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.redisplay ();
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         hbox.pack_start (output_selector, true, true);
377         hbox.pack_start (input_selector, true, true);
378
379         pack_start (hbox);
380 }
381
382 void
383 PortInsertUI::redisplay ()
384 {
385         input_selector.redisplay();
386         output_selector.redisplay();
387 }
388
389 void
390 PortInsertUI::finished (IOSelector::Result r)
391 {
392         input_selector.Finished (r);
393         output_selector.Finished (r);
394 }
395
396
397 PortInsertWindow::PortInsertWindow (ARDOUR::Session& sess, boost::shared_ptr<ARDOUR::PortInsert> pi, bool can_cancel)
398         : ArdourDialog ("port insert dialog"),
399           _portinsertui (sess, pi),
400           ok_button (can_cancel ? _("OK"): _("Close")),
401           cancel_button (_("Cancel")),
402           rescan_button (_("Rescan"))
403 {
404
405         set_name ("IOSelectorWindow");
406         string title = _("ardour: ");
407         title += pi->name();
408         set_title (title);
409         
410         ok_button.set_name ("IOSelectorButton");
411         if (!can_cancel) {
412                 ok_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::CLOSE, Gtk::ICON_SIZE_BUTTON)));
413         }
414         cancel_button.set_name ("IOSelectorButton");
415         rescan_button.set_name ("IOSelectorButton");
416         rescan_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::REFRESH, Gtk::ICON_SIZE_BUTTON)));
417
418         get_action_area()->pack_start (rescan_button, false, false);
419         if (can_cancel) {
420                 cancel_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::CANCEL, Gtk::ICON_SIZE_BUTTON)));
421                 get_action_area()->pack_start (cancel_button, false, false);
422         } else {
423                 cancel_button.hide();
424         }
425         get_action_area()->pack_start (ok_button, false, false);
426
427         get_vbox()->pack_start (_portinsertui);
428
429         ok_button.signal_clicked().connect (mem_fun (*this, &PortInsertWindow::accept));
430         cancel_button.signal_clicked().connect (mem_fun (*this, &PortInsertWindow::cancel));
431         rescan_button.signal_clicked().connect (mem_fun (*this, &PortInsertWindow::rescan));
432
433         signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), reinterpret_cast<Window *> (this))); 
434
435         going_away_connection = pi->GoingAway.connect (mem_fun (*this, &PortInsertWindow::plugin_going_away));
436 }
437
438 void
439 PortInsertWindow::plugin_going_away ()
440 {
441         ENSURE_GUI_THREAD (mem_fun (*this, &PortInsertWindow::plugin_going_away));
442         
443         going_away_connection.disconnect ();
444         delete_when_idle (this);
445 }
446
447 void
448 PortInsertWindow::on_map ()
449 {
450         _portinsertui.redisplay ();
451         Window::on_map ();
452 }
453
454
455 void
456 PortInsertWindow::rescan ()
457 {
458         _portinsertui.redisplay ();
459 }
460
461 void
462 PortInsertWindow::cancel ()
463 {
464         _portinsertui.finished (IOSelector::Cancelled);
465         hide ();
466 }
467
468 void
469 PortInsertWindow::accept ()
470 {
471         _portinsertui.finished (IOSelector::Accepted);
472         hide ();
473 }