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