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_presets ();
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);
253 PluginUIWindow::create_vst_editor(boost::shared_ptr<PluginInsert> insert)
255 PluginUIWindow::create_vst_editor(boost::shared_ptr<PluginInsert>)
262 boost::shared_ptr<VSTPlugin> vp;
264 if ((vp = boost::dynamic_pointer_cast<VSTPlugin> (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 VSTPluginUI* vpu = new VSTPluginUI (insert, vp);
272 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
274 vpu->package (*this);
283 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert> insert)
285 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert>)
292 _pluginui = create_au_gui (insert, &box);
293 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
296 Application::instance()->ActivationChanged.connect (mem_fun (*this, &PluginUIWindow::app_activated));
304 PluginUIWindow::app_activated (bool yn)
306 PluginUIWindow::app_activated (bool)
310 cerr << "APP activated ? " << yn << endl;
314 _pluginui->activate ();
319 was_visible = is_visible();
321 _pluginui->deactivate ();
328 PluginUIWindow::create_lv2_editor(boost::shared_ptr<PluginInsert> insert)
334 boost::shared_ptr<LV2Plugin> vp;
336 if ((vp = boost::dynamic_pointer_cast<LV2Plugin> (insert->plugin())) == 0) {
337 error << _("create_lv2_editor called on non-LV2 plugin") << endmsg;
338 throw failed_constructor ();
340 LV2PluginUI* lpu = new LV2PluginUI (insert, vp);
343 lpu->package (*this);
351 PluginUIWindow::keyboard_focused (bool yn)
353 _keyboard_focused = yn;
357 PluginUIWindow::on_key_press_event (GdkEventKey* event)
359 if (_keyboard_focused) {
361 if (_pluginui->non_gtk_gui()) {
362 _pluginui->forward_key_event (event);
364 return relay_key_press (event, this);
369 /* for us to be getting key press events, there really
370 MUST be a _pluginui, but just to be safe, check ...
374 if (_pluginui->non_gtk_gui()) {
375 /* pass editor window as the window for the event
376 to be handled in, not this one, because there are
377 no widgets in this window that we want to have
380 return relay_key_press (event, &PublicEditor::instance());
382 return relay_key_press (event, this);
391 PluginUIWindow::on_key_release_event (GdkEventKey *event)
393 if (_keyboard_focused) {
395 if (_pluginui->non_gtk_gui()) {
396 _pluginui->forward_key_event (event);
407 PluginUIWindow::plugin_going_away ()
409 ENSURE_GUI_THREAD (*this, &PluginUIWindow::plugin_going_away)
412 _pluginui->stop_updating(0);
415 death_connection.disconnect ();
417 delete_when_idle (this);
420 PlugUIBase::PlugUIBase (boost::shared_ptr<PluginInsert> pi)
422 plugin (insert->plugin()),
423 add_button (_("Add")),
424 save_button (_("Save")),
425 delete_button (_("Delete")),
426 bypass_button (_("Bypass")),
428 plugin_analysis_expander (_("Plugin analysis"))
430 //preset_combo.set_use_arrows_always(true);
432 update_sensitivity ();
434 preset_combo.set_size_request (100, -1);
435 preset_combo.set_active_text ("");
436 preset_combo.signal_changed().connect(sigc::mem_fun(*this, &PlugUIBase::setting_selected));
437 no_load_preset = false;
439 add_button.set_name ("PluginAddButton");
440 add_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::add_plugin_setting));
442 save_button.set_name ("PluginSaveButton");
443 save_button.signal_clicked().connect(sigc::mem_fun(*this, &PlugUIBase::save_plugin_setting));
445 delete_button.set_name ("PluginDeleteButton");
446 delete_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::delete_plugin_setting));
448 insert->ActiveChanged.connect (active_connection, invalidator (*this), boost::bind (&PlugUIBase::processor_active_changed, this, boost::weak_ptr<Processor>(insert)), gui_context());
450 bypass_button.set_active (!pi->active());
452 bypass_button.set_name ("PluginBypassButton");
453 bypass_button.signal_toggled().connect (sigc::mem_fun(*this, &PlugUIBase::bypass_toggled));
454 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
456 focus_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::focus_toggled));
457 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
459 /* these images are not managed, so that we can remove them at will */
461 focus_out_image = new Image (get_icon (X_("computer_keyboard")));
462 focus_in_image = new Image (get_icon (X_("computer_keyboard_active")));
464 focus_button.add (*focus_out_image);
466 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));
467 ARDOUR_UI::instance()->set_tip (bypass_button, _("Click to enable/disable this plugin"));
469 plugin_analysis_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_plugin_analysis));
470 plugin_analysis_expander.set_expanded(false);
472 insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PlugUIBase::plugin_going_away, this), gui_context());
474 plugin->PresetAdded.connect (preset_added_connection, invalidator (*this), boost::bind (&PlugUIBase::update_presets, this), gui_context ());
475 plugin->PresetRemoved.connect (preset_removed_connection, invalidator (*this), boost::bind (&PlugUIBase::update_presets, this), gui_context ());
478 PlugUIBase::~PlugUIBase()
484 PlugUIBase::plugin_going_away ()
486 /* drop references to the plugin/insert */
489 death_connection.disconnect ();
493 PlugUIBase::set_latency_label ()
496 framecnt_t const l = insert->effective_latency ();
497 framecnt_t const sr = insert->session().frame_rate ();
500 snprintf (buf, sizeof (buf), "latency (%" PRId64 " samples)", l);
502 snprintf (buf, sizeof (buf), "latency (%.2f msecs)", (float) l / ((float) sr / 1000.0f));
505 latency_label.set_text (buf);
509 PlugUIBase::latency_button_clicked ()
512 latency_gui = new LatencyGUI (*(insert.get()), insert->session().frame_rate(), insert->session().get_block_size());
513 latency_dialog = new ArdourDialog ("Edit Latency", false, false);
514 latency_dialog->get_vbox()->pack_start (*latency_gui);
515 latency_dialog->signal_hide().connect (sigc::mem_fun (*this, &PlugUIBase::set_latency_label));
518 latency_dialog->show_all ();
522 PlugUIBase::processor_active_changed (boost::weak_ptr<Processor> weak_p)
524 ENSURE_GUI_THREAD (*this, &PlugUIBase::processor_active_changed, weak_p)
525 boost::shared_ptr<Processor> p (weak_p);
527 bypass_button.set_active (!p->active());
532 PlugUIBase::setting_selected ()
534 if (no_load_preset) {
538 if (preset_combo.get_active_text().length() > 0) {
539 const Plugin::PresetRecord* pr = plugin->preset_by_label(preset_combo.get_active_text());
541 plugin->load_preset (pr->uri);
542 update_sensitivity ();
544 warning << string_compose(_("Plugin preset %1 not found"),
545 preset_combo.get_active_text()) << endmsg;
551 PlugUIBase::add_plugin_setting ()
553 NewPluginPresetDialog d (plugin);
556 case Gtk::RESPONSE_ACCEPT:
557 if (d.name().empty()) {
562 plugin->remove_preset (d.name ());
565 if (plugin->save_preset (d.name())) {
566 preset_combo.set_active_text (d.name());
573 PlugUIBase::save_plugin_setting ()
575 string const name = preset_combo.get_active_text ();
576 plugin->remove_preset (name);
577 plugin->save_preset (name);
578 preset_combo.set_active_text (name);
582 PlugUIBase::delete_plugin_setting ()
584 plugin->remove_preset (preset_combo.get_active_text ());
586 vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets();
587 if (presets.empty ()) {
588 preset_combo.set_active_text ("");
590 preset_combo.set_active_text (presets.front().label);
595 PlugUIBase::bypass_toggled ()
599 if ((x = bypass_button.get_active()) == insert->active()) {
601 insert->deactivate ();
609 PlugUIBase::focus_toggled (GdkEventButton*)
611 if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
612 Keyboard::the_keyboard().magic_widget_drop_focus();
613 focus_button.remove ();
614 focus_button.add (*focus_out_image);
615 focus_out_image->show ();
616 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));
617 KeyboardFocused (false);
619 Keyboard::the_keyboard().magic_widget_grab_focus();
620 focus_button.remove ();
621 focus_button.add (*focus_in_image);
622 focus_in_image->show ();
623 ARDOUR_UI::instance()->set_tip (focus_button, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME));
624 KeyboardFocused (true);
631 PlugUIBase::toggle_plugin_analysis()
633 if (plugin_analysis_expander.get_expanded() &&
634 !plugin_analysis_expander.get_child()) {
636 PluginEqGui *foo = new PluginEqGui(insert);
637 plugin_analysis_expander.add( *foo );
638 plugin_analysis_expander.show_all();
643 if (!plugin_analysis_expander.get_expanded() &&
644 (gui = plugin_analysis_expander.get_child())) {
647 //plugin_analysis_expander.remove(*gui);
648 plugin_analysis_expander.remove();
652 Gtk::Widget *toplevel = plugin_analysis_expander.get_toplevel();
654 std::cerr << "No toplevel widget?!?!" << std::endl;
658 Gtk::Container *cont = dynamic_cast<Gtk::Container *>(toplevel);
660 std::cerr << "Toplevel widget is not a container?!?" << std::endl;
664 Gtk::Allocation alloc(0, 0, 50, 50); // Just make it small
665 toplevel->size_allocate(alloc);
670 PlugUIBase::update_presets ()
672 vector<string> preset_labels;
673 vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets();
675 no_load_preset = true;
677 for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator i = presets.begin(); i != presets.end(); ++i) {
678 preset_labels.push_back(i->label);
681 set_popdown_strings (preset_combo, preset_labels);
683 no_load_preset = false;
685 update_sensitivity ();
689 PlugUIBase::update_sensitivity ()
691 bool const have_user_preset =
692 !preset_combo.get_model()->children().empty() && preset_combo.get_active_row_number() >= plugin->first_user_preset_index();
694 save_button.set_sensitive (have_user_preset);
695 delete_button.set_sensitive (have_user_preset);