add VU and IEC meter DSP (from jmeters)
[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         boost::shared_ptr<PluginInsert> insert,
86         bool                            scrollable,
87         bool                            editor)
88         : ArdourWindow (string())
89         , was_visible (false)
90         , _keyboard_focused (false)
91 #ifdef AUDIOUNIT_SUPPORT
92         , pre_deactivate_x (-1)
93         , pre_deactivate_y (-1)
94 #endif
95
96 {
97         bool have_gui = false;
98         Label* label = manage (new Label());
99         label->set_markup ("<b>THIS IS THE PLUGIN UI</b>");
100
101         if (editor && insert->plugin()->has_editor()) {
102                 switch (insert->type()) {
103                 case ARDOUR::Windows_VST:
104                         have_gui = create_windows_vst_editor (insert);
105                         break;
106                         
107                 case ARDOUR::LXVST:
108                         have_gui = create_lxvst_editor (insert);
109                         break;
110
111                 case ARDOUR::AudioUnit:
112                         have_gui = create_audiounit_editor (insert);
113                         break;
114
115                 case ARDOUR::LADSPA:
116                         error << _("Eh? LADSPA plugins don't have editors!") << endmsg;
117                         break;
118
119                 case ARDOUR::LV2:
120                         have_gui = create_lv2_editor (insert);
121                         break;
122
123                 default:
124 #ifndef WINDOWS_VST_SUPPORT
125                         error << string_compose (_("unknown type of editor-supplying plugin (note: no VST support in this version of %1)"), PROGRAM_NAME)
126                               << endmsg;
127 #else
128                         error << _("unknown type of editor-supplying plugin")
129                               << endmsg;
130 #endif
131                         throw failed_constructor ();
132                 }
133
134         }
135
136         if (!have_gui) {
137                 GenericPluginUI* pu = new GenericPluginUI (insert, scrollable);
138
139                 _pluginui = pu;
140                 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
141                 add (*pu);
142                 set_wmclass (X_("ardour_plugin_editor"), PROGRAM_NAME);
143
144                 signal_map_event().connect (sigc::mem_fun (*pu, &GenericPluginUI::start_updating));
145                 signal_unmap_event().connect (sigc::mem_fun (*pu, &GenericPluginUI::stop_updating));
146         }
147
148         set_name ("PluginEditor");
149         add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
150
151         insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PluginUIWindow::plugin_going_away, this), gui_context());
152
153         gint h = _pluginui->get_preferred_height ();
154         gint w = _pluginui->get_preferred_width ();
155
156         if (scrollable) {
157                 if (h > 600) h = 600;
158         }
159
160         set_default_size (w, h);
161         set_resizable (_pluginui->resizable());
162 }
163
164 PluginUIWindow::~PluginUIWindow ()
165 {
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);
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         }
337
338         return true;
339 #else
340         return false;
341 #endif
342 }
343
344 void
345 PluginUIWindow::keyboard_focused (bool yn)
346 {
347         _keyboard_focused = yn;
348 }
349
350 bool
351 PluginUIWindow::on_key_press_event (GdkEventKey* event)
352 {
353         if (_keyboard_focused) {
354                 if (_pluginui) {
355                         if (_pluginui->non_gtk_gui()) {
356                                 _pluginui->forward_key_event (event);
357                         } else {
358                                 return relay_key_press (event, this);
359                         }
360                 }
361                 return true;
362         } else {
363                 /* for us to be getting key press events, there really
364                    MUST be a _pluginui, but just to be safe, check ...
365                 */
366
367                 if (_pluginui) {
368                         if (_pluginui->non_gtk_gui()) {
369                                 /* pass editor window as the window for the event
370                                    to be handled in, not this one, because there are
371                                    no widgets in this window that we want to have
372                                    key focus.
373                                 */
374                                 return relay_key_press (event, &PublicEditor::instance());
375                         } else {
376                                 return relay_key_press (event, this);
377                         }
378                 } else {
379                         return false;
380                 }
381         }
382 }
383
384 bool
385 PluginUIWindow::on_key_release_event (GdkEventKey *event)
386 {
387         if (_keyboard_focused) {
388                 if (_pluginui) {
389                         if (_pluginui->non_gtk_gui()) {
390                                 _pluginui->forward_key_event (event);
391                         }
392                         return true;
393                 }
394                 return false;
395         } else {
396                 return true;
397         }
398 }
399
400 void
401 PluginUIWindow::plugin_going_away ()
402 {
403         ENSURE_GUI_THREAD (*this, &PluginUIWindow::plugin_going_away)
404
405         if (_pluginui) {
406                 _pluginui->stop_updating(0);
407         }
408
409         death_connection.disconnect ();
410
411         delete_when_idle (this);
412 }
413
414 PlugUIBase::PlugUIBase (boost::shared_ptr<PluginInsert> pi)
415         : insert (pi)
416         , plugin (insert->plugin())
417         , add_button (_("Add"))
418         , save_button (_("Save"))
419         , delete_button (_("Delete"))
420         , bypass_button (ArdourButton::led_default_elements)
421         , description_expander (_("Description"))
422         , plugin_analysis_expander (_("Plugin analysis"))
423         , latency_gui (0)
424         , latency_dialog (0)
425         , eqgui (0)
426 {
427         _preset_modified.set_size_request (16, -1);
428         _preset_combo.signal_changed().connect(sigc::mem_fun(*this, &PlugUIBase::preset_selected));
429         ARDOUR_UI::instance()->set_tip (_preset_combo, _("Presets (if any) for this plugin\n(Both factory and user-created)"));
430         ARDOUR_UI::instance()->set_tip (add_button, _("Save a new preset"));
431         ARDOUR_UI::instance()->set_tip (save_button, _("Save the current preset"));
432         ARDOUR_UI::instance()->set_tip (delete_button, _("Delete the current preset"));
433         ARDOUR_UI::instance()->set_tip (bypass_button, _("Disable signal processing by the plugin"));
434         _no_load_preset = 0;
435
436         update_preset_list ();
437         update_preset ();
438
439         add_button.set_name ("PluginAddButton");
440         add_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::add_plugin_setting));
441
442         save_button.set_name ("PluginSaveButton");
443         save_button.signal_clicked().connect(sigc::mem_fun(*this, &PlugUIBase::save_plugin_setting));
444
445         delete_button.set_name ("PluginDeleteButton");
446         delete_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::delete_plugin_setting));
447
448         insert->ActiveChanged.connect (active_connection, invalidator (*this), boost::bind (&PlugUIBase::processor_active_changed, this,  boost::weak_ptr<Processor>(insert)), gui_context());
449
450         bypass_button.set_name ("plugin bypass button");
451         bypass_button.set_text (_("Bypass"));
452         bypass_button.set_active (!pi->active());
453         bypass_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::bypass_button_release));
454         focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
455
456         focus_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::focus_toggled));
457         focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
458
459         /* these images are not managed, so that we can remove them at will */
460
461         focus_out_image = new Image (get_icon (X_("computer_keyboard")));
462         focus_in_image = new Image (get_icon (X_("computer_keyboard_active")));
463
464         focus_button.add (*focus_out_image);
465
466         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));
467         ARDOUR_UI::instance()->set_tip (bypass_button, _("Click to enable/disable this plugin"));
468
469         description_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_description));
470         description_expander.set_expanded(false);
471
472         plugin_analysis_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_plugin_analysis));
473         plugin_analysis_expander.set_expanded(false);
474
475         insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PlugUIBase::plugin_going_away, this), gui_context());
476
477         plugin->PresetAdded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
478         plugin->PresetRemoved.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
479         plugin->PresetLoaded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset, this), gui_context ());
480         plugin->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::parameter_changed, this, _1, _2), gui_context ());
481 }
482
483 PlugUIBase::~PlugUIBase()
484 {
485         delete eqgui;
486         delete latency_gui;
487 }
488
489 void
490 PlugUIBase::plugin_going_away ()
491 {
492         /* drop references to the plugin/insert */
493         insert.reset ();
494         plugin.reset ();
495 }
496
497 void
498 PlugUIBase::set_latency_label ()
499 {
500         framecnt_t const l = insert->effective_latency ();
501         framecnt_t const sr = insert->session().frame_rate ();
502
503         string t;
504
505         if (l < sr / 1000) {
506                 t = string_compose (P_("latency (%1 sample)", "latency (%1 samples)", l), l);
507         } else {
508                 t = string_compose (_("latency (%1 ms)"), (float) l / ((float) sr / 1000.0f));
509         }
510
511         latency_label.set_text (t);
512 }
513
514 void
515 PlugUIBase::latency_button_clicked ()
516 {
517         if (!latency_gui) {
518                 latency_gui = new LatencyGUI (*(insert.get()), insert->session().frame_rate(), insert->session().get_block_size());
519                 latency_dialog = new ArdourWindow (_("Edit Latency"));
520                 /* use both keep-above and transient for to try cover as many
521                    different WM's as possible.
522                 */
523                 latency_dialog->set_keep_above (true);
524                 Window* win = dynamic_cast<Window*> (bypass_button.get_toplevel ());
525                 if (win) {
526                         latency_dialog->set_transient_for (*win);
527                 }
528                 latency_dialog->add (*latency_gui);
529                 latency_dialog->signal_hide().connect (sigc::mem_fun (*this, &PlugUIBase::set_latency_label));
530         }
531
532         latency_dialog->show_all ();
533 }
534
535 void
536 PlugUIBase::processor_active_changed (boost::weak_ptr<Processor> weak_p)
537 {
538         ENSURE_GUI_THREAD (*this, &PlugUIBase::processor_active_changed, weak_p);
539         boost::shared_ptr<Processor> p (weak_p.lock());
540
541         if (p) {
542                 bypass_button.set_active (!p->active());
543         }
544 }
545
546 void
547 PlugUIBase::preset_selected ()
548 {
549         if (_no_load_preset) {
550                 return;
551         }
552
553         if (_preset_combo.get_active_text().length() > 0) {
554                 const Plugin::PresetRecord* pr = plugin->preset_by_label (_preset_combo.get_active_text());
555                 if (pr) {
556                         plugin->load_preset (*pr);
557                 } else {
558                         warning << string_compose(_("Plugin preset %1 not found"),
559                                                   _preset_combo.get_active_text()) << endmsg;
560                 }
561         } else {
562                 // blank selected = no preset
563                 plugin->clear_preset();
564         }
565 }
566
567 #ifdef NO_PLUGIN_STATE
568 static bool seen_saving_message = false;
569 #endif
570
571 void
572 PlugUIBase::add_plugin_setting ()
573 {
574 #ifndef NO_PLUGIN_STATE
575         NewPluginPresetDialog d (plugin);
576
577         switch (d.run ()) {
578         case Gtk::RESPONSE_ACCEPT:
579                 if (d.name().empty()) {
580                         break;
581                 }
582
583                 if (d.replace ()) {
584                         plugin->remove_preset (d.name ());
585                 }
586
587                 Plugin::PresetRecord const r = plugin->save_preset (d.name());
588                 if (!r.uri.empty ()) {
589                         plugin->load_preset (r);
590                 }
591                 break;
592         }
593 #else 
594         if (!seen_saving_message) {
595                 info << string_compose (_("Plugin presets are not supported in this build of %1. Consider paying for a full version"),
596                                         PROGRAM_NAME)
597                      << endmsg;
598                 seen_saving_message = true;
599         }
600 #endif
601 }
602
603 void
604 PlugUIBase::save_plugin_setting ()
605 {
606 #ifndef NO_PLUGIN_STATE
607         string const name = _preset_combo.get_active_text ();
608         plugin->remove_preset (name);
609         Plugin::PresetRecord const r = plugin->save_preset (name);
610         if (!r.uri.empty ()) {
611                 plugin->load_preset (r);
612         }
613 #else 
614         if (!seen_saving_message) {
615                 info << string_compose (_("Plugin presets are not supported in this build of %1. Consider paying for a newer version"),
616                                         PROGRAM_NAME)
617                      << endmsg;
618                 seen_saving_message = true;
619         }
620 #endif
621 }
622
623 void
624 PlugUIBase::delete_plugin_setting ()
625 {
626 #ifndef NO_PLUGIN_STATE
627         plugin->remove_preset (_preset_combo.get_active_text ());
628 #else
629         if (!seen_saving_message) {
630                 info << string_compose (_("Plugin presets are not supported in this build of %1. Consider paying for a newer version"),
631                                         PROGRAM_NAME)
632                      << endmsg;
633                 seen_saving_message = true;
634         }
635 #endif
636 }
637
638 bool
639 PlugUIBase::bypass_button_release (GdkEventButton*)
640 {
641         bool view_says_bypassed = (bypass_button.active_state() != 0);
642         
643         if (view_says_bypassed != insert->active()) {
644                 if (view_says_bypassed) {
645                         insert->activate ();
646                 } else {
647                         insert->deactivate ();
648                 }
649         }
650
651         return false;
652 }
653
654 bool
655 PlugUIBase::focus_toggled (GdkEventButton*)
656 {
657         if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
658                 Keyboard::the_keyboard().magic_widget_drop_focus();
659                 focus_button.remove ();
660                 focus_button.add (*focus_out_image);
661                 focus_out_image->show ();
662                 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));
663                 KeyboardFocused (false);
664         } else {
665                 Keyboard::the_keyboard().magic_widget_grab_focus();
666                 focus_button.remove ();
667                 focus_button.add (*focus_in_image);
668                 focus_in_image->show ();
669                 ARDOUR_UI::instance()->set_tip (focus_button, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME));
670                 KeyboardFocused (true);
671         }
672
673         return true;
674 }
675
676 void
677 PlugUIBase::toggle_description()
678 {
679         if (description_expander.get_expanded() &&
680             !description_expander.get_child()) {
681                 const std::string text = plugin->get_docs();
682                 if (text.empty()) {
683                         return;
684                 }
685
686                 Gtk::Label* label = manage(new Gtk::Label(text));
687                 label->set_line_wrap(true);
688                 label->set_line_wrap_mode(Pango::WRAP_WORD);
689                 description_expander.add(*label);
690                 description_expander.show_all();
691         }
692         
693         if (!description_expander.get_expanded()) {
694                 description_expander.remove();
695         }
696 }
697
698
699 void
700 PlugUIBase::toggle_plugin_analysis()
701 {
702         if (plugin_analysis_expander.get_expanded() &&
703             !plugin_analysis_expander.get_child()) {
704                 // Create the GUI
705                 if (eqgui == 0) {
706                         eqgui = new PluginEqGui (insert);
707                 }
708
709                 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
710
711                 if (toplevel) {
712                         toplevel->get_size (pre_eq_size.width, pre_eq_size.height);
713                 }
714
715                 plugin_analysis_expander.add (*eqgui);
716                 plugin_analysis_expander.show_all ();
717                 eqgui->start_listening ();
718         }
719
720         if (!plugin_analysis_expander.get_expanded()) {
721                 // Hide & remove from expander
722
723                 eqgui->hide ();
724                 eqgui->stop_listening ();
725                 plugin_analysis_expander.remove();
726
727                 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
728
729                 if (toplevel) {
730                         toplevel->resize (pre_eq_size.width, pre_eq_size.height);
731                 }
732         }
733 }
734
735 void
736 PlugUIBase::update_preset_list ()
737 {
738         vector<string> preset_labels;
739         vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets();
740
741         ++_no_load_preset;
742
743         for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator i = presets.begin(); i != presets.end(); ++i) {
744                 preset_labels.push_back (i->label);
745         }
746
747         preset_labels.push_back("");
748
749         set_popdown_strings (_preset_combo, preset_labels);
750
751         --_no_load_preset;
752 }
753
754 void
755 PlugUIBase::update_preset ()
756 {
757         Plugin::PresetRecord p = plugin->last_preset();
758
759         ++_no_load_preset;
760         _preset_combo.set_active_text (p.label);
761         --_no_load_preset;
762
763         save_button.set_sensitive (!p.uri.empty() && p.user);
764         delete_button.set_sensitive (!p.uri.empty() && p.user);
765
766         update_preset_modified ();
767 }
768
769 void
770 PlugUIBase::update_preset_modified ()
771 {
772
773         if (plugin->last_preset().uri.empty()) {
774                 _preset_modified.set_text ("");
775                 return;
776         }
777
778         bool const c = plugin->parameter_changed_since_last_preset ();
779         if (_preset_modified.get_text().empty() == c) {
780                 _preset_modified.set_text (c ? "*" : "");
781         }
782 }
783
784 void
785 PlugUIBase::parameter_changed (uint32_t, float)
786 {
787         update_preset_modified ();
788 }
789
790 void
791 PlugUIBase::preset_added_or_removed ()
792 {
793         /* Update both the list and the currently-displayed preset */
794         update_preset_list ();
795         update_preset ();
796 }
797