2 Copyright (C) 2000 Paul Davis
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.
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.
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.
21 #include "gtk2ardour-config.h"
29 #include "pbd/stl_delete.h"
30 #include "pbd/xml++.h"
31 #include "pbd/failed_constructor.h"
33 #include <gtkmm/widget.h>
34 #include <gtkmm/box.h>
35 #include <gtkmm2ext/click_box.h>
36 #include <gtkmm2ext/fastmeter.h>
37 #include <gtkmm2ext/barcontroller.h>
38 #include <gtkmm2ext/utils.h>
39 #include <gtkmm2ext/doi.h>
40 #include <gtkmm2ext/slider_controller.h>
41 #include <gtkmm2ext/application.h>
43 #include "midi++/manager.h"
45 #include "ardour/session.h"
46 #include "ardour/plugin.h"
47 #include "ardour/plugin_insert.h"
48 #include "ardour/ladspa_plugin.h"
49 #ifdef WINDOWS_VST_SUPPORT
50 #include "ardour/windows_vst_plugin.h"
51 #include "windows_vst_plugin_ui.h"
54 #include "ardour/lxvst_plugin.h"
55 #include "lxvst_plugin_ui.h"
58 #include "ardour/lv2_plugin.h"
59 #include "lv2_plugin_ui.h"
64 #include "ardour_window.h"
65 #include "ardour_ui.h"
67 #include "plugin_ui.h"
69 #include "gui_thread.h"
70 #include "public_editor.h"
72 #include "latency_gui.h"
73 #include "plugin_eq_gui.h"
74 #include "new_plugin_preset_dialog.h"
79 using namespace ARDOUR;
81 using namespace Gtkmm2ext;
84 PluginUIWindow::PluginUIWindow (
86 boost::shared_ptr<PluginInsert> insert,
91 , _keyboard_focused (false)
93 bool have_gui = false;
95 Label* label = manage (new Label());
96 label->set_markup ("<b>THIS IS THE PLUGIN UI</b>");
98 std::cout << "SHOW UI " << insert->plugin()->unique_id()
99 << " editor: " << editor << std::endl;
100 if (editor && insert->plugin()->has_editor()) {
101 switch (insert->type()) {
102 case ARDOUR::Windows_VST:
103 have_gui = create_windows_vst_editor (insert);
107 have_gui = create_lxvst_editor (insert);
110 case ARDOUR::AudioUnit:
111 have_gui = create_audiounit_editor (insert);
115 error << _("Eh? LADSPA plugins don't have editors!") << endmsg;
119 have_gui = create_lv2_editor (insert);
123 #ifndef WINDOWS_VST_SUPPORT
124 error << _("unknown type of editor-supplying plugin (note: no VST support in this version of ardour)")
127 error << _("unknown type of editor-supplying plugin")
130 throw failed_constructor ();
136 GenericPluginUI* pu = new GenericPluginUI (insert, scrollable);
139 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
141 set_wmclass (X_("ardour_plugin_editor"), PROGRAM_NAME);
143 signal_map_event().connect (sigc::mem_fun (*pu, &GenericPluginUI::start_updating));
144 signal_unmap_event().connect (sigc::mem_fun (*pu, &GenericPluginUI::stop_updating));
147 // set_position (Gtk::WIN_POS_MOUSE);
148 set_name ("PluginEditor");
149 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
151 signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), reinterpret_cast<Window*> (this)), false);
152 insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PluginUIWindow::plugin_going_away, this), gui_context());
154 gint h = _pluginui->get_preferred_height ();
155 gint w = _pluginui->get_preferred_width ();
158 if (h > 600) h = 600;
161 set_default_size (w, h);
164 PluginUIWindow::~PluginUIWindow ()
170 PluginUIWindow::set_parent (Gtk::Window* win)
176 PluginUIWindow::on_map ()
179 set_keep_above (true);
183 PluginUIWindow::on_enter_notify_event (GdkEventCrossing *ev)
185 Keyboard::the_keyboard().enter_window (ev, this);
190 PluginUIWindow::on_leave_notify_event (GdkEventCrossing *ev)
192 Keyboard::the_keyboard().leave_window (ev, this);
197 PluginUIWindow::on_focus_in_event (GdkEventFocus *ev)
199 Window::on_focus_in_event (ev);
200 //Keyboard::the_keyboard().magic_widget_grab_focus ();
205 PluginUIWindow::on_focus_out_event (GdkEventFocus *ev)
207 Window::on_focus_out_event (ev);
208 //Keyboard::the_keyboard().magic_widget_drop_focus ();
213 PluginUIWindow::on_show ()
215 set_role("plugin_ui");
218 _pluginui->update_preset_list ();
219 _pluginui->update_preset ();
223 if (_pluginui->on_window_show (_title)) {
229 // set_transient_for (*parent);
234 PluginUIWindow::on_hide ()
239 _pluginui->on_window_hide ();
244 PluginUIWindow::set_title(const std::string& title)
246 Gtk::Window::set_title(title);
251 #ifdef WINDOWS_VST_SUPPORT
252 PluginUIWindow::create_windows_vst_editor(boost::shared_ptr<PluginInsert> insert)
254 PluginUIWindow::create_windows_vst_editor(boost::shared_ptr<PluginInsert>)
257 #ifndef WINDOWS_VST_SUPPORT
261 boost::shared_ptr<WindowsVSTPlugin> vp;
263 if ((vp = boost::dynamic_pointer_cast<WindowsVSTPlugin> (insert->plugin())) == 0) {
264 error << _("unknown type of editor-supplying plugin (note: no VST support in this version of ardour)")
266 throw failed_constructor ();
268 WindowsVSTPluginUI* vpu = new WindowsVSTPluginUI (insert, vp);
271 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
273 vpu->package (*this);
282 PluginUIWindow::create_lxvst_editor(boost::shared_ptr<PluginInsert> insert)
284 PluginUIWindow::create_lxvst_editor(boost::shared_ptr<PluginInsert>)
287 #ifndef LXVST_SUPPORT
291 boost::shared_ptr<LXVSTPlugin> lxvp;
293 if ((lxvp = boost::dynamic_pointer_cast<LXVSTPlugin> (insert->plugin())) == 0) {
294 error << _("unknown type of editor-supplying plugin (note: no linuxVST support in this version of ardour)")
296 throw failed_constructor ();
298 LXVSTPluginUI* lxvpu = new LXVSTPluginUI (insert, lxvp);
301 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
303 lxvpu->package (*this);
311 #ifdef AUDIOUNIT_SUPPORT
312 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert> insert)
314 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert>)
317 #ifndef AUDIOUNIT_SUPPORT
321 _pluginui = create_au_gui (insert, &box);
322 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
325 Application::instance()->ActivationChanged.connect (mem_fun (*this, &PluginUIWindow::app_activated));
333 PluginUIWindow::app_activated (bool yn)
335 PluginUIWindow::app_activated (bool)
338 #ifdef AUDIOUNIT_SUPPORT
342 _pluginui->activate ();
347 was_visible = is_visible();
349 _pluginui->deactivate ();
356 PluginUIWindow::create_lv2_editor(boost::shared_ptr<PluginInsert> insert)
359 boost::shared_ptr<LV2Plugin> vp;
361 if ((vp = boost::dynamic_pointer_cast<LV2Plugin> (insert->plugin())) == 0) {
362 error << _("create_lv2_editor called on non-LV2 plugin") << endmsg;
363 throw failed_constructor ();
365 LV2PluginUI* lpu = new LV2PluginUI (insert, vp);
368 lpu->package (*this);
378 PluginUIWindow::keyboard_focused (bool yn)
380 _keyboard_focused = yn;
384 PluginUIWindow::on_key_press_event (GdkEventKey* event)
386 if (_keyboard_focused) {
388 if (_pluginui->non_gtk_gui()) {
389 _pluginui->forward_key_event (event);
391 return relay_key_press (event, this);
396 /* for us to be getting key press events, there really
397 MUST be a _pluginui, but just to be safe, check ...
401 if (_pluginui->non_gtk_gui()) {
402 /* pass editor window as the window for the event
403 to be handled in, not this one, because there are
404 no widgets in this window that we want to have
407 return relay_key_press (event, &PublicEditor::instance());
409 return relay_key_press (event, this);
418 PluginUIWindow::on_key_release_event (GdkEventKey *event)
420 if (_keyboard_focused) {
422 if (_pluginui->non_gtk_gui()) {
423 _pluginui->forward_key_event (event);
434 PluginUIWindow::plugin_going_away ()
436 ENSURE_GUI_THREAD (*this, &PluginUIWindow::plugin_going_away)
439 _pluginui->stop_updating(0);
442 death_connection.disconnect ();
444 delete_when_idle (this);
447 PlugUIBase::PlugUIBase (boost::shared_ptr<PluginInsert> pi)
449 , plugin (insert->plugin())
450 , add_button (_("Add"))
451 , save_button (_("Save"))
452 , delete_button (_("Delete"))
453 , bypass_button (ArdourButton::led_default_elements)
454 , plugin_analysis_expander (_("Plugin analysis"))
459 _preset_modified.set_size_request (16, -1);
460 _preset_combo.signal_changed().connect(sigc::mem_fun(*this, &PlugUIBase::preset_selected));
461 ARDOUR_UI::instance()->set_tip (_preset_combo, _("Presets (if any) for this plugin\n(Both factory and user-created)"));
462 ARDOUR_UI::instance()->set_tip (add_button, _("Save a new preset"));
463 ARDOUR_UI::instance()->set_tip (save_button, _("Save the current preset"));
464 ARDOUR_UI::instance()->set_tip (delete_button, _("Delete the current preset"));
465 ARDOUR_UI::instance()->set_tip (bypass_button, _("Disable signal processing by the plugin"));
468 update_preset_list ();
471 add_button.set_name ("PluginAddButton");
472 add_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::add_plugin_setting));
474 save_button.set_name ("PluginSaveButton");
475 save_button.signal_clicked().connect(sigc::mem_fun(*this, &PlugUIBase::save_plugin_setting));
477 delete_button.set_name ("PluginDeleteButton");
478 delete_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::delete_plugin_setting));
480 insert->ActiveChanged.connect (active_connection, invalidator (*this), boost::bind (&PlugUIBase::processor_active_changed, this, boost::weak_ptr<Processor>(insert)), gui_context());
482 bypass_button.set_name ("plugin bypass button");
483 bypass_button.set_text (_("Bypass"));
484 bypass_button.set_active (!pi->active());
485 bypass_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::bypass_button_release));
486 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
488 focus_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::focus_toggled));
489 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
491 /* these images are not managed, so that we can remove them at will */
493 focus_out_image = new Image (get_icon (X_("computer_keyboard")));
494 focus_in_image = new Image (get_icon (X_("computer_keyboard_active")));
496 focus_button.add (*focus_out_image);
498 ARDOUR_UI::instance()->set_tip (focus_button, string_compose (_("Click to allow the plugin to receive keyboard events that %1 would normally use as a shortcut"), PROGRAM_NAME));
499 ARDOUR_UI::instance()->set_tip (bypass_button, _("Click to enable/disable this plugin"));
501 plugin_analysis_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_plugin_analysis));
502 plugin_analysis_expander.set_expanded(false);
504 insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PlugUIBase::plugin_going_away, this), gui_context());
506 plugin->PresetAdded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
507 plugin->PresetRemoved.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
508 plugin->PresetLoaded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset, this), gui_context ());
509 plugin->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::parameter_changed, this, _1, _2), gui_context ());
512 PlugUIBase::~PlugUIBase()
519 PlugUIBase::plugin_going_away ()
521 /* drop references to the plugin/insert */
524 death_connection.disconnect ();
528 PlugUIBase::set_latency_label ()
530 framecnt_t const l = insert->effective_latency ();
531 framecnt_t const sr = insert->session().frame_rate ();
536 t = string_compose (_("latency (%1 samples)"), l);
538 t = string_compose (_("latency (%1 ms)"), (float) l / ((float) sr / 1000.0f));
541 latency_label.set_text (t);
545 PlugUIBase::latency_button_clicked ()
548 latency_gui = new LatencyGUI (*(insert.get()), insert->session().frame_rate(), insert->session().get_block_size());
549 latency_dialog = new ArdourWindow (_("Edit Latency"));
550 latency_dialog->add (*latency_gui);
551 latency_dialog->signal_hide().connect (sigc::mem_fun (*this, &PlugUIBase::set_latency_label));
554 latency_dialog->show_all ();
558 PlugUIBase::processor_active_changed (boost::weak_ptr<Processor> weak_p)
560 ENSURE_GUI_THREAD (*this, &PlugUIBase::processor_active_changed, weak_p);
561 boost::shared_ptr<Processor> p (weak_p.lock());
564 bypass_button.set_active (!p->active());
569 PlugUIBase::preset_selected ()
571 if (_no_load_preset) {
575 if (_preset_combo.get_active_text().length() > 0) {
576 const Plugin::PresetRecord* pr = plugin->preset_by_label (_preset_combo.get_active_text());
578 plugin->load_preset (*pr);
580 warning << string_compose(_("Plugin preset %1 not found"),
581 _preset_combo.get_active_text()) << endmsg;
587 PlugUIBase::add_plugin_setting ()
589 NewPluginPresetDialog d (plugin);
592 case Gtk::RESPONSE_ACCEPT:
593 if (d.name().empty()) {
598 plugin->remove_preset (d.name ());
601 Plugin::PresetRecord const r = plugin->save_preset (d.name());
602 if (!r.uri.empty ()) {
603 plugin->load_preset (r);
610 PlugUIBase::save_plugin_setting ()
612 string const name = _preset_combo.get_active_text ();
613 plugin->remove_preset (name);
614 Plugin::PresetRecord const r = plugin->save_preset (name);
615 if (!r.uri.empty ()) {
616 plugin->load_preset (r);
621 PlugUIBase::delete_plugin_setting ()
623 plugin->remove_preset (_preset_combo.get_active_text ());
627 PlugUIBase::bypass_button_release (GdkEventButton*)
629 bool view_says_bypassed = (bypass_button.active_state() != 0);
631 if (view_says_bypassed != insert->active()) {
632 if (view_says_bypassed) {
635 insert->deactivate ();
643 PlugUIBase::focus_toggled (GdkEventButton*)
645 if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
646 Keyboard::the_keyboard().magic_widget_drop_focus();
647 focus_button.remove ();
648 focus_button.add (*focus_out_image);
649 focus_out_image->show ();
650 ARDOUR_UI::instance()->set_tip (focus_button, string_compose (_("Click to allow the plugin to receive keyboard events that %1 would normally use as a shortcut"), PROGRAM_NAME));
651 KeyboardFocused (false);
653 Keyboard::the_keyboard().magic_widget_grab_focus();
654 focus_button.remove ();
655 focus_button.add (*focus_in_image);
656 focus_in_image->show ();
657 ARDOUR_UI::instance()->set_tip (focus_button, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME));
658 KeyboardFocused (true);
665 PlugUIBase::toggle_plugin_analysis()
667 if (plugin_analysis_expander.get_expanded() &&
668 !plugin_analysis_expander.get_child()) {
671 eqgui = new PluginEqGui (insert);
674 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
677 toplevel->get_size (pre_eq_size.width, pre_eq_size.height);
680 plugin_analysis_expander.add (*eqgui);
681 plugin_analysis_expander.show_all ();
682 eqgui->start_listening ();
685 if (!plugin_analysis_expander.get_expanded()) {
687 // Hide & remove from expander
690 eqgui->stop_listening ();
691 plugin_analysis_expander.remove();
693 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
696 toplevel->resize (pre_eq_size.width, pre_eq_size.height);
702 PlugUIBase::update_preset_list ()
704 vector<string> preset_labels;
705 vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets();
709 for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator i = presets.begin(); i != presets.end(); ++i) {
710 preset_labels.push_back (i->label);
713 set_popdown_strings (_preset_combo, preset_labels);
719 PlugUIBase::update_preset ()
721 Plugin::PresetRecord p = plugin->last_preset();
724 _preset_combo.set_active_text (p.label);
727 save_button.set_sensitive (!p.uri.empty() && p.user);
728 delete_button.set_sensitive (!p.uri.empty() && p.user);
730 update_preset_modified ();
734 PlugUIBase::update_preset_modified ()
736 if (plugin->last_preset().uri.empty()) {
737 _preset_modified.set_text ("");
741 bool const c = plugin->parameter_changed_since_last_preset ();
742 if (_preset_modified.get_text().empty() == c) {
743 _preset_modified.set_text (c ? "*" : "");
748 PlugUIBase::parameter_changed (uint32_t, float)
750 update_preset_modified ();
754 PlugUIBase::preset_added_or_removed ()
756 /* Update both the list and the currently-displayed preset */
757 update_preset_list ();