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"
50 #include "ardour/vst_plugin.h"
51 #include "vst_pluginui.h"
54 #include "ardour/lv2_plugin.h"
55 #include "lv2_plugin_ui.h"
60 #include "ardour_dialog.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;
77 using namespace Gtkmm2ext;
80 PluginUIWindow::PluginUIWindow (Gtk::Window* win, boost::shared_ptr<PluginInsert> insert, bool scrollable)
83 , _keyboard_focused (false)
85 bool have_gui = false;
87 Label* label = manage (new Label());
88 label->set_markup ("<b>THIS IS THE PLUGIN UI</b>");
90 if (insert->plugin()->has_editor()) {
91 switch (insert->type()) {
93 have_gui = create_vst_editor (insert);
96 case ARDOUR::AudioUnit:
97 have_gui = create_audiounit_editor (insert);
101 error << _("Eh? LADSPA plugins don't have editors!") << endmsg;
105 have_gui = create_lv2_editor (insert);
110 error << _("unknown type of editor-supplying plugin (note: no VST support in this version of ardour)")
113 error << _("unknown type of editor-supplying plugin")
116 throw failed_constructor ();
123 GenericPluginUI* pu = new GenericPluginUI (insert, scrollable);
126 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
128 set_wmclass (X_("ardour_plugin_editor"), PROGRAM_NAME);
130 signal_map_event().connect (sigc::mem_fun (*pu, &GenericPluginUI::start_updating));
131 signal_unmap_event().connect (sigc::mem_fun (*pu, &GenericPluginUI::stop_updating));
134 // set_position (Gtk::WIN_POS_MOUSE);
135 set_name ("PluginEditor");
136 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
138 signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), reinterpret_cast<Window*> (this)), false);
139 insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PluginUIWindow::plugin_going_away, this), gui_context());
141 gint h = _pluginui->get_preferred_height ();
142 gint w = _pluginui->get_preferred_width ();
145 if (h > 600) h = 600;
146 if (w > 600) w = 600;
153 set_default_size (w, h);
156 PluginUIWindow::~PluginUIWindow ()
162 PluginUIWindow::set_parent (Gtk::Window* win)
168 PluginUIWindow::on_map ()
171 set_keep_above (true);
175 PluginUIWindow::on_enter_notify_event (GdkEventCrossing *ev)
177 Keyboard::the_keyboard().enter_window (ev, this);
182 PluginUIWindow::on_leave_notify_event (GdkEventCrossing *ev)
184 Keyboard::the_keyboard().leave_window (ev, this);
189 PluginUIWindow::on_focus_in_event (GdkEventFocus *ev)
191 Window::on_focus_in_event (ev);
192 //Keyboard::the_keyboard().magic_widget_grab_focus ();
197 PluginUIWindow::on_focus_out_event (GdkEventFocus *ev)
199 Window::on_focus_out_event (ev);
200 //Keyboard::the_keyboard().magic_widget_drop_focus ();
205 PluginUIWindow::on_show ()
207 set_role("plugin_ui");
210 _pluginui->update_preset_list ();
211 _pluginui->update_preset ();
215 if (_pluginui->on_window_show (_title)) {
221 // set_transient_for (*parent);
226 PluginUIWindow::on_hide ()
231 _pluginui->on_window_hide ();
236 PluginUIWindow::set_title(const std::string& title)
238 Gtk::Window::set_title(title);
244 PluginUIWindow::create_vst_editor(boost::shared_ptr<PluginInsert> insert)
246 PluginUIWindow::create_vst_editor(boost::shared_ptr<PluginInsert>)
253 boost::shared_ptr<VSTPlugin> vp;
255 if ((vp = boost::dynamic_pointer_cast<VSTPlugin> (insert->plugin())) == 0) {
256 error << _("unknown type of editor-supplying plugin (note: no VST support in this version of ardour)")
258 throw failed_constructor ();
260 VSTPluginUI* vpu = new VSTPluginUI (insert, vp);
263 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
265 vpu->package (*this);
274 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert> insert)
276 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert>)
283 _pluginui = create_au_gui (insert, &box);
284 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
287 Application::instance()->ActivationChanged.connect (mem_fun (*this, &PluginUIWindow::app_activated));
295 PluginUIWindow::app_activated (bool yn)
297 PluginUIWindow::app_activated (bool)
304 _pluginui->activate ();
309 was_visible = is_visible();
311 _pluginui->deactivate ();
318 PluginUIWindow::create_lv2_editor(boost::shared_ptr<PluginInsert> insert)
324 boost::shared_ptr<LV2Plugin> vp;
326 if ((vp = boost::dynamic_pointer_cast<LV2Plugin> (insert->plugin())) == 0) {
327 error << _("create_lv2_editor called on non-LV2 plugin") << endmsg;
328 throw failed_constructor ();
330 LV2PluginUI* lpu = new LV2PluginUI (insert, vp);
333 lpu->package (*this);
341 PluginUIWindow::keyboard_focused (bool yn)
343 _keyboard_focused = yn;
347 PluginUIWindow::on_key_press_event (GdkEventKey* event)
349 if (_keyboard_focused) {
351 if (_pluginui->non_gtk_gui()) {
352 _pluginui->forward_key_event (event);
354 return relay_key_press (event, this);
359 /* for us to be getting key press events, there really
360 MUST be a _pluginui, but just to be safe, check ...
364 if (_pluginui->non_gtk_gui()) {
365 /* pass editor window as the window for the event
366 to be handled in, not this one, because there are
367 no widgets in this window that we want to have
370 return relay_key_press (event, &PublicEditor::instance());
372 return relay_key_press (event, this);
381 PluginUIWindow::on_key_release_event (GdkEventKey *event)
383 if (_keyboard_focused) {
385 if (_pluginui->non_gtk_gui()) {
386 _pluginui->forward_key_event (event);
397 PluginUIWindow::plugin_going_away ()
399 ENSURE_GUI_THREAD (*this, &PluginUIWindow::plugin_going_away)
402 _pluginui->stop_updating(0);
405 death_connection.disconnect ();
407 delete_when_idle (this);
410 PlugUIBase::PlugUIBase (boost::shared_ptr<PluginInsert> pi)
412 , plugin (insert->plugin())
413 , add_button (_("Add"))
414 , save_button (_("Save"))
415 , delete_button (_("Delete"))
416 , bypass_button (_("Bypass"))
419 , plugin_analysis_expander (_("Plugin analysis"))
422 _preset_combo.set_size_request (100, -1);
423 _preset_modified.set_size_request (16, -1);
424 _preset_combo.signal_changed().connect(sigc::mem_fun(*this, &PlugUIBase::preset_selected));
427 _preset_box.pack_start (_preset_combo);
428 _preset_box.pack_start (_preset_modified);
430 update_preset_list ();
433 add_button.set_name ("PluginAddButton");
434 add_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::add_plugin_setting));
436 save_button.set_name ("PluginSaveButton");
437 save_button.signal_clicked().connect(sigc::mem_fun(*this, &PlugUIBase::save_plugin_setting));
439 delete_button.set_name ("PluginDeleteButton");
440 delete_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::delete_plugin_setting));
442 insert->ActiveChanged.connect (active_connection, invalidator (*this), boost::bind (&PlugUIBase::processor_active_changed, this, boost::weak_ptr<Processor>(insert)), gui_context());
444 bypass_button.set_active (!pi->active());
446 bypass_button.set_name ("PluginBypassButton");
447 bypass_button.signal_toggled().connect (sigc::mem_fun(*this, &PlugUIBase::bypass_toggled));
448 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
450 focus_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::focus_toggled));
451 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
453 /* these images are not managed, so that we can remove them at will */
455 focus_out_image = new Image (get_icon (X_("computer_keyboard")));
456 focus_in_image = new Image (get_icon (X_("computer_keyboard_active")));
458 focus_button.add (*focus_out_image);
460 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));
461 ARDOUR_UI::instance()->set_tip (bypass_button, _("Click to enable/disable this plugin"));
463 plugin_analysis_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_plugin_analysis));
464 plugin_analysis_expander.set_expanded(false);
466 insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PlugUIBase::plugin_going_away, this), gui_context());
468 plugin->PresetAdded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
469 plugin->PresetRemoved.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
470 plugin->PresetLoaded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset, this), gui_context ());
471 plugin->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::parameter_changed, this, _1, _2), gui_context ());
474 PlugUIBase::~PlugUIBase()
481 PlugUIBase::plugin_going_away ()
483 /* drop references to the plugin/insert */
486 death_connection.disconnect ();
490 PlugUIBase::set_latency_label ()
492 framecnt_t const l = insert->effective_latency ();
493 framecnt_t const sr = insert->session().frame_rate ();
498 t = string_compose (_("latency (%1 samples)"), l);
500 t = string_compose (_("latency (%1 ms)"), (float) l / ((float) sr / 1000.0f));
503 latency_label.set_text (t);
507 PlugUIBase::latency_button_clicked ()
510 latency_gui = new LatencyGUI (*(insert.get()), insert->session().frame_rate(), insert->session().get_block_size());
511 latency_dialog = new ArdourDialog (_("Edit Latency"), false, false);
512 latency_dialog->get_vbox()->pack_start (*latency_gui);
513 latency_dialog->signal_hide().connect (sigc::mem_fun (*this, &PlugUIBase::set_latency_label));
516 latency_dialog->show_all ();
520 PlugUIBase::processor_active_changed (boost::weak_ptr<Processor> weak_p)
522 ENSURE_GUI_THREAD (*this, &PlugUIBase::processor_active_changed, weak_p)
523 boost::shared_ptr<Processor> p (weak_p);
525 bypass_button.set_active (!p->active());
530 PlugUIBase::preset_selected ()
532 if (_no_load_preset) {
536 if (_preset_combo.get_active_text().length() > 0) {
537 const Plugin::PresetRecord* pr = plugin->preset_by_label (_preset_combo.get_active_text());
539 plugin->load_preset (*pr);
541 warning << string_compose(_("Plugin preset %1 not found"),
542 _preset_combo.get_active_text()) << endmsg;
548 PlugUIBase::add_plugin_setting ()
550 NewPluginPresetDialog d (plugin);
553 case Gtk::RESPONSE_ACCEPT:
554 if (d.name().empty()) {
559 plugin->remove_preset (d.name ());
562 Plugin::PresetRecord const r = plugin->save_preset (d.name());
563 if (!r.uri.empty ()) {
564 plugin->load_preset (r);
571 PlugUIBase::save_plugin_setting ()
573 string const name = _preset_combo.get_active_text ();
574 plugin->remove_preset (name);
575 Plugin::PresetRecord const r = plugin->save_preset (name);
576 if (!r.uri.empty ()) {
577 plugin->load_preset (r);
582 PlugUIBase::delete_plugin_setting ()
584 plugin->remove_preset (_preset_combo.get_active_text ());
588 PlugUIBase::bypass_toggled ()
592 if ((x = bypass_button.get_active()) == insert->active()) {
594 insert->deactivate ();
602 PlugUIBase::focus_toggled (GdkEventButton*)
604 if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
605 Keyboard::the_keyboard().magic_widget_drop_focus();
606 focus_button.remove ();
607 focus_button.add (*focus_out_image);
608 focus_out_image->show ();
609 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));
610 KeyboardFocused (false);
612 Keyboard::the_keyboard().magic_widget_grab_focus();
613 focus_button.remove ();
614 focus_button.add (*focus_in_image);
615 focus_in_image->show ();
616 ARDOUR_UI::instance()->set_tip (focus_button, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME));
617 KeyboardFocused (true);
624 PlugUIBase::toggle_plugin_analysis()
626 if (plugin_analysis_expander.get_expanded() &&
627 !plugin_analysis_expander.get_child()) {
630 eqgui = new PluginEqGui (insert);
633 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
636 toplevel->get_size (pre_eq_size.width, pre_eq_size.height);
639 plugin_analysis_expander.add (*eqgui);
640 plugin_analysis_expander.show_all ();
641 eqgui->start_listening ();
644 if (!plugin_analysis_expander.get_expanded()) {
646 // Hide & remove from expander
649 eqgui->stop_listening ();
650 plugin_analysis_expander.remove();
652 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
655 toplevel->resize (pre_eq_size.width, pre_eq_size.height);
661 PlugUIBase::update_preset_list ()
663 vector<string> preset_labels;
664 vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets();
668 for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator i = presets.begin(); i != presets.end(); ++i) {
669 preset_labels.push_back (i->label);
672 set_popdown_strings (_preset_combo, preset_labels);
678 PlugUIBase::update_preset ()
680 Plugin::PresetRecord p = plugin->last_preset();
683 _preset_combo.set_active_text (p.label);
686 save_button.set_sensitive (!p.uri.empty() && p.user);
687 delete_button.set_sensitive (!p.uri.empty() && p.user);
689 update_preset_modified ();
693 PlugUIBase::update_preset_modified ()
695 if (plugin->last_preset().uri.empty()) {
696 _preset_modified.set_text ("");
700 bool const c = plugin->parameter_changed_since_last_preset ();
701 if (_preset_modified.get_text().empty() == c) {
702 _preset_modified.set_text (c ? "*" : "");
707 PlugUIBase::parameter_changed (uint32_t, float)
709 update_preset_modified ();
713 PlugUIBase::preset_added_or_removed ()
715 /* Update both the list and the currently-displayed preset */
716 update_preset_list ();