i18n fixes.
[ardour2.git] / gtk2_ardour / plugin_ui.cc
blob341fadbeb8696045c10301205f2a693c948be48e
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 framecnt_t const l = insert->effective_latency ();
501 framecnt_t const sr = insert->session().frame_rate ();
503 string t;
505 if (l < sr / 1000) {
506 t = string_compose (_("latency (%1 samples)"), l);
507 } else {
508 t = string_compose (_("latency (%1 ms)"), (float) l / ((float) sr / 1000.0f));
511 latency_label.set_text (t);
514 void
515 PlugUIBase::latency_button_clicked ()
517 if (!latency_gui) {
518 latency_gui = new LatencyGUI (*(insert.get()), insert->session().frame_rate(), insert->session().get_block_size());
519 latency_dialog = new ArdourDialog (_("Edit Latency"), false, false);
520 latency_dialog->get_vbox()->pack_start (*latency_gui);
521 latency_dialog->signal_hide().connect (sigc::mem_fun (*this, &PlugUIBase::set_latency_label));
524 latency_dialog->show_all ();
527 void
528 PlugUIBase::processor_active_changed (boost::weak_ptr<Processor> weak_p)
530 ENSURE_GUI_THREAD (*this, &PlugUIBase::processor_active_changed, weak_p)
531 boost::shared_ptr<Processor> p (weak_p);
532 if (p) {
533 bypass_button.set_active (!p->active());
537 void
538 PlugUIBase::preset_selected ()
540 if (_no_load_preset) {
541 return;
544 if (_preset_combo.get_active_text().length() > 0) {
545 const Plugin::PresetRecord* pr = plugin->preset_by_label (_preset_combo.get_active_text());
546 if (pr) {
547 plugin->load_preset (*pr);
548 } else {
549 warning << string_compose(_("Plugin preset %1 not found"),
550 _preset_combo.get_active_text()) << endmsg;
555 void
556 PlugUIBase::add_plugin_setting ()
558 NewPluginPresetDialog d (plugin);
560 switch (d.run ()) {
561 case Gtk::RESPONSE_ACCEPT:
562 if (d.name().empty()) {
563 break;
566 if (d.replace ()) {
567 plugin->remove_preset (d.name ());
570 Plugin::PresetRecord const r = plugin->save_preset (d.name());
571 if (!r.uri.empty ()) {
572 plugin->load_preset (r);
574 break;
578 void
579 PlugUIBase::save_plugin_setting ()
581 string const name = _preset_combo.get_active_text ();
582 plugin->remove_preset (name);
583 Plugin::PresetRecord const r = plugin->save_preset (name);
584 if (!r.uri.empty ()) {
585 plugin->load_preset (r);
589 void
590 PlugUIBase::delete_plugin_setting ()
592 plugin->remove_preset (_preset_combo.get_active_text ());
595 void
596 PlugUIBase::bypass_toggled ()
598 bool x;
600 if ((x = bypass_button.get_active()) == insert->active()) {
601 if (x) {
602 insert->deactivate ();
603 } else {
604 insert->activate ();
609 bool
610 PlugUIBase::focus_toggled (GdkEventButton*)
612 if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
613 Keyboard::the_keyboard().magic_widget_drop_focus();
614 focus_button.remove ();
615 focus_button.add (*focus_out_image);
616 focus_out_image->show ();
617 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));
618 KeyboardFocused (false);
619 } else {
620 Keyboard::the_keyboard().magic_widget_grab_focus();
621 focus_button.remove ();
622 focus_button.add (*focus_in_image);
623 focus_in_image->show ();
624 ARDOUR_UI::instance()->set_tip (focus_button, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME));
625 KeyboardFocused (true);
628 return true;
631 void
632 PlugUIBase::toggle_plugin_analysis()
634 if (plugin_analysis_expander.get_expanded() &&
635 !plugin_analysis_expander.get_child()) {
636 // Create the GUI
637 PluginEqGui *foo = new PluginEqGui(insert);
638 plugin_analysis_expander.add( *foo );
639 plugin_analysis_expander.show_all();
642 Gtk::Widget *gui;
644 if (!plugin_analysis_expander.get_expanded() &&
645 (gui = plugin_analysis_expander.get_child())) {
646 // Hide & remove
647 gui->hide();
648 //plugin_analysis_expander.remove(*gui);
649 plugin_analysis_expander.remove();
651 delete gui;
653 Gtk::Widget *toplevel = plugin_analysis_expander.get_toplevel();
654 if (!toplevel) {
655 std::cerr << "No toplevel widget?!?!" << std::endl;
656 return;
659 Gtk::Container *cont = dynamic_cast<Gtk::Container *>(toplevel);
660 if (!cont) {
661 std::cerr << "Toplevel widget is not a container?!?" << std::endl;
662 return;
665 Gtk::Allocation alloc(0, 0, 50, 50); // Just make it small
666 toplevel->size_allocate(alloc);
670 void
671 PlugUIBase::update_preset_list ()
673 vector<string> preset_labels;
674 vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets();
676 ++_no_load_preset;
678 for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator i = presets.begin(); i != presets.end(); ++i) {
679 preset_labels.push_back (i->label);
682 set_popdown_strings (_preset_combo, preset_labels);
684 --_no_load_preset;
687 void
688 PlugUIBase::update_preset ()
690 Plugin::PresetRecord p = plugin->last_preset();
692 ++_no_load_preset;
693 _preset_combo.set_active_text (p.label);
694 --_no_load_preset;
696 save_button.set_sensitive (!p.uri.empty() && p.user);
697 delete_button.set_sensitive (!p.uri.empty() && p.user);
699 update_preset_modified ();
702 void
703 PlugUIBase::update_preset_modified ()
705 if (plugin->last_preset().uri.empty()) {
706 _preset_modified.set_text ("");
707 return;
710 bool const c = plugin->parameter_changed_since_last_preset ();
711 if (_preset_modified.get_text().empty() == c) {
712 _preset_modified.set_text (c ? "*" : "");
716 void
717 PlugUIBase::parameter_changed (uint32_t, float)
719 update_preset_modified ();
722 void
723 PlugUIBase::preset_added_or_removed ()
725 /* Update both the list and the currently-displayed preset */
726 update_preset_list ();
727 update_preset ();