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