1 /*
2  * Copyright (C) 2005-2006 Doug McLain <doug@nostar.net>
3  * Copyright (C) 2005-2007 Taybin Rutkin <taybin@taybin.com>
4  * Copyright (C) 2005-2017 Paul Davis <paul@linuxaudiosystems.com>
5  * Copyright (C) 2006 Nick Mainsbridge <mainsbridge@gmail.com>
6  * Copyright (C) 2007-2012 David Robillard <d@drobilla.net>
7  * Copyright (C) 2009-2011 Carl Hetherington <carl@carlh.net>
8  * Copyright (C) 2013-2014 John Emmas <john@creativepost.co.uk>
9  * Copyright (C) 2014-2018 Ben Loftis <ben@harrisonconsoles.com>
10  * Copyright (C) 2014-2019 Robin Gareus <robin@gareus.org>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License along
23  * with this program; if not, write to the Free Software Foundation, Inc.,
24  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25  */
26 #ifdef WAF_BUILD
27 #include "gtk2ardour-config.h"
28 #endif
29 
30 #include <cstdio>
31 #include <map>
32 
33 #include <algorithm>
34 
35 #include <gtkmm/button.h>
36 #include <gtkmm/comboboxtext.h>
37 #include <gtkmm/frame.h>
38 #include <gtkmm/messagedialog.h>
39 #include <gtkmm/notebook.h>
40 #include <gtkmm/stock.h>
41 #include <gtkmm/table.h>
42 #include <gtkmm/treestore.h>
43 
44 #include "gtkmm2ext/utils.h"
45 
46 #include "widgets/tooltips.h"
47 
48 #include "pbd/convert.h"
49 #include "pbd/tokenizer.h"
50 
51 #include "ardour/utils.h"
52 #include "ardour/rc_configuration.h"
53 
54 #include "ardour_message.h"
55 #include "plugin_scan_dialog.h"
56 #include "plugin_selector.h"
57 #include "plugin_utils.h"
58 #include "gui_thread.h"
59 #include "ui_config.h"
60 
61 #include "pbd/i18n.h"
62 
63 using namespace ARDOUR;
64 using namespace PBD;
65 using namespace Gtk;
66 using namespace std;
67 using namespace ArdourWidgets;
68 using namespace ARDOUR_PLUGIN_UTILS;
69 
70 static const uint32_t MAX_CREATOR_LEN = 24;
71 
PluginSelector(PluginManager & mgr)72 PluginSelector::PluginSelector (PluginManager& mgr)
73 	: ArdourDialog (_("Plugin Selector"), true, false)
74 	, search_clear_button (Stock::CLEAR)
75 	, manager (mgr)
76 	, _need_tag_save (false)
77 	, _need_status_save (false)
78 	, _need_menu_rebuild (false)
79 	, _inhibit_refill (false)
80 {
81 	set_name ("PluginSelectorWindow");
82 	add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
83 
84 	_plugin_menu = 0;
85 	in_row_change = false;
86 
87 	manager.PluginListChanged.connect (plugin_list_changed_connection, invalidator (*this), boost::bind (&PluginSelector::build_plugin_menu, this), gui_context());
88 	manager.PluginStatusChanged.connect (plugin_list_changed_connection, invalidator (*this), boost::bind (&PluginSelector::build_plugin_menu, this), gui_context());
89 	manager.PluginTagChanged.connect (plugin_list_changed_connection, invalidator (*this), boost::bind (&PluginSelector::build_plugin_menu, this), gui_context());
90 
91 	manager.PluginStatusChanged.connect (plugin_list_changed_connection, invalidator (*this), boost::bind (&PluginSelector::plugin_status_changed, this, _1, _2, _3), gui_context());
92 	manager.PluginTagChanged.connect(plugin_list_changed_connection, invalidator (*this), boost::bind (&PluginSelector::tags_changed, this, _1, _2, _3), gui_context());
93 
94 	plugin_model = Gtk::ListStore::create (plugin_columns);
95 	plugin_display.set_model (plugin_model);
96 	/* XXX translators: try to convert "Fav" into a short term
97 	 * related to "favorite" and "Hid" into a short term
98 	 * related to "hidden"
99 	 */
100 	plugin_display.append_column (_("Fav"), plugin_columns.favorite);
101 	plugin_display.append_column (_("Hide"), plugin_columns.hidden);
102 	plugin_display.append_column (_("Name"), plugin_columns.name);
103 	plugin_display.append_column (_("Tags"), plugin_columns.tags);
104 	plugin_display.append_column (_("Creator"), plugin_columns.creator);
105 	plugin_display.append_column (_("Type"), plugin_columns.type_name);
106 	plugin_display.append_column (_("Audio I/O"),plugin_columns.audio_io);
107 	plugin_display.append_column (_("MIDI I/O"), plugin_columns.midi_io);
108 	plugin_display.set_headers_visible (true);
109 	plugin_display.set_headers_clickable (true);
110 	plugin_display.set_reorderable (false);
111 	plugin_display.set_rules_hint (true);
112 	plugin_display.add_object_drag (plugin_columns.plugin.index(), "PluginInfoPtr");
113 	plugin_display.set_drag_column (plugin_columns.name.index());
114 
115 	// setting a sort-column prevents re-ordering via Drag/Drop
116 	plugin_model->set_sort_column (plugin_columns.name.index(), Gtk::SORT_ASCENDING);
117 
118 	plugin_display.set_name("PluginSelectorDisplay");
119 	plugin_display.signal_row_activated().connect_notify (sigc::mem_fun(*this, &PluginSelector::row_activated));
120 	plugin_display.get_selection()->signal_changed().connect (sigc::mem_fun(*this, &PluginSelector::display_selection_changed));
121 
122 	CellRendererToggle* fav_cell = dynamic_cast<CellRendererToggle*>(plugin_display.get_column_cell_renderer (0));
123 	fav_cell->property_activatable() = true;
124 	fav_cell->property_radio() = true;
125 	fav_cell->signal_toggled().connect (sigc::mem_fun (*this, &PluginSelector::favorite_changed));
126 
127 	CellRendererToggle* hidden_cell = dynamic_cast<CellRendererToggle*>(plugin_display.get_column_cell_renderer (1));
128 	hidden_cell->property_activatable() = true;
129 	hidden_cell->property_radio() = true;
130 	hidden_cell->signal_toggled().connect (sigc::mem_fun (*this, &PluginSelector::hidden_changed));
131 
132 	scroller.set_border_width(10);
133 	scroller.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
134 	scroller.add(plugin_display);
135 
136 	amodel = Gtk::ListStore::create(acols);
137 	added_list.set_model (amodel);
138 	added_list.append_column (_("Plugins to be connected"), acols.text);
139 	added_list.set_headers_visible (true);
140 	added_list.set_reorderable (false);
141 
142 	for (int i = 2; i <= 7; ++i) {
143 		Gtk::TreeView::Column* column = plugin_display.get_column(i);
144 		if (column) {
145 			column->set_sort_column(i);
146 		}
147 	}
148 
149 	ascroller.set_border_width(10);
150 	ascroller.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
151 	ascroller.add(added_list);
152 	btn_add = manage(new Gtk::Button(Stock::ADD));
153 	set_tooltip(*btn_add, _("Add a plugin to the effect list"));
154 	btn_add->set_sensitive (false);
155 	btn_remove = manage(new Gtk::Button(Stock::REMOVE));
156 	btn_remove->set_sensitive (false);
157 	set_tooltip(*btn_remove, _("Remove a plugin from the effect list"));
158 
159 	btn_add->set_name("PluginSelectorButton");
160 	btn_remove->set_name("PluginSelectorButton");
161 
162 	/* SEARCH */
163 
164 	Gtk::Table* search_table = manage(new Gtk::Table(2, 2));
165 
166 	search_entry.signal_changed().connect (sigc::mem_fun (*this, &PluginSelector::search_entry_changed));
167 	search_clear_button.signal_clicked().connect (sigc::mem_fun (*this, &PluginSelector::search_clear_button_clicked));
168 
169 	_search_name_checkbox = manage (new ArdourButton (_("Name"), ArdourButton::led_default_elements, true));
170 	_search_name_checkbox->set_active(true);
171 	_search_name_checkbox->set_name ("pluginlist filter button");
172 
173 	_search_tags_checkbox = manage (new ArdourButton (_("Tags"), ArdourButton::led_default_elements, true));
174 	_search_tags_checkbox->set_active(true);
175 	_search_tags_checkbox->set_name ("pluginlist filter button");
176 
177 	_search_ignore_checkbox = manage (new ArdourButton(_("Ignore Filters when searching"), ArdourButton::led_default_elements, true));
178 	_search_ignore_checkbox->set_active(true);
179 	_search_ignore_checkbox->set_name ("pluginlist filter button");
180 
181 	Gtk::Label* search_help_label1 = manage (new Label(
182 		_("All search terms must be matched."), Gtk::ALIGN_LEFT));
183 
184 	Gtk::Label* search_help_label2 = manage (new Label(
185 		_("Ex: \"ess dyn\" will find \"dynamic de-esser\" but not \"de-esser\"."), Gtk::ALIGN_LEFT));
186 
187 	search_table->attach (search_entry,            0, 3, 0, 1, FILL|EXPAND, FILL);
188 	search_table->attach (search_clear_button,     3, 4, 0, 1, FILL, FILL);
189 	search_table->attach (*_search_name_checkbox,  0, 1, 1, 2, FILL, FILL);
190 	search_table->attach (*_search_tags_checkbox,  1, 2, 1, 2, FILL, FILL);
191 	search_table->attach (*_search_ignore_checkbox,2, 3, 1, 2, FILL, FILL);
192 	search_table->attach (*search_help_label1,     0, 3, 2, 3, FILL, FILL);
193 	search_table->attach (*search_help_label2,     0, 3, 3, 4, FILL, FILL);
194 
195 	search_table->set_border_width (4);
196 	search_table->set_col_spacings (4);
197 	search_table->set_row_spacings (4);
198 
199 	Frame* search_frame = manage (new Frame);
200 	search_frame->set_name ("BaseFrame");
201 	search_frame->set_label (_("Search"));
202 	search_frame->add (*search_table);
203 	search_frame->show_all ();
204 
205 	_search_name_checkbox->signal_clicked.connect (sigc::mem_fun (*this, &PluginSelector::refill));
206 	_search_tags_checkbox->signal_clicked.connect (sigc::mem_fun (*this, &PluginSelector::refill));
207 	_search_ignore_checkbox->signal_clicked.connect (sigc::mem_fun (*this, &PluginSelector::set_sensitive_widgets));
208 
209 	/* FILTER */
210 
211 	Gtk::RadioButtonGroup fil_radio_group;
212 
213 	_fil_effects_radio = manage (new RadioButton (fil_radio_group, _("Show Effects Only")));
214 	_fil_instruments_radio = manage (new RadioButton (fil_radio_group, _("Show Instruments Only")));
215 	_fil_utils_radio = manage (new RadioButton (fil_radio_group, _("Show Utilities Only")));
216 	_fil_favorites_radio = manage (new RadioButton (fil_radio_group, _("Show Favorites Only")));
217 	_fil_hidden_radio = manage (new RadioButton (fil_radio_group, _("Show Hidden Only")));
218 	_fil_all_radio = manage (new RadioButton (fil_radio_group, _("Show All")));
219 
220 	//_fil_type_combo = manage (new ComboBoxText);
221 	_fil_type_combo.append_text_item (_("Show All Formats"));
222 
223 #if (defined WINDOWS_VST_SUPPORT || defined LXVST_SUPPORT || defined MACVST_SUPPORT)
224 	_fil_type_combo.append_text_item (X_("VST"));
225 #endif
226 #ifdef VST3_SUPPORT
227 	_fil_type_combo.append_text_item (X_("VST3"));
228 #endif
229 #ifdef AUDIOUNIT_SUPPORT
230 	_fil_type_combo.append_text_item (X_("AudioUnit"));
231 #endif
232 	_fil_type_combo.append_text_item (X_("LV2"));
233 	_fil_type_combo.append_text_item (X_("Lua"));
234 	_fil_type_combo.append_text_item (X_("LADSPA"));
235 	_fil_type_combo.set_text (_("Show All Formats"));
236 
237 	/* note: _fil_creator_combo menu gets filled in build_plugin_menu */
238 	_fil_creator_combo.set_text_ellipsize (Pango::ELLIPSIZE_END);
239 	_fil_creator_combo.set_layout_ellipsize_width (PANGO_SCALE * 160 * UIConfiguration::instance ().get_ui_scale ());
240 
241 	VBox* filter_vbox = manage (new VBox);
242 	filter_vbox->pack_start (*_fil_effects_radio,     false, false);
243 	filter_vbox->pack_start (*_fil_instruments_radio, false, false);
244 	filter_vbox->pack_start (*_fil_utils_radio,       false, false);
245 	filter_vbox->pack_start (*_fil_favorites_radio,   false, false);
246 	filter_vbox->pack_start (*_fil_hidden_radio,      false, false);
247 	filter_vbox->pack_start (*_fil_all_radio,         false, false);
248 	filter_vbox->pack_start (_fil_type_combo,         false, false);
249 	filter_vbox->pack_start (_fil_creator_combo,      false, false);
250 
251 	filter_vbox->set_border_width (4);
252 	filter_vbox->set_spacing (4);
253 
254 	Frame* filter_frame = manage (new Frame);
255 	filter_frame->set_name ("BaseFrame");
256 	filter_frame->set_label (_("Filter"));
257 	filter_frame->add (*filter_vbox);
258 	filter_frame->show_all ();
259 
260 	_fil_effects_radio->signal_clicked().connect (sigc::mem_fun (*this, &PluginSelector::refill));
261 	_fil_instruments_radio->signal_clicked().connect (sigc::mem_fun (*this, &PluginSelector::refill));
262 	_fil_utils_radio->signal_clicked().connect (sigc::mem_fun (*this, &PluginSelector::refill));
263 	_fil_favorites_radio->signal_clicked().connect (sigc::mem_fun (*this, &PluginSelector::refill));
264 	_fil_hidden_radio->signal_clicked().connect (sigc::mem_fun (*this, &PluginSelector::refill));
265 
266 	_fil_type_combo.StateChanged.connect (sigc::mem_fun (*this, &PluginSelector::refill));
267 	_fil_creator_combo.StateChanged.connect (sigc::mem_fun (*this, &PluginSelector::refill));
268 
269 	/* TAG entry */
270 
271 	Gtk::Table* tagging_table = manage(new Gtk::Table(1, 2));
272 	tagging_table->set_border_width (4);
273 	tagging_table->set_col_spacings (4);
274 	tagging_table->set_row_spacings (4);
275 
276 	tag_entry = manage (new Gtk::Entry);
277 	tag_entry_connection = tag_entry->signal_changed().connect (sigc::mem_fun (*this, &PluginSelector::tag_entry_changed));
278 
279 	tag_reset_button = manage (new Button (_("Reset")));
280 	tag_reset_button->signal_clicked().connect (sigc::mem_fun (*this, &PluginSelector::tag_reset_button_clicked));
281 
282 	Gtk::Label* tagging_help_label1 = manage (new Label(
283 		_("Enter space-separated, one-word Tags for the selected plugin."), Gtk::ALIGN_LEFT));
284 
285 	Gtk::Label* tagging_help_label2 = manage (new Label(
286 		_("You can include dashes, colons or underscores in a Tag."), Gtk::ALIGN_LEFT));
287 
288 	Gtk::Label* tagging_help_label3 = manage (new Label(
289 		_("Ex: \"dynamic de-esser vocal\" applies 3 Tags."), Gtk::ALIGN_LEFT));
290 
291 	int p = 0;
292 	tagging_table->attach (*tag_entry,           0, 1, p, p+1, FILL|EXPAND, FILL);
293 	tagging_table->attach (*tag_reset_button,    1, 2, p, p+1, FILL, FILL); p++;
294 	tagging_table->attach (*tagging_help_label1, 0, 2, p, p+1, FILL, FILL); p++;
295 	tagging_table->attach (*tagging_help_label2, 0, 2, p, p+1, FILL, FILL); p++;
296 	tagging_table->attach (*tagging_help_label3, 0, 2, p, p+1, FILL, FILL); p++;
297 
298 	Frame* tag_frame = manage (new Frame);
299 	tag_frame->set_name ("BaseFrame");
300 	tag_frame->set_label (_("Tags for Selected Plugin"));
301 	tag_frame->add (*tagging_table);
302 	tag_frame->show_all ();
303 
304 	/* Add & remove buttons */
305 
306 	HBox* add_remove = manage (new HBox);
307 	add_remove->pack_start (*btn_add, true, true);
308 	add_remove->pack_start (*btn_remove, true, true);
309 
310 	btn_add->signal_clicked().connect(sigc::mem_fun(*this, &PluginSelector::btn_add_clicked));
311 	btn_remove->signal_clicked().connect(sigc::mem_fun(*this, &PluginSelector::btn_remove_clicked));
312 	added_list.get_selection()->signal_changed().connect (sigc::mem_fun(*this, &PluginSelector::added_list_selection_changed));
313 	added_list.signal_button_press_event().connect_notify (mem_fun(*this, &PluginSelector::added_row_clicked));
314 
315 	added_list.set_name("PluginSelectorList");
316 
317 	/* Top-level Layout */
318 
319 	VBox* to_be_inserted_vbox = manage (new VBox);
320 	to_be_inserted_vbox->pack_start (ascroller);
321 	to_be_inserted_vbox->pack_start (*add_remove, false, false);
322 
323 	int min_width  = std::max (200.f, rintf(200.f * UIConfiguration::instance().get_ui_scale()));
324 	int min_height = std::max (600.f, rintf(600.f * UIConfiguration::instance().get_ui_scale()));
325 
326 	to_be_inserted_vbox->set_size_request (min_width, -1);
327 
328 	Gtk::Table* table = manage(new Gtk::Table(3, 3));
329 	table->set_size_request(-1, min_height);
330 
331 	table->attach (scroller,               0, 3, 0, 5); /* this is the main plugin list */
332 	table->attach (*search_frame,          0, 1, 6, 7, FILL, FILL, 5, 5);
333 	table->attach (*tag_frame,             0, 1, 7, 8, FILL, FILL, 5, 5);
334 	table->attach (*filter_frame,          1, 2, 6, 8, FILL, FILL, 5, 5);
335 	table->attach (*to_be_inserted_vbox,   2, 3, 6, 8, FILL|EXPAND, FILL, 5, 5); /* to be inserted... */
336 
337 	add_button (Stock::CLOSE, RESPONSE_CLOSE);
338 	add_button (_("Insert Plugin(s)"), RESPONSE_APPLY);
339 	set_default_response (RESPONSE_APPLY);
340 	set_response_sensitive (RESPONSE_APPLY, false);
341 	get_vbox()->pack_start (*table);
342 
343 	table->set_name("PluginSelectorTable");
344 
345 	plugin_display.grab_focus();
346 
347 	build_plugin_menu ();
348 	display_selection_changed ();
349 }
350 
~PluginSelector()351 PluginSelector::~PluginSelector ()
352 {
353 	delete _plugin_menu;
354 }
355 
356 void
row_activated(Gtk::TreeModel::Path,Gtk::TreeViewColumn *)357 PluginSelector::row_activated(Gtk::TreeModel::Path, Gtk::TreeViewColumn*)
358 {
359 	btn_add_clicked();
360 }
361 
362 void
added_row_clicked(GdkEventButton * event)363 PluginSelector::added_row_clicked(GdkEventButton* event)
364 {
365 	if (event->type == GDK_2BUTTON_PRESS)
366 		btn_remove_clicked();
367 }
368 
369 bool
show_this_plugin(const PluginInfoPtr & info,const std::string & searchstr)370 PluginSelector::show_this_plugin (const PluginInfoPtr& info, const std::string& searchstr)
371 {
372 	string mode;
373 	bool maybe_show = false;
374 	PluginManager::PluginStatusType status = manager.get_status (info);
375 
376 	if (!searchstr.empty()) {
377 
378 		if (_search_name_checkbox->get_active()) { /* name contains */
379 			std::string compstr = info->name;
380 			setup_search_string (compstr);
381 			maybe_show |= match_search_strings (compstr, searchstr);
382 		}
383 
384 		if (_search_tags_checkbox->get_active()) { /* tag contains */
385 			std::string compstr = manager.get_tags_as_string (info);
386 			setup_search_string (compstr);
387 			maybe_show |= match_search_strings (compstr, searchstr);
388 		}
389 
390 		if (!maybe_show) {
391 			return false;
392 		}
393 
394 		/* user asked to ignore filters */
395 		if (maybe_show && _search_ignore_checkbox->get_active()) {
396 			if (status == PluginManager::Hidden) {
397 				return false;
398 			}
399 			if (status == PluginManager::Concealed) {
400 				return false;
401 			}
402 			return true;
403 		}
404 	}
405 
406 	if (_fil_effects_radio->get_active() && !info->is_effect()) {
407 		return false;
408 	}
409 
410 	if (_fil_instruments_radio->get_active() && !info->is_instrument()) {
411 		return false;
412 	}
413 
414 	if (_fil_utils_radio->get_active() && !(info->is_utility() || info->is_analyzer())) {
415 		return false;
416 	}
417 
418 	if (_fil_favorites_radio->get_active() && status != PluginManager::Favorite) {
419 		return false;
420 	}
421 
422 	if (_fil_hidden_radio->get_active() && (status != PluginManager::Hidden && status != PluginManager::Concealed)) {
423 		return false;
424 	}
425 
426 	if (!_fil_hidden_radio->get_active() && status == PluginManager::Hidden) {
427 		return false;
428 	}
429 
430 	if (!_fil_hidden_radio->get_active() && status == PluginManager::Concealed) {
431 		return false;
432 	}
433 
434 	/* Filter "type" combobox */
435 
436 	if (_fil_type_combo.get_text() == X_("VST") && PluginManager::to_generic_vst(info->type) != LXVST) {
437 		return false;
438 	}
439 
440 	if (_fil_type_combo.get_text() == X_("AudioUnit") && info->type != AudioUnit) {
441 		return false;
442 	}
443 
444 	if (_fil_type_combo.get_text() == X_("VST3") && info->type != VST3) {
445 		return false;
446 	}
447 
448 	if (_fil_type_combo.get_text() == X_("LV2") && info->type != LV2) {
449 		return false;
450 	}
451 
452 	if (_fil_type_combo.get_text() == X_("Lua") && info->type != Lua) {
453 		return false;
454 	}
455 
456 	if (_fil_type_combo.get_text() == X_("LADSPA") && info->type != LADSPA) {
457 		return false;
458 	}
459 
460 	/* Filter "creator" combobox */
461 
462 	if (_fil_creator_combo.get_text() != _("Show All Creators")) {
463 		if (_fil_creator_combo.get_text() != info->creator) {
464 			return false;
465 		}
466 	}
467 
468 	return true;
469 }
470 
471 void
set_sensitive_widgets()472 PluginSelector::set_sensitive_widgets ()
473 {
474 	if (_search_ignore_checkbox->get_active() && !search_entry.get_text().empty()) {
475 		_fil_effects_radio->set_sensitive(false);
476 		_fil_instruments_radio->set_sensitive(false);
477 		_fil_utils_radio->set_sensitive(false);
478 		_fil_favorites_radio->set_sensitive(false);
479 		_fil_hidden_radio->set_sensitive(false);
480 		_fil_all_radio->set_sensitive(false);
481 		_inhibit_refill = true;
482 		_fil_type_combo.set_sensitive(false);
483 		_fil_creator_combo.set_sensitive(false);
484 		_inhibit_refill = false;
485 	} else {
486 		_fil_effects_radio->set_sensitive(true);
487 		_fil_instruments_radio->set_sensitive(true);
488 		_fil_utils_radio->set_sensitive(true);
489 		_fil_favorites_radio->set_sensitive(true);
490 		_fil_hidden_radio->set_sensitive(true);
491 		_fil_all_radio->set_sensitive(true);
492 		_inhibit_refill = true;
493 		_fil_type_combo.set_sensitive(true);
494 		_fil_creator_combo.set_sensitive(true);
495 		_inhibit_refill = false;
496 	}
497 	if (!search_entry.get_text().empty()) {
498 		refill ();
499 	}
500 }
501 
502 void
refill()503 PluginSelector::refill ()
504 {
505 	if (_inhibit_refill) {
506 		return;
507 	}
508 
509 	in_row_change = true;
510 
511 	plugin_display.set_model (Glib::RefPtr<Gtk::TreeStore>(0));
512 
513 	int sort_col;
514 	SortType sort_type;
515 	bool sorted = plugin_model->get_sort_column_id (sort_col, sort_type);
516 
517 	/* Disable sorting to gain performance */
518 	plugin_model->set_sort_column (-2, SORT_ASCENDING);
519 
520 	plugin_model->clear ();
521 
522 	std::string searchstr = search_entry.get_text ();
523 	setup_search_string (searchstr);
524 
525 	ladspa_refiller (searchstr);
526 	lv2_refiller (searchstr);
527 	vst_refiller (searchstr);
528 	lxvst_refiller (searchstr);
529 	mac_vst_refiller (searchstr);
530 	au_refiller (searchstr);
531 	lua_refiller (searchstr);
532 	vst3_refiller (searchstr);
533 
534 	in_row_change = false;
535 
536 	plugin_display.set_model (plugin_model);
537 	if (sorted) {
538 		plugin_model->set_sort_column (sort_col, sort_type);
539 	}
540 }
541 
542 void
refiller(const PluginInfoList & plugs,const::std::string & searchstr,const char * type)543 PluginSelector::refiller (const PluginInfoList& plugs, const::std::string& searchstr, const char* type)
544 {
545 	char buf[16];
546 
547 	for (PluginInfoList::const_iterator i = plugs.begin(); i != plugs.end(); ++i) {
548 
549 		if (show_this_plugin (*i, searchstr)) {
550 
551 			TreeModel::Row newrow = *(plugin_model->append());
552 
553 			PluginManager::PluginStatusType status = manager.get_status (*i);
554 			newrow[plugin_columns.favorite] = status == PluginManager::Favorite;
555 			newrow[plugin_columns.hidden] = status == PluginManager::Hidden;
556 
557 			string name = (*i)->name;
558 			if (name.length() > 48) {
559 				name = name.substr (0, 48);
560 				name.append("...");
561 			}
562 			newrow[plugin_columns.name] = name;
563 
564 			newrow[plugin_columns.type_name] = type;
565 
566 			/* Creator */
567 			string creator = (*i)->creator;
568 			string::size_type pos = 0;
569 			if ((*i)->type == ARDOUR::LADSPA) {
570 				/* stupid LADSPA creator strings */
571 #ifdef PLATFORM_WINDOWS
572 				while (pos < creator.length() && creator[pos] > -2 && creator[pos] < 256 && (isalnum (creator[pos]) || isspace (creator[pos]))) ++pos;
573 #else
574 				while (pos < creator.length() && (isalnum (creator[pos]) || isspace (creator[pos]))) ++pos;
575 #endif
576 			} else {
577 				pos = creator.length ();
578 			}
579 			// If there were too few characters to create a
580 			// meaningful name, mark this creator as 'Unknown'
581 			if (creator.length() < 2 || pos < 3) {
582 				creator = "Unknown";
583 			} else{
584 				creator = creator.substr (0, pos);
585 			}
586 
587 			if (creator.length() > MAX_CREATOR_LEN) {
588 				creator = creator.substr (0, MAX_CREATOR_LEN);
589 				creator.append("...");
590 			}
591 			newrow[plugin_columns.creator] = creator;
592 
593 			/* Tags */
594 			string tags = manager.get_tags_as_string(*i);
595 			if (tags.length() > 32) {
596 				tags = tags.substr (0, 32);
597 				tags.append("...");
598 			}
599 			newrow[plugin_columns.tags] = tags;
600 
601 			if ((*i)->reconfigurable_io ()) {
602 				newrow[plugin_columns.audio_io] = "* / *";
603 				newrow[plugin_columns.midi_io] = "* / *";
604 			} else {
605 				snprintf (buf, sizeof(buf), "%d / %d", (*i)->n_inputs.n_audio(), (*i)->n_outputs.n_audio());
606 				newrow[plugin_columns.audio_io] = buf;
607 				snprintf (buf, sizeof(buf), "%d / %d", (*i)->n_inputs.n_midi(), (*i)->n_outputs.n_midi());
608 				newrow[plugin_columns.midi_io] = buf;
609 			}
610 
611 			newrow[plugin_columns.plugin] = *i;
612 		}
613 	}
614 }
615 
616 void
ladspa_refiller(const std::string & searchstr)617 PluginSelector::ladspa_refiller (const std::string& searchstr)
618 {
619 	refiller (manager.ladspa_plugin_info(), searchstr, "LADSPA");
620 }
621 
622 void
lua_refiller(const std::string & searchstr)623 PluginSelector::lua_refiller (const std::string& searchstr)
624 {
625 	refiller (manager.lua_plugin_info(), searchstr, "Lua");
626 }
627 
628 void
lv2_refiller(const std::string & searchstr)629 PluginSelector::lv2_refiller (const std::string& searchstr)
630 {
631 	refiller (manager.lv2_plugin_info(), searchstr, "LV2");
632 }
633 
634 void
vst_refiller(const std::string & searchstr)635 PluginSelector::vst_refiller (const std::string& searchstr)
636 {
637 #ifdef WINDOWS_VST_SUPPORT
638 	refiller (manager.windows_vst_plugin_info(), searchstr, "VST");
639 #endif
640 }
641 
642 void
lxvst_refiller(const std::string & searchstr)643 PluginSelector::lxvst_refiller (const std::string& searchstr)
644 {
645 #ifdef LXVST_SUPPORT
646 	refiller (manager.lxvst_plugin_info(), searchstr, "LXVST");
647 #endif
648 }
649 
650 void
mac_vst_refiller(const std::string & searchstr)651 PluginSelector::mac_vst_refiller (const std::string& searchstr)
652 {
653 #ifdef MACVST_SUPPORT
654 	refiller (manager.mac_vst_plugin_info(), searchstr, "MacVST");
655 #endif
656 }
657 
658 void
vst3_refiller(const std::string & searchstr)659 PluginSelector::vst3_refiller (const std::string& searchstr)
660 {
661 #ifdef VST3_SUPPORT
662 	refiller (manager.vst3_plugin_info(), searchstr, "VST3");
663 #endif
664 }
665 
666 void
au_refiller(const std::string & searchstr)667 PluginSelector::au_refiller (const std::string& searchstr)
668 {
669 #ifdef AUDIOUNIT_SUPPORT
670 	refiller (manager.au_plugin_info(), searchstr, "AU");
671 #endif
672 }
673 
674 PluginPtr
load_plugin(PluginInfoPtr pi)675 PluginSelector::load_plugin (PluginInfoPtr pi)
676 {
677 	if (_session == 0) {
678 		return PluginPtr();
679 	}
680 
681 	return pi->load (*_session);
682 }
683 
684 void
btn_add_clicked()685 PluginSelector::btn_add_clicked()
686 {
687 	if (plugin_display.get_selection()->count_selected_rows() == 0) {
688 		/* may happen with ctrl + double-click un-selecting but activating a row */
689 		return;
690 	}
691 	std::string name;
692 	PluginInfoPtr pi;
693 	TreeModel::Row newrow = *(amodel->append());
694 	TreeModel::Row row;
695 
696 	row = *(plugin_display.get_selection()->get_selected());
697 	name = row[plugin_columns.name];
698 	pi = row[plugin_columns.plugin];
699 
700 	newrow[acols.text] = name;
701 	newrow[acols.plugin] = pi;
702 
703 	if (!amodel->children().empty()) {
704 		set_response_sensitive (RESPONSE_APPLY, true);
705 	}
706 }
707 
708 void
btn_remove_clicked()709 PluginSelector::btn_remove_clicked()
710 {
711 	TreeModel::iterator iter = added_list.get_selection()->get_selected();
712 
713 	amodel->erase(iter);
714 	if (amodel->children().empty()) {
715 		set_response_sensitive (RESPONSE_APPLY, false);
716 	}
717 }
718 
719 void
display_selection_changed()720 PluginSelector::display_selection_changed()
721 {
722 	tag_entry_connection.block ();
723 	if (plugin_display.get_selection()->count_selected_rows() != 0) {
724 
725 		/* a plugin row is selected; allow the user to edit the "tags" on it. */
726 		TreeModel::Row row = *(plugin_display.get_selection()->get_selected());
727 		string tags = manager.get_tags_as_string (row[plugin_columns.plugin]);
728 		tag_entry->set_text (tags);
729 
730 		tag_entry->set_sensitive (true);
731 		tag_reset_button->set_sensitive (true);
732 		btn_add->set_sensitive (true);
733 
734 	} else {
735 		tag_entry->set_text ("");
736 
737 		tag_entry->set_sensitive (false);
738 		tag_reset_button->set_sensitive (false);
739 		btn_add->set_sensitive (false);
740 	}
741 	tag_entry_connection.unblock ();
742 }
743 
744 void
added_list_selection_changed()745 PluginSelector::added_list_selection_changed()
746 {
747 	if (added_list.get_selection()->count_selected_rows() != 0) {
748 		btn_remove->set_sensitive (true);
749 	} else {
750 		btn_remove->set_sensitive (false);
751 	}
752 }
753 
754 int
run()755 PluginSelector::run ()
756 {
757 	ResponseType r;
758 	TreeModel::Children::iterator i;
759 
760 	bool finish = false;
761 
762 	while (!finish) {
763 
764 		SelectedPlugins plugins;
765 		r = (ResponseType) Dialog::run ();
766 
767 		switch (r) {
768 		case RESPONSE_APPLY:
769 			for (i = amodel->children().begin(); i != amodel->children().end(); ++i) {
770 				PluginInfoPtr pp = (*i)[acols.plugin];
771 				PluginPtr p = load_plugin (pp);
772 				if (p) {
773 					plugins.push_back (p);
774 				} else {
775 					MessageDialog msg (string_compose (_("The plugin \"%1\" could not be loaded\n\nSee the Log window for more details (maybe)"), pp->name));
776 					msg.run ();
777 				}
778 			}
779 			if (interested_object && !plugins.empty()) {
780 				finish = !interested_object->use_plugins (plugins);
781 			}
782 
783 			break;
784 
785 		default:
786 			finish = true;
787 			break;
788 		}
789 	}
790 
791 
792 	hide();
793 	amodel->clear();
794 	interested_object = 0;
795 
796 	if (_need_tag_save) {
797 		manager.save_tags();
798 	}
799 
800 	if (_need_status_save) {
801 		manager.save_statuses();
802 	}
803 	if (_need_menu_rebuild) {
804 		build_plugin_menu ();
805 	}
806 
807 	return (int) r;
808 }
809 
810 void
search_clear_button_clicked()811 PluginSelector::search_clear_button_clicked ()
812 {
813 	search_entry.set_text ("");
814 }
815 
816 void
tag_reset_button_clicked()817 PluginSelector::tag_reset_button_clicked ()
818 {
819 	if (plugin_display.get_selection()->count_selected_rows() != 0) {
820 		TreeModel::Row row = *(plugin_display.get_selection()->get_selected());
821 		ARDOUR::PluginInfoPtr pi = row[plugin_columns.plugin];
822 		manager.reset_tags (pi);
823 		display_selection_changed ();
824 		_need_tag_save = true;
825 	}
826 }
827 
828 void
search_entry_changed()829 PluginSelector::search_entry_changed ()
830 {
831 	set_sensitive_widgets();
832 	if (search_entry.get_text().empty()) {
833 		refill ();
834 	}
835 }
836 
837 void
tag_entry_changed()838 PluginSelector::tag_entry_changed ()
839 {
840 	if (plugin_display.get_selection()->count_selected_rows() != 0) {
841 		TreeModel::Row row = *(plugin_display.get_selection()->get_selected());
842 
843 		ARDOUR::PluginInfoPtr pi = row[plugin_columns.plugin];
844 		manager.set_tags (pi->type, pi->unique_id, tag_entry->get_text(), pi->name, PluginManager::FromGui);
845 
846 		_need_tag_save = true;
847 	}
848 }
849 
850 void
tags_changed(PluginType t,std::string unique_id,std::string tags)851 PluginSelector::tags_changed (PluginType t, std::string unique_id, std::string tags)
852 {
853 	if (plugin_display.get_selection()->count_selected_rows() != 0) {
854 		TreeModel::Row row = *(plugin_display.get_selection()->get_selected());
855 		if (tags.length() > 32) {
856 			tags = tags.substr (0, 32);
857 			tags.append ("...");
858 		}
859 		row[plugin_columns.tags] = tags;
860 	}
861 }
862 
863 void
plugin_status_changed(PluginType t,std::string uid,PluginManager::PluginStatusType stat)864 PluginSelector::plugin_status_changed (PluginType t, std::string uid, PluginManager::PluginStatusType stat)
865 {
866 	Gtk::TreeModel::iterator i;
867 	for (i = plugin_model->children().begin(); i != plugin_model->children().end(); ++i) {
868 		PluginInfoPtr pp = (*i)[plugin_columns.plugin];
869 		if ((pp->type == t) && (pp->unique_id == uid)) {
870 			(*i)[plugin_columns.favorite] = (stat == PluginManager::Favorite) ? true : false;
871 			(*i)[plugin_columns.hidden] = (stat == PluginManager::Hidden) ? true : false;
872 
873 			/* if plug was hidden, remove it from the view */
874 			if (stat == PluginManager::Hidden || stat == PluginManager::Concealed) {
875 				if (!_fil_hidden_radio->get_active() && !_fil_all_radio->get_active()) {
876 					plugin_model->erase(i);
877 				}
878 			} else if (_fil_hidden_radio->get_active()) {
879 				plugin_model->erase(i);
880 			}
881 			/* if no longer a favorite, remove it from the view */
882 			if (stat != PluginManager::Favorite && _fil_favorites_radio->get_active()) {
883 					plugin_model->erase(i);
884 			}
885 
886 			return;
887 		}
888 	}
889 }
890 
891 void
on_show()892 PluginSelector::on_show ()
893 {
894 	ArdourDialog::on_show ();
895 	search_entry.grab_focus ();
896 
897 	refill ();
898 
899 	_need_tag_save = false;
900 	_need_status_save = false;
901 }
902 
903 struct PluginMenuCompareByCreator {
operator ()PluginMenuCompareByCreator904 	bool operator() (PluginInfoPtr a, PluginInfoPtr b) const {
905 		int cmp;
906 
907 		cmp = cmp_nocase_utf8 (a->creator, b->creator);
908 
909 		if (cmp < 0) {
910 			return true;
911 		} else if (cmp == 0) {
912 			/* same creator ... compare names */
913 			if (cmp_nocase_utf8 (a->name, b->name) < 0) {
914 				return true;
915 			}
916 		}
917 		return false;
918 	}
919 };
920 
921 /** @return Plugin menu. The caller should not delete it */
922 Gtk::Menu*
plugin_menu()923 PluginSelector::plugin_menu()
924 {
925 	return _plugin_menu;
926 }
927 
928 void
build_plugin_menu()929 PluginSelector::build_plugin_menu ()
930 {
931 	if (is_visible ()) {
932 		_need_menu_rebuild = true;
933 		return;
934 	}
935 	_need_menu_rebuild = false;
936 	PluginInfoList all_plugs;
937 
938 	all_plugs.insert (all_plugs.end(), manager.ladspa_plugin_info().begin(), manager.ladspa_plugin_info().end());
939 	all_plugs.insert (all_plugs.end(), manager.lua_plugin_info().begin(), manager.lua_plugin_info().end());
940 	all_plugs.insert (all_plugs.end(), manager.lv2_plugin_info().begin(), manager.lv2_plugin_info().end());
941 #ifdef WINDOWS_VST_SUPPORT
942 	all_plugs.insert (all_plugs.end(), manager.windows_vst_plugin_info().begin(), manager.windows_vst_plugin_info().end());
943 #endif
944 #ifdef LXVST_SUPPORT
945 	all_plugs.insert (all_plugs.end(), manager.lxvst_plugin_info().begin(), manager.lxvst_plugin_info().end());
946 #endif
947 #ifdef MACVST_SUPPORT
948 	all_plugs.insert (all_plugs.end(), manager.mac_vst_plugin_info().begin(), manager.mac_vst_plugin_info().end());
949 #endif
950 #ifdef VST3_SUPPORT
951 	all_plugs.insert (all_plugs.end(), manager.vst3_plugin_info().begin(), manager.vst3_plugin_info().end());
952 #endif
953 #ifdef AUDIOUNIT_SUPPORT
954 	all_plugs.insert (all_plugs.end(), manager.au_plugin_info().begin(), manager.au_plugin_info().end());
955 #endif
956 
957 	using namespace Menu_Helpers;
958 
959 	delete _plugin_menu;
960 
961 	_plugin_menu = new Menu;
962 	_plugin_menu->set_name("ArdourContextMenu");
963 
964 	MenuList& items = _plugin_menu->items();
965 	items.clear ();
966 
967 	Gtk::Menu* favs = create_favs_menu(all_plugs);
968 	items.push_back (MenuElem (_("Favorites"), *manage (favs)));
969 
970 	items.push_back (MenuElem (_("Plugin Selector..."), sigc::mem_fun (*this, &PluginSelector::show_manager)));
971 	items.push_back (SeparatorElem ());
972 
973 	Menu* charts = create_charts_menu(all_plugs);
974 	items.push_back (MenuElem (_("By Popularity"), *manage (charts)));
975 
976 	Menu* by_creator = create_by_creator_menu(all_plugs);
977 	items.push_back (MenuElem (_("By Creator"), *manage (by_creator)));
978 
979 	Menu* by_tags = create_by_tags_menu(all_plugs);
980 	items.push_back (MenuElem (_("By Tags"), *manage (by_tags)));
981 }
982 
983 string
GetPluginTypeStr(PluginInfoPtr info)984 GetPluginTypeStr(PluginInfoPtr info)
985 {
986 	return string_compose (" (%1)", PluginManager::plugin_type_name (info->type, false));
987 }
988 
989 Gtk::Menu*
create_favs_menu(PluginInfoList & all_plugs)990 PluginSelector::create_favs_menu (PluginInfoList& all_plugs)
991 {
992 	using namespace Menu_Helpers;
993 
994 	Menu* favs = new Menu();
995 	favs->set_name("ArdourContextMenu");
996 
997 	PluginABCSorter cmp_by_name;
998 	all_plugs.sort (cmp_by_name);
999 
1000 	for (PluginInfoList::const_iterator i = all_plugs.begin(); i != all_plugs.end(); ++i) {
1001 		if (manager.get_status (*i) == PluginManager::Favorite) {
1002 			string typ = GetPluginTypeStr(*i);
1003 			MenuElem elem ((*i)->name + typ, (sigc::bind (sigc::mem_fun (*this, &PluginSelector::plugin_chosen_from_menu), *i)));
1004 			elem.get_child()->set_use_underline (false);
1005 			favs->items().push_back (elem);
1006 		}
1007 	}
1008 	return favs;
1009 }
1010 
1011 Gtk::Menu*
create_charts_menu(PluginInfoList & all_plugs)1012 PluginSelector::create_charts_menu (PluginInfoList& all_plugs)
1013 {
1014 	using namespace Menu_Helpers;
1015 
1016 	Menu* charts = new Menu();
1017 	charts->set_name("ArdourContextMenu");
1018 
1019 	PluginInfoList plugs;
1020 
1021 	for (PluginInfoList::const_iterator i = all_plugs.begin(); i != all_plugs.end(); ++i) {
1022 		int64_t lru;
1023 		uint64_t use_count;
1024 		if (manager.stats (*i, lru, use_count)) {
1025 			plugs.push_back (*i);
1026 		}
1027 	}
1028 	PluginChartsSorter cmp;
1029 	plugs.sort (cmp);
1030 	plugs.resize (std::min (plugs.size(), size_t(UIConfiguration::instance().get_max_plugin_chart())));
1031 
1032 	PluginABCSorter abc;
1033 	plugs.sort (abc);
1034 
1035 	for (PluginInfoList::const_iterator i = plugs.begin(); i != plugs.end(); ++i) {
1036 		string typ = GetPluginTypeStr(*i);
1037 		MenuElem elem ((*i)->name + typ, (sigc::bind (sigc::mem_fun (*this, &PluginSelector::plugin_chosen_from_menu), *i)));
1038 		elem.get_child()->set_use_underline (false);
1039 		charts->items().push_back (elem);
1040 	}
1041 	return charts;
1042 }
1043 
1044 Gtk::Menu*
create_by_creator_menu(ARDOUR::PluginInfoList & all_plugs)1045 PluginSelector::create_by_creator_menu (ARDOUR::PluginInfoList& all_plugs)
1046 {
1047 	_inhibit_refill = true;
1048 	_fil_creator_combo.clear_items ();
1049 	_fil_creator_combo.append_text_item (_("Show All Creators"));
1050 	_fil_creator_combo.set_text (_("Show All Creators"));
1051 	_inhibit_refill = false;
1052 
1053 	using namespace Menu_Helpers;
1054 
1055 	typedef std::map<std::string,Gtk::Menu*> SubmenuMap;
1056 	SubmenuMap creator_submenu_map;
1057 
1058 	Menu* by_creator = new Menu();
1059 	by_creator->set_name("ArdourContextMenu");
1060 
1061 	MenuList& by_creator_items = by_creator->items();
1062 	PluginMenuCompareByCreator cmp_by_creator;
1063 	all_plugs.sort (cmp_by_creator);
1064 
1065 	for (PluginInfoList::const_iterator i = all_plugs.begin(); i != all_plugs.end(); ++i) {
1066 
1067 		PluginManager::PluginStatusType status = manager.get_status (*i);
1068 		if (status == PluginManager::Hidden) continue;
1069 		if (status == PluginManager::Concealed) continue;
1070 
1071 		string creator = (*i)->creator;
1072 
1073 		/* If there were too few characters to create a
1074 		 * meaningful name, mark this creator as 'Unknown'
1075 		 */
1076 		if (creator.length() < 2) {
1077 			creator = "Unknown";
1078 		}
1079 
1080 		SubmenuMap::iterator x;
1081 		Gtk::Menu* submenu;
1082 		if ((x = creator_submenu_map.find (creator)) != creator_submenu_map.end()) {
1083 			submenu = x->second;
1084 		} else {
1085 
1086 			_fil_creator_combo.append_text_item (creator);
1087 
1088 			submenu = new Gtk::Menu;
1089 			by_creator_items.push_back (MenuElem (creator, *manage (submenu)));
1090 			creator_submenu_map.insert (pair<std::string,Menu*> (creator, submenu));
1091 			submenu->set_name("ArdourContextMenu");
1092 		}
1093 		string typ = GetPluginTypeStr(*i);
1094 		MenuElem elem ((*i)->name+typ, (sigc::bind (sigc::mem_fun (*this, &PluginSelector::plugin_chosen_from_menu), *i)));
1095 		elem.get_child()->set_use_underline (false);
1096 		submenu->items().push_back (elem);
1097 	}
1098 
1099 	return by_creator;
1100 }
1101 
1102 Gtk::Menu*
create_by_tags_menu(ARDOUR::PluginInfoList & all_plugs)1103 PluginSelector::create_by_tags_menu (ARDOUR::PluginInfoList& all_plugs)
1104 {
1105 	using namespace Menu_Helpers;
1106 
1107 	typedef std::map<std::string,Gtk::Menu*> SubmenuMap;
1108 	SubmenuMap tags_submenu_map;
1109 
1110 	Menu* by_tags = new Menu();
1111 	by_tags->set_name("ArdourContextMenu");
1112 	MenuList& by_tags_items = by_tags->items();
1113 
1114 	std::vector<std::string> all_tags = manager.get_all_tags (PluginManager::NoHidden);
1115 	for (vector<string>::iterator t = all_tags.begin(); t != all_tags.end(); ++t) {
1116 		Gtk::Menu *submenu = new Gtk::Menu;
1117 		by_tags_items.push_back (MenuElem (*t, *manage (submenu)));
1118 		tags_submenu_map.insert (pair<std::string,Menu*> (*t, submenu));
1119 		submenu->set_name("ArdourContextMenu");
1120 	}
1121 
1122 	PluginABCSorter cmp_by_name;
1123 	all_plugs.sort (cmp_by_name);
1124 
1125 	for (PluginInfoList::const_iterator i = all_plugs.begin(); i != all_plugs.end(); ++i) {
1126 
1127 		PluginManager::PluginStatusType status = manager.get_status (*i);
1128 		if (status == PluginManager::Hidden) continue;
1129 		if (status == PluginManager::Concealed) continue;
1130 
1131 		/* for each tag in the plugins tag list, add it to that submenu */
1132 		vector<string> tokens = manager.get_tags(*i);
1133 		for (vector<string>::iterator t = tokens.begin(); t != tokens.end(); ++t) {
1134 			SubmenuMap::iterator x;
1135 			Gtk::Menu* submenu;
1136 			if ((x = tags_submenu_map.find (*t)) != tags_submenu_map.end()) {
1137 				submenu = x->second;
1138 				string typ = GetPluginTypeStr(*i);
1139 				MenuElem elem ((*i)->name + typ, (sigc::bind (sigc::mem_fun (*this, &PluginSelector::plugin_chosen_from_menu), *i)));
1140 				elem.get_child()->set_use_underline (false);
1141 				submenu->items().push_back (elem);
1142 			}
1143 		}
1144 	}
1145 	return by_tags;
1146 }
1147 
1148 void
plugin_chosen_from_menu(const PluginInfoPtr & pi)1149 PluginSelector::plugin_chosen_from_menu (const PluginInfoPtr& pi)
1150 {
1151 	PluginPtr p = load_plugin (pi);
1152 
1153 	if (p && interested_object) {
1154 		SelectedPlugins plugins;
1155 		plugins.push_back (p);
1156 		interested_object->use_plugins (plugins);
1157 	}
1158 
1159 	interested_object = 0;
1160 }
1161 
1162 void
favorite_changed(const std::string & path)1163 PluginSelector::favorite_changed (const std::string& path)
1164 {
1165 	PluginInfoPtr pi;
1166 
1167 	if (in_row_change) {
1168 		return;
1169 	}
1170 
1171 	in_row_change = true;
1172 
1173 	TreeModel::iterator iter = plugin_model->get_iter (path);
1174 
1175 	if (iter) {
1176 
1177 		bool favorite = !(*iter)[plugin_columns.favorite];
1178 
1179 		/* change state */
1180 
1181 		PluginManager::PluginStatusType status = (favorite ? PluginManager::Favorite : PluginManager::Normal);
1182 
1183 		/* save new statuses list */
1184 
1185 		pi = (*iter)[plugin_columns.plugin];
1186 
1187 		manager.set_status (pi->type, pi->unique_id, status);
1188 
1189 		_need_status_save = true;
1190 	}
1191 	in_row_change = false;
1192 }
1193 
1194 void
hidden_changed(const std::string & path)1195 PluginSelector::hidden_changed (const std::string& path)
1196 {
1197 	PluginInfoPtr pi;
1198 
1199 	if (in_row_change) {
1200 		return;
1201 	}
1202 
1203 	in_row_change = true;
1204 
1205 	TreeModel::iterator iter = plugin_model->get_iter (path);
1206 
1207 	if (iter) {
1208 
1209 		bool hidden = !(*iter)[plugin_columns.hidden];
1210 
1211 		/* change state */
1212 
1213 		PluginManager::PluginStatusType status = (hidden ? PluginManager::Hidden : PluginManager::Normal);
1214 
1215 		/* save new statuses list */
1216 
1217 		pi = (*iter)[plugin_columns.plugin];
1218 
1219 		manager.set_status (pi->type, pi->unique_id, status);
1220 
1221 		_need_status_save = true;
1222 	}
1223 	in_row_change = false;
1224 }
1225 
1226 void
show_manager()1227 PluginSelector::show_manager ()
1228 {
1229 	bool scan_now = false;
1230 	if (!manager.cache_valid ()) {
1231 		ArdourMessageDialog q (
1232 #ifdef __APPLE__
1233 				_("Scan VST2/3 and AudioUnit plugins now?")
1234 #else
1235 				_("Scan VST2/3 Plugins now?")
1236 #endif
1237 				, false, MESSAGE_QUESTION, BUTTONS_YES_NO);
1238 
1239 #ifdef __APPLE__
1240 		q.set_title (_("Discover VST/AU Plugins?"));
1241     q.set_secondary_text (_("Third party plugins have not yet been indexed. AudioUnit and VST plugins have to be scanned before they can be used. This can also be done manually from Preferences > Plugins. Depending on the number of installed plugins the process can take several minutes."));
1242 #else
1243 		q.set_title (_("Discover VST Plugins?"));
1244     q.set_secondary_text (_("Third party plugins have not yet been indexed. VST plugins have to be scanned before they can be used. This can also be done manually from Preferences > Plugins. Depending on the number of installed plugins the process can take several minutes."));
1245 #endif
1246 
1247     if (q.run () == RESPONSE_YES) {
1248 			scan_now = true;
1249 		}
1250 	}
1251 
1252 	if (scan_now) {
1253 		PluginScanDialog psd (false, true);
1254 		psd.start ();
1255 	}
1256 
1257 	show_all();
1258 	run ();
1259 }
1260 
1261 void
set_interested_object(PluginInterestedObject & obj)1262 PluginSelector::set_interested_object (PluginInterestedObject& obj)
1263 {
1264 	interested_object = &obj;
1265 }
1266