Display profile-data in generic plugin-UI.
[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
36 #include "gtkmm2ext/utils.h"
37 #include "gtkmm2ext/doi.h"
38 #include "gtkmm2ext/application.h"
39
40 #include "widgets/tooltips.h"
41 #include "widgets/fastmeter.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 "plugin_ui.h"
67 #include "utils.h"
68 #include "gui_thread.h"
69 #include "public_editor.h"
70 #include "processor_box.h"
71 #include "keyboard.h"
72 #include "latency_gui.h"
73 #include "plugin_eq_gui.h"
74 #include "timers.h"
75 #include "new_plugin_preset_dialog.h"
76
77 #include "pbd/i18n.h"
78
79 using namespace std;
80 using namespace ARDOUR;
81 using namespace ARDOUR_UI_UTILS;
82 using namespace ArdourWidgets;
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                 }
434         } else {
435                 gtk_window_propagate_key_event (GTK_WINDOW(gobj()), event);
436         }
437         /* don't forward releases */
438         return true;
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         , cpuload_expander (_("CPU Profile"))
465         , latency_gui (0)
466         , latency_dialog (0)
467         , eqgui (0)
468 {
469         _preset_modified.set_size_request (16, -1);
470         _preset_combo.set_text("(default)");
471         set_tooltip (_preset_combo, _("Presets (if any) for this plugin\n(Both factory and user-created)"));
472         set_tooltip (add_button, _("Save a new preset"));
473         set_tooltip (save_button, _("Save the current preset"));
474         set_tooltip (delete_button, _("Delete the current preset"));
475         set_tooltip (reset_button, _("Reset parameters to default (if no parameters are in automation play mode)"));
476         set_tooltip (pin_management_button, _("Show Plugin Pin Management Dialog"));
477         set_tooltip (bypass_button, _("Disable signal processing by the plugin"));
478         _no_load_preset = 0;
479
480         update_preset_list ();
481         update_preset ();
482
483         add_button.set_name ("generic button");
484         add_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::add_plugin_setting));
485
486         save_button.set_name ("generic button");
487         save_button.signal_clicked.connect(sigc::mem_fun(*this, &PlugUIBase::save_plugin_setting));
488
489         delete_button.set_name ("generic button");
490         delete_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::delete_plugin_setting));
491
492         reset_button.set_name ("generic button");
493         reset_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::reset_plugin_parameters));
494
495         pin_management_button.set_name ("generic button");
496         pin_management_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::manage_pins));
497
498         insert->ActiveChanged.connect (active_connection, invalidator (*this), boost::bind (&PlugUIBase::processor_active_changed, this,  boost::weak_ptr<Processor>(insert)), gui_context());
499
500         bypass_button.set_name ("plugin bypass button");
501         bypass_button.set_text (_("Bypass"));
502         bypass_button.set_active (!pi->enabled ());
503         bypass_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::bypass_button_release), false);
504         focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
505
506         focus_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::focus_toggled));
507         focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
508
509         /* these images are not managed, so that we can remove them at will */
510
511         focus_out_image = new Image (get_icon (X_("computer_keyboard")));
512         focus_in_image = new Image (get_icon (X_("computer_keyboard_active")));
513
514         focus_button.add (*focus_out_image);
515
516         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));
517         set_tooltip (bypass_button, _("Click to enable/disable this plugin"));
518
519         description_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_description));
520         description_expander.set_expanded(false);
521
522         plugin_analysis_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_plugin_analysis));
523         plugin_analysis_expander.set_expanded(false);
524
525         cpuload_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_cpuload_display));
526         cpuload_expander.set_expanded(false);
527
528         insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PlugUIBase::plugin_going_away, this), gui_context());
529
530         plugin->PresetAdded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
531         plugin->PresetRemoved.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
532         plugin->PresetLoaded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset, this), gui_context ());
533         plugin->PresetDirty.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset_modified, this), gui_context ());
534
535         insert->AutomationStateChanged.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::automation_state_changed, this), gui_context());
536
537         automation_state_changed();
538 }
539
540 PlugUIBase::~PlugUIBase()
541 {
542         delete eqgui;
543         delete latency_gui;
544 }
545
546 void
547 PlugUIBase::plugin_going_away ()
548 {
549         /* drop references to the plugin/insert */
550         insert.reset ();
551         plugin.reset ();
552 }
553
554 void
555 PlugUIBase::set_latency_label ()
556 {
557         samplecnt_t const l = insert->effective_latency ();
558         samplecnt_t const sr = insert->session().sample_rate ();
559
560         string t;
561
562         if (l < sr / 1000) {
563                 t = string_compose (P_("latency (%1 sample)", "latency (%1 samples)", l), l);
564         } else {
565                 t = string_compose (_("latency (%1 ms)"), (float) l / ((float) sr / 1000.0f));
566         }
567
568         latency_button.set_text (t);
569 }
570
571 void
572 PlugUIBase::latency_button_clicked ()
573 {
574         if (!latency_gui) {
575                 latency_gui = new LatencyGUI (*(insert.get()), insert->session().sample_rate(), insert->session().get_block_size());
576                 latency_dialog = new ArdourWindow (_("Edit Latency"));
577                 /* use both keep-above and transient for to try cover as many
578                    different WM's as possible.
579                 */
580                 latency_dialog->set_keep_above (true);
581                 Window* win = dynamic_cast<Window*> (bypass_button.get_toplevel ());
582                 if (win) {
583                         latency_dialog->set_transient_for (*win);
584                 }
585                 latency_dialog->add (*latency_gui);
586                 latency_dialog->signal_hide().connect (sigc::mem_fun (*this, &PlugUIBase::set_latency_label));
587         }
588
589         latency_dialog->show_all ();
590 }
591
592 void
593 PlugUIBase::processor_active_changed (boost::weak_ptr<Processor> weak_p)
594 {
595         ENSURE_GUI_THREAD (*this, &PlugUIBase::processor_active_changed, weak_p);
596         boost::shared_ptr<Processor> p (weak_p.lock());
597
598         if (p) {
599                 bypass_button.set_active (!p->enabled ());
600         }
601 }
602
603 void
604 PlugUIBase::preset_selected (Plugin::PresetRecord preset)
605 {
606         if (_no_load_preset) {
607                 return;
608         }
609         if (!preset.label.empty()) {
610                 insert->load_preset (preset);
611         } else {
612                 // blank selected = no preset
613                 plugin->clear_preset();
614         }
615 }
616
617 #ifdef NO_PLUGIN_STATE
618 static bool seen_saving_message = false;
619
620 static void show_no_plugin_message()
621 {
622         info << string_compose (_("Plugin presets are not supported in this build of %1. Consider paying for a full version"),
623                         PROGRAM_NAME)
624              << endmsg;
625         info << _("To get full access to updates without this limitation\n"
626                   "consider becoming a subscriber for a low cost every month.")
627              << endmsg;
628         info << X_("https://community.ardour.org/s/subscribe")
629              << endmsg;
630         ARDOUR_UI::instance()->popup_error(_("Plugin presets are not supported in this build, see the Log window for more information."));
631 }
632 #endif
633
634 void
635 PlugUIBase::add_plugin_setting ()
636 {
637 #ifndef NO_PLUGIN_STATE
638         NewPluginPresetDialog d (plugin, _("New Preset"));
639
640         switch (d.run ()) {
641         case Gtk::RESPONSE_ACCEPT:
642                 if (d.name().empty()) {
643                         break;
644                 }
645
646                 if (d.replace ()) {
647                         plugin->remove_preset (d.name ());
648                 }
649
650                 Plugin::PresetRecord const r = plugin->save_preset (d.name());
651                 if (!r.uri.empty ()) {
652                         plugin->load_preset (r);
653                 }
654                 break;
655         }
656 #else
657         if (!seen_saving_message) {
658                 seen_saving_message = true;
659                 show_no_plugin_message();
660         }
661 #endif
662 }
663
664 void
665 PlugUIBase::save_plugin_setting ()
666 {
667 #ifndef NO_PLUGIN_STATE
668         string const name = _preset_combo.get_text ();
669         plugin->remove_preset (name);
670         Plugin::PresetRecord const r = plugin->save_preset (name);
671         if (!r.uri.empty ()) {
672                 plugin->load_preset (r);
673         }
674 #else
675         if (!seen_saving_message) {
676                 seen_saving_message = true;
677                 show_no_plugin_message();
678         }
679 #endif
680 }
681
682 void
683 PlugUIBase::delete_plugin_setting ()
684 {
685 #ifndef NO_PLUGIN_STATE
686         plugin->remove_preset (_preset_combo.get_text ());
687 #else
688         if (!seen_saving_message) {
689                 seen_saving_message = true;
690                 show_no_plugin_message();
691         }
692 #endif
693 }
694
695 void
696 PlugUIBase::automation_state_changed ()
697 {
698         reset_button.set_sensitive (insert->can_reset_all_parameters());
699 }
700
701 void
702 PlugUIBase::reset_plugin_parameters ()
703 {
704         insert->reset_parameters_to_default ();
705 }
706
707 void
708 PlugUIBase::manage_pins ()
709 {
710         PluginPinWindowProxy* proxy = insert->pinmgr_proxy ();
711         if (proxy) {
712                 proxy->get (true);
713                 proxy->present ();
714                 proxy->get ()->raise();
715         }
716 }
717
718 bool
719 PlugUIBase::bypass_button_release (GdkEventButton*)
720 {
721         bool view_says_bypassed = (bypass_button.active_state() != 0);
722
723         if (view_says_bypassed != insert->enabled ()) {
724                 insert->enable (view_says_bypassed);
725         }
726
727         return false;
728 }
729
730 bool
731 PlugUIBase::focus_toggled (GdkEventButton*)
732 {
733         if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
734                 Keyboard::the_keyboard().magic_widget_drop_focus();
735                 focus_button.remove ();
736                 focus_button.add (*focus_out_image);
737                 focus_out_image->show ();
738                 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));
739                 KeyboardFocused (false);
740         } else {
741                 Keyboard::the_keyboard().magic_widget_grab_focus();
742                 focus_button.remove ();
743                 focus_button.add (*focus_in_image);
744                 focus_in_image->show ();
745                 set_tooltip (focus_button, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME));
746                 KeyboardFocused (true);
747         }
748
749         return true;
750 }
751
752 void
753 PlugUIBase::toggle_description()
754 {
755         if (description_expander.get_expanded() &&
756             !description_expander.get_child()) {
757                 const std::string text = plugin->get_docs();
758                 if (text.empty()) {
759                         return;
760                 }
761
762                 Gtk::Label* label = manage(new Gtk::Label(text));
763                 label->set_line_wrap(true);
764                 label->set_line_wrap_mode(Pango::WRAP_WORD);
765                 description_expander.add(*label);
766                 description_expander.show_all();
767         }
768
769         if (!description_expander.get_expanded()) {
770                 const int child_height = description_expander.get_child ()->get_height ();
771
772                 description_expander.remove();
773
774                 Gtk::Window *toplevel = (Gtk::Window*) description_expander.get_ancestor (GTK_TYPE_WINDOW);
775
776                 if (toplevel) {
777                         Gtk::Requisition wr;
778                         toplevel->get_size (wr.width, wr.height);
779                         wr.height -= child_height;
780                         toplevel->resize (wr.width, wr.height);
781                 }
782
783         }
784 }
785
786
787 void
788 PlugUIBase::toggle_plugin_analysis()
789 {
790         if (plugin_analysis_expander.get_expanded() &&
791             !plugin_analysis_expander.get_child()) {
792                 // Create the GUI
793                 if (eqgui == 0) {
794                         eqgui = new PluginEqGui (insert);
795                 }
796
797                 plugin_analysis_expander.add (*eqgui);
798                 plugin_analysis_expander.show_all ();
799                 eqgui->start_listening ();
800         }
801
802         if (!plugin_analysis_expander.get_expanded()) {
803                 // Hide & remove from expander
804                 const int child_height = plugin_analysis_expander.get_child ()->get_height ();
805
806                 eqgui->hide ();
807                 eqgui->stop_listening ();
808                 plugin_analysis_expander.remove();
809
810                 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
811
812                 if (toplevel) {
813                         Gtk::Requisition wr;
814                         toplevel->get_size (wr.width, wr.height);
815                         wr.height -= child_height;
816                         toplevel->resize (wr.width, wr.height);
817                 }
818         }
819 }
820
821 void
822 PlugUIBase::update_cpu_label()
823 {
824         uint64_t min, max;
825         double avg, dev;
826         string t;
827         if (insert->get_stats (min, max, avg, dev)) {
828                 t = string_compose (_("Timing min: %1 [ms] max: %2 [ms]\navg: %3 [ms] std.dev: %4"),
829                                 rint (min / 100.) / 10.,
830                                 rint (max / 100.) / 10.,
831                                 rint (avg) / 1000.,
832                                 rint (dev) / 1000.);
833         } else {
834                 t = _("No data available");
835         }
836         cpuload_label.set_text (t);
837 }
838
839 void
840 PlugUIBase::toggle_cpuload_display()
841 {
842         if (cpuload_expander.get_expanded() && !cpuload_expander.get_child()) {
843                 update_cpu_label ();
844                 cpuload_label.set_line_wrap(true);
845                 cpuload_label.set_line_wrap_mode(Pango::WRAP_WORD);
846                 update_cpu_label_connection = Timers::second_connect (sigc::mem_fun(*this, &PlugUIBase::update_cpu_label));
847
848                 cpuload_expander.add(cpuload_label);
849                 cpuload_expander.show_all();
850         }
851
852         if (!cpuload_expander.get_expanded()) {
853                 update_cpu_label_connection.disconnect ();
854                 const int child_height = cpuload_expander.get_child ()->get_height ();
855                 cpuload_expander.remove();
856                 Gtk::Window *toplevel = (Gtk::Window*) cpuload_expander.get_ancestor (GTK_TYPE_WINDOW);
857
858                 if (toplevel) {
859                         Gtk::Requisition wr;
860                         toplevel->get_size (wr.width, wr.height);
861                         wr.height -= child_height;
862                         toplevel->resize (wr.width, wr.height);
863                 }
864         }
865
866 }
867
868 void
869 PlugUIBase::update_preset_list ()
870 {
871         using namespace Menu_Helpers;
872
873         vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets();
874
875         ++_no_load_preset;
876
877         // Add a menu entry for each preset
878         _preset_combo.clear_items();
879         for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator i = presets.begin(); i != presets.end(); ++i) {
880                 _preset_combo.AddMenuElem(
881                         MenuElem(i->label, sigc::bind(sigc::mem_fun(*this, &PlugUIBase::preset_selected), *i)));
882         }
883
884         // Add an empty entry for un-setting current preset (see preset_selected)
885         Plugin::PresetRecord no_preset;
886         _preset_combo.AddMenuElem(
887                 MenuElem("", sigc::bind(sigc::mem_fun(*this, &PlugUIBase::preset_selected), no_preset)));
888
889         --_no_load_preset;
890 }
891
892 void
893 PlugUIBase::update_preset ()
894 {
895         Plugin::PresetRecord p = plugin->last_preset();
896
897         ++_no_load_preset;
898         if (p.uri.empty()) {
899                 _preset_combo.set_text (_("(none)"));
900         } else {
901                 _preset_combo.set_text (p.label);
902         }
903         --_no_load_preset;
904
905         save_button.set_sensitive (!p.uri.empty() && p.user);
906         delete_button.set_sensitive (!p.uri.empty() && p.user);
907
908         update_preset_modified ();
909 }
910
911 void
912 PlugUIBase::update_preset_modified ()
913 {
914
915         if (plugin->last_preset().uri.empty()) {
916                 _preset_modified.set_text ("");
917                 return;
918         }
919
920         bool const c = plugin->parameter_changed_since_last_preset ();
921         if (_preset_modified.get_text().empty() == c) {
922                 _preset_modified.set_text (c ? "*" : "");
923         }
924 }
925
926 void
927 PlugUIBase::preset_added_or_removed ()
928 {
929         /* Update both the list and the currently-displayed preset */
930         update_preset_list ();
931         update_preset ();
932 }
933