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.
21 #include "gtk2ardour-config.h"
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 "gtkmm/separator.h"
37 #include "gtkmm2ext/utils.h"
38 #include "gtkmm2ext/doi.h"
39 #include "gtkmm2ext/application.h"
41 #include "widgets/tooltips.h"
42 #include "widgets/fastmeter.h"
44 #include "ardour/session.h"
45 #include "ardour/plugin.h"
46 #include "ardour/plugin_insert.h"
47 #include "ardour/ladspa_plugin.h"
48 #ifdef WINDOWS_VST_SUPPORT
49 #include "ardour/windows_vst_plugin.h"
50 #include "windows_vst_plugin_ui.h"
53 #include "ardour/lxvst_plugin.h"
54 #include "lxvst_plugin_ui.h"
57 #include "ardour/mac_vst_plugin.h"
58 #include "vst_plugin_ui.h"
61 #include "ardour/lv2_plugin.h"
62 #include "lv2_plugin_ui.h"
65 #include "ardour_window.h"
66 #include "ardour_ui.h"
67 #include "plugin_ui.h"
69 #include "gui_thread.h"
70 #include "public_editor.h"
71 #include "processor_box.h"
73 #include "latency_gui.h"
74 #include "plugin_eq_gui.h"
76 #include "new_plugin_preset_dialog.h"
81 using namespace ARDOUR;
82 using namespace ARDOUR_UI_UTILS;
83 using namespace ArdourWidgets;
85 using namespace Gtkmm2ext;
89 class PluginLoadStatsGui : public Gtk::Table
92 PluginLoadStatsGui (boost::shared_ptr<ARDOUR::PluginInsert>);
94 void start_updating () {
96 update_cpu_label_connection = Timers::second_connect (sigc::mem_fun(*this, &PluginLoadStatsGui::update_cpu_label));
99 void stop_updating () {
101 update_cpu_label_connection.disconnect ();
105 void update_cpu_label ();
106 bool draw_bar (GdkEventExpose*);
107 void clear_stats () {
108 _insert->clear_stats ();
111 boost::shared_ptr<ARDOUR::PluginInsert> _insert;
112 sigc::connection update_cpu_label_connection;
119 ArdourWidgets::ArdourButton _reset_button;
120 Gtk::DrawingArea _darea;
127 PluginLoadStatsGui::PluginLoadStatsGui (boost::shared_ptr<ARDOUR::PluginInsert> insert)
129 , _lbl_min ("", ALIGN_RIGHT, ALIGN_CENTER)
130 , _lbl_max ("", ALIGN_RIGHT, ALIGN_CENTER)
131 , _lbl_avg ("", ALIGN_RIGHT, ALIGN_CENTER)
132 , _lbl_dev ("", ALIGN_RIGHT, ALIGN_CENTER)
133 , _reset_button (_("Reset"))
136 _reset_button.set_name ("generic button");
137 _reset_button.signal_clicked.connect (sigc::mem_fun (*this, &PluginLoadStatsGui::clear_stats));
138 _darea.signal_expose_event ().connect (sigc::mem_fun (*this, &PluginLoadStatsGui::draw_bar));
139 set_size_request_to_display_given_text (_lbl_dev, string_compose (_("%1 [ms]"), 99.123), 0, 0);
141 attach (*manage (new Gtk::Label (_("Minimum"), ALIGN_RIGHT, ALIGN_CENTER)),
142 0, 1, 0, 1, Gtk::FILL, Gtk::SHRINK, 2, 0);
143 attach (*manage (new Gtk::Label (_("Maximum"), ALIGN_RIGHT, ALIGN_CENTER)),
144 0, 1, 1, 2, Gtk::FILL, Gtk::SHRINK, 2, 0);
145 attach (*manage (new Gtk::Label (_("Average"), ALIGN_RIGHT, ALIGN_CENTER)),
146 0, 1, 2, 3, Gtk::FILL, Gtk::SHRINK, 2, 0);
147 attach (*manage (new Gtk::Label (_("Std.Dev"), ALIGN_RIGHT, ALIGN_CENTER)),
148 0, 1, 3, 4, Gtk::FILL, Gtk::SHRINK, 2, 0);
150 attach (_lbl_min, 1, 2, 0, 1, Gtk::FILL, Gtk::SHRINK, 2, 0);
151 attach (_lbl_max, 1, 2, 1, 2, Gtk::FILL, Gtk::SHRINK, 2, 0);
152 attach (_lbl_avg, 1, 2, 2, 3, Gtk::FILL, Gtk::SHRINK, 2, 0);
153 attach (_lbl_dev, 1, 2, 3, 4, Gtk::FILL, Gtk::SHRINK, 2, 0);
155 attach (*manage (new Gtk::VSeparator ()),
156 2, 3, 0, 4, Gtk::FILL, Gtk::FILL, 4, 0);
158 attach (_darea, 3, 4, 0, 4, Gtk::FILL|Gtk::EXPAND, Gtk::FILL, 4, 4);
160 attach (_reset_button, 4, 5, 2, 4, Gtk::FILL, Gtk::SHRINK);
164 PluginLoadStatsGui::update_cpu_label()
166 if (_insert->get_stats (_min, _max, _avg, _dev)) {
168 _lbl_min.set_text (string_compose (_("%1 [ms]"), rint (_min / 10.) / 100.));
169 _lbl_max.set_text (string_compose (_("%1 [ms]"), rint (_max / 10.) / 100.));
170 _lbl_avg.set_text (string_compose (_("%1 [ms]"), rint (_avg) / 1000.));
171 _lbl_dev.set_text (string_compose (_("%1 [ms]"), rint (_dev) / 1000.));
172 _lbl_dev.set_text (string_compose (_("%1 [ms]"), rint (_dev) / 1000.));
175 _lbl_min.set_text ("-");
176 _lbl_max.set_text ("-");
177 _lbl_avg.set_text ("-");
178 _lbl_dev.set_text ("-");
180 _darea.queue_draw ();
184 PluginLoadStatsGui::draw_bar (GdkEventExpose* ev)
186 Gtk::Allocation a = _darea.get_allocation ();
187 const int width = a.get_width ();
188 const int height = a.get_height ();
189 cairo_t* cr = gdk_cairo_create (_darea.get_window ()->gobj ());
190 cairo_rectangle (cr, ev->area.x, ev->area.y, ev->area.width, ev->area.height);
193 Gdk::Color const bg = get_style ()->get_bg (STATE_NORMAL);
194 Gdk::Color const fg = get_style ()->get_fg (STATE_NORMAL);
196 cairo_set_source_rgb (cr, bg.get_red_p (), bg.get_green_p (), bg.get_blue_p ());
197 cairo_rectangle (cr, 0, 0, width, height);
200 int border = (height / 7) | 1;
205 int y1 = (height - 3 * border) & ~1;
207 const int w = x1 - x0;
208 const int h = y1 - y0;
209 const double cycle_ms = 1000. * _insert->session().get_block_size() / (double)_insert->session().nominal_sample_rate();
211 const double base_mult = std::max (1.0, cycle_ms / 2.0);
212 const double log_base = log1p (base_mult);
215 # define DEFLECT(T) ( (T) * w * 8. / (9. * cycle_ms) )
217 # define DEFLECT(T) ( log1p((T) * base_mult / cycle_ms) * w * 8. / (9 * log_base) )
221 rounded_rectangle (cr, x0, y0, w, h, 7);
223 cairo_set_source_rgba (cr, .0, .0, .0, 1);
224 cairo_set_line_width (cr, 1);
225 cairo_stroke_preserve (cr);
228 cairo_pattern_t *pat = cairo_pattern_create_linear (x0, 0.0, w, 0.0);
229 cairo_pattern_add_color_stop_rgba (pat, 0, 0, 1, 0, .7);
230 cairo_pattern_add_color_stop_rgba (pat, 6. / 9., 0, 1, 0, .7);
231 cairo_pattern_add_color_stop_rgba (pat, 6.5 / 9., .8, .8, 0, .7);
232 cairo_pattern_add_color_stop_rgba (pat, 7.5 / 9., .8, .8, 0, .7);
233 cairo_pattern_add_color_stop_rgba (pat, 8. / 9., 1, 0, 0, .7);
234 cairo_set_source (cr, pat);
235 cairo_pattern_destroy (pat);
236 cairo_fill_preserve (cr);
238 cairo_set_source_rgba (cr, .4, .3, .1, .5);
239 cairo_fill_preserve (cr);
246 double xmin = DEFLECT(_min / 1000.);
247 double xmax = DEFLECT(_max / 1000.);
249 rounded_rectangle (cr, x0 + xmin, y0, xmax - xmin, h, 7);
250 cairo_set_source_rgba (cr, .8, .8, .9, .5);
256 Glib::RefPtr<Pango::Layout> layout;
257 layout = Pango::Layout::create (get_pango_context ());
259 cairo_set_line_width (cr, 1);
261 for (int i = 1; i < 9; ++i) {
262 int text_width, text_height;
264 double v = cycle_ms * i / 8.;
266 double v = (exp (i * log_base / 8) - 1) * cycle_ms / base_mult;
268 double decimal = v > 10 ? 10 : 100;
269 layout->set_text (string_compose ("%1", rint (decimal * v) / decimal));
270 layout->get_pixel_size (text_width, text_height);
272 const int dx = w * i / 9.; // == DEFLECT (v)
274 cairo_move_to (cr, x0 + dx - .5, y0);
275 cairo_line_to (cr, x0 + dx - .5, y1);
276 cairo_set_source_rgba (cr, 1., 1., 1., 1.);
279 cairo_move_to (cr, x0 + dx - .5 * text_width, y1 + 1);
280 cairo_set_source_rgb (cr, fg.get_red_p (), fg.get_green_p (), fg.get_blue_p ());
281 pango_cairo_show_layout (cr, layout->gobj ());
285 int text_width, text_height;
286 layout->set_text ("0");
287 cairo_move_to (cr, x0 + 1, y1 + 1);
288 cairo_set_source_rgb (cr, fg.get_red_p (), fg.get_green_p (), fg.get_blue_p ());
289 pango_cairo_show_layout (cr, layout->gobj ());
291 layout->set_text (_("[ms]"));
292 layout->get_pixel_size (text_width, text_height);
293 cairo_move_to (cr, x0 + w - text_width - 1, y1 + 1);
294 pango_cairo_show_layout (cr, layout->gobj ());
298 double xavg = round (DEFLECT(_avg / 1000.));
299 double xd0 = DEFLECT((_avg - _dev) / 1000.);
300 double xd1 = DEFLECT((_avg + _dev) / 1000.);
302 cairo_move_to (cr, x0 + xavg - .5, y0 - 1);
303 cairo_rel_line_to (cr, -5, -5);
304 cairo_rel_line_to (cr, 10, 0);
305 cairo_close_path (cr);
306 cairo_set_source_rgb (cr, fg.get_red_p (), fg.get_green_p (), fg.get_blue_p ());
311 rounded_rectangle (cr, x0, y0, w, h, 7);
314 const double dashes[] = { 1, 2 };
315 cairo_set_dash (cr, dashes, 2, 0);
316 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
317 cairo_set_line_width (cr, 1);
318 cairo_move_to (cr, x0 + xavg - .5, y0 - .5);
319 cairo_line_to (cr, x0 + xavg - .5, y1 + .5);
320 cairo_set_source_rgba (cr, .0, .0, .0, 1.);
322 cairo_set_dash (cr, 0, 0, 0);
325 cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
326 const double ym = .5 + floor ((double)(y0 + h / 2));
327 const int h4 = h / 4;
329 cairo_set_line_width (cr, 1);
330 cairo_set_source_rgba (cr, 0, 0, 0, 1.);
331 cairo_move_to (cr, floor (x0 + xd0), ym);
332 cairo_line_to (cr, ceil (x0 + xd1), ym);
335 cairo_move_to (cr, floor (x0 + xd0) - .5, ym - h4);
336 cairo_line_to (cr, floor (x0 + xd0) - .5, ym + h4);
338 cairo_move_to (cr, ceil (x0 + xd1) - .5, ym - h4);
339 cairo_line_to (cr, ceil (x0 + xd1) - .5, ym + h4);
354 PluginUIWindow::PluginUIWindow (
355 boost::shared_ptr<PluginInsert> insert,
358 : ArdourWindow (string())
359 , was_visible (false)
360 , _keyboard_focused (false)
361 #ifdef AUDIOUNIT_SUPPORT
362 , pre_deactivate_x (-1)
363 , pre_deactivate_y (-1)
367 bool have_gui = false;
368 Label* label = manage (new Label());
369 label->set_markup ("<b>THIS IS THE PLUGIN UI</b>");
371 if (editor && insert->plugin()->has_editor()) {
372 switch (insert->type()) {
373 case ARDOUR::Windows_VST:
374 have_gui = create_windows_vst_editor (insert);
378 have_gui = create_lxvst_editor (insert);
382 have_gui = create_mac_vst_editor (insert);
385 case ARDOUR::AudioUnit:
386 have_gui = create_audiounit_editor (insert);
390 error << _("Eh? LADSPA plugins don't have editors!") << endmsg;
394 have_gui = create_lv2_editor (insert);
398 #ifndef WINDOWS_VST_SUPPORT
399 error << string_compose (_("unknown type of editor-supplying plugin (note: no VST support in this version of %1)"), PROGRAM_NAME)
402 error << _("unknown type of editor-supplying plugin")
405 throw failed_constructor ();
411 GenericPluginUI* pu = new GenericPluginUI (insert, scrollable);
414 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
416 set_wmclass (X_("ardour_plugin_editor"), PROGRAM_NAME);
418 signal_map_event().connect (sigc::mem_fun (*pu, &GenericPluginUI::start_updating));
419 signal_unmap_event().connect (sigc::mem_fun (*pu, &GenericPluginUI::stop_updating));
422 set_name ("PluginEditor");
423 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
425 insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PluginUIWindow::plugin_going_away, this), gui_context());
427 gint h = _pluginui->get_preferred_height ();
428 gint w = _pluginui->get_preferred_width ();
431 if (h > 600) h = 600;
434 set_default_size (w, h);
435 set_resizable (_pluginui->resizable());
438 PluginUIWindow::~PluginUIWindow ()
441 cerr << "PluginWindow deleted for " << this << endl;
447 PluginUIWindow::on_show ()
449 set_role("plugin_ui");
452 _pluginui->update_preset_list ();
453 _pluginui->update_preset ();
457 #if defined (HAVE_AUDIOUNITS) && defined(__APPLE__)
458 if (pre_deactivate_x >= 0) {
459 move (pre_deactivate_x, pre_deactivate_y);
463 if (_pluginui->on_window_show (_title)) {
470 PluginUIWindow::on_hide ()
472 #if defined (HAVE_AUDIOUNITS) && defined(__APPLE__)
473 get_position (pre_deactivate_x, pre_deactivate_y);
479 _pluginui->on_window_hide ();
484 PluginUIWindow::set_title(const std::string& title)
486 Gtk::Window::set_title(title);
491 #ifdef WINDOWS_VST_SUPPORT
492 PluginUIWindow::create_windows_vst_editor(boost::shared_ptr<PluginInsert> insert)
494 PluginUIWindow::create_windows_vst_editor(boost::shared_ptr<PluginInsert>)
497 #ifndef WINDOWS_VST_SUPPORT
501 boost::shared_ptr<WindowsVSTPlugin> vp;
503 if ((vp = boost::dynamic_pointer_cast<WindowsVSTPlugin> (insert->plugin())) == 0) {
504 error << string_compose (_("unknown type of editor-supplying plugin (note: no VST support in this version of %1)"), PROGRAM_NAME)
506 throw failed_constructor ();
508 WindowsVSTPluginUI* vpu = new WindowsVSTPluginUI (insert, vp, GTK_WIDGET(this->gobj()));
511 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
513 vpu->package (*this);
522 PluginUIWindow::create_lxvst_editor(boost::shared_ptr<PluginInsert> insert)
524 PluginUIWindow::create_lxvst_editor(boost::shared_ptr<PluginInsert>)
527 #ifndef LXVST_SUPPORT
531 boost::shared_ptr<LXVSTPlugin> lxvp;
533 if ((lxvp = boost::dynamic_pointer_cast<LXVSTPlugin> (insert->plugin())) == 0) {
534 error << string_compose (_("unknown type of editor-supplying plugin (note: no linuxVST support in this version of %1)"), PROGRAM_NAME)
536 throw failed_constructor ();
538 LXVSTPluginUI* lxvpu = new LXVSTPluginUI (insert, lxvp);
541 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
543 lxvpu->package (*this);
551 #ifdef MACVST_SUPPORT
552 PluginUIWindow::create_mac_vst_editor (boost::shared_ptr<PluginInsert> insert)
554 PluginUIWindow::create_mac_vst_editor (boost::shared_ptr<PluginInsert>)
557 #ifndef MACVST_SUPPORT
560 boost::shared_ptr<MacVSTPlugin> mvst;
561 if ((mvst = boost::dynamic_pointer_cast<MacVSTPlugin> (insert->plugin())) == 0) {
562 error << string_compose (_("unknown type of editor-supplying plugin (note: no MacVST support in this version of %1)"), PROGRAM_NAME)
564 throw failed_constructor ();
566 VSTPluginUI* vpu = create_mac_vst_gui (insert);
568 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
570 vpu->package (*this);
572 Application::instance()->ActivationChanged.connect (mem_fun (*this, &PluginUIWindow::app_activated));
580 #ifdef AUDIOUNIT_SUPPORT
581 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert> insert)
583 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert>)
586 #ifndef AUDIOUNIT_SUPPORT
590 _pluginui = create_au_gui (insert, &box);
591 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
594 Application::instance()->ActivationChanged.connect (mem_fun (*this, &PluginUIWindow::app_activated));
602 PluginUIWindow::app_activated (bool yn)
604 PluginUIWindow::app_activated (bool)
607 #ifdef AUDIOUNIT_SUPPORT
611 _pluginui->activate ();
612 if (pre_deactivate_x >= 0) {
613 move (pre_deactivate_x, pre_deactivate_y);
619 was_visible = is_visible();
620 get_position (pre_deactivate_x, pre_deactivate_y);
622 _pluginui->deactivate ();
629 PluginUIWindow::create_lv2_editor(boost::shared_ptr<PluginInsert> insert)
632 boost::shared_ptr<LV2Plugin> vp;
634 if ((vp = boost::dynamic_pointer_cast<LV2Plugin> (insert->plugin())) == 0) {
635 error << _("create_lv2_editor called on non-LV2 plugin") << endmsg;
636 throw failed_constructor ();
638 LV2PluginUI* lpu = new LV2PluginUI (insert, vp);
641 lpu->package (*this);
642 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
652 PluginUIWindow::keyboard_focused (bool yn)
654 _keyboard_focused = yn;
658 PluginUIWindow::on_key_press_event (GdkEventKey* event)
660 if (_keyboard_focused) {
662 _pluginui->grab_focus();
663 if (_pluginui->non_gtk_gui()) {
664 _pluginui->forward_key_event (event);
666 return relay_key_press (event, this);
671 /* for us to be getting key press events, there really
672 MUST be a _pluginui, but just to be safe, check ...
676 _pluginui->grab_focus();
677 if (_pluginui->non_gtk_gui()) {
678 /* pass main window as the window for the event
679 to be handled in, not this one, because there are
680 no widgets in this window that we want to have
683 return relay_key_press (event, &ARDOUR_UI::instance()->main_window());
685 return relay_key_press (event, this);
693 PluginUIWindow::on_key_release_event (GdkEventKey *event)
695 if (_keyboard_focused) {
697 if (_pluginui->non_gtk_gui()) {
698 _pluginui->forward_key_event (event);
702 gtk_window_propagate_key_event (GTK_WINDOW(gobj()), event);
704 /* don't forward releases */
709 PluginUIWindow::plugin_going_away ()
711 ENSURE_GUI_THREAD (*this, &PluginUIWindow::plugin_going_away)
714 _pluginui->stop_updating(0);
717 death_connection.disconnect ();
720 PlugUIBase::PlugUIBase (boost::shared_ptr<PluginInsert> pi)
722 , plugin (insert->plugin())
723 , add_button (_("Add"))
724 , save_button (_("Save"))
725 , delete_button (_("Delete"))
726 , reset_button (_("Reset"))
727 , bypass_button (ArdourButton::led_default_elements)
728 , pin_management_button (_("Pinout"))
729 , description_expander (_("Description"))
730 , plugin_analysis_expander (_("Plugin analysis"))
731 , cpuload_expander (_("CPU Profile"))
737 _preset_modified.set_size_request (16, -1);
738 _preset_combo.set_text("(default)");
739 set_tooltip (_preset_combo, _("Presets (if any) for this plugin\n(Both factory and user-created)"));
740 set_tooltip (add_button, _("Save a new preset"));
741 set_tooltip (save_button, _("Save the current preset"));
742 set_tooltip (delete_button, _("Delete the current preset"));
743 set_tooltip (reset_button, _("Reset parameters to default (if no parameters are in automation play mode)"));
744 set_tooltip (pin_management_button, _("Show Plugin Pin Management Dialog"));
745 set_tooltip (bypass_button, _("Disable signal processing by the plugin"));
748 update_preset_list ();
751 add_button.set_name ("generic button");
752 add_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::add_plugin_setting));
754 save_button.set_name ("generic button");
755 save_button.signal_clicked.connect(sigc::mem_fun(*this, &PlugUIBase::save_plugin_setting));
757 delete_button.set_name ("generic button");
758 delete_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::delete_plugin_setting));
760 reset_button.set_name ("generic button");
761 reset_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::reset_plugin_parameters));
763 pin_management_button.set_name ("generic button");
764 pin_management_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::manage_pins));
766 insert->ActiveChanged.connect (active_connection, invalidator (*this), boost::bind (&PlugUIBase::processor_active_changed, this, boost::weak_ptr<Processor>(insert)), gui_context());
768 bypass_button.set_name ("plugin bypass button");
769 bypass_button.set_text (_("Bypass"));
770 bypass_button.set_active (!pi->enabled ());
771 bypass_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::bypass_button_release), false);
772 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
774 focus_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::focus_toggled));
775 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
777 /* these images are not managed, so that we can remove them at will */
779 focus_out_image = new Image (get_icon (X_("computer_keyboard")));
780 focus_in_image = new Image (get_icon (X_("computer_keyboard_active")));
782 focus_button.add (*focus_out_image);
784 set_tooltip (focus_button, string_compose (_("Click to allow the plugin to receive keyboard events that %1 would normally use as a shortcut"), PROGRAM_NAME));
785 set_tooltip (bypass_button, _("Click to enable/disable this plugin"));
787 description_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_description));
788 description_expander.set_expanded(false);
790 plugin_analysis_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_plugin_analysis));
791 plugin_analysis_expander.set_expanded(false);
793 cpuload_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_cpuload_display));
794 cpuload_expander.set_expanded(false);
796 insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PlugUIBase::plugin_going_away, this), gui_context());
798 plugin->PresetAdded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
799 plugin->PresetRemoved.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
800 plugin->PresetLoaded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset, this), gui_context ());
801 plugin->PresetDirty.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset_modified, this), gui_context ());
803 insert->AutomationStateChanged.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::automation_state_changed, this), gui_context());
805 automation_state_changed();
808 PlugUIBase::~PlugUIBase()
816 PlugUIBase::plugin_going_away ()
818 /* drop references to the plugin/insert */
824 PlugUIBase::set_latency_label ()
826 samplecnt_t const l = insert->effective_latency ();
827 samplecnt_t const sr = insert->session().sample_rate ();
832 t = string_compose (P_("latency (%1 sample)", "latency (%1 samples)", l), l);
834 t = string_compose (_("latency (%1 ms)"), (float) l / ((float) sr / 1000.0f));
837 latency_button.set_text (t);
841 PlugUIBase::latency_button_clicked ()
844 latency_gui = new LatencyGUI (*(insert.get()), insert->session().sample_rate(), insert->session().get_block_size());
845 latency_dialog = new ArdourWindow (_("Edit Latency"));
846 /* use both keep-above and transient for to try cover as many
847 different WM's as possible.
849 latency_dialog->set_keep_above (true);
850 Window* win = dynamic_cast<Window*> (bypass_button.get_toplevel ());
852 latency_dialog->set_transient_for (*win);
854 latency_dialog->add (*latency_gui);
855 latency_dialog->signal_hide().connect (sigc::mem_fun (*this, &PlugUIBase::set_latency_label));
858 latency_dialog->show_all ();
862 PlugUIBase::processor_active_changed (boost::weak_ptr<Processor> weak_p)
864 ENSURE_GUI_THREAD (*this, &PlugUIBase::processor_active_changed, weak_p);
865 boost::shared_ptr<Processor> p (weak_p.lock());
868 bypass_button.set_active (!p->enabled ());
873 PlugUIBase::preset_selected (Plugin::PresetRecord preset)
875 if (_no_load_preset) {
878 if (!preset.label.empty()) {
879 insert->load_preset (preset);
881 // blank selected = no preset
882 plugin->clear_preset();
886 #ifdef NO_PLUGIN_STATE
887 static bool seen_saving_message = false;
889 static void show_no_plugin_message()
891 info << string_compose (_("Plugin presets are not supported in this build of %1. Consider paying for a full version"),
894 info << _("To get full access to updates without this limitation\n"
895 "consider becoming a subscriber for a low cost every month.")
897 info << X_("https://community.ardour.org/s/subscribe")
899 ARDOUR_UI::instance()->popup_error(_("Plugin presets are not supported in this build, see the Log window for more information."));
904 PlugUIBase::add_plugin_setting ()
906 #ifndef NO_PLUGIN_STATE
907 NewPluginPresetDialog d (plugin, _("New Preset"));
910 case Gtk::RESPONSE_ACCEPT:
911 if (d.name().empty()) {
916 plugin->remove_preset (d.name ());
919 Plugin::PresetRecord const r = plugin->save_preset (d.name());
920 if (!r.uri.empty ()) {
921 plugin->load_preset (r);
926 if (!seen_saving_message) {
927 seen_saving_message = true;
928 show_no_plugin_message();
934 PlugUIBase::save_plugin_setting ()
936 #ifndef NO_PLUGIN_STATE
937 string const name = _preset_combo.get_text ();
938 plugin->remove_preset (name);
939 Plugin::PresetRecord const r = plugin->save_preset (name);
940 if (!r.uri.empty ()) {
941 plugin->load_preset (r);
944 if (!seen_saving_message) {
945 seen_saving_message = true;
946 show_no_plugin_message();
952 PlugUIBase::delete_plugin_setting ()
954 #ifndef NO_PLUGIN_STATE
955 plugin->remove_preset (_preset_combo.get_text ());
957 if (!seen_saving_message) {
958 seen_saving_message = true;
959 show_no_plugin_message();
965 PlugUIBase::automation_state_changed ()
967 reset_button.set_sensitive (insert->can_reset_all_parameters());
971 PlugUIBase::reset_plugin_parameters ()
973 insert->reset_parameters_to_default ();
977 PlugUIBase::manage_pins ()
979 PluginPinWindowProxy* proxy = insert->pinmgr_proxy ();
983 proxy->get ()->raise();
988 PlugUIBase::bypass_button_release (GdkEventButton*)
990 bool view_says_bypassed = (bypass_button.active_state() != 0);
992 if (view_says_bypassed != insert->enabled ()) {
993 insert->enable (view_says_bypassed);
1000 PlugUIBase::focus_toggled (GdkEventButton*)
1002 if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
1003 Keyboard::the_keyboard().magic_widget_drop_focus();
1004 focus_button.remove ();
1005 focus_button.add (*focus_out_image);
1006 focus_out_image->show ();
1007 set_tooltip (focus_button, string_compose (_("Click to allow the plugin to receive keyboard events that %1 would normally use as a shortcut"), PROGRAM_NAME));
1008 KeyboardFocused (false);
1010 Keyboard::the_keyboard().magic_widget_grab_focus();
1011 focus_button.remove ();
1012 focus_button.add (*focus_in_image);
1013 focus_in_image->show ();
1014 set_tooltip (focus_button, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME));
1015 KeyboardFocused (true);
1022 PlugUIBase::toggle_description()
1024 if (description_expander.get_expanded() &&
1025 !description_expander.get_child()) {
1026 const std::string text = plugin->get_docs();
1031 Gtk::Label* label = manage(new Gtk::Label(text));
1032 label->set_line_wrap(true);
1033 label->set_line_wrap_mode(Pango::WRAP_WORD);
1034 description_expander.add(*label);
1035 description_expander.show_all();
1038 if (!description_expander.get_expanded()) {
1039 const int child_height = description_expander.get_child ()->get_height ();
1041 description_expander.remove();
1043 Gtk::Window *toplevel = (Gtk::Window*) description_expander.get_ancestor (GTK_TYPE_WINDOW);
1046 Gtk::Requisition wr;
1047 toplevel->get_size (wr.width, wr.height);
1048 wr.height -= child_height;
1049 toplevel->resize (wr.width, wr.height);
1055 PlugUIBase::toggle_plugin_analysis()
1057 if (plugin_analysis_expander.get_expanded() &&
1058 !plugin_analysis_expander.get_child()) {
1061 eqgui = new PluginEqGui (insert);
1064 plugin_analysis_expander.add (*eqgui);
1065 plugin_analysis_expander.show_all ();
1066 eqgui->start_listening ();
1069 if (!plugin_analysis_expander.get_expanded()) {
1070 // Hide & remove from expander
1071 const int child_height = plugin_analysis_expander.get_child ()->get_height ();
1074 eqgui->stop_listening ();
1075 plugin_analysis_expander.remove();
1077 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
1080 Gtk::Requisition wr;
1081 toplevel->get_size (wr.width, wr.height);
1082 wr.height -= child_height;
1083 toplevel->resize (wr.width, wr.height);
1089 PlugUIBase::toggle_cpuload_display()
1091 if (cpuload_expander.get_expanded() && !cpuload_expander.get_child()) {
1092 if (stats_gui == 0) {
1093 stats_gui = new PluginLoadStatsGui (insert);
1095 cpuload_expander.add (*stats_gui);
1096 cpuload_expander.show_all();
1097 stats_gui->start_updating ();
1100 if (!cpuload_expander.get_expanded()) {
1101 const int child_height = cpuload_expander.get_child ()->get_height ();
1104 stats_gui->stop_updating ();
1105 cpuload_expander.remove();
1107 Gtk::Window *toplevel = (Gtk::Window*) cpuload_expander.get_ancestor (GTK_TYPE_WINDOW);
1110 Gtk::Requisition wr;
1111 toplevel->get_size (wr.width, wr.height);
1112 wr.height -= child_height;
1113 toplevel->resize (wr.width, wr.height);
1120 PlugUIBase::update_preset_list ()
1122 using namespace Menu_Helpers;
1124 vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets();
1128 // Add a menu entry for each preset
1129 _preset_combo.clear_items();
1130 for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator i = presets.begin(); i != presets.end(); ++i) {
1131 _preset_combo.AddMenuElem(
1132 MenuElem(i->label, sigc::bind(sigc::mem_fun(*this, &PlugUIBase::preset_selected), *i)));
1135 // Add an empty entry for un-setting current preset (see preset_selected)
1136 Plugin::PresetRecord no_preset;
1137 _preset_combo.AddMenuElem(
1138 MenuElem("", sigc::bind(sigc::mem_fun(*this, &PlugUIBase::preset_selected), no_preset)));
1144 PlugUIBase::update_preset ()
1146 Plugin::PresetRecord p = plugin->last_preset();
1149 if (p.uri.empty()) {
1150 _preset_combo.set_text (_("(none)"));
1152 _preset_combo.set_text (p.label);
1156 save_button.set_sensitive (!p.uri.empty() && p.user);
1157 delete_button.set_sensitive (!p.uri.empty() && p.user);
1159 update_preset_modified ();
1163 PlugUIBase::update_preset_modified ()
1166 if (plugin->last_preset().uri.empty()) {
1167 _preset_modified.set_text ("");
1171 bool const c = plugin->parameter_changed_since_last_preset ();
1172 if (_preset_modified.get_text().empty() == c) {
1173 _preset_modified.set_text (c ? "*" : "");
1178 PlugUIBase::preset_added_or_removed ()
1180 /* Update both the list and the currently-displayed preset */
1181 update_preset_list ();