2 * Copyright (C) 2005-2006 Nick Mainsbridge <mainsbridge@gmail.com>
3 * Copyright (C) 2005-2017 Paul Davis <paul@linuxaudiosystems.com>
4 * Copyright (C) 2005 Taybin Rutkin <taybin@taybin.com>
5 * Copyright (C) 2006-2009 Sampo Savolainen <v2@iki.fi>
6 * Copyright (C) 2006-2015 David Robillard <d@drobilla.net>
7 * Copyright (C) 2009-2011 Carl Hetherington <carl@carlh.net>
8 * Copyright (C) 2012-2019 Robin Gareus <robin@gareus.org>
9 * Copyright (C) 2013-2018 John Emmas <john@creativepost.co.uk>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License along
22 * with this program; if not, write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 #include "gtk2ardour-config.h"
35 #include "pbd/stl_delete.h"
36 #include "pbd/xml++.h"
37 #include "pbd/failed_constructor.h"
39 #include "gtkmm/widget.h"
40 #include "gtkmm/box.h"
42 #include "gtkmm2ext/utils.h"
43 #include "gtkmm2ext/doi.h"
44 #include "gtkmm2ext/application.h"
46 #include "widgets/tooltips.h"
47 #include "widgets/fastmeter.h"
49 #include "ardour/session.h"
50 #include "ardour/plugin.h"
51 #include "ardour/plugin_insert.h"
52 #include "ardour/ladspa_plugin.h"
53 #ifdef WINDOWS_VST_SUPPORT
54 #include "ardour/windows_vst_plugin.h"
55 #include "windows_vst_plugin_ui.h"
58 #include "ardour/lxvst_plugin.h"
59 #include "lxvst_plugin_ui.h"
62 #include "ardour/mac_vst_plugin.h"
63 #include "vst_plugin_ui.h"
66 #include "ardour/lv2_plugin.h"
67 #include "lv2_plugin_ui.h"
70 #include "ardour_window.h"
71 #include "ardour_ui.h"
72 #include "plugin_ui.h"
74 #include "gui_thread.h"
75 #include "public_editor.h"
76 #include "processor_box.h"
78 #include "latency_gui.h"
79 #include "plugin_dspload_ui.h"
80 #include "plugin_eq_gui.h"
81 #include "plugin_presets_ui.h"
83 #include "new_plugin_preset_dialog.h"
88 using namespace ARDOUR;
89 using namespace ARDOUR_UI_UTILS;
90 using namespace ArdourWidgets;
92 using namespace Gtkmm2ext;
96 PluginUIWindow::PluginUIWindow (
97 boost::shared_ptr<PluginInsert> insert,
100 : ArdourWindow (string())
101 , was_visible (false)
102 , _keyboard_focused (false)
103 #ifdef AUDIOUNIT_SUPPORT
104 , pre_deactivate_x (-1)
105 , pre_deactivate_y (-1)
109 bool have_gui = false;
110 Label* label = manage (new Label());
111 label->set_markup ("<b>THIS IS THE PLUGIN UI</b>");
113 if (editor && insert->plugin()->has_editor()) {
114 switch (insert->type()) {
115 case ARDOUR::Windows_VST:
116 have_gui = create_windows_vst_editor (insert);
120 have_gui = create_lxvst_editor (insert);
124 have_gui = create_mac_vst_editor (insert);
127 case ARDOUR::AudioUnit:
128 have_gui = create_audiounit_editor (insert);
132 error << _("Eh? LADSPA plugins don't have editors!") << endmsg;
136 have_gui = create_lv2_editor (insert);
140 #ifndef WINDOWS_VST_SUPPORT
141 error << string_compose (_("unknown type of editor-supplying plugin (note: no VST support in this version of %1)"), PROGRAM_NAME)
144 error << _("unknown type of editor-supplying plugin")
147 throw failed_constructor ();
153 GenericPluginUI* pu = new GenericPluginUI (insert, scrollable);
156 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
158 set_wmclass (X_("ardour_plugin_editor"), PROGRAM_NAME);
160 signal_map_event().connect (sigc::mem_fun (*pu, &GenericPluginUI::start_updating));
161 signal_unmap_event().connect (sigc::mem_fun (*pu, &GenericPluginUI::stop_updating));
164 set_name ("PluginEditor");
165 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
167 insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PluginUIWindow::plugin_going_away, this), gui_context());
169 gint h = _pluginui->get_preferred_height ();
170 gint w = _pluginui->get_preferred_width ();
173 if (h > 600) h = 600;
176 set_default_size (w, h);
177 set_resizable (_pluginui->resizable());
180 PluginUIWindow::~PluginUIWindow ()
183 cerr << "PluginWindow deleted for " << this << endl;
189 PluginUIWindow::on_show ()
191 set_role("plugin_ui");
194 _pluginui->update_preset_list ();
195 _pluginui->update_preset ();
199 #if defined (HAVE_AUDIOUNITS) && defined(__APPLE__)
200 if (pre_deactivate_x >= 0) {
201 move (pre_deactivate_x, pre_deactivate_y);
205 if (_pluginui->on_window_show (_title)) {
212 PluginUIWindow::on_hide ()
214 #if defined (HAVE_AUDIOUNITS) && defined(__APPLE__)
215 get_position (pre_deactivate_x, pre_deactivate_y);
221 _pluginui->on_window_hide ();
226 PluginUIWindow::set_title(const std::string& title)
228 Gtk::Window::set_title(title);
233 #ifdef WINDOWS_VST_SUPPORT
234 PluginUIWindow::create_windows_vst_editor(boost::shared_ptr<PluginInsert> insert)
236 PluginUIWindow::create_windows_vst_editor(boost::shared_ptr<PluginInsert>)
239 #ifndef WINDOWS_VST_SUPPORT
243 boost::shared_ptr<WindowsVSTPlugin> vp;
245 if ((vp = boost::dynamic_pointer_cast<WindowsVSTPlugin> (insert->plugin())) == 0) {
246 error << string_compose (_("unknown type of editor-supplying plugin (note: no VST support in this version of %1)"), PROGRAM_NAME)
248 throw failed_constructor ();
250 WindowsVSTPluginUI* vpu = new WindowsVSTPluginUI (insert, vp, GTK_WIDGET(this->gobj()));
253 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
255 vpu->package (*this);
264 PluginUIWindow::create_lxvst_editor(boost::shared_ptr<PluginInsert> insert)
266 PluginUIWindow::create_lxvst_editor(boost::shared_ptr<PluginInsert>)
269 #ifndef LXVST_SUPPORT
273 boost::shared_ptr<LXVSTPlugin> lxvp;
275 if ((lxvp = boost::dynamic_pointer_cast<LXVSTPlugin> (insert->plugin())) == 0) {
276 error << string_compose (_("unknown type of editor-supplying plugin (note: no linuxVST support in this version of %1)"), PROGRAM_NAME)
278 throw failed_constructor ();
280 LXVSTPluginUI* lxvpu = new LXVSTPluginUI (insert, lxvp);
283 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
285 lxvpu->package (*this);
293 #ifdef MACVST_SUPPORT
294 PluginUIWindow::create_mac_vst_editor (boost::shared_ptr<PluginInsert> insert)
296 PluginUIWindow::create_mac_vst_editor (boost::shared_ptr<PluginInsert>)
299 #ifndef MACVST_SUPPORT
302 boost::shared_ptr<MacVSTPlugin> mvst;
303 if ((mvst = boost::dynamic_pointer_cast<MacVSTPlugin> (insert->plugin())) == 0) {
304 error << string_compose (_("unknown type of editor-supplying plugin (note: no MacVST support in this version of %1)"), PROGRAM_NAME)
306 throw failed_constructor ();
308 VSTPluginUI* vpu = create_mac_vst_gui (insert);
310 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
312 vpu->package (*this);
314 Application::instance()->ActivationChanged.connect (mem_fun (*this, &PluginUIWindow::app_activated));
322 #ifdef AUDIOUNIT_SUPPORT
323 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert> insert)
325 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert>)
328 #ifndef AUDIOUNIT_SUPPORT
332 _pluginui = create_au_gui (insert, &box);
333 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
336 Application::instance()->ActivationChanged.connect (mem_fun (*this, &PluginUIWindow::app_activated));
344 PluginUIWindow::app_activated (bool yn)
346 PluginUIWindow::app_activated (bool)
349 #ifdef AUDIOUNIT_SUPPORT
353 _pluginui->activate ();
354 if (pre_deactivate_x >= 0) {
355 move (pre_deactivate_x, pre_deactivate_y);
361 was_visible = is_visible();
362 get_position (pre_deactivate_x, pre_deactivate_y);
364 _pluginui->deactivate ();
371 PluginUIWindow::create_lv2_editor(boost::shared_ptr<PluginInsert> insert)
374 boost::shared_ptr<LV2Plugin> vp;
376 if ((vp = boost::dynamic_pointer_cast<LV2Plugin> (insert->plugin())) == 0) {
377 error << _("create_lv2_editor called on non-LV2 plugin") << endmsg;
378 throw failed_constructor ();
380 LV2PluginUI* lpu = new LV2PluginUI (insert, vp);
383 lpu->package (*this);
384 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
394 PluginUIWindow::keyboard_focused (bool yn)
396 _keyboard_focused = yn;
400 PluginUIWindow::on_key_press_event (GdkEventKey* event)
402 if (_keyboard_focused) {
404 _pluginui->grab_focus();
405 if (_pluginui->non_gtk_gui()) {
406 _pluginui->forward_key_event (event);
408 return relay_key_press (event, this);
413 /* for us to be getting key press events, there really
414 MUST be a _pluginui, but just to be safe, check ...
418 _pluginui->grab_focus();
419 if (_pluginui->non_gtk_gui()) {
420 /* pass main window as the window for the event
421 to be handled in, not this one, because there are
422 no widgets in this window that we want to have
425 return relay_key_press (event, &ARDOUR_UI::instance()->main_window());
427 return relay_key_press (event, this);
435 PluginUIWindow::on_key_release_event (GdkEventKey *event)
437 if (_keyboard_focused) {
439 if (_pluginui->non_gtk_gui()) {
440 _pluginui->forward_key_event (event);
444 gtk_window_propagate_key_event (GTK_WINDOW(gobj()), event);
446 /* don't forward releases */
451 PluginUIWindow::plugin_going_away ()
453 ENSURE_GUI_THREAD (*this, &PluginUIWindow::plugin_going_away)
456 _pluginui->stop_updating(0);
459 death_connection.disconnect ();
462 PlugUIBase::PlugUIBase (boost::shared_ptr<PluginInsert> pi)
464 , plugin (insert->plugin())
465 , add_button (_("Add"))
466 , save_button (_("Save"))
467 , delete_button (_("Delete"))
468 , reset_button (_("Reset"))
469 , bypass_button (ArdourButton::led_default_elements)
470 , pin_management_button (_("Pinout"))
471 , description_expander (_("Description"))
472 , plugin_analysis_expander (_("Plugin analysis"))
473 , cpuload_expander (_("CPU Profile"))
480 _preset_modified.set_size_request (16, -1);
481 _preset_combo.set_text("(default)");
482 set_tooltip (_preset_combo, _("Presets (if any) for this plugin\n(Both factory and user-created)"));
483 set_tooltip (add_button, _("Save a new preset"));
484 set_tooltip (save_button, _("Save the current preset"));
485 set_tooltip (delete_button, _("Delete the current preset"));
486 set_tooltip (reset_button, _("Reset parameters to default (if no parameters are in automation play mode)"));
487 set_tooltip (pin_management_button, _("Show Plugin Pin Management Dialog"));
488 set_tooltip (bypass_button, _("Disable signal processing by the plugin"));
491 update_preset_list ();
494 add_button.set_name ("generic button");
495 add_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::add_plugin_setting));
497 save_button.set_name ("generic button");
498 save_button.signal_clicked.connect(sigc::mem_fun(*this, &PlugUIBase::save_plugin_setting));
500 delete_button.set_name ("generic button");
501 delete_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::delete_plugin_setting));
503 reset_button.set_name ("generic button");
504 reset_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::reset_plugin_parameters));
506 pin_management_button.set_name ("generic button");
507 pin_management_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::manage_pins));
509 insert->ActiveChanged.connect (active_connection, invalidator (*this), boost::bind (&PlugUIBase::processor_active_changed, this, boost::weak_ptr<Processor>(insert)), gui_context());
511 bypass_button.set_name ("plugin bypass button");
512 bypass_button.set_text (_("Bypass"));
513 bypass_button.set_active (!pi->enabled ());
514 bypass_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::bypass_button_release), false);
515 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
517 focus_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::focus_toggled));
518 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
520 /* these images are not managed, so that we can remove them at will */
522 focus_out_image = new Image (get_icon (X_("computer_keyboard")));
523 focus_in_image = new Image (get_icon (X_("computer_keyboard_active")));
525 focus_button.add (*focus_out_image);
527 set_tooltip (focus_button, string_compose (_("Click to allow the plugin to receive keyboard events that %1 would normally use as a shortcut"), PROGRAM_NAME));
528 set_tooltip (bypass_button, _("Click to enable/disable this plugin"));
530 description_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_description));
531 description_expander.set_expanded(false);
533 plugin_analysis_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_plugin_analysis));
534 plugin_analysis_expander.set_expanded(false);
536 cpuload_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_cpuload_display));
537 cpuload_expander.set_expanded(false);
539 insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PlugUIBase::plugin_going_away, this), gui_context());
541 plugin->PresetAdded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
542 plugin->PresetRemoved.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
543 plugin->PresetLoaded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset, this), gui_context ());
544 plugin->PresetDirty.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset_modified, this), gui_context ());
546 insert->AutomationStateChanged.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::automation_state_changed, this), gui_context());
548 insert->LatencyChanged.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::set_latency_label, this), gui_context());
550 automation_state_changed();
553 PlugUIBase::~PlugUIBase()
559 delete latency_dialog;
563 PlugUIBase::plugin_going_away ()
565 /* drop references to the plugin/insert */
571 PlugUIBase::set_latency_label ()
573 samplecnt_t const l = insert->effective_latency ();
574 samplecnt_t const sr = insert->session().sample_rate ();
579 t = string_compose (P_("latency (%1 sample)", "latency (%1 samples)", l), l);
581 t = string_compose (_("latency (%1 ms)"), (float) l / ((float) sr / 1000.0f));
584 latency_button.set_text (t);
588 PlugUIBase::latency_button_clicked ()
591 latency_gui = new LatencyGUI (*(insert.get()), insert->session().sample_rate(), insert->session().get_block_size());
592 latency_dialog = new ArdourWindow (_("Edit Latency"));
593 /* use both keep-above and transient for to try cover as many
594 different WM's as possible.
596 latency_dialog->set_keep_above (true);
597 Window* win = dynamic_cast<Window*> (bypass_button.get_toplevel ());
599 latency_dialog->set_transient_for (*win);
601 latency_dialog->add (*latency_gui);
604 latency_gui->refresh ();
605 latency_dialog->show_all ();
609 PlugUIBase::processor_active_changed (boost::weak_ptr<Processor> weak_p)
611 ENSURE_GUI_THREAD (*this, &PlugUIBase::processor_active_changed, weak_p);
612 boost::shared_ptr<Processor> p (weak_p.lock());
615 bypass_button.set_active (!p->enabled ());
620 PlugUIBase::preset_selected (Plugin::PresetRecord preset)
622 if (_no_load_preset) {
625 if (!preset.label.empty()) {
626 insert->load_preset (preset);
628 // blank selected = no preset
629 plugin->clear_preset();
633 #ifdef NO_PLUGIN_STATE
634 static bool seen_saving_message = false;
636 static void show_no_plugin_message()
638 info << string_compose (_("Plugin presets are not supported in this build of %1. Consider paying for a full version"),
641 info << _("To get full access to updates without this limitation\n"
642 "consider becoming a subscriber for a low cost every month.")
644 info << X_("https://community.ardour.org/s/subscribe")
646 ARDOUR_UI::instance()->popup_error(_("Plugin presets are not supported in this build, see the Log window for more information."));
651 PlugUIBase::add_plugin_setting ()
653 #ifndef NO_PLUGIN_STATE
654 NewPluginPresetDialog d (plugin, _("New Preset"));
657 case Gtk::RESPONSE_ACCEPT:
658 if (d.name().empty()) {
663 plugin->remove_preset (d.name ());
666 Plugin::PresetRecord const r = plugin->save_preset (d.name());
667 if (!r.uri.empty ()) {
668 plugin->load_preset (r);
673 if (!seen_saving_message) {
674 seen_saving_message = true;
675 show_no_plugin_message();
681 PlugUIBase::save_plugin_setting ()
683 #ifndef NO_PLUGIN_STATE
684 string const name = _preset_combo.get_text ();
685 plugin->remove_preset (name);
686 Plugin::PresetRecord const r = plugin->save_preset (name);
687 if (!r.uri.empty ()) {
688 plugin->load_preset (r);
691 if (!seen_saving_message) {
692 seen_saving_message = true;
693 show_no_plugin_message();
699 PlugUIBase::delete_plugin_setting ()
701 #ifndef NO_PLUGIN_STATE
702 plugin->remove_preset (_preset_combo.get_text ());
704 if (!seen_saving_message) {
705 seen_saving_message = true;
706 show_no_plugin_message();
712 PlugUIBase::automation_state_changed ()
714 reset_button.set_sensitive (insert->can_reset_all_parameters());
718 PlugUIBase::reset_plugin_parameters ()
720 insert->reset_parameters_to_default ();
724 PlugUIBase::manage_pins ()
726 PluginPinWindowProxy* proxy = insert->pinmgr_proxy ();
730 proxy->get ()->raise();
735 PlugUIBase::bypass_button_release (GdkEventButton*)
737 bool view_says_bypassed = (bypass_button.active_state() != 0);
739 if (view_says_bypassed != insert->enabled ()) {
740 insert->enable (view_says_bypassed);
747 PlugUIBase::focus_toggled (GdkEventButton*)
749 if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
750 Keyboard::the_keyboard().magic_widget_drop_focus();
751 focus_button.remove ();
752 focus_button.add (*focus_out_image);
753 focus_out_image->show ();
754 set_tooltip (focus_button, string_compose (_("Click to allow the plugin to receive keyboard events that %1 would normally use as a shortcut"), PROGRAM_NAME));
755 KeyboardFocused (false);
757 Keyboard::the_keyboard().magic_widget_grab_focus();
758 focus_button.remove ();
759 focus_button.add (*focus_in_image);
760 focus_in_image->show ();
761 set_tooltip (focus_button, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME));
762 KeyboardFocused (true);
769 PlugUIBase::toggle_description()
771 if (description_expander.get_expanded() &&
772 !description_expander.get_child()) {
773 const std::string text = plugin->get_docs();
778 Gtk::Label* label = manage(new Gtk::Label(text));
779 label->set_line_wrap(true);
780 label->set_line_wrap_mode(Pango::WRAP_WORD);
781 description_expander.add(*label);
782 description_expander.show_all();
785 if (!description_expander.get_expanded()) {
786 const int child_height = description_expander.get_child ()->get_height ();
788 description_expander.remove();
790 Gtk::Window *toplevel = (Gtk::Window*) description_expander.get_ancestor (GTK_TYPE_WINDOW);
794 toplevel->get_size (wr.width, wr.height);
795 wr.height -= child_height;
796 toplevel->resize (wr.width, wr.height);
802 PlugUIBase::toggle_plugin_analysis()
804 if (plugin_analysis_expander.get_expanded() &&
805 !plugin_analysis_expander.get_child()) {
808 eqgui = new PluginEqGui (insert);
811 plugin_analysis_expander.add (*eqgui);
812 plugin_analysis_expander.show_all ();
813 eqgui->start_listening ();
816 if (!plugin_analysis_expander.get_expanded()) {
817 // Hide & remove from expander
818 const int child_height = plugin_analysis_expander.get_child ()->get_height ();
821 eqgui->stop_listening ();
822 plugin_analysis_expander.remove();
824 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
828 toplevel->get_size (wr.width, wr.height);
829 wr.height -= child_height;
830 toplevel->resize (wr.width, wr.height);
836 PlugUIBase::toggle_cpuload_display()
838 if (cpuload_expander.get_expanded() && !cpuload_expander.get_child()) {
839 if (stats_gui == 0) {
840 stats_gui = new PluginLoadStatsGui (insert);
842 cpuload_expander.add (*stats_gui);
843 cpuload_expander.show_all();
844 stats_gui->start_updating ();
847 if (!cpuload_expander.get_expanded()) {
848 const int child_height = cpuload_expander.get_child ()->get_height ();
851 stats_gui->stop_updating ();
852 cpuload_expander.remove();
854 Gtk::Window *toplevel = (Gtk::Window*) cpuload_expander.get_ancestor (GTK_TYPE_WINDOW);
858 toplevel->get_size (wr.width, wr.height);
859 wr.height -= child_height;
860 toplevel->resize (wr.width, wr.height);
867 PlugUIBase::update_preset_list ()
869 using namespace Menu_Helpers;
871 vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets();
875 // Add a menu entry for each preset
876 _preset_combo.clear_items();
877 for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator i = presets.begin(); i != presets.end(); ++i) {
878 _preset_combo.AddMenuElem(
879 MenuElem(i->label, sigc::bind(sigc::mem_fun(*this, &PlugUIBase::preset_selected), *i)));
882 // Add an empty entry for un-setting current preset (see preset_selected)
883 Plugin::PresetRecord no_preset;
884 _preset_combo.AddMenuElem(
885 MenuElem("", sigc::bind(sigc::mem_fun(*this, &PlugUIBase::preset_selected), no_preset)));
891 PlugUIBase::update_preset ()
893 Plugin::PresetRecord p = plugin->last_preset();
897 _preset_combo.set_text (_("(none)"));
899 _preset_combo.set_text (p.label);
903 delete_button.set_sensitive (!p.uri.empty() && p.user);
904 update_preset_modified ();
908 PlugUIBase::update_preset_modified ()
910 Plugin::PresetRecord p = plugin->last_preset();
913 save_button.set_sensitive (false);
914 _preset_modified.set_text ("");
918 bool const c = plugin->parameter_changed_since_last_preset ();
919 if (_preset_modified.get_text().empty() == c) {
920 _preset_modified.set_text (c ? "*" : "");
922 save_button.set_sensitive (c && p.user);
926 PlugUIBase::preset_added_or_removed ()
928 /* Update both the list and the currently-displayed preset */
929 update_preset_list ();