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"
53 #include "ardour/lv2_plugin.h"
54 #include "lv2_plugin_ui.h"
59 #include "ardour_dialog.h"
60 #include "ardour_ui.h"
62 #include "plugin_ui.h"
64 #include "gui_thread.h"
65 #include "public_editor.h"
67 #include "latency_gui.h"
68 #include "plugin_eq_gui.h"
69 #include "new_plugin_preset_dialog.h"
70 #include "edit_plugin_presets_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 //cout << "PluginUIWindow::set_title(\"" << title << "\"" << endl;
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 if (_pluginui->non_gtk_gui()) {
371 /* pass editor window as the window for the event
372 to be handled in, not this one, because there are
373 no widgets in this window that we want to have
376 return relay_key_press (event, &PublicEditor::instance());
378 return relay_key_press (event, this);
384 PluginUIWindow::on_key_release_event (GdkEventKey *event)
386 if (_keyboard_focused) {
388 if (_pluginui->non_gtk_gui()) {
389 _pluginui->forward_key_event (event);
400 PluginUIWindow::plugin_going_away ()
402 ENSURE_GUI_THREAD (*this, &PluginUIWindow::plugin_going_away)
405 _pluginui->stop_updating(0);
408 death_connection.disconnect ();
410 delete_when_idle (this);
413 PlugUIBase::PlugUIBase (boost::shared_ptr<PluginInsert> pi)
415 plugin (insert->plugin()),
416 save_button (_("Add")),
417 edit_button (_("Edit")),
418 bypass_button (_("Bypass")),
420 plugin_analysis_expander (_("Plugin analysis"))
422 //preset_combo.set_use_arrows_always(true);
424 preset_combo.set_size_request (100, -1);
425 preset_combo.set_active_text ("");
426 preset_combo.signal_changed().connect(sigc::mem_fun(*this, &PlugUIBase::setting_selected));
427 no_load_preset = false;
429 save_button.set_name ("PluginSaveButton");
430 save_button.signal_clicked().connect(sigc::mem_fun(*this, &PlugUIBase::save_plugin_setting));
432 edit_button.set_name ("PluginEditButton");
433 edit_button.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::edit_plugin_settings));
435 insert->ActiveChanged.connect (active_connection, invalidator (*this), boost::bind (&PlugUIBase::processor_active_changed, this, boost::weak_ptr<Processor>(insert)), gui_context());
437 bypass_button.set_active (!pi->active());
439 bypass_button.set_name ("PluginBypassButton");
440 bypass_button.signal_toggled().connect (sigc::mem_fun(*this, &PlugUIBase::bypass_toggled));
441 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
443 focus_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::focus_toggled));
444 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
446 /* these images are not managed, so that we can remove them at will */
448 focus_out_image = new Image (get_icon (X_("computer_keyboard")));
449 focus_in_image = new Image (get_icon (X_("computer_keyboard_active")));
451 focus_button.add (*focus_out_image);
453 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));
454 ARDOUR_UI::instance()->set_tip (bypass_button, _("Click to enable/disable this plugin"));
456 plugin_analysis_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_plugin_analysis));
457 plugin_analysis_expander.set_expanded(false);
459 insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PlugUIBase::plugin_going_away, this), gui_context());
461 plugin->PresetAdded.connect (preset_added_connection, invalidator (*this), boost::bind (&PlugUIBase::update_presets, this), gui_context ());
462 plugin->PresetRemoved.connect (preset_removed_connection, invalidator (*this), boost::bind (&PlugUIBase::update_presets, this), gui_context ());
465 PlugUIBase::~PlugUIBase()
471 PlugUIBase::plugin_going_away ()
473 /* drop references to the plugin/insert */
476 death_connection.disconnect ();
480 PlugUIBase::set_latency_label ()
483 framecnt_t const l = insert->effective_latency ();
484 framecnt_t const sr = insert->session().frame_rate ();
487 snprintf (buf, sizeof (buf), "latency (%" PRId64 " samples)", l);
489 snprintf (buf, sizeof (buf), "latency (%.2f msecs)", (float) l / ((float) sr / 1000.0f));
492 latency_label.set_text (buf);
496 PlugUIBase::latency_button_clicked ()
499 latency_gui = new LatencyGUI (*(insert.get()), insert->session().frame_rate(), insert->session().get_block_size());
500 latency_dialog = new ArdourDialog ("Edit Latency", false, false);
501 latency_dialog->get_vbox()->pack_start (*latency_gui);
502 latency_dialog->signal_hide().connect (sigc::mem_fun (*this, &PlugUIBase::set_latency_label));
505 latency_dialog->show_all ();
509 PlugUIBase::processor_active_changed (boost::weak_ptr<Processor> weak_p)
511 ENSURE_GUI_THREAD (*this, &PlugUIBase::processor_active_changed, weak_p)
512 boost::shared_ptr<Processor> p (weak_p);
514 bypass_button.set_active (!p->active());
519 PlugUIBase::setting_selected()
521 if (no_load_preset) {
525 if (preset_combo.get_active_text().length() > 0) {
526 const Plugin::PresetRecord* pr = plugin->preset_by_label(preset_combo.get_active_text());
528 plugin->load_preset(pr->uri);
530 warning << string_compose(_("Plugin preset %1 not found"),
531 preset_combo.get_active_text()) << endmsg;
537 PlugUIBase::save_plugin_setting ()
539 NewPluginPresetDialog d (plugin);
542 case Gtk::RESPONSE_ACCEPT:
543 if (d.name().empty()) {
548 plugin->remove_preset (d.name ());
551 if (plugin->save_preset (d.name())) {
553 no_load_preset = true;
554 preset_combo.set_active_text (d.name());
555 no_load_preset = false;
562 PlugUIBase::edit_plugin_settings ()
564 EditPluginPresetsDialog d (plugin);
569 PlugUIBase::bypass_toggled ()
573 if ((x = bypass_button.get_active()) == insert->active()) {
575 insert->deactivate ();
583 PlugUIBase::focus_toggled (GdkEventButton*)
585 if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
586 Keyboard::the_keyboard().magic_widget_drop_focus();
587 focus_button.remove ();
588 focus_button.add (*focus_out_image);
589 focus_out_image->show ();
590 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));
591 KeyboardFocused (false);
593 Keyboard::the_keyboard().magic_widget_grab_focus();
594 focus_button.remove ();
595 focus_button.add (*focus_in_image);
596 focus_in_image->show ();
597 ARDOUR_UI::instance()->set_tip (focus_button, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME));
598 KeyboardFocused (true);
605 PlugUIBase::toggle_plugin_analysis()
607 if (plugin_analysis_expander.get_expanded() &&
608 !plugin_analysis_expander.get_child()) {
610 PluginEqGui *foo = new PluginEqGui(insert);
611 plugin_analysis_expander.add( *foo );
612 plugin_analysis_expander.show_all();
617 if (!plugin_analysis_expander.get_expanded() &&
618 (gui = plugin_analysis_expander.get_child())) {
621 //plugin_analysis_expander.remove(*gui);
622 plugin_analysis_expander.remove();
626 Gtk::Widget *toplevel = plugin_analysis_expander.get_toplevel();
628 std::cerr << "No toplevel widget?!?!" << std::endl;
632 Gtk::Container *cont = dynamic_cast<Gtk::Container *>(toplevel);
634 std::cerr << "Toplevel widget is not a container?!?" << std::endl;
638 Gtk::Allocation alloc(0, 0, 50, 50); // Just make it small
639 toplevel->size_allocate(alloc);
644 PlugUIBase::update_presets ()
646 vector<string> preset_labels;
647 vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets();
649 no_load_preset = true;
651 for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator i = presets.begin(); i != presets.end(); ++i) {
652 preset_labels.push_back(i->label);
655 set_popdown_strings (preset_combo, preset_labels);
657 no_load_preset = false;