try to fix mistake introduced in 7207 that stole key events from GTK plugin GUIs
[ardour2.git] / gtk2_ardour / plugin_ui.cc
blobe8bde421e947d3b846bd2ddaa5cd53d7954efe0b
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 #include <climits>
21 #include <cerrno>
22 #include <cmath>
23 #include <string>
25 #include <pbd/stl_delete.h>
26 #include <pbd/xml++.h>
27 #include <pbd/failed_constructor.h>
29 #include <gtkmm/widget.h>
30 #include <gtkmm2ext/click_box.h>
31 #include <gtkmm2ext/fastmeter.h>
32 #include <gtkmm2ext/barcontroller.h>
33 #include <gtkmm2ext/utils.h>
34 #include <gtkmm2ext/doi.h>
35 #include <gtkmm2ext/slider_controller.h>
36 #include <gtkmm2ext/application.h>
38 #include <midi++/manager.h>
40 #include <ardour/plugin.h>
41 #include <ardour/insert.h>
42 #include <ardour/ladspa_plugin.h>
43 #ifdef VST_SUPPORT
44 #include <ardour/vst_plugin.h>
45 #endif
46 #ifdef HAVE_LV2
47 #include <ardour/lv2_plugin.h>
48 #include "lv2_plugin_ui.h"
49 #endif
51 #include <lrdf.h>
53 #include "ardour_ui.h"
54 #include "prompter.h"
55 #include "plugin_ui.h"
56 #include "utils.h"
57 #include "gui_thread.h"
58 #include "public_editor.h"
59 #include "keyboard.h"
61 #include "i18n.h"
63 using namespace std;
64 using namespace ARDOUR;
65 using namespace PBD;
66 using namespace Gtkmm2ext;
67 using namespace Gtk;
68 using namespace sigc;
70 PluginUIWindow::PluginUIWindow (Gtk::Window* win, boost::shared_ptr<PluginInsert> insert, bool scrollable)
71 : parent (win)
72 , was_visible (false)
73 , _keyboard_focused (false)
75 bool have_gui = false;
76 was_visible = false;
78 if (insert->plugin()->has_editor()) {
79 switch (insert->type()) {
80 case ARDOUR::VST:
81 have_gui = create_vst_editor (insert);
82 break;
84 case ARDOUR::AudioUnit:
85 have_gui = create_audiounit_editor (insert);
86 break;
88 case ARDOUR::LADSPA:
89 error << _("Eh? LADSPA plugins don't have editors!") << endmsg;
90 break;
92 case ARDOUR::LV2:
93 have_gui = create_lv2_editor (insert);
94 break;
96 default:
97 #ifndef VST_SUPPORT
98 error << _("unknown type of editor-supplying plugin (note: no VST support in this version of ardour)")
99 << endmsg;
100 #else
101 error << _("unknown type of editor-supplying plugin")
102 << endmsg;
103 #endif
104 throw failed_constructor ();
109 if (!have_gui) {
111 GenericPluginUI* pu = new GenericPluginUI (insert, scrollable);
113 _pluginui = pu;
114 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
115 add (*pu);
117 set_wmclass (X_("ardour_plugin_editor"), PROGRAM_NAME);
119 signal_map_event().connect (mem_fun (*pu, &GenericPluginUI::start_updating));
120 signal_unmap_event().connect (mem_fun (*pu, &GenericPluginUI::stop_updating));
123 // set_position (Gtk::WIN_POS_MOUSE);
124 set_name ("PluginEditor");
125 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
127 signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), reinterpret_cast<Window*> (this)), false);
128 death_connection = insert->GoingAway.connect (mem_fun(*this, &PluginUIWindow::plugin_going_away));
130 gint h = _pluginui->get_preferred_height ();
131 gint w = _pluginui->get_preferred_width ();
133 if (scrollable) {
134 if (h > 600) h = 600;
135 if (w > 600) w = 600;
137 if (w < 0) {
138 w = 450;
142 set_default_size (w, h);
145 PluginUIWindow::~PluginUIWindow ()
147 delete _pluginui;
150 void
151 PluginUIWindow::set_parent (Gtk::Window* win)
153 parent = win;
156 void
157 PluginUIWindow::on_map ()
159 Window::on_map ();
160 set_keep_above (true);
163 bool
164 PluginUIWindow::on_enter_notify_event (GdkEventCrossing *ev)
166 Keyboard::the_keyboard().enter_window (ev, this);
167 return false;
170 bool
171 PluginUIWindow::on_leave_notify_event (GdkEventCrossing *ev)
173 Keyboard::the_keyboard().leave_window (ev, this);
174 return false;
177 bool
178 PluginUIWindow::on_focus_in_event (GdkEventFocus *ev)
180 Window::on_focus_in_event (ev);
181 //Keyboard::the_keyboard().magic_widget_grab_focus ();
182 return false;
185 bool
186 PluginUIWindow::on_focus_out_event (GdkEventFocus *ev)
188 Window::on_focus_out_event (ev);
189 //Keyboard::the_keyboard().magic_widget_drop_focus ();
190 return false;
193 void
194 PluginUIWindow::on_show ()
196 set_role("plugin_ui");
198 if (_pluginui) {
199 _pluginui->update_presets ();
202 if (_pluginui) {
203 if (_pluginui->on_window_show (_title)) {
204 Window::on_show ();
208 if (parent) {
209 // set_transient_for (*parent);
213 void
214 PluginUIWindow::on_hide ()
216 Window::on_hide ();
218 if (_pluginui) {
219 _pluginui->on_window_hide ();
223 void
224 PluginUIWindow::set_title(const Glib::ustring& title)
226 //cout << "PluginUIWindow::set_title(\"" << title << "\"" << endl;
227 Gtk::Window::set_title(title);
228 _title = title;
231 bool
232 PluginUIWindow::create_vst_editor(boost::shared_ptr<PluginInsert> insert)
234 #ifndef VST_SUPPORT
235 return false;
236 #else
238 boost::shared_ptr<VSTPlugin> vp;
240 if ((vp = boost::dynamic_pointer_cast<VSTPlugin> (insert->plugin())) == 0) {
241 error << _("unknown type of editor-supplying plugin (note: no VST support in this version of ardour)")
242 << endmsg;
243 throw failed_constructor ();
244 } else {
245 VSTPluginUI* vpu = new VSTPluginUI (insert, vp);
247 _pluginui = vpu;
248 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
249 add (*vpu);
250 vpu->package (*this);
252 return true;
253 #endif
256 bool
257 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert> insert)
259 #if !defined(HAVE_AUDIOUNITS) || !defined(GTKOSX)
260 return false;
261 #else
262 VBox* box;
263 _pluginui = create_au_gui (insert, &box);
264 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
265 add (*box);
267 Application::instance()->ActivationChanged.connect (mem_fun (*this, &PluginUIWindow::app_activated));
269 return true;
270 #endif
273 void
274 PluginUIWindow::app_activated (bool yn)
276 #if defined (HAVE_AUDIOUNITS) && defined(GTKOSX)
277 if (_pluginui) {
278 if (yn) {
279 if (was_visible) {
280 _pluginui->activate ();
281 // present ();
282 show ();
283 was_visible = true;
285 } else {
286 was_visible = is_visible();
287 hide ();
288 _pluginui->deactivate ();
291 #endif
294 bool
295 PluginUIWindow::create_lv2_editor(boost::shared_ptr<PluginInsert> insert)
297 #ifndef HAVE_LV2
298 return false;
299 #else
301 boost::shared_ptr<LV2Plugin> vp;
303 if ((vp = boost::dynamic_pointer_cast<LV2Plugin> (insert->plugin())) == 0) {
304 error << _("create_lv2_editor called on non-LV2 plugin") << endmsg;
305 throw failed_constructor ();
306 } else {
307 LV2PluginUI* lpu = new LV2PluginUI (insert, vp);
308 _pluginui = lpu;
309 add (*lpu);
310 lpu->package (*this);
313 return true;
314 #endif
317 void
318 PluginUIWindow::keyboard_focused (bool yn)
320 _keyboard_focused = yn;
323 bool
324 PluginUIWindow::on_key_press_event (GdkEventKey* event)
326 if (_keyboard_focused) {
327 if (_pluginui) {
328 _pluginui->forward_key_event (event);
330 return true;
331 } else {
332 if (_pluginui->non_gtk_gui()) {
333 /* pass editor window as the window for the event
334 to be handled in, not this one, because there are
335 no widgets in this window that we want to have
336 key focus.
338 return relay_key_press (event, &PublicEditor::instance());
339 } else {
340 return relay_key_press (event, this);
345 bool
346 PluginUIWindow::on_key_release_event (GdkEventKey* event)
348 if (_keyboard_focused) {
349 if (_pluginui) {
350 _pluginui->forward_key_event (event);
352 return false;
353 } else {
354 return true;
358 void
359 PluginUIWindow::plugin_going_away ()
361 ENSURE_GUI_THREAD(mem_fun(*this, &PluginUIWindow::plugin_going_away));
363 if (_pluginui) {
364 _pluginui->stop_updating(0);
367 death_connection.disconnect ();
368 delete_when_idle (this);
371 PlugUIBase::PlugUIBase (boost::shared_ptr<PluginInsert> pi)
372 : insert (pi),
373 plugin (insert->plugin()),
374 save_button(_("Save")),
375 bypass_button (_("Bypass"))
377 //preset_combo.set_use_arrows_always(true);
378 preset_combo.set_size_request (100, -1);
379 update_presets ();
381 preset_combo.signal_changed().connect(mem_fun(*this, &PlugUIBase::setting_selected));
382 no_load_preset = false;
384 save_button.set_name ("PluginSaveButton");
385 save_button.signal_clicked().connect(mem_fun(*this, &PlugUIBase::save_plugin_setting));
387 insert->active_changed.connect (mem_fun(*this, &PlugUIBase::redirect_active_changed));
388 bypass_button.set_active (!pi->active());
390 bypass_button.set_name ("PluginBypassButton");
391 bypass_button.signal_toggled().connect (mem_fun(*this, &PlugUIBase::bypass_toggled));
392 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
394 focus_button.signal_button_release_event().connect (mem_fun(*this, &PlugUIBase::focus_toggled));
395 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
397 /* these images are not managed, so that we can remove them at will */
399 focus_out_image = new Image (get_icon (X_("computer_keyboard")));
400 focus_in_image = new Image (get_icon (X_("computer_keyboard_active")));
402 focus_button.add (*focus_out_image);
404 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).c_str(), "");
405 ARDOUR_UI::instance()->set_tip (&bypass_button, _("Click to enable/disable this plugin"), "");
407 insert->GoingAway.connect (mem_fun (*this, &PlugUIBase::plugin_going_away));
410 PlugUIBase::~PlugUIBase ()
414 void
415 PlugUIBase::plugin_going_away ()
417 /* drop references to the plugin/insert */
418 insert.reset ();
419 plugin.reset ();
422 void
423 PlugUIBase::redirect_active_changed (Redirect* r, void* src)
425 ENSURE_GUI_THREAD(bind (mem_fun(*this, &PlugUIBase::redirect_active_changed), r, src));
426 bypass_button.set_active (!r->active());
429 void
430 PlugUIBase::setting_selected()
432 if (no_load_preset) {
433 return;
436 if (preset_combo.get_active_text().length() > 0) {
437 if (!plugin->load_preset(preset_combo.get_active_text())) {
438 warning << string_compose(_("Plugin preset %1 not found"), preset_combo.get_active_text()) << endmsg;
443 void
444 PlugUIBase::save_plugin_setting ()
446 ArdourPrompter prompter (true);
447 prompter.set_prompt(_("Name of New Preset:"));
448 prompter.add_button (Gtk::Stock::ADD, Gtk::RESPONSE_ACCEPT);
449 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
450 prompter.set_type_hint (Gdk::WINDOW_TYPE_HINT_UTILITY);
452 prompter.show_all();
453 prompter.present ();
455 switch (prompter.run ()) {
456 case Gtk::RESPONSE_ACCEPT:
458 string name;
460 prompter.get_result(name);
462 if (name.length()) {
463 if (plugin->save_preset(name)) {
465 /* a rather inefficient way to add the newly saved preset
466 to the list.
469 no_load_preset = true;
470 set_popdown_strings (preset_combo, plugin->get_presets());
471 preset_combo.set_active_text (name);
472 no_load_preset = false;
475 break;
479 void
480 PlugUIBase::bypass_toggled ()
482 bool x;
484 if ((x = bypass_button.get_active()) == insert->active()) {
485 insert->set_active (!x, this);
489 bool
490 PlugUIBase::focus_toggled (GdkEventButton* ev)
492 if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
493 Keyboard::the_keyboard().magic_widget_drop_focus();
494 focus_button.remove ();
495 focus_button.add (*focus_out_image);
496 focus_out_image->show ();
497 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).c_str(), "");
498 KeyboardFocused (false);
499 } else {
500 Keyboard::the_keyboard().magic_widget_grab_focus();
501 focus_button.remove ();
502 focus_button.add (*focus_in_image);
503 focus_in_image->show ();
504 ARDOUR_UI::instance()->set_tip (&focus_button, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME).c_str(), "");
505 KeyboardFocused (true);
508 return true;
511 void
512 PlugUIBase::update_presets ()
514 vector<string> presets = plugin->get_presets();
515 no_load_preset = true;
517 set_popdown_strings (preset_combo, plugin->get_presets());
519 string current_preset = plugin->current_preset();
521 if (!current_preset.empty()) {
522 for (vector<string>::iterator p = presets.begin(); p != presets.end(); ++p) {
523 if (*p == current_preset) {
524 preset_combo.set_active_text (current_preset);
529 no_load_preset = false;