changes associated with save/restore of AutomationControl id's
[ardour2.git] / gtk2_ardour / plugin_ui.cc
blobb08b5a3a0f34a627cb8226360c7003b06c278468
1 /*
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.
20 #ifdef WAF_BUILD
21 #include "gtk2ardour-config.h"
22 #endif
24 #include <climits>
25 #include <cerrno>
26 #include <cmath>
27 #include <string>
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"
49 #ifdef VST_SUPPORT
50 #include "ardour/vst_plugin.h"
51 #endif
52 #ifdef HAVE_SLV2
53 #include "ardour/lv2_plugin.h"
54 #include "lv2_plugin_ui.h"
55 #endif
57 #include <lrdf.h>
59 #include "ardour_dialog.h"
60 #include "ardour_ui.h"
61 #include "prompter.h"
62 #include "plugin_ui.h"
63 #include "utils.h"
64 #include "gui_thread.h"
65 #include "public_editor.h"
66 #include "keyboard.h"
67 #include "latency_gui.h"
68 #include "plugin_eq_gui.h"
70 #include "i18n.h"
72 using namespace std;
73 using namespace ARDOUR;
74 using namespace PBD;
75 using namespace Gtkmm2ext;
76 using namespace Gtk;
78 PluginUIWindow::PluginUIWindow (Gtk::Window* win, boost::shared_ptr<PluginInsert> insert, bool scrollable)
79 : parent (win)
80 , was_visible (false)
81 , _keyboard_focused (false)
83 bool have_gui = false;
85 Label* label = manage (new Label());
86 label->set_markup ("<b>THIS IS THE PLUGIN UI</b>");
88 if (insert->plugin()->has_editor()) {
89 switch (insert->type()) {
90 case ARDOUR::VST:
91 have_gui = create_vst_editor (insert);
92 break;
94 case ARDOUR::AudioUnit:
95 have_gui = create_audiounit_editor (insert);
96 break;
98 case ARDOUR::LADSPA:
99 error << _("Eh? LADSPA plugins don't have editors!") << endmsg;
100 break;
102 case ARDOUR::LV2:
103 have_gui = create_lv2_editor (insert);
104 break;
106 default:
107 #ifndef VST_SUPPORT
108 error << _("unknown type of editor-supplying plugin (note: no VST support in this version of ardour)")
109 << endmsg;
110 #else
111 error << _("unknown type of editor-supplying plugin")
112 << endmsg;
113 #endif
114 throw failed_constructor ();
119 if (!have_gui) {
121 GenericPluginUI* pu = new GenericPluginUI (insert, scrollable);
123 _pluginui = pu;
124 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
125 add (*pu);
128 Gtk::HBox *hbox = new Gtk::HBox();
129 hbox->pack_start( *pu);
130 // TODO: this should be nicer
131 hbox->pack_start( eqgui_bin );
133 add (*manage(hbox));
136 set_wmclass (X_("ardour_plugin_editor"), PROGRAM_NAME);
138 signal_map_event().connect (sigc::mem_fun (*pu, &GenericPluginUI::start_updating));
139 signal_unmap_event().connect (sigc::mem_fun (*pu, &GenericPluginUI::stop_updating));
142 // set_position (Gtk::WIN_POS_MOUSE);
143 set_name ("PluginEditor");
144 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
146 signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), reinterpret_cast<Window*> (this)), false);
147 insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PluginUIWindow::plugin_going_away, this), gui_context());
149 gint h = _pluginui->get_preferred_height ();
150 gint w = _pluginui->get_preferred_width ();
152 if (scrollable) {
153 if (h > 600) h = 600;
154 if (w > 600) w = 600;
156 if (w < 0) {
157 w = 450;
161 set_default_size (w, h);
164 PluginUIWindow::~PluginUIWindow ()
166 delete _pluginui;
169 void
170 PluginUIWindow::set_parent (Gtk::Window* win)
172 parent = win;
175 void
176 PluginUIWindow::on_map ()
178 Window::on_map ();
179 set_keep_above (true);
182 bool
183 PluginUIWindow::on_enter_notify_event (GdkEventCrossing *ev)
185 Keyboard::the_keyboard().enter_window (ev, this);
186 return false;
189 bool
190 PluginUIWindow::on_leave_notify_event (GdkEventCrossing *ev)
192 Keyboard::the_keyboard().leave_window (ev, this);
193 return false;
196 bool
197 PluginUIWindow::on_focus_in_event (GdkEventFocus *ev)
199 Window::on_focus_in_event (ev);
200 //Keyboard::the_keyboard().magic_widget_grab_focus ();
201 return false;
204 bool
205 PluginUIWindow::on_focus_out_event (GdkEventFocus *ev)
207 Window::on_focus_out_event (ev);
208 //Keyboard::the_keyboard().magic_widget_drop_focus ();
209 return false;
212 void
213 PluginUIWindow::on_show ()
215 set_role("plugin_ui");
217 if (_pluginui) {
218 _pluginui->update_presets ();
221 if (_pluginui) {
222 if (_pluginui->on_window_show (_title)) {
223 Window::on_show ();
227 if (parent) {
228 // set_transient_for (*parent);
232 void
233 PluginUIWindow::on_hide ()
235 Window::on_hide ();
237 if (_pluginui) {
238 _pluginui->on_window_hide ();
242 void
243 PluginUIWindow::set_title(const std::string& title)
245 //cout << "PluginUIWindow::set_title(\"" << title << "\"" << endl;
246 Gtk::Window::set_title(title);
247 _title = title;
250 bool
251 #ifdef VST_SUPPORT
252 PluginUIWindow::create_vst_editor(boost::shared_ptr<PluginInsert> insert)
253 #else
254 PluginUIWindow::create_vst_editor(boost::shared_ptr<PluginInsert>)
255 #endif
257 #ifndef VST_SUPPORT
258 return false;
259 #else
261 boost::shared_ptr<VSTPlugin> vp;
263 if ((vp = boost::dynamic_pointer_cast<VSTPlugin> (insert->plugin())) == 0) {
264 error << _("unknown type of editor-supplying plugin (note: no VST support in this version of ardour)")
265 << endmsg;
266 throw failed_constructor ();
267 } else {
268 VSTPluginUI* vpu = new VSTPluginUI (insert, vp);
270 _pluginui = vpu;
271 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
272 add (*vpu);
273 vpu->package (*this);
276 return true;
277 #endif
280 bool
281 #ifdef GTKOSX
282 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert> insert)
283 #else
284 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert>)
285 #endif
287 #ifndef GTKOSX
288 return false;
289 #else
290 VBox* box;
291 _pluginui = create_au_gui (insert, &box);
292 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
293 add (*box);
295 Application::instance()->ActivationChanged.connect (mem_fun (*this, &PluginUIWindow::app_activated));
297 return true;
298 #endif
301 void
302 #ifdef GTKOSX
303 PluginUIWindow::app_activated (bool yn)
304 #else
305 PluginUIWindow::app_activated (bool)
306 #endif
308 #ifdef GTKOSX
309 cerr << "APP activated ? " << yn << endl;
310 if (_pluginui) {
311 if (yn) {
312 if (was_visible) {
313 _pluginui->activate ();
314 present ();
315 was_visible = true;
317 } else {
318 was_visible = is_visible();
319 hide ();
320 _pluginui->deactivate ();
323 #endif
326 bool
327 PluginUIWindow::create_lv2_editor(boost::shared_ptr<PluginInsert> insert)
329 #ifndef HAVE_SLV2
330 return false;
331 #else
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 ();
338 } else {
339 LV2PluginUI* lpu = new LV2PluginUI (insert, vp);
340 _pluginui = lpu;
341 add (*lpu);
342 lpu->package (*this);
345 return true;
346 #endif
349 void
350 PluginUIWindow::keyboard_focused (bool yn)
352 _keyboard_focused = yn;
355 bool
356 PluginUIWindow::on_key_press_event (GdkEventKey* event)
358 if (_keyboard_focused) {
359 if (_pluginui) {
360 if (_pluginui->non_gtk_gui()) {
361 _pluginui->forward_key_event (event);
362 } else {
363 return relay_key_press (event, this);
366 return true;
367 } else {
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
372 key focus.
374 return relay_key_press (event, &PublicEditor::instance());
375 } else {
376 return relay_key_press (event, this);
381 bool
382 PluginUIWindow::on_key_release_event (GdkEventKey *event)
384 if (_keyboard_focused) {
385 if (_pluginui) {
386 if (_pluginui->non_gtk_gui()) {
387 _pluginui->forward_key_event (event);
389 return true;
391 return false;
392 } else {
393 return true;
397 void
398 PluginUIWindow::plugin_going_away ()
400 ENSURE_GUI_THREAD (*this, &PluginUIWindow::plugin_going_away)
402 if (_pluginui) {
403 _pluginui->stop_updating(0);
406 death_connection.disconnect ();
408 delete_when_idle (this);
411 PlugUIBase::PlugUIBase (boost::shared_ptr<PluginInsert> pi)
412 : insert (pi),
413 plugin (insert->plugin()),
414 save_button(_("Add")),
415 bypass_button (_("Bypass")),
416 latency_gui (0),
417 plugin_analysis_expander (_("Plugin analysis"))
419 //preset_combo.set_use_arrows_always(true);
420 update_presets();
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()
458 delete latency_gui;
461 void
462 PlugUIBase::plugin_going_away ()
464 /* drop references to the plugin/insert */
465 insert.reset ();
466 plugin.reset ();
467 death_connection.disconnect ();
470 void
471 PlugUIBase::set_latency_label ()
473 char buf[64];
474 nframes_t l = insert->effective_latency ();
475 nframes_t sr = insert->session().frame_rate();
477 if (l < sr / 1000) {
478 snprintf (buf, sizeof (buf), "latency (%d samples)", l);
479 } else {
480 snprintf (buf, sizeof (buf), "latency (%.2f msecs)", (float) l / ((float) sr / 1000.0f));
483 latency_label.set_text (buf);
486 void
487 PlugUIBase::latency_button_clicked ()
489 if (!latency_gui) {
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 ();
499 void
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);
504 if (p) {
505 bypass_button.set_active (!p->active());
509 void
510 PlugUIBase::setting_selected()
512 if (no_load_preset) {
513 return;
516 if (preset_combo.get_active_text().length() > 0) {
517 const Plugin::PresetRecord* pr = plugin->preset_by_label(preset_combo.get_active_text());
518 if (pr) {
519 plugin->load_preset(pr->uri);
520 } else {
521 warning << string_compose(_("Plugin preset %1 not found"),
522 preset_combo.get_active_text()) << endmsg;
527 void
528 PlugUIBase::save_plugin_setting ()
530 ArdourPrompter prompter (true);
531 prompter.set_title(_("New Preset"));
532 prompter.set_prompt(_("Name of New Preset:"));
533 prompter.add_button (Gtk::Stock::ADD, Gtk::RESPONSE_ACCEPT);
534 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
535 prompter.set_type_hint (Gdk::WINDOW_TYPE_HINT_UTILITY);
537 prompter.show_all();
538 prompter.present ();
540 switch (prompter.run ()) {
541 case Gtk::RESPONSE_ACCEPT:
542 string name;
543 prompter.get_result(name);
544 if (name.length()) {
545 if (plugin->save_preset(name)) {
546 update_presets();
547 no_load_preset = true;
548 preset_combo.set_active_text (name);
549 no_load_preset = false;
552 break;
556 void
557 PlugUIBase::bypass_toggled ()
559 bool x;
561 if ((x = bypass_button.get_active()) == insert->active()) {
562 if (x) {
563 insert->deactivate ();
564 } else {
565 insert->activate ();
570 bool
571 PlugUIBase::focus_toggled (GdkEventButton*)
573 if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
574 Keyboard::the_keyboard().magic_widget_drop_focus();
575 focus_button.remove ();
576 focus_button.add (*focus_out_image);
577 focus_out_image->show ();
578 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));
579 KeyboardFocused (false);
580 } else {
581 Keyboard::the_keyboard().magic_widget_grab_focus();
582 focus_button.remove ();
583 focus_button.add (*focus_in_image);
584 focus_in_image->show ();
585 ARDOUR_UI::instance()->set_tip (focus_button, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME));
586 KeyboardFocused (true);
589 return true;
592 void
593 PlugUIBase::toggle_plugin_analysis()
595 if (plugin_analysis_expander.get_expanded() &&
596 !plugin_analysis_expander.get_child()) {
597 // Create the GUI
598 PluginEqGui *foo = new PluginEqGui(insert);
599 plugin_analysis_expander.add( *foo );
600 plugin_analysis_expander.show_all();
603 Gtk::Widget *gui;
605 if (!plugin_analysis_expander.get_expanded() &&
606 (gui = plugin_analysis_expander.get_child())) {
607 // Hide & remove
608 gui->hide();
609 //plugin_analysis_expander.remove(*gui);
610 plugin_analysis_expander.remove();
612 delete gui;
614 Gtk::Widget *toplevel = plugin_analysis_expander.get_toplevel();
615 if (!toplevel) {
616 std::cerr << "No toplevel widget?!?!" << std::endl;
617 return;
620 Gtk::Container *cont = dynamic_cast<Gtk::Container *>(toplevel);
621 if (!cont) {
622 std::cerr << "Toplevel widget is not a container?!?" << std::endl;
623 return;
626 Gtk::Allocation alloc(0, 0, 50, 50); // Just make it small
627 toplevel->size_allocate(alloc);
631 void
632 PlugUIBase::update_presets ()
634 vector<string> preset_labels;
635 vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets();
637 no_load_preset = true;
639 for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator i = presets.begin(); i != presets.end(); ++i) {
640 preset_labels.push_back(i->label);
643 set_popdown_strings (preset_combo, preset_labels);
645 no_load_preset = false;