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 "ardour/session.h"
44 #include "ardour/plugin.h"
45 #include "ardour/plugin_insert.h"
46 #include "ardour/ladspa_plugin.h"
47 #ifdef WINDOWS_VST_SUPPORT
48 #include "ardour/windows_vst_plugin.h"
49 #include "windows_vst_plugin_ui.h"
52 #include "ardour/lxvst_plugin.h"
53 #include "lxvst_plugin_ui.h"
56 #include "ardour/lv2_plugin.h"
57 #include "lv2_plugin_ui.h"
60 #include "ardour_window.h"
61 #include "ardour_ui.h"
63 #include "plugin_ui.h"
65 #include "gui_thread.h"
66 #include "public_editor.h"
68 #include "latency_gui.h"
69 #include "plugin_eq_gui.h"
70 #include "new_plugin_preset_dialog.h"
75 using namespace ARDOUR;
76 using namespace ARDOUR_UI_UTILS;
78 using namespace Gtkmm2ext;
81 PluginUIWindow::PluginUIWindow (
82 boost::shared_ptr<PluginInsert> insert,
85 : ArdourWindow (string())
87 , _keyboard_focused (false)
88 #ifdef AUDIOUNIT_SUPPORT
89 , pre_deactivate_x (-1)
90 , pre_deactivate_y (-1)
94 bool have_gui = false;
95 Label* label = manage (new Label());
96 label->set_markup ("<b>THIS IS THE PLUGIN UI</b>");
98 if (editor && insert->plugin()->has_editor()) {
99 switch (insert->type()) {
100 case ARDOUR::Windows_VST:
101 have_gui = create_windows_vst_editor (insert);
105 have_gui = create_lxvst_editor (insert);
108 case ARDOUR::AudioUnit:
109 have_gui = create_audiounit_editor (insert);
113 error << _("Eh? LADSPA plugins don't have editors!") << endmsg;
117 have_gui = create_lv2_editor (insert);
121 #ifndef WINDOWS_VST_SUPPORT
122 error << string_compose (_("unknown type of editor-supplying plugin (note: no VST support in this version of %1)"), PROGRAM_NAME)
125 error << _("unknown type of editor-supplying plugin")
128 throw failed_constructor ();
134 GenericPluginUI* pu = new GenericPluginUI (insert, scrollable);
137 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
139 set_wmclass (X_("ardour_plugin_editor"), PROGRAM_NAME);
141 signal_map_event().connect (sigc::mem_fun (*pu, &GenericPluginUI::start_updating));
142 signal_unmap_event().connect (sigc::mem_fun (*pu, &GenericPluginUI::stop_updating));
145 set_name ("PluginEditor");
146 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
148 insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PluginUIWindow::plugin_going_away, this), gui_context());
150 gint h = _pluginui->get_preferred_height ();
151 gint w = _pluginui->get_preferred_width ();
154 if (h > 600) h = 600;
157 set_default_size (w, h);
158 set_resizable (_pluginui->resizable());
161 PluginUIWindow::~PluginUIWindow ()
164 cerr << "PluginWindow deleted for " << this << endl;
170 PluginUIWindow::on_show ()
172 set_role("plugin_ui");
175 _pluginui->update_preset_list ();
176 _pluginui->update_preset ();
180 #if defined (HAVE_AUDIOUNITS) && defined(GTKOSX)
181 if (pre_deactivate_x >= 0) {
182 move (pre_deactivate_x, pre_deactivate_y);
186 if (_pluginui->on_window_show (_title)) {
193 PluginUIWindow::on_hide ()
195 #if defined (HAVE_AUDIOUNITS) && defined(GTKOSX)
196 get_position (pre_deactivate_x, pre_deactivate_y);
202 _pluginui->on_window_hide ();
207 PluginUIWindow::set_title(const std::string& title)
209 Gtk::Window::set_title(title);
214 #ifdef WINDOWS_VST_SUPPORT
215 PluginUIWindow::create_windows_vst_editor(boost::shared_ptr<PluginInsert> insert)
217 PluginUIWindow::create_windows_vst_editor(boost::shared_ptr<PluginInsert>)
220 #ifndef WINDOWS_VST_SUPPORT
224 boost::shared_ptr<WindowsVSTPlugin> vp;
226 if ((vp = boost::dynamic_pointer_cast<WindowsVSTPlugin> (insert->plugin())) == 0) {
227 error << string_compose (_("unknown type of editor-supplying plugin (note: no VST support in this version of %1)"), PROGRAM_NAME)
229 throw failed_constructor ();
231 WindowsVSTPluginUI* vpu = new WindowsVSTPluginUI (insert, vp, GTK_WIDGET(this->gobj()));
234 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
236 vpu->package (*this);
245 PluginUIWindow::create_lxvst_editor(boost::shared_ptr<PluginInsert> insert)
247 PluginUIWindow::create_lxvst_editor(boost::shared_ptr<PluginInsert>)
250 #ifndef LXVST_SUPPORT
254 boost::shared_ptr<LXVSTPlugin> lxvp;
256 if ((lxvp = boost::dynamic_pointer_cast<LXVSTPlugin> (insert->plugin())) == 0) {
257 error << string_compose (_("unknown type of editor-supplying plugin (note: no linuxVST support in this version of %1)"), PROGRAM_NAME)
259 throw failed_constructor ();
261 LXVSTPluginUI* lxvpu = new LXVSTPluginUI (insert, lxvp);
264 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
266 lxvpu->package (*this);
274 #ifdef AUDIOUNIT_SUPPORT
275 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert> insert)
277 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert>)
280 #ifndef AUDIOUNIT_SUPPORT
284 _pluginui = create_au_gui (insert, &box);
285 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
288 Application::instance()->ActivationChanged.connect (mem_fun (*this, &PluginUIWindow::app_activated));
296 PluginUIWindow::app_activated (bool yn)
298 PluginUIWindow::app_activated (bool)
301 #ifdef AUDIOUNIT_SUPPORT
305 _pluginui->activate ();
306 if (pre_deactivate_x >= 0) {
307 move (pre_deactivate_x, pre_deactivate_y);
313 was_visible = is_visible();
314 get_position (pre_deactivate_x, pre_deactivate_y);
316 _pluginui->deactivate ();
323 PluginUIWindow::create_lv2_editor(boost::shared_ptr<PluginInsert> insert)
326 boost::shared_ptr<LV2Plugin> vp;
328 if ((vp = boost::dynamic_pointer_cast<LV2Plugin> (insert->plugin())) == 0) {
329 error << _("create_lv2_editor called on non-LV2 plugin") << endmsg;
330 throw failed_constructor ();
332 LV2PluginUI* lpu = new LV2PluginUI (insert, vp);
335 lpu->package (*this);
336 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
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 ();
413 PlugUIBase::PlugUIBase (boost::shared_ptr<PluginInsert> pi)
415 , plugin (insert->plugin())
416 , add_button (_("Add"))
417 , save_button (_("Save"))
418 , delete_button (_("Delete"))
419 , bypass_button (ArdourButton::led_default_elements)
420 , description_expander (_("Description"))
421 , plugin_analysis_expander (_("Plugin analysis"))
426 _preset_modified.set_size_request (16, -1);
427 _preset_combo.signal_changed().connect(sigc::mem_fun(*this, &PlugUIBase::preset_selected));
428 ARDOUR_UI::instance()->set_tip (_preset_combo, _("Presets (if any) for this plugin\n(Both factory and user-created)"));
429 ARDOUR_UI::instance()->set_tip (add_button, _("Save a new preset"));
430 ARDOUR_UI::instance()->set_tip (save_button, _("Save the current preset"));
431 ARDOUR_UI::instance()->set_tip (delete_button, _("Delete the current preset"));
432 ARDOUR_UI::instance()->set_tip (bypass_button, _("Disable signal processing by the plugin"));
435 update_preset_list ();
438 add_button.set_name ("PluginAddButton");
439 add_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::add_plugin_setting));
441 save_button.set_name ("PluginSaveButton");
442 save_button.signal_clicked().connect(sigc::mem_fun(*this, &PlugUIBase::save_plugin_setting));
444 delete_button.set_name ("PluginDeleteButton");
445 delete_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::delete_plugin_setting));
447 insert->ActiveChanged.connect (active_connection, invalidator (*this), boost::bind (&PlugUIBase::processor_active_changed, this, boost::weak_ptr<Processor>(insert)), gui_context());
449 bypass_button.set_name ("plugin bypass button");
450 bypass_button.set_text (_("Bypass"));
451 bypass_button.set_active (!pi->active());
452 bypass_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::bypass_button_release), false);
453 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
455 focus_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::focus_toggled));
456 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
458 /* these images are not managed, so that we can remove them at will */
460 focus_out_image = new Image (get_icon (X_("computer_keyboard")));
461 focus_in_image = new Image (get_icon (X_("computer_keyboard_active")));
463 focus_button.add (*focus_out_image);
465 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));
466 ARDOUR_UI::instance()->set_tip (bypass_button, _("Click to enable/disable this plugin"));
468 description_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_description));
469 description_expander.set_expanded(false);
471 plugin_analysis_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_plugin_analysis));
472 plugin_analysis_expander.set_expanded(false);
474 insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PlugUIBase::plugin_going_away, this), gui_context());
476 plugin->PresetAdded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
477 plugin->PresetRemoved.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
478 plugin->PresetLoaded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset, this), gui_context ());
479 plugin->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::parameter_changed, this, _1, _2), gui_context ());
482 PlugUIBase::~PlugUIBase()
489 PlugUIBase::plugin_going_away ()
491 /* drop references to the plugin/insert */
497 PlugUIBase::set_latency_label ()
499 framecnt_t const l = insert->effective_latency ();
500 framecnt_t const sr = insert->session().frame_rate ();
505 t = string_compose (P_("latency (%1 sample)", "latency (%1 samples)", l), l);
507 t = string_compose (_("latency (%1 ms)"), (float) l / ((float) sr / 1000.0f));
510 latency_label.set_text (t);
514 PlugUIBase::latency_button_clicked ()
517 latency_gui = new LatencyGUI (*(insert.get()), insert->session().frame_rate(), insert->session().get_block_size());
518 latency_dialog = new ArdourWindow (_("Edit Latency"));
519 /* use both keep-above and transient for to try cover as many
520 different WM's as possible.
522 latency_dialog->set_keep_above (true);
523 Window* win = dynamic_cast<Window*> (bypass_button.get_toplevel ());
525 latency_dialog->set_transient_for (*win);
527 latency_dialog->add (*latency_gui);
528 latency_dialog->signal_hide().connect (sigc::mem_fun (*this, &PlugUIBase::set_latency_label));
531 latency_dialog->show_all ();
535 PlugUIBase::processor_active_changed (boost::weak_ptr<Processor> weak_p)
537 ENSURE_GUI_THREAD (*this, &PlugUIBase::processor_active_changed, weak_p);
538 boost::shared_ptr<Processor> p (weak_p.lock());
541 bypass_button.set_active (!p->active());
546 PlugUIBase::preset_selected ()
548 if (_no_load_preset) {
552 if (_preset_combo.get_active_text().length() > 0) {
553 const Plugin::PresetRecord* pr = plugin->preset_by_label (_preset_combo.get_active_text());
555 plugin->load_preset (*pr);
557 warning << string_compose(_("Plugin preset %1 not found"),
558 _preset_combo.get_active_text()) << endmsg;
561 // blank selected = no preset
562 plugin->clear_preset();
566 #ifdef NO_PLUGIN_STATE
567 static bool seen_saving_message = false;
571 PlugUIBase::add_plugin_setting ()
573 #ifndef NO_PLUGIN_STATE
574 NewPluginPresetDialog d (plugin);
577 case Gtk::RESPONSE_ACCEPT:
578 if (d.name().empty()) {
583 plugin->remove_preset (d.name ());
586 Plugin::PresetRecord const r = plugin->save_preset (d.name());
587 if (!r.uri.empty ()) {
588 plugin->load_preset (r);
593 if (!seen_saving_message) {
594 info << string_compose (_("Plugin presets are not supported in this build of %1. Consider paying for a full version"),
597 seen_saving_message = true;
603 PlugUIBase::save_plugin_setting ()
605 #ifndef NO_PLUGIN_STATE
606 string const name = _preset_combo.get_active_text ();
607 plugin->remove_preset (name);
608 Plugin::PresetRecord const r = plugin->save_preset (name);
609 if (!r.uri.empty ()) {
610 plugin->load_preset (r);
613 if (!seen_saving_message) {
614 info << string_compose (_("Plugin presets are not supported in this build of %1. Consider paying for a newer version"),
617 seen_saving_message = true;
623 PlugUIBase::delete_plugin_setting ()
625 #ifndef NO_PLUGIN_STATE
626 plugin->remove_preset (_preset_combo.get_active_text ());
628 if (!seen_saving_message) {
629 info << string_compose (_("Plugin presets are not supported in this build of %1. Consider paying for a newer version"),
632 seen_saving_message = true;
638 PlugUIBase::bypass_button_release (GdkEventButton*)
640 bool view_says_bypassed = (bypass_button.active_state() != 0);
642 if (view_says_bypassed != insert->active()) {
643 if (view_says_bypassed) {
646 insert->deactivate ();
654 PlugUIBase::focus_toggled (GdkEventButton*)
656 if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
657 Keyboard::the_keyboard().magic_widget_drop_focus();
658 focus_button.remove ();
659 focus_button.add (*focus_out_image);
660 focus_out_image->show ();
661 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));
662 KeyboardFocused (false);
664 Keyboard::the_keyboard().magic_widget_grab_focus();
665 focus_button.remove ();
666 focus_button.add (*focus_in_image);
667 focus_in_image->show ();
668 ARDOUR_UI::instance()->set_tip (focus_button, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME));
669 KeyboardFocused (true);
676 PlugUIBase::toggle_description()
678 if (description_expander.get_expanded() &&
679 !description_expander.get_child()) {
680 const std::string text = plugin->get_docs();
685 Gtk::Label* label = manage(new Gtk::Label(text));
686 label->set_line_wrap(true);
687 label->set_line_wrap_mode(Pango::WRAP_WORD);
688 description_expander.add(*label);
689 description_expander.show_all();
692 if (!description_expander.get_expanded()) {
693 description_expander.remove();
699 PlugUIBase::toggle_plugin_analysis()
701 if (plugin_analysis_expander.get_expanded() &&
702 !plugin_analysis_expander.get_child()) {
705 eqgui = new PluginEqGui (insert);
708 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
711 toplevel->get_size (pre_eq_size.width, pre_eq_size.height);
714 plugin_analysis_expander.add (*eqgui);
715 plugin_analysis_expander.show_all ();
716 eqgui->start_listening ();
719 if (!plugin_analysis_expander.get_expanded()) {
720 // Hide & remove from expander
723 eqgui->stop_listening ();
724 plugin_analysis_expander.remove();
726 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
729 toplevel->resize (pre_eq_size.width, pre_eq_size.height);
735 PlugUIBase::update_preset_list ()
737 vector<string> preset_labels;
738 vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets();
742 for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator i = presets.begin(); i != presets.end(); ++i) {
743 preset_labels.push_back (i->label);
746 preset_labels.push_back("");
748 set_popdown_strings (_preset_combo, preset_labels);
754 PlugUIBase::update_preset ()
756 Plugin::PresetRecord p = plugin->last_preset();
759 _preset_combo.set_active_text (p.label);
762 save_button.set_sensitive (!p.uri.empty() && p.user);
763 delete_button.set_sensitive (!p.uri.empty() && p.user);
765 update_preset_modified ();
769 PlugUIBase::update_preset_modified ()
772 if (plugin->last_preset().uri.empty()) {
773 _preset_modified.set_text ("");
777 bool const c = plugin->parameter_changed_since_last_preset ();
778 if (_preset_modified.get_text().empty() == c) {
779 _preset_modified.set_text (c ? "*" : "");
784 PlugUIBase::parameter_changed (uint32_t, float)
786 update_preset_modified ();
790 PlugUIBase::preset_added_or_removed ()
792 /* Update both the list and the currently-displayed preset */
793 update_preset_list ();