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]"), 999.123), 0, 0);
141 attach (*manage (new Gtk::Label (_("Minimum:"), ALIGN_RIGHT, ALIGN_CENTER)),
142 0, 1, 0, 1, Gtk::FILL, Gtk::SHRINK, 4, 0);
143 attach (*manage (new Gtk::Label (_("Maximum:"), ALIGN_RIGHT, ALIGN_CENTER)),
144 0, 1, 1, 2, Gtk::FILL, Gtk::SHRINK, 4, 0);
145 attach (*manage (new Gtk::Label (_("Average:"), ALIGN_RIGHT, ALIGN_CENTER)),
146 0, 1, 2, 3, Gtk::FILL, Gtk::SHRINK, 4, 0);
147 attach (*manage (new Gtk::Label (_("Std.Dev:"), ALIGN_RIGHT, ALIGN_CENTER)),
148 0, 1, 3, 4, Gtk::FILL, Gtk::SHRINK, 4, 0);
150 attach (_lbl_min, 1, 2, 0, 1, Gtk::FILL, Gtk::SHRINK);
151 attach (_lbl_max, 1, 2, 1, 2, Gtk::FILL, Gtk::SHRINK);
152 attach (_lbl_avg, 1, 2, 2, 3, Gtk::FILL, Gtk::SHRINK);
153 attach (_lbl_dev, 1, 2, 3, 4, Gtk::FILL, Gtk::SHRINK);
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.));
174 _lbl_min.set_text ("-");
175 _lbl_max.set_text ("-");
176 _lbl_avg.set_text ("-");
177 _lbl_dev.set_text ("-");
179 _darea.queue_draw ();
183 PluginLoadStatsGui::draw_bar (GdkEventExpose* ev)
185 Gtk::Allocation a = _darea.get_allocation ();
186 const int width = a.get_width ();
187 const int height = a.get_height ();
188 cairo_t* cr = gdk_cairo_create (_darea.get_window ()->gobj ());
189 cairo_rectangle (cr, ev->area.x, ev->area.y, ev->area.width, ev->area.height);
192 Gdk::Color const bg = get_style ()->get_bg (STATE_NORMAL);
193 Gdk::Color const fg = get_style ()->get_fg (STATE_NORMAL);
195 cairo_set_source_rgb (cr, bg.get_red_p (), bg.get_green_p (), bg.get_blue_p ());
196 cairo_rectangle (cr, 0, 0, width, height);
199 int border = (height / 7) | 1;
203 int x1 = width - border;
204 int y1 = (height - 3 * border) & ~1;
206 const int w = x1 - x0;
207 const int h = y1 - y0;
208 const double cycle_ms = 1000. * _insert->session().get_block_size() / (double)_insert->session().nominal_sample_rate();
210 const double base_mult = std::max (1.0, cycle_ms / 2.0);
211 const double log_base = log1p (base_mult);
214 # define DEFLECT(T) ( (T) * w * 8. / (9. * cycle_ms) )
216 # define DEFLECT(T) ( log1p((T) * base_mult / cycle_ms) * w * 8. / (9 * log_base) )
220 rounded_rectangle (cr, x0, y0, w, h, 7);
222 cairo_set_source_rgba (cr, .0, .0, .0, 1);
223 cairo_set_line_width (cr, 1);
224 cairo_stroke_preserve (cr);
227 cairo_pattern_t *pat = cairo_pattern_create_linear (x0, 0.0, w, 0.0);
228 cairo_pattern_add_color_stop_rgba (pat, 0, 0, 1, 0, .7);
229 cairo_pattern_add_color_stop_rgba (pat, 6. / 9., 0, 1, 0, .7);
230 cairo_pattern_add_color_stop_rgba (pat, 6.5 / 9., .8, .8, 0, .7);
231 cairo_pattern_add_color_stop_rgba (pat, 7.5 / 9., .8, .8, 0, .7);
232 cairo_pattern_add_color_stop_rgba (pat, 8. / 9., 1, 0, 0, .7);
233 cairo_set_source (cr, pat);
234 cairo_pattern_destroy (pat);
235 cairo_fill_preserve (cr);
237 cairo_set_source_rgba (cr, .4, .3, .1, .5);
238 cairo_fill_preserve (cr);
245 double xmin = DEFLECT(_min / 1000.);
246 double xmax = DEFLECT(_max / 1000.);
248 rounded_rectangle (cr, x0 + xmin, y0, xmax - xmin, h, 7);
249 cairo_set_source_rgba (cr, .8, .8, .9, .5);
255 Glib::RefPtr<Pango::Layout> layout;
256 layout = Pango::Layout::create (get_pango_context ());
258 cairo_set_line_width (cr, 1);
260 for (int i = 1; i < 9; ++i) {
261 int text_width, text_height;
263 double v = cycle_ms * i / 8.;
265 double v = (exp (i * log_base / 8) - 1) * cycle_ms / base_mult;
267 double decimal = v > 10 ? 10 : 100;
268 layout->set_text (string_compose ("%1", rint (decimal * v) / decimal));
269 layout->get_pixel_size (text_width, text_height);
271 const int dx = w * i / 9.; // == DEFLECT (v)
273 cairo_move_to (cr, x0 + dx - .5, y0);
274 cairo_line_to (cr, x0 + dx - .5, y1);
275 cairo_set_source_rgba (cr, 1., 1., 1., 1.);
278 cairo_move_to (cr, x0 + dx - .5 * text_width, y1 + 1);
279 cairo_set_source_rgb (cr, fg.get_red_p (), fg.get_green_p (), fg.get_blue_p ());
280 pango_cairo_show_layout (cr, layout->gobj ());
284 int text_width, text_height;
285 layout->set_text ("0");
286 cairo_move_to (cr, x0 + 1, y1 + 1);
287 cairo_set_source_rgb (cr, fg.get_red_p (), fg.get_green_p (), fg.get_blue_p ());
288 pango_cairo_show_layout (cr, layout->gobj ());
290 layout->set_text (_("[ms]"));
291 layout->get_pixel_size (text_width, text_height);
292 cairo_move_to (cr, x0 + w - text_width - 1, y1 + 1);
293 pango_cairo_show_layout (cr, layout->gobj ());
298 rounded_rectangle (cr, x0, y0, w, h, 7);
301 double xavg = DEFLECT(_avg / 1000.);
302 double xd0 = DEFLECT((_avg - _dev) / 1000.);
303 double xd1 = DEFLECT((_avg + _dev) / 1000.);
305 cairo_set_line_width (cr, 3);
306 cairo_set_source_rgba (cr, .0, .1, .0, 1.);
307 cairo_move_to (cr, x0 + xavg - 1.5, y0);
308 cairo_line_to (cr, x0 + xavg - 1.5, y1);
312 const double ym = .5 + floor ((double)(y0 + h / 2));
313 const int h4 = h / 4;
315 cairo_set_line_width (cr, 1);
316 cairo_set_source_rgba (cr, 0, 0, 0, 1.);
317 cairo_move_to (cr, floor (x0 + xd0), ym);
318 cairo_line_to (cr, ceil (x0 + xd1), ym);
321 cairo_move_to (cr, floor (x0 + xd0) - .5, ym - h4);
322 cairo_line_to (cr, floor (x0 + xd0) - .5, ym + h4);
324 cairo_move_to (cr, ceil (x0 + xd1) - .5, ym - h4);
325 cairo_line_to (cr, ceil (x0 + xd1) - .5, ym + h4);
340 PluginUIWindow::PluginUIWindow (
341 boost::shared_ptr<PluginInsert> insert,
344 : ArdourWindow (string())
345 , was_visible (false)
346 , _keyboard_focused (false)
347 #ifdef AUDIOUNIT_SUPPORT
348 , pre_deactivate_x (-1)
349 , pre_deactivate_y (-1)
353 bool have_gui = false;
354 Label* label = manage (new Label());
355 label->set_markup ("<b>THIS IS THE PLUGIN UI</b>");
357 if (editor && insert->plugin()->has_editor()) {
358 switch (insert->type()) {
359 case ARDOUR::Windows_VST:
360 have_gui = create_windows_vst_editor (insert);
364 have_gui = create_lxvst_editor (insert);
368 have_gui = create_mac_vst_editor (insert);
371 case ARDOUR::AudioUnit:
372 have_gui = create_audiounit_editor (insert);
376 error << _("Eh? LADSPA plugins don't have editors!") << endmsg;
380 have_gui = create_lv2_editor (insert);
384 #ifndef WINDOWS_VST_SUPPORT
385 error << string_compose (_("unknown type of editor-supplying plugin (note: no VST support in this version of %1)"), PROGRAM_NAME)
388 error << _("unknown type of editor-supplying plugin")
391 throw failed_constructor ();
397 GenericPluginUI* pu = new GenericPluginUI (insert, scrollable);
400 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
402 set_wmclass (X_("ardour_plugin_editor"), PROGRAM_NAME);
404 signal_map_event().connect (sigc::mem_fun (*pu, &GenericPluginUI::start_updating));
405 signal_unmap_event().connect (sigc::mem_fun (*pu, &GenericPluginUI::stop_updating));
408 set_name ("PluginEditor");
409 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
411 insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PluginUIWindow::plugin_going_away, this), gui_context());
413 gint h = _pluginui->get_preferred_height ();
414 gint w = _pluginui->get_preferred_width ();
417 if (h > 600) h = 600;
420 set_default_size (w, h);
421 set_resizable (_pluginui->resizable());
424 PluginUIWindow::~PluginUIWindow ()
427 cerr << "PluginWindow deleted for " << this << endl;
433 PluginUIWindow::on_show ()
435 set_role("plugin_ui");
438 _pluginui->update_preset_list ();
439 _pluginui->update_preset ();
443 #if defined (HAVE_AUDIOUNITS) && defined(__APPLE__)
444 if (pre_deactivate_x >= 0) {
445 move (pre_deactivate_x, pre_deactivate_y);
449 if (_pluginui->on_window_show (_title)) {
456 PluginUIWindow::on_hide ()
458 #if defined (HAVE_AUDIOUNITS) && defined(__APPLE__)
459 get_position (pre_deactivate_x, pre_deactivate_y);
465 _pluginui->on_window_hide ();
470 PluginUIWindow::set_title(const std::string& title)
472 Gtk::Window::set_title(title);
477 #ifdef WINDOWS_VST_SUPPORT
478 PluginUIWindow::create_windows_vst_editor(boost::shared_ptr<PluginInsert> insert)
480 PluginUIWindow::create_windows_vst_editor(boost::shared_ptr<PluginInsert>)
483 #ifndef WINDOWS_VST_SUPPORT
487 boost::shared_ptr<WindowsVSTPlugin> vp;
489 if ((vp = boost::dynamic_pointer_cast<WindowsVSTPlugin> (insert->plugin())) == 0) {
490 error << string_compose (_("unknown type of editor-supplying plugin (note: no VST support in this version of %1)"), PROGRAM_NAME)
492 throw failed_constructor ();
494 WindowsVSTPluginUI* vpu = new WindowsVSTPluginUI (insert, vp, GTK_WIDGET(this->gobj()));
497 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
499 vpu->package (*this);
508 PluginUIWindow::create_lxvst_editor(boost::shared_ptr<PluginInsert> insert)
510 PluginUIWindow::create_lxvst_editor(boost::shared_ptr<PluginInsert>)
513 #ifndef LXVST_SUPPORT
517 boost::shared_ptr<LXVSTPlugin> lxvp;
519 if ((lxvp = boost::dynamic_pointer_cast<LXVSTPlugin> (insert->plugin())) == 0) {
520 error << string_compose (_("unknown type of editor-supplying plugin (note: no linuxVST support in this version of %1)"), PROGRAM_NAME)
522 throw failed_constructor ();
524 LXVSTPluginUI* lxvpu = new LXVSTPluginUI (insert, lxvp);
527 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
529 lxvpu->package (*this);
537 #ifdef MACVST_SUPPORT
538 PluginUIWindow::create_mac_vst_editor (boost::shared_ptr<PluginInsert> insert)
540 PluginUIWindow::create_mac_vst_editor (boost::shared_ptr<PluginInsert>)
543 #ifndef MACVST_SUPPORT
546 boost::shared_ptr<MacVSTPlugin> mvst;
547 if ((mvst = boost::dynamic_pointer_cast<MacVSTPlugin> (insert->plugin())) == 0) {
548 error << string_compose (_("unknown type of editor-supplying plugin (note: no MacVST support in this version of %1)"), PROGRAM_NAME)
550 throw failed_constructor ();
552 VSTPluginUI* vpu = create_mac_vst_gui (insert);
554 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
556 vpu->package (*this);
558 Application::instance()->ActivationChanged.connect (mem_fun (*this, &PluginUIWindow::app_activated));
566 #ifdef AUDIOUNIT_SUPPORT
567 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert> insert)
569 PluginUIWindow::create_audiounit_editor (boost::shared_ptr<PluginInsert>)
572 #ifndef AUDIOUNIT_SUPPORT
576 _pluginui = create_au_gui (insert, &box);
577 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
580 Application::instance()->ActivationChanged.connect (mem_fun (*this, &PluginUIWindow::app_activated));
588 PluginUIWindow::app_activated (bool yn)
590 PluginUIWindow::app_activated (bool)
593 #ifdef AUDIOUNIT_SUPPORT
597 _pluginui->activate ();
598 if (pre_deactivate_x >= 0) {
599 move (pre_deactivate_x, pre_deactivate_y);
605 was_visible = is_visible();
606 get_position (pre_deactivate_x, pre_deactivate_y);
608 _pluginui->deactivate ();
615 PluginUIWindow::create_lv2_editor(boost::shared_ptr<PluginInsert> insert)
618 boost::shared_ptr<LV2Plugin> vp;
620 if ((vp = boost::dynamic_pointer_cast<LV2Plugin> (insert->plugin())) == 0) {
621 error << _("create_lv2_editor called on non-LV2 plugin") << endmsg;
622 throw failed_constructor ();
624 LV2PluginUI* lpu = new LV2PluginUI (insert, vp);
627 lpu->package (*this);
628 _pluginui->KeyboardFocused.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused));
638 PluginUIWindow::keyboard_focused (bool yn)
640 _keyboard_focused = yn;
644 PluginUIWindow::on_key_press_event (GdkEventKey* event)
646 if (_keyboard_focused) {
648 _pluginui->grab_focus();
649 if (_pluginui->non_gtk_gui()) {
650 _pluginui->forward_key_event (event);
652 return relay_key_press (event, this);
657 /* for us to be getting key press events, there really
658 MUST be a _pluginui, but just to be safe, check ...
662 _pluginui->grab_focus();
663 if (_pluginui->non_gtk_gui()) {
664 /* pass main window as the window for the event
665 to be handled in, not this one, because there are
666 no widgets in this window that we want to have
669 return relay_key_press (event, &ARDOUR_UI::instance()->main_window());
671 return relay_key_press (event, this);
679 PluginUIWindow::on_key_release_event (GdkEventKey *event)
681 if (_keyboard_focused) {
683 if (_pluginui->non_gtk_gui()) {
684 _pluginui->forward_key_event (event);
688 gtk_window_propagate_key_event (GTK_WINDOW(gobj()), event);
690 /* don't forward releases */
695 PluginUIWindow::plugin_going_away ()
697 ENSURE_GUI_THREAD (*this, &PluginUIWindow::plugin_going_away)
700 _pluginui->stop_updating(0);
703 death_connection.disconnect ();
706 PlugUIBase::PlugUIBase (boost::shared_ptr<PluginInsert> pi)
708 , plugin (insert->plugin())
709 , add_button (_("Add"))
710 , save_button (_("Save"))
711 , delete_button (_("Delete"))
712 , reset_button (_("Reset"))
713 , bypass_button (ArdourButton::led_default_elements)
714 , pin_management_button (_("Pinout"))
715 , description_expander (_("Description"))
716 , plugin_analysis_expander (_("Plugin analysis"))
717 , cpuload_expander (_("CPU Profile"))
723 _preset_modified.set_size_request (16, -1);
724 _preset_combo.set_text("(default)");
725 set_tooltip (_preset_combo, _("Presets (if any) for this plugin\n(Both factory and user-created)"));
726 set_tooltip (add_button, _("Save a new preset"));
727 set_tooltip (save_button, _("Save the current preset"));
728 set_tooltip (delete_button, _("Delete the current preset"));
729 set_tooltip (reset_button, _("Reset parameters to default (if no parameters are in automation play mode)"));
730 set_tooltip (pin_management_button, _("Show Plugin Pin Management Dialog"));
731 set_tooltip (bypass_button, _("Disable signal processing by the plugin"));
734 update_preset_list ();
737 add_button.set_name ("generic button");
738 add_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::add_plugin_setting));
740 save_button.set_name ("generic button");
741 save_button.signal_clicked.connect(sigc::mem_fun(*this, &PlugUIBase::save_plugin_setting));
743 delete_button.set_name ("generic button");
744 delete_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::delete_plugin_setting));
746 reset_button.set_name ("generic button");
747 reset_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::reset_plugin_parameters));
749 pin_management_button.set_name ("generic button");
750 pin_management_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::manage_pins));
752 insert->ActiveChanged.connect (active_connection, invalidator (*this), boost::bind (&PlugUIBase::processor_active_changed, this, boost::weak_ptr<Processor>(insert)), gui_context());
754 bypass_button.set_name ("plugin bypass button");
755 bypass_button.set_text (_("Bypass"));
756 bypass_button.set_active (!pi->enabled ());
757 bypass_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::bypass_button_release), false);
758 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
760 focus_button.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::focus_toggled));
761 focus_button.add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
763 /* these images are not managed, so that we can remove them at will */
765 focus_out_image = new Image (get_icon (X_("computer_keyboard")));
766 focus_in_image = new Image (get_icon (X_("computer_keyboard_active")));
768 focus_button.add (*focus_out_image);
770 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));
771 set_tooltip (bypass_button, _("Click to enable/disable this plugin"));
773 description_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_description));
774 description_expander.set_expanded(false);
776 plugin_analysis_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_plugin_analysis));
777 plugin_analysis_expander.set_expanded(false);
779 cpuload_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_cpuload_display));
780 cpuload_expander.set_expanded(false);
782 insert->DropReferences.connect (death_connection, invalidator (*this), boost::bind (&PlugUIBase::plugin_going_away, this), gui_context());
784 plugin->PresetAdded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
785 plugin->PresetRemoved.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed, this), gui_context ());
786 plugin->PresetLoaded.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset, this), gui_context ());
787 plugin->PresetDirty.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset_modified, this), gui_context ());
789 insert->AutomationStateChanged.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::automation_state_changed, this), gui_context());
791 automation_state_changed();
794 PlugUIBase::~PlugUIBase()
802 PlugUIBase::plugin_going_away ()
804 /* drop references to the plugin/insert */
810 PlugUIBase::set_latency_label ()
812 samplecnt_t const l = insert->effective_latency ();
813 samplecnt_t const sr = insert->session().sample_rate ();
818 t = string_compose (P_("latency (%1 sample)", "latency (%1 samples)", l), l);
820 t = string_compose (_("latency (%1 ms)"), (float) l / ((float) sr / 1000.0f));
823 latency_button.set_text (t);
827 PlugUIBase::latency_button_clicked ()
830 latency_gui = new LatencyGUI (*(insert.get()), insert->session().sample_rate(), insert->session().get_block_size());
831 latency_dialog = new ArdourWindow (_("Edit Latency"));
832 /* use both keep-above and transient for to try cover as many
833 different WM's as possible.
835 latency_dialog->set_keep_above (true);
836 Window* win = dynamic_cast<Window*> (bypass_button.get_toplevel ());
838 latency_dialog->set_transient_for (*win);
840 latency_dialog->add (*latency_gui);
841 latency_dialog->signal_hide().connect (sigc::mem_fun (*this, &PlugUIBase::set_latency_label));
844 latency_dialog->show_all ();
848 PlugUIBase::processor_active_changed (boost::weak_ptr<Processor> weak_p)
850 ENSURE_GUI_THREAD (*this, &PlugUIBase::processor_active_changed, weak_p);
851 boost::shared_ptr<Processor> p (weak_p.lock());
854 bypass_button.set_active (!p->enabled ());
859 PlugUIBase::preset_selected (Plugin::PresetRecord preset)
861 if (_no_load_preset) {
864 if (!preset.label.empty()) {
865 insert->load_preset (preset);
867 // blank selected = no preset
868 plugin->clear_preset();
872 #ifdef NO_PLUGIN_STATE
873 static bool seen_saving_message = false;
875 static void show_no_plugin_message()
877 info << string_compose (_("Plugin presets are not supported in this build of %1. Consider paying for a full version"),
880 info << _("To get full access to updates without this limitation\n"
881 "consider becoming a subscriber for a low cost every month.")
883 info << X_("https://community.ardour.org/s/subscribe")
885 ARDOUR_UI::instance()->popup_error(_("Plugin presets are not supported in this build, see the Log window for more information."));
890 PlugUIBase::add_plugin_setting ()
892 #ifndef NO_PLUGIN_STATE
893 NewPluginPresetDialog d (plugin, _("New Preset"));
896 case Gtk::RESPONSE_ACCEPT:
897 if (d.name().empty()) {
902 plugin->remove_preset (d.name ());
905 Plugin::PresetRecord const r = plugin->save_preset (d.name());
906 if (!r.uri.empty ()) {
907 plugin->load_preset (r);
912 if (!seen_saving_message) {
913 seen_saving_message = true;
914 show_no_plugin_message();
920 PlugUIBase::save_plugin_setting ()
922 #ifndef NO_PLUGIN_STATE
923 string const name = _preset_combo.get_text ();
924 plugin->remove_preset (name);
925 Plugin::PresetRecord const r = plugin->save_preset (name);
926 if (!r.uri.empty ()) {
927 plugin->load_preset (r);
930 if (!seen_saving_message) {
931 seen_saving_message = true;
932 show_no_plugin_message();
938 PlugUIBase::delete_plugin_setting ()
940 #ifndef NO_PLUGIN_STATE
941 plugin->remove_preset (_preset_combo.get_text ());
943 if (!seen_saving_message) {
944 seen_saving_message = true;
945 show_no_plugin_message();
951 PlugUIBase::automation_state_changed ()
953 reset_button.set_sensitive (insert->can_reset_all_parameters());
957 PlugUIBase::reset_plugin_parameters ()
959 insert->reset_parameters_to_default ();
963 PlugUIBase::manage_pins ()
965 PluginPinWindowProxy* proxy = insert->pinmgr_proxy ();
969 proxy->get ()->raise();
974 PlugUIBase::bypass_button_release (GdkEventButton*)
976 bool view_says_bypassed = (bypass_button.active_state() != 0);
978 if (view_says_bypassed != insert->enabled ()) {
979 insert->enable (view_says_bypassed);
986 PlugUIBase::focus_toggled (GdkEventButton*)
988 if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
989 Keyboard::the_keyboard().magic_widget_drop_focus();
990 focus_button.remove ();
991 focus_button.add (*focus_out_image);
992 focus_out_image->show ();
993 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));
994 KeyboardFocused (false);
996 Keyboard::the_keyboard().magic_widget_grab_focus();
997 focus_button.remove ();
998 focus_button.add (*focus_in_image);
999 focus_in_image->show ();
1000 set_tooltip (focus_button, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME));
1001 KeyboardFocused (true);
1008 PlugUIBase::toggle_description()
1010 if (description_expander.get_expanded() &&
1011 !description_expander.get_child()) {
1012 const std::string text = plugin->get_docs();
1017 Gtk::Label* label = manage(new Gtk::Label(text));
1018 label->set_line_wrap(true);
1019 label->set_line_wrap_mode(Pango::WRAP_WORD);
1020 description_expander.add(*label);
1021 description_expander.show_all();
1024 if (!description_expander.get_expanded()) {
1025 const int child_height = description_expander.get_child ()->get_height ();
1027 description_expander.remove();
1029 Gtk::Window *toplevel = (Gtk::Window*) description_expander.get_ancestor (GTK_TYPE_WINDOW);
1032 Gtk::Requisition wr;
1033 toplevel->get_size (wr.width, wr.height);
1034 wr.height -= child_height;
1035 toplevel->resize (wr.width, wr.height);
1041 PlugUIBase::toggle_plugin_analysis()
1043 if (plugin_analysis_expander.get_expanded() &&
1044 !plugin_analysis_expander.get_child()) {
1047 eqgui = new PluginEqGui (insert);
1050 plugin_analysis_expander.add (*eqgui);
1051 plugin_analysis_expander.show_all ();
1052 eqgui->start_listening ();
1055 if (!plugin_analysis_expander.get_expanded()) {
1056 // Hide & remove from expander
1057 const int child_height = plugin_analysis_expander.get_child ()->get_height ();
1060 eqgui->stop_listening ();
1061 plugin_analysis_expander.remove();
1063 Gtk::Window *toplevel = (Gtk::Window*) plugin_analysis_expander.get_ancestor (GTK_TYPE_WINDOW);
1066 Gtk::Requisition wr;
1067 toplevel->get_size (wr.width, wr.height);
1068 wr.height -= child_height;
1069 toplevel->resize (wr.width, wr.height);
1075 PlugUIBase::toggle_cpuload_display()
1077 if (cpuload_expander.get_expanded() && !cpuload_expander.get_child()) {
1078 if (stats_gui == 0) {
1079 stats_gui = new PluginLoadStatsGui (insert);
1081 cpuload_expander.add (*stats_gui);
1082 cpuload_expander.show_all();
1083 stats_gui->start_updating ();
1086 if (!cpuload_expander.get_expanded()) {
1087 const int child_height = cpuload_expander.get_child ()->get_height ();
1090 stats_gui->stop_updating ();
1091 cpuload_expander.remove();
1093 Gtk::Window *toplevel = (Gtk::Window*) cpuload_expander.get_ancestor (GTK_TYPE_WINDOW);
1096 Gtk::Requisition wr;
1097 toplevel->get_size (wr.width, wr.height);
1098 wr.height -= child_height;
1099 toplevel->resize (wr.width, wr.height);
1106 PlugUIBase::update_preset_list ()
1108 using namespace Menu_Helpers;
1110 vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets();
1114 // Add a menu entry for each preset
1115 _preset_combo.clear_items();
1116 for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator i = presets.begin(); i != presets.end(); ++i) {
1117 _preset_combo.AddMenuElem(
1118 MenuElem(i->label, sigc::bind(sigc::mem_fun(*this, &PlugUIBase::preset_selected), *i)));
1121 // Add an empty entry for un-setting current preset (see preset_selected)
1122 Plugin::PresetRecord no_preset;
1123 _preset_combo.AddMenuElem(
1124 MenuElem("", sigc::bind(sigc::mem_fun(*this, &PlugUIBase::preset_selected), no_preset)));
1130 PlugUIBase::update_preset ()
1132 Plugin::PresetRecord p = plugin->last_preset();
1135 if (p.uri.empty()) {
1136 _preset_combo.set_text (_("(none)"));
1138 _preset_combo.set_text (p.label);
1142 save_button.set_sensitive (!p.uri.empty() && p.user);
1143 delete_button.set_sensitive (!p.uri.empty() && p.user);
1145 update_preset_modified ();
1149 PlugUIBase::update_preset_modified ()
1152 if (plugin->last_preset().uri.empty()) {
1153 _preset_modified.set_text ("");
1157 bool const c = plugin->parameter_changed_since_last_preset ();
1158 if (_preset_modified.get_text().empty() == c) {
1159 _preset_modified.set_text (c ? "*" : "");
1164 PlugUIBase::preset_added_or_removed ()
1166 /* Update both the list and the currently-displayed preset */
1167 update_preset_list ();