Allow Icon + Text on ArdourButton
[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_default_size (w, h);
177         set_resizable (_pluginui->resizable());
178 }
179
180 PluginUIWindow::~PluginUIWindow ()
181 {
182 #ifndef NDEBUG
183         cerr << "PluginWindow deleted for " << this << endl;
184 #endif
185         delete _pluginui;
186 }
187
188 void
189 PluginUIWindow::on_show ()
190 {
191         set_role("plugin_ui");
192
193         if (_pluginui) {
194                 _pluginui->update_preset_list ();
195                 _pluginui->update_preset ();
196         }
197
198         if (_pluginui) {
199 #if defined (HAVE_AUDIOUNITS) && defined(__APPLE__)
200                 if (pre_deactivate_x >= 0) {
201                         move (pre_deactivate_x, pre_deactivate_y);
202                 }
203 #endif
204
205                 if (_pluginui->on_window_show (_title)) {
206                         Window::on_show ();
207                 }
208         }
209 }
210
211 void
212 PluginUIWindow::on_hide ()
213 {
214 #if defined (HAVE_AUDIOUNITS) && defined(__APPLE__)
215         get_position (pre_deactivate_x, pre_deactivate_y);
216 #endif
217
218         Window::on_hide ();
219
220         if (_pluginui) {
221                 _pluginui->on_window_hide ();
222         }
223 }
224
225 void
226 PluginUIWindow::set_title(const std::string& title)
227 {
228         Gtk::Window::set_title(title);
229         _title = title;
230 }
231
232 bool
233 #ifdef WINDOWS_VST_SUPPORT
234 PluginUIWindow::create_windows_vst_editor(boost::shared_ptr<PluginInsert> insert)
235 #else
236 PluginUIWindow::create_windows_vst_editor(boost::shared_ptr<PluginInsert>)
237 #endif
238 {
239 #ifndef WINDOWS_VST_SUPPORT
240         return false;
241 #else
242
243         boost::shared_ptr<WindowsVSTPlugin> vp;
244
245         if ((vp = boost::dynamic_pointer_cast<WindowsVSTPlugin> (insert->plugin())) == 0) {
246                 error << string_compose (_("unknown type of editor-supplying plugin (note: no VST support in this version of %1)"), PROGRAM_NAME)
247                       << endmsg;
248                 throw failed_constructor ();
249         } else {
250                 WindowsVSTPluginUI* vpu = new WindowsVSTPluginUI (insert, vp, GTK_WIDGET(this->gobj()));
251
252                 _pluginui = vpu;
253                 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
254                 add (*vpu);
255                 vpu->package (*this);
256         }
257
258         return true;
259 #endif
260 }
261
262 bool
263 #ifdef LXVST_SUPPORT
264 PluginUIWindow::create_lxvst_editor(boost::shared_ptr<PluginInsert> insert)
265 #else
266 PluginUIWindow::create_lxvst_editor(boost::shared_ptr<PluginInsert>)
267 #endif
268 {
269 #ifndef LXVST_SUPPORT
270         return false;
271 #else
272
273         boost::shared_ptr<LXVSTPlugin> lxvp;
274
275         if ((lxvp = boost::dynamic_pointer_cast<LXVSTPlugin> (insert->plugin())) == 0) {
276                 error << string_compose (_("unknown type of editor-supplying plugin (note: no linuxVST support in this version of %1)"), PROGRAM_NAME)
277                       << endmsg;
278                 throw failed_constructor ();
279         } else {
280                 LXVSTPluginUI* lxvpu = new LXVSTPluginUI (insert, lxvp);
281
282                 _pluginui = lxvpu;
283                 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
284                 add (*lxvpu);
285                 lxvpu->package (*this);
286         }
287
288         return true;
289 #endif
290 }
291
292 bool
293 #ifdef MACVST_SUPPORT
294 PluginUIWindow::create_mac_vst_editor (boost::shared_ptr<PluginInsert> insert)
295 #else
296 PluginUIWindow::create_mac_vst_editor (boost::shared_ptr<PluginInsert>)
297 #endif
298 {
299 #ifndef MACVST_SUPPORT
300         return false;
301 #else
302         boost::shared_ptr<MacVSTPlugin> mvst;
303         if ((mvst = boost::dynamic_pointer_cast<MacVSTPlugin> (insert->plugin())) == 0) {
304                 error << string_compose (_("unknown type of editor-supplying plugin (note: no MacVST support in this version of %1)"), PROGRAM_NAME)
305                       << endmsg;
306                 throw failed_constructor ();
307         }
308         VSTPluginUI* vpu = create_mac_vst_gui (insert);
309         _pluginui = vpu;
310         _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
311         add (*vpu);
312         vpu->package (*this);
313
314         Application::instance()->ActivationChanged.connect (mem_fun (*this, &PluginUIWindow::app_activated));
315
316         return true;
317 #endif
318 }
319
320
321 bool
322 #ifdef AUDIOUNIT_SUPPORT
323 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert> insert)
324 #else
325 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert>)
326 #endif
327 {
328 #ifndef AUDIOUNIT_SUPPORT
329         return false;
330 #else
331         VBox* box;
332         _pluginui = create_au_gui (insert, &box);
333         _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
334         add (*box);
335
336         Application::instance()->ActivationChanged.connect (mem_fun (*this, &PluginUIWindow::app_activated));
337
338         return true;
339 #endif
340 }
341
342 void
343 #ifdef __APPLE__
344 PluginUIWindow::app_activated (bool yn)
345 #else
346 PluginUIWindow::app_activated (bool)
347 #endif
348 {
349 #ifdef AUDIOUNIT_SUPPORT
350         if (_pluginui) {
351                 if (yn) {
352                         if (was_visible) {
353                                 _pluginui->activate ();
354                                 if (pre_deactivate_x >= 0) {
355                                         move (pre_deactivate_x, pre_deactivate_y);
356                                 }
357                                 present ();
358                                 was_visible = true;
359                         }
360                 } else {
361                         was_visible = is_visible();
362                         get_position (pre_deactivate_x, pre_deactivate_y);
363                         hide ();
364                         _pluginui->deactivate ();
365                 }
366         }
367 #endif
368 }
369
370 bool
371 PluginUIWindow::create_lv2_editor(boost::shared_ptr<PluginInsert> insert)
372 {
373 #ifdef HAVE_SUIL
374         boost::shared_ptr<LV2Plugin> vp;
375
376         if ((vp = boost::dynamic_pointer_cast<LV2Plugin> (insert->plugin())) == 0) {
377                 error << _("create_lv2_editor called on non-LV2 plugin") << endmsg;
378                 throw failed_constructor ();
379         } else {
380                 LV2PluginUI* lpu = new LV2PluginUI (insert, vp);
381                 _pluginui = lpu;
382                 add (*lpu);
383                 lpu->package (*this);
384                 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
385         }
386
387         return true;
388 #else
389         return false;
390 #endif
391 }
392
393 void
394 PluginUIWindow::keyboard_focused (bool yn)
395 {
396         _keyboard_focused = yn;
397 }
398
399 bool
400 PluginUIWindow::on_key_press_event (GdkEventKey* event)
401 {
402         if (_keyboard_focused) {
403                 if (_pluginui) {
404                         _pluginui->grab_focus();
405                         if (_pluginui->non_gtk_gui()) {
406                                 _pluginui->forward_key_event (event);
407                         } else {
408                                         return relay_key_press (event, this);
409                         }
410                 }
411                 return true;
412         }
413         /* for us to be getting key press events, there really
414            MUST be a _pluginui, but just to be safe, check ...
415         */
416
417         if (_pluginui) {
418                 _pluginui->grab_focus();
419                 if (_pluginui->non_gtk_gui()) {
420                         /* pass main window as the window for the event
421                            to be handled in, not this one, because there are
422                            no widgets in this window that we want to have
423                            key focus.
424                         */
425                         return relay_key_press (event, &ARDOUR_UI::instance()->main_window());
426                 } else {
427                         return relay_key_press (event, this);
428                 }
429         }
430
431         return false;
432 }
433
434 bool
435 PluginUIWindow::on_key_release_event (GdkEventKey *event)
436 {
437         if (_keyboard_focused) {
438                 if (_pluginui) {
439                         if (_pluginui->non_gtk_gui()) {
440                                 _pluginui->forward_key_event (event);
441                         }
442                 }
443         } else {
444                 gtk_window_propagate_key_event (GTK_WINDOW(gobj()), event);
445         }
446         /* don't forward releases */
447         return true;
448 }
449
450 void
451 PluginUIWindow::plugin_going_away ()
452 {
453         ENSURE_GUI_THREAD (*this, &PluginUIWindow::plugin_going_away)
454
455         if (_pluginui) {
456                 _pluginui->stop_updating(0);
457         }
458
459         death_connection.disconnect ();
460 }
461
462 PlugUIBase::PlugUIBase (boost::shared_ptr<PluginInsert> pi)
463         : insert (pi)
464         , plugin (insert->plugin())
465         , add_button (_("Add"))
466         , save_button (_("Save"))
467         , delete_button (_("Delete"))
468         , reset_button (_("Reset"))
469         , bypass_button (ArdourButton::led_default_elements)
470         , pin_management_button (_("Pinout"))
471         , description_expander (_("Description"))
472         , plugin_analysis_expander (_("Plugin analysis"))
473         , cpuload_expander (_("CPU Profile"))
474         , latency_gui (0)
475         , latency_dialog (0)
476         , eqgui (0)
477         , stats_gui (0)
478         , preset_gui (0)
479 {
480         _preset_modified.set_size_request (16, -1);
481         _preset_combo.set_text("(default)");
482         set_tooltip (_preset_combo, _("Presets (if any) for this plugin\n(Both factory and user-created)"));
483         set_tooltip (add_button, _("Save a new preset"));
484         set_tooltip (save_button, _("Save the current preset"));
485         set_tooltip (delete_button, _("Delete the current preset"));
486         set_tooltip (reset_button, _("Reset parameters to default (if no parameters are in automation play mode)"));
487         set_tooltip (pin_management_button, _("Show Plugin Pin Management Dialog"));
488         set_tooltip (bypass_button, _("Disable signal processing by the plugin"));
489         _no_load_preset = 0;
490
491         update_preset_list ();
492         update_preset ();
493
494         add_button.set_name ("generic button");
495         add_button.set_icon (ArdourIcon::PsetAdd);
496         add_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::add_plugin_setting));
497
498         save_button.set_name ("generic button");
499         save_button.set_icon (ArdourIcon::PsetSave);
500         save_button.signal_clicked.connect(sigc::mem_fun(*this, &PlugUIBase::save_plugin_setting));
501
502         delete_button.set_name ("generic button");
503         delete_button.set_icon (ArdourIcon::PsetDelete);
504         delete_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::delete_plugin_setting));
505
506         reset_button.set_name ("generic button");
507         reset_button.set_icon (ArdourIcon::PluginReset);
508         reset_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::reset_plugin_parameters));
509
510         pin_management_button.set_name ("generic button");
511         pin_management_button.set_icon (ArdourIcon::PluginPinout);
512         pin_management_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::manage_pins));
513
514         insert->ActiveChanged.connect (active_connection, invalidator (*this), boost::bind (&PlugUIBase::processor_active_changed, this,  boost::weak_ptr<Processor>(insert)), gui_context());
515
516         bypass_button.set_name ("plugin bypass button");
517         bypass_button.set_text (_("Bypass"));
518         bypass_button.set_icon (ArdourIcon::PluginBypass);
519         bypass_button.set_active (!pi->enabled ());
520         bypass_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::bypass_button_release), false);
521         focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
522
523         focus_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::focus_toggled));
524         focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
525
526         /* these images are not managed, so that we can remove them at will */
527
528         focus_out_image = new Image (get_icon (X_("computer_keyboard")));
529         focus_in_image = new Image (get_icon (X_("computer_keyboard_active")));
530
531         focus_button.add (*focus_out_image);
532
533         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));
534         set_tooltip (bypass_button, _("Click to enable/disable this plugin"));
535
536         description_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_description));
537         description_expander.set_expanded(false);
538
539         plugin_analysis_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_plugin_analysis));
540         plugin_analysis_expander.set_expanded(false);
541
542         cpuload_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_cpuload_display));
543         cpuload_expander.set_expanded(false);
544
545         insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PlugUIBase::plugin_going_away, this), gui_context());
546
547         plugin->PresetAdded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
548         plugin->PresetRemoved.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
549         plugin->PresetLoaded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset, this), gui_context ());
550         plugin->PresetDirty.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset_modified, this), gui_context ());
551
552         insert->AutomationStateChanged.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::automation_state_changed, this), gui_context());
553
554         insert->LatencyChanged.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::set_latency_label, this), gui_context());
555
556         automation_state_changed();
557 }
558
559 PlugUIBase::~PlugUIBase()
560 {
561         delete eqgui;
562         delete stats_gui;
563         delete preset_gui;
564         delete latency_gui;
565         delete latency_dialog;
566 }
567
568 void
569 PlugUIBase::plugin_going_away ()
570 {
571         /* drop references to the plugin/insert */
572         insert.reset ();
573         plugin.reset ();
574 }
575
576 void
577 PlugUIBase::set_latency_label ()
578 {
579         samplecnt_t const l = insert->effective_latency ();
580         samplecnt_t const sr = insert->session().sample_rate ();
581
582         string t;
583
584         if (l < sr / 1000) {
585                 t = string_compose (P_("latency (%1 sample)", "latency (%1 samples)", l), l);
586         } else {
587                 t = string_compose (_("latency (%1 ms)"), (float) l / ((float) sr / 1000.0f));
588         }
589
590         latency_button.set_text (t);
591 }
592
593 void
594 PlugUIBase::latency_button_clicked ()
595 {
596         if (!latency_gui) {
597                 latency_gui = new LatencyGUI (*(insert.get()), insert->session().sample_rate(), insert->session().get_block_size());
598                 latency_dialog = new ArdourWindow (_("Edit Latency"));
599                 /* use both keep-above and transient for to try cover as many
600                    different WM's as possible.
601                 */
602                 latency_dialog->set_keep_above (true);
603                 Window* win = dynamic_cast<Window*> (bypass_button.get_toplevel ());
604                 if (win) {
605                         latency_dialog->set_transient_for (*win);
606                 }
607                 latency_dialog->add (*latency_gui);
608         }
609
610         latency_gui->refresh ();
611         latency_dialog->show_all ();
612 }
613
614 void
615 PlugUIBase::processor_active_changed (boost::weak_ptr<Processor> weak_p)
616 {
617         ENSURE_GUI_THREAD (*this, &PlugUIBase::processor_active_changed, weak_p);
618         boost::shared_ptr<Processor> p (weak_p.lock());
619
620         if (p) {
621                 bypass_button.set_active (!p->enabled ());
622         }
623 }
624
625 void
626 PlugUIBase::preset_selected (Plugin::PresetRecord preset)
627 {
628         if (_no_load_preset) {
629                 return;
630         }
631         if (!preset.label.empty()) {
632                 insert->load_preset (preset);
633         } else {
634                 // blank selected = no preset
635                 plugin->clear_preset();
636         }
637 }
638
639 #ifdef NO_PLUGIN_STATE
640 static bool seen_saving_message = false;
641
642 static void show_no_plugin_message()
643 {
644         info << string_compose (_("Plugin presets are not supported in this build of %1. Consider paying for a full version"),
645                         PROGRAM_NAME)
646              << endmsg;
647         info << _("To get full access to updates without this limitation\n"
648                   "consider becoming a subscriber for a low cost every month.")
649              << endmsg;
650         info << X_("https://community.ardour.org/s/subscribe")
651              << endmsg;
652         ARDOUR_UI::instance()->popup_error(_("Plugin presets are not supported in this build, see the Log window for more information."));
653 }
654 #endif
655
656 void
657 PlugUIBase::add_plugin_setting ()
658 {
659 #ifndef NO_PLUGIN_STATE
660         NewPluginPresetDialog d (plugin, _("New Preset"));
661
662         switch (d.run ()) {
663         case Gtk::RESPONSE_ACCEPT:
664                 if (d.name().empty()) {
665                         break;
666                 }
667
668                 if (d.replace ()) {
669                         plugin->remove_preset (d.name ());
670                 }
671
672                 Plugin::PresetRecord const r = plugin->save_preset (d.name());
673                 if (!r.uri.empty ()) {
674                         plugin->load_preset (r);
675                 }
676                 break;
677         }
678 #else
679         if (!seen_saving_message) {
680                 seen_saving_message = true;
681                 show_no_plugin_message();
682         }
683 #endif
684 }
685
686 void
687 PlugUIBase::save_plugin_setting ()
688 {
689 #ifndef NO_PLUGIN_STATE
690         string const name = _preset_combo.get_text ();
691         plugin->remove_preset (name);
692         Plugin::PresetRecord const r = plugin->save_preset (name);
693         if (!r.uri.empty ()) {
694                 plugin->load_preset (r);
695         }
696 #else
697         if (!seen_saving_message) {
698                 seen_saving_message = true;
699                 show_no_plugin_message();
700         }
701 #endif
702 }
703
704 void
705 PlugUIBase::delete_plugin_setting ()
706 {
707 #ifndef NO_PLUGIN_STATE
708         plugin->remove_preset (_preset_combo.get_text ());
709 #else
710         if (!seen_saving_message) {
711                 seen_saving_message = true;
712                 show_no_plugin_message();
713         }
714 #endif
715 }
716
717 void
718 PlugUIBase::automation_state_changed ()
719 {
720         reset_button.set_sensitive (insert->can_reset_all_parameters());
721 }
722
723 void
724 PlugUIBase::reset_plugin_parameters ()
725 {
726         insert->reset_parameters_to_default ();
727 }
728
729 void
730 PlugUIBase::manage_pins ()
731 {
732         PluginPinWindowProxy* proxy = insert->pinmgr_proxy ();
733         if (proxy) {
734                 proxy->get (true);
735                 proxy->present ();
736                 proxy->get ()->raise();
737         }
738 }
739
740 bool
741 PlugUIBase::bypass_button_release (GdkEventButton*)
742 {
743         bool view_says_bypassed = (bypass_button.active_state() != 0);
744
745         if (view_says_bypassed != insert->enabled ()) {
746                 insert->enable (view_says_bypassed);
747         }
748
749         return false;
750 }
751
752 bool
753 PlugUIBase::focus_toggled (GdkEventButton*)
754 {
755         if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
756                 Keyboard::the_keyboard().magic_widget_drop_focus();
757                 focus_button.remove ();
758                 focus_button.add (*focus_out_image);
759                 focus_out_image->show ();
760                 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));
761                 KeyboardFocused (false);
762         } else {
763                 Keyboard::the_keyboard().magic_widget_grab_focus();
764                 focus_button.remove ();
765                 focus_button.add (*focus_in_image);
766                 focus_in_image->show ();
767                 set_tooltip (focus_button, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME));
768                 KeyboardFocused (true);
769         }
770
771         return true;
772 }
773
774 void
775 PlugUIBase::toggle_description()
776 {
777         if (description_expander.get_expanded() &&
778             !description_expander.get_child()) {
779                 const std::string text = plugin->get_docs();
780                 if (text.empty()) {
781                         return;
782                 }
783
784                 Gtk::Label* label = manage(new Gtk::Label(text));
785                 label->set_line_wrap(true);
786                 label->set_line_wrap_mode(Pango::WRAP_WORD);
787                 description_expander.add(*label);
788                 description_expander.show_all();
789         }
790
791         if (!description_expander.get_expanded()) {
792                 const int child_height = description_expander.get_child ()->get_height ();
793
794                 description_expander.remove();
795
796                 Gtk::Window *toplevel = (Gtk::Window*) description_expander.get_ancestor (GTK_TYPE_WINDOW);
797
798                 if (toplevel) {
799                         Gtk::Requisition wr;
800                         toplevel->get_size (wr.width, wr.height);
801                         wr.height -= child_height;
802                         toplevel->resize (wr.width, wr.height);
803                 }
804         }
805 }
806
807 void
808 PlugUIBase::toggle_plugin_analysis()
809 {
810         if (plugin_analysis_expander.get_expanded() &&
811             !plugin_analysis_expander.get_child()) {
812                 // Create the GUI
813                 if (eqgui == 0) {
814                         eqgui = new PluginEqGui (insert);
815                 }
816
817                 plugin_analysis_expander.add (*eqgui);
818                 plugin_analysis_expander.show_all ();
819                 eqgui->start_listening ();
820         }
821
822         if (!plugin_analysis_expander.get_expanded()) {
823                 // Hide & remove from expander
824                 const int child_height = plugin_analysis_expander.get_child ()->get_height ();
825
826                 eqgui->hide ();
827                 eqgui->stop_listening ();
828                 plugin_analysis_expander.remove();
829
830                 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
831
832                 if (toplevel) {
833                         Gtk::Requisition wr;
834                         toplevel->get_size (wr.width, wr.height);
835                         wr.height -= child_height;
836                         toplevel->resize (wr.width, wr.height);
837                 }
838         }
839 }
840
841 void
842 PlugUIBase::toggle_cpuload_display()
843 {
844         if (cpuload_expander.get_expanded() && !cpuload_expander.get_child()) {
845                 if (stats_gui == 0) {
846                         stats_gui = new PluginLoadStatsGui (insert);
847                 }
848                 cpuload_expander.add (*stats_gui);
849                 cpuload_expander.show_all();
850                 stats_gui->start_updating ();
851         }
852
853         if (!cpuload_expander.get_expanded()) {
854                 const int child_height = cpuload_expander.get_child ()->get_height ();
855
856                 stats_gui->hide ();
857                 stats_gui->stop_updating ();
858                 cpuload_expander.remove();
859
860                 Gtk::Window *toplevel = (Gtk::Window*) cpuload_expander.get_ancestor (GTK_TYPE_WINDOW);
861
862                 if (toplevel) {
863                         Gtk::Requisition wr;
864                         toplevel->get_size (wr.width, wr.height);
865                         wr.height -= child_height;
866                         toplevel->resize (wr.width, wr.height);
867                 }
868         }
869
870 }
871
872 void
873 PlugUIBase::update_preset_list ()
874 {
875         using namespace Menu_Helpers;
876
877         vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets();
878
879         ++_no_load_preset;
880
881         // Add a menu entry for each preset
882         _preset_combo.clear_items();
883         for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator i = presets.begin(); i != presets.end(); ++i) {
884                 _preset_combo.AddMenuElem(
885                         MenuElem(i->label, sigc::bind(sigc::mem_fun(*this, &PlugUIBase::preset_selected), *i)));
886         }
887
888         // Add an empty entry for un-setting current preset (see preset_selected)
889         Plugin::PresetRecord no_preset;
890         _preset_combo.AddMenuElem(
891                 MenuElem("", sigc::bind(sigc::mem_fun(*this, &PlugUIBase::preset_selected), no_preset)));
892
893         --_no_load_preset;
894 }
895
896 void
897 PlugUIBase::update_preset ()
898 {
899         Plugin::PresetRecord p = plugin->last_preset();
900
901         ++_no_load_preset;
902         if (p.uri.empty()) {
903                 _preset_combo.set_text (_("(none)"));
904         } else {
905                 _preset_combo.set_text (p.label);
906         }
907         --_no_load_preset;
908
909         delete_button.set_sensitive (!p.uri.empty() && p.user);
910         update_preset_modified ();
911 }
912
913 void
914 PlugUIBase::update_preset_modified ()
915 {
916         Plugin::PresetRecord p = plugin->last_preset();
917
918         if (p.uri.empty()) {
919                 save_button.set_sensitive (false);
920                 _preset_modified.set_text ("");
921                 return;
922         }
923
924         bool const c = plugin->parameter_changed_since_last_preset ();
925         if (_preset_modified.get_text().empty() == c) {
926                 _preset_modified.set_text (c ? "*" : "");
927         }
928         save_button.set_sensitive (c && p.user);
929 }
930
931 void
932 PlugUIBase::preset_added_or_removed ()
933 {
934         /* Update both the list and the currently-displayed preset */
935         update_preset_list ();
936         update_preset ();
937 }
938