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)
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()) {
102 case ARDOUR::Windows_VST:
103 have_gui = create_windows_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);
123 #ifndef WINDOWS_VST_SUPPORT
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;
161 set_default_size (w, h);
162 set_resizable (_pluginui->resizable());
165 PluginUIWindow::~PluginUIWindow ()
171 PluginUIWindow::set_parent (Gtk::Window* win)
177 PluginUIWindow::on_map ()
180 set_keep_above (true);
184 PluginUIWindow::on_enter_notify_event (GdkEventCrossing *ev)
186 Keyboard::the_keyboard().enter_window (ev, this);
191 PluginUIWindow::on_leave_notify_event (GdkEventCrossing *ev)
193 Keyboard::the_keyboard().leave_window (ev, this);
198 PluginUIWindow::on_focus_in_event (GdkEventFocus *ev)
200 Window::on_focus_in_event (ev);
201 //Keyboard::the_keyboard().magic_widget_grab_focus ();
206 PluginUIWindow::on_focus_out_event (GdkEventFocus *ev)
208 Window::on_focus_out_event (ev);
209 //Keyboard::the_keyboard().magic_widget_drop_focus ();
214 PluginUIWindow::on_show ()
216 set_role("plugin_ui");
219 _pluginui->update_preset_list ();
220 _pluginui->update_preset ();
224 if (_pluginui->on_window_show (_title)) {
230 // set_transient_for (*parent);
235 PluginUIWindow::on_hide ()
240 _pluginui->on_window_hide ();
245 PluginUIWindow::set_title(const std::string& title)
247 Gtk::Window::set_title(title);
252 #ifdef WINDOWS_VST_SUPPORT
253 PluginUIWindow::create_windows_vst_editor(boost::shared_ptr<PluginInsert> insert)
255 PluginUIWindow::create_windows_vst_editor(boost::shared_ptr<PluginInsert>)
258 #ifndef WINDOWS_VST_SUPPORT
262 boost::shared_ptr<WindowsVSTPlugin> vp;
264 if ((vp = boost::dynamic_pointer_cast<WindowsVSTPlugin> (insert->plugin())) == 0) {
265 error << _("unknown type of editor-supplying plugin (note: no VST support in this version of ardour)")
267 throw failed_constructor ();
269 WindowsVSTPluginUI* vpu = new WindowsVSTPluginUI (insert, vp);
272 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
274 vpu->package (*this);
283 PluginUIWindow::create_lxvst_editor(boost::shared_ptr<PluginInsert> insert)
285 PluginUIWindow::create_lxvst_editor(boost::shared_ptr<PluginInsert>)
288 #ifndef LXVST_SUPPORT
292 boost::shared_ptr<LXVSTPlugin> lxvp;
294 if ((lxvp = boost::dynamic_pointer_cast<LXVSTPlugin> (insert->plugin())) == 0) {
295 error << _("unknown type of editor-supplying plugin (note: no linuxVST support in this version of ardour)")
297 throw failed_constructor ();
299 LXVSTPluginUI* lxvpu = new LXVSTPluginUI (insert, lxvp);
302 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
304 lxvpu->package (*this);
312 #ifdef AUDIOUNIT_SUPPORT
313 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert> insert)
315 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert>)
318 #ifndef AUDIOUNIT_SUPPORT
322 _pluginui = create_au_gui (insert, &box);
323 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
326 Application::instance()->ActivationChanged.connect (mem_fun (*this, &PluginUIWindow::app_activated));
334 PluginUIWindow::app_activated (bool yn)
336 PluginUIWindow::app_activated (bool)
339 #ifdef AUDIOUNIT_SUPPORT
343 _pluginui->activate ();
348 was_visible = is_visible();
350 _pluginui->deactivate ();
357 PluginUIWindow::create_lv2_editor(boost::shared_ptr<PluginInsert> insert)
360 boost::shared_ptr<LV2Plugin> vp;
362 if ((vp = boost::dynamic_pointer_cast<LV2Plugin> (insert->plugin())) == 0) {
363 error << _("create_lv2_editor called on non-LV2 plugin") << endmsg;
364 throw failed_constructor ();
366 LV2PluginUI* lpu = new LV2PluginUI (insert, vp);
369 lpu->package (*this);
379 PluginUIWindow::keyboard_focused (bool yn)
381 _keyboard_focused = yn;
385 PluginUIWindow::on_key_press_event (GdkEventKey* event)
387 if (_keyboard_focused) {
389 if (_pluginui->non_gtk_gui()) {
390 _pluginui->forward_key_event (event);
392 return relay_key_press (event, this);
397 /* for us to be getting key press events, there really
398 MUST be a _pluginui, but just to be safe, check ...
402 if (_pluginui->non_gtk_gui()) {
403 /* pass editor window as the window for the event
404 to be handled in, not this one, because there are
405 no widgets in this window that we want to have
408 return relay_key_press (event, &PublicEditor::instance());
410 return relay_key_press (event, this);
419 PluginUIWindow::on_key_release_event (GdkEventKey *event)
421 if (_keyboard_focused) {
423 if (_pluginui->non_gtk_gui()) {
424 _pluginui->forward_key_event (event);
435 PluginUIWindow::plugin_going_away ()
437 ENSURE_GUI_THREAD (*this, &PluginUIWindow::plugin_going_away)
440 _pluginui->stop_updating(0);
443 death_connection.disconnect ();
445 delete_when_idle (this);
448 PlugUIBase::PlugUIBase (boost::shared_ptr<PluginInsert> pi)
450 , plugin (insert->plugin())
451 , add_button (_("Add"))
452 , save_button (_("Save"))
453 , delete_button (_("Delete"))
454 , bypass_button (ArdourButton::led_default_elements)
455 , description_expander (_("Description"))
456 , plugin_analysis_expander (_("Plugin analysis"))
461 _preset_modified.set_size_request (16, -1);
462 _preset_combo.signal_changed().connect(sigc::mem_fun(*this, &PlugUIBase::preset_selected));
463 ARDOUR_UI::instance()->set_tip (_preset_combo, _("Presets (if any) for this plugin\n(Both factory and user-created)"));
464 ARDOUR_UI::instance()->set_tip (add_button, _("Save a new preset"));
465 ARDOUR_UI::instance()->set_tip (save_button, _("Save the current preset"));
466 ARDOUR_UI::instance()->set_tip (delete_button, _("Delete the current preset"));
467 ARDOUR_UI::instance()->set_tip (bypass_button, _("Disable signal processing by the plugin"));
470 update_preset_list ();
473 add_button.set_name ("PluginAddButton");
474 add_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::add_plugin_setting));
476 save_button.set_name ("PluginSaveButton");
477 save_button.signal_clicked().connect(sigc::mem_fun(*this, &PlugUIBase::save_plugin_setting));
479 delete_button.set_name ("PluginDeleteButton");
480 delete_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::delete_plugin_setting));
482 insert->ActiveChanged.connect (active_connection, invalidator (*this), boost::bind (&PlugUIBase::processor_active_changed, this, boost::weak_ptr<Processor>(insert)), gui_context());
484 bypass_button.set_name ("plugin bypass button");
485 bypass_button.set_text (_("Bypass"));
486 bypass_button.set_active (!pi->active());
487 bypass_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::bypass_button_release));
488 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
490 focus_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::focus_toggled));
491 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
493 /* these images are not managed, so that we can remove them at will */
495 focus_out_image = new Image (get_icon (X_("computer_keyboard")));
496 focus_in_image = new Image (get_icon (X_("computer_keyboard_active")));
498 focus_button.add (*focus_out_image);
500 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));
501 ARDOUR_UI::instance()->set_tip (bypass_button, _("Click to enable/disable this plugin"));
503 description_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_description));
504 description_expander.set_expanded(false);
506 plugin_analysis_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_plugin_analysis));
507 plugin_analysis_expander.set_expanded(false);
509 insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PlugUIBase::plugin_going_away, this), gui_context());
511 plugin->PresetAdded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
512 plugin->PresetRemoved.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
513 plugin->PresetLoaded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset, this), gui_context ());
514 plugin->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::parameter_changed, this, _1, _2), gui_context ());
517 PlugUIBase::~PlugUIBase()
524 PlugUIBase::plugin_going_away ()
526 /* drop references to the plugin/insert */
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 ArdourWindow (_("Edit Latency"));
554 latency_dialog->add (*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.lock());
568 bypass_button.set_active (!p->active());
573 PlugUIBase::preset_selected ()
575 if (_no_load_preset) {
579 if (_preset_combo.get_active_text().length() > 0) {
580 const Plugin::PresetRecord* pr = plugin->preset_by_label (_preset_combo.get_active_text());
582 plugin->load_preset (*pr);
584 warning << string_compose(_("Plugin preset %1 not found"),
585 _preset_combo.get_active_text()) << endmsg;
591 PlugUIBase::add_plugin_setting ()
593 NewPluginPresetDialog d (plugin);
596 case Gtk::RESPONSE_ACCEPT:
597 if (d.name().empty()) {
602 plugin->remove_preset (d.name ());
605 Plugin::PresetRecord const r = plugin->save_preset (d.name());
606 if (!r.uri.empty ()) {
607 plugin->load_preset (r);
614 PlugUIBase::save_plugin_setting ()
616 string const name = _preset_combo.get_active_text ();
617 plugin->remove_preset (name);
618 Plugin::PresetRecord const r = plugin->save_preset (name);
619 if (!r.uri.empty ()) {
620 plugin->load_preset (r);
625 PlugUIBase::delete_plugin_setting ()
627 plugin->remove_preset (_preset_combo.get_active_text ());
631 PlugUIBase::bypass_button_release (GdkEventButton*)
633 bool view_says_bypassed = (bypass_button.active_state() != 0);
635 if (view_says_bypassed != insert->active()) {
636 if (view_says_bypassed) {
639 insert->deactivate ();
647 PlugUIBase::focus_toggled (GdkEventButton*)
649 if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
650 Keyboard::the_keyboard().magic_widget_drop_focus();
651 focus_button.remove ();
652 focus_button.add (*focus_out_image);
653 focus_out_image->show ();
654 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));
655 KeyboardFocused (false);
657 Keyboard::the_keyboard().magic_widget_grab_focus();
658 focus_button.remove ();
659 focus_button.add (*focus_in_image);
660 focus_in_image->show ();
661 ARDOUR_UI::instance()->set_tip (focus_button, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME));
662 KeyboardFocused (true);
669 PlugUIBase::toggle_description()
671 if (description_expander.get_expanded() &&
672 !description_expander.get_child()) {
673 const std::string text = plugin->get_docs();
678 Gtk::Label* label = manage(new Gtk::Label(text));
679 label->set_line_wrap(true);
680 label->set_line_wrap_mode(Pango::WRAP_WORD);
681 description_expander.add(*label);
682 description_expander.show_all();
685 if (!description_expander.get_expanded()) {
686 description_expander.remove();
692 PlugUIBase::toggle_plugin_analysis()
694 if (plugin_analysis_expander.get_expanded() &&
695 !plugin_analysis_expander.get_child()) {
698 eqgui = new PluginEqGui (insert);
701 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
704 toplevel->get_size (pre_eq_size.width, pre_eq_size.height);
707 plugin_analysis_expander.add (*eqgui);
708 plugin_analysis_expander.show_all ();
709 eqgui->start_listening ();
712 if (!plugin_analysis_expander.get_expanded()) {
713 // Hide & remove from expander
716 eqgui->stop_listening ();
717 plugin_analysis_expander.remove();
719 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
722 toplevel->resize (pre_eq_size.width, pre_eq_size.height);
728 PlugUIBase::update_preset_list ()
730 vector<string> preset_labels;
731 vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets();
735 for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator i = presets.begin(); i != presets.end(); ++i) {
736 preset_labels.push_back (i->label);
739 set_popdown_strings (_preset_combo, preset_labels);
745 PlugUIBase::update_preset ()
747 Plugin::PresetRecord p = plugin->last_preset();
750 _preset_combo.set_active_text (p.label);
753 save_button.set_sensitive (!p.uri.empty() && p.user);
754 delete_button.set_sensitive (!p.uri.empty() && p.user);
756 update_preset_modified ();
760 PlugUIBase::update_preset_modified ()
762 if (plugin->last_preset().uri.empty()) {
763 _preset_modified.set_text ("");
767 bool const c = plugin->parameter_changed_since_last_preset ();
768 if (_preset_modified.get_text().empty() == c) {
769 _preset_modified.set_text (c ? "*" : "");
774 PlugUIBase::parameter_changed (uint32_t, float)
776 update_preset_modified ();
780 PlugUIBase::preset_added_or_removed ()
782 /* Update both the list and the currently-displayed preset */
783 update_preset_list ();