build changes from robin gareus for OS X, changing AU support to be used appropriatel...
[ardour.git] / gtk2_ardour / plugin_ui.cc
1 /*
2     Copyright (C) 2000 Paul Davis
3
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.
8
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.
13
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.
17
18 */
19
20 #ifdef WAF_BUILD
21 #include "gtk2ardour-config.h"
22 #endif
23
24 #include <climits>
25 #include <cerrno>
26 #include <cmath>
27 #include <string>
28
29 #include "pbd/stl_delete.h"
30 #include "pbd/xml++.h"
31 #include "pbd/failed_constructor.h"
32
33 #include <gtkmm/widget.h>
34 #include <gtkmm/box.h>
35 #include <gtkmm2ext/click_box.h>
36 #include <gtkmm2ext/fastmeter.h>
37 #include <gtkmm2ext/barcontroller.h>
38 #include <gtkmm2ext/utils.h>
39 #include <gtkmm2ext/doi.h>
40 #include <gtkmm2ext/slider_controller.h>
41 #include <gtkmm2ext/application.h>
42
43 #include "midi++/manager.h"
44
45 #include "ardour/session.h"
46 #include "ardour/plugin.h"
47 #include "ardour/plugin_insert.h"
48 #include "ardour/ladspa_plugin.h"
49 #ifdef WINDOWS_VST_SUPPORT
50 #include "ardour/windows_vst_plugin.h"
51 #include "windows_vst_plugin_ui.h"
52 #endif
53 #ifdef LXVST_SUPPORT
54 #include "ardour/lxvst_plugin.h"
55 #include "lxvst_plugin_ui.h"
56 #endif
57 #ifdef LV2_SUPPORT
58 #include "ardour/lv2_plugin.h"
59 #include "lv2_plugin_ui.h"
60 #endif
61
62 #include <lrdf.h>
63
64 #include "ardour_window.h"
65 #include "ardour_ui.h"
66 #include "prompter.h"
67 #include "plugin_ui.h"
68 #include "utils.h"
69 #include "gui_thread.h"
70 #include "public_editor.h"
71 #include "keyboard.h"
72 #include "latency_gui.h"
73 #include "plugin_eq_gui.h"
74 #include "new_plugin_preset_dialog.h"
75
76 #include "i18n.h"
77
78 using namespace std;
79 using namespace ARDOUR;
80 using namespace PBD;
81 using namespace Gtkmm2ext;
82 using namespace Gtk;
83
84 PluginUIWindow::PluginUIWindow (
85         Gtk::Window*                    win,
86         boost::shared_ptr<PluginInsert> insert,
87         bool                            scrollable,
88         bool                            editor)
89         : parent (win)
90         , was_visible (false)
91         , _keyboard_focused (false)
92 {
93         bool have_gui = false;
94
95         Label* label = manage (new Label());
96         label->set_markup ("<b>THIS IS THE PLUGIN UI</b>");
97
98         std::cout << "SHOW UI " << insert->plugin()->unique_id()
99                   << " editor: " << editor << std::endl;
100         if (editor && insert->plugin()->has_editor()) {
101                 switch (insert->type()) {
102                 case ARDOUR::Windows_VST:
103                         have_gui = create_windows_vst_editor (insert);
104                         break;
105                         
106                 case ARDOUR::LXVST:
107                         have_gui = create_lxvst_editor (insert);
108                         break;
109
110                 case ARDOUR::AudioUnit:
111                         have_gui = create_audiounit_editor (insert);
112                         break;
113
114                 case ARDOUR::LADSPA:
115                         error << _("Eh? LADSPA plugins don't have editors!") << endmsg;
116                         break;
117
118                 case ARDOUR::LV2:
119                         have_gui = create_lv2_editor (insert);
120                         break;
121
122                 default:
123 #ifndef WINDOWS_VST_SUPPORT
124                         error << _("unknown type of editor-supplying plugin (note: no VST support in this version of ardour)")
125                               << endmsg;
126 #else
127                         error << _("unknown type of editor-supplying plugin")
128                               << endmsg;
129 #endif
130                         throw failed_constructor ();
131                 }
132
133         }
134
135         if (!have_gui) {
136                 GenericPluginUI* pu = new GenericPluginUI (insert, scrollable);
137
138                 _pluginui = pu;
139                 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
140                 add (*pu);
141                 set_wmclass (X_("ardour_plugin_editor"), PROGRAM_NAME);
142
143                 signal_map_event().connect (sigc::mem_fun (*pu, &GenericPluginUI::start_updating));
144                 signal_unmap_event().connect (sigc::mem_fun (*pu, &GenericPluginUI::stop_updating));
145         }
146
147         // set_position (Gtk::WIN_POS_MOUSE);
148         set_name ("PluginEditor");
149         add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
150
151         signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), reinterpret_cast<Window*> (this)), false);
152         insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PluginUIWindow::plugin_going_away, this), gui_context());
153
154         gint h = _pluginui->get_preferred_height ();
155         gint w = _pluginui->get_preferred_width ();
156
157         if (scrollable) {
158                 if (h > 600) h = 600;
159                 if (w > 600) w = 600;
160
161                 if (w < 0) {
162                         w = 450;
163                 }
164         }
165
166         set_default_size (w, h);
167 }
168
169 PluginUIWindow::~PluginUIWindow ()
170 {
171         delete _pluginui;
172 }
173
174 void
175 PluginUIWindow::set_parent (Gtk::Window* win)
176 {
177         parent = win;
178 }
179
180 void
181 PluginUIWindow::on_map ()
182 {
183         Window::on_map ();
184         set_keep_above (true);
185 }
186
187 bool
188 PluginUIWindow::on_enter_notify_event (GdkEventCrossing *ev)
189 {
190         Keyboard::the_keyboard().enter_window (ev, this);
191         return false;
192 }
193
194 bool
195 PluginUIWindow::on_leave_notify_event (GdkEventCrossing *ev)
196 {
197         Keyboard::the_keyboard().leave_window (ev, this);
198         return false;
199 }
200
201 bool
202 PluginUIWindow::on_focus_in_event (GdkEventFocus *ev)
203 {
204         Window::on_focus_in_event (ev);
205         //Keyboard::the_keyboard().magic_widget_grab_focus ();
206         return false;
207 }
208
209 bool
210 PluginUIWindow::on_focus_out_event (GdkEventFocus *ev)
211 {
212         Window::on_focus_out_event (ev);
213         //Keyboard::the_keyboard().magic_widget_drop_focus ();
214         return false;
215 }
216
217 void
218 PluginUIWindow::on_show ()
219 {
220         set_role("plugin_ui");
221
222         if (_pluginui) {
223                 _pluginui->update_preset_list ();
224                 _pluginui->update_preset ();
225         }
226
227         if (_pluginui) {
228                 if (_pluginui->on_window_show (_title)) {
229                         Window::on_show ();
230                 }
231         }
232
233         if (parent) {
234                 // set_transient_for (*parent);
235         }
236 }
237
238 void
239 PluginUIWindow::on_hide ()
240 {
241         Window::on_hide ();
242
243         if (_pluginui) {
244                 _pluginui->on_window_hide ();
245         }
246 }
247
248 void
249 PluginUIWindow::set_title(const std::string& title)
250 {
251         Gtk::Window::set_title(title);
252         _title = title;
253 }
254
255 bool
256 #ifdef WINDOWS_VST_SUPPORT
257 PluginUIWindow::create_windows_vst_editor(boost::shared_ptr<PluginInsert> insert)
258 #else
259 PluginUIWindow::create_windows_vst_editor(boost::shared_ptr<PluginInsert>)
260 #endif
261 {
262 #ifndef WINDOWS_VST_SUPPORT
263         return false;
264 #else
265
266         boost::shared_ptr<WindowsVSTPlugin> vp;
267
268         if ((vp = boost::dynamic_pointer_cast<WindowsVSTPlugin> (insert->plugin())) == 0) {
269                 error << _("unknown type of editor-supplying plugin (note: no VST support in this version of ardour)")
270                               << endmsg;
271                 throw failed_constructor ();
272         } else {
273                 WindowsVSTPluginUI* vpu = new WindowsVSTPluginUI (insert, vp);
274
275                 _pluginui = vpu;
276                 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
277                 add (*vpu);
278                 vpu->package (*this);
279         }
280
281         return true;
282 #endif
283 }
284
285 bool
286 #ifdef LXVST_SUPPORT
287 PluginUIWindow::create_lxvst_editor(boost::shared_ptr<PluginInsert> insert)
288 #else
289 PluginUIWindow::create_lxvst_editor(boost::shared_ptr<PluginInsert>)
290 #endif
291 {
292 #ifndef LXVST_SUPPORT
293         return false;
294 #else
295
296         boost::shared_ptr<LXVSTPlugin> lxvp;
297
298         if ((lxvp = boost::dynamic_pointer_cast<LXVSTPlugin> (insert->plugin())) == 0) {
299                 error << _("unknown type of editor-supplying plugin (note: no linuxVST support in this version of ardour)")
300                               << endmsg;
301                 throw failed_constructor ();
302         } else {
303                 LXVSTPluginUI* lxvpu = new LXVSTPluginUI (insert, lxvp);
304
305                 _pluginui = lxvpu;
306                 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
307                 add (*lxvpu);
308                 lxvpu->package (*this);
309         }
310
311         return true;
312 #endif
313 }
314
315 bool
316 #ifdef AUDIOUNIT_SUPPORT
317 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert> insert)
318 #else
319 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert>)
320 #endif
321 {
322 #ifndef AUDIOUNIT_SUPPORT
323         return false;
324 #else
325         VBox* box;
326         _pluginui = create_au_gui (insert, &box);
327         _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
328         add (*box);
329
330         Application::instance()->ActivationChanged.connect (mem_fun (*this, &PluginUIWindow::app_activated));
331
332         return true;
333 #endif
334 }
335
336 void
337 #ifdef GTKOSX
338 PluginUIWindow::app_activated (bool yn)
339 #else
340 PluginUIWindow::app_activated (bool)
341 #endif
342 {
343 #ifdef AUDIOUNIT_SUPPORT
344         if (_pluginui) {
345                 if (yn) {
346                         if (was_visible) {
347                                 _pluginui->activate ();
348                                 present ();
349                                 was_visible = true;
350                         }
351                 } else {
352                         was_visible = is_visible();
353                         hide ();
354                         _pluginui->deactivate ();
355                 }
356         }
357 #endif
358 }
359
360 bool
361 PluginUIWindow::create_lv2_editor(boost::shared_ptr<PluginInsert> insert)
362 {
363 #ifdef HAVE_SUIL
364         boost::shared_ptr<LV2Plugin> vp;
365
366         if ((vp = boost::dynamic_pointer_cast<LV2Plugin> (insert->plugin())) == 0) {
367                 error << _("create_lv2_editor called on non-LV2 plugin") << endmsg;
368                 throw failed_constructor ();
369         } else {
370                 LV2PluginUI* lpu = new LV2PluginUI (insert, vp);
371                 _pluginui = lpu;
372                 add (*lpu);
373                 lpu->package (*this);
374         }
375
376         return true;
377 #else
378         return false;
379 #endif
380 }
381
382 void
383 PluginUIWindow::keyboard_focused (bool yn)
384 {
385         _keyboard_focused = yn;
386 }
387
388 bool
389 PluginUIWindow::on_key_press_event (GdkEventKey* event)
390 {
391         if (_keyboard_focused) {
392                 if (_pluginui) {
393                         if (_pluginui->non_gtk_gui()) {
394                                 _pluginui->forward_key_event (event);
395                         } else {
396                                 return relay_key_press (event, this);
397                         }
398                 }
399                 return true;
400         } else {
401                 /* for us to be getting key press events, there really
402                    MUST be a _pluginui, but just to be safe, check ...
403                 */
404
405                 if (_pluginui) {
406                         if (_pluginui->non_gtk_gui()) {
407                                 /* pass editor window as the window for the event
408                                    to be handled in, not this one, because there are
409                                    no widgets in this window that we want to have
410                                    key focus.
411                                 */
412                                 return relay_key_press (event, &PublicEditor::instance());
413                         } else {
414                                 return relay_key_press (event, this);
415                         }
416                 } else {
417                         return false;
418                 }
419         }
420 }
421
422 bool
423 PluginUIWindow::on_key_release_event (GdkEventKey *event)
424 {
425         if (_keyboard_focused) {
426                 if (_pluginui) {
427                         if (_pluginui->non_gtk_gui()) {
428                                 _pluginui->forward_key_event (event);
429                         }
430                         return true;
431                 }
432                 return false;
433         } else {
434                 return true;
435         }
436 }
437
438 void
439 PluginUIWindow::plugin_going_away ()
440 {
441         ENSURE_GUI_THREAD (*this, &PluginUIWindow::plugin_going_away)
442
443         if (_pluginui) {
444                 _pluginui->stop_updating(0);
445         }
446
447         death_connection.disconnect ();
448
449         delete_when_idle (this);
450 }
451
452 PlugUIBase::PlugUIBase (boost::shared_ptr<PluginInsert> pi)
453         : insert (pi)
454         , plugin (insert->plugin())
455         , add_button (_("Add"))
456         , save_button (_("Save"))
457         , delete_button (_("Delete"))
458         , bypass_button (ArdourButton::led_default_elements)
459         , plugin_analysis_expander (_("Plugin analysis"))
460         , latency_gui (0)
461         , latency_dialog (0)
462         , eqgui (0)
463 {
464         _preset_modified.set_size_request (16, -1);
465         _preset_combo.signal_changed().connect(sigc::mem_fun(*this, &PlugUIBase::preset_selected));
466         ARDOUR_UI::instance()->set_tip (_preset_combo, _("Presets (if any) for this plugin\n(Both factory and user-created)"));
467         ARDOUR_UI::instance()->set_tip (add_button, _("Save a new preset"));
468         ARDOUR_UI::instance()->set_tip (save_button, _("Save the current preset"));
469         ARDOUR_UI::instance()->set_tip (delete_button, _("Delete the current preset"));
470         ARDOUR_UI::instance()->set_tip (bypass_button, _("Disable signal processing by the plugin"));
471         _no_load_preset = 0;
472
473         update_preset_list ();
474         update_preset ();
475
476         add_button.set_name ("PluginAddButton");
477         add_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::add_plugin_setting));
478
479         save_button.set_name ("PluginSaveButton");
480         save_button.signal_clicked().connect(sigc::mem_fun(*this, &PlugUIBase::save_plugin_setting));
481
482         delete_button.set_name ("PluginDeleteButton");
483         delete_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::delete_plugin_setting));
484
485         insert->ActiveChanged.connect (active_connection, invalidator (*this), boost::bind (&PlugUIBase::processor_active_changed, this,  boost::weak_ptr<Processor>(insert)), gui_context());
486
487         bypass_button.set_name ("plugin bypass button");
488         bypass_button.set_text (_("Bypass"));
489
490         if (!pi->active()) {
491                 bypass_button.set_active_state (Gtkmm2ext::Active);
492         } else {
493                 bypass_button.unset_active_state ();
494         }
495
496         bypass_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::bypass_button_release));
497         focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
498
499         focus_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::focus_toggled));
500         focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
501
502         /* these images are not managed, so that we can remove them at will */
503
504         focus_out_image = new Image (get_icon (X_("computer_keyboard")));
505         focus_in_image = new Image (get_icon (X_("computer_keyboard_active")));
506
507         focus_button.add (*focus_out_image);
508
509         ARDOUR_UI::instance()->set_tip (focus_button, string_compose (_("Click to allow the plugin to receive keyboard events that %1 would normally use as a shortcut"), PROGRAM_NAME));
510         ARDOUR_UI::instance()->set_tip (bypass_button, _("Click to enable/disable this plugin"));
511
512         plugin_analysis_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_plugin_analysis));
513         plugin_analysis_expander.set_expanded(false);
514
515         insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PlugUIBase::plugin_going_away, this), gui_context());
516
517         plugin->PresetAdded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
518         plugin->PresetRemoved.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
519         plugin->PresetLoaded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset, this), gui_context ());
520         plugin->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::parameter_changed, this, _1, _2), gui_context ());
521 }
522
523 PlugUIBase::~PlugUIBase()
524 {
525         delete eqgui;
526         delete latency_gui;
527 }
528
529 void
530 PlugUIBase::plugin_going_away ()
531 {
532         /* drop references to the plugin/insert */
533         insert.reset ();
534         plugin.reset ();
535         death_connection.disconnect ();
536 }
537
538 void
539 PlugUIBase::set_latency_label ()
540 {
541         framecnt_t const l = insert->effective_latency ();
542         framecnt_t const sr = insert->session().frame_rate ();
543
544         string t;
545
546         if (l < sr / 1000) {
547                 t = string_compose (_("latency (%1 samples)"), l);
548         } else {
549                 t = string_compose (_("latency (%1 ms)"), (float) l / ((float) sr / 1000.0f));
550         }
551
552         latency_label.set_text (t);
553 }
554
555 void
556 PlugUIBase::latency_button_clicked ()
557 {
558         if (!latency_gui) {
559                 latency_gui = new LatencyGUI (*(insert.get()), insert->session().frame_rate(), insert->session().get_block_size());
560                 latency_dialog = new ArdourWindow (_("Edit Latency"));
561                 latency_dialog->add (*latency_gui);
562                 latency_dialog->signal_hide().connect (sigc::mem_fun (*this, &PlugUIBase::set_latency_label));
563         }
564
565         latency_dialog->show_all ();
566 }
567
568 void
569 PlugUIBase::processor_active_changed (boost::weak_ptr<Processor> weak_p)
570 {
571         ENSURE_GUI_THREAD (*this, &PlugUIBase::processor_active_changed, weak_p);
572         boost::shared_ptr<Processor> p (weak_p.lock());
573
574         if (p) {
575                 if (!p->active()) {
576                         bypass_button.set_active_state (Gtkmm2ext::Active);
577                 } else {
578                         bypass_button.unset_active_state ();
579                 }
580         }
581 }
582
583 void
584 PlugUIBase::preset_selected ()
585 {
586         if (_no_load_preset) {
587                 return;
588         }
589
590         if (_preset_combo.get_active_text().length() > 0) {
591                 const Plugin::PresetRecord* pr = plugin->preset_by_label (_preset_combo.get_active_text());
592                 if (pr) {
593                         plugin->load_preset (*pr);
594                 } else {
595                         warning << string_compose(_("Plugin preset %1 not found"),
596                                                   _preset_combo.get_active_text()) << endmsg;
597                 }
598         }
599 }
600
601 void
602 PlugUIBase::add_plugin_setting ()
603 {
604         NewPluginPresetDialog d (plugin);
605
606         switch (d.run ()) {
607         case Gtk::RESPONSE_ACCEPT:
608                 if (d.name().empty()) {
609                         break;
610                 }
611
612                 if (d.replace ()) {
613                         plugin->remove_preset (d.name ());
614                 }
615
616                 Plugin::PresetRecord const r = plugin->save_preset (d.name());
617                 if (!r.uri.empty ()) {
618                         plugin->load_preset (r);
619                 }
620                 break;
621         }
622 }
623
624 void
625 PlugUIBase::save_plugin_setting ()
626 {
627         string const name = _preset_combo.get_active_text ();
628         plugin->remove_preset (name);
629         Plugin::PresetRecord const r = plugin->save_preset (name);
630         if (!r.uri.empty ()) {
631                 plugin->load_preset (r);
632         }
633 }
634
635 void
636 PlugUIBase::delete_plugin_setting ()
637 {
638         plugin->remove_preset (_preset_combo.get_active_text ());
639 }
640
641 bool
642 PlugUIBase::bypass_button_release (GdkEventButton*)
643 {
644         bool view_says_bypassed = (bypass_button.active_state() != 0);
645         
646         if (view_says_bypassed != insert->active()) {
647                 if (view_says_bypassed) {
648                         insert->activate ();
649                 } else {
650                         insert->deactivate ();
651                 }
652         }
653
654         return false;
655 }
656
657 bool
658 PlugUIBase::focus_toggled (GdkEventButton*)
659 {
660         if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
661                 Keyboard::the_keyboard().magic_widget_drop_focus();
662                 focus_button.remove ();
663                 focus_button.add (*focus_out_image);
664                 focus_out_image->show ();
665                 ARDOUR_UI::instance()->set_tip (focus_button, string_compose (_("Click to allow the plugin to receive keyboard events that %1 would normally use as a shortcut"), PROGRAM_NAME));
666                 KeyboardFocused (false);
667         } else {
668                 Keyboard::the_keyboard().magic_widget_grab_focus();
669                 focus_button.remove ();
670                 focus_button.add (*focus_in_image);
671                 focus_in_image->show ();
672                 ARDOUR_UI::instance()->set_tip (focus_button, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME));
673                 KeyboardFocused (true);
674         }
675
676         return true;
677 }
678
679 void
680 PlugUIBase::toggle_plugin_analysis()
681 {
682         if (plugin_analysis_expander.get_expanded() &&
683             !plugin_analysis_expander.get_child()) {
684                 // Create the GUI
685                 if (eqgui == 0) {
686                         eqgui = new PluginEqGui (insert);
687                 }
688
689                 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
690
691                 if (toplevel) {
692                         toplevel->get_size (pre_eq_size.width, pre_eq_size.height);
693                 }
694
695                 plugin_analysis_expander.add (*eqgui);
696                 plugin_analysis_expander.show_all ();
697                 eqgui->start_listening ();
698         }
699
700         if (!plugin_analysis_expander.get_expanded()) {
701
702                 // Hide & remove from expander
703
704                 eqgui->hide ();
705                 eqgui->stop_listening ();
706                 plugin_analysis_expander.remove();
707
708                 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
709
710                 if (toplevel) {
711                         toplevel->resize (pre_eq_size.width, pre_eq_size.height);
712                 }
713         }
714 }
715
716 void
717 PlugUIBase::update_preset_list ()
718 {
719         vector<string> preset_labels;
720         vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets();
721
722         ++_no_load_preset;
723
724         for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator i = presets.begin(); i != presets.end(); ++i) {
725                 preset_labels.push_back (i->label);
726         }
727
728         set_popdown_strings (_preset_combo, preset_labels);
729
730         --_no_load_preset;
731 }
732
733 void
734 PlugUIBase::update_preset ()
735 {
736         Plugin::PresetRecord p = plugin->last_preset();
737
738         ++_no_load_preset;
739         _preset_combo.set_active_text (p.label);
740         --_no_load_preset;
741
742         save_button.set_sensitive (!p.uri.empty() && p.user);
743         delete_button.set_sensitive (!p.uri.empty() && p.user);
744
745         update_preset_modified ();
746 }
747
748 void
749 PlugUIBase::update_preset_modified ()
750 {
751         if (plugin->last_preset().uri.empty()) {
752                 _preset_modified.set_text ("");
753                 return;
754         }
755
756         bool const c = plugin->parameter_changed_since_last_preset ();
757         if (_preset_modified.get_text().empty() == c) {
758                 _preset_modified.set_text (c ? "*" : "");
759         }
760 }
761
762 void
763 PlugUIBase::parameter_changed (uint32_t, float)
764 {
765         update_preset_modified ();
766 }
767
768 void
769 PlugUIBase::preset_added_or_removed ()
770 {
771         /* Update both the list and the currently-displayed preset */
772         update_preset_list ();
773         update_preset ();
774 }