Most PluginManager refactoring is out of the way. Time to begin on AudioUnit support...
[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 = PluginInfo::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_COREAUDIO
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         using namespace Gtk::Notebook_Helpers;
145         notebook.pages().push_back (TabElem (lscroller, _("LADSPA")));
146
147 #ifdef VST_SUPPORT
148         if (Config->get_use_vst()) {
149                 notebook.pages().push_back (TabElem (vscroller, _("VST")));
150         }
151 #endif
152
153 #ifdef HAVE_COREAUDIO
154         notebook.pages().push_back (TabElem (auscroller, _("AudioUnit")));
155 #endif
156
157         table->set_name("PluginSelectorTable");
158         ladspa_display.set_name("PluginSelectorDisplay");
159         //ladspa_display.set_name("PluginSelectorList");
160         added_list.set_name("PluginSelectorList");
161
162         ladspa_display.signal_button_press_event().connect_notify (mem_fun(*this, &PluginSelector::row_clicked));
163         ladspa_display.get_selection()->signal_changed().connect (mem_fun(*this, &PluginSelector::ladspa_display_selection_changed));
164         
165 #ifdef VST_SUPPORT
166         if (Config->get_use_vst()) {
167                 vst_display.signal_button_press_event().connect_notify (mem_fun(*this, &PluginSelector::row_clicked));
168                 vst_display.get_selection()->signal_changed().connect (mem_fun(*this, &PluginSelector::vst_display_selection_changed));
169         }
170 #endif
171
172 #ifdef HAVE_COREAUDIO
173         au_display.signal_button_press_event().connect_notify (mem_fun(*this, &PluginSelector::row_clicked));
174         au_display.get_selection()->signal_changed().connect (mem_fun(*this, &PluginSelector::au_display_selection_changed));
175 #endif
176
177         btn_update->signal_clicked().connect (mem_fun(*this, &PluginSelector::btn_update_clicked));
178         btn_add->signal_clicked().connect(mem_fun(*this, &PluginSelector::btn_add_clicked));
179         btn_remove->signal_clicked().connect(mem_fun(*this, &PluginSelector::btn_remove_clicked));
180         added_list.get_selection()->signal_changed().connect (mem_fun(*this, &PluginSelector::added_list_selection_changed));
181
182         input_refiller ();
183         
184 #ifdef VST_SUPPORT
185         vst_refiller ();
186 #endif
187
188 #ifdef HAVE_COREAUDIO
189         au_refiller ();
190 #endif
191 }
192
193 void
194 PluginSelector::row_clicked(GdkEventButton* event)
195 {
196         if (event->type == GDK_2BUTTON_PRESS)
197                 btn_add_clicked();
198 }
199
200 void
201 PluginSelector::set_session (Session* s)
202 {
203         ENSURE_GUI_THREAD(bind (mem_fun(*this, &PluginSelector::set_session), s));
204         
205         session = s;
206
207         if (session) {
208                 session->going_away.connect (bind (mem_fun(*this, &PluginSelector::set_session), static_cast<Session*> (0)));
209         }
210 }
211
212 void
213 PluginSelector::_input_refiller (void *arg)
214 {
215         ((PluginSelector *) arg)->input_refiller ();
216 }
217
218 int compare(const void *left, const void *right)
219 {
220   return strcmp(*((char**)left), *((char**)right));
221 }
222
223 void
224 PluginSelector::input_refiller ()
225 {
226         guint row;
227         PluginInfoList &plugs = manager->ladspa_plugin_info ();
228         PluginInfoList::iterator i;
229         char ibuf[16], obuf[16];
230         lmodel->clear();
231
232         // Insert into GTK list
233         for (row = 0, i=plugs.begin(); i != plugs.end(); ++i, ++row) {
234                 snprintf (ibuf, sizeof(ibuf)-1, "%d", (*i)->n_inputs);
235                 snprintf (obuf, sizeof(obuf)-1, "%d", (*i)->n_outputs);         
236                 
237                 Gtk::TreeModel::Row newrow = *(lmodel->append());
238                 newrow[lcols.name] = (*i)->name.c_str();
239                 newrow[lcols.type] = (*i)->category.c_str();
240                 newrow[lcols.ins] = ibuf;
241                 newrow[lcols.outs] = obuf;
242                 newrow[lcols.plugin] = *i;
243         }
244
245         lmodel->set_sort_column (0, Gtk::SORT_ASCENDING);
246 }
247
248 #ifdef VST_SUPPORT
249
250 void
251 PluginSelector::_vst_refiller (void *arg)
252 {
253         ((PluginSelector *) arg)->vst_refiller ();
254 }
255
256 void
257 PluginSelector::vst_refiller ()
258 {
259         guint row;
260         PluginInfoList &plugs = manager->vst_plugin_info ();
261         PluginInfoList::iterator i;
262         char ibuf[16], obuf[16];
263         vmodel->clear();
264         
265         // Insert into GTK list
266         for (row = 0, i=plugs.begin(); i != plugs.end(); ++i, ++row) {
267
268                 snprintf (ibuf, sizeof(ibuf)-1, "%d", (*i)->n_inputs);
269                 snprintf (obuf, sizeof(obuf)-1, "%d", (*i)->n_outputs);         
270                 
271                 Gtk::TreeModel::Row newrow = *(vmodel->append());
272                 newrow[vcols.name] = (*i)->name.c_str();
273                 newrow[vcols.ins] = ibuf;
274                 newrow[vcols.outs] = obuf;
275                 newrow[vcols.plugin] = *i;
276         }
277         vmodel->set_sort_column (0, Gtk::SORT_ASCENDING);
278 }
279
280 void
281 PluginSelector::vst_display_selection_changed()
282 {
283         if (vst_display.get_selection()->count_selected_rows() != 0) {
284                 btn_add->set_sensitive (true);
285         } else {
286                 btn_add->set_sensitive (false);
287         }
288
289         current_selection = PluginInfo::VST;
290 }
291
292 #endif //VST_SUPPORT
293
294 #ifdef HAVE_COREAUDIO
295
296 void
297 PluginSelector::_au_refiller (void *arg)
298 {
299         ((PluginSelector *) arg)->au_refiller ();
300 }
301
302 void
303 PluginSelector::au_refiller ()
304 {
305         guint row;
306         PluginInfoList plugs (AUPluginInfo::discover ());
307         PluginInfoList::iterator i;
308         char ibuf[16], obuf[16];
309         aumodel->clear();
310         
311         // Insert into GTK list
312         for (row = 0, i=plugs.begin(); i != plugs.end(); ++i, ++row) {
313
314                 snprintf (ibuf, sizeof(ibuf)-1, "%d", (*i)->n_inputs);
315                 snprintf (obuf, sizeof(obuf)-1, "%d", (*i)->n_outputs);         
316                 
317                 Gtk::TreeModel::Row newrow = *(aumodel->append());
318                 newrow[aucols.name] = (*i)->name.c_str();
319                 newrow[aucols.ins] = ibuf;
320                 newrow[aucols.outs] = obuf;
321                 newrow[aucols.plugin] = *i;
322         }
323         aumodel->set_sort_column (0, Gtk::SORT_ASCENDING);
324 }
325
326 void
327 PluginSelector::au_display_selection_changed()
328 {
329         if (au_display.get_selection()->count_selected_rows() != 0) {
330                 btn_add->set_sensitive (true);
331         } else {
332                 btn_add->set_sensitive (false);
333         }
334         
335         current_selection = PluginInfo::AudioUnit;
336 }
337
338 #endif //HAVE_COREAUDIO
339
340 void
341 PluginSelector::use_plugin (PluginInfoPtr pi)
342 {
343         if (session == 0) {
344                 return;
345         }
346
347         PluginPtr plugin = pi->load (*session);
348
349         if (plugin) {
350                 PluginCreated (plugin);
351         }
352 }
353
354 void
355 PluginSelector::btn_add_clicked()
356 {
357         std::string name;
358         PluginInfoPtr pi;
359         Gtk::TreeModel::Row newrow = *(amodel->append());
360         
361         Gtk::TreeModel::Row row;
362
363         switch (current_selection) {
364                 case PluginInfo::LADSPA:
365                         row = *(ladspa_display.get_selection()->get_selected());
366                         name = row[lcols.name];
367                         pi = row[lcols.plugin];
368                         break;
369                 case PluginInfo::VST:
370 #ifdef VST_SUPPORT
371                         row = *(vst_display.get_selection()->get_selected());
372                         name = row[vcols.name];
373                         pi = row[vcols.plugin];
374 #endif
375                         break;
376                 case PluginInfo::AudioUnit:
377 #ifdef HAVE_COREAUDIO
378                         row = *(au_display.get_selection()->get_selected());
379                         name = row[aucols.name];
380                         pi = row[aucols.plugin];
381 #endif
382                         break;
383                 default:
384                         error << "Programming error.  Unknown plugin selected." << endmsg;
385                         return;
386         }
387
388         newrow[acols.text] = name;
389         newrow[acols.plugin] = pi;
390
391         if (!amodel->children().empty()) {
392                 set_response_sensitive (RESPONSE_APPLY, true);
393         }
394 }
395
396 void
397 PluginSelector::btn_remove_clicked()
398 {
399         Gtk::TreeModel::iterator iter = added_list.get_selection()->get_selected();
400         
401         amodel->erase(iter);
402         if (amodel->children().empty()) {
403                 set_response_sensitive (RESPONSE_APPLY, false);
404         }
405 }
406
407 void
408 PluginSelector::btn_update_clicked()
409 {
410         manager->refresh ();
411         input_refiller ();
412 #ifdef VST_SUPPORT
413         vst_refiller ();
414 #endif  
415 #ifdef HAVE_COREAUDIO
416         au_refiller ();
417 #endif
418 }
419
420 void
421 PluginSelector::ladspa_display_selection_changed()
422 {
423         if (ladspa_display.get_selection()->count_selected_rows() != 0) {
424                 btn_add->set_sensitive (true);
425         } else {
426                 btn_add->set_sensitive (false);
427         }
428         
429         current_selection = PluginInfo::LADSPA;
430 }
431
432 void
433 PluginSelector::added_list_selection_changed()
434 {
435   if (added_list.get_selection()->count_selected_rows() != 0) {
436     btn_remove->set_sensitive (true);
437   } else {
438     btn_remove->set_sensitive (false);
439   }
440 }
441
442 int
443 PluginSelector::run ()
444 {
445         ResponseType r;
446         TreeModel::Children::iterator i;
447
448         r = (ResponseType) Dialog::run ();
449
450         switch (r) {
451         case RESPONSE_APPLY:
452                 for (i = amodel->children().begin(); i != amodel->children().end(); ++i) {
453                         use_plugin ((*i)[acols.plugin]);
454                 }
455                 break;
456
457         default:
458                 break;
459         }
460
461         cleanup ();
462
463         return (int) r;
464 }
465
466 void
467 PluginSelector::cleanup ()
468 {
469         hide();
470         amodel->clear();
471 }