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