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