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