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"),
61 PluginSelector::PluginSelector (PluginManager
*mgr
)
62 : ArdourDialog (_("ardour: plugins"), true, false),
63 filter_button (Stock::CLEAR
)
65 set_position (Gtk::WIN_POS_MOUSE
);
66 set_name ("PluginSelectorWindow");
67 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
76 related to "favorite" and "Hid" into a short term
79 plugin_display
.append_column (_("Fav"), plugin_columns
.favorite
);
80 plugin_display
.append_column (_("Hid"), plugin_columns
.hidden
);
81 plugin_display
.append_column (_("Available Plugins"), plugin_columns
.name
);
82 plugin_display
.append_column (_("Type"), plugin_columns
.type_name
);
83 plugin_display
.append_column (_("Category"), plugin_columns
.category
);
84 plugin_display
.append_column (_("Creator"), plugin_columns
.creator
);
85 plugin_display
.append_column (_("# Inputs"),plugin_columns
.ins
);
86 plugin_display
.append_column (_("# Outputs"), plugin_columns
.outs
);
87 plugin_display
.set_headers_visible (true);
88 plugin_display
.set_headers_clickable (true);
89 plugin_display
.set_reorderable (false);
90 plugin_display
.set_rules_hint (true);
92 CellRendererToggle
* fav_cell
= dynamic_cast<CellRendererToggle
*>(plugin_display
.get_column_cell_renderer (0));
93 fav_cell
->property_activatable() = true;
94 fav_cell
->property_radio() = true;
95 fav_cell
->signal_toggled().connect (mem_fun (*this, &PluginSelector::favorite_changed
));
97 CellRendererToggle
* hidden_cell
= dynamic_cast<CellRendererToggle
*>(plugin_display
.get_column_cell_renderer (1));
98 hidden_cell
->property_activatable() = true;
99 hidden_cell
->property_radio() = true;
100 hidden_cell
->signal_toggled().connect (mem_fun (*this, &PluginSelector::hidden_changed
));
102 scroller
.set_border_width(10);
103 scroller
.set_policy(Gtk::POLICY_AUTOMATIC
, Gtk::POLICY_AUTOMATIC
);
104 scroller
.add(plugin_display
);
106 amodel
= Gtk::ListStore::create(acols
);
107 added_list
.set_model (amodel
);
108 added_list
.append_column (_("Plugins to be connected"), acols
.text
);
109 added_list
.set_headers_visible (true);
110 added_list
.set_reorderable (false);
112 for (int i
= 0; i
<=4; i
++) {
113 Gtk::TreeView::Column
* column
= plugin_display
.get_column(i
);
114 column
->set_sort_column(i
);
117 ascroller
.set_border_width(10);
118 ascroller
.set_policy(Gtk::POLICY_AUTOMATIC
, Gtk::POLICY_AUTOMATIC
);
119 ascroller
.add(added_list
);
120 btn_add
= manage(new Gtk::Button(Stock::ADD
));
121 ARDOUR_UI::instance()->tooltips().set_tip(*btn_add
, _("Add a plugin to the effect list"));
122 btn_add
->set_sensitive (false);
123 btn_remove
= manage(new Gtk::Button(Stock::REMOVE
));
124 btn_remove
->set_sensitive (false);
125 ARDOUR_UI::instance()->tooltips().set_tip(*btn_remove
, _("Remove a plugin from the effect list"));
126 Gtk::Button
*btn_update
= manage(new Gtk::Button(Stock::REFRESH
));
127 ARDOUR_UI::instance()->tooltips().set_tip(*btn_update
, _("Update available plugins"));
129 btn_add
->set_name("PluginSelectorButton");
130 btn_remove
->set_name("PluginSelectorButton");
132 Gtk::Table
* table
= manage(new Gtk::Table(7, 11));
133 table
->set_size_request(750, 500);
134 table
->attach(scroller
, 0, 7, 0, 5);
136 HBox
* filter_box
= manage (new HBox
);
138 vector
<string
> filter_strings
= I18N (_filter_mode_strings
);
139 Gtkmm2ext::set_popdown_strings (filter_mode
, filter_strings
);
140 filter_mode
.set_active_text (filter_strings
.front());
142 filter_box
->pack_start (filter_mode
, false, false);
143 filter_box
->pack_start (filter_entry
, true, true);
144 filter_box
->pack_start (filter_button
, false, false);
146 filter_entry
.signal_changed().connect (mem_fun (*this, &PluginSelector::filter_entry_changed
));
147 filter_button
.signal_clicked().connect (mem_fun (*this, &PluginSelector::filter_button_clicked
));
148 filter_mode
.signal_changed().connect (mem_fun (*this, &PluginSelector::filter_mode_changed
));
152 filter_entry
.show ();
153 filter_button
.show ();
155 table
->attach (*filter_box
, 0, 7, 5, 6, FILL
|EXPAND
, FILL
, 5, 5);
157 table
->attach(*btn_add
, 1, 2, 6, 7, FILL
, FILL
, 5, 5);
158 table
->attach(*btn_remove
, 3, 4, 6, 7, FILL
, FILL
, 5, 5);
159 table
->attach(*btn_update
, 5, 6, 6, 7, FILL
, FILL
, 5, 5);
161 table
->attach(ascroller
, 0, 7, 8, 10);
163 add_button (Stock::CLOSE
, RESPONSE_CLOSE
);
164 add_button (_("Insert Plugin(s)"), RESPONSE_APPLY
);
165 set_default_response (RESPONSE_APPLY
);
166 set_response_sensitive (RESPONSE_APPLY
, false);
167 get_vbox()->pack_start (*table
);
169 table
->set_name("PluginSelectorTable");
170 plugin_display
.set_name("PluginSelectorDisplay");
171 //plugin_display.set_name("PluginSelectorList");
172 added_list
.set_name("PluginSelectorList");
174 plugin_display
.signal_button_press_event().connect_notify (mem_fun(*this, &PluginSelector::row_clicked
));
175 plugin_display
.get_selection()->signal_changed().connect (mem_fun(*this, &PluginSelector::display_selection_changed
));
176 plugin_display
.grab_focus();
178 btn_update
->signal_clicked().connect (mem_fun(*this, &PluginSelector::btn_update_clicked
));
179 btn_add
->signal_clicked().connect(mem_fun(*this, &PluginSelector::btn_add_clicked
));
180 btn_remove
->signal_clicked().connect(mem_fun(*this, &PluginSelector::btn_remove_clicked
));
181 added_list
.get_selection()->signal_changed().connect (mem_fun(*this, &PluginSelector::added_list_selection_changed
));
187 PluginSelector::row_clicked(GdkEventButton
* event
)
189 if (event
->type
== GDK_2BUTTON_PRESS
)
194 PluginSelector::set_session (Session
* s
)
196 ENSURE_GUI_THREAD(bind (mem_fun(*this, &PluginSelector::set_session
), s
));
201 session
->GoingAway
.connect (bind (mem_fun(*this, &PluginSelector::set_session
), static_cast<Session
*> (0)));
206 PluginSelector::show_this_plugin (const PluginInfoPtr
& info
, const std::string
& filterstr
)
209 std::string mode
= filter_mode
.get_active_text ();
211 if (mode
== _("Favorites only")) {
212 return manager
->get_status (info
) == PluginManager::Favorite
;
215 if (mode
== _("Hidden only")) {
216 return manager
->get_status (info
) == PluginManager::Hidden
;
219 if (!filterstr
.empty()) {
221 if (mode
== _("Name contains")) {
222 compstr
= info
->name
;
223 } else if (mode
== _("Category contains")) {
224 compstr
= info
->category
;
225 } else if (mode
== _("Type contains")) {
227 switch (info
->type
) {
229 compstr
= X_("LADSPA");
232 compstr
= X_("AudioUnit");
242 } else if (mode
== _("Author contains")) {
243 compstr
= info
->creator
;
244 } else if (mode
== _("Library contains")) {
245 compstr
= info
->path
;
248 if (compstr
.empty()) {
252 transform (compstr
.begin(), compstr
.end(), compstr
.begin(), ::toupper
);
254 if (compstr
.find (filterstr
) != string::npos
) {
265 PluginSelector::setup_filter_string (string
& filterstr
)
267 filterstr
= filter_entry
.get_text ();
268 transform (filterstr
.begin(), filterstr
.end(), filterstr
.begin(), ::toupper
);
272 PluginSelector::refill ()
274 std::string filterstr
;
276 in_row_change
= true;
278 plugin_model
->clear ();
280 setup_filter_string (filterstr
);
282 ladspa_refiller (filterstr
);
283 lv2_refiller (filterstr
);
284 vst_refiller (filterstr
);
285 au_refiller (filterstr
);
287 in_row_change
= false;
291 PluginSelector::refiller (const PluginInfoList
& plugs
, const::std::string
& filterstr
, const char* type
)
295 for (PluginInfoList::const_iterator i
= plugs
.begin(); i
!= plugs
.end(); ++i
) {
297 if (show_this_plugin (*i
, filterstr
)) {
299 TreeModel::Row newrow
= *(plugin_model
->append());
300 newrow
[plugin_columns
.favorite
] = (manager
->get_status (*i
) == PluginManager::Favorite
);
301 newrow
[plugin_columns
.hidden
] = (manager
->get_status (*i
) == PluginManager::Hidden
);
302 newrow
[plugin_columns
.name
] = (*i
)->name
;
303 newrow
[plugin_columns
.type_name
] = type
;
304 newrow
[plugin_columns
.category
] = (*i
)->category
;
306 string creator
= (*i
)->creator
;
307 string::size_type pos
= 0;
309 /* stupid LADSPA creator strings */
311 while (pos
< creator
.length() && (isalnum (creator
[pos
]) || isspace (creator
[pos
]))) ++pos
;
312 creator
= creator
.substr (0, pos
);
314 newrow
[plugin_columns
.creator
] = creator
;
316 if ((*i
)->n_inputs
< 0) {
317 newrow
[plugin_columns
.ins
] = "various";
319 snprintf (buf
, sizeof(buf
), "%d", (*i
)->n_inputs
);
320 newrow
[plugin_columns
.ins
] = buf
;
322 if ((*i
)->n_outputs
< 0) {
323 newrow
[plugin_columns
.outs
] = "various";
325 snprintf (buf
, sizeof(buf
), "%d", (*i
)->n_outputs
);
326 newrow
[plugin_columns
.outs
] = buf
;
329 newrow
[plugin_columns
.plugin
] = *i
;
335 PluginSelector::ladspa_refiller (const std::string
& filterstr
)
337 refiller (manager
->ladspa_plugin_info(), filterstr
, "LADSPA");
341 PluginSelector::lv2_refiller (const std::string
& filterstr
)
344 refiller (manager
->lv2_plugin_info(), filterstr
, "LV2");
349 PluginSelector::vst_refiller (const std::string
& filterstr
)
352 refiller (manager
->vst_plugin_info(), filterstr
, "VST");
357 PluginSelector::au_refiller (const std::string
& filterstr
)
359 #ifdef HAVE_AUDIOUNITS
360 refiller (manager
->au_plugin_info(), filterstr
, "AU");
365 PluginSelector::load_plugin (PluginInfoPtr pi
)
371 return pi
->load (*session
);
375 PluginSelector::btn_add_clicked()
379 TreeModel::Row newrow
= *(amodel
->append());
382 row
= *(plugin_display
.get_selection()->get_selected());
383 name
= row
[plugin_columns
.name
];
384 pi
= row
[plugin_columns
.plugin
];
386 newrow
[acols
.text
] = name
;
387 newrow
[acols
.plugin
] = pi
;
389 if (!amodel
->children().empty()) {
390 set_response_sensitive (RESPONSE_APPLY
, true);
395 PluginSelector::btn_remove_clicked()
397 TreeModel::iterator iter
= added_list
.get_selection()->get_selected();
400 if (amodel
->children().empty()) {
401 set_response_sensitive (RESPONSE_APPLY
, false);
406 PluginSelector::btn_update_clicked()
413 PluginSelector::display_selection_changed()
415 if (plugin_display
.get_selection()->count_selected_rows() != 0) {
416 btn_add
->set_sensitive (true);
418 btn_add
->set_sensitive (false);
423 PluginSelector::added_list_selection_changed()
425 if (added_list
.get_selection()->count_selected_rows() != 0) {
426 btn_remove
->set_sensitive (true);
428 btn_remove
->set_sensitive (false);
433 PluginSelector::run ()
436 TreeModel::Children::iterator i
;
437 SelectedPlugins plugins
;
439 r
= (ResponseType
) Dialog::run ();
443 for (i
= amodel
->children().begin(); i
!= amodel
->children().end(); ++i
) {
444 PluginInfoPtr pp
= (*i
)[acols
.plugin
];
445 PluginPtr p
= load_plugin (pp
);
447 plugins
.push_back (p
);
450 if (interested_object
&& !plugins
.empty()) {
451 interested_object
->use_plugins (plugins
);
462 interested_object
= 0;
468 PluginSelector::filter_button_clicked ()
470 filter_entry
.set_text ("");
474 PluginSelector::filter_entry_changed ()
480 PluginSelector::filter_mode_changed ()
482 std::string mode
= filter_mode
.get_active_text ();
484 if (mode
== _("Favorites only") || mode
== _("Hidden only")) {
485 filter_entry
.set_sensitive (false);
487 filter_entry
.set_sensitive (true);
494 PluginSelector::on_show ()
496 ArdourDialog::on_show ();
497 filter_entry
.grab_focus ();
500 struct PluginMenuCompareByCreator
{
501 bool operator() (PluginInfoPtr a
, PluginInfoPtr b
) const {
504 cmp
= strcasecmp (a
->creator
.c_str(), b
->creator
.c_str());
508 } else if (cmp
== 0) {
509 /* same creator ... compare names */
510 if (strcasecmp (a
->name
.c_str(), b
->name
.c_str()) < 0) {
518 struct PluginMenuCompareByName
{
519 bool operator() (PluginInfoPtr a
, PluginInfoPtr b
) const {
522 cmp
= strcasecmp (a
->name
.c_str(), b
->name
.c_str());
526 } else if (cmp
== 0) {
527 /* same name ... compare type */
528 if (a
->type
< b
->type
) {
536 struct PluginMenuCompareByCategory
{
537 bool operator() (PluginInfoPtr a
, PluginInfoPtr b
) const {
540 cmp
= strcasecmp (a
->category
.c_str(), b
->category
.c_str());
544 } else if (cmp
== 0) {
545 /* same category ... compare names */
546 if (strcasecmp (a
->name
.c_str(), b
->name
.c_str()) < 0) {
555 PluginSelector::plugin_menu()
557 PluginInfoList all_plugs
;
559 all_plugs
.insert (all_plugs
.end(), manager
->ladspa_plugin_info().begin(), manager
->ladspa_plugin_info().end());
561 all_plugs
.insert (all_plugs
.end(), manager
->vst_plugin_info().begin(), manager
->vst_plugin_info().end());
563 #ifdef HAVE_AUDIOUNITS
564 all_plugs
.insert (all_plugs
.end(), manager
->au_plugin_info().begin(), manager
->au_plugin_info().end());
567 all_plugs
.insert (all_plugs
.end(), manager
->lv2_plugin_info().begin(), manager
->lv2_plugin_info().end());
570 using namespace Menu_Helpers
;
572 Menu
* menu
= manage (new Menu());
573 menu
->set_name("ArdourContextMenu");
575 MenuList
& items
= menu
->items();
578 Gtk::Menu
* favs
= create_favs_menu(all_plugs
);
579 items
.push_back (MenuElem (_("Favorites"), *manage (favs
)));
581 items
.push_back (MenuElem (_("Plugin Manager"), mem_fun (*this, &PluginSelector::show_manager
)));
582 items
.push_back (SeparatorElem ());
584 Menu
* by_creator
= create_by_creator_menu(all_plugs
);
585 items
.push_back (MenuElem (_("By Creator"), *manage (by_creator
)));
587 Menu
* by_category
= create_by_category_menu(all_plugs
);
588 items
.push_back (MenuElem (_("By Category"), *manage (by_category
)));
594 PluginSelector::create_favs_menu (PluginInfoList
& all_plugs
)
596 using namespace Menu_Helpers
;
598 Menu
* favs
= manage (new Menu());
599 favs
->set_name("ArdourContextMenu");
601 PluginMenuCompareByName cmp_by_name
;
602 all_plugs
.sort (cmp_by_name
);
604 for (PluginInfoList::const_iterator i
= all_plugs
.begin(); i
!= all_plugs
.end(); ++i
) {
605 if (manager
->get_status (*i
) == PluginManager::Favorite
) {
606 favs
->items().push_back (MenuElem ((*i
)->name
, (bind (mem_fun (*this, &PluginSelector::plugin_chosen_from_menu
), *i
))));
613 PluginSelector::create_by_creator_menu (ARDOUR::PluginInfoList
& all_plugs
)
615 using namespace Menu_Helpers
;
617 typedef std::map
<Glib::ustring
,Gtk::Menu
*> SubmenuMap
;
618 SubmenuMap creator_submenu_map
;
620 Menu
* by_creator
= manage (new Menu());
621 by_creator
->set_name("ArdourContextMenu");
623 MenuList
& by_creator_items
= by_creator
->items();
624 PluginMenuCompareByCreator cmp_by_creator
;
625 all_plugs
.sort (cmp_by_creator
);
627 for (PluginInfoList::const_iterator i
= all_plugs
.begin(); i
!= all_plugs
.end(); ++i
) {
629 if (manager
->get_status (*i
) == PluginManager::Hidden
) continue;
631 string creator
= (*i
)->creator
;
633 /* stupid LADSPA creator strings */
634 string::size_type pos
= 0;
635 while (pos
< creator
.length() && (isalnum (creator
[pos
]) || isspace (creator
[pos
]))) ++pos
;
636 creator
= creator
.substr (0, pos
);
638 SubmenuMap::iterator x
;
640 if ((x
= creator_submenu_map
.find (creator
)) != creator_submenu_map
.end()) {
643 submenu
= new Gtk::Menu
;
644 by_creator_items
.push_back (MenuElem (creator
, *manage (submenu
)));
645 creator_submenu_map
.insert (pair
<Glib::ustring
,Menu
*> (creator
, submenu
));
646 submenu
->set_name("ArdourContextMenu");
648 submenu
->items().push_back (MenuElem ((*i
)->name
, (bind (mem_fun (*this, &PluginSelector::plugin_chosen_from_menu
), *i
))));
654 PluginSelector::create_by_category_menu (ARDOUR::PluginInfoList
& all_plugs
)
656 using namespace Menu_Helpers
;
658 typedef std::map
<Glib::ustring
,Gtk::Menu
*> SubmenuMap
;
659 SubmenuMap category_submenu_map
;
661 Menu
* by_category
= manage (new Menu());
662 by_category
->set_name("ArdourContextMenu");
664 MenuList
& by_category_items
= by_category
->items();
665 PluginMenuCompareByCategory cmp_by_category
;
666 all_plugs
.sort (cmp_by_category
);
668 for (PluginInfoList::const_iterator i
= all_plugs
.begin(); i
!= all_plugs
.end(); ++i
) {
670 if (manager
->get_status (*i
) == PluginManager::Hidden
) continue;
672 string category
= (*i
)->category
;
674 SubmenuMap::iterator x
;
676 if ((x
= category_submenu_map
.find (category
)) != category_submenu_map
.end()) {
679 submenu
= new Gtk::Menu
;
680 by_category_items
.push_back (MenuElem (category
, *manage (submenu
)));
681 category_submenu_map
.insert (pair
<Glib::ustring
,Menu
*> (category
, submenu
));
682 submenu
->set_name("ArdourContextMenu");
684 submenu
->items().push_back (MenuElem ((*i
)->name
, (bind (mem_fun (*this, &PluginSelector::plugin_chosen_from_menu
), *i
))));
690 PluginSelector::plugin_chosen_from_menu (const PluginInfoPtr
& pi
)
692 PluginPtr p
= load_plugin (pi
);
694 if (p
&& interested_object
) {
695 SelectedPlugins plugins
;
696 plugins
.push_back (p
);
697 interested_object
->use_plugins (plugins
);
700 interested_object
= 0;
704 PluginSelector::favorite_changed (const Glib::ustring
& path
)
712 in_row_change
= true;
714 TreeModel::iterator iter
= plugin_model
->get_iter (path
);
718 bool favorite
= !(*iter
)[plugin_columns
.favorite
];
722 (*iter
)[plugin_columns
.favorite
] = favorite
;
723 (*iter
)[plugin_columns
.hidden
] = false;
724 PluginManager::PluginStatusType status
= (favorite
? PluginManager::Favorite
: PluginManager::Normal
);
726 /* save new statuses list */
728 pi
= (*iter
)[plugin_columns
.plugin
];
730 manager
->set_status (pi
->type
, pi
->unique_id
, status
);
732 manager
->save_statuses ();
734 in_row_change
= false;
738 PluginSelector::hidden_changed (const Glib::ustring
& path
)
746 in_row_change
= true;
748 TreeModel::iterator iter
= plugin_model
->get_iter (path
);
752 bool hidden
= !(*iter
)[plugin_columns
.hidden
];
756 (*iter
)[plugin_columns
.favorite
] = false;
757 (*iter
)[plugin_columns
.hidden
] = hidden
;
758 PluginManager::PluginStatusType status
= (hidden
? PluginManager::Hidden
: PluginManager::Normal
);
760 /* save new statuses list */
762 pi
= (*iter
)[plugin_columns
.plugin
];
764 manager
->set_status (pi
->type
, pi
->unique_id
, status
);
766 manager
->save_statuses ();
768 in_row_change
= false;
772 PluginSelector::show_manager ()
779 PluginSelector::set_interested_object (PluginInterestedObject
& obj
)
781 interested_object
= &obj
;