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 (
85 boost::shared_ptr<PluginInsert> insert,
88 : ArdourWindow (string())
90 , _keyboard_focused (false)
91 #ifdef AUDIOUNIT_SUPPORT
92 , pre_deactivate_x (-1)
93 , pre_deactivate_y (-1)
97 bool have_gui = false;
98 Label* label = manage (new Label());
99 label->set_markup ("<b>THIS IS THE PLUGIN UI</b>");
101 if (editor && insert->plugin()->has_editor()) {
102 switch (insert->type()) {
103 case ARDOUR::Windows_VST:
104 have_gui = create_windows_vst_editor (insert);
108 have_gui = create_lxvst_editor (insert);
111 case ARDOUR::AudioUnit:
112 have_gui = create_audiounit_editor (insert);
116 error << _("Eh? LADSPA plugins don't have editors!") << endmsg;
120 have_gui = create_lv2_editor (insert);
124 #ifndef WINDOWS_VST_SUPPORT
125 error << string_compose (_("unknown type of editor-supplying plugin (note: no VST support in this version of %1)"), PROGRAM_NAME)
128 error << _("unknown type of editor-supplying plugin")
131 throw failed_constructor ();
137 GenericPluginUI* pu = new GenericPluginUI (insert, scrollable);
140 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
142 set_wmclass (X_("ardour_plugin_editor"), PROGRAM_NAME);
144 signal_map_event().connect (sigc::mem_fun (*pu, &GenericPluginUI::start_updating));
145 signal_unmap_event().connect (sigc::mem_fun (*pu, &GenericPluginUI::stop_updating));
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::on_show ()
173 set_role("plugin_ui");
176 _pluginui->update_preset_list ();
177 _pluginui->update_preset ();
181 #if defined (HAVE_AUDIOUNITS) && defined(GTKOSX)
182 if (pre_deactivate_x >= 0) {
183 move (pre_deactivate_x, pre_deactivate_y);
187 if (_pluginui->on_window_show (_title)) {
194 PluginUIWindow::on_hide ()
196 #if defined (HAVE_AUDIOUNITS) && defined(GTKOSX)
197 get_position (pre_deactivate_x, pre_deactivate_y);
203 _pluginui->on_window_hide ();
208 PluginUIWindow::set_title(const std::string& title)
210 Gtk::Window::set_title(title);
215 #ifdef WINDOWS_VST_SUPPORT
216 PluginUIWindow::create_windows_vst_editor(boost::shared_ptr<PluginInsert> insert)
218 PluginUIWindow::create_windows_vst_editor(boost::shared_ptr<PluginInsert>)
221 #ifndef WINDOWS_VST_SUPPORT
225 boost::shared_ptr<WindowsVSTPlugin> vp;
227 if ((vp = boost::dynamic_pointer_cast<WindowsVSTPlugin> (insert->plugin())) == 0) {
228 error << string_compose (_("unknown type of editor-supplying plugin (note: no VST support in this version of %1)"), PROGRAM_NAME)
230 throw failed_constructor ();
232 WindowsVSTPluginUI* vpu = new WindowsVSTPluginUI (insert, vp);
235 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
237 vpu->package (*this);
246 PluginUIWindow::create_lxvst_editor(boost::shared_ptr<PluginInsert> insert)
248 PluginUIWindow::create_lxvst_editor(boost::shared_ptr<PluginInsert>)
251 #ifndef LXVST_SUPPORT
255 boost::shared_ptr<LXVSTPlugin> lxvp;
257 if ((lxvp = boost::dynamic_pointer_cast<LXVSTPlugin> (insert->plugin())) == 0) {
258 error << string_compose (_("unknown type of editor-supplying plugin (note: no linuxVST support in this version of %1)"), PROGRAM_NAME)
260 throw failed_constructor ();
262 LXVSTPluginUI* lxvpu = new LXVSTPluginUI (insert, lxvp);
265 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
267 lxvpu->package (*this);
275 #ifdef AUDIOUNIT_SUPPORT
276 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert> insert)
278 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert>)
281 #ifndef AUDIOUNIT_SUPPORT
285 _pluginui = create_au_gui (insert, &box);
286 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
289 Application::instance()->ActivationChanged.connect (mem_fun (*this, &PluginUIWindow::app_activated));
297 PluginUIWindow::app_activated (bool yn)
299 PluginUIWindow::app_activated (bool)
302 #ifdef AUDIOUNIT_SUPPORT
306 _pluginui->activate ();
307 if (pre_deactivate_x >= 0) {
308 move (pre_deactivate_x, pre_deactivate_y);
314 was_visible = is_visible();
315 get_position (pre_deactivate_x, pre_deactivate_y);
317 _pluginui->deactivate ();
324 PluginUIWindow::create_lv2_editor(boost::shared_ptr<PluginInsert> insert)
327 boost::shared_ptr<LV2Plugin> vp;
329 if ((vp = boost::dynamic_pointer_cast<LV2Plugin> (insert->plugin())) == 0) {
330 error << _("create_lv2_editor called on non-LV2 plugin") << endmsg;
331 throw failed_constructor ();
333 LV2PluginUI* lpu = new LV2PluginUI (insert, vp);
336 lpu->package (*this);
346 PluginUIWindow::keyboard_focused (bool yn)
348 _keyboard_focused = yn;
352 PluginUIWindow::on_key_press_event (GdkEventKey* event)
354 if (_keyboard_focused) {
356 if (_pluginui->non_gtk_gui()) {
357 _pluginui->forward_key_event (event);
359 return relay_key_press (event, this);
364 /* for us to be getting key press events, there really
365 MUST be a _pluginui, but just to be safe, check ...
369 if (_pluginui->non_gtk_gui()) {
370 /* pass editor window as the window for the event
371 to be handled in, not this one, because there are
372 no widgets in this window that we want to have
375 return relay_key_press (event, &PublicEditor::instance());
377 return relay_key_press (event, this);
386 PluginUIWindow::on_key_release_event (GdkEventKey *event)
388 if (_keyboard_focused) {
390 if (_pluginui->non_gtk_gui()) {
391 _pluginui->forward_key_event (event);
402 PluginUIWindow::plugin_going_away ()
404 ENSURE_GUI_THREAD (*this, &PluginUIWindow::plugin_going_away)
407 _pluginui->stop_updating(0);
410 death_connection.disconnect ();
412 delete_when_idle (this);
415 PlugUIBase::PlugUIBase (boost::shared_ptr<PluginInsert> pi)
417 , plugin (insert->plugin())
418 , add_button (_("Add"))
419 , save_button (_("Save"))
420 , delete_button (_("Delete"))
421 , bypass_button (ArdourButton::led_default_elements)
422 , description_expander (_("Description"))
423 , plugin_analysis_expander (_("Plugin analysis"))
428 _preset_modified.set_size_request (16, -1);
429 _preset_combo.signal_changed().connect(sigc::mem_fun(*this, &PlugUIBase::preset_selected));
430 ARDOUR_UI::instance()->set_tip (_preset_combo, _("Presets (if any) for this plugin\n(Both factory and user-created)"));
431 ARDOUR_UI::instance()->set_tip (add_button, _("Save a new preset"));
432 ARDOUR_UI::instance()->set_tip (save_button, _("Save the current preset"));
433 ARDOUR_UI::instance()->set_tip (delete_button, _("Delete the current preset"));
434 ARDOUR_UI::instance()->set_tip (bypass_button, _("Disable signal processing by the plugin"));
437 update_preset_list ();
440 add_button.set_name ("PluginAddButton");
441 add_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::add_plugin_setting));
443 save_button.set_name ("PluginSaveButton");
444 save_button.signal_clicked().connect(sigc::mem_fun(*this, &PlugUIBase::save_plugin_setting));
446 delete_button.set_name ("PluginDeleteButton");
447 delete_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::delete_plugin_setting));
449 insert->ActiveChanged.connect (active_connection, invalidator (*this), boost::bind (&PlugUIBase::processor_active_changed, this, boost::weak_ptr<Processor>(insert)), gui_context());
451 bypass_button.set_name ("plugin bypass button");
452 bypass_button.set_text (_("Bypass"));
453 bypass_button.set_active (!pi->active());
454 bypass_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::bypass_button_release));
455 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
457 focus_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::focus_toggled));
458 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
460 /* these images are not managed, so that we can remove them at will */
462 focus_out_image = new Image (get_icon (X_("computer_keyboard")));
463 focus_in_image = new Image (get_icon (X_("computer_keyboard_active")));
465 focus_button.add (*focus_out_image);
467 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));
468 ARDOUR_UI::instance()->set_tip (bypass_button, _("Click to enable/disable this plugin"));
470 description_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_description));
471 description_expander.set_expanded(false);
473 plugin_analysis_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_plugin_analysis));
474 plugin_analysis_expander.set_expanded(false);
476 insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PlugUIBase::plugin_going_away, this), gui_context());
478 plugin->PresetAdded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
479 plugin->PresetRemoved.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
480 plugin->PresetLoaded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset, this), gui_context ());
481 plugin->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::parameter_changed, this, _1, _2), gui_context ());
484 PlugUIBase::~PlugUIBase()
491 PlugUIBase::plugin_going_away ()
493 /* drop references to the plugin/insert */
499 PlugUIBase::set_latency_label ()
501 framecnt_t const l = insert->effective_latency ();
502 framecnt_t const sr = insert->session().frame_rate ();
507 t = string_compose (P_("latency (%1 sample)", "latency (%1 samples)", l), l);
509 t = string_compose (_("latency (%1 ms)"), (float) l / ((float) sr / 1000.0f));
512 latency_label.set_text (t);
516 PlugUIBase::latency_button_clicked ()
519 latency_gui = new LatencyGUI (*(insert.get()), insert->session().frame_rate(), insert->session().get_block_size());
520 latency_dialog = new ArdourWindow (_("Edit Latency"));
521 /* use both keep-above and transient for to try cover as many
522 different WM's as possible.
524 latency_dialog->set_keep_above (true);
525 Window* win = dynamic_cast<Window*> (bypass_button.get_toplevel ());
527 latency_dialog->set_transient_for (*win);
529 latency_dialog->add (*latency_gui);
530 latency_dialog->signal_hide().connect (sigc::mem_fun (*this, &PlugUIBase::set_latency_label));
533 latency_dialog->show_all ();
537 PlugUIBase::processor_active_changed (boost::weak_ptr<Processor> weak_p)
539 ENSURE_GUI_THREAD (*this, &PlugUIBase::processor_active_changed, weak_p);
540 boost::shared_ptr<Processor> p (weak_p.lock());
543 bypass_button.set_active (!p->active());
548 PlugUIBase::preset_selected ()
550 if (_no_load_preset) {
554 if (_preset_combo.get_active_text().length() > 0) {
555 const Plugin::PresetRecord* pr = plugin->preset_by_label (_preset_combo.get_active_text());
557 plugin->load_preset (*pr);
559 warning << string_compose(_("Plugin preset %1 not found"),
560 _preset_combo.get_active_text()) << endmsg;
563 // blank selected = no preset
564 plugin->clear_preset();
568 #ifdef NO_PLUGIN_STATE
569 static bool seen_saving_message = false;
573 PlugUIBase::add_plugin_setting ()
575 #ifndef NO_PLUGIN_STATE
576 NewPluginPresetDialog d (plugin);
579 case Gtk::RESPONSE_ACCEPT:
580 if (d.name().empty()) {
585 plugin->remove_preset (d.name ());
588 Plugin::PresetRecord const r = plugin->save_preset (d.name());
589 if (!r.uri.empty ()) {
590 plugin->load_preset (r);
595 if (!seen_saving_message) {
596 info << string_compose (_("Plugin presets are not supported in this build of %1. Consider paying for a full version"),
599 seen_saving_message = true;
605 PlugUIBase::save_plugin_setting ()
607 #ifndef NO_PLUGIN_STATE
608 string const name = _preset_combo.get_active_text ();
609 plugin->remove_preset (name);
610 Plugin::PresetRecord const r = plugin->save_preset (name);
611 if (!r.uri.empty ()) {
612 plugin->load_preset (r);
615 if (!seen_saving_message) {
616 info << string_compose (_("Plugin presets are not supported in this build of %1. Consider paying for a newer version"),
619 seen_saving_message = true;
625 PlugUIBase::delete_plugin_setting ()
627 #ifndef NO_PLUGIN_STATE
628 plugin->remove_preset (_preset_combo.get_active_text ());
630 if (!seen_saving_message) {
631 info << string_compose (_("Plugin presets are not supported in this build of %1. Consider paying for a newer version"),
634 seen_saving_message = true;
640 PlugUIBase::bypass_button_release (GdkEventButton*)
642 bool view_says_bypassed = (bypass_button.active_state() != 0);
644 if (view_says_bypassed != insert->active()) {
645 if (view_says_bypassed) {
648 insert->deactivate ();
656 PlugUIBase::focus_toggled (GdkEventButton*)
658 if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
659 Keyboard::the_keyboard().magic_widget_drop_focus();
660 focus_button.remove ();
661 focus_button.add (*focus_out_image);
662 focus_out_image->show ();
663 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));
664 KeyboardFocused (false);
666 Keyboard::the_keyboard().magic_widget_grab_focus();
667 focus_button.remove ();
668 focus_button.add (*focus_in_image);
669 focus_in_image->show ();
670 ARDOUR_UI::instance()->set_tip (focus_button, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME));
671 KeyboardFocused (true);
678 PlugUIBase::toggle_description()
680 if (description_expander.get_expanded() &&
681 !description_expander.get_child()) {
682 const std::string text = plugin->get_docs();
687 Gtk::Label* label = manage(new Gtk::Label(text));
688 label->set_line_wrap(true);
689 label->set_line_wrap_mode(Pango::WRAP_WORD);
690 description_expander.add(*label);
691 description_expander.show_all();
694 if (!description_expander.get_expanded()) {
695 description_expander.remove();
701 PlugUIBase::toggle_plugin_analysis()
703 if (plugin_analysis_expander.get_expanded() &&
704 !plugin_analysis_expander.get_child()) {
707 eqgui = new PluginEqGui (insert);
710 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
713 toplevel->get_size (pre_eq_size.width, pre_eq_size.height);
716 plugin_analysis_expander.add (*eqgui);
717 plugin_analysis_expander.show_all ();
718 eqgui->start_listening ();
721 if (!plugin_analysis_expander.get_expanded()) {
722 // Hide & remove from expander
725 eqgui->stop_listening ();
726 plugin_analysis_expander.remove();
728 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
731 toplevel->resize (pre_eq_size.width, pre_eq_size.height);
737 PlugUIBase::update_preset_list ()
739 vector<string> preset_labels;
740 vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets();
744 for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator i = presets.begin(); i != presets.end(); ++i) {
745 preset_labels.push_back (i->label);
748 preset_labels.push_back("");
750 set_popdown_strings (_preset_combo, preset_labels);
756 PlugUIBase::update_preset ()
758 Plugin::PresetRecord p = plugin->last_preset();
761 _preset_combo.set_active_text (p.label);
764 save_button.set_sensitive (!p.uri.empty() && p.user);
765 delete_button.set_sensitive (!p.uri.empty() && p.user);
767 update_preset_modified ();
771 PlugUIBase::update_preset_modified ()
774 if (plugin->last_preset().uri.empty()) {
775 _preset_modified.set_text ("");
779 bool const c = plugin->parameter_changed_since_last_preset ();
780 if (_preset_modified.get_text().empty() == c) {
781 _preset_modified.set_text (c ? "*" : "");
786 PlugUIBase::parameter_changed (uint32_t, float)
788 update_preset_modified ();
792 PlugUIBase::preset_added_or_removed ()
794 /* Update both the list and the currently-displayed preset */
795 update_preset_list ();