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