son't try to select non-existent notes after editing (and thus crash).
[ardour.git] / gtk2_ardour / plugin_ui.cc
1 /*
2     Copyright (C) 2000 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #ifdef WAF_BUILD
21 #include "gtk2ardour-config.h"
22 #endif
23
24 #include <climits>
25 #include <cerrno>
26 #include <cmath>
27 #include <string>
28
29 #include "pbd/stl_delete.h"
30 #include "pbd/xml++.h"
31 #include "pbd/failed_constructor.h"
32
33 #include <gtkmm/widget.h>
34 #include <gtkmm/box.h>
35 #include <gtkmm2ext/click_box.h>
36 #include <gtkmm2ext/fastmeter.h>
37 #include <gtkmm2ext/barcontroller.h>
38 #include <gtkmm2ext/utils.h>
39 #include <gtkmm2ext/doi.h>
40 #include <gtkmm2ext/slider_controller.h>
41 #include <gtkmm2ext/application.h>
42
43 #include "ardour/session.h"
44 #include "ardour/plugin.h"
45 #include "ardour/plugin_insert.h"
46 #include "ardour/ladspa_plugin.h"
47 #ifdef WINDOWS_VST_SUPPORT
48 #include "ardour/windows_vst_plugin.h"
49 #include "windows_vst_plugin_ui.h"
50 #endif
51 #ifdef LXVST_SUPPORT
52 #include "ardour/lxvst_plugin.h"
53 #include "lxvst_plugin_ui.h"
54 #endif
55 #ifdef MACVST_SUPPORT
56 #include "ardour/mac_vst_plugin.h"
57 #include "vst_plugin_ui.h"
58 #endif
59 #ifdef LV2_SUPPORT
60 #include "ardour/lv2_plugin.h"
61 #include "lv2_plugin_ui.h"
62 #endif
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 "processor_box.h"
72 #include "keyboard.h"
73 #include "latency_gui.h"
74 #include "plugin_eq_gui.h"
75 #include "new_plugin_preset_dialog.h"
76 #include "tooltips.h"
77
78 #include "pbd/i18n.h"
79
80 using namespace std;
81 using namespace ARDOUR;
82 using namespace ARDOUR_UI_UTILS;
83 using namespace PBD;
84 using namespace Gtkmm2ext;
85 using namespace Gtk;
86
87 PluginUIWindow::PluginUIWindow (
88         boost::shared_ptr<PluginInsert> insert,
89         bool                            scrollable,
90         bool                            editor)
91         : ArdourWindow (string())
92         , was_visible (false)
93         , _keyboard_focused (false)
94 #ifdef AUDIOUNIT_SUPPORT
95         , pre_deactivate_x (-1)
96         , pre_deactivate_y (-1)
97 #endif
98
99 {
100         bool have_gui = false;
101         Label* label = manage (new Label());
102         label->set_markup ("<b>THIS IS THE PLUGIN UI</b>");
103
104         if (editor && insert->plugin()->has_editor()) {
105                 switch (insert->type()) {
106                 case ARDOUR::Windows_VST:
107                         have_gui = create_windows_vst_editor (insert);
108                         break;
109
110                 case ARDOUR::LXVST:
111                         have_gui = create_lxvst_editor (insert);
112                         break;
113
114                 case ARDOUR::MacVST:
115                         have_gui = create_mac_vst_editor (insert);
116                         break;
117
118                 case ARDOUR::AudioUnit:
119                         have_gui = create_audiounit_editor (insert);
120                         break;
121
122                 case ARDOUR::LADSPA:
123                         error << _("Eh? LADSPA plugins don't have editors!") << endmsg;
124                         break;
125
126                 case ARDOUR::LV2:
127                         have_gui = create_lv2_editor (insert);
128                         break;
129
130                 default:
131 #ifndef WINDOWS_VST_SUPPORT
132                         error << string_compose (_("unknown type of editor-supplying plugin (note: no VST support in this version of %1)"), PROGRAM_NAME)
133                               << endmsg;
134 #else
135                         error << _("unknown type of editor-supplying plugin")
136                               << endmsg;
137 #endif
138                         throw failed_constructor ();
139                 }
140
141         }
142
143         if (!have_gui) {
144                 GenericPluginUI* pu = new GenericPluginUI (insert, scrollable);
145
146                 _pluginui = pu;
147                 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
148                 add (*pu);
149                 set_wmclass (X_("ardour_plugin_editor"), PROGRAM_NAME);
150
151                 signal_map_event().connect (sigc::mem_fun (*pu, &GenericPluginUI::start_updating));
152                 signal_unmap_event().connect (sigc::mem_fun (*pu, &GenericPluginUI::stop_updating));
153         }
154
155         set_name ("PluginEditor");
156         add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
157
158         insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PluginUIWindow::plugin_going_away, this), gui_context());
159
160         gint h = _pluginui->get_preferred_height ();
161         gint w = _pluginui->get_preferred_width ();
162
163         if (scrollable) {
164                 if (h > 600) h = 600;
165         }
166
167         set_default_size (w, h);
168         set_resizable (_pluginui->resizable());
169 }
170
171 PluginUIWindow::~PluginUIWindow ()
172 {
173 #ifndef NDEBUG
174         cerr << "PluginWindow deleted for " << this << endl;
175 #endif
176         delete _pluginui;
177 }
178
179 void
180 PluginUIWindow::on_show ()
181 {
182         set_role("plugin_ui");
183
184         if (_pluginui) {
185                 _pluginui->update_preset_list ();
186                 _pluginui->update_preset ();
187         }
188
189         if (_pluginui) {
190 #if defined (HAVE_AUDIOUNITS) && defined(__APPLE__)
191                 if (pre_deactivate_x >= 0) {
192                         move (pre_deactivate_x, pre_deactivate_y);
193                 }
194 #endif
195
196                 if (_pluginui->on_window_show (_title)) {
197                         Window::on_show ();
198                 }
199         }
200 }
201
202 void
203 PluginUIWindow::on_hide ()
204 {
205 #if defined (HAVE_AUDIOUNITS) && defined(__APPLE__)
206         get_position (pre_deactivate_x, pre_deactivate_y);
207 #endif
208
209         Window::on_hide ();
210
211         if (_pluginui) {
212                 _pluginui->on_window_hide ();
213         }
214 }
215
216 void
217 PluginUIWindow::set_title(const std::string& title)
218 {
219         Gtk::Window::set_title(title);
220         _title = title;
221 }
222
223 bool
224 #ifdef WINDOWS_VST_SUPPORT
225 PluginUIWindow::create_windows_vst_editor(boost::shared_ptr<PluginInsert> insert)
226 #else
227 PluginUIWindow::create_windows_vst_editor(boost::shared_ptr<PluginInsert>)
228 #endif
229 {
230 #ifndef WINDOWS_VST_SUPPORT
231         return false;
232 #else
233
234         boost::shared_ptr<WindowsVSTPlugin> vp;
235
236         if ((vp = boost::dynamic_pointer_cast<WindowsVSTPlugin> (insert->plugin())) == 0) {
237                 error << string_compose (_("unknown type of editor-supplying plugin (note: no VST support in this version of %1)"), PROGRAM_NAME)
238                       << endmsg;
239                 throw failed_constructor ();
240         } else {
241                 WindowsVSTPluginUI* vpu = new WindowsVSTPluginUI (insert, vp, GTK_WIDGET(this->gobj()));
242
243                 _pluginui = vpu;
244                 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
245                 add (*vpu);
246                 vpu->package (*this);
247         }
248
249         return true;
250 #endif
251 }
252
253 bool
254 #ifdef LXVST_SUPPORT
255 PluginUIWindow::create_lxvst_editor(boost::shared_ptr<PluginInsert> insert)
256 #else
257 PluginUIWindow::create_lxvst_editor(boost::shared_ptr<PluginInsert>)
258 #endif
259 {
260 #ifndef LXVST_SUPPORT
261         return false;
262 #else
263
264         boost::shared_ptr<LXVSTPlugin> lxvp;
265
266         if ((lxvp = boost::dynamic_pointer_cast<LXVSTPlugin> (insert->plugin())) == 0) {
267                 error << string_compose (_("unknown type of editor-supplying plugin (note: no linuxVST support in this version of %1)"), PROGRAM_NAME)
268                       << endmsg;
269                 throw failed_constructor ();
270         } else {
271                 LXVSTPluginUI* lxvpu = new LXVSTPluginUI (insert, lxvp);
272
273                 _pluginui = lxvpu;
274                 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
275                 add (*lxvpu);
276                 lxvpu->package (*this);
277         }
278
279         return true;
280 #endif
281 }
282
283 bool
284 #ifdef MACVST_SUPPORT
285 PluginUIWindow::create_mac_vst_editor (boost::shared_ptr<PluginInsert> insert)
286 #else
287 PluginUIWindow::create_mac_vst_editor (boost::shared_ptr<PluginInsert>)
288 #endif
289 {
290 #ifndef MACVST_SUPPORT
291         return false;
292 #else
293         boost::shared_ptr<MacVSTPlugin> mvst;
294         if ((mvst = boost::dynamic_pointer_cast<MacVSTPlugin> (insert->plugin())) == 0) {
295                 error << string_compose (_("unknown type of editor-supplying plugin (note: no MacVST support in this version of %1)"), PROGRAM_NAME)
296                       << endmsg;
297                 throw failed_constructor ();
298         }
299         VSTPluginUI* vpu = create_mac_vst_gui (insert);
300         _pluginui = vpu;
301         _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
302         add (*vpu);
303         vpu->package (*this);
304
305         Application::instance()->ActivationChanged.connect (mem_fun (*this, &PluginUIWindow::app_activated));
306
307         return true;
308 #endif
309 }
310
311
312 bool
313 #ifdef AUDIOUNIT_SUPPORT
314 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert> insert)
315 #else
316 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert>)
317 #endif
318 {
319 #ifndef AUDIOUNIT_SUPPORT
320         return false;
321 #else
322         VBox* box;
323         _pluginui = create_au_gui (insert, &box);
324         _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
325         add (*box);
326
327         Application::instance()->ActivationChanged.connect (mem_fun (*this, &PluginUIWindow::app_activated));
328
329         return true;
330 #endif
331 }
332
333 void
334 #ifdef __APPLE__
335 PluginUIWindow::app_activated (bool yn)
336 #else
337 PluginUIWindow::app_activated (bool)
338 #endif
339 {
340 #ifdef AUDIOUNIT_SUPPORT
341         if (_pluginui) {
342                 if (yn) {
343                         if (was_visible) {
344                                 _pluginui->activate ();
345                                 if (pre_deactivate_x >= 0) {
346                                         move (pre_deactivate_x, pre_deactivate_y);
347                                 }
348                                 present ();
349                                 was_visible = true;
350                         }
351                 } else {
352                         was_visible = is_visible();
353                         get_position (pre_deactivate_x, pre_deactivate_y);
354                         hide ();
355                         _pluginui->deactivate ();
356                 }
357         }
358 #endif
359 }
360
361 bool
362 PluginUIWindow::create_lv2_editor(boost::shared_ptr<PluginInsert> insert)
363 {
364 #ifdef HAVE_SUIL
365         boost::shared_ptr<LV2Plugin> vp;
366
367         if ((vp = boost::dynamic_pointer_cast<LV2Plugin> (insert->plugin())) == 0) {
368                 error << _("create_lv2_editor called on non-LV2 plugin") << endmsg;
369                 throw failed_constructor ();
370         } else {
371                 LV2PluginUI* lpu = new LV2PluginUI (insert, vp);
372                 _pluginui = lpu;
373                 add (*lpu);
374                 lpu->package (*this);
375                 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
376         }
377
378         return true;
379 #else
380         return false;
381 #endif
382 }
383
384 void
385 PluginUIWindow::keyboard_focused (bool yn)
386 {
387         _keyboard_focused = yn;
388 }
389
390 bool
391 PluginUIWindow::on_key_press_event (GdkEventKey* event)
392 {
393         if (_keyboard_focused) {
394                 if (_pluginui) {
395                         _pluginui->grab_focus();
396                         if (_pluginui->non_gtk_gui()) {
397                                 _pluginui->forward_key_event (event);
398                         } else {
399                                         return relay_key_press (event, this);
400                         }
401                 }
402                 return true;
403         }
404         /* for us to be getting key press events, there really
405            MUST be a _pluginui, but just to be safe, check ...
406         */
407
408         if (_pluginui) {
409                 _pluginui->grab_focus();
410                 if (_pluginui->non_gtk_gui()) {
411                         /* pass main window as the window for the event
412                            to be handled in, not this one, because there are
413                            no widgets in this window that we want to have
414                            key focus.
415                         */
416                         return relay_key_press (event, &ARDOUR_UI::instance()->main_window());
417                 } else {
418                         return relay_key_press (event, this);
419                 }
420         }
421
422         return false;
423 }
424
425 bool
426 PluginUIWindow::on_key_release_event (GdkEventKey *event)
427 {
428         if (_keyboard_focused) {
429                 if (_pluginui) {
430                         if (_pluginui->non_gtk_gui()) {
431                                 _pluginui->forward_key_event (event);
432                         }
433                         return true;
434                 }
435                 return false;
436         } else {
437                 return true;
438         }
439 }
440
441 void
442 PluginUIWindow::plugin_going_away ()
443 {
444         ENSURE_GUI_THREAD (*this, &PluginUIWindow::plugin_going_away)
445
446         if (_pluginui) {
447                 _pluginui->stop_updating(0);
448         }
449
450         death_connection.disconnect ();
451 }
452
453 PlugUIBase::PlugUIBase (boost::shared_ptr<PluginInsert> pi)
454         : insert (pi)
455         , plugin (insert->plugin())
456         , add_button (_("Add"))
457         , save_button (_("Save"))
458         , delete_button (_("Delete"))
459         , reset_button (_("Reset"))
460         , bypass_button (ArdourButton::led_default_elements)
461         , pin_management_button (_("Pinout"))
462         , description_expander (_("Description"))
463         , plugin_analysis_expander (_("Plugin analysis"))
464         , latency_gui (0)
465         , latency_dialog (0)
466         , eqgui (0)
467 {
468         _preset_modified.set_size_request (16, -1);
469         _preset_combo.set_text("(default)");
470         set_tooltip (_preset_combo, _("Presets (if any) for this plugin\n(Both factory and user-created)"));
471         set_tooltip (add_button, _("Save a new preset"));
472         set_tooltip (save_button, _("Save the current preset"));
473         set_tooltip (delete_button, _("Delete the current preset"));
474         set_tooltip (reset_button, _("Reset parameters to default (if no parameters are in automation play mode)"));
475         set_tooltip (pin_management_button, _("Show Plugin Pin Management Dialog"));
476         set_tooltip (bypass_button, _("Disable signal processing by the plugin"));
477         _no_load_preset = 0;
478
479         update_preset_list ();
480         update_preset ();
481
482         add_button.set_name ("generic button");
483         add_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::add_plugin_setting));
484
485         save_button.set_name ("generic button");
486         save_button.signal_clicked.connect(sigc::mem_fun(*this, &PlugUIBase::save_plugin_setting));
487
488         delete_button.set_name ("generic button");
489         delete_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::delete_plugin_setting));
490
491         reset_button.set_name ("generic button");
492         reset_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::reset_plugin_parameters));
493
494         pin_management_button.set_name ("generic button");
495         pin_management_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::manage_pins));
496
497         insert->ActiveChanged.connect (active_connection, invalidator (*this), boost::bind (&PlugUIBase::processor_active_changed, this,  boost::weak_ptr<Processor>(insert)), gui_context());
498
499         bypass_button.set_name ("plugin bypass button");
500         bypass_button.set_text (_("Bypass"));
501         bypass_button.set_active (!pi->enabled ());
502         bypass_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::bypass_button_release), false);
503         focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
504
505         focus_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::focus_toggled));
506         focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
507
508         /* these images are not managed, so that we can remove them at will */
509
510         focus_out_image = new Image (get_icon (X_("computer_keyboard")));
511         focus_in_image = new Image (get_icon (X_("computer_keyboard_active")));
512
513         focus_button.add (*focus_out_image);
514
515         set_tooltip (focus_button, string_compose (_("Click to allow the plugin to receive keyboard events that %1 would normally use as a shortcut"), PROGRAM_NAME));
516         set_tooltip (bypass_button, _("Click to enable/disable this plugin"));
517
518         description_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_description));
519         description_expander.set_expanded(false);
520
521         plugin_analysis_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_plugin_analysis));
522         plugin_analysis_expander.set_expanded(false);
523
524         insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PlugUIBase::plugin_going_away, this), gui_context());
525
526         plugin->PresetAdded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
527         plugin->PresetRemoved.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
528         plugin->PresetLoaded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset, this), gui_context ());
529         plugin->PresetDirty.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset_modified, this), gui_context ());
530
531         insert->AutomationStateChanged.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::automation_state_changed, this), gui_context());
532
533         automation_state_changed();
534 }
535
536 PlugUIBase::~PlugUIBase()
537 {
538         delete eqgui;
539         delete latency_gui;
540 }
541
542 void
543 PlugUIBase::plugin_going_away ()
544 {
545         /* drop references to the plugin/insert */
546         insert.reset ();
547         plugin.reset ();
548 }
549
550 void
551 PlugUIBase::set_latency_label ()
552 {
553         framecnt_t const l = insert->effective_latency ();
554         framecnt_t const sr = insert->session().frame_rate ();
555
556         string t;
557
558         if (l < sr / 1000) {
559                 t = string_compose (P_("latency (%1 sample)", "latency (%1 samples)", l), l);
560         } else {
561                 t = string_compose (_("latency (%1 ms)"), (float) l / ((float) sr / 1000.0f));
562         }
563
564         latency_button.set_text (t);
565 }
566
567 void
568 PlugUIBase::latency_button_clicked ()
569 {
570         if (!latency_gui) {
571                 latency_gui = new LatencyGUI (*(insert.get()), insert->session().frame_rate(), insert->session().get_block_size());
572                 latency_dialog = new ArdourWindow (_("Edit Latency"));
573                 /* use both keep-above and transient for to try cover as many
574                    different WM's as possible.
575                 */
576                 latency_dialog->set_keep_above (true);
577                 Window* win = dynamic_cast<Window*> (bypass_button.get_toplevel ());
578                 if (win) {
579                         latency_dialog->set_transient_for (*win);
580                 }
581                 latency_dialog->add (*latency_gui);
582                 latency_dialog->signal_hide().connect (sigc::mem_fun (*this, &PlugUIBase::set_latency_label));
583         }
584
585         latency_dialog->show_all ();
586 }
587
588 void
589 PlugUIBase::processor_active_changed (boost::weak_ptr<Processor> weak_p)
590 {
591         ENSURE_GUI_THREAD (*this, &PlugUIBase::processor_active_changed, weak_p);
592         boost::shared_ptr<Processor> p (weak_p.lock());
593
594         if (p) {
595                 bypass_button.set_active (!p->enabled ());
596         }
597 }
598
599 void
600 PlugUIBase::preset_selected (Plugin::PresetRecord preset)
601 {
602         if (_no_load_preset) {
603                 return;
604         }
605         if (!preset.label.empty()) {
606                 insert->load_preset (preset);
607         } else {
608                 // blank selected = no preset
609                 plugin->clear_preset();
610         }
611 }
612
613 #ifdef NO_PLUGIN_STATE
614 static bool seen_saving_message = false;
615
616 static void show_no_plugin_message()
617 {
618         info << string_compose (_("Plugin presets are not supported in this build of %1. Consider paying for a full version"),
619                         PROGRAM_NAME)
620              << endmsg;
621         info << _("To get full access to updates without this limitation\n"
622                   "consider becoming a subscriber for a low cost every month.")
623              << endmsg;
624         info << X_("https://community.ardour.org/s/subscribe")
625              << endmsg;
626         ARDOUR_UI::instance()->popup_error(_("Plugin presets are not supported in this build, see the Log window for more information."));
627 }
628 #endif
629
630 void
631 PlugUIBase::add_plugin_setting ()
632 {
633 #ifndef NO_PLUGIN_STATE
634         NewPluginPresetDialog d (plugin, _("New Preset"));
635
636         switch (d.run ()) {
637         case Gtk::RESPONSE_ACCEPT:
638                 if (d.name().empty()) {
639                         break;
640                 }
641
642                 if (d.replace ()) {
643                         plugin->remove_preset (d.name ());
644                 }
645
646                 Plugin::PresetRecord const r = plugin->save_preset (d.name());
647                 if (!r.uri.empty ()) {
648                         plugin->load_preset (r);
649                 }
650                 break;
651         }
652 #else
653         if (!seen_saving_message) {
654                 seen_saving_message = true;
655                 show_no_plugin_message();
656         }
657 #endif
658 }
659
660 void
661 PlugUIBase::save_plugin_setting ()
662 {
663 #ifndef NO_PLUGIN_STATE
664         string const name = _preset_combo.get_text ();
665         plugin->remove_preset (name);
666         Plugin::PresetRecord const r = plugin->save_preset (name);
667         if (!r.uri.empty ()) {
668                 plugin->load_preset (r);
669         }
670 #else
671         if (!seen_saving_message) {
672                 seen_saving_message = true;
673                 show_no_plugin_message();
674         }
675 #endif
676 }
677
678 void
679 PlugUIBase::delete_plugin_setting ()
680 {
681 #ifndef NO_PLUGIN_STATE
682         plugin->remove_preset (_preset_combo.get_text ());
683 #else
684         if (!seen_saving_message) {
685                 seen_saving_message = true;
686                 show_no_plugin_message();
687         }
688 #endif
689 }
690
691 void
692 PlugUIBase::automation_state_changed ()
693 {
694         reset_button.set_sensitive (insert->can_reset_all_parameters());
695 }
696
697 void
698 PlugUIBase::reset_plugin_parameters ()
699 {
700         insert->reset_parameters_to_default ();
701 }
702
703 void
704 PlugUIBase::manage_pins ()
705 {
706         PluginPinWindowProxy* proxy = insert->pinmgr_proxy ();
707         if (proxy) {
708                 proxy->get (true);
709                 proxy->present ();
710                 proxy->get ()->raise();
711         }
712 }
713
714 bool
715 PlugUIBase::bypass_button_release (GdkEventButton*)
716 {
717         bool view_says_bypassed = (bypass_button.active_state() != 0);
718
719         if (view_says_bypassed != insert->enabled ()) {
720                 insert->enable (view_says_bypassed);
721         }
722
723         return false;
724 }
725
726 bool
727 PlugUIBase::focus_toggled (GdkEventButton*)
728 {
729         if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
730                 Keyboard::the_keyboard().magic_widget_drop_focus();
731                 focus_button.remove ();
732                 focus_button.add (*focus_out_image);
733                 focus_out_image->show ();
734                 set_tooltip (focus_button, string_compose (_("Click to allow the plugin to receive keyboard events that %1 would normally use as a shortcut"), PROGRAM_NAME));
735                 KeyboardFocused (false);
736         } else {
737                 Keyboard::the_keyboard().magic_widget_grab_focus();
738                 focus_button.remove ();
739                 focus_button.add (*focus_in_image);
740                 focus_in_image->show ();
741                 set_tooltip (focus_button, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME));
742                 KeyboardFocused (true);
743         }
744
745         return true;
746 }
747
748 void
749 PlugUIBase::toggle_description()
750 {
751         if (description_expander.get_expanded() &&
752             !description_expander.get_child()) {
753                 const std::string text = plugin->get_docs();
754                 if (text.empty()) {
755                         return;
756                 }
757
758                 Gtk::Label* label = manage(new Gtk::Label(text));
759                 label->set_line_wrap(true);
760                 label->set_line_wrap_mode(Pango::WRAP_WORD);
761                 description_expander.add(*label);
762                 description_expander.show_all();
763         }
764
765         if (!description_expander.get_expanded()) {
766                 const int child_height = description_expander.get_child ()->get_height ();
767
768                 description_expander.remove();
769
770                 Gtk::Window *toplevel = (Gtk::Window*) description_expander.get_ancestor (GTK_TYPE_WINDOW);
771
772                 if (toplevel) {
773                         Gtk::Requisition wr;
774                         toplevel->get_size (wr.width, wr.height);
775                         wr.height -= child_height;
776                         toplevel->resize (wr.width, wr.height);
777                 }
778
779         }
780 }
781
782
783 void
784 PlugUIBase::toggle_plugin_analysis()
785 {
786         if (plugin_analysis_expander.get_expanded() &&
787             !plugin_analysis_expander.get_child()) {
788                 // Create the GUI
789                 if (eqgui == 0) {
790                         eqgui = new PluginEqGui (insert);
791                 }
792
793                 plugin_analysis_expander.add (*eqgui);
794                 plugin_analysis_expander.show_all ();
795                 eqgui->start_listening ();
796         }
797
798         if (!plugin_analysis_expander.get_expanded()) {
799                 // Hide & remove from expander
800                 const int child_height = plugin_analysis_expander.get_child ()->get_height ();
801
802                 eqgui->hide ();
803                 eqgui->stop_listening ();
804                 plugin_analysis_expander.remove();
805
806                 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
807
808                 if (toplevel) {
809                         Gtk::Requisition wr;
810                         toplevel->get_size (wr.width, wr.height);
811                         wr.height -= child_height;
812                         toplevel->resize (wr.width, wr.height);
813                 }
814         }
815 }
816
817 void
818 PlugUIBase::update_preset_list ()
819 {
820         using namespace Menu_Helpers;
821
822         vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets();
823
824         ++_no_load_preset;
825
826         // Add a menu entry for each preset
827         _preset_combo.clear_items();
828         for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator i = presets.begin(); i != presets.end(); ++i) {
829                 _preset_combo.AddMenuElem(
830                         MenuElem(i->label, sigc::bind(sigc::mem_fun(*this, &PlugUIBase::preset_selected), *i)));
831         }
832
833         // Add an empty entry for un-setting current preset (see preset_selected)
834         Plugin::PresetRecord no_preset;
835         _preset_combo.AddMenuElem(
836                 MenuElem("", sigc::bind(sigc::mem_fun(*this, &PlugUIBase::preset_selected), no_preset)));
837
838         --_no_load_preset;
839 }
840
841 void
842 PlugUIBase::update_preset ()
843 {
844         Plugin::PresetRecord p = plugin->last_preset();
845
846         ++_no_load_preset;
847         if (p.uri.empty()) {
848                 _preset_combo.set_text (_("(none)"));
849         } else {
850                 _preset_combo.set_text (p.label);
851         }
852         --_no_load_preset;
853
854         save_button.set_sensitive (!p.uri.empty() && p.user);
855         delete_button.set_sensitive (!p.uri.empty() && p.user);
856
857         update_preset_modified ();
858 }
859
860 void
861 PlugUIBase::update_preset_modified ()
862 {
863
864         if (plugin->last_preset().uri.empty()) {
865                 _preset_modified.set_text ("");
866                 return;
867         }
868
869         bool const c = plugin->parameter_changed_since_last_preset ();
870         if (_preset_modified.get_text().empty() == c) {
871                 _preset_modified.set_text (c ? "*" : "");
872         }
873 }
874
875 void
876 PlugUIBase::preset_added_or_removed ()
877 {
878         /* Update both the list and the currently-displayed preset */
879         update_preset_list ();
880         update_preset ();
881 }
882