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