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