Update export dialog to match the new fancy one from 2.0-ongoing.
[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         lv2_refiller (filterstr);
231         vst_refiller (filterstr);
232         au_refiller (filterstr);
233 }
234
235 void
236 PluginSelector::refiller (const PluginInfoList& plugs, const::std::string& filterstr, const char* type)
237 {
238         char buf[16];
239
240         for (PluginInfoList::const_iterator i = plugs.begin(); i != plugs.end(); ++i) {
241
242                 if (show_this_plugin (*i, filterstr)) {
243
244                         TreeModel::Row newrow = *(plugin_model->append());
245                         newrow[plugin_columns.name] = (*i)->name;
246                         newrow[plugin_columns.type_name] = type;
247                         newrow[plugin_columns.category] = (*i)->category;
248
249
250                         string creator = (*i)->creator;
251                         string::size_type pos = 0;
252
253                         /* stupid LADSPA creator strings */
254
255                         while (pos < creator.length() && (isalnum (creator[pos]) || isspace (creator[pos]))) ++pos;
256                         creator = creator.substr (0, pos);
257
258                         newrow[plugin_columns.creator] = creator;
259
260                         if ((*i)->n_inputs.n_total() < 0) {
261                                 newrow[plugin_columns.ins] = "various";
262                         } else {
263                                 snprintf (buf, sizeof(buf), "%d", (*i)->n_inputs.n_total());
264                                 newrow[plugin_columns.ins] = buf;
265                         }
266                         if ((*i)->n_outputs.n_total() < 0) {
267                                 newrow[plugin_columns.outs] = "various";
268                         } else {
269                                 snprintf (buf, sizeof(buf), "%d", (*i)->n_outputs.n_total());           
270                                 newrow[plugin_columns.outs] = buf;
271                         }
272
273                         newrow[plugin_columns.plugin] = *i;
274                 }
275         }       
276 }
277
278 void
279 PluginSelector::ladspa_refiller (const std::string& filterstr)
280 {
281         refiller (manager->ladspa_plugin_info(), filterstr, "LADSPA");
282 }
283
284 void
285 PluginSelector::lv2_refiller (const std::string& filterstr)
286 {
287 #ifdef HAVE_SLV2
288         refiller (manager->lv2_plugin_info(), filterstr, "LV2");
289 #endif
290 }
291
292 void
293 PluginSelector::vst_refiller (const std::string& filterstr)
294 {
295 #ifdef VST_SUPPORT
296         refiller (manager->vst_plugin_info(), filterstr, "VST");
297 #endif
298 }
299
300 void
301 PluginSelector::au_refiller (const std::string& filterstr)
302 {
303 #ifdef HAVE_AUDIOUNITS
304         refiller (manager->au_plugin_info(), filterstr, "AU");
305 #endif
306 }
307
308 void
309 PluginSelector::use_plugin (PluginInfoPtr pi)
310 {
311         if (session == 0) {
312                 return;
313         }
314
315         PluginPtr plugin = pi->load (*session);
316
317         if (plugin) {
318                 PluginCreated (plugin);
319         }
320 }
321
322 void
323 PluginSelector::btn_add_clicked()
324 {
325         std::string name;
326         PluginInfoPtr pi;
327         TreeModel::Row newrow = *(amodel->append());
328         TreeModel::Row row;
329
330         row = *(plugin_display.get_selection()->get_selected());
331         name = row[plugin_columns.name];
332         pi = row[plugin_columns.plugin];
333
334         newrow[acols.text] = name;
335         newrow[acols.plugin] = pi;
336
337         if (!amodel->children().empty()) {
338                 set_response_sensitive (RESPONSE_APPLY, true);
339         }
340 }
341
342 void
343 PluginSelector::btn_remove_clicked()
344 {
345         TreeModel::iterator iter = added_list.get_selection()->get_selected();
346         
347         amodel->erase(iter);
348         if (amodel->children().empty()) {
349                 set_response_sensitive (RESPONSE_APPLY, false);
350         }
351 }
352
353 void
354 PluginSelector::btn_update_clicked()
355 {
356         manager->refresh ();
357         refill();
358 }
359
360 void
361 PluginSelector::display_selection_changed()
362 {
363         if (plugin_display.get_selection()->count_selected_rows() != 0) {
364                 btn_add->set_sensitive (true);
365         } else {
366                 btn_add->set_sensitive (false);
367         }
368 }
369
370 void
371 PluginSelector::added_list_selection_changed()
372 {
373         if (added_list.get_selection()->count_selected_rows() != 0) {
374                 btn_remove->set_sensitive (true);
375         } else {
376                 btn_remove->set_sensitive (false);
377         }
378 }
379
380 int
381 PluginSelector::run ()
382 {
383         ResponseType r;
384         TreeModel::Children::iterator i;
385
386         r = (ResponseType) Dialog::run ();
387
388         switch (r) {
389         case RESPONSE_APPLY:
390                 for (i = amodel->children().begin(); i != amodel->children().end(); ++i) {
391                         PluginInfoPtr pp = (*i)[acols.plugin];
392                         use_plugin (pp);
393                 }
394                 break;
395
396         default:
397                 break;
398         }
399
400         cleanup ();
401
402         return (int) r;
403 }
404
405 void
406 PluginSelector::cleanup ()
407 {
408         hide();
409         amodel->clear();
410 }
411
412 void
413 PluginSelector::filter_button_clicked ()
414 {
415         filter_entry.set_text ("");
416 }
417
418 void
419 PluginSelector::filter_entry_changed ()
420 {
421         refill ();
422 }
423
424 void 
425 PluginSelector::filter_mode_changed ()
426 {
427         refill ();
428 }
429