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