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