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);
162 set_resizable (_pluginui->resizable());
165 PluginUIWindow::~PluginUIWindow ()
171 PluginUIWindow::set_parent (Gtk::Window* win)
177 PluginUIWindow::on_map ()
180 set_keep_above (true);
184 PluginUIWindow::on_enter_notify_event (GdkEventCrossing *ev)
186 Keyboard::the_keyboard().enter_window (ev, this);
191 PluginUIWindow::on_leave_notify_event (GdkEventCrossing *ev)
193 Keyboard::the_keyboard().leave_window (ev, this);
198 PluginUIWindow::on_focus_in_event (GdkEventFocus *ev)
200 Window::on_focus_in_event (ev);
201 //Keyboard::the_keyboard().magic_widget_grab_focus ();
206 PluginUIWindow::on_focus_out_event (GdkEventFocus *ev)
208 Window::on_focus_out_event (ev);
209 //Keyboard::the_keyboard().magic_widget_drop_focus ();
214 PluginUIWindow::on_show ()
216 set_role("plugin_ui");
219 _pluginui->update_preset_list ();
220 _pluginui->update_preset ();
224 if (_pluginui->on_window_show (_title)) {
230 // set_transient_for (*parent);
235 PluginUIWindow::on_hide ()
240 _pluginui->on_window_hide ();
245 PluginUIWindow::set_title(const std::string& title)
247 Gtk::Window::set_title(title);
252 #ifdef WINDOWS_VST_SUPPORT
253 PluginUIWindow::create_windows_vst_editor(boost::shared_ptr<PluginInsert> insert)
255 PluginUIWindow::create_windows_vst_editor(boost::shared_ptr<PluginInsert>)
258 #ifndef WINDOWS_VST_SUPPORT
262 boost::shared_ptr<WindowsVSTPlugin> vp;
264 if ((vp = boost::dynamic_pointer_cast<WindowsVSTPlugin> (insert->plugin())) == 0) {
265 error << _("unknown type of editor-supplying plugin (note: no VST support in this version of ardour)")
267 throw failed_constructor ();
269 WindowsVSTPluginUI* vpu = new WindowsVSTPluginUI (insert, vp);
272 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
274 vpu->package (*this);
283 PluginUIWindow::create_lxvst_editor(boost::shared_ptr<PluginInsert> insert)
285 PluginUIWindow::create_lxvst_editor(boost::shared_ptr<PluginInsert>)
288 #ifndef LXVST_SUPPORT
292 boost::shared_ptr<LXVSTPlugin> lxvp;
294 if ((lxvp = boost::dynamic_pointer_cast<LXVSTPlugin> (insert->plugin())) == 0) {
295 error << _("unknown type of editor-supplying plugin (note: no linuxVST support in this version of ardour)")
297 throw failed_constructor ();
299 LXVSTPluginUI* lxvpu = new LXVSTPluginUI (insert, lxvp);
302 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
304 lxvpu->package (*this);
312 #ifdef AUDIOUNIT_SUPPORT
313 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert> insert)
315 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert>)
318 #ifndef AUDIOUNIT_SUPPORT
322 _pluginui = create_au_gui (insert, &box);
323 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
326 Application::instance()->ActivationChanged.connect (mem_fun (*this, &PluginUIWindow::app_activated));
334 PluginUIWindow::app_activated (bool yn)
336 PluginUIWindow::app_activated (bool)
339 #ifdef AUDIOUNIT_SUPPORT
343 _pluginui->activate ();
348 was_visible = is_visible();
350 _pluginui->deactivate ();
357 PluginUIWindow::create_lv2_editor(boost::shared_ptr<PluginInsert> insert)
360 boost::shared_ptr<LV2Plugin> vp;
362 if ((vp = boost::dynamic_pointer_cast<LV2Plugin> (insert->plugin())) == 0) {
363 error << _("create_lv2_editor called on non-LV2 plugin") << endmsg;
364 throw failed_constructor ();
366 LV2PluginUI* lpu = new LV2PluginUI (insert, vp);
369 lpu->package (*this);
379 PluginUIWindow::keyboard_focused (bool yn)
381 _keyboard_focused = yn;
385 PluginUIWindow::on_key_press_event (GdkEventKey* event)
387 if (_keyboard_focused) {
389 if (_pluginui->non_gtk_gui()) {
390 _pluginui->forward_key_event (event);
392 return relay_key_press (event, this);
397 /* for us to be getting key press events, there really
398 MUST be a _pluginui, but just to be safe, check ...
402 if (_pluginui->non_gtk_gui()) {
403 /* pass editor window as the window for the event
404 to be handled in, not this one, because there are
405 no widgets in this window that we want to have
408 return relay_key_press (event, &PublicEditor::instance());
410 return relay_key_press (event, this);
419 PluginUIWindow::on_key_release_event (GdkEventKey *event)
421 if (_keyboard_focused) {
423 if (_pluginui->non_gtk_gui()) {
424 _pluginui->forward_key_event (event);
435 PluginUIWindow::plugin_going_away ()
437 ENSURE_GUI_THREAD (*this, &PluginUIWindow::plugin_going_away)
440 _pluginui->stop_updating(0);
443 death_connection.disconnect ();
445 delete_when_idle (this);
448 PlugUIBase::PlugUIBase (boost::shared_ptr<PluginInsert> pi)
450 , plugin (insert->plugin())
451 , add_button (_("Add"))
452 , save_button (_("Save"))
453 , delete_button (_("Delete"))
454 , bypass_button (ArdourButton::led_default_elements)
455 , description_expander (_("Description"))
456 , plugin_analysis_expander (_("Plugin analysis"))
461 _preset_modified.set_size_request (16, -1);
462 _preset_combo.signal_changed().connect(sigc::mem_fun(*this, &PlugUIBase::preset_selected));
463 ARDOUR_UI::instance()->set_tip (_preset_combo, _("Presets (if any) for this plugin\n(Both factory and user-created)"));
464 ARDOUR_UI::instance()->set_tip (add_button, _("Save a new preset"));
465 ARDOUR_UI::instance()->set_tip (save_button, _("Save the current preset"));
466 ARDOUR_UI::instance()->set_tip (delete_button, _("Delete the current preset"));
467 ARDOUR_UI::instance()->set_tip (bypass_button, _("Disable signal processing by the plugin"));
470 update_preset_list ();
473 add_button.set_name ("PluginAddButton");
474 add_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::add_plugin_setting));
476 save_button.set_name ("PluginSaveButton");
477 save_button.signal_clicked().connect(sigc::mem_fun(*this, &PlugUIBase::save_plugin_setting));
479 delete_button.set_name ("PluginDeleteButton");
480 delete_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::delete_plugin_setting));
482 insert->ActiveChanged.connect (active_connection, invalidator (*this), boost::bind (&PlugUIBase::processor_active_changed, this, boost::weak_ptr<Processor>(insert)), gui_context());
484 bypass_button.set_name ("plugin bypass button");
485 bypass_button.set_text (_("Bypass"));
486 bypass_button.set_active (!pi->active());
487 bypass_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::bypass_button_release));
488 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
490 focus_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::focus_toggled));
491 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
493 /* these images are not managed, so that we can remove them at will */
495 focus_out_image = new Image (get_icon (X_("computer_keyboard")));
496 focus_in_image = new Image (get_icon (X_("computer_keyboard_active")));
498 focus_button.add (*focus_out_image);
500 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));
501 ARDOUR_UI::instance()->set_tip (bypass_button, _("Click to enable/disable this plugin"));
503 description_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_description));
504 description_expander.set_expanded(false);
506 plugin_analysis_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_plugin_analysis));
507 plugin_analysis_expander.set_expanded(false);
509 insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PlugUIBase::plugin_going_away, this), gui_context());
511 plugin->PresetAdded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
512 plugin->PresetRemoved.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
513 plugin->PresetLoaded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset, this), gui_context ());
514 plugin->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::parameter_changed, this, _1, _2), gui_context ());
517 PlugUIBase::~PlugUIBase()
524 PlugUIBase::plugin_going_away ()
526 /* drop references to the plugin/insert */
529 death_connection.disconnect ();
533 PlugUIBase::set_latency_label ()
535 framecnt_t const l = insert->effective_latency ();
536 framecnt_t const sr = insert->session().frame_rate ();
541 t = string_compose (_("latency (%1 samples)"), l);
543 t = string_compose (_("latency (%1 ms)"), (float) l / ((float) sr / 1000.0f));
546 latency_label.set_text (t);
550 PlugUIBase::latency_button_clicked ()
553 latency_gui = new LatencyGUI (*(insert.get()), insert->session().frame_rate(), insert->session().get_block_size());
554 latency_dialog = new ArdourWindow (_("Edit Latency"));
555 latency_dialog->add (*latency_gui);
556 latency_dialog->signal_hide().connect (sigc::mem_fun (*this, &PlugUIBase::set_latency_label));
559 latency_dialog->show_all ();
563 PlugUIBase::processor_active_changed (boost::weak_ptr<Processor> weak_p)
565 ENSURE_GUI_THREAD (*this, &PlugUIBase::processor_active_changed, weak_p);
566 boost::shared_ptr<Processor> p (weak_p.lock());
569 bypass_button.set_active (!p->active());
574 PlugUIBase::preset_selected ()
576 if (_no_load_preset) {
580 if (_preset_combo.get_active_text().length() > 0) {
581 const Plugin::PresetRecord* pr = plugin->preset_by_label (_preset_combo.get_active_text());
583 plugin->load_preset (*pr);
585 warning << string_compose(_("Plugin preset %1 not found"),
586 _preset_combo.get_active_text()) << endmsg;
592 PlugUIBase::add_plugin_setting ()
594 NewPluginPresetDialog d (plugin);
597 case Gtk::RESPONSE_ACCEPT:
598 if (d.name().empty()) {
603 plugin->remove_preset (d.name ());
606 Plugin::PresetRecord const r = plugin->save_preset (d.name());
607 if (!r.uri.empty ()) {
608 plugin->load_preset (r);
615 PlugUIBase::save_plugin_setting ()
617 string const name = _preset_combo.get_active_text ();
618 plugin->remove_preset (name);
619 Plugin::PresetRecord const r = plugin->save_preset (name);
620 if (!r.uri.empty ()) {
621 plugin->load_preset (r);
626 PlugUIBase::delete_plugin_setting ()
628 plugin->remove_preset (_preset_combo.get_active_text ());
632 PlugUIBase::bypass_button_release (GdkEventButton*)
634 bool view_says_bypassed = (bypass_button.active_state() != 0);
636 if (view_says_bypassed != insert->active()) {
637 if (view_says_bypassed) {
640 insert->deactivate ();
648 PlugUIBase::focus_toggled (GdkEventButton*)
650 if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
651 Keyboard::the_keyboard().magic_widget_drop_focus();
652 focus_button.remove ();
653 focus_button.add (*focus_out_image);
654 focus_out_image->show ();
655 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));
656 KeyboardFocused (false);
658 Keyboard::the_keyboard().magic_widget_grab_focus();
659 focus_button.remove ();
660 focus_button.add (*focus_in_image);
661 focus_in_image->show ();
662 ARDOUR_UI::instance()->set_tip (focus_button, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME));
663 KeyboardFocused (true);
670 PlugUIBase::toggle_description()
672 if (description_expander.get_expanded() &&
673 !description_expander.get_child()) {
674 const std::string text = plugin->get_docs();
679 Gtk::Label* label = manage(new Gtk::Label(text));
680 label->set_line_wrap(true);
681 label->set_line_wrap_mode(Pango::WRAP_WORD);
682 description_expander.add(*label);
683 description_expander.show_all();
686 if (!description_expander.get_expanded()) {
687 description_expander.remove();
693 PlugUIBase::toggle_plugin_analysis()
695 if (plugin_analysis_expander.get_expanded() &&
696 !plugin_analysis_expander.get_child()) {
699 eqgui = new PluginEqGui (insert);
702 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
705 toplevel->get_size (pre_eq_size.width, pre_eq_size.height);
708 plugin_analysis_expander.add (*eqgui);
709 plugin_analysis_expander.show_all ();
710 eqgui->start_listening ();
713 if (!plugin_analysis_expander.get_expanded()) {
714 // Hide & remove from expander
717 eqgui->stop_listening ();
718 plugin_analysis_expander.remove();
720 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
723 toplevel->resize (pre_eq_size.width, pre_eq_size.height);
729 PlugUIBase::update_preset_list ()
731 vector<string> preset_labels;
732 vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets();
736 for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator i = presets.begin(); i != presets.end(); ++i) {
737 preset_labels.push_back (i->label);
740 set_popdown_strings (_preset_combo, preset_labels);
746 PlugUIBase::update_preset ()
748 Plugin::PresetRecord p = plugin->last_preset();
751 _preset_combo.set_active_text (p.label);
754 save_button.set_sensitive (!p.uri.empty() && p.user);
755 delete_button.set_sensitive (!p.uri.empty() && p.user);
757 update_preset_modified ();
761 PlugUIBase::update_preset_modified ()
763 if (plugin->last_preset().uri.empty()) {
764 _preset_modified.set_text ("");
768 bool const c = plugin->parameter_changed_since_last_preset ();
769 if (_preset_modified.get_text().empty() == c) {
770 _preset_modified.set_text (c ? "*" : "");
775 PlugUIBase::parameter_changed (uint32_t, float)
777 update_preset_modified ();
781 PlugUIBase::preset_added_or_removed ()
783 /* Update both the list and the currently-displayed preset */
784 update_preset_list ();