fixes for OS X compile of last commit
[ardour2.git] / gtk2_ardour / plugin_ui.cc
blob233774dba2823e030f727d16c60f3b7d5cb8157e
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)
73 bool have_gui = false;
74 non_gtk_gui = false;
75 was_visible = 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 add (*pu);
115 set_wmclass (X_("ardour_plugin_editor"), "Ardour");
117 signal_map_event().connect (mem_fun (*pu, &GenericPluginUI::start_updating));
118 signal_unmap_event().connect (mem_fun (*pu, &GenericPluginUI::stop_updating));
121 // set_position (Gtk::WIN_POS_MOUSE);
122 set_name ("PluginEditor");
123 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
125 signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), reinterpret_cast<Window*> (this)), false);
126 death_connection = insert->GoingAway.connect (mem_fun(*this, &PluginUIWindow::plugin_going_away));
128 gint h = _pluginui->get_preferred_height ();
129 gint w = _pluginui->get_preferred_width ();
131 if (scrollable) {
132 if (h > 600) h = 600;
133 if (w > 600) w = 600;
135 if (w < 0) {
136 w = 450;
140 set_default_size (w, h);
143 PluginUIWindow::~PluginUIWindow ()
145 delete _pluginui;
148 void
149 PluginUIWindow::set_parent (Gtk::Window* win)
151 parent = win;
154 void
155 PluginUIWindow::on_map ()
157 Window::on_map ();
158 set_keep_above (true);
161 bool
162 PluginUIWindow::on_enter_notify_event (GdkEventCrossing *ev)
164 Keyboard::the_keyboard().enter_window (ev, this);
165 return false;
168 bool
169 PluginUIWindow::on_leave_notify_event (GdkEventCrossing *ev)
171 Keyboard::the_keyboard().leave_window (ev, this);
172 return false;
175 bool
176 PluginUIWindow::on_focus_in_event (GdkEventFocus *ev)
178 Window::on_focus_in_event (ev);
179 //Keyboard::the_keyboard().magic_widget_grab_focus ();
180 return false;
183 bool
184 PluginUIWindow::on_focus_out_event (GdkEventFocus *ev)
186 Window::on_focus_out_event (ev);
187 //Keyboard::the_keyboard().magic_widget_drop_focus ();
188 return false;
191 void
192 PluginUIWindow::on_show ()
194 set_role("plugin_ui");
196 if (_pluginui) {
197 _pluginui->update_presets ();
200 if (_pluginui) {
201 if (_pluginui->on_window_show (_title)) {
202 Window::on_show ();
206 if (parent) {
207 // set_transient_for (*parent);
211 void
212 PluginUIWindow::on_hide ()
214 Window::on_hide ();
216 if (_pluginui) {
217 _pluginui->on_window_hide ();
221 void
222 PluginUIWindow::set_title(const Glib::ustring& title)
224 //cout << "PluginUIWindow::set_title(\"" << title << "\"" << endl;
225 Gtk::Window::set_title(title);
226 _title = title;
229 bool
230 PluginUIWindow::create_vst_editor(boost::shared_ptr<PluginInsert> insert)
232 #ifndef VST_SUPPORT
233 return false;
234 #else
236 boost::shared_ptr<VSTPlugin> vp;
238 if ((vp = boost::dynamic_pointer_cast<VSTPlugin> (insert->plugin())) == 0) {
239 error << _("unknown type of editor-supplying plugin (note: no VST support in this version of ardour)")
240 << endmsg;
241 throw failed_constructor ();
242 } else {
243 VSTPluginUI* vpu = new VSTPluginUI (insert, vp);
245 _pluginui = vpu;
246 add (*vpu);
247 vpu->package (*this);
250 non_gtk_gui = true;
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 add (*box);
264 non_gtk_gui = true;
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 non_gtk_gui = false;
313 return true;
314 #endif
317 bool
318 PluginUIWindow::on_key_press_event (GdkEventKey* event)
320 return relay_key_press (event, this);
323 bool
324 PluginUIWindow::on_key_release_event (GdkEventKey* event)
326 return true;
329 void
330 PluginUIWindow::plugin_going_away ()
332 ENSURE_GUI_THREAD(mem_fun(*this, &PluginUIWindow::plugin_going_away));
334 if (_pluginui) {
335 _pluginui->stop_updating(0);
338 death_connection.disconnect ();
339 delete_when_idle (this);
342 PlugUIBase::PlugUIBase (boost::shared_ptr<PluginInsert> pi)
343 : insert (pi),
344 plugin (insert->plugin()),
345 save_button(_("Save")),
346 bypass_button (_("Bypass"))
348 //preset_combo.set_use_arrows_always(true);
349 preset_combo.set_size_request (100, -1);
350 update_presets ();
352 preset_combo.signal_changed().connect(mem_fun(*this, &PlugUIBase::setting_selected));
353 no_load_preset = false;
355 save_button.set_name ("PluginSaveButton");
356 save_button.signal_clicked().connect(mem_fun(*this, &PlugUIBase::save_plugin_setting));
358 insert->active_changed.connect (mem_fun(*this, &PlugUIBase::redirect_active_changed));
359 bypass_button.set_active (!pi->active());
361 bypass_button.set_name ("PluginBypassButton");
362 bypass_button.signal_toggled().connect (mem_fun(*this, &PlugUIBase::bypass_toggled));
363 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
365 focus_button.signal_button_release_event().connect (mem_fun(*this, &PlugUIBase::focus_toggled));
366 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
368 /* these images are not managed, so that we can remove them at will */
370 focus_out_image = new Image (get_icon (X_("computer_keyboard")));
371 focus_in_image = new Image (get_icon (X_("computer_keyboard_active")));
373 focus_button.add (*focus_out_image);
375 ARDOUR_UI::instance()->set_tip (&focus_button, _("Click to allow the plugin to receive keyboard events that Ardour would normally use as a shortcut"), "");
376 ARDOUR_UI::instance()->set_tip (&bypass_button, _("Click to enable/disable this plugin"), "");
378 insert->GoingAway.connect (mem_fun (*this, &PlugUIBase::plugin_going_away));
381 PlugUIBase::~PlugUIBase ()
385 void
386 PlugUIBase::plugin_going_away ()
388 /* drop references to the plugin/insert */
389 insert.reset ();
390 plugin.reset ();
393 void
394 PlugUIBase::redirect_active_changed (Redirect* r, void* src)
396 ENSURE_GUI_THREAD(bind (mem_fun(*this, &PlugUIBase::redirect_active_changed), r, src));
397 bypass_button.set_active (!r->active());
400 void
401 PlugUIBase::setting_selected()
403 if (no_load_preset) {
404 return;
407 if (preset_combo.get_active_text().length() > 0) {
408 if (!plugin->load_preset(preset_combo.get_active_text())) {
409 warning << string_compose(_("Plugin preset %1 not found"), preset_combo.get_active_text()) << endmsg;
414 void
415 PlugUIBase::save_plugin_setting ()
417 ArdourPrompter prompter (true);
418 prompter.set_prompt(_("Name of New Preset:"));
419 prompter.add_button (Gtk::Stock::ADD, Gtk::RESPONSE_ACCEPT);
420 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
421 prompter.set_type_hint (Gdk::WINDOW_TYPE_HINT_UTILITY);
423 prompter.show_all();
424 prompter.present ();
426 switch (prompter.run ()) {
427 case Gtk::RESPONSE_ACCEPT:
429 string name;
431 prompter.get_result(name);
433 if (name.length()) {
434 if (plugin->save_preset(name)) {
436 /* a rather inefficient way to add the newly saved preset
437 to the list.
440 no_load_preset = true;
441 set_popdown_strings (preset_combo, plugin->get_presets());
442 preset_combo.set_active_text (name);
443 no_load_preset = false;
446 break;
450 void
451 PlugUIBase::bypass_toggled ()
453 bool x;
455 if ((x = bypass_button.get_active()) == insert->active()) {
456 insert->set_active (!x, this);
460 bool
461 PlugUIBase::focus_toggled (GdkEventButton* ev)
463 if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
464 Keyboard::the_keyboard().magic_widget_drop_focus();
465 focus_button.remove ();
466 focus_button.add (*focus_out_image);
467 focus_out_image->show ();
468 ARDOUR_UI::instance()->set_tip (&focus_button, _("Click to allow the plugin to receive keyboard events that Ardour would normally use as a shortcut"), "");
469 } else {
470 Keyboard::the_keyboard().magic_widget_grab_focus();
471 focus_button.remove ();
472 focus_button.add (*focus_in_image);
473 focus_in_image->show ();
474 ARDOUR_UI::instance()->set_tip (&focus_button, _("Click to allow normal use of Ardour keyboard shortcuts"), "");
477 return true;
480 void
481 PlugUIBase::update_presets ()
483 vector<string> presets = plugin->get_presets();
484 no_load_preset = true;
486 set_popdown_strings (preset_combo, plugin->get_presets());
488 string current_preset = plugin->current_preset();
490 if (!current_preset.empty()) {
491 for (vector<string>::iterator p = presets.begin(); p != presets.end(); ++p) {
492 if (*p == current_preset) {
493 preset_combo.set_active_text (current_preset);
498 no_load_preset = false;