Clean up region.h and trim include tree.
[ardour2.git] / gtk2_ardour / plugin_ui.cc
blob9ad9e38f8ea3a951685650e8bab208fa188f2bbf
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 #include "vst_pluginui.h"
52 #endif
53 #ifdef HAVE_SLV2
54 #include "ardour/lv2_plugin.h"
55 #include "lv2_plugin_ui.h"
56 #endif
58 #include <lrdf.h>
60 #include "ardour_dialog.h"
61 #include "ardour_ui.h"
62 #include "prompter.h"
63 #include "plugin_ui.h"
64 #include "utils.h"
65 #include "gui_thread.h"
66 #include "public_editor.h"
67 #include "keyboard.h"
68 #include "latency_gui.h"
69 #include "plugin_eq_gui.h"
70 #include "new_plugin_preset_dialog.h"
72 #include "i18n.h"
74 using namespace std;
75 using namespace ARDOUR;
76 using namespace PBD;
77 using namespace Gtkmm2ext;
78 using namespace Gtk;
80 PluginUIWindow::PluginUIWindow (Gtk::Window* win, boost::shared_ptr<PluginInsert> insert, bool scrollable)
81 : parent (win)
82 , was_visible (false)
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()) {
92 case ARDOUR::VST:
93 have_gui = create_vst_editor (insert);
94 break;
96 case ARDOUR::AudioUnit:
97 have_gui = create_audiounit_editor (insert);
98 break;
100 case ARDOUR::LADSPA:
101 error << _("Eh? LADSPA plugins don't have editors!") << endmsg;
102 break;
104 case ARDOUR::LV2:
105 have_gui = create_lv2_editor (insert);
106 break;
108 default:
109 #ifndef VST_SUPPORT
110 error << _("unknown type of editor-supplying plugin (note: no VST support in this version of ardour)")
111 << endmsg;
112 #else
113 error << _("unknown type of editor-supplying plugin")
114 << endmsg;
115 #endif
116 throw failed_constructor ();
121 if (!have_gui) {
123 GenericPluginUI* pu = new GenericPluginUI (insert, scrollable);
125 _pluginui = pu;
126 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
127 add (*pu);
130 Gtk::HBox *hbox = new Gtk::HBox();
131 hbox->pack_start( *pu);
132 // TODO: this should be nicer
133 hbox->pack_start( eqgui_bin );
135 add (*manage(hbox));
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 ();
154 if (scrollable) {
155 if (h > 600) h = 600;
156 if (w > 600) w = 600;
158 if (w < 0) {
159 w = 450;
163 set_default_size (w, h);
166 PluginUIWindow::~PluginUIWindow ()
168 delete _pluginui;
171 void
172 PluginUIWindow::set_parent (Gtk::Window* win)
174 parent = win;
177 void
178 PluginUIWindow::on_map ()
180 Window::on_map ();
181 set_keep_above (true);
184 bool
185 PluginUIWindow::on_enter_notify_event (GdkEventCrossing *ev)
187 Keyboard::the_keyboard().enter_window (ev, this);
188 return false;
191 bool
192 PluginUIWindow::on_leave_notify_event (GdkEventCrossing *ev)
194 Keyboard::the_keyboard().leave_window (ev, this);
195 return false;
198 bool
199 PluginUIWindow::on_focus_in_event (GdkEventFocus *ev)
201 Window::on_focus_in_event (ev);
202 //Keyboard::the_keyboard().magic_widget_grab_focus ();
203 return false;
206 bool
207 PluginUIWindow::on_focus_out_event (GdkEventFocus *ev)
209 Window::on_focus_out_event (ev);
210 //Keyboard::the_keyboard().magic_widget_drop_focus ();
211 return false;
214 void
215 PluginUIWindow::on_show ()
217 set_role("plugin_ui");
219 if (_pluginui) {
220 _pluginui->update_preset_list ();
221 _pluginui->update_preset ();
224 if (_pluginui) {
225 if (_pluginui->on_window_show (_title)) {
226 Window::on_show ();
230 if (parent) {
231 // set_transient_for (*parent);
235 void
236 PluginUIWindow::on_hide ()
238 Window::on_hide ();
240 if (_pluginui) {
241 _pluginui->on_window_hide ();
245 void
246 PluginUIWindow::set_title(const std::string& title)
248 Gtk::Window::set_title(title);
249 _title = title;
252 bool
253 #ifdef VST_SUPPORT
254 PluginUIWindow::create_vst_editor(boost::shared_ptr<PluginInsert> insert)
255 #else
256 PluginUIWindow::create_vst_editor(boost::shared_ptr<PluginInsert>)
257 #endif
259 #ifndef VST_SUPPORT
260 return false;
261 #else
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)")
267 << endmsg;
268 throw failed_constructor ();
269 } else {
270 VSTPluginUI* vpu = new VSTPluginUI (insert, vp);
272 _pluginui = vpu;
273 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
274 add (*vpu);
275 vpu->package (*this);
278 return true;
279 #endif
282 bool
283 #ifdef GTKOSX
284 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert> insert)
285 #else
286 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert>)
287 #endif
289 #ifndef GTKOSX
290 return false;
291 #else
292 VBox* box;
293 _pluginui = create_au_gui (insert, &box);
294 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
295 add (*box);
297 Application::instance()->ActivationChanged.connect (mem_fun (*this, &PluginUIWindow::app_activated));
299 return true;
300 #endif
303 void
304 #ifdef GTKOSX
305 PluginUIWindow::app_activated (bool yn)
306 #else
307 PluginUIWindow::app_activated (bool)
308 #endif
310 #ifdef GTKOSX
311 cerr << "APP activated ? " << yn << endl;
312 if (_pluginui) {
313 if (yn) {
314 if (was_visible) {
315 _pluginui->activate ();
316 present ();
317 was_visible = true;
319 } else {
320 was_visible = is_visible();
321 hide ();
322 _pluginui->deactivate ();
325 #endif
328 bool
329 PluginUIWindow::create_lv2_editor(boost::shared_ptr<PluginInsert> insert)
331 #ifndef HAVE_SLV2
332 return false;
333 #else
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 ();
340 } else {
341 LV2PluginUI* lpu = new LV2PluginUI (insert, vp);
342 _pluginui = lpu;
343 add (*lpu);
344 lpu->package (*this);
347 return true;
348 #endif
351 void
352 PluginUIWindow::keyboard_focused (bool yn)
354 _keyboard_focused = yn;
357 bool
358 PluginUIWindow::on_key_press_event (GdkEventKey* event)
360 if (_keyboard_focused) {
361 if (_pluginui) {
362 if (_pluginui->non_gtk_gui()) {
363 _pluginui->forward_key_event (event);
364 } else {
365 return relay_key_press (event, this);
368 return true;
369 } else {
370 /* for us to be getting key press events, there really
371 MUST be a _pluginui, but just to be safe, check ...
374 if (_pluginui) {
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
379 key focus.
381 return relay_key_press (event, &PublicEditor::instance());
382 } else {
383 return relay_key_press (event, this);
385 } else {
386 return false;
391 bool
392 PluginUIWindow::on_key_release_event (GdkEventKey *event)
394 if (_keyboard_focused) {
395 if (_pluginui) {
396 if (_pluginui->non_gtk_gui()) {
397 _pluginui->forward_key_event (event);
399 return true;
401 return false;
402 } else {
403 return true;
407 void
408 PluginUIWindow::plugin_going_away ()
410 ENSURE_GUI_THREAD (*this, &PluginUIWindow::plugin_going_away)
412 if (_pluginui) {
413 _pluginui->stop_updating(0);
416 death_connection.disconnect ();
418 delete_when_idle (this);
421 PlugUIBase::PlugUIBase (boost::shared_ptr<PluginInsert> pi)
422 : insert (pi),
423 plugin (insert->plugin()),
424 add_button (_("Add")),
425 save_button (_("Save")),
426 delete_button (_("Delete")),
427 bypass_button (_("Bypass")),
428 latency_gui (0),
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));
434 _no_load_preset = 0;
436 _preset_box.pack_start (_preset_combo);
437 _preset_box.pack_start (_preset_modified);
439 update_preset_list ();
440 update_preset ();
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()
485 delete latency_gui;
488 void
489 PlugUIBase::plugin_going_away ()
491 /* drop references to the plugin/insert */
492 insert.reset ();
493 plugin.reset ();
494 death_connection.disconnect ();
497 void
498 PlugUIBase::set_latency_label ()
500 char buf[64];
501 framecnt_t const l = insert->effective_latency ();
502 framecnt_t const sr = insert->session().frame_rate ();
504 if (l < sr / 1000) {
505 snprintf (buf, sizeof (buf), "latency (%" PRId64 " samples)", l);
506 } else {
507 snprintf (buf, sizeof (buf), "latency (%.2f msecs)", (float) l / ((float) sr / 1000.0f));
510 latency_label.set_text (buf);
513 void
514 PlugUIBase::latency_button_clicked ()
516 if (!latency_gui) {
517 latency_gui = new LatencyGUI (*(insert.get()), insert->session().frame_rate(), insert->session().get_block_size());
518 latency_dialog = new ArdourDialog ("Edit Latency", false, false);
519 latency_dialog->get_vbox()->pack_start (*latency_gui);
520 latency_dialog->signal_hide().connect (sigc::mem_fun (*this, &PlugUIBase::set_latency_label));
523 latency_dialog->show_all ();
526 void
527 PlugUIBase::processor_active_changed (boost::weak_ptr<Processor> weak_p)
529 ENSURE_GUI_THREAD (*this, &PlugUIBase::processor_active_changed, weak_p)
530 boost::shared_ptr<Processor> p (weak_p);
531 if (p) {
532 bypass_button.set_active (!p->active());
536 void
537 PlugUIBase::preset_selected ()
539 if (_no_load_preset) {
540 return;
543 if (_preset_combo.get_active_text().length() > 0) {
544 const Plugin::PresetRecord* pr = plugin->preset_by_label (_preset_combo.get_active_text());
545 if (pr) {
546 plugin->load_preset (*pr);
547 } else {
548 warning << string_compose(_("Plugin preset %1 not found"),
549 _preset_combo.get_active_text()) << endmsg;
554 void
555 PlugUIBase::add_plugin_setting ()
557 NewPluginPresetDialog d (plugin);
559 switch (d.run ()) {
560 case Gtk::RESPONSE_ACCEPT:
561 if (d.name().empty()) {
562 break;
565 if (d.replace ()) {
566 plugin->remove_preset (d.name ());
569 Plugin::PresetRecord const r = plugin->save_preset (d.name());
570 if (!r.uri.empty ()) {
571 plugin->load_preset (r);
573 break;
577 void
578 PlugUIBase::save_plugin_setting ()
580 string const name = _preset_combo.get_active_text ();
581 plugin->remove_preset (name);
582 Plugin::PresetRecord const r = plugin->save_preset (name);
583 if (!r.uri.empty ()) {
584 plugin->load_preset (r);
588 void
589 PlugUIBase::delete_plugin_setting ()
591 plugin->remove_preset (_preset_combo.get_active_text ());
594 void
595 PlugUIBase::bypass_toggled ()
597 bool x;
599 if ((x = bypass_button.get_active()) == insert->active()) {
600 if (x) {
601 insert->deactivate ();
602 } else {
603 insert->activate ();
608 bool
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);
618 } else {
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);
627 return true;
630 void
631 PlugUIBase::toggle_plugin_analysis()
633 if (plugin_analysis_expander.get_expanded() &&
634 !plugin_analysis_expander.get_child()) {
635 // Create the GUI
636 PluginEqGui *foo = new PluginEqGui(insert);
637 plugin_analysis_expander.add( *foo );
638 plugin_analysis_expander.show_all();
641 Gtk::Widget *gui;
643 if (!plugin_analysis_expander.get_expanded() &&
644 (gui = plugin_analysis_expander.get_child())) {
645 // Hide & remove
646 gui->hide();
647 //plugin_analysis_expander.remove(*gui);
648 plugin_analysis_expander.remove();
650 delete gui;
652 Gtk::Widget *toplevel = plugin_analysis_expander.get_toplevel();
653 if (!toplevel) {
654 std::cerr << "No toplevel widget?!?!" << std::endl;
655 return;
658 Gtk::Container *cont = dynamic_cast<Gtk::Container *>(toplevel);
659 if (!cont) {
660 std::cerr << "Toplevel widget is not a container?!?" << std::endl;
661 return;
664 Gtk::Allocation alloc(0, 0, 50, 50); // Just make it small
665 toplevel->size_allocate(alloc);
669 void
670 PlugUIBase::update_preset_list ()
672 vector<string> preset_labels;
673 vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets();
675 ++_no_load_preset;
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;
686 void
687 PlugUIBase::update_preset ()
689 Plugin::PresetRecord p = plugin->last_preset();
691 ++_no_load_preset;
692 _preset_combo.set_active_text (p.label);
693 --_no_load_preset;
695 save_button.set_sensitive (!p.uri.empty() && p.user);
696 delete_button.set_sensitive (!p.uri.empty() && p.user);
698 update_preset_modified ();
701 void
702 PlugUIBase::update_preset_modified ()
704 if (plugin->last_preset().uri.empty()) {
705 _preset_modified.set_text ("");
706 return;
709 bool const c = plugin->parameter_changed_since_last_preset ();
710 if (_preset_modified.get_text().empty() == c) {
711 _preset_modified.set_text (c ? "*" : "");
715 void
716 PlugUIBase::parameter_changed (uint32_t, float)
718 update_preset_modified ();
721 void
722 PlugUIBase::preset_added_or_removed ()
724 /* Update both the list and the currently-displayed preset */
725 update_preset_list ();
726 update_preset ();