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;
159 if (w > 600) w = 600;
166 set_default_size (w, h);
169 PluginUIWindow::~PluginUIWindow ()
175 PluginUIWindow::set_parent (Gtk::Window* win)
181 PluginUIWindow::on_map ()
184 set_keep_above (true);
188 PluginUIWindow::on_enter_notify_event (GdkEventCrossing *ev)
190 Keyboard::the_keyboard().enter_window (ev, this);
195 PluginUIWindow::on_leave_notify_event (GdkEventCrossing *ev)
197 Keyboard::the_keyboard().leave_window (ev, this);
202 PluginUIWindow::on_focus_in_event (GdkEventFocus *ev)
204 Window::on_focus_in_event (ev);
205 //Keyboard::the_keyboard().magic_widget_grab_focus ();
210 PluginUIWindow::on_focus_out_event (GdkEventFocus *ev)
212 Window::on_focus_out_event (ev);
213 //Keyboard::the_keyboard().magic_widget_drop_focus ();
218 PluginUIWindow::on_show ()
220 set_role("plugin_ui");
223 _pluginui->update_preset_list ();
224 _pluginui->update_preset ();
228 if (_pluginui->on_window_show (_title)) {
234 // set_transient_for (*parent);
239 PluginUIWindow::on_hide ()
244 _pluginui->on_window_hide ();
249 PluginUIWindow::set_title(const std::string& title)
251 Gtk::Window::set_title(title);
256 #ifdef WINDOWS_VST_SUPPORT
257 PluginUIWindow::create_windows_vst_editor(boost::shared_ptr<PluginInsert> insert)
259 PluginUIWindow::create_windows_vst_editor(boost::shared_ptr<PluginInsert>)
262 #ifndef WINDOWS_VST_SUPPORT
266 boost::shared_ptr<WindowsVSTPlugin> vp;
268 if ((vp = boost::dynamic_pointer_cast<WindowsVSTPlugin> (insert->plugin())) == 0) {
269 error << _("unknown type of editor-supplying plugin (note: no VST support in this version of ardour)")
271 throw failed_constructor ();
273 WindowsVSTPluginUI* vpu = new WindowsVSTPluginUI (insert, vp);
276 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
278 vpu->package (*this);
287 PluginUIWindow::create_lxvst_editor(boost::shared_ptr<PluginInsert> insert)
289 PluginUIWindow::create_lxvst_editor(boost::shared_ptr<PluginInsert>)
292 #ifndef LXVST_SUPPORT
296 boost::shared_ptr<LXVSTPlugin> lxvp;
298 if ((lxvp = boost::dynamic_pointer_cast<LXVSTPlugin> (insert->plugin())) == 0) {
299 error << _("unknown type of editor-supplying plugin (note: no linuxVST support in this version of ardour)")
301 throw failed_constructor ();
303 LXVSTPluginUI* lxvpu = new LXVSTPluginUI (insert, lxvp);
306 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
308 lxvpu->package (*this);
316 #ifdef AUDIOUNIT_SUPPORT
317 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert> insert)
319 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert>)
322 #ifndef AUDIOUNIT_SUPPORT
326 _pluginui = create_au_gui (insert, &box);
327 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
330 Application::instance()->ActivationChanged.connect (mem_fun (*this, &PluginUIWindow::app_activated));
338 PluginUIWindow::app_activated (bool yn)
340 PluginUIWindow::app_activated (bool)
343 #ifdef AUDIOUNIT_SUPPORT
347 _pluginui->activate ();
352 was_visible = is_visible();
354 _pluginui->deactivate ();
361 PluginUIWindow::create_lv2_editor(boost::shared_ptr<PluginInsert> insert)
364 boost::shared_ptr<LV2Plugin> vp;
366 if ((vp = boost::dynamic_pointer_cast<LV2Plugin> (insert->plugin())) == 0) {
367 error << _("create_lv2_editor called on non-LV2 plugin") << endmsg;
368 throw failed_constructor ();
370 LV2PluginUI* lpu = new LV2PluginUI (insert, vp);
373 lpu->package (*this);
383 PluginUIWindow::keyboard_focused (bool yn)
385 _keyboard_focused = yn;
389 PluginUIWindow::on_key_press_event (GdkEventKey* event)
391 if (_keyboard_focused) {
393 if (_pluginui->non_gtk_gui()) {
394 _pluginui->forward_key_event (event);
396 return relay_key_press (event, this);
401 /* for us to be getting key press events, there really
402 MUST be a _pluginui, but just to be safe, check ...
406 if (_pluginui->non_gtk_gui()) {
407 /* pass editor window as the window for the event
408 to be handled in, not this one, because there are
409 no widgets in this window that we want to have
412 return relay_key_press (event, &PublicEditor::instance());
414 return relay_key_press (event, this);
423 PluginUIWindow::on_key_release_event (GdkEventKey *event)
425 if (_keyboard_focused) {
427 if (_pluginui->non_gtk_gui()) {
428 _pluginui->forward_key_event (event);
439 PluginUIWindow::plugin_going_away ()
441 ENSURE_GUI_THREAD (*this, &PluginUIWindow::plugin_going_away)
444 _pluginui->stop_updating(0);
447 death_connection.disconnect ();
449 delete_when_idle (this);
452 PlugUIBase::PlugUIBase (boost::shared_ptr<PluginInsert> pi)
454 , plugin (insert->plugin())
455 , add_button (_("Add"))
456 , save_button (_("Save"))
457 , delete_button (_("Delete"))
458 , bypass_button (ArdourButton::led_default_elements)
459 , plugin_analysis_expander (_("Plugin analysis"))
464 _preset_modified.set_size_request (16, -1);
465 _preset_combo.signal_changed().connect(sigc::mem_fun(*this, &PlugUIBase::preset_selected));
466 ARDOUR_UI::instance()->set_tip (_preset_combo, _("Presets (if any) for this plugin\n(Both factory and user-created)"));
467 ARDOUR_UI::instance()->set_tip (add_button, _("Save a new preset"));
468 ARDOUR_UI::instance()->set_tip (save_button, _("Save the current preset"));
469 ARDOUR_UI::instance()->set_tip (delete_button, _("Delete the current preset"));
470 ARDOUR_UI::instance()->set_tip (bypass_button, _("Disable signal processing by the plugin"));
473 update_preset_list ();
476 add_button.set_name ("PluginAddButton");
477 add_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::add_plugin_setting));
479 save_button.set_name ("PluginSaveButton");
480 save_button.signal_clicked().connect(sigc::mem_fun(*this, &PlugUIBase::save_plugin_setting));
482 delete_button.set_name ("PluginDeleteButton");
483 delete_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::delete_plugin_setting));
485 insert->ActiveChanged.connect (active_connection, invalidator (*this), boost::bind (&PlugUIBase::processor_active_changed, this, boost::weak_ptr<Processor>(insert)), gui_context());
487 bypass_button.set_name ("plugin bypass button");
488 bypass_button.set_text (_("Bypass"));
491 bypass_button.set_active_state (Gtkmm2ext::Active);
493 bypass_button.unset_active_state ();
496 bypass_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::bypass_button_release));
497 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
499 focus_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::focus_toggled));
500 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
502 /* these images are not managed, so that we can remove them at will */
504 focus_out_image = new Image (get_icon (X_("computer_keyboard")));
505 focus_in_image = new Image (get_icon (X_("computer_keyboard_active")));
507 focus_button.add (*focus_out_image);
509 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));
510 ARDOUR_UI::instance()->set_tip (bypass_button, _("Click to enable/disable this plugin"));
512 plugin_analysis_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_plugin_analysis));
513 plugin_analysis_expander.set_expanded(false);
515 insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PlugUIBase::plugin_going_away, this), gui_context());
517 plugin->PresetAdded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
518 plugin->PresetRemoved.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
519 plugin->PresetLoaded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset, this), gui_context ());
520 plugin->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::parameter_changed, this, _1, _2), gui_context ());
523 PlugUIBase::~PlugUIBase()
530 PlugUIBase::plugin_going_away ()
532 /* drop references to the plugin/insert */
535 death_connection.disconnect ();
539 PlugUIBase::set_latency_label ()
541 framecnt_t const l = insert->effective_latency ();
542 framecnt_t const sr = insert->session().frame_rate ();
547 t = string_compose (_("latency (%1 samples)"), l);
549 t = string_compose (_("latency (%1 ms)"), (float) l / ((float) sr / 1000.0f));
552 latency_label.set_text (t);
556 PlugUIBase::latency_button_clicked ()
559 latency_gui = new LatencyGUI (*(insert.get()), insert->session().frame_rate(), insert->session().get_block_size());
560 latency_dialog = new ArdourWindow (_("Edit Latency"));
561 latency_dialog->add (*latency_gui);
562 latency_dialog->signal_hide().connect (sigc::mem_fun (*this, &PlugUIBase::set_latency_label));
565 latency_dialog->show_all ();
569 PlugUIBase::processor_active_changed (boost::weak_ptr<Processor> weak_p)
571 ENSURE_GUI_THREAD (*this, &PlugUIBase::processor_active_changed, weak_p);
572 boost::shared_ptr<Processor> p (weak_p.lock());
576 bypass_button.set_active_state (Gtkmm2ext::Active);
578 bypass_button.unset_active_state ();
584 PlugUIBase::preset_selected ()
586 if (_no_load_preset) {
590 if (_preset_combo.get_active_text().length() > 0) {
591 const Plugin::PresetRecord* pr = plugin->preset_by_label (_preset_combo.get_active_text());
593 plugin->load_preset (*pr);
595 warning << string_compose(_("Plugin preset %1 not found"),
596 _preset_combo.get_active_text()) << endmsg;
602 PlugUIBase::add_plugin_setting ()
604 NewPluginPresetDialog d (plugin);
607 case Gtk::RESPONSE_ACCEPT:
608 if (d.name().empty()) {
613 plugin->remove_preset (d.name ());
616 Plugin::PresetRecord const r = plugin->save_preset (d.name());
617 if (!r.uri.empty ()) {
618 plugin->load_preset (r);
625 PlugUIBase::save_plugin_setting ()
627 string const name = _preset_combo.get_active_text ();
628 plugin->remove_preset (name);
629 Plugin::PresetRecord const r = plugin->save_preset (name);
630 if (!r.uri.empty ()) {
631 plugin->load_preset (r);
636 PlugUIBase::delete_plugin_setting ()
638 plugin->remove_preset (_preset_combo.get_active_text ());
642 PlugUIBase::bypass_button_release (GdkEventButton*)
644 bool view_says_bypassed = (bypass_button.active_state() != 0);
646 if (view_says_bypassed != insert->active()) {
647 if (view_says_bypassed) {
650 insert->deactivate ();
658 PlugUIBase::focus_toggled (GdkEventButton*)
660 if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
661 Keyboard::the_keyboard().magic_widget_drop_focus();
662 focus_button.remove ();
663 focus_button.add (*focus_out_image);
664 focus_out_image->show ();
665 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));
666 KeyboardFocused (false);
668 Keyboard::the_keyboard().magic_widget_grab_focus();
669 focus_button.remove ();
670 focus_button.add (*focus_in_image);
671 focus_in_image->show ();
672 ARDOUR_UI::instance()->set_tip (focus_button, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME));
673 KeyboardFocused (true);
680 PlugUIBase::toggle_plugin_analysis()
682 if (plugin_analysis_expander.get_expanded() &&
683 !plugin_analysis_expander.get_child()) {
686 eqgui = new PluginEqGui (insert);
689 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
692 toplevel->get_size (pre_eq_size.width, pre_eq_size.height);
695 plugin_analysis_expander.add (*eqgui);
696 plugin_analysis_expander.show_all ();
697 eqgui->start_listening ();
700 if (!plugin_analysis_expander.get_expanded()) {
702 // Hide & remove from expander
705 eqgui->stop_listening ();
706 plugin_analysis_expander.remove();
708 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
711 toplevel->resize (pre_eq_size.width, pre_eq_size.height);
717 PlugUIBase::update_preset_list ()
719 vector<string> preset_labels;
720 vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets();
724 for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator i = presets.begin(); i != presets.end(); ++i) {
725 preset_labels.push_back (i->label);
728 set_popdown_strings (_preset_combo, preset_labels);
734 PlugUIBase::update_preset ()
736 Plugin::PresetRecord p = plugin->last_preset();
739 _preset_combo.set_active_text (p.label);
742 save_button.set_sensitive (!p.uri.empty() && p.user);
743 delete_button.set_sensitive (!p.uri.empty() && p.user);
745 update_preset_modified ();
749 PlugUIBase::update_preset_modified ()
751 if (plugin->last_preset().uri.empty()) {
752 _preset_modified.set_text ("");
756 bool const c = plugin->parameter_changed_since_last_preset ();
757 if (_preset_modified.get_text().empty() == c) {
758 _preset_modified.set_text (c ? "*" : "");
763 PlugUIBase::parameter_changed (uint32_t, float)
765 update_preset_modified ();
769 PlugUIBase::preset_added_or_removed ()
771 /* Update both the list and the currently-displayed preset */
772 update_preset_list ();