2 Copyright (C) 2000-2006 Paul Davis
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.
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.
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.
26 #include <gtkmm/table.h>
27 #include <gtkmm/stock.h>
28 #include <gtkmm/button.h>
29 #include <gtkmm/notebook.h>
31 #include <gtkmm2ext/utils.h>
33 #include <pbd/convert.h>
35 #include <ardour/plugin_manager.h>
36 #include <ardour/plugin.h>
37 #include <ardour/configuration.h>
39 #include "ardour_ui.h"
40 #include "plugin_selector.h"
41 #include "gui_thread.h"
45 using namespace ARDOUR
;
50 static const char* _filter_mode_strings
[] = {
53 N_("Category contains"),
54 N_("Author contains"),
55 N_("Library contains"),
60 PluginSelector::PluginSelector (PluginManager
*mgr
)
61 : ArdourDialog (_("ardour: plugins"), true, false),
62 filter_button (Stock::CLEAR
)
64 set_position (Gtk::WIN_POS_MOUSE
);
65 set_name ("PluginSelectorWindow");
66 add_events (Gdk::KEY_PRESS_MASK
|Gdk::KEY_RELEASE_MASK
);
71 in_row_change
= false;
73 plugin_model
= Gtk::ListStore::create (plugin_columns
);
74 plugin_display
.set_model (plugin_model
);
75 /* XXX translators: try to convert "Fav" into a short term
78 plugin_display
.append_column (_("Fav"), plugin_columns
.favorite
);
79 plugin_display
.append_column (_("Available Plugins"), plugin_columns
.name
);
80 plugin_display
.append_column (_("Type"), plugin_columns
.type_name
);
81 plugin_display
.append_column (_("Category"), plugin_columns
.category
);
82 plugin_display
.append_column (_("Creator"), plugin_columns
.creator
);
83 plugin_display
.append_column (_("# Inputs"),plugin_columns
.ins
);
84 plugin_display
.append_column (_("# Outputs"), plugin_columns
.outs
);
85 plugin_display
.set_headers_visible (true);
86 plugin_display
.set_headers_clickable (true);
87 plugin_display
.set_reorderable (false);
88 plugin_display
.set_rules_hint (true);
90 CellRendererToggle
* fav_cell
= dynamic_cast<CellRendererToggle
*>(plugin_display
.get_column_cell_renderer (0));
91 fav_cell
->property_activatable() = true;
92 fav_cell
->property_radio() = false;
93 fav_cell
->signal_toggled().connect (mem_fun (*this, &PluginSelector::favorite_changed
));
95 scroller
.set_border_width(10);
96 scroller
.set_policy(Gtk::POLICY_AUTOMATIC
, Gtk::POLICY_AUTOMATIC
);
97 scroller
.add(plugin_display
);
99 amodel
= Gtk::ListStore::create(acols
);
100 added_list
.set_model (amodel
);
101 added_list
.append_column (_("Plugins to be connected"), acols
.text
);
102 added_list
.set_headers_visible (true);
103 added_list
.set_reorderable (false);
105 for (int i
= 0; i
<=3; i
++) {
106 Gtk::TreeView::Column
* column
= plugin_display
.get_column(i
);
107 column
->set_sort_column(i
);
110 ascroller
.set_border_width(10);
111 ascroller
.set_policy(Gtk::POLICY_AUTOMATIC
, Gtk::POLICY_AUTOMATIC
);
112 ascroller
.add(added_list
);
113 btn_add
= manage(new Gtk::Button(Stock::ADD
));
114 ARDOUR_UI::instance()->tooltips().set_tip(*btn_add
, _("Add a plugin to the effect list"));
115 btn_add
->set_sensitive (false);
116 btn_remove
= manage(new Gtk::Button(Stock::REMOVE
));
117 btn_remove
->set_sensitive (false);
118 ARDOUR_UI::instance()->tooltips().set_tip(*btn_remove
, _("Remove a plugin from the effect list"));
119 Gtk::Button
*btn_update
= manage(new Gtk::Button(Stock::REFRESH
));
120 ARDOUR_UI::instance()->tooltips().set_tip(*btn_update
, _("Update available plugins"));
122 btn_add
->set_name("PluginSelectorButton");
123 btn_remove
->set_name("PluginSelectorButton");
125 Gtk::Table
* table
= manage(new Gtk::Table(7, 11));
126 table
->set_size_request(750, 500);
127 table
->attach(scroller
, 0, 7, 0, 5);
129 HBox
* filter_box
= manage (new HBox
);
131 vector
<string
> filter_strings
= I18N (_filter_mode_strings
);
132 Gtkmm2ext::set_popdown_strings (filter_mode
, filter_strings
);
133 filter_mode
.set_active_text (filter_strings
.front());
135 filter_box
->pack_start (filter_mode
, false, false);
136 filter_box
->pack_start (filter_entry
, true, true);
137 filter_box
->pack_start (filter_button
, false, false);
139 filter_entry
.signal_changed().connect (mem_fun (*this, &PluginSelector::filter_entry_changed
));
140 filter_button
.signal_clicked().connect (mem_fun (*this, &PluginSelector::filter_button_clicked
));
141 filter_mode
.signal_changed().connect (mem_fun (*this, &PluginSelector::filter_mode_changed
));
145 filter_entry
.show ();
146 filter_button
.show ();
148 table
->attach (*filter_box
, 0, 7, 5, 6, FILL
|EXPAND
, FILL
, 5, 5);
150 table
->attach(*btn_add
, 1, 2, 6, 7, FILL
, FILL
, 5, 5);
151 table
->attach(*btn_remove
, 3, 4, 6, 7, FILL
, FILL
, 5, 5);
152 table
->attach(*btn_update
, 5, 6, 6, 7, FILL
, FILL
, 5, 5);
154 table
->attach(ascroller
, 0, 7, 8, 10);
156 add_button (Stock::CANCEL
, RESPONSE_CANCEL
);
157 add_button (_("Insert Plugin(s)"), RESPONSE_APPLY
);
158 set_default_response (RESPONSE_APPLY
);
159 set_response_sensitive (RESPONSE_APPLY
, false);
160 get_vbox()->pack_start (*table
);
162 table
->set_name("PluginSelectorTable");
163 plugin_display
.set_name("PluginSelectorDisplay");
164 //plugin_display.set_name("PluginSelectorList");
165 added_list
.set_name("PluginSelectorList");
167 plugin_display
.signal_button_press_event().connect_notify (mem_fun(*this, &PluginSelector::row_clicked
));
168 plugin_display
.get_selection()->signal_changed().connect (mem_fun(*this, &PluginSelector::display_selection_changed
));
169 plugin_display
.grab_focus();
171 btn_update
->signal_clicked().connect (mem_fun(*this, &PluginSelector::btn_update_clicked
));
172 btn_add
->signal_clicked().connect(mem_fun(*this, &PluginSelector::btn_add_clicked
));
173 btn_remove
->signal_clicked().connect(mem_fun(*this, &PluginSelector::btn_remove_clicked
));
174 added_list
.get_selection()->signal_changed().connect (mem_fun(*this, &PluginSelector::added_list_selection_changed
));
180 PluginSelector::row_clicked(GdkEventButton
* event
)
182 if (event
->type
== GDK_2BUTTON_PRESS
)
187 PluginSelector::set_session (Session
* s
)
189 ENSURE_GUI_THREAD(bind (mem_fun(*this, &PluginSelector::set_session
), s
));
194 session
->GoingAway
.connect (bind (mem_fun(*this, &PluginSelector::set_session
), static_cast<Session
*> (0)));
199 PluginSelector::show_this_plugin (const PluginInfoPtr
& info
, const std::string
& filterstr
)
202 std::string mode
= filter_mode
.get_active_text ();
204 if (mode
== _("Favorites only")) {
205 return manager
->is_a_favorite_plugin (info
);
208 if (!filterstr
.empty()) {
210 if (mode
== _("Name contains")) {
211 compstr
= info
->name
;
212 } else if (mode
== _("Category contains")) {
213 compstr
= info
->category
;
214 } else if (mode
== _("Type contains")) {
216 switch (info
->type
) {
218 compstr
= X_("LADSPA");
221 compstr
= X_("AudioUnit");
231 } else if (mode
== _("Author contains")) {
232 compstr
= info
->creator
;
233 } else if (mode
== _("Library contains")) {
234 compstr
= info
->path
;
237 if (compstr
.empty()) {
241 transform (compstr
.begin(), compstr
.end(), compstr
.begin(), ::toupper
);
243 if (compstr
.find (filterstr
) != string::npos
) {
254 PluginSelector::setup_filter_string (string
& filterstr
)
256 filterstr
= filter_entry
.get_text ();
257 transform (filterstr
.begin(), filterstr
.end(), filterstr
.begin(), ::toupper
);
261 PluginSelector::refill ()
263 std::string filterstr
;
265 in_row_change
= true;
267 plugin_model
->clear ();
269 setup_filter_string (filterstr
);
271 ladspa_refiller (filterstr
);
272 lv2_refiller (filterstr
);
273 vst_refiller (filterstr
);
274 au_refiller (filterstr
);
276 in_row_change
= false;
280 PluginSelector::refiller (const PluginInfoList
& plugs
, const::std::string
& filterstr
, const char* type
)
284 for (PluginInfoList::const_iterator i
= plugs
.begin(); i
!= plugs
.end(); ++i
) {
286 if (show_this_plugin (*i
, filterstr
)) {
288 TreeModel::Row newrow
= *(plugin_model
->append());
289 newrow
[plugin_columns
.favorite
] = manager
->is_a_favorite_plugin (*i
);
290 newrow
[plugin_columns
.name
] = (*i
)->name
;
291 newrow
[plugin_columns
.type_name
] = type
;
292 newrow
[plugin_columns
.category
] = (*i
)->category
;
294 string creator
= (*i
)->creator
;
295 string::size_type pos
= 0;
297 /* stupid LADSPA creator strings */
299 while (pos
< creator
.length() && (isalnum (creator
[pos
]) || isspace (creator
[pos
]))) ++pos
;
300 creator
= creator
.substr (0, pos
);
302 newrow
[plugin_columns
.creator
] = creator
;
304 if ((*i
)->n_inputs
< 0) {
305 newrow
[plugin_columns
.ins
] = "various";
307 snprintf (buf
, sizeof(buf
), "%d", (*i
)->n_inputs
);
308 newrow
[plugin_columns
.ins
] = buf
;
310 if ((*i
)->n_outputs
< 0) {
311 newrow
[plugin_columns
.outs
] = "various";
313 snprintf (buf
, sizeof(buf
), "%d", (*i
)->n_outputs
);
314 newrow
[plugin_columns
.outs
] = buf
;
317 newrow
[plugin_columns
.plugin
] = *i
;
323 PluginSelector::ladspa_refiller (const std::string
& filterstr
)
325 refiller (manager
->ladspa_plugin_info(), filterstr
, "LADSPA");
329 PluginSelector::lv2_refiller (const std::string
& filterstr
)
332 refiller (manager
->lv2_plugin_info(), filterstr
, "LV2");
337 PluginSelector::vst_refiller (const std::string
& filterstr
)
340 refiller (manager
->vst_plugin_info(), filterstr
, "VST");
345 PluginSelector::au_refiller (const std::string
& filterstr
)
347 #ifdef HAVE_AUDIOUNITS
348 refiller (manager
->au_plugin_info(), filterstr
, "AU");
353 PluginSelector::load_plugin (PluginInfoPtr pi
)
359 return pi
->load (*session
);
363 PluginSelector::btn_add_clicked()
367 TreeModel::Row newrow
= *(amodel
->append());
370 row
= *(plugin_display
.get_selection()->get_selected());
371 name
= row
[plugin_columns
.name
];
372 pi
= row
[plugin_columns
.plugin
];
374 newrow
[acols
.text
] = name
;
375 newrow
[acols
.plugin
] = pi
;
377 if (!amodel
->children().empty()) {
378 set_response_sensitive (RESPONSE_APPLY
, true);
383 PluginSelector::btn_remove_clicked()
385 TreeModel::iterator iter
= added_list
.get_selection()->get_selected();
388 if (amodel
->children().empty()) {
389 set_response_sensitive (RESPONSE_APPLY
, false);
394 PluginSelector::btn_update_clicked()
401 PluginSelector::display_selection_changed()
403 if (plugin_display
.get_selection()->count_selected_rows() != 0) {
404 btn_add
->set_sensitive (true);
406 btn_add
->set_sensitive (false);
411 PluginSelector::added_list_selection_changed()
413 if (added_list
.get_selection()->count_selected_rows() != 0) {
414 btn_remove
->set_sensitive (true);
416 btn_remove
->set_sensitive (false);
421 PluginSelector::run ()
424 TreeModel::Children::iterator i
;
425 SelectedPlugins plugins
;
427 r
= (ResponseType
) Dialog::run ();
431 for (i
= amodel
->children().begin(); i
!= amodel
->children().end(); ++i
) {
432 PluginInfoPtr pp
= (*i
)[acols
.plugin
];
433 PluginPtr p
= load_plugin (pp
);
435 plugins
.push_back (p
);
438 if (interested_object
&& !plugins
.empty()) {
439 interested_object
->use_plugins (plugins
);
450 interested_object
= 0;
456 PluginSelector::filter_button_clicked ()
458 filter_entry
.set_text ("");
462 PluginSelector::filter_entry_changed ()
468 PluginSelector::filter_mode_changed ()
470 std::string mode
= filter_mode
.get_active_text ();
472 if (mode
== _("Favorites only")) {
473 filter_entry
.set_sensitive (false);
475 filter_entry
.set_sensitive (true);
482 PluginSelector::on_show ()
484 ArdourDialog::on_show ();
485 filter_entry
.grab_focus ();
488 struct PluginMenuCompare
{
489 bool operator() (PluginInfoPtr a
, PluginInfoPtr b
) const {
492 cmp
= strcasecmp (a
->creator
.c_str(), b
->creator
.c_str());
496 } else if (cmp
== 0) {
497 /* same creator ... compare names */
498 if (strcasecmp (a
->name
.c_str(), b
->name
.c_str()) < 0) {
507 PluginSelector::plugin_menu()
509 using namespace Menu_Helpers
;
511 typedef std::map
<Glib::ustring
,Gtk::Menu
*> SubmenuMap
;
512 SubmenuMap submenu_map
;
516 _menu
->set_name("ArdourContextMenu");
519 MenuList
& items
= _menu
->items();
520 Menu
* favs
= new Menu();
521 favs
->set_name("ArdourContextMenu");
524 items
.push_back (MenuElem (_("Favorites"), *favs
));
525 items
.push_back (MenuElem (_("Plugin Manager"), mem_fun (*this, &PluginSelector::show_manager
)));
526 items
.push_back (SeparatorElem ());
528 PluginInfoList all_plugs
;
530 all_plugs
.insert (all_plugs
.end(), manager
->ladspa_plugin_info().begin(), manager
->ladspa_plugin_info().end());
532 all_plugs
.insert (all_plugs
.end(), manager
->vst_plugin_info().begin(), manager
->vst_plugin_info().end());
534 #ifdef HAVE_AUDIOUNITS
535 all_plugs
.insert (all_plugs
.end(), manager
->au_plugin_info().begin(), manager
->au_plugin_info().end());
538 all_plugs
.insert (all_plugs
.end(), manager
->lv2_plugin_info().begin(), manager
->lv2_plugin_info().end());
541 PluginMenuCompare cmp
;
542 all_plugs
.sort (cmp
);
544 for (PluginInfoList::const_iterator i
= all_plugs
.begin(); i
!= all_plugs
.end(); ++i
) {
545 SubmenuMap::iterator x
;
548 string creator
= (*i
)->creator
;
549 string::size_type pos
= 0;
551 if (manager
->is_a_favorite_plugin (*i
)) {
552 favs
->items().push_back (MenuElem ((*i
)->name
, (bind (mem_fun (*this, &PluginSelector::plugin_chosen_from_menu
), *i
))));
555 /* stupid LADSPA creator strings */
557 while (pos
< creator
.length() && (isalnum (creator
[pos
]) || isspace (creator
[pos
]))) ++pos
;
558 creator
= creator
.substr (0, pos
);
560 if ((x
= submenu_map
.find (creator
)) != submenu_map
.end()) {
563 submenu
= new Gtk::Menu
;
564 items
.push_back (MenuElem (creator
, *submenu
));
565 submenu_map
.insert (pair
<Glib::ustring
,Menu
*> (creator
, submenu
));
566 submenu
->set_name("ArdourContextMenu");
569 submenu
->items().push_back (MenuElem ((*i
)->name
, (bind (mem_fun (*this, &PluginSelector::plugin_chosen_from_menu
), *i
))));
576 PluginSelector::plugin_chosen_from_menu (const PluginInfoPtr
& pi
)
578 PluginPtr p
= load_plugin (pi
);
580 if (p
&& interested_object
) {
581 SelectedPlugins plugins
;
582 plugins
.push_back (p
);
583 interested_object
->use_plugins (plugins
);
586 interested_object
= 0;
590 PluginSelector::favorite_changed (const Glib::ustring
& path
)
598 in_row_change
= true;
600 TreeModel::iterator iter
= plugin_model
->get_iter (path
);
604 bool favorite
= !(*iter
)[plugin_columns
.favorite
];
608 (*iter
)[plugin_columns
.favorite
] = favorite
;
610 /* save new favorites list */
612 pi
= (*iter
)[plugin_columns
.plugin
];
615 manager
->add_favorite (pi
->type
, pi
->unique_id
);
617 manager
->remove_favorite (pi
->type
, pi
->unique_id
);
620 manager
->save_favorites ();
622 in_row_change
= false;
626 PluginSelector::show_manager ()
633 PluginSelector::set_interested_object (PluginInterestedObject
& obj
)
635 interested_object
= &obj
;