tiny cleanup
[ardour2.git] / gtk2_ardour / plugin_ui.cc
blob11e931b91db9e16fd2a0b7bec090a1a467b8c9cc
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;
77 if (insert->plugin()->has_editor()) {
78 switch (insert->type()) {
79 case ARDOUR::VST:
80 have_gui = create_vst_editor (insert);
81 break;
83 case ARDOUR::AudioUnit:
84 have_gui = create_audiounit_editor (insert);
85 break;
87 case ARDOUR::LADSPA:
88 error << _("Eh? LADSPA plugins don't have editors!") << endmsg;
89 break;
91 case ARDOUR::LV2:
92 have_gui = create_lv2_editor (insert);
93 break;
95 default:
96 #ifndef VST_SUPPORT
97 error << _("unknown type of editor-supplying plugin (note: no VST support in this version of ardour)")
98 << endmsg;
99 #else
100 error << _("unknown type of editor-supplying plugin")
101 << endmsg;
102 #endif
103 throw failed_constructor ();
108 if (!have_gui) {
110 GenericPluginUI* pu = new GenericPluginUI (insert, scrollable);
112 _pluginui = pu;
113 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
114 add (*pu);
116 set_wmclass (X_("ardour_plugin_editor"), PROGRAM_NAME);
118 signal_map_event().connect (mem_fun (*pu, &GenericPluginUI::start_updating));
119 signal_unmap_event().connect (mem_fun (*pu, &GenericPluginUI::stop_updating));
122 // set_position (Gtk::WIN_POS_MOUSE);
123 set_name ("PluginEditor");
124 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
126 signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), reinterpret_cast<Window*> (this)), false);
127 death_connection = insert->GoingAway.connect (mem_fun(*this, &PluginUIWindow::plugin_going_away));
129 gint h = _pluginui->get_preferred_height ();
130 gint w = _pluginui->get_preferred_width ();
132 if (scrollable) {
133 if (h > 600) h = 600;
134 if (w > 600) w = 600;
136 if (w < 0) {
137 w = 450;
141 set_default_size (w, h);
144 PluginUIWindow::~PluginUIWindow ()
146 delete _pluginui;
149 void
150 PluginUIWindow::set_parent (Gtk::Window* win)
152 parent = win;
155 void
156 PluginUIWindow::on_map ()
158 Window::on_map ();
159 set_keep_above (true);
162 bool
163 PluginUIWindow::on_enter_notify_event (GdkEventCrossing *ev)
165 Keyboard::the_keyboard().enter_window (ev, this);
166 return false;
169 bool
170 PluginUIWindow::on_leave_notify_event (GdkEventCrossing *ev)
172 Keyboard::the_keyboard().leave_window (ev, this);
173 return false;
176 bool
177 PluginUIWindow::on_focus_in_event (GdkEventFocus *ev)
179 Window::on_focus_in_event (ev);
180 //Keyboard::the_keyboard().magic_widget_grab_focus ();
181 return false;
184 bool
185 PluginUIWindow::on_focus_out_event (GdkEventFocus *ev)
187 Window::on_focus_out_event (ev);
188 //Keyboard::the_keyboard().magic_widget_drop_focus ();
189 return false;
192 void
193 PluginUIWindow::on_show ()
195 set_role("plugin_ui");
197 if (_pluginui) {
198 _pluginui->update_presets ();
201 if (_pluginui) {
202 if (_pluginui->on_window_show (_title)) {
203 Window::on_show ();
207 if (parent) {
208 // set_transient_for (*parent);
212 void
213 PluginUIWindow::on_hide ()
215 Window::on_hide ();
217 if (_pluginui) {
218 _pluginui->on_window_hide ();
222 void
223 PluginUIWindow::set_title(const Glib::ustring& title)
225 //cout << "PluginUIWindow::set_title(\"" << title << "\"" << endl;
226 Gtk::Window::set_title(title);
227 _title = title;
230 bool
231 PluginUIWindow::create_vst_editor(boost::shared_ptr<PluginInsert> insert)
233 #ifndef VST_SUPPORT
234 return false;
235 #else
237 boost::shared_ptr<VSTPlugin> vp;
239 if ((vp = boost::dynamic_pointer_cast<VSTPlugin> (insert->plugin())) == 0) {
240 error << _("unknown type of editor-supplying plugin (note: no VST support in this version of ardour)")
241 << endmsg;
242 throw failed_constructor ();
243 } else {
244 VSTPluginUI* vpu = new VSTPluginUI (insert, vp);
246 _pluginui = vpu;
247 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
248 add (*vpu);
249 vpu->package (*this);
251 return true;
252 #endif
255 bool
256 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert> insert)
258 #if !defined(HAVE_AUDIOUNITS) || !defined(GTKOSX)
259 return false;
260 #else
261 VBox* box;
262 _pluginui = create_au_gui (insert, &box);
263 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
264 add (*box);
266 Application::instance()->ActivationChanged.connect (mem_fun (*this, &PluginUIWindow::app_activated));
268 return true;
269 #endif
272 void
273 PluginUIWindow::app_activated (bool yn)
275 #if defined (HAVE_AUDIOUNITS) && defined(GTKOSX)
276 if (_pluginui) {
277 if (yn) {
278 if (was_visible) {
279 _pluginui->activate ();
280 // present ();
281 show ();
282 was_visible = true;
284 } else {
285 was_visible = is_visible();
286 hide ();
287 _pluginui->deactivate ();
290 #endif
293 bool
294 PluginUIWindow::create_lv2_editor(boost::shared_ptr<PluginInsert> insert)
296 #ifndef HAVE_LV2
297 return false;
298 #else
300 boost::shared_ptr<LV2Plugin> vp;
302 if ((vp = boost::dynamic_pointer_cast<LV2Plugin> (insert->plugin())) == 0) {
303 error << _("create_lv2_editor called on non-LV2 plugin") << endmsg;
304 throw failed_constructor ();
305 } else {
306 LV2PluginUI* lpu = new LV2PluginUI (insert, vp);
307 _pluginui = lpu;
308 add (*lpu);
309 lpu->package (*this);
312 return true;
313 #endif
316 void
317 PluginUIWindow::keyboard_focused (bool yn)
319 _keyboard_focused = yn;
322 bool
323 PluginUIWindow::on_key_press_event (GdkEventKey* event)
325 if (_keyboard_focused) {
326 if (_pluginui) {
327 _pluginui->forward_key_event (event);
329 return true;
330 } else {
331 if (_pluginui->non_gtk_gui()) {
332 /* pass editor window as the window for the event
333 to be handled in, not this one, because there are
334 no widgets in this window that we want to have
335 key focus.
337 return relay_key_press (event, &PublicEditor::instance());
338 } else {
339 return relay_key_press (event, this);
344 bool
345 PluginUIWindow::on_key_release_event (GdkEventKey* event)
347 if (_keyboard_focused) {
348 if (_pluginui) {
349 _pluginui->forward_key_event (event);
351 return false;
352 } else {
353 return true;
357 void
358 PluginUIWindow::plugin_going_away ()
360 ENSURE_GUI_THREAD(mem_fun(*this, &PluginUIWindow::plugin_going_away));
362 if (_pluginui) {
363 _pluginui->stop_updating(0);
366 death_connection.disconnect ();
367 delete_when_idle (this);
370 PlugUIBase::PlugUIBase (boost::shared_ptr<PluginInsert> pi)
371 : insert (pi),
372 plugin (insert->plugin()),
373 save_button(_("Save")),
374 bypass_button (_("Bypass"))
376 //preset_combo.set_use_arrows_always(true);
377 preset_combo.set_size_request (100, -1);
378 update_presets ();
380 preset_combo.signal_changed().connect(mem_fun(*this, &PlugUIBase::setting_selected));
381 no_load_preset = false;
383 save_button.set_name ("PluginSaveButton");
384 save_button.signal_clicked().connect(mem_fun(*this, &PlugUIBase::save_plugin_setting));
386 insert->active_changed.connect (mem_fun(*this, &PlugUIBase::redirect_active_changed));
387 bypass_button.set_active (!pi->active());
389 bypass_button.set_name ("PluginBypassButton");
390 bypass_button.signal_toggled().connect (mem_fun(*this, &PlugUIBase::bypass_toggled));
391 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
393 focus_button.signal_button_release_event().connect (mem_fun(*this, &PlugUIBase::focus_toggled));
394 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
396 /* these images are not managed, so that we can remove them at will */
398 focus_out_image = new Image (get_icon (X_("computer_keyboard")));
399 focus_in_image = new Image (get_icon (X_("computer_keyboard_active")));
401 focus_button.add (*focus_out_image);
403 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(), "");
404 ARDOUR_UI::instance()->set_tip (&bypass_button, _("Click to enable/disable this plugin"), "");
406 insert->GoingAway.connect (mem_fun (*this, &PlugUIBase::plugin_going_away));
409 PlugUIBase::~PlugUIBase ()
413 void
414 PlugUIBase::plugin_going_away ()
416 /* drop references to the plugin/insert */
417 insert.reset ();
418 plugin.reset ();
421 void
422 PlugUIBase::redirect_active_changed (Redirect* r, void* src)
424 ENSURE_GUI_THREAD(bind (mem_fun(*this, &PlugUIBase::redirect_active_changed), r, src));
425 bypass_button.set_active (!r->active());
428 void
429 PlugUIBase::setting_selected()
431 if (no_load_preset) {
432 return;
435 if (preset_combo.get_active_text().length() > 0) {
436 if (!plugin->load_preset(preset_combo.get_active_text())) {
437 warning << string_compose(_("Plugin preset %1 not found"), preset_combo.get_active_text()) << endmsg;
442 void
443 PlugUIBase::save_plugin_setting ()
445 ArdourPrompter prompter (true);
446 prompter.set_prompt(_("Name of New Preset:"));
447 prompter.add_button (Gtk::Stock::ADD, Gtk::RESPONSE_ACCEPT);
448 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
449 prompter.set_type_hint (Gdk::WINDOW_TYPE_HINT_UTILITY);
451 prompter.show_all();
452 prompter.present ();
454 switch (prompter.run ()) {
455 case Gtk::RESPONSE_ACCEPT:
457 string name;
459 prompter.get_result(name);
461 if (name.length()) {
462 if (plugin->save_preset(name)) {
464 /* a rather inefficient way to add the newly saved preset
465 to the list.
468 no_load_preset = true;
469 set_popdown_strings (preset_combo, plugin->get_presets());
470 preset_combo.set_active_text (name);
471 no_load_preset = false;
474 break;
478 void
479 PlugUIBase::bypass_toggled ()
481 bool x;
483 if ((x = bypass_button.get_active()) == insert->active()) {
484 insert->set_active (!x, this);
488 bool
489 PlugUIBase::focus_toggled (GdkEventButton* ev)
491 if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
492 Keyboard::the_keyboard().magic_widget_drop_focus();
493 focus_button.remove ();
494 focus_button.add (*focus_out_image);
495 focus_out_image->show ();
496 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(), "");
497 KeyboardFocused (false);
498 } else {
499 Keyboard::the_keyboard().magic_widget_grab_focus();
500 focus_button.remove ();
501 focus_button.add (*focus_in_image);
502 focus_in_image->show ();
503 ARDOUR_UI::instance()->set_tip (&focus_button, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME).c_str(), "");
504 KeyboardFocused (true);
507 return true;
510 void
511 PlugUIBase::update_presets ()
513 vector<string> presets = plugin->get_presets();
514 no_load_preset = true;
516 set_popdown_strings (preset_combo, plugin->get_presets());
518 string current_preset = plugin->current_preset();
520 if (!current_preset.empty()) {
521 for (vector<string>::iterator p = presets.begin(); p != presets.end(); ++p) {
522 if (*p == current_preset) {
523 preset_combo.set_active_text (current_preset);
528 no_load_preset = false;