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 (_("Bypass"))
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));
469 _preset_box.pack_start (_preset_combo);
470 _preset_box.pack_start (_preset_modified);
472 update_preset_list ();
475 add_button.set_name ("PluginAddButton");
476 add_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::add_plugin_setting));
478 save_button.set_name ("PluginSaveButton");
479 save_button.signal_clicked().connect(sigc::mem_fun(*this, &PlugUIBase::save_plugin_setting));
481 delete_button.set_name ("PluginDeleteButton");
482 delete_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::delete_plugin_setting));
484 insert->ActiveChanged.connect (active_connection, invalidator (*this), boost::bind (&PlugUIBase::processor_active_changed, this, boost::weak_ptr<Processor>(insert)), gui_context());
486 bypass_button.set_active (!pi->active());
488 bypass_button.set_name ("PluginBypassButton");
489 bypass_button.signal_toggled().connect (sigc::mem_fun(*this, &PlugUIBase::bypass_toggled));
490 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
492 focus_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::focus_toggled));
493 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
495 /* these images are not managed, so that we can remove them at will */
497 focus_out_image = new Image (get_icon (X_("computer_keyboard")));
498 focus_in_image = new Image (get_icon (X_("computer_keyboard_active")));
500 focus_button.add (*focus_out_image);
502 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));
503 ARDOUR_UI::instance()->set_tip (bypass_button, _("Click to enable/disable this plugin"));
505 plugin_analysis_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_plugin_analysis));
506 plugin_analysis_expander.set_expanded(false);
508 insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PlugUIBase::plugin_going_away, this), gui_context());
510 plugin->PresetAdded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
511 plugin->PresetRemoved.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
512 plugin->PresetLoaded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset, this), gui_context ());
513 plugin->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::parameter_changed, this, _1, _2), gui_context ());
516 PlugUIBase::~PlugUIBase()
523 PlugUIBase::plugin_going_away ()
525 /* drop references to the plugin/insert */
528 death_connection.disconnect ();
532 PlugUIBase::set_latency_label ()
534 framecnt_t const l = insert->effective_latency ();
535 framecnt_t const sr = insert->session().frame_rate ();
540 t = string_compose (_("latency (%1 samples)"), l);
542 t = string_compose (_("latency (%1 ms)"), (float) l / ((float) sr / 1000.0f));
545 latency_label.set_text (t);
549 PlugUIBase::latency_button_clicked ()
552 latency_gui = new LatencyGUI (*(insert.get()), insert->session().frame_rate(), insert->session().get_block_size());
553 latency_dialog = new ArdourDialog (_("Edit Latency"), false, false);
554 latency_dialog->get_vbox()->pack_start (*latency_gui);
555 latency_dialog->signal_hide().connect (sigc::mem_fun (*this, &PlugUIBase::set_latency_label));
558 latency_dialog->show_all ();
562 PlugUIBase::processor_active_changed (boost::weak_ptr<Processor> weak_p)
564 ENSURE_GUI_THREAD (*this, &PlugUIBase::processor_active_changed, weak_p)
565 boost::shared_ptr<Processor> p (weak_p);
567 bypass_button.set_active (!p->active());
572 PlugUIBase::preset_selected ()
574 if (_no_load_preset) {
578 if (_preset_combo.get_active_text().length() > 0) {
579 const Plugin::PresetRecord* pr = plugin->preset_by_label (_preset_combo.get_active_text());
581 plugin->load_preset (*pr);
583 warning << string_compose(_("Plugin preset %1 not found"),
584 _preset_combo.get_active_text()) << endmsg;
590 PlugUIBase::add_plugin_setting ()
592 NewPluginPresetDialog d (plugin);
595 case Gtk::RESPONSE_ACCEPT:
596 if (d.name().empty()) {
601 plugin->remove_preset (d.name ());
604 Plugin::PresetRecord const r = plugin->save_preset (d.name());
605 if (!r.uri.empty ()) {
606 plugin->load_preset (r);
613 PlugUIBase::save_plugin_setting ()
615 string const name = _preset_combo.get_active_text ();
616 plugin->remove_preset (name);
617 Plugin::PresetRecord const r = plugin->save_preset (name);
618 if (!r.uri.empty ()) {
619 plugin->load_preset (r);
624 PlugUIBase::delete_plugin_setting ()
626 plugin->remove_preset (_preset_combo.get_active_text ());
630 PlugUIBase::bypass_toggled ()
634 if ((x = bypass_button.get_active()) == insert->active()) {
636 insert->deactivate ();
644 PlugUIBase::focus_toggled (GdkEventButton*)
646 if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
647 Keyboard::the_keyboard().magic_widget_drop_focus();
648 focus_button.remove ();
649 focus_button.add (*focus_out_image);
650 focus_out_image->show ();
651 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));
652 KeyboardFocused (false);
654 Keyboard::the_keyboard().magic_widget_grab_focus();
655 focus_button.remove ();
656 focus_button.add (*focus_in_image);
657 focus_in_image->show ();
658 ARDOUR_UI::instance()->set_tip (focus_button, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME));
659 KeyboardFocused (true);
666 PlugUIBase::toggle_plugin_analysis()
668 if (plugin_analysis_expander.get_expanded() &&
669 !plugin_analysis_expander.get_child()) {
672 eqgui = new PluginEqGui (insert);
675 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
678 toplevel->get_size (pre_eq_size.width, pre_eq_size.height);
681 plugin_analysis_expander.add (*eqgui);
682 plugin_analysis_expander.show_all ();
683 eqgui->start_listening ();
686 if (!plugin_analysis_expander.get_expanded()) {
688 // Hide & remove from expander
691 eqgui->stop_listening ();
692 plugin_analysis_expander.remove();
694 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
697 toplevel->resize (pre_eq_size.width, pre_eq_size.height);
703 PlugUIBase::update_preset_list ()
705 vector<string> preset_labels;
706 vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets();
710 for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator i = presets.begin(); i != presets.end(); ++i) {
711 preset_labels.push_back (i->label);
714 set_popdown_strings (_preset_combo, preset_labels);
720 PlugUIBase::update_preset ()
722 Plugin::PresetRecord p = plugin->last_preset();
725 _preset_combo.set_active_text (p.label);
728 save_button.set_sensitive (!p.uri.empty() && p.user);
729 delete_button.set_sensitive (!p.uri.empty() && p.user);
731 update_preset_modified ();
735 PlugUIBase::update_preset_modified ()
737 if (plugin->last_preset().uri.empty()) {
738 _preset_modified.set_text ("");
742 bool const c = plugin->parameter_changed_since_last_preset ();
743 if (_preset_modified.get_text().empty() == c) {
744 _preset_modified.set_text (c ? "*" : "");
749 PlugUIBase::parameter_changed (uint32_t, float)
751 update_preset_modified ();
755 PlugUIBase::preset_added_or_removed ()
757 /* Update both the list and the currently-displayed preset */
758 update_preset_list ();