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"
50 #include "ardour/vst_plugin.h"
51 #include "vst_pluginui.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_dialog.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()) {
103 have_gui = create_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);
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);
257 PluginUIWindow::create_vst_editor(boost::shared_ptr<PluginInsert> insert)
259 PluginUIWindow::create_vst_editor(boost::shared_ptr<PluginInsert>)
266 boost::shared_ptr<VSTPlugin> vp;
268 if ((vp = boost::dynamic_pointer_cast<VSTPlugin> (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 VSTPluginUI* vpu = new VSTPluginUI (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);
317 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert> insert)
319 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert>)
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)
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)
461 , plugin_analysis_expander (_("Plugin analysis"))
464 _preset_combo.set_size_request (100, -1);
465 _preset_modified.set_size_request (16, -1);
466 _preset_combo.signal_changed().connect(sigc::mem_fun(*this, &PlugUIBase::preset_selected));
467 ARDOUR_UI::instance()->set_tip (_preset_combo, _("Presets (if any) for this plugin\n(Both factory and user-created)"));
468 ARDOUR_UI::instance()->set_tip (add_button, _("Save a new preset"));
469 ARDOUR_UI::instance()->set_tip (save_button, _("Save the current preset"));
470 ARDOUR_UI::instance()->set_tip (delete_button, _("Delete the current preset"));
471 ARDOUR_UI::instance()->set_tip (bypass_button, _("Disable signal processing by the plugin"));
474 _preset_box.pack_start (_preset_combo);
475 _preset_box.pack_start (_preset_modified);
477 update_preset_list ();
480 add_button.set_name ("PluginAddButton");
481 add_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::add_plugin_setting));
483 save_button.set_name ("PluginSaveButton");
484 save_button.signal_clicked().connect(sigc::mem_fun(*this, &PlugUIBase::save_plugin_setting));
486 delete_button.set_name ("PluginDeleteButton");
487 delete_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::delete_plugin_setting));
489 insert->ActiveChanged.connect (active_connection, invalidator (*this), boost::bind (&PlugUIBase::processor_active_changed, this, boost::weak_ptr<Processor>(insert)), gui_context());
491 bypass_button.set_name ("plugin bypass button");
492 bypass_button.set_text (_("Bypass"));
495 bypass_button.set_active_state (Gtkmm2ext::Active);
497 bypass_button.unset_active_state ();
500 bypass_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::bypass_button_release));
501 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
503 focus_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::focus_toggled));
504 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
506 /* these images are not managed, so that we can remove them at will */
508 focus_out_image = new Image (get_icon (X_("computer_keyboard")));
509 focus_in_image = new Image (get_icon (X_("computer_keyboard_active")));
511 focus_button.add (*focus_out_image);
513 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));
514 ARDOUR_UI::instance()->set_tip (bypass_button, _("Click to enable/disable this plugin"));
516 plugin_analysis_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_plugin_analysis));
517 plugin_analysis_expander.set_expanded(false);
519 insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PlugUIBase::plugin_going_away, this), gui_context());
521 plugin->PresetAdded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
522 plugin->PresetRemoved.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
523 plugin->PresetLoaded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset, this), gui_context ());
524 plugin->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::parameter_changed, this, _1, _2), gui_context ());
527 PlugUIBase::~PlugUIBase()
534 PlugUIBase::plugin_going_away ()
536 /* drop references to the plugin/insert */
539 death_connection.disconnect ();
543 PlugUIBase::set_latency_label ()
545 framecnt_t const l = insert->effective_latency ();
546 framecnt_t const sr = insert->session().frame_rate ();
551 t = string_compose (_("latency (%1 samples)"), l);
553 t = string_compose (_("latency (%1 ms)"), (float) l / ((float) sr / 1000.0f));
556 latency_label.set_text (t);
560 PlugUIBase::latency_button_clicked ()
563 latency_gui = new LatencyGUI (*(insert.get()), insert->session().frame_rate(), insert->session().get_block_size());
564 latency_dialog = new ArdourDialog (_("Edit Latency"), false, false);
565 latency_dialog->get_vbox()->pack_start (*latency_gui);
566 latency_dialog->signal_hide().connect (sigc::mem_fun (*this, &PlugUIBase::set_latency_label));
569 latency_dialog->show_all ();
573 PlugUIBase::processor_active_changed (boost::weak_ptr<Processor> weak_p)
575 ENSURE_GUI_THREAD (*this, &PlugUIBase::processor_active_changed, weak_p);
576 boost::shared_ptr<Processor> p (weak_p.lock());
580 bypass_button.set_active_state (Gtkmm2ext::Active);
582 bypass_button.unset_active_state ();
588 PlugUIBase::preset_selected ()
590 if (_no_load_preset) {
594 if (_preset_combo.get_active_text().length() > 0) {
595 const Plugin::PresetRecord* pr = plugin->preset_by_label (_preset_combo.get_active_text());
597 plugin->load_preset (*pr);
599 warning << string_compose(_("Plugin preset %1 not found"),
600 _preset_combo.get_active_text()) << endmsg;
606 PlugUIBase::add_plugin_setting ()
608 NewPluginPresetDialog d (plugin);
611 case Gtk::RESPONSE_ACCEPT:
612 if (d.name().empty()) {
617 plugin->remove_preset (d.name ());
620 Plugin::PresetRecord const r = plugin->save_preset (d.name());
621 if (!r.uri.empty ()) {
622 plugin->load_preset (r);
629 PlugUIBase::save_plugin_setting ()
631 string const name = _preset_combo.get_active_text ();
632 plugin->remove_preset (name);
633 Plugin::PresetRecord const r = plugin->save_preset (name);
634 if (!r.uri.empty ()) {
635 plugin->load_preset (r);
640 PlugUIBase::delete_plugin_setting ()
642 plugin->remove_preset (_preset_combo.get_active_text ());
646 PlugUIBase::bypass_button_release (GdkEventButton*)
648 bool view_says_bypassed = (bypass_button.active_state() != 0);
650 if (view_says_bypassed != insert->active()) {
651 if (view_says_bypassed) {
654 insert->deactivate ();
662 PlugUIBase::focus_toggled (GdkEventButton*)
664 if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
665 Keyboard::the_keyboard().magic_widget_drop_focus();
666 focus_button.remove ();
667 focus_button.add (*focus_out_image);
668 focus_out_image->show ();
669 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));
670 KeyboardFocused (false);
672 Keyboard::the_keyboard().magic_widget_grab_focus();
673 focus_button.remove ();
674 focus_button.add (*focus_in_image);
675 focus_in_image->show ();
676 ARDOUR_UI::instance()->set_tip (focus_button, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME));
677 KeyboardFocused (true);
684 PlugUIBase::toggle_plugin_analysis()
686 if (plugin_analysis_expander.get_expanded() &&
687 !plugin_analysis_expander.get_child()) {
690 eqgui = new PluginEqGui (insert);
693 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
696 toplevel->get_size (pre_eq_size.width, pre_eq_size.height);
699 plugin_analysis_expander.add (*eqgui);
700 plugin_analysis_expander.show_all ();
701 eqgui->start_listening ();
704 if (!plugin_analysis_expander.get_expanded()) {
706 // Hide & remove from expander
709 eqgui->stop_listening ();
710 plugin_analysis_expander.remove();
712 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
715 toplevel->resize (pre_eq_size.width, pre_eq_size.height);
721 PlugUIBase::update_preset_list ()
723 vector<string> preset_labels;
724 vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets();
728 for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator i = presets.begin(); i != presets.end(); ++i) {
729 preset_labels.push_back (i->label);
732 set_popdown_strings (_preset_combo, preset_labels);
738 PlugUIBase::update_preset ()
740 Plugin::PresetRecord p = plugin->last_preset();
743 _preset_combo.set_active_text (p.label);
746 save_button.set_sensitive (!p.uri.empty() && p.user);
747 delete_button.set_sensitive (!p.uri.empty() && p.user);
749 update_preset_modified ();
753 PlugUIBase::update_preset_modified ()
755 if (plugin->last_preset().uri.empty()) {
756 _preset_modified.set_text ("");
760 bool const c = plugin->parameter_changed_since_last_preset ();
761 if (_preset_modified.get_text().empty() == c) {
762 _preset_modified.set_text (c ? "*" : "");
767 PlugUIBase::parameter_changed (uint32_t, float)
769 update_preset_modified ();
773 PlugUIBase::preset_added_or_removed ()
775 /* Update both the list and the currently-displayed preset */
776 update_preset_list ();