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"));
486 bypass_button.set_active_state (Gtkmm2ext::Active);
488 bypass_button.unset_active_state ();
491 bypass_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::bypass_button_release));
492 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
494 focus_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::focus_toggled));
495 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
497 /* these images are not managed, so that we can remove them at will */
499 focus_out_image = new Image (get_icon (X_("computer_keyboard")));
500 focus_in_image = new Image (get_icon (X_("computer_keyboard_active")));
502 focus_button.add (*focus_out_image);
504 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));
505 ARDOUR_UI::instance()->set_tip (bypass_button, _("Click to enable/disable this plugin"));
507 plugin_analysis_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_plugin_analysis));
508 plugin_analysis_expander.set_expanded(false);
510 insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PlugUIBase::plugin_going_away, this), gui_context());
512 plugin->PresetAdded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
513 plugin->PresetRemoved.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
514 plugin->PresetLoaded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset, this), gui_context ());
515 plugin->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::parameter_changed, this, _1, _2), gui_context ());
518 PlugUIBase::~PlugUIBase()
525 PlugUIBase::plugin_going_away ()
527 /* drop references to the plugin/insert */
530 death_connection.disconnect ();
534 PlugUIBase::set_latency_label ()
536 framecnt_t const l = insert->effective_latency ();
537 framecnt_t const sr = insert->session().frame_rate ();
542 t = string_compose (_("latency (%1 samples)"), l);
544 t = string_compose (_("latency (%1 ms)"), (float) l / ((float) sr / 1000.0f));
547 latency_label.set_text (t);
551 PlugUIBase::latency_button_clicked ()
554 latency_gui = new LatencyGUI (*(insert.get()), insert->session().frame_rate(), insert->session().get_block_size());
555 latency_dialog = new ArdourWindow (_("Edit Latency"));
556 latency_dialog->add (*latency_gui);
557 latency_dialog->signal_hide().connect (sigc::mem_fun (*this, &PlugUIBase::set_latency_label));
560 latency_dialog->show_all ();
564 PlugUIBase::processor_active_changed (boost::weak_ptr<Processor> weak_p)
566 ENSURE_GUI_THREAD (*this, &PlugUIBase::processor_active_changed, weak_p);
567 boost::shared_ptr<Processor> p (weak_p.lock());
571 bypass_button.set_active_state (Gtkmm2ext::Active);
573 bypass_button.unset_active_state ();
579 PlugUIBase::preset_selected ()
581 if (_no_load_preset) {
585 if (_preset_combo.get_active_text().length() > 0) {
586 const Plugin::PresetRecord* pr = plugin->preset_by_label (_preset_combo.get_active_text());
588 plugin->load_preset (*pr);
590 warning << string_compose(_("Plugin preset %1 not found"),
591 _preset_combo.get_active_text()) << endmsg;
597 PlugUIBase::add_plugin_setting ()
599 NewPluginPresetDialog d (plugin);
602 case Gtk::RESPONSE_ACCEPT:
603 if (d.name().empty()) {
608 plugin->remove_preset (d.name ());
611 Plugin::PresetRecord const r = plugin->save_preset (d.name());
612 if (!r.uri.empty ()) {
613 plugin->load_preset (r);
620 PlugUIBase::save_plugin_setting ()
622 string const name = _preset_combo.get_active_text ();
623 plugin->remove_preset (name);
624 Plugin::PresetRecord const r = plugin->save_preset (name);
625 if (!r.uri.empty ()) {
626 plugin->load_preset (r);
631 PlugUIBase::delete_plugin_setting ()
633 plugin->remove_preset (_preset_combo.get_active_text ());
637 PlugUIBase::bypass_button_release (GdkEventButton*)
639 bool view_says_bypassed = (bypass_button.active_state() != 0);
641 if (view_says_bypassed != insert->active()) {
642 if (view_says_bypassed) {
645 insert->deactivate ();
653 PlugUIBase::focus_toggled (GdkEventButton*)
655 if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
656 Keyboard::the_keyboard().magic_widget_drop_focus();
657 focus_button.remove ();
658 focus_button.add (*focus_out_image);
659 focus_out_image->show ();
660 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));
661 KeyboardFocused (false);
663 Keyboard::the_keyboard().magic_widget_grab_focus();
664 focus_button.remove ();
665 focus_button.add (*focus_in_image);
666 focus_in_image->show ();
667 ARDOUR_UI::instance()->set_tip (focus_button, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME));
668 KeyboardFocused (true);
675 PlugUIBase::toggle_plugin_analysis()
677 if (plugin_analysis_expander.get_expanded() &&
678 !plugin_analysis_expander.get_child()) {
681 eqgui = new PluginEqGui (insert);
684 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
687 toplevel->get_size (pre_eq_size.width, pre_eq_size.height);
690 plugin_analysis_expander.add (*eqgui);
691 plugin_analysis_expander.show_all ();
692 eqgui->start_listening ();
695 if (!plugin_analysis_expander.get_expanded()) {
697 // Hide & remove from expander
700 eqgui->stop_listening ();
701 plugin_analysis_expander.remove();
703 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
706 toplevel->resize (pre_eq_size.width, pre_eq_size.height);
712 PlugUIBase::update_preset_list ()
714 vector<string> preset_labels;
715 vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets();
719 for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator i = presets.begin(); i != presets.end(); ++i) {
720 preset_labels.push_back (i->label);
723 set_popdown_strings (_preset_combo, preset_labels);
729 PlugUIBase::update_preset ()
731 Plugin::PresetRecord p = plugin->last_preset();
734 _preset_combo.set_active_text (p.label);
737 save_button.set_sensitive (!p.uri.empty() && p.user);
738 delete_button.set_sensitive (!p.uri.empty() && p.user);
740 update_preset_modified ();
744 PlugUIBase::update_preset_modified ()
746 if (plugin->last_preset().uri.empty()) {
747 _preset_modified.set_text ("");
751 bool const c = plugin->parameter_changed_since_last_preset ();
752 if (_preset_modified.get_text().empty() == c) {
753 _preset_modified.set_text (c ? "*" : "");
758 PlugUIBase::parameter_changed (uint32_t, float)
760 update_preset_modified ();
764 PlugUIBase::preset_added_or_removed ()
766 /* Update both the list and the currently-displayed preset */
767 update_preset_list ();