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