patch from colinf to fix the width of generic plugin editors
[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         }
160
161         set_default_size (w, h);
162 }
163
164 PluginUIWindow::~PluginUIWindow ()
165 {
166         delete _pluginui;
167 }
168
169 void
170 PluginUIWindow::set_parent (Gtk::Window* win)
171 {
172         parent = win;
173 }
174
175 void
176 PluginUIWindow::on_map ()
177 {
178         Window::on_map ();
179         set_keep_above (true);
180 }
181
182 bool
183 PluginUIWindow::on_enter_notify_event (GdkEventCrossing *ev)
184 {
185         Keyboard::the_keyboard().enter_window (ev, this);
186         return false;
187 }
188
189 bool
190 PluginUIWindow::on_leave_notify_event (GdkEventCrossing *ev)
191 {
192         Keyboard::the_keyboard().leave_window (ev, this);
193         return false;
194 }
195
196 bool
197 PluginUIWindow::on_focus_in_event (GdkEventFocus *ev)
198 {
199         Window::on_focus_in_event (ev);
200         //Keyboard::the_keyboard().magic_widget_grab_focus ();
201         return false;
202 }
203
204 bool
205 PluginUIWindow::on_focus_out_event (GdkEventFocus *ev)
206 {
207         Window::on_focus_out_event (ev);
208         //Keyboard::the_keyboard().magic_widget_drop_focus ();
209         return false;
210 }
211
212 void
213 PluginUIWindow::on_show ()
214 {
215         set_role("plugin_ui");
216
217         if (_pluginui) {
218                 _pluginui->update_preset_list ();
219                 _pluginui->update_preset ();
220         }
221
222         if (_pluginui) {
223                 if (_pluginui->on_window_show (_title)) {
224                         Window::on_show ();
225                 }
226         }
227
228         if (parent) {
229                 // set_transient_for (*parent);
230         }
231 }
232
233 void
234 PluginUIWindow::on_hide ()
235 {
236         Window::on_hide ();
237
238         if (_pluginui) {
239                 _pluginui->on_window_hide ();
240         }
241 }
242
243 void
244 PluginUIWindow::set_title(const std::string& title)
245 {
246         Gtk::Window::set_title(title);
247         _title = title;
248 }
249
250 bool
251 #ifdef WINDOWS_VST_SUPPORT
252 PluginUIWindow::create_windows_vst_editor(boost::shared_ptr<PluginInsert> insert)
253 #else
254 PluginUIWindow::create_windows_vst_editor(boost::shared_ptr<PluginInsert>)
255 #endif
256 {
257 #ifndef WINDOWS_VST_SUPPORT
258         return false;
259 #else
260
261         boost::shared_ptr<WindowsVSTPlugin> vp;
262
263         if ((vp = boost::dynamic_pointer_cast<WindowsVSTPlugin> (insert->plugin())) == 0) {
264                 error << _("unknown type of editor-supplying plugin (note: no VST support in this version of ardour)")
265                               << endmsg;
266                 throw failed_constructor ();
267         } else {
268                 WindowsVSTPluginUI* vpu = new WindowsVSTPluginUI (insert, vp);
269
270                 _pluginui = vpu;
271                 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
272                 add (*vpu);
273                 vpu->package (*this);
274         }
275
276         return true;
277 #endif
278 }
279
280 bool
281 #ifdef LXVST_SUPPORT
282 PluginUIWindow::create_lxvst_editor(boost::shared_ptr<PluginInsert> insert)
283 #else
284 PluginUIWindow::create_lxvst_editor(boost::shared_ptr<PluginInsert>)
285 #endif
286 {
287 #ifndef LXVST_SUPPORT
288         return false;
289 #else
290
291         boost::shared_ptr<LXVSTPlugin> lxvp;
292
293         if ((lxvp = boost::dynamic_pointer_cast<LXVSTPlugin> (insert->plugin())) == 0) {
294                 error << _("unknown type of editor-supplying plugin (note: no linuxVST support in this version of ardour)")
295                               << endmsg;
296                 throw failed_constructor ();
297         } else {
298                 LXVSTPluginUI* lxvpu = new LXVSTPluginUI (insert, lxvp);
299
300                 _pluginui = lxvpu;
301                 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
302                 add (*lxvpu);
303                 lxvpu->package (*this);
304         }
305
306         return true;
307 #endif
308 }
309
310 bool
311 #ifdef AUDIOUNIT_SUPPORT
312 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert> insert)
313 #else
314 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert>)
315 #endif
316 {
317 #ifndef AUDIOUNIT_SUPPORT
318         return false;
319 #else
320         VBox* box;
321         _pluginui = create_au_gui (insert, &box);
322         _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
323         add (*box);
324
325         Application::instance()->ActivationChanged.connect (mem_fun (*this, &PluginUIWindow::app_activated));
326
327         return true;
328 #endif
329 }
330
331 void
332 #ifdef GTKOSX
333 PluginUIWindow::app_activated (bool yn)
334 #else
335 PluginUIWindow::app_activated (bool)
336 #endif
337 {
338 #ifdef AUDIOUNIT_SUPPORT
339         if (_pluginui) {
340                 if (yn) {
341                         if (was_visible) {
342                                 _pluginui->activate ();
343                                 present ();
344                                 was_visible = true;
345                         }
346                 } else {
347                         was_visible = is_visible();
348                         hide ();
349                         _pluginui->deactivate ();
350                 }
351         }
352 #endif
353 }
354
355 bool
356 PluginUIWindow::create_lv2_editor(boost::shared_ptr<PluginInsert> insert)
357 {
358 #ifdef HAVE_SUIL
359         boost::shared_ptr<LV2Plugin> vp;
360
361         if ((vp = boost::dynamic_pointer_cast<LV2Plugin> (insert->plugin())) == 0) {
362                 error << _("create_lv2_editor called on non-LV2 plugin") << endmsg;
363                 throw failed_constructor ();
364         } else {
365                 LV2PluginUI* lpu = new LV2PluginUI (insert, vp);
366                 _pluginui = lpu;
367                 add (*lpu);
368                 lpu->package (*this);
369         }
370
371         return true;
372 #else
373         return false;
374 #endif
375 }
376
377 void
378 PluginUIWindow::keyboard_focused (bool yn)
379 {
380         _keyboard_focused = yn;
381 }
382
383 bool
384 PluginUIWindow::on_key_press_event (GdkEventKey* event)
385 {
386         if (_keyboard_focused) {
387                 if (_pluginui) {
388                         if (_pluginui->non_gtk_gui()) {
389                                 _pluginui->forward_key_event (event);
390                         } else {
391                                 return relay_key_press (event, this);
392                         }
393                 }
394                 return true;
395         } else {
396                 /* for us to be getting key press events, there really
397                    MUST be a _pluginui, but just to be safe, check ...
398                 */
399
400                 if (_pluginui) {
401                         if (_pluginui->non_gtk_gui()) {
402                                 /* pass editor window as the window for the event
403                                    to be handled in, not this one, because there are
404                                    no widgets in this window that we want to have
405                                    key focus.
406                                 */
407                                 return relay_key_press (event, &PublicEditor::instance());
408                         } else {
409                                 return relay_key_press (event, this);
410                         }
411                 } else {
412                         return false;
413                 }
414         }
415 }
416
417 bool
418 PluginUIWindow::on_key_release_event (GdkEventKey *event)
419 {
420         if (_keyboard_focused) {
421                 if (_pluginui) {
422                         if (_pluginui->non_gtk_gui()) {
423                                 _pluginui->forward_key_event (event);
424                         }
425                         return true;
426                 }
427                 return false;
428         } else {
429                 return true;
430         }
431 }
432
433 void
434 PluginUIWindow::plugin_going_away ()
435 {
436         ENSURE_GUI_THREAD (*this, &PluginUIWindow::plugin_going_away)
437
438         if (_pluginui) {
439                 _pluginui->stop_updating(0);
440         }
441
442         death_connection.disconnect ();
443
444         delete_when_idle (this);
445 }
446
447 PlugUIBase::PlugUIBase (boost::shared_ptr<PluginInsert> pi)
448         : insert (pi)
449         , plugin (insert->plugin())
450         , add_button (_("Add"))
451         , save_button (_("Save"))
452         , delete_button (_("Delete"))
453         , bypass_button (ArdourButton::led_default_elements)
454         , plugin_analysis_expander (_("Plugin analysis"))
455         , latency_gui (0)
456         , latency_dialog (0)
457         , eqgui (0)
458 {
459         _preset_modified.set_size_request (16, -1);
460         _preset_combo.signal_changed().connect(sigc::mem_fun(*this, &PlugUIBase::preset_selected));
461         ARDOUR_UI::instance()->set_tip (_preset_combo, _("Presets (if any) for this plugin\n(Both factory and user-created)"));
462         ARDOUR_UI::instance()->set_tip (add_button, _("Save a new preset"));
463         ARDOUR_UI::instance()->set_tip (save_button, _("Save the current preset"));
464         ARDOUR_UI::instance()->set_tip (delete_button, _("Delete the current preset"));
465         ARDOUR_UI::instance()->set_tip (bypass_button, _("Disable signal processing by the plugin"));
466         _no_load_preset = 0;
467
468         update_preset_list ();
469         update_preset ();
470
471         add_button.set_name ("PluginAddButton");
472         add_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::add_plugin_setting));
473
474         save_button.set_name ("PluginSaveButton");
475         save_button.signal_clicked().connect(sigc::mem_fun(*this, &PlugUIBase::save_plugin_setting));
476
477         delete_button.set_name ("PluginDeleteButton");
478         delete_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::delete_plugin_setting));
479
480         insert->ActiveChanged.connect (active_connection, invalidator (*this), boost::bind (&PlugUIBase::processor_active_changed, this,  boost::weak_ptr<Processor>(insert)), gui_context());
481
482         bypass_button.set_name ("plugin bypass button");
483         bypass_button.set_text (_("Bypass"));
484
485         if (!pi->active()) {
486                 bypass_button.set_active_state (Gtkmm2ext::Active);
487         } else {
488                 bypass_button.unset_active_state ();
489         }
490
491         bypass_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::bypass_button_release));
492         focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
493
494         focus_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::focus_toggled));
495         focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
496
497         /* these images are not managed, so that we can remove them at will */
498
499         focus_out_image = new Image (get_icon (X_("computer_keyboard")));
500         focus_in_image = new Image (get_icon (X_("computer_keyboard_active")));
501
502         focus_button.add (*focus_out_image);
503
504         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));
505         ARDOUR_UI::instance()->set_tip (bypass_button, _("Click to enable/disable this plugin"));
506
507         plugin_analysis_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_plugin_analysis));
508         plugin_analysis_expander.set_expanded(false);
509
510         insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PlugUIBase::plugin_going_away, this), gui_context());
511
512         plugin->PresetAdded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
513         plugin->PresetRemoved.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
514         plugin->PresetLoaded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset, this), gui_context ());
515         plugin->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::parameter_changed, this, _1, _2), gui_context ());
516 }
517
518 PlugUIBase::~PlugUIBase()
519 {
520         delete eqgui;
521         delete latency_gui;
522 }
523
524 void
525 PlugUIBase::plugin_going_away ()
526 {
527         /* drop references to the plugin/insert */
528         insert.reset ();
529         plugin.reset ();
530         death_connection.disconnect ();
531 }
532
533 void
534 PlugUIBase::set_latency_label ()
535 {
536         framecnt_t const l = insert->effective_latency ();
537         framecnt_t const sr = insert->session().frame_rate ();
538
539         string t;
540
541         if (l < sr / 1000) {
542                 t = string_compose (_("latency (%1 samples)"), l);
543         } else {
544                 t = string_compose (_("latency (%1 ms)"), (float) l / ((float) sr / 1000.0f));
545         }
546
547         latency_label.set_text (t);
548 }
549
550 void
551 PlugUIBase::latency_button_clicked ()
552 {
553         if (!latency_gui) {
554                 latency_gui = new LatencyGUI (*(insert.get()), insert->session().frame_rate(), insert->session().get_block_size());
555                 latency_dialog = new ArdourWindow (_("Edit Latency"));
556                 latency_dialog->add (*latency_gui);
557                 latency_dialog->signal_hide().connect (sigc::mem_fun (*this, &PlugUIBase::set_latency_label));
558         }
559
560         latency_dialog->show_all ();
561 }
562
563 void
564 PlugUIBase::processor_active_changed (boost::weak_ptr<Processor> weak_p)
565 {
566         ENSURE_GUI_THREAD (*this, &PlugUIBase::processor_active_changed, weak_p);
567         boost::shared_ptr<Processor> p (weak_p.lock());
568
569         if (p) {
570                 if (!p->active()) {
571                         bypass_button.set_active_state (Gtkmm2ext::Active);
572                 } else {
573                         bypass_button.unset_active_state ();
574                 }
575         }
576 }
577
578 void
579 PlugUIBase::preset_selected ()
580 {
581         if (_no_load_preset) {
582                 return;
583         }
584
585         if (_preset_combo.get_active_text().length() > 0) {
586                 const Plugin::PresetRecord* pr = plugin->preset_by_label (_preset_combo.get_active_text());
587                 if (pr) {
588                         plugin->load_preset (*pr);
589                 } else {
590                         warning << string_compose(_("Plugin preset %1 not found"),
591                                                   _preset_combo.get_active_text()) << endmsg;
592                 }
593         }
594 }
595
596 void
597 PlugUIBase::add_plugin_setting ()
598 {
599         NewPluginPresetDialog d (plugin);
600
601         switch (d.run ()) {
602         case Gtk::RESPONSE_ACCEPT:
603                 if (d.name().empty()) {
604                         break;
605                 }
606
607                 if (d.replace ()) {
608                         plugin->remove_preset (d.name ());
609                 }
610
611                 Plugin::PresetRecord const r = plugin->save_preset (d.name());
612                 if (!r.uri.empty ()) {
613                         plugin->load_preset (r);
614                 }
615                 break;
616         }
617 }
618
619 void
620 PlugUIBase::save_plugin_setting ()
621 {
622         string const name = _preset_combo.get_active_text ();
623         plugin->remove_preset (name);
624         Plugin::PresetRecord const r = plugin->save_preset (name);
625         if (!r.uri.empty ()) {
626                 plugin->load_preset (r);
627         }
628 }
629
630 void
631 PlugUIBase::delete_plugin_setting ()
632 {
633         plugin->remove_preset (_preset_combo.get_active_text ());
634 }
635
636 bool
637 PlugUIBase::bypass_button_release (GdkEventButton*)
638 {
639         bool view_says_bypassed = (bypass_button.active_state() != 0);
640         
641         if (view_says_bypassed != insert->active()) {
642                 if (view_says_bypassed) {
643                         insert->activate ();
644                 } else {
645                         insert->deactivate ();
646                 }
647         }
648
649         return false;
650 }
651
652 bool
653 PlugUIBase::focus_toggled (GdkEventButton*)
654 {
655         if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
656                 Keyboard::the_keyboard().magic_widget_drop_focus();
657                 focus_button.remove ();
658                 focus_button.add (*focus_out_image);
659                 focus_out_image->show ();
660                 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));
661                 KeyboardFocused (false);
662         } else {
663                 Keyboard::the_keyboard().magic_widget_grab_focus();
664                 focus_button.remove ();
665                 focus_button.add (*focus_in_image);
666                 focus_in_image->show ();
667                 ARDOUR_UI::instance()->set_tip (focus_button, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME));
668                 KeyboardFocused (true);
669         }
670
671         return true;
672 }
673
674 void
675 PlugUIBase::toggle_plugin_analysis()
676 {
677         if (plugin_analysis_expander.get_expanded() &&
678             !plugin_analysis_expander.get_child()) {
679                 // Create the GUI
680                 if (eqgui == 0) {
681                         eqgui = new PluginEqGui (insert);
682                 }
683
684                 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
685
686                 if (toplevel) {
687                         toplevel->get_size (pre_eq_size.width, pre_eq_size.height);
688                 }
689
690                 plugin_analysis_expander.add (*eqgui);
691                 plugin_analysis_expander.show_all ();
692                 eqgui->start_listening ();
693         }
694
695         if (!plugin_analysis_expander.get_expanded()) {
696
697                 // Hide & remove from expander
698
699                 eqgui->hide ();
700                 eqgui->stop_listening ();
701                 plugin_analysis_expander.remove();
702
703                 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
704
705                 if (toplevel) {
706                         toplevel->resize (pre_eq_size.width, pre_eq_size.height);
707                 }
708         }
709 }
710
711 void
712 PlugUIBase::update_preset_list ()
713 {
714         vector<string> preset_labels;
715         vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets();
716
717         ++_no_load_preset;
718
719         for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator i = presets.begin(); i != presets.end(); ++i) {
720                 preset_labels.push_back (i->label);
721         }
722
723         set_popdown_strings (_preset_combo, preset_labels);
724
725         --_no_load_preset;
726 }
727
728 void
729 PlugUIBase::update_preset ()
730 {
731         Plugin::PresetRecord p = plugin->last_preset();
732
733         ++_no_load_preset;
734         _preset_combo.set_active_text (p.label);
735         --_no_load_preset;
736
737         save_button.set_sensitive (!p.uri.empty() && p.user);
738         delete_button.set_sensitive (!p.uri.empty() && p.user);
739
740         update_preset_modified ();
741 }
742
743 void
744 PlugUIBase::update_preset_modified ()
745 {
746         if (plugin->last_preset().uri.empty()) {
747                 _preset_modified.set_text ("");
748                 return;
749         }
750
751         bool const c = plugin->parameter_changed_since_last_preset ();
752         if (_preset_modified.get_text().empty() == c) {
753                 _preset_modified.set_text (c ? "*" : "");
754         }
755 }
756
757 void
758 PlugUIBase::parameter_changed (uint32_t, float)
759 {
760         update_preset_modified ();
761 }
762
763 void
764 PlugUIBase::preset_added_or_removed ()
765 {
766         /* Update both the list and the currently-displayed preset */
767         update_preset_list ();
768         update_preset ();
769 }