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*);
108 boost::shared_ptr<ARDOUR::PluginInsert> _insert;
109 sigc::connection update_cpu_label_connection;
116 Gtk::DrawingArea _darea;
123 PluginLoadStatsGui::PluginLoadStatsGui (boost::shared_ptr<ARDOUR::PluginInsert> insert)
125 , _lbl_min ("", ALIGN_RIGHT, ALIGN_CENTER)
126 , _lbl_max ("", ALIGN_RIGHT, ALIGN_CENTER)
127 , _lbl_avg ("", ALIGN_RIGHT, ALIGN_CENTER)
128 , _lbl_dev ("", ALIGN_RIGHT, ALIGN_CENTER)
131 _darea.signal_expose_event ().connect (sigc::mem_fun (*this, &PluginLoadStatsGui::draw_bar));
132 set_size_request_to_display_given_text (_lbl_dev, string_compose (_("%1 [ms]"), 999.123), 0, 0);
134 attach (*manage (new Gtk::Label (_("Min:"), ALIGN_RIGHT, ALIGN_CENTER)),
135 0, 1, 0, 1, Gtk::FILL, Gtk::SHRINK, 4, 0);
136 attach (*manage (new Gtk::Label (_("Max:"), ALIGN_RIGHT, ALIGN_CENTER)),
137 0, 1, 1, 2, Gtk::FILL, Gtk::SHRINK, 4, 0);
138 attach (*manage (new Gtk::Label (_("Avg:"), ALIGN_RIGHT, ALIGN_CENTER)),
139 0, 1, 2, 3, Gtk::FILL, Gtk::SHRINK, 4, 0);
140 attach (*manage (new Gtk::Label (_("Dev:"), ALIGN_RIGHT, ALIGN_CENTER)),
141 0, 1, 3, 4, Gtk::FILL, Gtk::SHRINK, 4, 0);
143 attach (_lbl_min, 1, 2, 0, 1, Gtk::FILL, Gtk::SHRINK);
144 attach (_lbl_max, 1, 2, 1, 2, Gtk::FILL, Gtk::SHRINK);
145 attach (_lbl_avg, 1, 2, 2, 3, Gtk::FILL, Gtk::SHRINK);
146 attach (_lbl_dev, 1, 2, 3, 4, Gtk::FILL, Gtk::SHRINK);
148 attach (*manage (new Gtk::VSeparator ()),
149 2, 3, 0, 4, Gtk::FILL, Gtk::FILL, 4, 0);
151 attach (_darea, 3, 4, 0, 4, Gtk::FILL|Gtk::EXPAND, Gtk::FILL, 4, 4);
155 PluginLoadStatsGui::update_cpu_label()
157 if (_insert->get_stats (_min, _max, _avg, _dev)) {
159 _lbl_min.set_text (string_compose (_("%1 [ms]"), rint (_min / 10.) / 100.));
160 _lbl_max.set_text (string_compose (_("%1 [ms]"), rint (_max / 10.) / 100.));
161 _lbl_avg.set_text (string_compose (_("%1 [ms]"), rint (_avg) / 1000.));
162 _lbl_dev.set_text (string_compose (_("%1 [ms]"), rint (_dev) / 1000.));
165 _lbl_min.set_text ("-");
166 _lbl_max.set_text ("-");
167 _lbl_avg.set_text ("-");
168 _lbl_dev.set_text ("-");
170 _darea.queue_draw ();
174 PluginLoadStatsGui::draw_bar (GdkEventExpose* ev)
176 Gtk::Allocation a = _darea.get_allocation ();
177 double const width = a.get_width ();
178 double const height = a.get_height ();
179 cairo_t* cr = gdk_cairo_create (_darea.get_window ()->gobj ());
180 cairo_rectangle (cr, ev->area.x, ev->area.y, ev->area.width, ev->area.height);
183 Gdk::Color const bg = get_style ()->get_bg (STATE_NORMAL);
184 Gdk::Color const fg = get_style ()->get_fg (STATE_NORMAL);
186 cairo_set_source_rgb (cr, bg.get_red_p (), bg.get_green_p (), bg.get_blue_p ());
187 cairo_rectangle (cr, 0, 0, width, height);
190 int border = height / 7;
194 int x1 = width - border;
195 int y1 = height - 3 * border;
197 const int w = x1 - x0;
198 const int h = y1 - y0;
199 const double cycle_ms = 1000. * _insert->session().get_block_size() / (double)_insert->session().nominal_sample_rate();
202 # define DEFLECT(T) ( (T) * w * 8. / (9. * cycle_ms) )
204 # define DEFLECT(T) ( log1p((T) * 9. / cycle_ms) * w * 8. / (9 * log (10)) )
208 rounded_rectangle (cr, x0, y0, w, h, 7);
210 cairo_pattern_t *pat = cairo_pattern_create_linear (x0, 0.0, w, 0.0);
211 cairo_pattern_add_color_stop_rgba (pat, 0, 0, 1, 0, .7);
212 cairo_pattern_add_color_stop_rgba (pat, 6. / 9., 0, 1, 0, .7);
213 cairo_pattern_add_color_stop_rgba (pat, 6.5 / 9., .8, .8, 0, .7);
214 cairo_pattern_add_color_stop_rgba (pat, 7.5 / 9., .8, .8, 0, .7);
215 cairo_pattern_add_color_stop_rgba (pat, 8. / 9., 1, 0, 0, .7);
216 cairo_set_source (cr, pat);
217 cairo_pattern_destroy (pat);
218 cairo_fill_preserve (cr);
220 cairo_set_source_rgba (cr, .4, .3, .1, .5);
221 cairo_fill_preserve (cr);
227 double xmin = DEFLECT(_min / 1000.);
228 double xmax = DEFLECT(_max / 1000.);
230 rounded_rectangle (cr, x0 + xmin, y0, xmax - xmin, h, 7);
231 cairo_set_source_rgba (cr, .8, .8, .9, .4);
237 Glib::RefPtr<Pango::Layout> layout;
238 layout = Pango::Layout::create (get_pango_context ());
240 cairo_set_line_width (cr, 1);
242 for (int i = 1; i < 9; ++i) {
243 int text_width, text_height;
245 double v = cycle_ms * i / 8.;
247 double v = (exp (i * log (10) / 8) - 1) * cycle_ms / 9.;
249 double decimal = v > 10 ? 10 : 100;
250 layout->set_text (string_compose ("%1", rint (decimal * v) / decimal));
251 layout->get_pixel_size (text_width, text_height);
253 const int dx = w * i / 9.; // == DEFLECT (v)
255 cairo_move_to (cr, x0 + dx - .5, y0);
256 cairo_line_to (cr, x0 + dx - .5, y1);
257 cairo_set_source_rgba (cr, 1., 1., 1., 1.);
260 cairo_move_to (cr, x0 + dx - .5 * text_width, y1 + 1);
261 cairo_set_source_rgb (cr, fg.get_red_p (), fg.get_green_p (), fg.get_blue_p ());
262 pango_cairo_show_layout (cr, layout->gobj ());
266 int text_width, text_height;
267 layout->set_text ("0");
268 cairo_move_to (cr, x0 + 1, y1 + 1);
269 cairo_set_source_rgb (cr, fg.get_red_p (), fg.get_green_p (), fg.get_blue_p ());
270 pango_cairo_show_layout (cr, layout->gobj ());
272 layout->set_text (_("[ms]"));
273 layout->get_pixel_size (text_width, text_height);
274 cairo_move_to (cr, x0 + w - text_width - 1, y1 + 1);
275 pango_cairo_show_layout (cr, layout->gobj ());
280 rounded_rectangle (cr, x0, y0, w, h, 7);
283 double xavg = DEFLECT(_avg / 1000.);
284 double xd0 = DEFLECT((_avg - _dev) / 1000.);
285 double xd1 = DEFLECT((_avg + _dev) / 1000.);
287 cairo_set_line_width (cr, 3);
288 cairo_set_source_rgba (cr, .0, .1, .0, 1.);
289 cairo_move_to (cr, x0 + xavg - 1.5, y0);
290 cairo_line_to (cr, x0 + xavg - 1.5, y1);
294 cairo_set_line_width (cr, 1);
295 cairo_set_source_rgba (cr, 0, 0, 0, 1.);
296 cairo_move_to (cr, floor (x0 + xd0), .5 + y0 + h / 2);
297 cairo_line_to (cr, ceil (x0 + xd1), .5 + y0 + h / 2);
300 cairo_move_to (cr, floor (x0 + xd0) - .5, y0 + h / 4);
301 cairo_line_to (cr, floor (x0 + xd0) - .5, y0 + h * 3 / 4);
303 cairo_move_to (cr, ceil (x0 + xd1) - .5, y0 + h / 4);
304 cairo_line_to (cr, ceil (x0 + xd1) - .5, y0 + h * 3 / 4);
319 PluginUIWindow::PluginUIWindow (
320 boost::shared_ptr<PluginInsert> insert,
323 : ArdourWindow (string())
324 , was_visible (false)
325 , _keyboard_focused (false)
326 #ifdef AUDIOUNIT_SUPPORT
327 , pre_deactivate_x (-1)
328 , pre_deactivate_y (-1)
332 bool have_gui = false;
333 Label* label = manage (new Label());
334 label->set_markup ("<b>THIS IS THE PLUGIN UI</b>");
336 if (editor && insert->plugin()->has_editor()) {
337 switch (insert->type()) {
338 case ARDOUR::Windows_VST:
339 have_gui = create_windows_vst_editor (insert);
343 have_gui = create_lxvst_editor (insert);
347 have_gui = create_mac_vst_editor (insert);
350 case ARDOUR::AudioUnit:
351 have_gui = create_audiounit_editor (insert);
355 error << _("Eh? LADSPA plugins don't have editors!") << endmsg;
359 have_gui = create_lv2_editor (insert);
363 #ifndef WINDOWS_VST_SUPPORT
364 error << string_compose (_("unknown type of editor-supplying plugin (note: no VST support in this version of %1)"), PROGRAM_NAME)
367 error << _("unknown type of editor-supplying plugin")
370 throw failed_constructor ();
376 GenericPluginUI* pu = new GenericPluginUI (insert, scrollable);
379 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
381 set_wmclass (X_("ardour_plugin_editor"), PROGRAM_NAME);
383 signal_map_event().connect (sigc::mem_fun (*pu, &GenericPluginUI::start_updating));
384 signal_unmap_event().connect (sigc::mem_fun (*pu, &GenericPluginUI::stop_updating));
387 set_name ("PluginEditor");
388 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
390 insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PluginUIWindow::plugin_going_away, this), gui_context());
392 gint h = _pluginui->get_preferred_height ();
393 gint w = _pluginui->get_preferred_width ();
396 if (h > 600) h = 600;
399 set_default_size (w, h);
400 set_resizable (_pluginui->resizable());
403 PluginUIWindow::~PluginUIWindow ()
406 cerr << "PluginWindow deleted for " << this << endl;
412 PluginUIWindow::on_show ()
414 set_role("plugin_ui");
417 _pluginui->update_preset_list ();
418 _pluginui->update_preset ();
422 #if defined (HAVE_AUDIOUNITS) && defined(__APPLE__)
423 if (pre_deactivate_x >= 0) {
424 move (pre_deactivate_x, pre_deactivate_y);
428 if (_pluginui->on_window_show (_title)) {
435 PluginUIWindow::on_hide ()
437 #if defined (HAVE_AUDIOUNITS) && defined(__APPLE__)
438 get_position (pre_deactivate_x, pre_deactivate_y);
444 _pluginui->on_window_hide ();
449 PluginUIWindow::set_title(const std::string& title)
451 Gtk::Window::set_title(title);
456 #ifdef WINDOWS_VST_SUPPORT
457 PluginUIWindow::create_windows_vst_editor(boost::shared_ptr<PluginInsert> insert)
459 PluginUIWindow::create_windows_vst_editor(boost::shared_ptr<PluginInsert>)
462 #ifndef WINDOWS_VST_SUPPORT
466 boost::shared_ptr<WindowsVSTPlugin> vp;
468 if ((vp = boost::dynamic_pointer_cast<WindowsVSTPlugin> (insert->plugin())) == 0) {
469 error << string_compose (_("unknown type of editor-supplying plugin (note: no VST support in this version of %1)"), PROGRAM_NAME)
471 throw failed_constructor ();
473 WindowsVSTPluginUI* vpu = new WindowsVSTPluginUI (insert, vp, GTK_WIDGET(this->gobj()));
476 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
478 vpu->package (*this);
487 PluginUIWindow::create_lxvst_editor(boost::shared_ptr<PluginInsert> insert)
489 PluginUIWindow::create_lxvst_editor(boost::shared_ptr<PluginInsert>)
492 #ifndef LXVST_SUPPORT
496 boost::shared_ptr<LXVSTPlugin> lxvp;
498 if ((lxvp = boost::dynamic_pointer_cast<LXVSTPlugin> (insert->plugin())) == 0) {
499 error << string_compose (_("unknown type of editor-supplying plugin (note: no linuxVST support in this version of %1)"), PROGRAM_NAME)
501 throw failed_constructor ();
503 LXVSTPluginUI* lxvpu = new LXVSTPluginUI (insert, lxvp);
506 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
508 lxvpu->package (*this);
516 #ifdef MACVST_SUPPORT
517 PluginUIWindow::create_mac_vst_editor (boost::shared_ptr<PluginInsert> insert)
519 PluginUIWindow::create_mac_vst_editor (boost::shared_ptr<PluginInsert>)
522 #ifndef MACVST_SUPPORT
525 boost::shared_ptr<MacVSTPlugin> mvst;
526 if ((mvst = boost::dynamic_pointer_cast<MacVSTPlugin> (insert->plugin())) == 0) {
527 error << string_compose (_("unknown type of editor-supplying plugin (note: no MacVST support in this version of %1)"), PROGRAM_NAME)
529 throw failed_constructor ();
531 VSTPluginUI* vpu = create_mac_vst_gui (insert);
533 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
535 vpu->package (*this);
537 Application::instance()->ActivationChanged.connect (mem_fun (*this, &PluginUIWindow::app_activated));
545 #ifdef AUDIOUNIT_SUPPORT
546 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert> insert)
548 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert>)
551 #ifndef AUDIOUNIT_SUPPORT
555 _pluginui = create_au_gui (insert, &box);
556 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
559 Application::instance()->ActivationChanged.connect (mem_fun (*this, &PluginUIWindow::app_activated));
567 PluginUIWindow::app_activated (bool yn)
569 PluginUIWindow::app_activated (bool)
572 #ifdef AUDIOUNIT_SUPPORT
576 _pluginui->activate ();
577 if (pre_deactivate_x >= 0) {
578 move (pre_deactivate_x, pre_deactivate_y);
584 was_visible = is_visible();
585 get_position (pre_deactivate_x, pre_deactivate_y);
587 _pluginui->deactivate ();
594 PluginUIWindow::create_lv2_editor(boost::shared_ptr<PluginInsert> insert)
597 boost::shared_ptr<LV2Plugin> vp;
599 if ((vp = boost::dynamic_pointer_cast<LV2Plugin> (insert->plugin())) == 0) {
600 error << _("create_lv2_editor called on non-LV2 plugin") << endmsg;
601 throw failed_constructor ();
603 LV2PluginUI* lpu = new LV2PluginUI (insert, vp);
606 lpu->package (*this);
607 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
617 PluginUIWindow::keyboard_focused (bool yn)
619 _keyboard_focused = yn;
623 PluginUIWindow::on_key_press_event (GdkEventKey* event)
625 if (_keyboard_focused) {
627 _pluginui->grab_focus();
628 if (_pluginui->non_gtk_gui()) {
629 _pluginui->forward_key_event (event);
631 return relay_key_press (event, this);
636 /* for us to be getting key press events, there really
637 MUST be a _pluginui, but just to be safe, check ...
641 _pluginui->grab_focus();
642 if (_pluginui->non_gtk_gui()) {
643 /* pass main window as the window for the event
644 to be handled in, not this one, because there are
645 no widgets in this window that we want to have
648 return relay_key_press (event, &ARDOUR_UI::instance()->main_window());
650 return relay_key_press (event, this);
658 PluginUIWindow::on_key_release_event (GdkEventKey *event)
660 if (_keyboard_focused) {
662 if (_pluginui->non_gtk_gui()) {
663 _pluginui->forward_key_event (event);
667 gtk_window_propagate_key_event (GTK_WINDOW(gobj()), event);
669 /* don't forward releases */
674 PluginUIWindow::plugin_going_away ()
676 ENSURE_GUI_THREAD (*this, &PluginUIWindow::plugin_going_away)
679 _pluginui->stop_updating(0);
682 death_connection.disconnect ();
685 PlugUIBase::PlugUIBase (boost::shared_ptr<PluginInsert> pi)
687 , plugin (insert->plugin())
688 , add_button (_("Add"))
689 , save_button (_("Save"))
690 , delete_button (_("Delete"))
691 , reset_button (_("Reset"))
692 , bypass_button (ArdourButton::led_default_elements)
693 , pin_management_button (_("Pinout"))
694 , description_expander (_("Description"))
695 , plugin_analysis_expander (_("Plugin analysis"))
696 , cpuload_expander (_("CPU Profile"))
702 _preset_modified.set_size_request (16, -1);
703 _preset_combo.set_text("(default)");
704 set_tooltip (_preset_combo, _("Presets (if any) for this plugin\n(Both factory and user-created)"));
705 set_tooltip (add_button, _("Save a new preset"));
706 set_tooltip (save_button, _("Save the current preset"));
707 set_tooltip (delete_button, _("Delete the current preset"));
708 set_tooltip (reset_button, _("Reset parameters to default (if no parameters are in automation play mode)"));
709 set_tooltip (pin_management_button, _("Show Plugin Pin Management Dialog"));
710 set_tooltip (bypass_button, _("Disable signal processing by the plugin"));
713 update_preset_list ();
716 add_button.set_name ("generic button");
717 add_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::add_plugin_setting));
719 save_button.set_name ("generic button");
720 save_button.signal_clicked.connect(sigc::mem_fun(*this, &PlugUIBase::save_plugin_setting));
722 delete_button.set_name ("generic button");
723 delete_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::delete_plugin_setting));
725 reset_button.set_name ("generic button");
726 reset_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::reset_plugin_parameters));
728 pin_management_button.set_name ("generic button");
729 pin_management_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::manage_pins));
731 insert->ActiveChanged.connect (active_connection, invalidator (*this), boost::bind (&PlugUIBase::processor_active_changed, this, boost::weak_ptr<Processor>(insert)), gui_context());
733 bypass_button.set_name ("plugin bypass button");
734 bypass_button.set_text (_("Bypass"));
735 bypass_button.set_active (!pi->enabled ());
736 bypass_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::bypass_button_release), false);
737 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
739 focus_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::focus_toggled));
740 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
742 /* these images are not managed, so that we can remove them at will */
744 focus_out_image = new Image (get_icon (X_("computer_keyboard")));
745 focus_in_image = new Image (get_icon (X_("computer_keyboard_active")));
747 focus_button.add (*focus_out_image);
749 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));
750 set_tooltip (bypass_button, _("Click to enable/disable this plugin"));
752 description_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_description));
753 description_expander.set_expanded(false);
755 plugin_analysis_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_plugin_analysis));
756 plugin_analysis_expander.set_expanded(false);
758 cpuload_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_cpuload_display));
759 cpuload_expander.set_expanded(false);
761 insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PlugUIBase::plugin_going_away, this), gui_context());
763 plugin->PresetAdded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
764 plugin->PresetRemoved.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
765 plugin->PresetLoaded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset, this), gui_context ());
766 plugin->PresetDirty.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset_modified, this), gui_context ());
768 insert->AutomationStateChanged.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::automation_state_changed, this), gui_context());
770 automation_state_changed();
773 PlugUIBase::~PlugUIBase()
781 PlugUIBase::plugin_going_away ()
783 /* drop references to the plugin/insert */
789 PlugUIBase::set_latency_label ()
791 samplecnt_t const l = insert->effective_latency ();
792 samplecnt_t const sr = insert->session().sample_rate ();
797 t = string_compose (P_("latency (%1 sample)", "latency (%1 samples)", l), l);
799 t = string_compose (_("latency (%1 ms)"), (float) l / ((float) sr / 1000.0f));
802 latency_button.set_text (t);
806 PlugUIBase::latency_button_clicked ()
809 latency_gui = new LatencyGUI (*(insert.get()), insert->session().sample_rate(), insert->session().get_block_size());
810 latency_dialog = new ArdourWindow (_("Edit Latency"));
811 /* use both keep-above and transient for to try cover as many
812 different WM's as possible.
814 latency_dialog->set_keep_above (true);
815 Window* win = dynamic_cast<Window*> (bypass_button.get_toplevel ());
817 latency_dialog->set_transient_for (*win);
819 latency_dialog->add (*latency_gui);
820 latency_dialog->signal_hide().connect (sigc::mem_fun (*this, &PlugUIBase::set_latency_label));
823 latency_dialog->show_all ();
827 PlugUIBase::processor_active_changed (boost::weak_ptr<Processor> weak_p)
829 ENSURE_GUI_THREAD (*this, &PlugUIBase::processor_active_changed, weak_p);
830 boost::shared_ptr<Processor> p (weak_p.lock());
833 bypass_button.set_active (!p->enabled ());
838 PlugUIBase::preset_selected (Plugin::PresetRecord preset)
840 if (_no_load_preset) {
843 if (!preset.label.empty()) {
844 insert->load_preset (preset);
846 // blank selected = no preset
847 plugin->clear_preset();
851 #ifdef NO_PLUGIN_STATE
852 static bool seen_saving_message = false;
854 static void show_no_plugin_message()
856 info << string_compose (_("Plugin presets are not supported in this build of %1. Consider paying for a full version"),
859 info << _("To get full access to updates without this limitation\n"
860 "consider becoming a subscriber for a low cost every month.")
862 info << X_("https://community.ardour.org/s/subscribe")
864 ARDOUR_UI::instance()->popup_error(_("Plugin presets are not supported in this build, see the Log window for more information."));
869 PlugUIBase::add_plugin_setting ()
871 #ifndef NO_PLUGIN_STATE
872 NewPluginPresetDialog d (plugin, _("New Preset"));
875 case Gtk::RESPONSE_ACCEPT:
876 if (d.name().empty()) {
881 plugin->remove_preset (d.name ());
884 Plugin::PresetRecord const r = plugin->save_preset (d.name());
885 if (!r.uri.empty ()) {
886 plugin->load_preset (r);
891 if (!seen_saving_message) {
892 seen_saving_message = true;
893 show_no_plugin_message();
899 PlugUIBase::save_plugin_setting ()
901 #ifndef NO_PLUGIN_STATE
902 string const name = _preset_combo.get_text ();
903 plugin->remove_preset (name);
904 Plugin::PresetRecord const r = plugin->save_preset (name);
905 if (!r.uri.empty ()) {
906 plugin->load_preset (r);
909 if (!seen_saving_message) {
910 seen_saving_message = true;
911 show_no_plugin_message();
917 PlugUIBase::delete_plugin_setting ()
919 #ifndef NO_PLUGIN_STATE
920 plugin->remove_preset (_preset_combo.get_text ());
922 if (!seen_saving_message) {
923 seen_saving_message = true;
924 show_no_plugin_message();
930 PlugUIBase::automation_state_changed ()
932 reset_button.set_sensitive (insert->can_reset_all_parameters());
936 PlugUIBase::reset_plugin_parameters ()
938 insert->reset_parameters_to_default ();
942 PlugUIBase::manage_pins ()
944 PluginPinWindowProxy* proxy = insert->pinmgr_proxy ();
948 proxy->get ()->raise();
953 PlugUIBase::bypass_button_release (GdkEventButton*)
955 bool view_says_bypassed = (bypass_button.active_state() != 0);
957 if (view_says_bypassed != insert->enabled ()) {
958 insert->enable (view_says_bypassed);
965 PlugUIBase::focus_toggled (GdkEventButton*)
967 if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
968 Keyboard::the_keyboard().magic_widget_drop_focus();
969 focus_button.remove ();
970 focus_button.add (*focus_out_image);
971 focus_out_image->show ();
972 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));
973 KeyboardFocused (false);
975 Keyboard::the_keyboard().magic_widget_grab_focus();
976 focus_button.remove ();
977 focus_button.add (*focus_in_image);
978 focus_in_image->show ();
979 set_tooltip (focus_button, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME));
980 KeyboardFocused (true);
987 PlugUIBase::toggle_description()
989 if (description_expander.get_expanded() &&
990 !description_expander.get_child()) {
991 const std::string text = plugin->get_docs();
996 Gtk::Label* label = manage(new Gtk::Label(text));
997 label->set_line_wrap(true);
998 label->set_line_wrap_mode(Pango::WRAP_WORD);
999 description_expander.add(*label);
1000 description_expander.show_all();
1003 if (!description_expander.get_expanded()) {
1004 const int child_height = description_expander.get_child ()->get_height ();
1006 description_expander.remove();
1008 Gtk::Window *toplevel = (Gtk::Window*) description_expander.get_ancestor (GTK_TYPE_WINDOW);
1011 Gtk::Requisition wr;
1012 toplevel->get_size (wr.width, wr.height);
1013 wr.height -= child_height;
1014 toplevel->resize (wr.width, wr.height);
1020 PlugUIBase::toggle_plugin_analysis()
1022 if (plugin_analysis_expander.get_expanded() &&
1023 !plugin_analysis_expander.get_child()) {
1026 eqgui = new PluginEqGui (insert);
1029 plugin_analysis_expander.add (*eqgui);
1030 plugin_analysis_expander.show_all ();
1031 eqgui->start_listening ();
1034 if (!plugin_analysis_expander.get_expanded()) {
1035 // Hide & remove from expander
1036 const int child_height = plugin_analysis_expander.get_child ()->get_height ();
1039 eqgui->stop_listening ();
1040 plugin_analysis_expander.remove();
1042 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
1045 Gtk::Requisition wr;
1046 toplevel->get_size (wr.width, wr.height);
1047 wr.height -= child_height;
1048 toplevel->resize (wr.width, wr.height);
1054 PlugUIBase::toggle_cpuload_display()
1056 if (cpuload_expander.get_expanded() && !cpuload_expander.get_child()) {
1057 if (stats_gui == 0) {
1058 stats_gui = new PluginLoadStatsGui (insert);
1060 cpuload_expander.add (*stats_gui);
1061 cpuload_expander.show_all();
1062 stats_gui->start_updating ();
1065 if (!cpuload_expander.get_expanded()) {
1066 const int child_height = cpuload_expander.get_child ()->get_height ();
1069 stats_gui->stop_updating ();
1070 cpuload_expander.remove();
1072 Gtk::Window *toplevel = (Gtk::Window*) cpuload_expander.get_ancestor (GTK_TYPE_WINDOW);
1075 Gtk::Requisition wr;
1076 toplevel->get_size (wr.width, wr.height);
1077 wr.height -= child_height;
1078 toplevel->resize (wr.width, wr.height);
1085 PlugUIBase::update_preset_list ()
1087 using namespace Menu_Helpers;
1089 vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets();
1093 // Add a menu entry for each preset
1094 _preset_combo.clear_items();
1095 for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator i = presets.begin(); i != presets.end(); ++i) {
1096 _preset_combo.AddMenuElem(
1097 MenuElem(i->label, sigc::bind(sigc::mem_fun(*this, &PlugUIBase::preset_selected), *i)));
1100 // Add an empty entry for un-setting current preset (see preset_selected)
1101 Plugin::PresetRecord no_preset;
1102 _preset_combo.AddMenuElem(
1103 MenuElem("", sigc::bind(sigc::mem_fun(*this, &PlugUIBase::preset_selected), no_preset)));
1109 PlugUIBase::update_preset ()
1111 Plugin::PresetRecord p = plugin->last_preset();
1114 if (p.uri.empty()) {
1115 _preset_combo.set_text (_("(none)"));
1117 _preset_combo.set_text (p.label);
1121 save_button.set_sensitive (!p.uri.empty() && p.user);
1122 delete_button.set_sensitive (!p.uri.empty() && p.user);
1124 update_preset_modified ();
1128 PlugUIBase::update_preset_modified ()
1131 if (plugin->last_preset().uri.empty()) {
1132 _preset_modified.set_text ("");
1136 bool const c = plugin->parameter_changed_since_last_preset ();
1137 if (_preset_modified.get_text().empty() == c) {
1138 _preset_modified.set_text (c ? "*" : "");
1143 PlugUIBase::preset_added_or_removed ()
1145 /* Update both the list and the currently-displayed preset */
1146 update_preset_list ();