Merge libs/ardour and gtk2_ardour with 2.0-ongoing R2837.
[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_("Name contains"),
51         N_("Type contains"),
52         N_("Author contains"),
53         N_("Library contains"),
54         0
55 };
56
57 PluginSelector::PluginSelector (PluginManager *mgr)
58         : ArdourDialog (_("ardour: plugins"), true, false),
59           filter_button (Stock::CLEAR)
60 {
61         set_position (Gtk::WIN_POS_MOUSE);
62         set_name ("PluginSelectorWindow");
63         add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
64
65         manager = mgr;
66         session = 0;
67         
68         plugin_model = Gtk::ListStore::create (plugin_columns);
69         plugin_display.set_model (plugin_model);
70         plugin_display.append_column (_("Available Plugins"), plugin_columns.name);
71         plugin_display.append_column (_("Type"), plugin_columns.type_name);
72         plugin_display.append_column (_("Category"), plugin_columns.category);
73         plugin_display.append_column (_("Creator"), plugin_columns.creator);
74         plugin_display.append_column (_("# Inputs"),plugin_columns.ins);
75         plugin_display.append_column (_("# Outputs"), plugin_columns.outs);
76         plugin_display.set_headers_visible (true);
77         plugin_display.set_headers_clickable (true);
78         plugin_display.set_reorderable (false);
79         scroller.set_border_width(10);
80         scroller.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
81         scroller.add(plugin_display);
82
83         amodel = Gtk::ListStore::create(acols);
84         added_list.set_model (amodel);
85         added_list.append_column (_("Plugins to be connected"), acols.text);
86         added_list.set_headers_visible (true);
87         added_list.set_reorderable (false);
88
89         for (int i = 0; i <=3; i++) {
90                 Gtk::TreeView::Column* column = plugin_display.get_column(i);
91                 column->set_sort_column(i);
92         }
93
94         ascroller.set_border_width(10);
95         ascroller.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
96         ascroller.add(added_list);
97         btn_add = manage(new Gtk::Button(Stock::ADD));
98         ARDOUR_UI::instance()->tooltips().set_tip(*btn_add, _("Add a plugin to the effect list"));
99         btn_add->set_sensitive (false);
100         btn_remove = manage(new Gtk::Button(Stock::REMOVE));
101         btn_remove->set_sensitive (false);
102         ARDOUR_UI::instance()->tooltips().set_tip(*btn_remove, _("Remove a plugin from the effect list"));
103         Gtk::Button *btn_update = manage(new Gtk::Button(Stock::REFRESH));
104         ARDOUR_UI::instance()->tooltips().set_tip(*btn_update, _("Update available plugins"));
105
106         btn_add->set_name("PluginSelectorButton");
107         btn_remove->set_name("PluginSelectorButton");
108
109         Gtk::Table* table = manage(new Gtk::Table(7, 11));
110         table->set_size_request(750, 500);
111         table->attach(scroller, 0, 7, 0, 5);
112
113         HBox* filter_box = manage (new HBox);
114
115         vector<string> filter_strings = I18N (_filter_mode_strings);
116         Gtkmm2ext::set_popdown_strings (filter_mode, filter_strings);
117         filter_mode.set_active_text (filter_strings.front());
118
119         filter_box->pack_start (filter_mode, false, false);
120         filter_box->pack_start (filter_entry, true, true);
121         filter_box->pack_start (filter_button, false, false);
122
123         filter_entry.signal_changed().connect (mem_fun (*this, &PluginSelector::filter_entry_changed));
124         filter_button.signal_clicked().connect (mem_fun (*this, &PluginSelector::filter_button_clicked));
125         filter_mode.signal_changed().connect (mem_fun (*this, &PluginSelector::filter_mode_changed));
126
127         filter_box->show ();
128         filter_mode.show ();
129         filter_entry.show ();
130         filter_button.show ();
131
132         table->attach (*filter_box, 0, 7, 5, 6, FILL|EXPAND, FILL, 5, 5);
133
134         table->attach(*btn_add, 1, 2, 6, 7, FILL, FILL, 5, 5); 
135         table->attach(*btn_remove, 3, 4, 6, 7, FILL, FILL, 5, 5);
136         table->attach(*btn_update, 5, 6, 6, 7, FILL, FILL, 5, 5);
137
138         table->attach(ascroller, 0, 7, 8, 10);
139
140         add_button (Stock::CANCEL, RESPONSE_CANCEL);
141         add_button (_("Insert Plugin(s)"), RESPONSE_APPLY);
142         set_default_response (RESPONSE_APPLY);
143         set_response_sensitive (RESPONSE_APPLY, false);
144         get_vbox()->pack_start (*table);
145
146         table->set_name("PluginSelectorTable");
147         plugin_display.set_name("PluginSelectorDisplay");
148         //plugin_display.set_name("PluginSelectorList");
149         added_list.set_name("PluginSelectorList");
150
151         plugin_display.signal_button_press_event().connect_notify (mem_fun(*this, &PluginSelector::row_clicked));
152         plugin_display.get_selection()->signal_changed().connect (mem_fun(*this, &PluginSelector::display_selection_changed));
153         plugin_display.grab_focus();
154         
155         btn_update->signal_clicked().connect (mem_fun(*this, &PluginSelector::btn_update_clicked));
156         btn_add->signal_clicked().connect(mem_fun(*this, &PluginSelector::btn_add_clicked));
157         btn_remove->signal_clicked().connect(mem_fun(*this, &PluginSelector::btn_remove_clicked));
158         added_list.get_selection()->signal_changed().connect (mem_fun(*this, &PluginSelector::added_list_selection_changed));
159
160         refill ();
161 }
162
163 void
164 PluginSelector::row_clicked(GdkEventButton* event)
165 {
166         if (event->type == GDK_2BUTTON_PRESS)
167                 btn_add_clicked();
168 }
169
170 void
171 PluginSelector::set_session (Session* s)
172 {
173         ENSURE_GUI_THREAD(bind (mem_fun(*this, &PluginSelector::set_session), s));
174         
175         session = s;
176
177         if (session) {
178                 session->GoingAway.connect (bind (mem_fun(*this, &PluginSelector::set_session), static_cast<Session*> (0)));
179         }
180 }
181
182 bool
183 PluginSelector::show_this_plugin (const PluginInfoPtr& info, const std::string& filterstr)
184 {
185         std::string compstr;
186         std::string mode = filter_mode.get_active_text ();
187
188         if (!filterstr.empty()) {
189                 
190                 if (mode == _("Name contains")) {
191                         compstr = info->name;
192                 } else if (mode == _("Type contains")) {
193                         compstr = info->category;
194                 } else if (mode == _("Author contains")) {
195                         compstr = info->creator;
196                 } else if (mode == _("Library contains")) {
197                         compstr = info->path;
198                 }
199                 
200                 transform (compstr.begin(), compstr.end(), compstr.begin(), ::toupper);
201
202                 if (compstr.find (filterstr) != string::npos) {
203                         return true;
204                 } else {
205                         return false;
206                 }
207                 
208         }
209
210         return true;
211 }
212
213 void
214 PluginSelector::setup_filter_string (string& filterstr)
215 {
216         filterstr = filter_entry.get_text ();
217         transform (filterstr.begin(), filterstr.end(), filterstr.begin(), ::toupper);
218 }       
219
220 void
221 PluginSelector::refill ()
222 {
223         std::string filterstr;
224
225         plugin_model->clear ();
226
227         setup_filter_string (filterstr);
228
229         ladspa_refiller (filterstr);
230         vst_refiller (filterstr);
231         au_refiller (filterstr);
232 }
233
234 void
235 PluginSelector::refiller (const PluginInfoList& plugs, const::std::string& filterstr, const char* type)
236 {
237         char buf[16];
238
239         for (PluginInfoList::const_iterator i = plugs.begin(); i != plugs.end(); ++i) {
240
241                 if (show_this_plugin (*i, filterstr)) {
242
243                         TreeModel::Row newrow = *(plugin_model->append());
244                         newrow[plugin_columns.name] = (*i)->name;
245                         newrow[plugin_columns.type_name] = type;
246                         newrow[plugin_columns.category] = (*i)->category;
247
248
249                         string creator = (*i)->creator;
250                         string::size_type pos = 0;
251
252                         /* stupid LADSPA creator strings */
253
254                         while (pos < creator.length() && (isalnum (creator[pos]) || isspace (creator[pos]))) ++pos;
255                         creator = creator.substr (0, pos);
256
257                         newrow[plugin_columns.creator] = creator;
258
259                         if ((*i)->n_inputs.n_total() < 0) {
260                                 newrow[plugin_columns.ins] = "various";
261                         } else {
262                                 snprintf (buf, sizeof(buf), "%d", (*i)->n_inputs.n_total());
263                                 newrow[plugin_columns.ins] = buf;
264                         }
265                         if ((*i)->n_outputs.n_total() < 0) {
266                                 newrow[plugin_columns.outs] = "various";
267                         } else {
268                                 snprintf (buf, sizeof(buf), "%d", (*i)->n_outputs.n_total());           
269                                 newrow[plugin_columns.outs] = buf;
270                         }
271
272                         newrow[plugin_columns.plugin] = *i;
273                 }
274         }       
275 }
276
277 void
278 PluginSelector::ladspa_refiller (const std::string& filterstr)
279 {
280         refiller (manager->ladspa_plugin_info(), filterstr, "LADSPA");
281 }
282
283 void
284 PluginSelector::vst_refiller (const std::string& filterstr)
285 {
286 #ifdef VST_SUPPORT
287         refiller (manager->vst_plugin_info(), filterstr, "VST");
288 #endif
289 }
290
291 void
292 PluginSelector::au_refiller (const std::string& filterstr)
293 {
294 #ifdef HAVE_AUDIOUNITS
295         refiller (manager->au_plugin_info(), filterstr, "AU");
296 #endif
297 }
298
299 void
300 PluginSelector::use_plugin (PluginInfoPtr pi)
301 {
302         if (session == 0) {
303                 return;
304         }
305
306         PluginPtr plugin = pi->load (*session);
307
308         if (plugin) {
309                 PluginCreated (plugin);
310         }
311 }
312
313 void
314 PluginSelector::btn_add_clicked()
315 {
316         std::string name;
317         PluginInfoPtr pi;
318         TreeModel::Row newrow = *(amodel->append());
319         TreeModel::Row row;
320
321         row = *(plugin_display.get_selection()->get_selected());
322         name = row[plugin_columns.name];
323         pi = row[plugin_columns.plugin];
324
325         newrow[acols.text] = name;
326         newrow[acols.plugin] = pi;
327
328         if (!amodel->children().empty()) {
329                 set_response_sensitive (RESPONSE_APPLY, true);
330         }
331 }
332
333 void
334 PluginSelector::btn_remove_clicked()
335 {
336         TreeModel::iterator iter = added_list.get_selection()->get_selected();
337         
338         amodel->erase(iter);
339         if (amodel->children().empty()) {
340                 set_response_sensitive (RESPONSE_APPLY, false);
341         }
342 }
343
344 void
345 PluginSelector::btn_update_clicked()
346 {
347         manager->refresh ();
348         refill();
349 }
350
351 void
352 PluginSelector::display_selection_changed()
353 {
354         if (plugin_display.get_selection()->count_selected_rows() != 0) {
355                 btn_add->set_sensitive (true);
356         } else {
357                 btn_add->set_sensitive (false);
358         }
359 }
360
361 void
362 PluginSelector::added_list_selection_changed()
363 {
364         if (added_list.get_selection()->count_selected_rows() != 0) {
365                 btn_remove->set_sensitive (true);
366         } else {
367                 btn_remove->set_sensitive (false);
368         }
369 }
370
371 int
372 PluginSelector::run ()
373 {
374         ResponseType r;
375         TreeModel::Children::iterator i;
376
377         r = (ResponseType) Dialog::run ();
378
379         switch (r) {
380         case RESPONSE_APPLY:
381                 for (i = amodel->children().begin(); i != amodel->children().end(); ++i) {
382                         PluginInfoPtr pp = (*i)[acols.plugin];
383                         use_plugin (pp);
384                 }
385                 break;
386
387         default:
388                 break;
389         }
390
391         cleanup ();
392
393         return (int) r;
394 }
395
396 void
397 PluginSelector::cleanup ()
398 {
399         hide();
400         amodel->clear();
401 }
402
403 void
404 PluginSelector::filter_button_clicked ()
405 {
406         filter_entry.set_text ("");
407 }
408
409 void
410 PluginSelector::filter_entry_changed ()
411 {
412         refill ();
413 }
414
415 void 
416 PluginSelector::filter_mode_changed ()
417 {
418         refill ();
419 }
420