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>
42 #include "midi++/manager.h"
44 #include "ardour/session.h"
45 #include "ardour/plugin.h"
46 #include "ardour/plugin_insert.h"
47 #include "ardour/ladspa_plugin.h"
49 #include "ardour/vst_plugin.h"
52 #include "ardour/lv2_plugin.h"
53 #include "lv2_plugin_ui.h"
58 #include "ardour_dialog.h"
59 #include "ardour_ui.h"
61 #include "plugin_ui.h"
63 #include "gui_thread.h"
64 #include "public_editor.h"
66 #include "latency_gui.h"
67 #include "plugin_eq_gui.h"
72 using namespace ARDOUR;
74 using namespace Gtkmm2ext;
77 PluginUIWindow::PluginUIWindow (Gtk::Window* win, boost::shared_ptr<PluginInsert> insert, bool scrollable)
80 , _keyboard_focused (false)
82 bool have_gui = false;
84 Label* label = manage (new Label());
85 label->set_markup ("<b>THIS IS THE PLUGIN UI</b>");
87 if (insert->plugin()->has_editor()) {
88 switch (insert->type()) {
90 have_gui = create_vst_editor (insert);
93 case ARDOUR::AudioUnit:
94 have_gui = create_audiounit_editor (insert);
98 error << _("Eh? LADSPA plugins don't have editors!") << endmsg;
102 have_gui = create_lv2_editor (insert);
107 error << _("unknown type of editor-supplying plugin (note: no VST support in this version of ardour)")
110 error << _("unknown type of editor-supplying plugin")
113 throw failed_constructor ();
120 GenericPluginUI* pu = new GenericPluginUI (insert, scrollable);
123 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
127 Gtk::HBox *hbox = new Gtk::HBox();
128 hbox->pack_start( *pu);
129 // TODO: this should be nicer
130 hbox->pack_start( eqgui_bin );
135 set_wmclass (X_("ardour_plugin_editor"), PROGRAM_NAME);
137 signal_map_event().connect (sigc::mem_fun (*pu, &GenericPluginUI::start_updating));
138 signal_unmap_event().connect (sigc::mem_fun (*pu, &GenericPluginUI::stop_updating));
141 // set_position (Gtk::WIN_POS_MOUSE);
142 set_name ("PluginEditor");
143 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
145 signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), reinterpret_cast<Window*> (this)), false);
146 insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PluginUIWindow::plugin_going_away, this), gui_context());
148 gint h = _pluginui->get_preferred_height ();
149 gint w = _pluginui->get_preferred_width ();
152 if (h > 600) h = 600;
153 if (w > 600) w = 600;
160 set_default_size (w, h);
163 PluginUIWindow::~PluginUIWindow ()
169 PluginUIWindow::set_parent (Gtk::Window* win)
175 PluginUIWindow::on_map ()
178 set_keep_above (true);
182 PluginUIWindow::on_enter_notify_event (GdkEventCrossing *ev)
184 Keyboard::the_keyboard().enter_window (ev, this);
189 PluginUIWindow::on_leave_notify_event (GdkEventCrossing *ev)
191 Keyboard::the_keyboard().leave_window (ev, this);
196 PluginUIWindow::on_focus_in_event (GdkEventFocus *ev)
198 Window::on_focus_in_event (ev);
199 //Keyboard::the_keyboard().magic_widget_grab_focus ();
204 PluginUIWindow::on_focus_out_event (GdkEventFocus *ev)
206 Window::on_focus_out_event (ev);
207 //Keyboard::the_keyboard().magic_widget_drop_focus ();
212 PluginUIWindow::on_show ()
214 set_role("plugin_ui");
217 _pluginui->update_presets ();
221 if (_pluginui->on_window_show (_title)) {
227 // set_transient_for (*parent);
232 PluginUIWindow::on_hide ()
237 _pluginui->on_window_hide ();
242 PluginUIWindow::set_title(const std::string& title)
244 //cout << "PluginUIWindow::set_title(\"" << title << "\"" << endl;
245 Gtk::Window::set_title(title);
251 PluginUIWindow::create_vst_editor(boost::shared_ptr<PluginInsert> insert)
253 PluginUIWindow::create_vst_editor(boost::shared_ptr<PluginInsert>)
260 boost::shared_ptr<VSTPlugin> vp;
262 if ((vp = boost::dynamic_pointer_cast<VSTPlugin> (insert->plugin())) == 0) {
263 error << _("unknown type of editor-supplying plugin (note: no VST support in this version of ardour)")
265 throw failed_constructor ();
267 VSTPluginUI* vpu = new VSTPluginUI (insert, vp);
270 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
272 vpu->package (*this);
280 #if defined (HAVE_AUDIOUNITS) && defined (GTKOSX)
281 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert> insert)
283 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert>)
286 #if !defined(HAVE_AUDIOUNITS) || !defined(GTKOSX)
290 _pluginui = create_au_gui (insert, &box);
291 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
294 extern sigc::signal<void,bool> ApplicationActivationChanged;
295 ApplicationActivationChanged.connect (sigc::mem_fun (*this, &PluginUIWindow::app_activated));
302 #if defined (HAVE_AUDIOUNITS) && defined(GTKOSX)
303 PluginUIWindow::app_activated (bool yn)
305 PluginUIWindow::app_activated (bool)
308 #if defined (HAVE_AUDIOUNITS) && defined(GTKOSX)
309 cerr << "APP activated ? " << yn << endl;
313 _pluginui->activate ();
318 was_visible = is_visible();
320 _pluginui->deactivate ();
327 PluginUIWindow::create_lv2_editor(boost::shared_ptr<PluginInsert> insert)
333 boost::shared_ptr<LV2Plugin> vp;
335 if ((vp = boost::dynamic_pointer_cast<LV2Plugin> (insert->plugin())) == 0) {
336 error << _("create_lv2_editor called on non-LV2 plugin") << endmsg;
337 throw failed_constructor ();
339 LV2PluginUI* lpu = new LV2PluginUI (insert, vp);
342 lpu->package (*this);
350 PluginUIWindow::keyboard_focused (bool yn)
352 _keyboard_focused = yn;
356 PluginUIWindow::on_key_press_event (GdkEventKey* event)
358 if (_keyboard_focused) {
360 if (_pluginui->non_gtk_gui()) {
361 _pluginui->forward_key_event (event);
363 return relay_key_press (event, this);
368 if (_pluginui->non_gtk_gui()) {
369 /* pass editor window as the window for the event
370 to be handled in, not this one, because there are
371 no widgets in this window that we want to have
374 return relay_key_press (event, &PublicEditor::instance());
376 return relay_key_press (event, this);
382 PluginUIWindow::on_key_release_event (GdkEventKey *event)
384 if (_keyboard_focused) {
386 if (_pluginui->non_gtk_gui()) {
387 _pluginui->forward_key_event (event);
398 PluginUIWindow::plugin_going_away ()
400 ENSURE_GUI_THREAD (*this, &PluginUIWindow::plugin_going_away)
403 _pluginui->stop_updating(0);
406 death_connection.disconnect ();
408 delete_when_idle (this);
411 PlugUIBase::PlugUIBase (boost::shared_ptr<PluginInsert> pi)
413 plugin (insert->plugin()),
414 save_button(_("Add")),
415 bypass_button (_("Bypass")),
417 plugin_analysis_expander (_("Plugin analysis"))
419 //preset_combo.set_use_arrows_always(true);
421 preset_combo.set_size_request (100, -1);
422 preset_combo.set_active_text ("");
423 preset_combo.signal_changed().connect(sigc::mem_fun(*this, &PlugUIBase::setting_selected));
424 no_load_preset = false;
426 save_button.set_name ("PluginSaveButton");
427 save_button.signal_clicked().connect(sigc::mem_fun(*this, &PlugUIBase::save_plugin_setting));
429 insert->ActiveChanged.connect (active_connection, invalidator (*this), boost::bind (&PlugUIBase::processor_active_changed, this, boost::weak_ptr<Processor>(insert)), gui_context());
431 bypass_button.set_active (!pi->active());
433 bypass_button.set_name ("PluginBypassButton");
434 bypass_button.signal_toggled().connect (sigc::mem_fun(*this, &PlugUIBase::bypass_toggled));
435 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
437 focus_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::focus_toggled));
438 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
440 /* these images are not managed, so that we can remove them at will */
442 focus_out_image = new Image (get_icon (X_("computer_keyboard")));
443 focus_in_image = new Image (get_icon (X_("computer_keyboard_active")));
445 focus_button.add (*focus_out_image);
447 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));
448 ARDOUR_UI::instance()->set_tip (bypass_button, _("Click to enable/disable this plugin"));
450 plugin_analysis_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_plugin_analysis));
451 plugin_analysis_expander.set_expanded(false);
453 insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PlugUIBase::plugin_going_away, this), gui_context());
456 PlugUIBase::~PlugUIBase()
462 PlugUIBase::plugin_going_away ()
464 /* drop references to the plugin/insert */
467 death_connection.disconnect ();
471 PlugUIBase::set_latency_label ()
474 nframes_t l = insert->effective_latency ();
475 nframes_t sr = insert->session().frame_rate();
478 snprintf (buf, sizeof (buf), "latency (%d samples)", l);
480 snprintf (buf, sizeof (buf), "latency (%.2f msecs)", (float) l / ((float) sr / 1000.0f));
483 latency_label.set_text (buf);
487 PlugUIBase::latency_button_clicked ()
490 latency_gui = new LatencyGUI (*(insert.get()), insert->session().frame_rate(), insert->session().get_block_size());
491 latency_dialog = new ArdourDialog ("Edit Latency", false, false);
492 latency_dialog->get_vbox()->pack_start (*latency_gui);
493 latency_dialog->signal_hide().connect (sigc::mem_fun (*this, &PlugUIBase::set_latency_label));
496 latency_dialog->show_all ();
500 PlugUIBase::processor_active_changed (boost::weak_ptr<Processor> weak_p)
502 ENSURE_GUI_THREAD (*this, &PlugUIBase::processor_active_changed, weak_p)
503 boost::shared_ptr<Processor> p (weak_p);
505 bypass_button.set_active (!p->active());
510 PlugUIBase::setting_selected()
512 if (no_load_preset) {
516 if (preset_combo.get_active_text().length() > 0) {
517 const Plugin::PresetRecord* pr = plugin->preset_by_label(preset_combo.get_active_text());
519 plugin->load_preset(pr->uri);
521 warning << string_compose(_("Plugin preset %1 not found"),
522 preset_combo.get_active_text()) << endmsg;
528 PlugUIBase::save_plugin_setting ()
530 ArdourPrompter prompter (true);
531 prompter.set_prompt(_("Name of New Preset:"));
532 prompter.add_button (Gtk::Stock::ADD, Gtk::RESPONSE_ACCEPT);
533 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
534 prompter.set_type_hint (Gdk::WINDOW_TYPE_HINT_UTILITY);
539 switch (prompter.run ()) {
540 case Gtk::RESPONSE_ACCEPT:
542 prompter.get_result(name);
544 if (plugin->save_preset(name)) {
546 no_load_preset = true;
547 preset_combo.set_active_text (name);
548 no_load_preset = false;
556 PlugUIBase::bypass_toggled ()
560 if ((x = bypass_button.get_active()) == insert->active()) {
562 insert->deactivate ();
570 PlugUIBase::focus_toggled (GdkEventButton*)
572 if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
573 Keyboard::the_keyboard().magic_widget_drop_focus();
574 focus_button.remove ();
575 focus_button.add (*focus_out_image);
576 focus_out_image->show ();
577 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));
578 KeyboardFocused (false);
580 Keyboard::the_keyboard().magic_widget_grab_focus();
581 focus_button.remove ();
582 focus_button.add (*focus_in_image);
583 focus_in_image->show ();
584 ARDOUR_UI::instance()->set_tip (focus_button, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME));
585 KeyboardFocused (true);
592 PlugUIBase::toggle_plugin_analysis()
594 if (plugin_analysis_expander.get_expanded() &&
595 !plugin_analysis_expander.get_child()) {
597 PluginEqGui *foo = new PluginEqGui(insert);
598 plugin_analysis_expander.add( *foo );
599 plugin_analysis_expander.show_all();
604 if (!plugin_analysis_expander.get_expanded() &&
605 (gui = plugin_analysis_expander.get_child())) {
608 //plugin_analysis_expander.remove(*gui);
609 plugin_analysis_expander.remove();
613 Gtk::Widget *toplevel = plugin_analysis_expander.get_toplevel();
615 std::cerr << "No toplevel widget?!?!" << std::endl;
619 Gtk::Container *cont = dynamic_cast<Gtk::Container *>(toplevel);
621 std::cerr << "Toplevel widget is not a container?!?" << std::endl;
625 Gtk::Allocation alloc(0, 0, 50, 50); // Just make it small
626 toplevel->size_allocate(alloc);
631 PlugUIBase::update_presets ()
633 vector<string> preset_labels;
634 vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets();
636 no_load_preset = true;
638 for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator i = presets.begin(); i != presets.end(); ++i) {
639 preset_labels.push_back(i->label);
642 set_popdown_strings (preset_combo, preset_labels);
644 no_load_preset = false;