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_("Author contains"),
54 N_("Library contains"),
59 PluginSelector::PluginSelector (PluginManager
*mgr
)
60 : ArdourDialog (_("ardour: plugins"), true, false),
61 filter_button (Stock::CLEAR
)
63 set_position (Gtk::WIN_POS_MOUSE
);
64 set_name ("PluginSelectorWindow");
65 add_events (Gdk::KEY_PRESS_MASK
|Gdk::KEY_RELEASE_MASK
);
70 in_row_change
= false;
72 plugin_model
= Gtk::ListStore::create (plugin_columns
);
73 plugin_display
.set_model (plugin_model
);
74 /* XXX translators: try to convert "Fav" into a short term
77 plugin_display
.append_column (_("Fav"), plugin_columns
.favorite
);
78 plugin_display
.append_column (_("Available Plugins"), plugin_columns
.name
);
79 plugin_display
.append_column (_("Type"), plugin_columns
.type_name
);
80 plugin_display
.append_column (_("Category"), plugin_columns
.category
);
81 plugin_display
.append_column (_("Creator"), plugin_columns
.creator
);
82 plugin_display
.append_column (_("# Inputs"),plugin_columns
.ins
);
83 plugin_display
.append_column (_("# Outputs"), plugin_columns
.outs
);
84 plugin_display
.set_headers_visible (true);
85 plugin_display
.set_headers_clickable (true);
86 plugin_display
.set_reorderable (false);
87 plugin_display
.set_rules_hint (true);
89 CellRendererToggle
* fav_cell
= dynamic_cast<CellRendererToggle
*>(plugin_display
.get_column_cell_renderer (0));
90 fav_cell
->property_activatable() = true;
91 fav_cell
->property_radio() = false;
92 fav_cell
->signal_toggled().connect (mem_fun (*this, &PluginSelector::favorite_changed
));
94 scroller
.set_border_width(10);
95 scroller
.set_policy(Gtk::POLICY_AUTOMATIC
, Gtk::POLICY_AUTOMATIC
);
96 scroller
.add(plugin_display
);
98 amodel
= Gtk::ListStore::create(acols
);
99 added_list
.set_model (amodel
);
100 added_list
.append_column (_("Plugins to be connected"), acols
.text
);
101 added_list
.set_headers_visible (true);
102 added_list
.set_reorderable (false);
104 for (int i
= 0; i
<=3; i
++) {
105 Gtk::TreeView::Column
* column
= plugin_display
.get_column(i
);
106 column
->set_sort_column(i
);
109 ascroller
.set_border_width(10);
110 ascroller
.set_policy(Gtk::POLICY_AUTOMATIC
, Gtk::POLICY_AUTOMATIC
);
111 ascroller
.add(added_list
);
112 btn_add
= manage(new Gtk::Button(Stock::ADD
));
113 ARDOUR_UI::instance()->tooltips().set_tip(*btn_add
, _("Add a plugin to the effect list"));
114 btn_add
->set_sensitive (false);
115 btn_remove
= manage(new Gtk::Button(Stock::REMOVE
));
116 btn_remove
->set_sensitive (false);
117 ARDOUR_UI::instance()->tooltips().set_tip(*btn_remove
, _("Remove a plugin from the effect list"));
118 Gtk::Button
*btn_update
= manage(new Gtk::Button(Stock::REFRESH
));
119 ARDOUR_UI::instance()->tooltips().set_tip(*btn_update
, _("Update available plugins"));
121 btn_add
->set_name("PluginSelectorButton");
122 btn_remove
->set_name("PluginSelectorButton");
124 Gtk::Table
* table
= manage(new Gtk::Table(7, 11));
125 table
->set_size_request(750, 500);
126 table
->attach(scroller
, 0, 7, 0, 5);
128 HBox
* filter_box
= manage (new HBox
);
130 vector
<string
> filter_strings
= I18N (_filter_mode_strings
);
131 Gtkmm2ext::set_popdown_strings (filter_mode
, filter_strings
);
132 filter_mode
.set_active_text (filter_strings
.front());
134 filter_box
->pack_start (filter_mode
, false, false);
135 filter_box
->pack_start (filter_entry
, true, true);
136 filter_box
->pack_start (filter_button
, false, false);
138 filter_entry
.signal_changed().connect (mem_fun (*this, &PluginSelector::filter_entry_changed
));
139 filter_button
.signal_clicked().connect (mem_fun (*this, &PluginSelector::filter_button_clicked
));
140 filter_mode
.signal_changed().connect (mem_fun (*this, &PluginSelector::filter_mode_changed
));
144 filter_entry
.show ();
145 filter_button
.show ();
147 table
->attach (*filter_box
, 0, 7, 5, 6, FILL
|EXPAND
, FILL
, 5, 5);
149 table
->attach(*btn_add
, 1, 2, 6, 7, FILL
, FILL
, 5, 5);
150 table
->attach(*btn_remove
, 3, 4, 6, 7, FILL
, FILL
, 5, 5);
151 table
->attach(*btn_update
, 5, 6, 6, 7, FILL
, FILL
, 5, 5);
153 table
->attach(ascroller
, 0, 7, 8, 10);
155 add_button (Stock::CANCEL
, RESPONSE_CANCEL
);
156 add_button (_("Insert Plugin(s)"), RESPONSE_APPLY
);
157 set_default_response (RESPONSE_APPLY
);
158 set_response_sensitive (RESPONSE_APPLY
, false);
159 get_vbox()->pack_start (*table
);
161 table
->set_name("PluginSelectorTable");
162 plugin_display
.set_name("PluginSelectorDisplay");
163 //plugin_display.set_name("PluginSelectorList");
164 added_list
.set_name("PluginSelectorList");
166 plugin_display
.signal_button_press_event().connect_notify (mem_fun(*this, &PluginSelector::row_clicked
));
167 plugin_display
.get_selection()->signal_changed().connect (mem_fun(*this, &PluginSelector::display_selection_changed
));
168 plugin_display
.grab_focus();
170 btn_update
->signal_clicked().connect (mem_fun(*this, &PluginSelector::btn_update_clicked
));
171 btn_add
->signal_clicked().connect(mem_fun(*this, &PluginSelector::btn_add_clicked
));
172 btn_remove
->signal_clicked().connect(mem_fun(*this, &PluginSelector::btn_remove_clicked
));
173 added_list
.get_selection()->signal_changed().connect (mem_fun(*this, &PluginSelector::added_list_selection_changed
));
179 PluginSelector::row_clicked(GdkEventButton
* event
)
181 if (event
->type
== GDK_2BUTTON_PRESS
)
186 PluginSelector::set_session (Session
* s
)
188 ENSURE_GUI_THREAD(bind (mem_fun(*this, &PluginSelector::set_session
), s
));
193 session
->GoingAway
.connect (bind (mem_fun(*this, &PluginSelector::set_session
), static_cast<Session
*> (0)));
198 PluginSelector::show_this_plugin (const PluginInfoPtr
& info
, const std::string
& filterstr
)
201 std::string mode
= filter_mode
.get_active_text ();
203 if (mode
== _("Favorites only")) {
204 return manager
->is_a_favorite_plugin (info
);
207 if (!filterstr
.empty()) {
209 if (mode
== _("Name contains")) {
210 compstr
= info
->name
;
211 } else if (mode
== _("Type contains")) {
212 compstr
= info
->category
;
213 } else if (mode
== _("Author contains")) {
214 compstr
= info
->creator
;
215 } else if (mode
== _("Library contains")) {
216 compstr
= info
->path
;
219 transform (compstr
.begin(), compstr
.end(), compstr
.begin(), ::toupper
);
221 if (compstr
.find (filterstr
) != string::npos
) {
232 PluginSelector::setup_filter_string (string
& filterstr
)
234 filterstr
= filter_entry
.get_text ();
235 transform (filterstr
.begin(), filterstr
.end(), filterstr
.begin(), ::toupper
);
239 PluginSelector::refill ()
241 std::string filterstr
;
243 in_row_change
= true;
245 plugin_model
->clear ();
247 setup_filter_string (filterstr
);
249 ladspa_refiller (filterstr
);
250 lv2_refiller (filterstr
);
251 vst_refiller (filterstr
);
252 au_refiller (filterstr
);
254 in_row_change
= false;
258 PluginSelector::refiller (const PluginInfoList
& plugs
, const::std::string
& filterstr
, const char* type
)
262 for (PluginInfoList::const_iterator i
= plugs
.begin(); i
!= plugs
.end(); ++i
) {
264 if (show_this_plugin (*i
, filterstr
)) {
266 TreeModel::Row newrow
= *(plugin_model
->append());
267 newrow
[plugin_columns
.favorite
] = manager
->is_a_favorite_plugin (*i
);
268 newrow
[plugin_columns
.name
] = (*i
)->name
;
269 newrow
[plugin_columns
.type_name
] = type
;
270 newrow
[plugin_columns
.category
] = (*i
)->category
;
272 string creator
= (*i
)->creator
;
273 string::size_type pos
= 0;
275 /* stupid LADSPA creator strings */
277 while (pos
< creator
.length() && (isalnum (creator
[pos
]) || isspace (creator
[pos
]))) ++pos
;
278 creator
= creator
.substr (0, pos
);
280 newrow
[plugin_columns
.creator
] = creator
;
282 if ((*i
)->n_inputs
< 0) {
283 newrow
[plugin_columns
.ins
] = "various";
285 snprintf (buf
, sizeof(buf
), "%d", (*i
)->n_inputs
);
286 newrow
[plugin_columns
.ins
] = buf
;
288 if ((*i
)->n_outputs
< 0) {
289 newrow
[plugin_columns
.outs
] = "various";
291 snprintf (buf
, sizeof(buf
), "%d", (*i
)->n_outputs
);
292 newrow
[plugin_columns
.outs
] = buf
;
295 newrow
[plugin_columns
.plugin
] = *i
;
301 PluginSelector::ladspa_refiller (const std::string
& filterstr
)
303 refiller (manager
->ladspa_plugin_info(), filterstr
, "LADSPA");
307 PluginSelector::lv2_refiller (const std::string
& filterstr
)
310 refiller (manager
->lv2_plugin_info(), filterstr
, "LV2");
315 PluginSelector::vst_refiller (const std::string
& filterstr
)
318 refiller (manager
->vst_plugin_info(), filterstr
, "VST");
323 PluginSelector::au_refiller (const std::string
& filterstr
)
325 #ifdef HAVE_AUDIOUNITS
326 refiller (manager
->au_plugin_info(), filterstr
, "AU");
331 PluginSelector::load_plugin (PluginInfoPtr pi
)
337 return pi
->load (*session
);
341 PluginSelector::btn_add_clicked()
345 TreeModel::Row newrow
= *(amodel
->append());
348 row
= *(plugin_display
.get_selection()->get_selected());
349 name
= row
[plugin_columns
.name
];
350 pi
= row
[plugin_columns
.plugin
];
352 newrow
[acols
.text
] = name
;
353 newrow
[acols
.plugin
] = pi
;
355 if (!amodel
->children().empty()) {
356 set_response_sensitive (RESPONSE_APPLY
, true);
361 PluginSelector::btn_remove_clicked()
363 TreeModel::iterator iter
= added_list
.get_selection()->get_selected();
366 if (amodel
->children().empty()) {
367 set_response_sensitive (RESPONSE_APPLY
, false);
372 PluginSelector::btn_update_clicked()
379 PluginSelector::display_selection_changed()
381 if (plugin_display
.get_selection()->count_selected_rows() != 0) {
382 btn_add
->set_sensitive (true);
384 btn_add
->set_sensitive (false);
389 PluginSelector::added_list_selection_changed()
391 if (added_list
.get_selection()->count_selected_rows() != 0) {
392 btn_remove
->set_sensitive (true);
394 btn_remove
->set_sensitive (false);
399 PluginSelector::run ()
402 TreeModel::Children::iterator i
;
403 SelectedPlugins plugins
;
405 r
= (ResponseType
) Dialog::run ();
409 for (i
= amodel
->children().begin(); i
!= amodel
->children().end(); ++i
) {
410 PluginInfoPtr pp
= (*i
)[acols
.plugin
];
411 PluginPtr p
= load_plugin (pp
);
413 plugins
.push_back (p
);
416 if (interested_object
&& !plugins
.empty()) {
417 interested_object
->use_plugins (plugins
);
428 interested_object
= 0;
434 PluginSelector::filter_button_clicked ()
436 filter_entry
.set_text ("");
440 PluginSelector::filter_entry_changed ()
446 PluginSelector::filter_mode_changed ()
448 std::string mode
= filter_mode
.get_active_text ();
450 if (mode
== _("Favorites only")) {
451 filter_entry
.set_sensitive (false);
453 filter_entry
.set_sensitive (true);
460 PluginSelector::on_show ()
462 ArdourDialog::on_show ();
463 filter_entry
.grab_focus ();
466 struct PluginMenuCompare
{
467 bool operator() (PluginInfoPtr a
, PluginInfoPtr b
) const {
470 cmp
= strcasecmp (a
->creator
.c_str(), b
->creator
.c_str());
474 } else if (cmp
== 0) {
475 /* same creator ... compare names */
476 if (strcasecmp (a
->name
.c_str(), b
->name
.c_str()) < 0) {
485 PluginSelector::plugin_menu()
487 using namespace Menu_Helpers
;
489 typedef std::map
<Glib::ustring
,Gtk::Menu
*> SubmenuMap
;
490 SubmenuMap submenu_map
;
494 _menu
->set_name("ArdourContextMenu");
497 MenuList
& items
= _menu
->items();
498 Menu
* favs
= new Menu();
499 favs
->set_name("ArdourContextMenu");
502 items
.push_back (MenuElem (_("Favorites"), *favs
));
503 items
.push_back (MenuElem (_("Plugin Manager"), mem_fun (*this, &PluginSelector::show_manager
)));
504 items
.push_back (SeparatorElem ());
506 PluginInfoList all_plugs
;
508 all_plugs
.insert (all_plugs
.end(), manager
->ladspa_plugin_info().begin(), manager
->ladspa_plugin_info().end());
510 all_plugs
.insert (all_plugs
.end(), manager
->vst_plugin_info().begin(), manager
->vst_plugin_info().end());
512 #ifdef HAVE_AUDIOUNITS
513 all_plugs
.insert (all_plugs
.end(), manager
->au_plugin_info().begin(), manager
->au_plugin_info().end());
516 all_plugs
.insert (all_plugs
.end(), manager
->lv2_plugin_info().begin(), manager
->lv2_plugin_info().end());
519 PluginMenuCompare cmp
;
520 all_plugs
.sort (cmp
);
522 for (PluginInfoList::const_iterator i
= all_plugs
.begin(); i
!= all_plugs
.end(); ++i
) {
523 SubmenuMap::iterator x
;
526 string creator
= (*i
)->creator
;
527 string::size_type pos
= 0;
529 if (manager
->is_a_favorite_plugin (*i
)) {
530 favs
->items().push_back (MenuElem ((*i
)->name
, (bind (mem_fun (*this, &PluginSelector::plugin_chosen_from_menu
), *i
))));
533 /* stupid LADSPA creator strings */
535 while (pos
< creator
.length() && (isalnum (creator
[pos
]) || isspace (creator
[pos
]))) ++pos
;
536 creator
= creator
.substr (0, pos
);
538 if ((x
= submenu_map
.find (creator
)) != submenu_map
.end()) {
541 submenu
= new Gtk::Menu
;
542 items
.push_back (MenuElem (creator
, *submenu
));
543 submenu_map
.insert (pair
<Glib::ustring
,Menu
*> (creator
, submenu
));
544 submenu
->set_name("ArdourContextMenu");
547 submenu
->items().push_back (MenuElem ((*i
)->name
, (bind (mem_fun (*this, &PluginSelector::plugin_chosen_from_menu
), *i
))));
554 PluginSelector::plugin_chosen_from_menu (const PluginInfoPtr
& pi
)
556 PluginPtr p
= load_plugin (pi
);
558 if (p
&& interested_object
) {
559 SelectedPlugins plugins
;
560 plugins
.push_back (p
);
561 interested_object
->use_plugins (plugins
);
564 interested_object
= 0;
568 PluginSelector::favorite_changed (const Glib::ustring
& path
)
576 in_row_change
= true;
578 TreeModel::iterator iter
= plugin_model
->get_iter (path
);
582 bool favorite
= !(*iter
)[plugin_columns
.favorite
];
586 (*iter
)[plugin_columns
.favorite
] = favorite
;
588 /* save new favorites list */
590 pi
= (*iter
)[plugin_columns
.plugin
];
593 manager
->add_favorite (pi
->type
, pi
->unique_id
);
595 manager
->remove_favorite (pi
->type
, pi
->unique_id
);
598 manager
->save_favorites ();
600 in_row_change
= false;
604 PluginSelector::show_manager ()
611 PluginSelector::set_interested_object (PluginInterestedObject
& obj
)
613 interested_object
= &obj
;