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