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 std::cout << "SHOW UI " << insert->plugin()->unique_id()
104 << " editor: " << editor << std::endl;
105 if (editor && insert->plugin()->has_editor()) {
106 switch (insert->type()) {
107 case ARDOUR::Windows_VST:
108 have_gui = create_windows_vst_editor (insert);
112 have_gui = create_lxvst_editor (insert);
115 case ARDOUR::AudioUnit:
116 have_gui = create_audiounit_editor (insert);
120 error << _("Eh? LADSPA plugins don't have editors!") << endmsg;
124 have_gui = create_lv2_editor (insert);
128 #ifndef WINDOWS_VST_SUPPORT
129 error << _("unknown type of editor-supplying plugin (note: no VST support in this version of ardour)")
132 error << _("unknown type of editor-supplying plugin")
135 throw failed_constructor ();
141 GenericPluginUI* pu = new GenericPluginUI (insert, scrollable);
144 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
146 set_wmclass (X_("ardour_plugin_editor"), PROGRAM_NAME);
148 signal_map_event().connect (sigc::mem_fun (*pu, &GenericPluginUI::start_updating));
149 signal_unmap_event().connect (sigc::mem_fun (*pu, &GenericPluginUI::stop_updating));
152 // set_position (Gtk::WIN_POS_MOUSE);
153 set_name ("PluginEditor");
154 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
156 signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), reinterpret_cast<Window*> (this)), false);
157 insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PluginUIWindow::plugin_going_away, this), gui_context());
159 gint h = _pluginui->get_preferred_height ();
160 gint w = _pluginui->get_preferred_width ();
163 if (h > 600) h = 600;
166 set_default_size (w, h);
167 set_resizable (_pluginui->resizable());
170 PluginUIWindow::~PluginUIWindow ()
176 PluginUIWindow::set_parent (Gtk::Window* win)
182 PluginUIWindow::on_map ()
185 set_keep_above (true);
189 PluginUIWindow::on_enter_notify_event (GdkEventCrossing *ev)
191 Keyboard::the_keyboard().enter_window (ev, this);
196 PluginUIWindow::on_leave_notify_event (GdkEventCrossing *ev)
198 Keyboard::the_keyboard().leave_window (ev, this);
203 PluginUIWindow::on_focus_in_event (GdkEventFocus *ev)
205 Window::on_focus_in_event (ev);
206 //Keyboard::the_keyboard().magic_widget_grab_focus ();
211 PluginUIWindow::on_focus_out_event (GdkEventFocus *ev)
213 Window::on_focus_out_event (ev);
214 //Keyboard::the_keyboard().magic_widget_drop_focus ();
219 PluginUIWindow::on_show ()
221 set_role("plugin_ui");
224 _pluginui->update_preset_list ();
225 _pluginui->update_preset ();
229 if (_pluginui->on_window_show (_title)) {
235 // set_transient_for (*parent);
240 PluginUIWindow::on_hide ()
245 _pluginui->on_window_hide ();
250 PluginUIWindow::set_title(const std::string& title)
252 Gtk::Window::set_title(title);
257 #ifdef WINDOWS_VST_SUPPORT
258 PluginUIWindow::create_windows_vst_editor(boost::shared_ptr<PluginInsert> insert)
260 PluginUIWindow::create_windows_vst_editor(boost::shared_ptr<PluginInsert>)
263 #ifndef WINDOWS_VST_SUPPORT
267 boost::shared_ptr<WindowsVSTPlugin> vp;
269 if ((vp = boost::dynamic_pointer_cast<WindowsVSTPlugin> (insert->plugin())) == 0) {
270 error << _("unknown type of editor-supplying plugin (note: no VST support in this version of ardour)")
272 throw failed_constructor ();
274 WindowsVSTPluginUI* vpu = new WindowsVSTPluginUI (insert, vp);
277 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
279 vpu->package (*this);
288 PluginUIWindow::create_lxvst_editor(boost::shared_ptr<PluginInsert> insert)
290 PluginUIWindow::create_lxvst_editor(boost::shared_ptr<PluginInsert>)
293 #ifndef LXVST_SUPPORT
297 boost::shared_ptr<LXVSTPlugin> lxvp;
299 if ((lxvp = boost::dynamic_pointer_cast<LXVSTPlugin> (insert->plugin())) == 0) {
300 error << _("unknown type of editor-supplying plugin (note: no linuxVST support in this version of ardour)")
302 throw failed_constructor ();
304 LXVSTPluginUI* lxvpu = new LXVSTPluginUI (insert, lxvp);
307 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
309 lxvpu->package (*this);
317 #ifdef AUDIOUNIT_SUPPORT
318 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert> insert)
320 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert>)
323 #ifndef AUDIOUNIT_SUPPORT
327 _pluginui = create_au_gui (insert, &box);
328 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
331 Application::instance()->ActivationChanged.connect (mem_fun (*this, &PluginUIWindow::app_activated));
339 PluginUIWindow::app_activated (bool yn)
341 PluginUIWindow::app_activated (bool)
344 #ifdef AUDIOUNIT_SUPPORT
348 _pluginui->activate ();
349 if (pre_deactivate_x >= 0) {
350 move (pre_deactivate_x, pre_deactivate_y);
356 was_visible = is_visible();
357 get_position (pre_deactivate_x, pre_deactivate_y);
359 _pluginui->deactivate ();
366 PluginUIWindow::create_lv2_editor(boost::shared_ptr<PluginInsert> insert)
369 boost::shared_ptr<LV2Plugin> vp;
371 if ((vp = boost::dynamic_pointer_cast<LV2Plugin> (insert->plugin())) == 0) {
372 error << _("create_lv2_editor called on non-LV2 plugin") << endmsg;
373 throw failed_constructor ();
375 LV2PluginUI* lpu = new LV2PluginUI (insert, vp);
378 lpu->package (*this);
388 PluginUIWindow::keyboard_focused (bool yn)
390 _keyboard_focused = yn;
394 PluginUIWindow::on_key_press_event (GdkEventKey* event)
396 if (_keyboard_focused) {
398 if (_pluginui->non_gtk_gui()) {
399 _pluginui->forward_key_event (event);
401 return relay_key_press (event, this);
406 /* for us to be getting key press events, there really
407 MUST be a _pluginui, but just to be safe, check ...
411 if (_pluginui->non_gtk_gui()) {
412 /* pass editor window as the window for the event
413 to be handled in, not this one, because there are
414 no widgets in this window that we want to have
417 return relay_key_press (event, &PublicEditor::instance());
419 return relay_key_press (event, this);
428 PluginUIWindow::on_key_release_event (GdkEventKey *event)
430 if (_keyboard_focused) {
432 if (_pluginui->non_gtk_gui()) {
433 _pluginui->forward_key_event (event);
444 PluginUIWindow::plugin_going_away ()
446 ENSURE_GUI_THREAD (*this, &PluginUIWindow::plugin_going_away)
449 _pluginui->stop_updating(0);
452 death_connection.disconnect ();
454 delete_when_idle (this);
457 PlugUIBase::PlugUIBase (boost::shared_ptr<PluginInsert> pi)
459 , plugin (insert->plugin())
460 , add_button (_("Add"))
461 , save_button (_("Save"))
462 , delete_button (_("Delete"))
463 , bypass_button (ArdourButton::led_default_elements)
464 , description_expander (_("Description"))
465 , plugin_analysis_expander (_("Plugin analysis"))
470 _preset_modified.set_size_request (16, -1);
471 _preset_combo.signal_changed().connect(sigc::mem_fun(*this, &PlugUIBase::preset_selected));
472 ARDOUR_UI::instance()->set_tip (_preset_combo, _("Presets (if any) for this plugin\n(Both factory and user-created)"));
473 ARDOUR_UI::instance()->set_tip (add_button, _("Save a new preset"));
474 ARDOUR_UI::instance()->set_tip (save_button, _("Save the current preset"));
475 ARDOUR_UI::instance()->set_tip (delete_button, _("Delete the current preset"));
476 ARDOUR_UI::instance()->set_tip (bypass_button, _("Disable signal processing by the plugin"));
479 update_preset_list ();
482 add_button.set_name ("PluginAddButton");
483 add_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::add_plugin_setting));
485 save_button.set_name ("PluginSaveButton");
486 save_button.signal_clicked().connect(sigc::mem_fun(*this, &PlugUIBase::save_plugin_setting));
488 delete_button.set_name ("PluginDeleteButton");
489 delete_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::delete_plugin_setting));
491 insert->ActiveChanged.connect (active_connection, invalidator (*this), boost::bind (&PlugUIBase::processor_active_changed, this, boost::weak_ptr<Processor>(insert)), gui_context());
493 bypass_button.set_name ("plugin bypass button");
494 bypass_button.set_text (_("Bypass"));
495 bypass_button.set_active (!pi->active());
496 bypass_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::bypass_button_release));
497 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
499 focus_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::focus_toggled));
500 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
502 /* these images are not managed, so that we can remove them at will */
504 focus_out_image = new Image (get_icon (X_("computer_keyboard")));
505 focus_in_image = new Image (get_icon (X_("computer_keyboard_active")));
507 focus_button.add (*focus_out_image);
509 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));
510 ARDOUR_UI::instance()->set_tip (bypass_button, _("Click to enable/disable this plugin"));
512 description_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_description));
513 description_expander.set_expanded(false);
515 plugin_analysis_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_plugin_analysis));
516 plugin_analysis_expander.set_expanded(false);
518 insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PlugUIBase::plugin_going_away, this), gui_context());
520 plugin->PresetAdded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
521 plugin->PresetRemoved.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
522 plugin->PresetLoaded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset, this), gui_context ());
523 plugin->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::parameter_changed, this, _1, _2), gui_context ());
526 PlugUIBase::~PlugUIBase()
533 PlugUIBase::plugin_going_away ()
535 /* drop references to the plugin/insert */
541 PlugUIBase::set_latency_label ()
543 framecnt_t const l = insert->effective_latency ();
544 framecnt_t const sr = insert->session().frame_rate ();
549 t = string_compose (_("latency (%1 samples)"), l);
551 t = string_compose (_("latency (%1 ms)"), (float) l / ((float) sr / 1000.0f));
554 latency_label.set_text (t);
558 PlugUIBase::latency_button_clicked ()
561 latency_gui = new LatencyGUI (*(insert.get()), insert->session().frame_rate(), insert->session().get_block_size());
562 latency_dialog = new ArdourWindow (_("Edit Latency"));
563 latency_dialog->add (*latency_gui);
564 latency_dialog->signal_hide().connect (sigc::mem_fun (*this, &PlugUIBase::set_latency_label));
567 latency_dialog->show_all ();
571 PlugUIBase::processor_active_changed (boost::weak_ptr<Processor> weak_p)
573 ENSURE_GUI_THREAD (*this, &PlugUIBase::processor_active_changed, weak_p);
574 boost::shared_ptr<Processor> p (weak_p.lock());
577 bypass_button.set_active (!p->active());
582 PlugUIBase::preset_selected ()
584 if (_no_load_preset) {
588 if (_preset_combo.get_active_text().length() > 0) {
589 const Plugin::PresetRecord* pr = plugin->preset_by_label (_preset_combo.get_active_text());
591 plugin->load_preset (*pr);
593 warning << string_compose(_("Plugin preset %1 not found"),
594 _preset_combo.get_active_text()) << endmsg;
600 PlugUIBase::add_plugin_setting ()
602 NewPluginPresetDialog d (plugin);
605 case Gtk::RESPONSE_ACCEPT:
606 if (d.name().empty()) {
611 plugin->remove_preset (d.name ());
614 Plugin::PresetRecord const r = plugin->save_preset (d.name());
615 if (!r.uri.empty ()) {
616 plugin->load_preset (r);
623 PlugUIBase::save_plugin_setting ()
625 string const name = _preset_combo.get_active_text ();
626 plugin->remove_preset (name);
627 Plugin::PresetRecord const r = plugin->save_preset (name);
628 if (!r.uri.empty ()) {
629 plugin->load_preset (r);
634 PlugUIBase::delete_plugin_setting ()
636 plugin->remove_preset (_preset_combo.get_active_text ());
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 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 ()
771 if (plugin->last_preset().uri.empty()) {
772 _preset_modified.set_text ("");
776 bool const c = plugin->parameter_changed_since_last_preset ();
777 if (_preset_modified.get_text().empty() == c) {
778 _preset_modified.set_text (c ? "*" : "");
783 PlugUIBase::parameter_changed (uint32_t, float)
785 update_preset_modified ();
789 PlugUIBase::preset_added_or_removed ()
791 /* Update both the list and the currently-displayed preset */
792 update_preset_list ();