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)
92 #ifdef AUDIOUNIT_SUPPORT
93 , pre_deactivate_x (-1)
94 , pre_deactivate_y (-1)
98 bool have_gui = false;
100 Label* label = manage (new Label());
101 label->set_markup ("<b>THIS IS THE PLUGIN UI</b>");
103 if (editor && insert->plugin()->has_editor()) {
104 switch (insert->type()) {
105 case ARDOUR::Windows_VST:
106 have_gui = create_windows_vst_editor (insert);
110 have_gui = create_lxvst_editor (insert);
113 case ARDOUR::AudioUnit:
114 have_gui = create_audiounit_editor (insert);
118 error << _("Eh? LADSPA plugins don't have editors!") << endmsg;
122 have_gui = create_lv2_editor (insert);
126 #ifndef WINDOWS_VST_SUPPORT
127 error << _("unknown type of editor-supplying plugin (note: no VST support in this version of ardour)")
130 error << _("unknown type of editor-supplying plugin")
133 throw failed_constructor ();
139 GenericPluginUI* pu = new GenericPluginUI (insert, scrollable);
142 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
144 set_wmclass (X_("ardour_plugin_editor"), PROGRAM_NAME);
146 signal_map_event().connect (sigc::mem_fun (*pu, &GenericPluginUI::start_updating));
147 signal_unmap_event().connect (sigc::mem_fun (*pu, &GenericPluginUI::stop_updating));
150 // set_position (Gtk::WIN_POS_MOUSE);
151 set_name ("PluginEditor");
152 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
154 signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), reinterpret_cast<Window*> (this)), false);
155 insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PluginUIWindow::plugin_going_away, this), gui_context());
157 gint h = _pluginui->get_preferred_height ();
158 gint w = _pluginui->get_preferred_width ();
161 if (h > 600) h = 600;
164 set_default_size (w, h);
165 set_resizable (_pluginui->resizable());
168 PluginUIWindow::~PluginUIWindow ()
174 PluginUIWindow::set_parent (Gtk::Window* win)
180 PluginUIWindow::on_map ()
183 set_keep_above (true);
187 PluginUIWindow::on_enter_notify_event (GdkEventCrossing *ev)
189 Keyboard::the_keyboard().enter_window (ev, this);
194 PluginUIWindow::on_leave_notify_event (GdkEventCrossing *ev)
196 Keyboard::the_keyboard().leave_window (ev, this);
201 PluginUIWindow::on_focus_in_event (GdkEventFocus *ev)
203 Window::on_focus_in_event (ev);
204 //Keyboard::the_keyboard().magic_widget_grab_focus ();
209 PluginUIWindow::on_focus_out_event (GdkEventFocus *ev)
211 Window::on_focus_out_event (ev);
212 //Keyboard::the_keyboard().magic_widget_drop_focus ();
217 PluginUIWindow::on_show ()
219 set_role("plugin_ui");
222 _pluginui->update_preset_list ();
223 _pluginui->update_preset ();
227 if (_pluginui->on_window_show (_title)) {
233 // set_transient_for (*parent);
238 PluginUIWindow::on_hide ()
243 _pluginui->on_window_hide ();
248 PluginUIWindow::set_title(const std::string& title)
250 Gtk::Window::set_title(title);
255 #ifdef WINDOWS_VST_SUPPORT
256 PluginUIWindow::create_windows_vst_editor(boost::shared_ptr<PluginInsert> insert)
258 PluginUIWindow::create_windows_vst_editor(boost::shared_ptr<PluginInsert>)
261 #ifndef WINDOWS_VST_SUPPORT
265 boost::shared_ptr<WindowsVSTPlugin> vp;
267 if ((vp = boost::dynamic_pointer_cast<WindowsVSTPlugin> (insert->plugin())) == 0) {
268 error << _("unknown type of editor-supplying plugin (note: no VST support in this version of ardour)")
270 throw failed_constructor ();
272 WindowsVSTPluginUI* vpu = new WindowsVSTPluginUI (insert, vp);
275 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
277 vpu->package (*this);
286 PluginUIWindow::create_lxvst_editor(boost::shared_ptr<PluginInsert> insert)
288 PluginUIWindow::create_lxvst_editor(boost::shared_ptr<PluginInsert>)
291 #ifndef LXVST_SUPPORT
295 boost::shared_ptr<LXVSTPlugin> lxvp;
297 if ((lxvp = boost::dynamic_pointer_cast<LXVSTPlugin> (insert->plugin())) == 0) {
298 error << _("unknown type of editor-supplying plugin (note: no linuxVST support in this version of ardour)")
300 throw failed_constructor ();
302 LXVSTPluginUI* lxvpu = new LXVSTPluginUI (insert, lxvp);
305 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
307 lxvpu->package (*this);
315 #ifdef AUDIOUNIT_SUPPORT
316 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert> insert)
318 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert>)
321 #ifndef AUDIOUNIT_SUPPORT
325 _pluginui = create_au_gui (insert, &box);
326 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
329 Application::instance()->ActivationChanged.connect (mem_fun (*this, &PluginUIWindow::app_activated));
337 PluginUIWindow::app_activated (bool yn)
339 PluginUIWindow::app_activated (bool)
342 #ifdef AUDIOUNIT_SUPPORT
346 _pluginui->activate ();
347 if (pre_deactivate_x >= 0) {
348 move (pre_deactivate_x, pre_deactivate_y);
354 was_visible = is_visible();
355 get_position (pre_deactivate_x, pre_deactivate_y);
357 _pluginui->deactivate ();
364 PluginUIWindow::create_lv2_editor(boost::shared_ptr<PluginInsert> insert)
367 boost::shared_ptr<LV2Plugin> vp;
369 if ((vp = boost::dynamic_pointer_cast<LV2Plugin> (insert->plugin())) == 0) {
370 error << _("create_lv2_editor called on non-LV2 plugin") << endmsg;
371 throw failed_constructor ();
373 LV2PluginUI* lpu = new LV2PluginUI (insert, vp);
376 lpu->package (*this);
386 PluginUIWindow::keyboard_focused (bool yn)
388 _keyboard_focused = yn;
392 PluginUIWindow::on_key_press_event (GdkEventKey* event)
394 if (_keyboard_focused) {
396 if (_pluginui->non_gtk_gui()) {
397 _pluginui->forward_key_event (event);
399 return relay_key_press (event, this);
404 /* for us to be getting key press events, there really
405 MUST be a _pluginui, but just to be safe, check ...
409 if (_pluginui->non_gtk_gui()) {
410 /* pass editor window as the window for the event
411 to be handled in, not this one, because there are
412 no widgets in this window that we want to have
415 return relay_key_press (event, &PublicEditor::instance());
417 return relay_key_press (event, this);
426 PluginUIWindow::on_key_release_event (GdkEventKey *event)
428 if (_keyboard_focused) {
430 if (_pluginui->non_gtk_gui()) {
431 _pluginui->forward_key_event (event);
442 PluginUIWindow::plugin_going_away ()
444 ENSURE_GUI_THREAD (*this, &PluginUIWindow::plugin_going_away)
447 _pluginui->stop_updating(0);
450 death_connection.disconnect ();
452 delete_when_idle (this);
455 PlugUIBase::PlugUIBase (boost::shared_ptr<PluginInsert> pi)
457 , plugin (insert->plugin())
458 , add_button (_("Add"))
459 , save_button (_("Save"))
460 , delete_button (_("Delete"))
461 , bypass_button (ArdourButton::led_default_elements)
462 , description_expander (_("Description"))
463 , plugin_analysis_expander (_("Plugin analysis"))
468 _preset_modified.set_size_request (16, -1);
469 _preset_combo.signal_changed().connect(sigc::mem_fun(*this, &PlugUIBase::preset_selected));
470 ARDOUR_UI::instance()->set_tip (_preset_combo, _("Presets (if any) for this plugin\n(Both factory and user-created)"));
471 ARDOUR_UI::instance()->set_tip (add_button, _("Save a new preset"));
472 ARDOUR_UI::instance()->set_tip (save_button, _("Save the current preset"));
473 ARDOUR_UI::instance()->set_tip (delete_button, _("Delete the current preset"));
474 ARDOUR_UI::instance()->set_tip (bypass_button, _("Disable signal processing by the plugin"));
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"));
493 bypass_button.set_active (!pi->active());
494 bypass_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::bypass_button_release));
495 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
497 focus_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::focus_toggled));
498 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
500 /* these images are not managed, so that we can remove them at will */
502 focus_out_image = new Image (get_icon (X_("computer_keyboard")));
503 focus_in_image = new Image (get_icon (X_("computer_keyboard_active")));
505 focus_button.add (*focus_out_image);
507 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));
508 ARDOUR_UI::instance()->set_tip (bypass_button, _("Click to enable/disable this plugin"));
510 description_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_description));
511 description_expander.set_expanded(false);
513 plugin_analysis_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_plugin_analysis));
514 plugin_analysis_expander.set_expanded(false);
516 insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PlugUIBase::plugin_going_away, this), gui_context());
518 plugin->PresetAdded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
519 plugin->PresetRemoved.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
520 plugin->PresetLoaded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset, this), gui_context ());
521 plugin->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::parameter_changed, this, _1, _2), gui_context ());
524 PlugUIBase::~PlugUIBase()
531 PlugUIBase::plugin_going_away ()
533 /* drop references to the plugin/insert */
539 PlugUIBase::set_latency_label ()
541 framecnt_t const l = insert->effective_latency ();
542 framecnt_t const sr = insert->session().frame_rate ();
547 t = string_compose (_("latency (%1 samples)"), l);
549 t = string_compose (_("latency (%1 ms)"), (float) l / ((float) sr / 1000.0f));
552 latency_label.set_text (t);
556 PlugUIBase::latency_button_clicked ()
559 latency_gui = new LatencyGUI (*(insert.get()), insert->session().frame_rate(), insert->session().get_block_size());
560 latency_dialog = new ArdourWindow (_("Edit Latency"));
561 latency_dialog->add (*latency_gui);
562 latency_dialog->signal_hide().connect (sigc::mem_fun (*this, &PlugUIBase::set_latency_label));
565 latency_dialog->show_all ();
569 PlugUIBase::processor_active_changed (boost::weak_ptr<Processor> weak_p)
571 ENSURE_GUI_THREAD (*this, &PlugUIBase::processor_active_changed, weak_p);
572 boost::shared_ptr<Processor> p (weak_p.lock());
575 bypass_button.set_active (!p->active());
580 PlugUIBase::preset_selected ()
582 if (_no_load_preset) {
586 if (_preset_combo.get_active_text().length() > 0) {
587 const Plugin::PresetRecord* pr = plugin->preset_by_label (_preset_combo.get_active_text());
589 plugin->load_preset (*pr);
591 warning << string_compose(_("Plugin preset %1 not found"),
592 _preset_combo.get_active_text()) << endmsg;
595 // blank selected = no preset
596 plugin->clear_preset();
601 PlugUIBase::add_plugin_setting ()
603 NewPluginPresetDialog d (plugin);
606 case Gtk::RESPONSE_ACCEPT:
607 if (d.name().empty()) {
612 plugin->remove_preset (d.name ());
615 Plugin::PresetRecord const r = plugin->save_preset (d.name());
616 if (!r.uri.empty ()) {
617 plugin->load_preset (r);
624 PlugUIBase::save_plugin_setting ()
626 string const name = _preset_combo.get_active_text ();
627 plugin->remove_preset (name);
628 Plugin::PresetRecord const r = plugin->save_preset (name);
629 if (!r.uri.empty ()) {
630 plugin->load_preset (r);
635 PlugUIBase::delete_plugin_setting ()
637 plugin->remove_preset (_preset_combo.get_active_text ());
641 PlugUIBase::bypass_button_release (GdkEventButton*)
643 bool view_says_bypassed = (bypass_button.active_state() != 0);
645 if (view_says_bypassed != insert->active()) {
646 if (view_says_bypassed) {
649 insert->deactivate ();
657 PlugUIBase::focus_toggled (GdkEventButton*)
659 if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
660 Keyboard::the_keyboard().magic_widget_drop_focus();
661 focus_button.remove ();
662 focus_button.add (*focus_out_image);
663 focus_out_image->show ();
664 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));
665 KeyboardFocused (false);
667 Keyboard::the_keyboard().magic_widget_grab_focus();
668 focus_button.remove ();
669 focus_button.add (*focus_in_image);
670 focus_in_image->show ();
671 ARDOUR_UI::instance()->set_tip (focus_button, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME));
672 KeyboardFocused (true);
679 PlugUIBase::toggle_description()
681 if (description_expander.get_expanded() &&
682 !description_expander.get_child()) {
683 const std::string text = plugin->get_docs();
688 Gtk::Label* label = manage(new Gtk::Label(text));
689 label->set_line_wrap(true);
690 label->set_line_wrap_mode(Pango::WRAP_WORD);
691 description_expander.add(*label);
692 description_expander.show_all();
695 if (!description_expander.get_expanded()) {
696 description_expander.remove();
702 PlugUIBase::toggle_plugin_analysis()
704 if (plugin_analysis_expander.get_expanded() &&
705 !plugin_analysis_expander.get_child()) {
708 eqgui = new PluginEqGui (insert);
711 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
714 toplevel->get_size (pre_eq_size.width, pre_eq_size.height);
717 plugin_analysis_expander.add (*eqgui);
718 plugin_analysis_expander.show_all ();
719 eqgui->start_listening ();
722 if (!plugin_analysis_expander.get_expanded()) {
723 // Hide & remove from expander
726 eqgui->stop_listening ();
727 plugin_analysis_expander.remove();
729 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
732 toplevel->resize (pre_eq_size.width, pre_eq_size.height);
738 PlugUIBase::update_preset_list ()
740 vector<string> preset_labels;
741 vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets();
745 for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator i = presets.begin(); i != presets.end(); ++i) {
746 preset_labels.push_back (i->label);
749 preset_labels.push_back("");
751 set_popdown_strings (_preset_combo, preset_labels);
757 PlugUIBase::update_preset ()
759 Plugin::PresetRecord p = plugin->last_preset();
762 _preset_combo.set_active_text (p.label);
765 save_button.set_sensitive (!p.uri.empty() && p.user);
766 delete_button.set_sensitive (!p.uri.empty() && p.user);
768 update_preset_modified ();
772 PlugUIBase::update_preset_modified ()
775 if (plugin->last_preset().uri.empty()) {
776 _preset_modified.set_text ("");
780 bool const c = plugin->parameter_changed_since_last_preset ();
781 if (_preset_modified.get_text().empty() == c) {
782 _preset_modified.set_text (c ? "*" : "");
787 PlugUIBase::parameter_changed (uint32_t, float)
789 update_preset_modified ();
793 PlugUIBase::preset_added_or_removed ()
795 /* Update both the list and the currently-displayed preset */
796 update_preset_list ();