working nag screen url opening on OS X
[ardour.git] / gtk2_ardour / plugin_ui.cc
1 /*
2     Copyright (C) 2000 Paul Davis 
3
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.
8
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.
13
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.
17
18 */
19
20 #include <climits>
21 #include <cerrno>
22 #include <cmath>
23 #include <string>
24
25 #include <pbd/stl_delete.h>
26 #include <pbd/xml++.h>
27 #include <pbd/failed_constructor.h>
28
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
37 #include <midi++/manager.h>
38
39 #include <ardour/plugin.h>
40 #include <ardour/insert.h>
41 #include <ardour/ladspa_plugin.h>
42 #ifdef VST_SUPPORT
43 #include <ardour/vst_plugin.h>
44 #endif
45 #ifdef HAVE_LV2
46 #include <ardour/lv2_plugin.h>
47 #include "lv2_plugin_ui.h"
48 #endif
49
50 #include <lrdf.h>
51
52 #include "ardour_ui.h"
53 #include "prompter.h"
54 #include "plugin_ui.h"
55 #include "utils.h"
56 #include "gui_thread.h"
57 #include "public_editor.h"
58 #include "keyboard.h"
59
60 #include "i18n.h"
61
62 using namespace std;
63 using namespace ARDOUR;
64 using namespace PBD;
65 using namespace Gtkmm2ext;
66 using namespace Gtk;
67 using namespace sigc;
68
69 PluginUIWindow::PluginUIWindow (Gtk::Window* win, boost::shared_ptr<PluginInsert> insert, bool scrollable)
70         : parent (win)
71 {
72         bool have_gui = false;
73         non_gtk_gui = false;
74         was_visible = false;
75
76         if (insert->plugin()->has_editor()) {
77                 switch (insert->type()) {
78                 case ARDOUR::VST:
79                         have_gui = create_vst_editor (insert);
80                         break;
81
82                 case ARDOUR::AudioUnit:
83                         have_gui = create_audiounit_editor (insert);
84                         break;
85                         
86                 case ARDOUR::LADSPA:
87                         error << _("Eh? LADSPA plugins don't have editors!") << endmsg;
88                         break;
89
90                 case ARDOUR::LV2:
91                         have_gui = create_lv2_editor (insert);
92                         break;
93
94                 default:
95 #ifndef VST_SUPPORT
96                         error << _("unknown type of editor-supplying plugin (note: no VST support in this version of ardour)")
97                               << endmsg;
98 #else
99                         error << _("unknown type of editor-supplying plugin")
100                               << endmsg;
101 #endif
102                         throw failed_constructor ();
103                 }
104
105         } 
106
107         if (!have_gui) {
108
109                 GenericPluginUI*  pu  = new GenericPluginUI (insert, scrollable);
110                 
111                 _pluginui = pu;
112                 add (*pu);
113                 
114                 set_wmclass (X_("ardour_plugin_editor"), "Ardour");
115
116                 signal_map_event().connect (mem_fun (*pu, &GenericPluginUI::start_updating));
117                 signal_unmap_event().connect (mem_fun (*pu, &GenericPluginUI::stop_updating));
118         }
119
120         // set_position (Gtk::WIN_POS_MOUSE);
121         set_name ("PluginEditor");
122         add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
123
124         signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), reinterpret_cast<Window*> (this)), false);
125         insert->GoingAway.connect (mem_fun(*this, &PluginUIWindow::plugin_going_away));
126
127         gint h = _pluginui->get_preferred_height ();
128         gint w = _pluginui->get_preferred_width ();
129
130         if (scrollable) {
131                 if (h > 600) h = 600;
132                 if (w > 600) w = 600;
133
134                 if (w < 0) {
135                         w = 450;
136                 }
137         }
138
139         set_default_size (w, h); 
140 }
141
142 PluginUIWindow::~PluginUIWindow ()
143 {
144 }
145
146 void
147 PluginUIWindow::set_parent (Gtk::Window* win)
148 {
149         parent = win;
150 }
151
152 void
153 PluginUIWindow::on_map ()
154 {
155         Window::on_map ();
156         set_keep_above (true);
157 }
158
159 bool
160 PluginUIWindow::on_enter_notify_event (GdkEventCrossing *ev)
161 {
162         Keyboard::the_keyboard().enter_window (ev, this);
163         return false;
164 }
165
166 bool
167 PluginUIWindow::on_leave_notify_event (GdkEventCrossing *ev)
168 {
169         Keyboard::the_keyboard().leave_window (ev, this);
170         return false;
171 }
172
173 bool
174 PluginUIWindow::on_focus_in_event (GdkEventFocus *ev)
175 {
176         Window::on_focus_in_event (ev);
177         //Keyboard::the_keyboard().magic_widget_grab_focus ();
178         return false;
179 }
180
181 bool
182 PluginUIWindow::on_focus_out_event (GdkEventFocus *ev)
183 {
184         Window::on_focus_out_event (ev);
185         //Keyboard::the_keyboard().magic_widget_drop_focus ();
186         return false;
187 }
188
189 void
190 PluginUIWindow::on_show ()
191 {
192         if (_pluginui) {
193                 _pluginui->update_presets ();
194         }
195
196         Window::on_show ();
197
198         if (parent) {
199                 // set_transient_for (*parent);
200         }
201 }
202
203 void
204 PluginUIWindow::on_hide ()
205 {
206         Window::on_hide ();
207 }
208
209 bool
210 PluginUIWindow::create_vst_editor(boost::shared_ptr<PluginInsert> insert)
211 {
212 #ifndef VST_SUPPORT
213         return false;
214 #else
215
216         boost::shared_ptr<VSTPlugin> vp;
217
218         if ((vp = boost::dynamic_pointer_cast<VSTPlugin> (insert->plugin())) == 0) {
219                 error << _("unknown type of editor-supplying plugin (note: no VST support in this version of ardour)")
220                               << endmsg;
221                 throw failed_constructor ();
222         } else {
223                 VSTPluginUI* vpu = new VSTPluginUI (insert, vp);
224         
225                 _pluginui = vpu;
226                 add (*vpu);
227                 vpu->package (*this);
228         }
229
230         non_gtk_gui = true;
231         return true;
232 #endif
233 }
234
235 bool
236 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert> insert)
237 {
238 #if !defined(HAVE_AUDIOUNITS) || !defined(GTKOSX)
239         return false;
240 #else
241         VBox* box;
242         _pluginui = create_au_gui (insert, &box);
243         add (*box);
244         non_gtk_gui = true;
245
246         extern sigc::signal<void,bool> ApplicationActivationChanged;
247         ApplicationActivationChanged.connect (mem_fun (*this, &PluginUIWindow::app_activated));
248
249         return true;
250 #endif
251 }
252
253 void
254 PluginUIWindow::app_activated (bool yn)
255 {
256 #if defined (HAVE_AUDIOUNITS) && defined(GTKOSX)
257         cerr << "APP activated ? " << yn << endl;
258         if (_pluginui) {
259                 if (yn) {
260                         if (was_visible) {
261                                 _pluginui->activate ();
262                                 present ();
263                                 was_visible = true;
264                         }
265                 } else {
266                         was_visible = is_visible();
267                         hide ();
268                         _pluginui->deactivate ();
269                 }
270         } 
271 #endif
272 }
273
274 bool
275 PluginUIWindow::create_lv2_editor(boost::shared_ptr<PluginInsert> insert)
276 {
277 #ifndef HAVE_LV2
278         return false;
279 #else
280
281         boost::shared_ptr<LV2Plugin> vp;
282         
283         if ((vp = boost::dynamic_pointer_cast<LV2Plugin> (insert->plugin())) == 0) {
284                 error << _("create_lv2_editor called on non-LV2 plugin") << endmsg;
285                 throw failed_constructor ();
286         } else {
287                 LV2PluginUI* lpu = new LV2PluginUI (insert, vp);
288                 _pluginui = lpu;
289                 add (*lpu);
290                 lpu->package (*this);
291         }
292
293         non_gtk_gui = false;
294         return true;
295 #endif
296 }
297
298 bool
299 PluginUIWindow::on_key_press_event (GdkEventKey* event)
300 {
301         if (!key_press_focus_accelerator_handler (*this, event)) {
302                 return PublicEditor::instance().on_key_press_event(event);
303         } else {
304                 return true;
305         }
306 }
307
308 bool
309 PluginUIWindow::on_key_release_event (GdkEventKey* event)
310 {
311         return true;
312 }
313
314 void
315 PluginUIWindow::plugin_going_away ()
316 {
317         ENSURE_GUI_THREAD(mem_fun(*this, &PluginUIWindow::plugin_going_away));
318         
319         if (_pluginui) {
320                 _pluginui->stop_updating(0);
321         }
322         delete_when_idle (this);
323 }
324
325 PlugUIBase::PlugUIBase (boost::shared_ptr<PluginInsert> pi)
326         : insert (pi),
327           plugin (insert->plugin()),
328           save_button(_("Add")),
329           bypass_button (_("Bypass"))
330 {
331         //preset_combo.set_use_arrows_always(true);
332         set_popdown_strings (preset_combo, plugin->get_presets());
333         preset_combo.set_size_request (100, -1);
334         preset_combo.set_active_text ("");
335         preset_combo.signal_changed().connect(mem_fun(*this, &PlugUIBase::setting_selected));
336
337         save_button.set_name ("PluginSaveButton");
338         save_button.signal_clicked().connect(mem_fun(*this, &PlugUIBase::save_plugin_setting));
339
340         insert->active_changed.connect (mem_fun(*this, &PlugUIBase::redirect_active_changed));
341         bypass_button.set_active (!pi->active());
342
343         bypass_button.set_name ("PluginBypassButton");
344         bypass_button.signal_toggled().connect (mem_fun(*this, &PlugUIBase::bypass_toggled));
345         focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
346
347         focus_button.signal_button_release_event().connect (mem_fun(*this, &PlugUIBase::focus_toggled));
348         focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
349
350         /* these images are not managed, so that we can remove them at will */
351
352         focus_out_image = new Image (get_icon (X_("computer_keyboard")));
353         focus_in_image = new Image (get_icon (X_("computer_keyboard_active")));
354         
355         focus_button.add (*focus_out_image);
356
357         ARDOUR_UI::instance()->set_tip (&focus_button, _("Click to focus all keyboard events on this plugin window"), "");
358         ARDOUR_UI::instance()->set_tip (&bypass_button, _("Click to enable/disable this plugin"), "");
359 }
360
361 void
362 PlugUIBase::redirect_active_changed (Redirect* r, void* src)
363 {
364         ENSURE_GUI_THREAD(bind (mem_fun(*this, &PlugUIBase::redirect_active_changed), r, src));
365 }
366
367 void
368 PlugUIBase::setting_selected()
369 {
370         if (preset_combo.get_active_text().length() > 0) {
371                 if (!plugin->load_preset(preset_combo.get_active_text())) {
372                         warning << string_compose(_("Plugin preset %1 not found"), preset_combo.get_active_text()) << endmsg;
373                 }
374         }
375 }
376
377 void
378 PlugUIBase::save_plugin_setting ()
379 {
380         ArdourPrompter prompter (true);
381         prompter.set_prompt(_("Name of New Preset:"));
382         prompter.add_button (Gtk::Stock::ADD, Gtk::RESPONSE_ACCEPT);
383         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
384         prompter.set_type_hint (Gdk::WINDOW_TYPE_HINT_UTILITY);
385
386         prompter.show_all();
387
388         switch (prompter.run ()) {
389         case Gtk::RESPONSE_ACCEPT:
390
391                 string name;
392
393                 prompter.get_result(name);
394
395                 if (name.length()) {
396                         if(plugin->save_preset(name)){
397                                 set_popdown_strings (preset_combo, plugin->get_presets());
398                                 preset_combo.set_active_text (name);
399                         }
400                 }
401                 break;
402         }
403 }
404
405 void
406 PlugUIBase::bypass_toggled ()
407 {
408         bool x;
409
410         if ((x = bypass_button.get_active()) == insert->active()) {
411                 insert->set_active (!x, this);
412         }
413 }
414
415 bool
416 PlugUIBase::focus_toggled (GdkEventButton* ev)
417 {
418         if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
419                 Keyboard::the_keyboard().magic_widget_drop_focus();
420                 focus_button.remove ();
421                 focus_button.add (*focus_out_image);
422                 focus_out_image->show ();
423                 ARDOUR_UI::instance()->set_tip (&focus_button, _("Click to focus all keyboard events on this plugin window"), "");
424         } else {
425                 Keyboard::the_keyboard().magic_widget_grab_focus();
426                 focus_button.remove ();
427                 focus_button.add (*focus_in_image);
428                 focus_in_image->show ();
429                 ARDOUR_UI::instance()->set_tip (&focus_button, _("Click to remove keyboard focus from this plugin window"), "");
430         }
431
432         return true;
433 }
434
435 void
436 PlugUIBase::update_presets ()
437 {
438         set_popdown_strings (preset_combo, plugin->get_presets());
439 }