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));
130 Gtk::HBox *hbox = new Gtk::HBox();
131 hbox->pack_start( *pu);
132 // TODO: this should be nicer
133 hbox->pack_start( eqgui_bin );
138 set_wmclass (X_("ardour_plugin_editor"), PROGRAM_NAME);
140 signal_map_event().connect (sigc::mem_fun (*pu, &GenericPluginUI::start_updating));
141 signal_unmap_event().connect (sigc::mem_fun (*pu, &GenericPluginUI::stop_updating));
144 // set_position (Gtk::WIN_POS_MOUSE);
145 set_name ("PluginEditor");
146 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
148 signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), reinterpret_cast<Window*> (this)), false);
149 insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PluginUIWindow::plugin_going_away, this), gui_context());
151 gint h = _pluginui->get_preferred_height ();
152 gint w = _pluginui->get_preferred_width ();
155 if (h > 600) h = 600;
156 if (w > 600) w = 600;
163 set_default_size (w, h);
166 PluginUIWindow::~PluginUIWindow ()
172 PluginUIWindow::set_parent (Gtk::Window* win)
178 PluginUIWindow::on_map ()
181 set_keep_above (true);
185 PluginUIWindow::on_enter_notify_event (GdkEventCrossing *ev)
187 Keyboard::the_keyboard().enter_window (ev, this);
192 PluginUIWindow::on_leave_notify_event (GdkEventCrossing *ev)
194 Keyboard::the_keyboard().leave_window (ev, this);
199 PluginUIWindow::on_focus_in_event (GdkEventFocus *ev)
201 Window::on_focus_in_event (ev);
202 //Keyboard::the_keyboard().magic_widget_grab_focus ();
207 PluginUIWindow::on_focus_out_event (GdkEventFocus *ev)
209 Window::on_focus_out_event (ev);
210 //Keyboard::the_keyboard().magic_widget_drop_focus ();
215 PluginUIWindow::on_show ()
217 set_role("plugin_ui");
220 _pluginui->update_preset_list ();
221 _pluginui->update_preset ();
225 if (_pluginui->on_window_show (_title)) {
231 // set_transient_for (*parent);
236 PluginUIWindow::on_hide ()
241 _pluginui->on_window_hide ();
246 PluginUIWindow::set_title(const std::string& title)
248 Gtk::Window::set_title(title);
254 PluginUIWindow::create_vst_editor(boost::shared_ptr<PluginInsert> insert)
256 PluginUIWindow::create_vst_editor(boost::shared_ptr<PluginInsert>)
263 boost::shared_ptr<VSTPlugin> vp;
265 if ((vp = boost::dynamic_pointer_cast<VSTPlugin> (insert->plugin())) == 0) {
266 error << _("unknown type of editor-supplying plugin (note: no VST support in this version of ardour)")
268 throw failed_constructor ();
270 VSTPluginUI* vpu = new VSTPluginUI (insert, vp);
273 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
275 vpu->package (*this);
284 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert> insert)
286 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert>)
293 _pluginui = create_au_gui (insert, &box);
294 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
297 Application::instance()->ActivationChanged.connect (mem_fun (*this, &PluginUIWindow::app_activated));
305 PluginUIWindow::app_activated (bool yn)
307 PluginUIWindow::app_activated (bool)
311 cerr << "APP activated ? " << yn << endl;
315 _pluginui->activate ();
320 was_visible = is_visible();
322 _pluginui->deactivate ();
329 PluginUIWindow::create_lv2_editor(boost::shared_ptr<PluginInsert> insert)
335 boost::shared_ptr<LV2Plugin> vp;
337 if ((vp = boost::dynamic_pointer_cast<LV2Plugin> (insert->plugin())) == 0) {
338 error << _("create_lv2_editor called on non-LV2 plugin") << endmsg;
339 throw failed_constructor ();
341 LV2PluginUI* lpu = new LV2PluginUI (insert, vp);
344 lpu->package (*this);
352 PluginUIWindow::keyboard_focused (bool yn)
354 _keyboard_focused = yn;
358 PluginUIWindow::on_key_press_event (GdkEventKey* event)
360 if (_keyboard_focused) {
362 if (_pluginui->non_gtk_gui()) {
363 _pluginui->forward_key_event (event);
365 return relay_key_press (event, this);
370 /* for us to be getting key press events, there really
371 MUST be a _pluginui, but just to be safe, check ...
375 if (_pluginui->non_gtk_gui()) {
376 /* pass editor window as the window for the event
377 to be handled in, not this one, because there are
378 no widgets in this window that we want to have
381 return relay_key_press (event, &PublicEditor::instance());
383 return relay_key_press (event, this);
392 PluginUIWindow::on_key_release_event (GdkEventKey *event)
394 if (_keyboard_focused) {
396 if (_pluginui->non_gtk_gui()) {
397 _pluginui->forward_key_event (event);
408 PluginUIWindow::plugin_going_away ()
410 ENSURE_GUI_THREAD (*this, &PluginUIWindow::plugin_going_away)
413 _pluginui->stop_updating(0);
416 death_connection.disconnect ();
418 delete_when_idle (this);
421 PlugUIBase::PlugUIBase (boost::shared_ptr<PluginInsert> pi)
423 plugin (insert->plugin()),
424 add_button (_("Add")),
425 save_button (_("Save")),
426 delete_button (_("Delete")),
427 bypass_button (_("Bypass")),
429 plugin_analysis_expander (_("Plugin analysis"))
431 _preset_combo.set_size_request (100, -1);
432 _preset_modified.set_size_request (16, -1);
433 _preset_combo.signal_changed().connect(sigc::mem_fun(*this, &PlugUIBase::preset_selected));
436 _preset_box.pack_start (_preset_combo);
437 _preset_box.pack_start (_preset_modified);
439 update_preset_list ();
442 add_button.set_name ("PluginAddButton");
443 add_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::add_plugin_setting));
445 save_button.set_name ("PluginSaveButton");
446 save_button.signal_clicked().connect(sigc::mem_fun(*this, &PlugUIBase::save_plugin_setting));
448 delete_button.set_name ("PluginDeleteButton");
449 delete_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::delete_plugin_setting));
451 insert->ActiveChanged.connect (active_connection, invalidator (*this), boost::bind (&PlugUIBase::processor_active_changed, this, boost::weak_ptr<Processor>(insert)), gui_context());
453 bypass_button.set_active (!pi->active());
455 bypass_button.set_name ("PluginBypassButton");
456 bypass_button.signal_toggled().connect (sigc::mem_fun(*this, &PlugUIBase::bypass_toggled));
457 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
459 focus_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::focus_toggled));
460 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
462 /* these images are not managed, so that we can remove them at will */
464 focus_out_image = new Image (get_icon (X_("computer_keyboard")));
465 focus_in_image = new Image (get_icon (X_("computer_keyboard_active")));
467 focus_button.add (*focus_out_image);
469 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));
470 ARDOUR_UI::instance()->set_tip (bypass_button, _("Click to enable/disable this plugin"));
472 plugin_analysis_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_plugin_analysis));
473 plugin_analysis_expander.set_expanded(false);
475 insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PlugUIBase::plugin_going_away, this), gui_context());
477 plugin->PresetAdded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
478 plugin->PresetRemoved.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
479 plugin->PresetLoaded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset, this), gui_context ());
480 plugin->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::parameter_changed, this, _1, _2), gui_context ());
483 PlugUIBase::~PlugUIBase()
489 PlugUIBase::plugin_going_away ()
491 /* drop references to the plugin/insert */
494 death_connection.disconnect ();
498 PlugUIBase::set_latency_label ()
500 framecnt_t const l = insert->effective_latency ();
501 framecnt_t const sr = insert->session().frame_rate ();
506 t = string_compose (_("latency (%1 samples)"), l);
508 t = string_compose (_("latency (%1 ms)"), (float) l / ((float) sr / 1000.0f));
511 latency_label.set_text (t);
515 PlugUIBase::latency_button_clicked ()
518 latency_gui = new LatencyGUI (*(insert.get()), insert->session().frame_rate(), insert->session().get_block_size());
519 latency_dialog = new ArdourDialog (_("Edit Latency"), false, false);
520 latency_dialog->get_vbox()->pack_start (*latency_gui);
521 latency_dialog->signal_hide().connect (sigc::mem_fun (*this, &PlugUIBase::set_latency_label));
524 latency_dialog->show_all ();
528 PlugUIBase::processor_active_changed (boost::weak_ptr<Processor> weak_p)
530 ENSURE_GUI_THREAD (*this, &PlugUIBase::processor_active_changed, weak_p)
531 boost::shared_ptr<Processor> p (weak_p);
533 bypass_button.set_active (!p->active());
538 PlugUIBase::preset_selected ()
540 if (_no_load_preset) {
544 if (_preset_combo.get_active_text().length() > 0) {
545 const Plugin::PresetRecord* pr = plugin->preset_by_label (_preset_combo.get_active_text());
547 plugin->load_preset (*pr);
549 warning << string_compose(_("Plugin preset %1 not found"),
550 _preset_combo.get_active_text()) << endmsg;
556 PlugUIBase::add_plugin_setting ()
558 NewPluginPresetDialog d (plugin);
561 case Gtk::RESPONSE_ACCEPT:
562 if (d.name().empty()) {
567 plugin->remove_preset (d.name ());
570 Plugin::PresetRecord const r = plugin->save_preset (d.name());
571 if (!r.uri.empty ()) {
572 plugin->load_preset (r);
579 PlugUIBase::save_plugin_setting ()
581 string const name = _preset_combo.get_active_text ();
582 plugin->remove_preset (name);
583 Plugin::PresetRecord const r = plugin->save_preset (name);
584 if (!r.uri.empty ()) {
585 plugin->load_preset (r);
590 PlugUIBase::delete_plugin_setting ()
592 plugin->remove_preset (_preset_combo.get_active_text ());
596 PlugUIBase::bypass_toggled ()
600 if ((x = bypass_button.get_active()) == insert->active()) {
602 insert->deactivate ();
610 PlugUIBase::focus_toggled (GdkEventButton*)
612 if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
613 Keyboard::the_keyboard().magic_widget_drop_focus();
614 focus_button.remove ();
615 focus_button.add (*focus_out_image);
616 focus_out_image->show ();
617 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));
618 KeyboardFocused (false);
620 Keyboard::the_keyboard().magic_widget_grab_focus();
621 focus_button.remove ();
622 focus_button.add (*focus_in_image);
623 focus_in_image->show ();
624 ARDOUR_UI::instance()->set_tip (focus_button, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME));
625 KeyboardFocused (true);
632 PlugUIBase::toggle_plugin_analysis()
634 if (plugin_analysis_expander.get_expanded() &&
635 !plugin_analysis_expander.get_child()) {
637 PluginEqGui *foo = new PluginEqGui(insert);
638 plugin_analysis_expander.add( *foo );
639 plugin_analysis_expander.show_all();
644 if (!plugin_analysis_expander.get_expanded() &&
645 (gui = plugin_analysis_expander.get_child())) {
648 //plugin_analysis_expander.remove(*gui);
649 plugin_analysis_expander.remove();
653 Gtk::Widget *toplevel = plugin_analysis_expander.get_toplevel();
655 std::cerr << "No toplevel widget?!?!" << std::endl;
659 Gtk::Container *cont = dynamic_cast<Gtk::Container *>(toplevel);
661 std::cerr << "Toplevel widget is not a container?!?" << std::endl;
665 Gtk::Allocation alloc(0, 0, 50, 50); // Just make it small
666 toplevel->size_allocate(alloc);
671 PlugUIBase::update_preset_list ()
673 vector<string> preset_labels;
674 vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets();
678 for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator i = presets.begin(); i != presets.end(); ++i) {
679 preset_labels.push_back (i->label);
682 set_popdown_strings (_preset_combo, preset_labels);
688 PlugUIBase::update_preset ()
690 Plugin::PresetRecord p = plugin->last_preset();
693 _preset_combo.set_active_text (p.label);
696 save_button.set_sensitive (!p.uri.empty() && p.user);
697 delete_button.set_sensitive (!p.uri.empty() && p.user);
699 update_preset_modified ();
703 PlugUIBase::update_preset_modified ()
705 if (plugin->last_preset().uri.empty()) {
706 _preset_modified.set_text ("");
710 bool const c = plugin->parameter_changed_since_last_preset ();
711 if (_preset_modified.get_text().empty() == c) {
712 _preset_modified.set_text (c ? "*" : "");
717 PlugUIBase::parameter_changed (uint32_t, float)
719 update_preset_modified ();
723 PlugUIBase::preset_added_or_removed ()
725 /* Update both the list and the currently-displayed preset */
726 update_preset_list ();