2 Copyright (C) 2000 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.
21 #include "gtk2ardour-config.h"
29 #include "pbd/stl_delete.h"
30 #include "pbd/xml++.h"
31 #include "pbd/failed_constructor.h"
33 #include "gtkmm/widget.h"
34 #include "gtkmm/box.h"
36 #include "gtkmm2ext/utils.h"
37 #include "gtkmm2ext/doi.h"
38 #include "gtkmm2ext/application.h"
40 #include "widgets/tooltips.h"
41 #include "widgets/fastmeter.h"
43 #include "ardour/session.h"
44 #include "ardour/plugin.h"
45 #include "ardour/plugin_insert.h"
46 #include "ardour/ladspa_plugin.h"
47 #ifdef WINDOWS_VST_SUPPORT
48 #include "ardour/windows_vst_plugin.h"
49 #include "windows_vst_plugin_ui.h"
52 #include "ardour/lxvst_plugin.h"
53 #include "lxvst_plugin_ui.h"
56 #include "ardour/mac_vst_plugin.h"
57 #include "vst_plugin_ui.h"
60 #include "ardour/lv2_plugin.h"
61 #include "lv2_plugin_ui.h"
64 #include "ardour_window.h"
65 #include "ardour_ui.h"
66 #include "plugin_ui.h"
68 #include "gui_thread.h"
69 #include "public_editor.h"
70 #include "processor_box.h"
72 #include "latency_gui.h"
73 #include "plugin_dspload_ui.h"
74 #include "plugin_eq_gui.h"
75 #include "plugin_presets_ui.h"
77 #include "new_plugin_preset_dialog.h"
82 using namespace ARDOUR;
83 using namespace ARDOUR_UI_UTILS;
84 using namespace ArdourWidgets;
86 using namespace Gtkmm2ext;
90 PluginUIWindow::PluginUIWindow (
91 boost::shared_ptr<PluginInsert> insert,
94 : ArdourWindow (string())
96 , _keyboard_focused (false)
97 #ifdef AUDIOUNIT_SUPPORT
98 , pre_deactivate_x (-1)
99 , pre_deactivate_y (-1)
103 bool have_gui = false;
104 Label* label = manage (new Label());
105 label->set_markup ("<b>THIS IS THE PLUGIN UI</b>");
107 if (editor && insert->plugin()->has_editor()) {
108 switch (insert->type()) {
109 case ARDOUR::Windows_VST:
110 have_gui = create_windows_vst_editor (insert);
114 have_gui = create_lxvst_editor (insert);
118 have_gui = create_mac_vst_editor (insert);
121 case ARDOUR::AudioUnit:
122 have_gui = create_audiounit_editor (insert);
126 error << _("Eh? LADSPA plugins don't have editors!") << endmsg;
130 have_gui = create_lv2_editor (insert);
134 #ifndef WINDOWS_VST_SUPPORT
135 error << string_compose (_("unknown type of editor-supplying plugin (note: no VST support in this version of %1)"), PROGRAM_NAME)
138 error << _("unknown type of editor-supplying plugin")
141 throw failed_constructor ();
147 GenericPluginUI* pu = new GenericPluginUI (insert, scrollable);
150 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
152 set_wmclass (X_("ardour_plugin_editor"), PROGRAM_NAME);
154 signal_map_event().connect (sigc::mem_fun (*pu, &GenericPluginUI::start_updating));
155 signal_unmap_event().connect (sigc::mem_fun (*pu, &GenericPluginUI::stop_updating));
158 set_name ("PluginEditor");
159 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
161 insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PluginUIWindow::plugin_going_away, this), gui_context());
163 gint h = _pluginui->get_preferred_height ();
164 gint w = _pluginui->get_preferred_width ();
167 if (h > 600) h = 600;
170 set_default_size (w, h);
171 set_resizable (_pluginui->resizable());
174 PluginUIWindow::~PluginUIWindow ()
177 cerr << "PluginWindow deleted for " << this << endl;
183 PluginUIWindow::on_show ()
185 set_role("plugin_ui");
188 _pluginui->update_preset_list ();
189 _pluginui->update_preset ();
193 #if defined (HAVE_AUDIOUNITS) && defined(__APPLE__)
194 if (pre_deactivate_x >= 0) {
195 move (pre_deactivate_x, pre_deactivate_y);
199 if (_pluginui->on_window_show (_title)) {
206 PluginUIWindow::on_hide ()
208 #if defined (HAVE_AUDIOUNITS) && defined(__APPLE__)
209 get_position (pre_deactivate_x, pre_deactivate_y);
215 _pluginui->on_window_hide ();
220 PluginUIWindow::set_title(const std::string& title)
222 Gtk::Window::set_title(title);
227 #ifdef WINDOWS_VST_SUPPORT
228 PluginUIWindow::create_windows_vst_editor(boost::shared_ptr<PluginInsert> insert)
230 PluginUIWindow::create_windows_vst_editor(boost::shared_ptr<PluginInsert>)
233 #ifndef WINDOWS_VST_SUPPORT
237 boost::shared_ptr<WindowsVSTPlugin> vp;
239 if ((vp = boost::dynamic_pointer_cast<WindowsVSTPlugin> (insert->plugin())) == 0) {
240 error << string_compose (_("unknown type of editor-supplying plugin (note: no VST support in this version of %1)"), PROGRAM_NAME)
242 throw failed_constructor ();
244 WindowsVSTPluginUI* vpu = new WindowsVSTPluginUI (insert, vp, GTK_WIDGET(this->gobj()));
247 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
249 vpu->package (*this);
258 PluginUIWindow::create_lxvst_editor(boost::shared_ptr<PluginInsert> insert)
260 PluginUIWindow::create_lxvst_editor(boost::shared_ptr<PluginInsert>)
263 #ifndef LXVST_SUPPORT
267 boost::shared_ptr<LXVSTPlugin> lxvp;
269 if ((lxvp = boost::dynamic_pointer_cast<LXVSTPlugin> (insert->plugin())) == 0) {
270 error << string_compose (_("unknown type of editor-supplying plugin (note: no linuxVST support in this version of %1)"), PROGRAM_NAME)
272 throw failed_constructor ();
274 LXVSTPluginUI* lxvpu = new LXVSTPluginUI (insert, lxvp);
277 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
279 lxvpu->package (*this);
287 #ifdef MACVST_SUPPORT
288 PluginUIWindow::create_mac_vst_editor (boost::shared_ptr<PluginInsert> insert)
290 PluginUIWindow::create_mac_vst_editor (boost::shared_ptr<PluginInsert>)
293 #ifndef MACVST_SUPPORT
296 boost::shared_ptr<MacVSTPlugin> mvst;
297 if ((mvst = boost::dynamic_pointer_cast<MacVSTPlugin> (insert->plugin())) == 0) {
298 error << string_compose (_("unknown type of editor-supplying plugin (note: no MacVST support in this version of %1)"), PROGRAM_NAME)
300 throw failed_constructor ();
302 VSTPluginUI* vpu = create_mac_vst_gui (insert);
304 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
306 vpu->package (*this);
308 Application::instance()->ActivationChanged.connect (mem_fun (*this, &PluginUIWindow::app_activated));
316 #ifdef AUDIOUNIT_SUPPORT
317 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert> insert)
319 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert>)
322 #ifndef AUDIOUNIT_SUPPORT
326 _pluginui = create_au_gui (insert, &box);
327 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
330 Application::instance()->ActivationChanged.connect (mem_fun (*this, &PluginUIWindow::app_activated));
338 PluginUIWindow::app_activated (bool yn)
340 PluginUIWindow::app_activated (bool)
343 #ifdef AUDIOUNIT_SUPPORT
347 _pluginui->activate ();
348 if (pre_deactivate_x >= 0) {
349 move (pre_deactivate_x, pre_deactivate_y);
355 was_visible = is_visible();
356 get_position (pre_deactivate_x, pre_deactivate_y);
358 _pluginui->deactivate ();
365 PluginUIWindow::create_lv2_editor(boost::shared_ptr<PluginInsert> insert)
368 boost::shared_ptr<LV2Plugin> vp;
370 if ((vp = boost::dynamic_pointer_cast<LV2Plugin> (insert->plugin())) == 0) {
371 error << _("create_lv2_editor called on non-LV2 plugin") << endmsg;
372 throw failed_constructor ();
374 LV2PluginUI* lpu = new LV2PluginUI (insert, vp);
377 lpu->package (*this);
378 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
388 PluginUIWindow::keyboard_focused (bool yn)
390 _keyboard_focused = yn;
394 PluginUIWindow::on_key_press_event (GdkEventKey* event)
396 if (_keyboard_focused) {
398 _pluginui->grab_focus();
399 if (_pluginui->non_gtk_gui()) {
400 _pluginui->forward_key_event (event);
402 return relay_key_press (event, this);
407 /* for us to be getting key press events, there really
408 MUST be a _pluginui, but just to be safe, check ...
412 _pluginui->grab_focus();
413 if (_pluginui->non_gtk_gui()) {
414 /* pass main window as the window for the event
415 to be handled in, not this one, because there are
416 no widgets in this window that we want to have
419 return relay_key_press (event, &ARDOUR_UI::instance()->main_window());
421 return relay_key_press (event, this);
429 PluginUIWindow::on_key_release_event (GdkEventKey *event)
431 if (_keyboard_focused) {
433 if (_pluginui->non_gtk_gui()) {
434 _pluginui->forward_key_event (event);
438 gtk_window_propagate_key_event (GTK_WINDOW(gobj()), event);
440 /* don't forward releases */
445 PluginUIWindow::plugin_going_away ()
447 ENSURE_GUI_THREAD (*this, &PluginUIWindow::plugin_going_away)
450 _pluginui->stop_updating(0);
453 death_connection.disconnect ();
456 PlugUIBase::PlugUIBase (boost::shared_ptr<PluginInsert> pi)
458 , plugin (insert->plugin())
459 , add_button (_("Add"))
460 , save_button (_("Save"))
461 , delete_button (_("Delete"))
462 , reset_button (_("Reset"))
463 , bypass_button (ArdourButton::led_default_elements)
464 , pin_management_button (_("Pinout"))
465 , description_expander (_("Description"))
466 , plugin_analysis_expander (_("Plugin analysis"))
467 , cpuload_expander (_("CPU Profile"))
474 _preset_modified.set_size_request (16, -1);
475 _preset_combo.set_text("(default)");
476 set_tooltip (_preset_combo, _("Presets (if any) for this plugin\n(Both factory and user-created)"));
477 set_tooltip (add_button, _("Save a new preset"));
478 set_tooltip (save_button, _("Save the current preset"));
479 set_tooltip (delete_button, _("Delete the current preset"));
480 set_tooltip (reset_button, _("Reset parameters to default (if no parameters are in automation play mode)"));
481 set_tooltip (pin_management_button, _("Show Plugin Pin Management Dialog"));
482 set_tooltip (bypass_button, _("Disable signal processing by the plugin"));
485 update_preset_list ();
488 add_button.set_name ("generic button");
489 add_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::add_plugin_setting));
491 save_button.set_name ("generic button");
492 save_button.signal_clicked.connect(sigc::mem_fun(*this, &PlugUIBase::save_plugin_setting));
494 delete_button.set_name ("generic button");
495 delete_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::delete_plugin_setting));
497 reset_button.set_name ("generic button");
498 reset_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::reset_plugin_parameters));
500 pin_management_button.set_name ("generic button");
501 pin_management_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::manage_pins));
503 insert->ActiveChanged.connect (active_connection, invalidator (*this), boost::bind (&PlugUIBase::processor_active_changed, this, boost::weak_ptr<Processor>(insert)), gui_context());
505 bypass_button.set_name ("plugin bypass button");
506 bypass_button.set_text (_("Bypass"));
507 bypass_button.set_active (!pi->enabled ());
508 bypass_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::bypass_button_release), false);
509 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
511 focus_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::focus_toggled));
512 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
514 /* these images are not managed, so that we can remove them at will */
516 focus_out_image = new Image (get_icon (X_("computer_keyboard")));
517 focus_in_image = new Image (get_icon (X_("computer_keyboard_active")));
519 focus_button.add (*focus_out_image);
521 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));
522 set_tooltip (bypass_button, _("Click to enable/disable this plugin"));
524 description_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_description));
525 description_expander.set_expanded(false);
527 plugin_analysis_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_plugin_analysis));
528 plugin_analysis_expander.set_expanded(false);
530 cpuload_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_cpuload_display));
531 cpuload_expander.set_expanded(false);
533 insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PlugUIBase::plugin_going_away, this), gui_context());
535 plugin->PresetAdded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
536 plugin->PresetRemoved.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
537 plugin->PresetLoaded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset, this), gui_context ());
538 plugin->PresetDirty.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset_modified, this), gui_context ());
540 insert->AutomationStateChanged.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::automation_state_changed, this), gui_context());
542 insert->LatencyChanged.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::set_latency_label, this), gui_context());
544 automation_state_changed();
547 PlugUIBase::~PlugUIBase()
553 delete latency_dialog;
557 PlugUIBase::plugin_going_away ()
559 /* drop references to the plugin/insert */
565 PlugUIBase::set_latency_label ()
567 samplecnt_t const l = insert->effective_latency ();
568 samplecnt_t const sr = insert->session().sample_rate ();
573 t = string_compose (P_("latency (%1 sample)", "latency (%1 samples)", l), l);
575 t = string_compose (_("latency (%1 ms)"), (float) l / ((float) sr / 1000.0f));
578 latency_button.set_text (t);
582 PlugUIBase::latency_button_clicked ()
585 latency_gui = new LatencyGUI (*(insert.get()), insert->session().sample_rate(), insert->session().get_block_size());
586 latency_dialog = new ArdourWindow (_("Edit Latency"));
587 /* use both keep-above and transient for to try cover as many
588 different WM's as possible.
590 latency_dialog->set_keep_above (true);
591 Window* win = dynamic_cast<Window*> (bypass_button.get_toplevel ());
593 latency_dialog->set_transient_for (*win);
595 latency_dialog->add (*latency_gui);
598 latency_gui->refresh ();
599 latency_dialog->show_all ();
603 PlugUIBase::processor_active_changed (boost::weak_ptr<Processor> weak_p)
605 ENSURE_GUI_THREAD (*this, &PlugUIBase::processor_active_changed, weak_p);
606 boost::shared_ptr<Processor> p (weak_p.lock());
609 bypass_button.set_active (!p->enabled ());
614 PlugUIBase::preset_selected (Plugin::PresetRecord preset)
616 if (_no_load_preset) {
619 if (!preset.label.empty()) {
620 insert->load_preset (preset);
622 // blank selected = no preset
623 plugin->clear_preset();
627 #ifdef NO_PLUGIN_STATE
628 static bool seen_saving_message = false;
630 static void show_no_plugin_message()
632 info << string_compose (_("Plugin presets are not supported in this build of %1. Consider paying for a full version"),
635 info << _("To get full access to updates without this limitation\n"
636 "consider becoming a subscriber for a low cost every month.")
638 info << X_("https://community.ardour.org/s/subscribe")
640 ARDOUR_UI::instance()->popup_error(_("Plugin presets are not supported in this build, see the Log window for more information."));
645 PlugUIBase::add_plugin_setting ()
647 #ifndef NO_PLUGIN_STATE
648 NewPluginPresetDialog d (plugin, _("New Preset"));
651 case Gtk::RESPONSE_ACCEPT:
652 if (d.name().empty()) {
657 plugin->remove_preset (d.name ());
660 Plugin::PresetRecord const r = plugin->save_preset (d.name());
661 if (!r.uri.empty ()) {
662 plugin->load_preset (r);
667 if (!seen_saving_message) {
668 seen_saving_message = true;
669 show_no_plugin_message();
675 PlugUIBase::save_plugin_setting ()
677 #ifndef NO_PLUGIN_STATE
678 string const name = _preset_combo.get_text ();
679 plugin->remove_preset (name);
680 Plugin::PresetRecord const r = plugin->save_preset (name);
681 if (!r.uri.empty ()) {
682 plugin->load_preset (r);
685 if (!seen_saving_message) {
686 seen_saving_message = true;
687 show_no_plugin_message();
693 PlugUIBase::delete_plugin_setting ()
695 #ifndef NO_PLUGIN_STATE
696 plugin->remove_preset (_preset_combo.get_text ());
698 if (!seen_saving_message) {
699 seen_saving_message = true;
700 show_no_plugin_message();
706 PlugUIBase::automation_state_changed ()
708 reset_button.set_sensitive (insert->can_reset_all_parameters());
712 PlugUIBase::reset_plugin_parameters ()
714 insert->reset_parameters_to_default ();
718 PlugUIBase::manage_pins ()
720 PluginPinWindowProxy* proxy = insert->pinmgr_proxy ();
724 proxy->get ()->raise();
729 PlugUIBase::bypass_button_release (GdkEventButton*)
731 bool view_says_bypassed = (bypass_button.active_state() != 0);
733 if (view_says_bypassed != insert->enabled ()) {
734 insert->enable (view_says_bypassed);
741 PlugUIBase::focus_toggled (GdkEventButton*)
743 if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
744 Keyboard::the_keyboard().magic_widget_drop_focus();
745 focus_button.remove ();
746 focus_button.add (*focus_out_image);
747 focus_out_image->show ();
748 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));
749 KeyboardFocused (false);
751 Keyboard::the_keyboard().magic_widget_grab_focus();
752 focus_button.remove ();
753 focus_button.add (*focus_in_image);
754 focus_in_image->show ();
755 set_tooltip (focus_button, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME));
756 KeyboardFocused (true);
763 PlugUIBase::toggle_description()
765 if (description_expander.get_expanded() &&
766 !description_expander.get_child()) {
767 const std::string text = plugin->get_docs();
772 Gtk::Label* label = manage(new Gtk::Label(text));
773 label->set_line_wrap(true);
774 label->set_line_wrap_mode(Pango::WRAP_WORD);
775 description_expander.add(*label);
776 description_expander.show_all();
779 if (!description_expander.get_expanded()) {
780 const int child_height = description_expander.get_child ()->get_height ();
782 description_expander.remove();
784 Gtk::Window *toplevel = (Gtk::Window*) description_expander.get_ancestor (GTK_TYPE_WINDOW);
788 toplevel->get_size (wr.width, wr.height);
789 wr.height -= child_height;
790 toplevel->resize (wr.width, wr.height);
796 PlugUIBase::toggle_plugin_analysis()
798 if (plugin_analysis_expander.get_expanded() &&
799 !plugin_analysis_expander.get_child()) {
802 eqgui = new PluginEqGui (insert);
805 plugin_analysis_expander.add (*eqgui);
806 plugin_analysis_expander.show_all ();
807 eqgui->start_listening ();
810 if (!plugin_analysis_expander.get_expanded()) {
811 // Hide & remove from expander
812 const int child_height = plugin_analysis_expander.get_child ()->get_height ();
815 eqgui->stop_listening ();
816 plugin_analysis_expander.remove();
818 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
822 toplevel->get_size (wr.width, wr.height);
823 wr.height -= child_height;
824 toplevel->resize (wr.width, wr.height);
830 PlugUIBase::toggle_cpuload_display()
832 if (cpuload_expander.get_expanded() && !cpuload_expander.get_child()) {
833 if (stats_gui == 0) {
834 stats_gui = new PluginLoadStatsGui (insert);
836 cpuload_expander.add (*stats_gui);
837 cpuload_expander.show_all();
838 stats_gui->start_updating ();
841 if (!cpuload_expander.get_expanded()) {
842 const int child_height = cpuload_expander.get_child ()->get_height ();
845 stats_gui->stop_updating ();
846 cpuload_expander.remove();
848 Gtk::Window *toplevel = (Gtk::Window*) cpuload_expander.get_ancestor (GTK_TYPE_WINDOW);
852 toplevel->get_size (wr.width, wr.height);
853 wr.height -= child_height;
854 toplevel->resize (wr.width, wr.height);
861 PlugUIBase::update_preset_list ()
863 using namespace Menu_Helpers;
865 vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets();
869 // Add a menu entry for each preset
870 _preset_combo.clear_items();
871 for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator i = presets.begin(); i != presets.end(); ++i) {
872 _preset_combo.AddMenuElem(
873 MenuElem(i->label, sigc::bind(sigc::mem_fun(*this, &PlugUIBase::preset_selected), *i)));
876 // Add an empty entry for un-setting current preset (see preset_selected)
877 Plugin::PresetRecord no_preset;
878 _preset_combo.AddMenuElem(
879 MenuElem("", sigc::bind(sigc::mem_fun(*this, &PlugUIBase::preset_selected), no_preset)));
885 PlugUIBase::update_preset ()
887 Plugin::PresetRecord p = plugin->last_preset();
891 _preset_combo.set_text (_("(none)"));
893 _preset_combo.set_text (p.label);
897 delete_button.set_sensitive (!p.uri.empty() && p.user);
898 update_preset_modified ();
902 PlugUIBase::update_preset_modified ()
904 Plugin::PresetRecord p = plugin->last_preset();
907 save_button.set_sensitive (false);
908 _preset_modified.set_text ("");
912 bool const c = plugin->parameter_changed_since_last_preset ();
913 if (_preset_modified.get_text().empty() == c) {
914 _preset_modified.set_text (c ? "*" : "");
916 save_button.set_sensitive (c && p.user);
920 PlugUIBase::preset_added_or_removed ()
922 /* Update both the list and the currently-displayed preset */
923 update_preset_list ();