more ardour-button-ization; fix transparency of selection rects; use "correct" cursor...
[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 VST_SUPPORT
50 #include "ardour/vst_plugin.h"
51 #include "vst_pluginui.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_dialog.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::VST:
103                         have_gui = create_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 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 VST_SUPPORT
257 PluginUIWindow::create_vst_editor(boost::shared_ptr<PluginInsert> insert)
258 #else
259 PluginUIWindow::create_vst_editor(boost::shared_ptr<PluginInsert>)
260 #endif
261 {
262 #ifndef VST_SUPPORT
263         return false;
264 #else
265
266         boost::shared_ptr<VSTPlugin> vp;
267
268         if ((vp = boost::dynamic_pointer_cast<VSTPlugin> (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                 VSTPluginUI* vpu = new VSTPluginUI (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 GTKOSX
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 GTKOSX
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 GTKOSX
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         , latency_gui (0)
460         , latency_dialog (0)
461         , plugin_analysis_expander (_("Plugin analysis"))
462         , eqgui (0)
463 {
464         _preset_combo.set_size_request (100, -1);
465         _preset_modified.set_size_request (16, -1);
466         _preset_combo.signal_changed().connect(sigc::mem_fun(*this, &PlugUIBase::preset_selected));
467         ARDOUR_UI::instance()->set_tip (_preset_combo, _("Presets (if any) for this plugin\n(Both factory and user-created)"));
468         ARDOUR_UI::instance()->set_tip (add_button, _("Save a new preset"));
469         ARDOUR_UI::instance()->set_tip (save_button, _("Save the current preset"));
470         ARDOUR_UI::instance()->set_tip (delete_button, _("Delete the current preset"));
471         ARDOUR_UI::instance()->set_tip (bypass_button, _("Disable signal processing by the plugin"));
472         _no_load_preset = 0;
473
474         _preset_box.pack_start (_preset_combo);
475         _preset_box.pack_start (_preset_modified);
476
477         update_preset_list ();
478         update_preset ();
479
480         add_button.set_name ("PluginAddButton");
481         add_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::add_plugin_setting));
482
483         save_button.set_name ("PluginSaveButton");
484         save_button.signal_clicked().connect(sigc::mem_fun(*this, &PlugUIBase::save_plugin_setting));
485
486         delete_button.set_name ("PluginDeleteButton");
487         delete_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::delete_plugin_setting));
488
489         insert->ActiveChanged.connect (active_connection, invalidator (*this), boost::bind (&PlugUIBase::processor_active_changed, this,  boost::weak_ptr<Processor>(insert)), gui_context());
490
491         bypass_button.set_name ("plugin bypass button");
492         bypass_button.set_text (_("Bypass"));
493
494         if (!pi->active()) {
495                 bypass_button.set_active_state (Gtkmm2ext::Active);
496         } else {
497                 bypass_button.unset_active_state ();
498         }
499
500         bypass_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::bypass_button_release));
501         focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
502
503         focus_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::focus_toggled));
504         focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
505
506         /* these images are not managed, so that we can remove them at will */
507
508         focus_out_image = new Image (get_icon (X_("computer_keyboard")));
509         focus_in_image = new Image (get_icon (X_("computer_keyboard_active")));
510
511         focus_button.add (*focus_out_image);
512
513         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));
514         ARDOUR_UI::instance()->set_tip (bypass_button, _("Click to enable/disable this plugin"));
515
516         plugin_analysis_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_plugin_analysis));
517         plugin_analysis_expander.set_expanded(false);
518
519         insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PlugUIBase::plugin_going_away, this), gui_context());
520
521         plugin->PresetAdded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
522         plugin->PresetRemoved.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
523         plugin->PresetLoaded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset, this), gui_context ());
524         plugin->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::parameter_changed, this, _1, _2), gui_context ());
525 }
526
527 PlugUIBase::~PlugUIBase()
528 {
529         delete eqgui;
530         delete latency_gui;
531 }
532
533 void
534 PlugUIBase::plugin_going_away ()
535 {
536         /* drop references to the plugin/insert */
537         insert.reset ();
538         plugin.reset ();
539         death_connection.disconnect ();
540 }
541
542 void
543 PlugUIBase::set_latency_label ()
544 {
545         framecnt_t const l = insert->effective_latency ();
546         framecnt_t const sr = insert->session().frame_rate ();
547
548         string t;
549
550         if (l < sr / 1000) {
551                 t = string_compose (_("latency (%1 samples)"), l);
552         } else {
553                 t = string_compose (_("latency (%1 ms)"), (float) l / ((float) sr / 1000.0f));
554         }
555
556         latency_label.set_text (t);
557 }
558
559 void
560 PlugUIBase::latency_button_clicked ()
561 {
562         if (!latency_gui) {
563                 latency_gui = new LatencyGUI (*(insert.get()), insert->session().frame_rate(), insert->session().get_block_size());
564                 latency_dialog = new ArdourDialog (_("Edit Latency"), false, false);
565                 latency_dialog->get_vbox()->pack_start (*latency_gui);
566                 latency_dialog->signal_hide().connect (sigc::mem_fun (*this, &PlugUIBase::set_latency_label));
567         }
568
569         latency_dialog->show_all ();
570 }
571
572 void
573 PlugUIBase::processor_active_changed (boost::weak_ptr<Processor> weak_p)
574 {
575         ENSURE_GUI_THREAD (*this, &PlugUIBase::processor_active_changed, weak_p);
576         boost::shared_ptr<Processor> p (weak_p.lock());
577
578         if (p) {
579                 if (!p->active()) {
580                         bypass_button.set_active_state (Gtkmm2ext::Active);
581                 } else {
582                         bypass_button.unset_active_state ();
583                 }
584         }
585 }
586
587 void
588 PlugUIBase::preset_selected ()
589 {
590         if (_no_load_preset) {
591                 return;
592         }
593
594         if (_preset_combo.get_active_text().length() > 0) {
595                 const Plugin::PresetRecord* pr = plugin->preset_by_label (_preset_combo.get_active_text());
596                 if (pr) {
597                         plugin->load_preset (*pr);
598                 } else {
599                         warning << string_compose(_("Plugin preset %1 not found"),
600                                                   _preset_combo.get_active_text()) << endmsg;
601                 }
602         }
603 }
604
605 void
606 PlugUIBase::add_plugin_setting ()
607 {
608         NewPluginPresetDialog d (plugin);
609
610         switch (d.run ()) {
611         case Gtk::RESPONSE_ACCEPT:
612                 if (d.name().empty()) {
613                         break;
614                 }
615
616                 if (d.replace ()) {
617                         plugin->remove_preset (d.name ());
618                 }
619
620                 Plugin::PresetRecord const r = plugin->save_preset (d.name());
621                 if (!r.uri.empty ()) {
622                         plugin->load_preset (r);
623                 }
624                 break;
625         }
626 }
627
628 void
629 PlugUIBase::save_plugin_setting ()
630 {
631         string const name = _preset_combo.get_active_text ();
632         plugin->remove_preset (name);
633         Plugin::PresetRecord const r = plugin->save_preset (name);
634         if (!r.uri.empty ()) {
635                 plugin->load_preset (r);
636         }
637 }
638
639 void
640 PlugUIBase::delete_plugin_setting ()
641 {
642         plugin->remove_preset (_preset_combo.get_active_text ());
643 }
644
645 bool
646 PlugUIBase::bypass_button_release (GdkEventButton*)
647 {
648         bool view_says_bypassed = (bypass_button.active_state() != 0);
649         
650         if (view_says_bypassed != insert->active()) {
651                 if (view_says_bypassed) {
652                         insert->activate ();
653                 } else {
654                         insert->deactivate ();
655                 }
656         }
657
658         return false;
659 }
660
661 bool
662 PlugUIBase::focus_toggled (GdkEventButton*)
663 {
664         if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
665                 Keyboard::the_keyboard().magic_widget_drop_focus();
666                 focus_button.remove ();
667                 focus_button.add (*focus_out_image);
668                 focus_out_image->show ();
669                 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));
670                 KeyboardFocused (false);
671         } else {
672                 Keyboard::the_keyboard().magic_widget_grab_focus();
673                 focus_button.remove ();
674                 focus_button.add (*focus_in_image);
675                 focus_in_image->show ();
676                 ARDOUR_UI::instance()->set_tip (focus_button, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME));
677                 KeyboardFocused (true);
678         }
679
680         return true;
681 }
682
683 void
684 PlugUIBase::toggle_plugin_analysis()
685 {
686         if (plugin_analysis_expander.get_expanded() &&
687             !plugin_analysis_expander.get_child()) {
688                 // Create the GUI
689                 if (eqgui == 0) {
690                         eqgui = new PluginEqGui (insert);
691                 }
692
693                 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
694
695                 if (toplevel) {
696                         toplevel->get_size (pre_eq_size.width, pre_eq_size.height);
697                 }
698
699                 plugin_analysis_expander.add (*eqgui);
700                 plugin_analysis_expander.show_all ();
701                 eqgui->start_listening ();
702         }
703
704         if (!plugin_analysis_expander.get_expanded()) {
705
706                 // Hide & remove from expander
707
708                 eqgui->hide ();
709                 eqgui->stop_listening ();
710                 plugin_analysis_expander.remove();
711
712                 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
713
714                 if (toplevel) {
715                         toplevel->resize (pre_eq_size.width, pre_eq_size.height);
716                 }
717         }
718 }
719
720 void
721 PlugUIBase::update_preset_list ()
722 {
723         vector<string> preset_labels;
724         vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets();
725
726         ++_no_load_preset;
727
728         for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator i = presets.begin(); i != presets.end(); ++i) {
729                 preset_labels.push_back (i->label);
730         }
731
732         set_popdown_strings (_preset_combo, preset_labels);
733
734         --_no_load_preset;
735 }
736
737 void
738 PlugUIBase::update_preset ()
739 {
740         Plugin::PresetRecord p = plugin->last_preset();
741
742         ++_no_load_preset;
743         _preset_combo.set_active_text (p.label);
744         --_no_load_preset;
745
746         save_button.set_sensitive (!p.uri.empty() && p.user);
747         delete_button.set_sensitive (!p.uri.empty() && p.user);
748
749         update_preset_modified ();
750 }
751
752 void
753 PlugUIBase::update_preset_modified ()
754 {
755         if (plugin->last_preset().uri.empty()) {
756                 _preset_modified.set_text ("");
757                 return;
758         }
759
760         bool const c = plugin->parameter_changed_since_last_preset ();
761         if (_preset_modified.get_text().empty() == c) {
762                 _preset_modified.set_text (c ? "*" : "");
763         }
764 }
765
766 void
767 PlugUIBase::parameter_changed (uint32_t, float)
768 {
769         update_preset_modified ();
770 }
771
772 void
773 PlugUIBase::preset_added_or_removed ()
774 {
775         /* Update both the list and the currently-displayed preset */
776         update_preset_list ();
777         update_preset ();
778 }