merged with 1697 revision of trunk (which is post-rc1 but pre-rc2
[ardour.git] / gtk2_ardour / plugin_selector.cc
1 /*
2     Copyright (C) 2000-2006 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 <cstdio>
21 #include <lrdf.h>
22
23 #include <gtkmm/table.h>
24 #include <gtkmm/stock.h>
25 #include <gtkmm/button.h>
26 #include <gtkmm/notebook.h>
27
28 #include <ardour/plugin_manager.h>
29 #include <ardour/plugin.h>
30 #include <ardour/configuration.h>
31
32 #include "ardour_ui.h"
33 #include "plugin_selector.h"
34 #include "gui_thread.h"
35
36 #include "i18n.h"
37
38 using namespace ARDOUR;
39 using namespace PBD;
40 using namespace Gtk;
41
42 PluginSelector::PluginSelector (PluginManager *mgr)
43         : ArdourDialog (_("ardour: plugins"), true, false)
44 {
45         set_position (Gtk::WIN_POS_MOUSE);
46         set_name ("PluginSelectorWindow");
47         add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
48
49         manager = mgr;
50         session = 0;
51         
52         current_selection = ARDOUR::LADSPA;
53
54         lmodel = Gtk::ListStore::create(lcols);
55         ladspa_display.set_model (lmodel);
56         ladspa_display.append_column (_("Available LADSPA Plugins"), lcols.name);
57         ladspa_display.append_column (_("Type"), lcols.type);
58         ladspa_display.append_column (_("# Inputs"),lcols.ins);
59         ladspa_display.append_column (_("# Outputs"), lcols.outs);
60         ladspa_display.set_headers_visible (true);
61         ladspa_display.set_reorderable (false);
62         lscroller.set_border_width(10);
63         lscroller.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
64         lscroller.add(ladspa_display);
65
66         amodel = Gtk::ListStore::create(acols);
67         added_list.set_model (amodel);
68         added_list.append_column (_("Plugins to be Connected to Insert"), acols.text);
69         added_list.set_headers_visible (true);
70         added_list.set_reorderable (false);
71
72         for (int i = 0; i <=3; i++) {
73                 Gtk::TreeView::Column* column = ladspa_display.get_column(i);
74                 column->set_sort_column(i);
75         }
76
77 #ifdef VST_SUPPORT
78         vmodel = ListStore::create(vcols);
79         vst_display.set_model (vmodel);
80         vst_display.append_column (_("Available plugins"), vcols.name);
81         vst_display.append_column (_("# Inputs"), vcols.ins);
82         vst_display.append_column (_("# Outputs"), vcols.outs);
83         vst_display.set_headers_visible (true);
84         vst_display.set_reorderable (false);
85         vscroller.set_border_width(10);
86         vscroller.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
87         vscroller.add(vst_display);
88
89         for (int i = 0; i <=2; i++) {
90                 Gtk::TreeView::Column* column = vst_display.get_column(i);
91                 column->set_sort_column(i);
92         }
93 #endif
94
95 #ifdef HAVE_AUDIOUNIT
96         aumodel = ListStore::create(aucols);
97         au_display.set_model (aumodel);
98         au_display.append_column (_("Available plugins"), aucols.name);
99         au_display.append_column (_("# Inputs"), aucols.ins);
100         au_display.append_column (_("# Outputs"), aucols.outs);
101         au_display.set_headers_visible (true);
102         au_display.set_reorderable (false);
103         auscroller.set_border_width(10);
104         auscroller.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
105         auscroller.add(au_display);
106
107         for (int i = 0; i <=2; i++) {
108                 Gtk::TreeView::Column* column = au_display.get_column(i);
109                 column->set_sort_column(i);
110         }
111 #endif
112
113         ascroller.set_border_width(10);
114         ascroller.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
115         ascroller.add(added_list);
116         btn_add = manage(new Gtk::Button(Stock::ADD));
117         ARDOUR_UI::instance()->tooltips().set_tip(*btn_add, _("Add a plugin to the effect list"));
118         btn_add->set_sensitive (false);
119         btn_remove = manage(new Gtk::Button(Stock::REMOVE));
120         btn_remove->set_sensitive (false);
121         ARDOUR_UI::instance()->tooltips().set_tip(*btn_remove, _("Remove a plugin from the effect list"));
122         Gtk::Button *btn_update = manage(new Gtk::Button(Stock::REFRESH));
123         ARDOUR_UI::instance()->tooltips().set_tip(*btn_update, _("Update available plugins"));
124
125         btn_add->set_name("PluginSelectorButton");
126         btn_remove->set_name("PluginSelectorButton");
127
128         Gtk::Table* table = manage(new Gtk::Table(7, 10));
129         table->set_size_request(750, 500);
130         table->attach(notebook, 0, 7, 0, 5);
131
132         table->attach(*btn_add, 1, 2, 5, 6, Gtk::FILL, Gtk::FILL, 5, 5);
133         table->attach(*btn_remove, 3, 4, 5, 6, Gtk::FILL, Gtk::FILL, 5, 5);
134         table->attach(*btn_update, 5, 6, 5, 6, Gtk::FILL, Gtk::FILL, 5, 5);
135
136         table->attach(ascroller, 0, 7, 7, 9);
137
138         add_button (Stock::CANCEL, RESPONSE_CANCEL);
139         add_button (Stock::CONNECT, RESPONSE_APPLY);
140         set_default_response (RESPONSE_APPLY);
141         set_response_sensitive (RESPONSE_APPLY, false);
142         get_vbox()->pack_start (*table);
143
144         // Notebook tab order must be the same in here as in set_correct_focus()
145         using namespace Gtk::Notebook_Helpers;
146         notebook.pages().push_back (TabElem (lscroller, _("LADSPA")));
147
148 #ifdef VST_SUPPORT
149         if (Config->get_use_vst()) {
150                 notebook.pages().push_back (TabElem (vscroller, _("VST")));
151         }
152 #endif
153
154 #ifdef HAVE_AUDIOUNIT
155         notebook.pages().push_back (TabElem (auscroller, _("AudioUnit")));
156 #endif
157
158         table->set_name("PluginSelectorTable");
159         ladspa_display.set_name("PluginSelectorDisplay");
160         //ladspa_display.set_name("PluginSelectorList");
161         added_list.set_name("PluginSelectorList");
162
163         ladspa_display.signal_button_press_event().connect_notify (mem_fun(*this, &PluginSelector::row_clicked));
164         ladspa_display.get_selection()->signal_changed().connect (mem_fun(*this, &PluginSelector::ladspa_display_selection_changed));
165         ladspa_display.grab_focus();
166         
167 #ifdef VST_SUPPORT
168         if (Config->get_use_vst()) {
169                 vst_display.signal_button_press_event().connect_notify (mem_fun(*this, &PluginSelector::row_clicked));
170                 vst_display.get_selection()->signal_changed().connect (mem_fun(*this, &PluginSelector::vst_display_selection_changed));
171         }
172 #endif
173
174 #ifdef HAVE_AUDIOUNIT
175         au_display.signal_button_press_event().connect_notify (mem_fun(*this, &PluginSelector::row_clicked));
176         au_display.get_selection()->signal_changed().connect (mem_fun(*this, &PluginSelector::au_display_selection_changed));
177 #endif
178
179         btn_update->signal_clicked().connect (mem_fun(*this, &PluginSelector::btn_update_clicked));
180         btn_add->signal_clicked().connect(mem_fun(*this, &PluginSelector::btn_add_clicked));
181         btn_remove->signal_clicked().connect(mem_fun(*this, &PluginSelector::btn_remove_clicked));
182         added_list.get_selection()->signal_changed().connect (mem_fun(*this, &PluginSelector::added_list_selection_changed));
183
184         input_refiller ();
185         
186 #ifdef VST_SUPPORT
187         vst_refiller ();
188 #endif
189
190 #ifdef HAVE_AUDIOUNIT
191         au_refiller ();
192 #endif
193
194         signal_show().connect (mem_fun (*this, &PluginSelector::set_correct_focus));
195 }
196
197 /**
198  * Makes sure keyboard focus is always in the plugin list
199  * of the selected notebook tab.
200  **/
201 void
202 PluginSelector::set_correct_focus()
203 {
204         int cp = notebook.get_current_page();
205
206         if (cp == 0) {
207                 ladspa_display.grab_focus();
208                 return;
209         }
210
211 #ifdef VST_SUPPORT
212         if (Config->get_use_vst()) {
213                 cp--;
214         
215                 if (cp == 0) {
216                         vst_display.grab_focus();
217                         return;
218                 }
219         }
220 #endif
221
222 #ifdef HAVE_AUDIOUNIT
223         cp--;
224
225         if (cp == 0) {
226                 au_display.grab_focus();
227                 return;
228         }
229 #endif
230 }
231
232 void
233 PluginSelector::row_clicked(GdkEventButton* event)
234 {
235         if (event->type == GDK_2BUTTON_PRESS)
236                 btn_add_clicked();
237 }
238
239 void
240 PluginSelector::set_session (Session* s)
241 {
242         ENSURE_GUI_THREAD(bind (mem_fun(*this, &PluginSelector::set_session), s));
243         
244         session = s;
245
246         if (session) {
247                 session->GoingAway.connect (bind (mem_fun(*this, &PluginSelector::set_session), static_cast<Session*> (0)));
248         }
249 }
250
251 void
252 PluginSelector::_input_refiller (void *arg)
253 {
254         ((PluginSelector *) arg)->input_refiller ();
255 }
256
257 int compare(const void *left, const void *right)
258 {
259   return strcmp(*((char**)left), *((char**)right));
260 }
261
262 void
263 PluginSelector::input_refiller ()
264 {
265         guint row;
266         PluginInfoList &plugs = manager->ladspa_plugin_info ();
267         PluginInfoList::iterator i;
268         char ibuf[16], obuf[16];
269         lmodel->clear();
270
271         // Insert into GTK list
272         for (row = 0, i=plugs.begin(); i != plugs.end(); ++i, ++row) {
273                 snprintf (ibuf, sizeof(ibuf)-1, "%d", (*i)->n_inputs);
274                 snprintf (obuf, sizeof(obuf)-1, "%d", (*i)->n_outputs);         
275                 
276                 Gtk::TreeModel::Row newrow = *(lmodel->append());
277                 newrow[lcols.name] = (*i)->name.c_str();
278                 newrow[lcols.type] = (*i)->category.c_str();
279                 newrow[lcols.ins] = ibuf;
280                 newrow[lcols.outs] = obuf;
281                 newrow[lcols.plugin] = *i;
282         }
283
284         lmodel->set_sort_column (0, Gtk::SORT_ASCENDING);
285 }
286
287 #ifdef VST_SUPPORT
288
289 void
290 PluginSelector::_vst_refiller (void *arg)
291 {
292         ((PluginSelector *) arg)->vst_refiller ();
293 }
294
295 void
296 PluginSelector::vst_refiller ()
297 {
298         guint row;
299         PluginInfoList &plugs = manager->vst_plugin_info ();
300         PluginInfoList::iterator i;
301         char ibuf[16], obuf[16];
302         vmodel->clear();
303         
304         // Insert into GTK list
305         for (row = 0, i=plugs.begin(); i != plugs.end(); ++i, ++row) {
306
307                 snprintf (ibuf, sizeof(ibuf)-1, "%d", (*i)->n_inputs);
308                 snprintf (obuf, sizeof(obuf)-1, "%d", (*i)->n_outputs);         
309                 
310                 Gtk::TreeModel::Row newrow = *(vmodel->append());
311                 newrow[vcols.name] = (*i)->name.c_str();
312                 newrow[vcols.ins] = ibuf;
313                 newrow[vcols.outs] = obuf;
314                 newrow[vcols.plugin] = *i;
315         }
316         vmodel->set_sort_column (0, Gtk::SORT_ASCENDING);
317 }
318
319 void
320 PluginSelector::vst_display_selection_changed()
321 {
322         if (vst_display.get_selection()->count_selected_rows() != 0) {
323                 btn_add->set_sensitive (true);
324         } else {
325                 btn_add->set_sensitive (false);
326         }
327
328         current_selection = ARDOUR::VST;
329 }
330
331 #endif //VST_SUPPORT
332
333 #ifdef HAVE_AUDIOUNIT
334
335 void
336 PluginSelector::_au_refiller (void *arg)
337 {
338         ((PluginSelector *) arg)->au_refiller ();
339 }
340
341 void
342 PluginSelector::au_refiller ()
343 {
344         guint row;
345         PluginInfoList plugs (AUPluginInfo::discover ());
346         PluginInfoList::iterator i;
347         char ibuf[16], obuf[16];
348         aumodel->clear();
349         
350         // Insert into GTK list
351         for (row = 0, i=plugs.begin(); i != plugs.end(); ++i, ++row) {
352
353                 snprintf (ibuf, sizeof(ibuf)-1, "%d", (*i)->n_inputs);
354                 snprintf (obuf, sizeof(obuf)-1, "%d", (*i)->n_outputs);         
355                 
356                 Gtk::TreeModel::Row newrow = *(aumodel->append());
357                 newrow[aucols.name] = (*i)->name.c_str();
358                 newrow[aucols.ins] = ibuf;
359                 newrow[aucols.outs] = obuf;
360                 newrow[aucols.plugin] = *i;
361         }
362         aumodel->set_sort_column (0, Gtk::SORT_ASCENDING);
363 }
364
365 void
366 PluginSelector::au_display_selection_changed()
367 {
368         if (au_display.get_selection()->count_selected_rows() != 0) {
369                 btn_add->set_sensitive (true);
370         } else {
371                 btn_add->set_sensitive (false);
372         }
373         
374         current_selection = ARDOUR::AudioUnit;
375 }
376
377 #endif //HAVE_AUDIOUNIT
378
379 void
380 PluginSelector::use_plugin (PluginInfoPtr pi)
381 {
382         if (session == 0) {
383                 return;
384         }
385
386         PluginPtr plugin = pi->load (*session);
387
388         if (plugin) {
389                 PluginCreated (plugin);
390         }
391 }
392
393 void
394 PluginSelector::btn_add_clicked()
395 {
396         std::string name;
397         PluginInfoPtr pi;
398         Gtk::TreeModel::Row newrow = *(amodel->append());
399         
400         Gtk::TreeModel::Row row;
401
402         switch (current_selection) {
403                 case ARDOUR::LADSPA:
404                         row = *(ladspa_display.get_selection()->get_selected());
405                         name = row[lcols.name];
406                         pi = row[lcols.plugin];
407                         break;
408                 case ARDOUR::VST:
409 #ifdef VST_SUPPORT
410                         row = *(vst_display.get_selection()->get_selected());
411                         name = row[vcols.name];
412                         pi = row[vcols.plugin];
413 #endif
414                         break;
415                 case ARDOUR::AudioUnit:
416 #ifdef HAVE_AUDIOUNIT
417                         row = *(au_display.get_selection()->get_selected());
418                         name = row[aucols.name];
419                         pi = row[aucols.plugin];
420 #endif
421                         break;
422                 default:
423                         error << "Programming error.  Unknown plugin selected." << endmsg;
424                         return;
425         }
426
427         newrow[acols.text] = name;
428         newrow[acols.plugin] = pi;
429
430         if (!amodel->children().empty()) {
431                 set_response_sensitive (RESPONSE_APPLY, true);
432         }
433 }
434
435 void
436 PluginSelector::btn_remove_clicked()
437 {
438         Gtk::TreeModel::iterator iter = added_list.get_selection()->get_selected();
439         
440         amodel->erase(iter);
441         if (amodel->children().empty()) {
442                 set_response_sensitive (RESPONSE_APPLY, false);
443         }
444 }
445
446 void
447 PluginSelector::btn_update_clicked()
448 {
449         manager->refresh ();
450         input_refiller ();
451 #ifdef VST_SUPPORT
452         vst_refiller ();
453 #endif  
454 #ifdef HAVE_AUDIOUNIT
455         au_refiller ();
456 #endif
457 }
458
459 void
460 PluginSelector::ladspa_display_selection_changed()
461 {
462         if (ladspa_display.get_selection()->count_selected_rows() != 0) {
463                 btn_add->set_sensitive (true);
464         } else {
465                 btn_add->set_sensitive (false);
466         }
467         
468         current_selection = ARDOUR::LADSPA;
469 }
470
471 void
472 PluginSelector::added_list_selection_changed()
473 {
474         if (added_list.get_selection()->count_selected_rows() != 0) {
475                 btn_remove->set_sensitive (true);
476         } else {
477                 btn_remove->set_sensitive (false);
478         }
479 }
480
481 int
482 PluginSelector::run ()
483 {
484         ResponseType r;
485         TreeModel::Children::iterator i;
486
487         r = (ResponseType) Dialog::run ();
488
489         switch (r) {
490         case RESPONSE_APPLY:
491                 for (i = amodel->children().begin(); i != amodel->children().end(); ++i) {
492                         use_plugin ((*i)[acols.plugin]);
493                 }
494                 break;
495
496         default:
497                 break;
498         }
499
500         cleanup ();
501
502         return (int) r;
503 }
504
505 void
506 PluginSelector::cleanup ()
507 {
508         hide();
509         amodel->clear();
510 }