hook up focus_button for LV2 GUIs
[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                         if (_pluginui->non_gtk_gui()) {
357                                 _pluginui->forward_key_event (event);
358                         } else {
359                                 return relay_key_press (event, this);
360                         }
361                 }
362                 return true;
363         } else {
364                 /* for us to be getting key press events, there really
365                    MUST be a _pluginui, but just to be safe, check ...
366                 */
367
368                 if (_pluginui) {
369                         if (_pluginui->non_gtk_gui()) {
370                                 /* pass editor window as the window for the event
371                                    to be handled in, not this one, because there are
372                                    no widgets in this window that we want to have
373                                    key focus.
374                                 */
375                                 return relay_key_press (event, &PublicEditor::instance());
376                         } else {
377                                 return relay_key_press (event, this);
378                         }
379                 } else {
380                         return false;
381                 }
382         }
383 }
384
385 bool
386 PluginUIWindow::on_key_release_event (GdkEventKey *event)
387 {
388         if (_keyboard_focused) {
389                 if (_pluginui) {
390                         if (_pluginui->non_gtk_gui()) {
391                                 _pluginui->forward_key_event (event);
392                         }
393                         return true;
394                 }
395                 return false;
396         } else {
397                 return true;
398         }
399 }
400
401 void
402 PluginUIWindow::plugin_going_away ()
403 {
404         ENSURE_GUI_THREAD (*this, &PluginUIWindow::plugin_going_away)
405
406         if (_pluginui) {
407                 _pluginui->stop_updating(0);
408         }
409
410         death_connection.disconnect ();
411 }
412
413 PlugUIBase::PlugUIBase (boost::shared_ptr<PluginInsert> pi)
414         : insert (pi)
415         , plugin (insert->plugin())
416         , add_button (_("Add"))
417         , save_button (_("Save"))
418         , delete_button (_("Delete"))
419         , bypass_button (ArdourButton::led_default_elements)
420         , description_expander (_("Description"))
421         , plugin_analysis_expander (_("Plugin analysis"))
422         , latency_gui (0)
423         , latency_dialog (0)
424         , eqgui (0)
425 {
426         _preset_modified.set_size_request (16, -1);
427         _preset_combo.signal_changed().connect(sigc::mem_fun(*this, &PlugUIBase::preset_selected));
428         ARDOUR_UI::instance()->set_tip (_preset_combo, _("Presets (if any) for this plugin\n(Both factory and user-created)"));
429         ARDOUR_UI::instance()->set_tip (add_button, _("Save a new preset"));
430         ARDOUR_UI::instance()->set_tip (save_button, _("Save the current preset"));
431         ARDOUR_UI::instance()->set_tip (delete_button, _("Delete the current preset"));
432         ARDOUR_UI::instance()->set_tip (bypass_button, _("Disable signal processing by the plugin"));
433         _no_load_preset = 0;
434
435         update_preset_list ();
436         update_preset ();
437
438         add_button.set_name ("PluginAddButton");
439         add_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::add_plugin_setting));
440
441         save_button.set_name ("PluginSaveButton");
442         save_button.signal_clicked().connect(sigc::mem_fun(*this, &PlugUIBase::save_plugin_setting));
443
444         delete_button.set_name ("PluginDeleteButton");
445         delete_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::delete_plugin_setting));
446
447         insert->ActiveChanged.connect (active_connection, invalidator (*this), boost::bind (&PlugUIBase::processor_active_changed, this,  boost::weak_ptr<Processor>(insert)), gui_context());
448
449         bypass_button.set_name ("plugin bypass button");
450         bypass_button.set_text (_("Bypass"));
451         bypass_button.set_active (!pi->active());
452         bypass_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::bypass_button_release), false);
453         focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
454
455         focus_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::focus_toggled));
456         focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
457
458         /* these images are not managed, so that we can remove them at will */
459
460         focus_out_image = new Image (get_icon (X_("computer_keyboard")));
461         focus_in_image = new Image (get_icon (X_("computer_keyboard_active")));
462
463         focus_button.add (*focus_out_image);
464
465         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));
466         ARDOUR_UI::instance()->set_tip (bypass_button, _("Click to enable/disable this plugin"));
467
468         description_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_description));
469         description_expander.set_expanded(false);
470
471         plugin_analysis_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_plugin_analysis));
472         plugin_analysis_expander.set_expanded(false);
473
474         insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PlugUIBase::plugin_going_away, this), gui_context());
475
476         plugin->PresetAdded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
477         plugin->PresetRemoved.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
478         plugin->PresetLoaded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset, this), gui_context ());
479         plugin->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::parameter_changed, this, _1, _2), gui_context ());
480 }
481
482 PlugUIBase::~PlugUIBase()
483 {
484         delete eqgui;
485         delete latency_gui;
486 }
487
488 void
489 PlugUIBase::plugin_going_away ()
490 {
491         /* drop references to the plugin/insert */
492         insert.reset ();
493         plugin.reset ();
494 }
495
496 void
497 PlugUIBase::set_latency_label ()
498 {
499         framecnt_t const l = insert->effective_latency ();
500         framecnt_t const sr = insert->session().frame_rate ();
501
502         string t;
503
504         if (l < sr / 1000) {
505                 t = string_compose (P_("latency (%1 sample)", "latency (%1 samples)", l), l);
506         } else {
507                 t = string_compose (_("latency (%1 ms)"), (float) l / ((float) sr / 1000.0f));
508         }
509
510         latency_label.set_text (t);
511 }
512
513 void
514 PlugUIBase::latency_button_clicked ()
515 {
516         if (!latency_gui) {
517                 latency_gui = new LatencyGUI (*(insert.get()), insert->session().frame_rate(), insert->session().get_block_size());
518                 latency_dialog = new ArdourWindow (_("Edit Latency"));
519                 /* use both keep-above and transient for to try cover as many
520                    different WM's as possible.
521                 */
522                 latency_dialog->set_keep_above (true);
523                 Window* win = dynamic_cast<Window*> (bypass_button.get_toplevel ());
524                 if (win) {
525                         latency_dialog->set_transient_for (*win);
526                 }
527                 latency_dialog->add (*latency_gui);
528                 latency_dialog->signal_hide().connect (sigc::mem_fun (*this, &PlugUIBase::set_latency_label));
529         }
530
531         latency_dialog->show_all ();
532 }
533
534 void
535 PlugUIBase::processor_active_changed (boost::weak_ptr<Processor> weak_p)
536 {
537         ENSURE_GUI_THREAD (*this, &PlugUIBase::processor_active_changed, weak_p);
538         boost::shared_ptr<Processor> p (weak_p.lock());
539
540         if (p) {
541                 bypass_button.set_active (!p->active());
542         }
543 }
544
545 void
546 PlugUIBase::preset_selected ()
547 {
548         if (_no_load_preset) {
549                 return;
550         }
551
552         if (_preset_combo.get_active_text().length() > 0) {
553                 const Plugin::PresetRecord* pr = plugin->preset_by_label (_preset_combo.get_active_text());
554                 if (pr) {
555                         plugin->load_preset (*pr);
556                 } else {
557                         warning << string_compose(_("Plugin preset %1 not found"),
558                                                   _preset_combo.get_active_text()) << endmsg;
559                 }
560         } else {
561                 // blank selected = no preset
562                 plugin->clear_preset();
563         }
564 }
565
566 #ifdef NO_PLUGIN_STATE
567 static bool seen_saving_message = false;
568 #endif
569
570 void
571 PlugUIBase::add_plugin_setting ()
572 {
573 #ifndef NO_PLUGIN_STATE
574         NewPluginPresetDialog d (plugin);
575
576         switch (d.run ()) {
577         case Gtk::RESPONSE_ACCEPT:
578                 if (d.name().empty()) {
579                         break;
580                 }
581
582                 if (d.replace ()) {
583                         plugin->remove_preset (d.name ());
584                 }
585
586                 Plugin::PresetRecord const r = plugin->save_preset (d.name());
587                 if (!r.uri.empty ()) {
588                         plugin->load_preset (r);
589                 }
590                 break;
591         }
592 #else 
593         if (!seen_saving_message) {
594                 info << string_compose (_("Plugin presets are not supported in this build of %1. Consider paying for a full version"),
595                                         PROGRAM_NAME)
596                      << endmsg;
597                 seen_saving_message = true;
598         }
599 #endif
600 }
601
602 void
603 PlugUIBase::save_plugin_setting ()
604 {
605 #ifndef NO_PLUGIN_STATE
606         string const name = _preset_combo.get_active_text ();
607         plugin->remove_preset (name);
608         Plugin::PresetRecord const r = plugin->save_preset (name);
609         if (!r.uri.empty ()) {
610                 plugin->load_preset (r);
611         }
612 #else 
613         if (!seen_saving_message) {
614                 info << string_compose (_("Plugin presets are not supported in this build of %1. Consider paying for a newer version"),
615                                         PROGRAM_NAME)
616                      << endmsg;
617                 seen_saving_message = true;
618         }
619 #endif
620 }
621
622 void
623 PlugUIBase::delete_plugin_setting ()
624 {
625 #ifndef NO_PLUGIN_STATE
626         plugin->remove_preset (_preset_combo.get_active_text ());
627 #else
628         if (!seen_saving_message) {
629                 info << string_compose (_("Plugin presets are not supported in this build of %1. Consider paying for a newer version"),
630                                         PROGRAM_NAME)
631                      << endmsg;
632                 seen_saving_message = true;
633         }
634 #endif
635 }
636
637 bool
638 PlugUIBase::bypass_button_release (GdkEventButton*)
639 {
640         bool view_says_bypassed = (bypass_button.active_state() != 0);
641         
642         if (view_says_bypassed != insert->active()) {
643                 if (view_says_bypassed) {
644                         insert->activate ();
645                 } else {
646                         insert->deactivate ();
647                 }
648         }
649
650         return false;
651 }
652
653 bool
654 PlugUIBase::focus_toggled (GdkEventButton*)
655 {
656         if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
657                 Keyboard::the_keyboard().magic_widget_drop_focus();
658                 focus_button.remove ();
659                 focus_button.add (*focus_out_image);
660                 focus_out_image->show ();
661                 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));
662                 KeyboardFocused (false);
663         } else {
664                 Keyboard::the_keyboard().magic_widget_grab_focus();
665                 focus_button.remove ();
666                 focus_button.add (*focus_in_image);
667                 focus_in_image->show ();
668                 ARDOUR_UI::instance()->set_tip (focus_button, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME));
669                 KeyboardFocused (true);
670         }
671
672         return true;
673 }
674
675 void
676 PlugUIBase::toggle_description()
677 {
678         if (description_expander.get_expanded() &&
679             !description_expander.get_child()) {
680                 const std::string text = plugin->get_docs();
681                 if (text.empty()) {
682                         return;
683                 }
684
685                 Gtk::Label* label = manage(new Gtk::Label(text));
686                 label->set_line_wrap(true);
687                 label->set_line_wrap_mode(Pango::WRAP_WORD);
688                 description_expander.add(*label);
689                 description_expander.show_all();
690         }
691         
692         if (!description_expander.get_expanded()) {
693                 description_expander.remove();
694         }
695 }
696
697
698 void
699 PlugUIBase::toggle_plugin_analysis()
700 {
701         if (plugin_analysis_expander.get_expanded() &&
702             !plugin_analysis_expander.get_child()) {
703                 // Create the GUI
704                 if (eqgui == 0) {
705                         eqgui = new PluginEqGui (insert);
706                 }
707
708                 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
709
710                 if (toplevel) {
711                         toplevel->get_size (pre_eq_size.width, pre_eq_size.height);
712                 }
713
714                 plugin_analysis_expander.add (*eqgui);
715                 plugin_analysis_expander.show_all ();
716                 eqgui->start_listening ();
717         }
718
719         if (!plugin_analysis_expander.get_expanded()) {
720                 // Hide & remove from expander
721
722                 eqgui->hide ();
723                 eqgui->stop_listening ();
724                 plugin_analysis_expander.remove();
725
726                 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
727
728                 if (toplevel) {
729                         toplevel->resize (pre_eq_size.width, pre_eq_size.height);
730                 }
731         }
732 }
733
734 void
735 PlugUIBase::update_preset_list ()
736 {
737         vector<string> preset_labels;
738         vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets();
739
740         ++_no_load_preset;
741
742         for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator i = presets.begin(); i != presets.end(); ++i) {
743                 preset_labels.push_back (i->label);
744         }
745
746         preset_labels.push_back("");
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
772         if (plugin->last_preset().uri.empty()) {
773                 _preset_modified.set_text ("");
774                 return;
775         }
776
777         bool const c = plugin->parameter_changed_since_last_preset ();
778         if (_preset_modified.get_text().empty() == c) {
779                 _preset_modified.set_text (c ? "*" : "");
780         }
781 }
782
783 void
784 PlugUIBase::parameter_changed (uint32_t, float)
785 {
786         update_preset_modified ();
787 }
788
789 void
790 PlugUIBase::preset_added_or_removed ()
791 {
792         /* Update both the list and the currently-displayed preset */
793         update_preset_list ();
794         update_preset ();
795 }
796