Don't show DSP stats of inserts that don't collect them.
[ardour.git] / gtk2_ardour / generic_pluginui.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 #ifdef WAF_BUILD
21 #include "gtk2ardour-config.h"
22 #endif
23
24 #include <climits>
25 #include <cerrno>
26 #include <cmath>
27 #include <string>
28 #include <vector>
29
30 #include <gtkmm/separator.h>
31
32 #include "pbd/stl_delete.h"
33 #include "pbd/unwind.h"
34 #include "pbd/xml++.h"
35 #include "pbd/failed_constructor.h"
36
37 #include "evoral/midi_events.h"
38 #include "evoral/PatchChange.hpp"
39
40 #include "midi++/midnam_patch.h"
41
42 #include "ardour/midi_patch_manager.h"
43 #include "ardour/midi_track.h"
44 #include "ardour/plugin.h"
45 #include "ardour/plugin_insert.h"
46 #include "ardour/session.h"
47 #include "ardour/value_as_string.h"
48
49 #include "gtkmm2ext/menu_elems.h"
50 #include "gtkmm2ext/utils.h"
51 #include "gtkmm2ext/doi.h"
52
53 #include "widgets/ardour_knob.h"
54 #include "widgets/fastmeter.h"
55 #include "widgets/slider_controller.h"
56 #include "widgets/tooltips.h"
57
58 #include "plugin_ui.h"
59 #include "plugin_display.h"
60 #include "gui_thread.h"
61 #include "automation_controller.h"
62 #include "gain_meter.h"
63 #include "timers.h"
64 #include "ui_config.h"
65
66 #include "pbd/i18n.h"
67
68 using namespace std;
69 using namespace ARDOUR;
70 using namespace PBD;
71 using namespace Gtkmm2ext;
72 using namespace ArdourWidgets;
73 using namespace Gtk;
74 using namespace ARDOUR_UI_UTILS;
75
76 GenericPluginUI::GenericPluginUI (boost::shared_ptr<PluginInsert> pi, bool scrollable)
77         : PlugUIBase (pi)
78         , automation_menu (0)
79         , is_scrollable(scrollable)
80         , _plugin_pianokeyboard_expander (_("MIDI Keyboard"))
81         , _piano (0)
82         , _pianomm (0)
83         , _piano_velocity (*manage (new Adjustment (100, 1, 127, 1, 16)))
84         , _piano_channel (*manage (new Adjustment (0, 1, 16, 1, 1)))
85 {
86         set_name ("PluginEditor");
87         set_border_width (10);
88         //set_homogeneous (false);
89
90         pack_start (main_contents, true, true);
91         settings_box.set_homogeneous (false);
92
93         HBox* constraint_hbox = manage (new HBox);
94         HBox* smaller_hbox = manage (new HBox);
95         smaller_hbox->set_spacing (4);
96         Label* combo_label = manage (new Label (_("<span size=\"large\">Presets</span>")));
97         combo_label->set_use_markup (true);
98
99         latency_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::latency_button_clicked));
100         set_latency_label ();
101
102         smaller_hbox->pack_start (latency_button, false, false, 4);
103         smaller_hbox->pack_start (pin_management_button, false, false, 4);
104         smaller_hbox->pack_start (_preset_combo, false, false);
105         smaller_hbox->pack_start (_preset_modified, false, false);
106         smaller_hbox->pack_start (add_button, false, false);
107         smaller_hbox->pack_start (save_button, false, false);
108         smaller_hbox->pack_start (delete_button, false, false);
109         if (pi->controls().size() > 0) {
110                 smaller_hbox->pack_start (reset_button, false, false, 4);
111         }
112         smaller_hbox->pack_start (bypass_button, false, true, 4);
113
114         automation_manual_all_button.set_text(_("Manual"));
115         automation_manual_all_button.set_name (X_("generic button"));
116         automation_play_all_button.set_text(_("Play"));
117         automation_play_all_button.set_name (X_("generic button"));
118         automation_write_all_button.set_text(_("Write"));
119         automation_write_all_button.set_name (X_("generic button"));
120         automation_touch_all_button.set_text(_("Touch"));
121         automation_touch_all_button.set_name (X_("generic button"));
122         automation_latch_all_button.set_text(_("Touch"));
123         automation_latch_all_button.set_name (X_("generic button"));
124
125         constraint_hbox->set_spacing (5);
126         constraint_hbox->set_homogeneous (false);
127
128         VBox* v1_box = manage (new VBox);
129         VBox* v2_box = manage (new VBox);
130         if (pi->is_instrument ()) {
131                 _piano = (PianoKeyboard*)piano_keyboard_new();
132                 _pianomm = Glib::wrap((GtkWidget*)_piano);
133                 _pianomm->set_flags(Gtk::CAN_FOCUS);
134                 _pianomm->add_events(Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
135
136                 g_signal_connect (G_OBJECT (_piano), "note-on", G_CALLBACK (GenericPluginUI::_note_on_event_handler), this);
137                 g_signal_connect (G_OBJECT (_piano), "note-off", G_CALLBACK (GenericPluginUI::_note_off_event_handler), this);
138
139                 HBox* box = manage (new HBox);
140                 box->pack_start (*manage (new Label (_("Channel:"))), false, false);
141                 box->pack_start (_piano_channel, false, false);
142                 box->pack_start (*manage (new Label (_("Velocity:"))), false, false);
143                 box->pack_start (_piano_velocity, false, false);
144
145                 Box* box2 = manage (new HBox ());
146                 box2->pack_start (*box, true, false);
147
148                 _pianobox.set_spacing (4);
149                 _pianobox.pack_start (*box2, true, true);
150                 _pianobox.pack_start (*_pianomm, true, true);
151
152                 _plugin_pianokeyboard_expander.set_expanded(false);
153                 _plugin_pianokeyboard_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &GenericPluginUI::toggle_pianokeyboard));
154
155                 pack_end (_plugin_pianokeyboard_expander, false, false);
156         } else {
157                 pack_end (plugin_analysis_expander, false, false);
158         }
159
160         if (insert->provides_stats ()) {
161                 pack_end (cpuload_expander, false, false);
162         }
163
164         if (!plugin->get_docs().empty()) {
165                 pack_end (description_expander, false, false);
166         }
167
168         v1_box->set_spacing (6);
169         v1_box->pack_start (*smaller_hbox, false, true);
170         if (pi->controls().size() > 0) {
171                 HBox* automation_hbox = manage (new HBox);
172                 automation_hbox->set_spacing (6);
173                 Label* l = manage (new Label (_("All Automation")));
174                 l->set_alignment (1.0, 0.5);
175                 automation_hbox->pack_start (*l, true, true);
176                 automation_hbox->pack_start (automation_manual_all_button, false, false);
177                 automation_hbox->pack_start (automation_play_all_button, false, false);
178                 automation_hbox->pack_start (automation_write_all_button, false, false);
179                 automation_hbox->pack_start (automation_touch_all_button, false, false);
180                 v1_box->pack_start (*automation_hbox, false, true);
181         }
182         v2_box->pack_start (focus_button, false, true);
183
184         main_contents.pack_start (settings_box, false, false);
185
186         constraint_hbox->pack_end (*v2_box, false, false);
187         constraint_hbox->pack_end (*v1_box, false, false);
188
189         main_contents.pack_start (*constraint_hbox, false, false);
190
191         pi->ActiveChanged.connect (active_connection, invalidator (*this), boost::bind (&GenericPluginUI::processor_active_changed, this, boost::weak_ptr<Processor>(pi)), gui_context());
192
193         bypass_button.set_active (!pi->enabled());
194
195         /* ScrolledWindow will wrap hpacker in a Viewport */
196         scroller.add (hpacker);
197         Viewport* view = static_cast<Viewport*>(scroller.get_child());
198         view->set_shadow_type(Gtk::SHADOW_NONE);
199
200         main_contents.pack_start (scroller, true, true);
201
202         prefheight = 0;
203         build ();
204
205         if (insert->plugin()->has_midnam() && insert->plugin()->knows_bank_patch()) {
206                 build_midi_table ();
207         }
208
209         if (is_scrollable) {
210                 scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
211                 scroller.set_name ("PluginEditor");
212         } else {
213                 scroller.signal_size_request().connect (sigc::mem_fun(*this, &GenericPluginUI::scroller_size_request));
214                 scroller.signal_realize().connect (sigc::mem_fun(scroller, &Widget::queue_resize));
215                 scroller.set_policy (Gtk::POLICY_AUTOMATIC, Gtk::POLICY_NEVER);
216         }
217
218         main_contents.show ();
219 }
220
221 GenericPluginUI::~GenericPluginUI ()
222 {
223         if (output_controls.size() > 0) {
224                 screen_update_connection.disconnect();
225         }
226         delete _pianomm;
227 }
228
229 void
230 GenericPluginUI::scroller_size_request (Gtk::Requisition* a)
231 {
232         GtkRequisition request = hpacker.size_request();
233
234         Glib::RefPtr<Gdk::Window> window (scroller.get_window());
235         Glib::RefPtr<Gdk::Screen> screen;
236
237         if (window) {
238                 screen = window->get_screen();
239         }
240
241         if (!screen) {
242                 a->width = min(1024, request.width);
243                 return;
244         }
245
246         Gdk::Rectangle monitor;
247         const int monitor_num = screen->get_monitor_at_window (window);
248         screen->get_monitor_geometry (
249                         (monitor_num < 0) ? 0 : monitor_num,
250                         monitor);
251
252         const int maximum_width = monitor.get_width() * 0.9;
253
254         if (request.width > maximum_width) {
255                 for (vector<ControlUI*>::const_iterator cuip = input_controls.begin();
256                                                         cuip != input_controls.end();
257                                                         ++cuip) {
258                         if (!(*cuip)->short_autostate)
259                                 set_short_autostate(*cuip, true);
260                 }
261                 request = hpacker.size_request();
262         }
263
264         a->width = min(request.width, maximum_width);
265 }
266
267 // Some functions for calculating the 'similarity' of two plugin
268 // control labels.
269
270 static int get_number(string label) {
271 static const char *digits = "0123456789";
272 int value = -1;
273
274         std::size_t first_digit_pos = label.find_first_of(digits);
275         if (first_digit_pos != string::npos) {
276                 // found some digits: there's a number in there somewhere
277                 stringstream s;
278                 s << label.substr(first_digit_pos);
279                 s >> value;
280         }
281         return value;
282 }
283
284 static int match_or_digit(char c1, char c2) {
285         return c1 == c2 || (isdigit(c1) && isdigit(c2));
286 }
287
288 static std::size_t matching_chars_at_head(const string s1, const string s2) {
289 std::size_t length, n = 0;
290
291         length = min(s1.length(), s2.length());
292         while (n < length) {
293                 if (!match_or_digit(s1[n], s2[n]))
294                         break;
295                 n++;
296         }
297         return n;
298 }
299
300 static std::size_t matching_chars_at_tail(const string s1, const string s2) {
301 std::size_t s1pos, s2pos, n = 0;
302
303         s1pos = s1.length();
304         s2pos = s2.length();
305         while (s1pos-- > 0 && s2pos-- > 0) {
306                 if (!match_or_digit(s1[s1pos], s2[s2pos])       )
307                         break;
308                 n++;
309         }
310         return n;
311 }
312
313 static const guint32 min_controls_per_column = 17, max_controls_per_column = 24;
314 static const float default_similarity_threshold = 0.3;
315
316 void
317 GenericPluginUI::build ()
318 {
319         std::vector<ControlUI *> control_uis;
320         bool grid = plugin->parameter_count() > 0;
321
322         // Build a ControlUI for each control port
323         for (size_t i = 0; i < plugin->parameter_count(); ++i) {
324
325                 if (plugin->parameter_is_control (i)) {
326
327                         /* Don't show latency control ports */
328
329                         const Evoral::Parameter param(PluginAutomation, 0, i);
330                         if (plugin->describe_parameter (param) == X_("latency")) {
331                                 continue;
332                         }
333
334                         if (plugin->describe_parameter (param) == X_("hidden")) {
335                                 continue;
336                         }
337
338                         const float value = plugin->get_parameter(i);
339
340                         ControlUI* cui;
341                         Plugin::UILayoutHint hint;
342
343                         if (!plugin->get_layout(i, hint)) {
344                                 grid = false;
345                         }
346
347                         boost::shared_ptr<ARDOUR::AutomationControl> c
348                                 = boost::dynamic_pointer_cast<ARDOUR::AutomationControl>(
349                                         insert->control(param));
350
351                         ParameterDescriptor desc;
352                         plugin->get_parameter_descriptor(i, desc);
353                         if ((cui = build_control_ui (param, desc, c, value, plugin->parameter_is_input(i), hint.knob)) == 0) {
354                                 error << string_compose(_("Plugin Editor: could not build control element for port %1"), i) << endmsg;
355                                 continue;
356                         }
357
358                         if (grid) {
359                                 cui->x0 = hint.x0;
360                                 cui->x1 = hint.x1;
361                                 cui->y0 = hint.y0;
362                                 cui->y1 = hint.y1;
363                         }
364
365                         const std::string param_docs = plugin->get_parameter_docs(i);
366                         if (!param_docs.empty()) {
367                                 set_tooltip(cui, param_docs.c_str());
368                         }
369
370                         control_uis.push_back(cui);
371                 }
372         }
373
374         // Build a ControlUI for each property
375         const Plugin::PropertyDescriptors& descs = plugin->get_supported_properties();
376         for (Plugin::PropertyDescriptors::const_iterator d = descs.begin(); d != descs.end(); ++d) {
377                 const ParameterDescriptor& desc = d->second;
378                 const Evoral::Parameter    param(PluginPropertyAutomation, 0, desc.key);
379
380                 boost::shared_ptr<ARDOUR::AutomationControl> c
381                         = boost::dynamic_pointer_cast<ARDOUR::AutomationControl>(
382                                 insert->control(param));
383
384                 if (!c) {
385                         error << string_compose(_("Plugin Editor: no control for property %1"), desc.key) << endmsg;
386                         continue;
387                 }
388
389                 ControlUI* cui = build_control_ui(param, desc, c, c->get_value(), true);
390                 if (!cui) {
391                         error << string_compose(_("Plugin Editor: could not build control element for property %1"),
392                                                 desc.key) << endmsg;
393                         continue;
394                 }
395
396                 control_uis.push_back(cui);
397         }
398         if (!descs.empty()) {
399                 /* Listen for property changes that are not notified normally because
400                  * AutomationControl has only support for numeric values currently.
401                  * The only case is Variant::PATH for now */
402                 plugin->PropertyChanged.connect(*this, invalidator(*this),
403                                 boost::bind(&GenericPluginUI::path_property_changed, this, _1, _2),
404                                 gui_context());
405
406                 /* and query current property value */
407                 plugin->announce_property_values();
408         }
409
410         if (grid) {
411                 custom_layout (control_uis);
412         } else {
413                 automatic_layout (control_uis);
414         }
415
416         output_update ();
417
418         automation_manual_all_button.signal_clicked.connect(sigc::bind (sigc::mem_fun (*this, &GenericPluginUI::set_all_automation), ARDOUR::Off));
419         automation_play_all_button.signal_clicked.connect(sigc::bind (sigc::mem_fun (*this, &GenericPluginUI::set_all_automation), ARDOUR::Play));
420         automation_write_all_button.signal_clicked.connect(sigc::bind (sigc::mem_fun (*this, &GenericPluginUI::set_all_automation), ARDOUR::Write));
421         automation_touch_all_button.signal_clicked.connect(sigc::bind (sigc::mem_fun (*this, &GenericPluginUI::set_all_automation), ARDOUR::Touch));
422         automation_latch_all_button.signal_clicked.connect(sigc::bind (sigc::mem_fun (*this, &GenericPluginUI::set_all_automation), ARDOUR::Latch));
423
424         /* XXX This is a workaround for AutomationControl not knowing about preset loads */
425         plugin->PresetLoaded.connect (*this, invalidator (*this), boost::bind (&GenericPluginUI::update_input_displays, this), gui_context ());
426 }
427
428
429 void
430 GenericPluginUI::automatic_layout (const std::vector<ControlUI*>& control_uis)
431 {
432         guint32 x = 0;
433
434         static const int32_t initial_button_rows = 12;
435         static const int32_t initial_button_cols = 1;
436         static const int32_t initial_output_rows = 1;
437         static const int32_t initial_output_cols = 4;
438
439         Gtk::Table* button_table = manage (new Gtk::Table (initial_button_rows, initial_button_cols));
440         Gtk::Table* output_table = manage (new Gtk::Table (initial_output_rows, initial_output_cols));
441
442         Frame* frame;
443         Frame* bt_frame;
444         VBox* box;
445         int output_row, output_col;
446         int button_row, button_col;
447         int output_rows, output_cols;
448         int button_rows, button_cols;
449
450         hpacker.set_spacing (10);
451         hpacker.set_border_width (10);
452
453         output_rows = initial_output_rows;
454         output_cols = initial_output_cols;
455         button_rows = initial_button_rows;
456         button_cols = initial_button_cols;
457         output_row = 0;
458         button_row = 0;
459         output_col = 0;
460         button_col = 0;
461
462         button_table->set_homogeneous (false);
463         button_table->set_row_spacings (2);
464         button_table->set_col_spacings (2);
465         button_table->set_border_width (5);
466
467         output_table->set_homogeneous (true);
468         output_table->set_row_spacings (2);
469         output_table->set_col_spacings (2);
470         output_table->set_border_width (5);
471
472
473         bt_frame = manage (new Frame);
474         bt_frame->set_name ("BaseFrame");
475         bt_frame->set_label (_("Switches"));
476         bt_frame->add (*button_table);
477         hpacker.pack_start(*bt_frame, true, true);
478
479         box = manage (new VBox);
480         box->set_border_width (5);
481         box->set_spacing (1);
482
483         frame = manage (new Frame);
484         frame->set_name ("BaseFrame");
485         frame->set_label (_("Controls"));
486         frame->add (*box);
487         hpacker.pack_start(*frame, true, true);
488
489         // Add special controls to UI, and build list of normal controls to be layed out later
490         std::vector<ControlUI *> cui_controls_list;
491         for (size_t i = 0; i < control_uis.size(); ++i) {
492                 ControlUI* cui = control_uis[i];
493
494                 if (cui->button || cui->file_button) {
495
496                         if (!is_scrollable && button_row == button_rows) {
497                                 button_row = 0;
498                                 if (++button_col == button_cols) {
499                                         button_cols += 2;
500                                         button_table->resize (button_rows, button_cols);
501                                 }
502                         }
503
504                         button_table->attach (*cui, button_col, button_col + 1, button_row, button_row+1,
505                                              FILL|EXPAND, FILL);
506                         button_row++;
507
508                 } else if (cui->controller || cui->combo) {
509                         // Get all of the controls into a list, so that
510                         // we can lay them out a bit more nicely later.
511                         cui_controls_list.push_back(cui);
512
513                 } else if (cui->display) {
514
515                         output_table->attach (*cui, output_col, output_col + 1, output_row, output_row+1,
516                                              FILL|EXPAND, FILL);
517
518                         // TODO: The meters should be divided into multiple rows
519
520                         if (++output_col == output_cols) {
521                                 output_cols ++;
522                                 output_table->resize (output_rows, output_cols);
523                         }
524                 }
525         }
526
527         // Iterate over the list of controls to find which adjacent controls
528         // are similar enough to be grouped together.
529
530         string label, previous_label = "";
531         std::vector<int> numbers_in_labels(cui_controls_list.size());
532
533         std::vector<float> similarity_scores(cui_controls_list.size());
534         float most_similar = 0.0, least_similar = 1.0;
535
536         size_t i = 0;
537         for (vector<ControlUI*>::iterator cuip = cui_controls_list.begin(); cuip != cui_controls_list.end(); ++cuip, ++i) {
538                 label = (*cuip)->label.get_text();
539                 numbers_in_labels[i] = get_number(label);
540
541                 if (i > 0) {
542                         // A hand-wavy calculation of how similar this control's
543                         // label is to the previous.
544                         similarity_scores[i] =
545                                 (float) (
546                                         ( matching_chars_at_head(label, previous_label) +
547                                           matching_chars_at_tail(label, previous_label) +
548                                           1
549                                         )
550                                 ) / (label.length() + previous_label.length());
551                         if (numbers_in_labels[i] >= 0) {
552                                 similarity_scores[i] += (numbers_in_labels[i] == numbers_in_labels[i-1]);
553                         }
554                         least_similar = min(least_similar, similarity_scores[i]);
555                         most_similar  = max(most_similar, similarity_scores[i]);
556                 } else {
557                         similarity_scores[0] = 1.0;
558                 }
559
560                 // cerr << "label: " << label << " sim: " << fixed << setprecision(3) << similarity_scores[i] << " num: " << numbers_in_labels[i] << endl;
561                 previous_label = label;
562         }
563
564
565         // cerr << "most similar: " << most_similar << ", least similar: " << least_similar << endl;
566         float similarity_threshold;
567
568         if (most_similar > 1.0) {
569                 similarity_threshold = default_similarity_threshold;
570         } else {
571                 similarity_threshold = most_similar - (1 - default_similarity_threshold);
572         }
573
574         // Now iterate over the list of controls to display them, placing an
575         // HSeparator between controls of less than a certain similarity, and
576         // starting a new column when necessary.
577
578         i = 0;
579         for (vector<ControlUI*>::iterator cuip = cui_controls_list.begin(); cuip != cui_controls_list.end(); ++cuip, ++i) {
580
581                 ControlUI* cui = *cuip;
582
583                 if (!is_scrollable) {
584                         x++;
585                 }
586
587                 if (x > max_controls_per_column || similarity_scores[i] <= similarity_threshold) {
588                         if (x > min_controls_per_column) {
589                                 frame = manage (new Frame);
590                                 frame->set_name ("BaseFrame");
591                                 frame->set_label (_("Controls"));
592                                 box = manage (new VBox);
593                                 box->set_border_width (5);
594                                 box->set_spacing (1);
595                                 frame->add (*box);
596                                 hpacker.pack_start(*frame, true, true);
597                                 x = 0;
598                         } else {
599                                 HSeparator *split = new HSeparator();
600                                 split->set_size_request(-1, 5);
601                                 box->pack_start(*split, false, false, 0);
602                         }
603
604                 }
605                 box->pack_start (*cui, false, false);
606         }
607
608         if (is_scrollable) {
609                 prefheight = 30 * i;
610         }
611
612         if (box->children().empty()) {
613                 hpacker.remove (*frame);
614         }
615
616         if (button_table->children().empty()) {
617                 hpacker.remove (*bt_frame);
618                 delete button_table;
619         } else {
620                 button_table->show_all ();
621         }
622
623         if (!output_table->children().empty()) {
624                 frame = manage (new Frame);
625                 frame->set_name ("BaseFrame");
626                 frame->set_label(_("Meters"));
627                 frame->add (*output_table);
628                 hpacker.pack_end (*frame, true, true);
629                 output_table->show_all ();
630         } else {
631                 delete output_table;
632         }
633
634         if (plugin->has_inline_display () && plugin->inline_display_in_gui ()) {
635                 PluginDisplay* pd = manage (new PluginDisplay (plugin, 300));
636                 hpacker.pack_end (*pd, true, true);
637         }
638         show_all();
639
640 }
641
642 void
643 GenericPluginUI::custom_layout (const std::vector<ControlUI*>& control_uis)
644 {
645         Gtk::Table* layout = manage (new Gtk::Table ());
646
647         for (vector<ControlUI*>::const_iterator i = control_uis.begin(); i != control_uis.end(); ++i) {
648                 ControlUI* cui = *i;
649                 if (cui->x0 < 0 || cui->y0 < 0) {
650                         continue;
651                 }
652                 layout->attach (*cui, cui->x0, cui->x1, cui->y0, cui->y1, FILL, SHRINK, 2, 2);
653         }
654         hpacker.pack_start (*layout, true, true);
655
656         if (plugin->has_inline_display () && plugin->inline_display_in_gui ()) {
657                 PluginDisplay* pd = manage (new PluginDisplay (plugin, 300));
658                 hpacker.pack_end (*pd, true, true);
659         }
660 }
661
662 void
663 GenericPluginUI::build_midi_table ()
664 {
665         Gtk::Table* pgm_table = manage (new Gtk::Table (8, 5));
666
667         pgm_table->set_homogeneous (false);
668         pgm_table->set_row_spacings (2);
669         pgm_table->set_col_spacings (2);
670         pgm_table->set_border_width (5);
671         pgm_table->set_col_spacing (2, 10);
672
673         Frame* frame = manage (new Frame);
674         frame->set_name ("BaseFrame");
675         if (dynamic_cast<MidiTrack*> (insert->owner())) {
676                 frame->set_label (_("MIDI Programs (sent to track)"));
677         } else {
678                 frame->set_label (_("MIDI Programs (volatile)"));
679         }
680         frame->add (*pgm_table);
681         hpacker.pack_start (*frame, false, false);
682
683         for (uint8_t chn = 0; chn < 16; ++chn) {
684                 int col = 3 * (chn / 8);
685                 int row = chn % 8;
686                 ArdourDropdown* cui = manage (new ArdourWidgets::ArdourDropdown ());
687                 cui->set_sizing_text ("Stereo Grand Piano");
688                 cui->set_text_ellipsize (Pango::ELLIPSIZE_END);
689                 cui->set_layout_ellipsize_width (PANGO_SCALE * 112 * UIConfiguration::instance ().get_ui_scale ());
690                 midi_pgmsel.push_back (cui);
691                 pgm_table->attach (*manage (new Label (string_compose ("C%1:", (int)(chn + 1)), ALIGN_RIGHT)), col, col + 1, row, row+1, FILL, SHRINK);
692                 pgm_table->attach (*cui, col + 1, col + 2, row, row+1, SHRINK, SHRINK);
693         }
694
695         insert->plugin ()->read_midnam();
696
697         midi_refill_patches ();
698
699         insert->plugin()->BankPatchChange.connect (
700                         midi_connections, invalidator (*this),
701                         boost::bind (&GenericPluginUI::midi_bank_patch_change, this, _1),
702                         gui_context());
703
704         insert->plugin()->UpdatedMidnam.connect (
705                         midi_connections, invalidator (*this),
706                         boost::bind (&GenericPluginUI::midi_refill_patches, this),
707                         gui_context());
708 }
709
710 void
711 GenericPluginUI::midi_refill_patches ()
712 {
713         assert (midi_pgmsel.size() == 16);
714
715         pgm_names.clear ();
716
717         const std::string model = insert->plugin ()->midnam_model ();
718         std::string mode;
719         const std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance().custom_device_mode_names_by_model (model);
720         if (device_modes.size() > 0) {
721                 mode = device_modes.front();
722         }
723
724         for (uint8_t chn = 0; chn < 16; ++chn) {
725                 midi_pgmsel[chn]->clear_items ();
726                 boost::shared_ptr<MIDI::Name::ChannelNameSet> cns =
727                         MIDI::Name::MidiPatchManager::instance().find_channel_name_set (model, mode, chn);
728
729                 if (cns) {
730                         using namespace Menu_Helpers;
731                         using namespace Gtkmm2ext;
732
733                         for (MIDI::Name::ChannelNameSet::PatchBanks::const_iterator i = cns->patch_banks().begin(); i != cns->patch_banks().end(); ++i) {
734                                 const MIDI::Name::PatchNameList& patches = (*i)->patch_name_list ();
735                                 for (MIDI::Name::PatchNameList::const_iterator j = patches.begin(); j != patches.end(); ++j) {
736                                         const std::string pgm = (*j)->name ();
737                                         MIDI::Name::PatchPrimaryKey const& key = (*j)->patch_primary_key ();
738                                         const uint32_t bp = (key.bank() << 7) | key.program();
739                                         midi_pgmsel[chn]->AddMenuElem (MenuElemNoMnemonic (pgm, sigc::bind (sigc::mem_fun (*this, &GenericPluginUI::midi_bank_patch_select), chn, bp)));
740                                         pgm_names[bp] = pgm;
741                                 }
742                         }
743                 }
744
745                 midi_bank_patch_change (chn);
746         }
747 }
748
749 void
750 GenericPluginUI::midi_bank_patch_change (uint8_t chn)
751 {
752         assert (chn < 16 && midi_pgmsel.size() == 16);
753         uint32_t bankpgm = insert->plugin()->bank_patch (chn);
754         if (bankpgm == UINT32_MAX) {
755                 midi_pgmsel[chn]->set_text (_("--Unset--"));
756         } else {
757                 int bank = bankpgm >> 7;
758                 int pgm = bankpgm & 127;
759                 if (pgm_names.find (bankpgm) != pgm_names.end ()) {
760                         midi_pgmsel[chn]->set_text (pgm_names[bankpgm]);
761                 } else {
762                         midi_pgmsel[chn]->set_text (string_compose ("Bank %1,%2 Pgm %3",
763                                                 (bank >> 7) + 1, (bank & 127) + 1, pgm +1));
764                 }
765         }
766 }
767
768 void
769 GenericPluginUI::midi_bank_patch_select (uint8_t chn, uint32_t bankpgm)
770 {
771         int bank = bankpgm >> 7;
772         int pgm = bankpgm & 127;
773         MidiTrack* mt = dynamic_cast<MidiTrack*> (insert->owner());
774         if (mt) {
775                 /* send to track */
776                 boost::shared_ptr<AutomationControl> bank_msb = mt->automation_control(Evoral::Parameter (MidiCCAutomation, chn, MIDI_CTL_MSB_BANK), true);
777                 boost::shared_ptr<AutomationControl> bank_lsb = mt->automation_control(Evoral::Parameter (MidiCCAutomation, chn, MIDI_CTL_LSB_BANK), true);
778                 boost::shared_ptr<AutomationControl> program = mt->automation_control(Evoral::Parameter (MidiPgmChangeAutomation, chn), true);
779
780                 bank_msb->set_value (bank >> 7, PBD::Controllable::NoGroup);
781                 bank_lsb->set_value (bank & 127, PBD::Controllable::NoGroup);
782                 program->set_value (pgm, PBD::Controllable::NoGroup);
783         } else {
784                 uint8_t event[3];
785                 event[0] = (MIDI_CMD_CONTROL | chn);
786                 event[1] = 0x00;
787                 event[2] = bank >> 7;
788                 insert->write_immediate_event (3, event);
789
790                 event[1] = 0x20;
791                 event[2] = bank & 127;
792                 insert->write_immediate_event (3, event);
793
794                 event[0] = (MIDI_CMD_PGM_CHANGE | chn);
795                 event[1] = pgm;
796                 insert->write_immediate_event (2, event);
797         }
798 }
799
800 GenericPluginUI::ControlUI::ControlUI (const Evoral::Parameter& p)
801         : param(p)
802         , automate_button (X_("")) // force creation of a label
803         , combo (0)
804         , file_button (0)
805         , spin_box (0)
806         , display (0)
807         , hbox (0)
808         , vbox (0)
809         , meterinfo (0)
810         , knobtable (0)
811 {
812         automate_button.set_name ("plugin automation state button");
813         set_tooltip (automate_button, _("Automation control"));
814
815         ignore_change = false;
816         update_pending = false;
817         button = false;
818
819         x0 = x1 = y0 = y1 = -1;
820 }
821
822 GenericPluginUI::ControlUI::~ControlUI()
823 {
824         if (meterinfo) {
825                 delete meterinfo->meter;
826                 delete meterinfo;
827         }
828 }
829
830 void
831 GenericPluginUI::set_short_autostate (ControlUI* cui, bool value)
832 {
833         cui->short_autostate = value;
834         if (value) {
835                 cui->automate_button.set_sizing_text("M");
836         } else {
837                 /* XXX translators: use a string here that will be at least as long
838                    as the longest automation label (see ::automation_state_changed()
839                    below). be sure to include a descender. */
840                 cui->automate_button.set_sizing_text(_("Mgnual"));
841         }
842         automation_state_changed(cui);
843 }
844
845 void
846 GenericPluginUI::automation_state_changed (ControlUI* cui)
847 {
848         /* update button label */
849
850         // don't lock to avoid deadlock because we're triggered by
851         // AutomationControl::Changed() while the automation lock is taken
852
853         AutoState state = insert->get_parameter_automation_state (cui->parameter());
854
855         cui->automate_button.set_active((state != ARDOUR::Off));
856
857         if (cui->short_autostate) {
858                 cui->automate_button.set_text (
859                                 GainMeterBase::astate_string (state));
860                 return;
861         }
862
863         switch (state & (ARDOUR::Off|Play|Touch|Write|Latch)) {
864         case ARDOUR::Off:
865                 cui->automate_button.set_text (S_("Automation|Manual"));
866                 break;
867         case Play:
868                 cui->automate_button.set_text (_("Play"));
869                 break;
870         case Write:
871                 cui->automate_button.set_text (_("Write"));
872                 break;
873         case Touch:
874                 cui->automate_button.set_text (_("Touch"));
875                 break;
876         case Latch:
877                 cui->automate_button.set_text (_("Latch"));
878                 break;
879         default:
880                 cui->automate_button.set_text (_("???"));
881                 break;
882         }
883 }
884
885 bool
886 GenericPluginUI::integer_printer (char buf[32], Adjustment &adj, ControlUI* cui)
887 {
888         float const        v   = cui->control->interface_to_internal(adj.get_value ());
889         const std::string& str = ARDOUR::value_as_string(cui->control->desc(), Variant(v));
890         const size_t       len = str.copy(buf, 31);
891         buf[len] = '\0';
892         return true;
893 }
894
895 bool
896 GenericPluginUI::midinote_printer (char buf[32], Adjustment &adj, ControlUI* cui)
897 {
898         float const        v   = cui->control->interface_to_internal(adj.get_value ());
899         const std::string& str = ARDOUR::value_as_string(cui->control->desc(), Variant(v));
900         const size_t       len = str.copy(buf, 31);
901         buf[len] = '\0';
902         return true;
903 }
904
905 void
906 GenericPluginUI::print_parameter (char *buf, uint32_t len, uint32_t param)
907 {
908         plugin->print_parameter (param, buf, len);
909 }
910
911 /** Build a ControlUI for a parameter/property.
912  * Note that mcontrol may be NULL for outputs.
913  */
914 GenericPluginUI::ControlUI*
915 GenericPluginUI::build_control_ui (const Evoral::Parameter&             param,
916                                    const ParameterDescriptor&           desc,
917                                    boost::shared_ptr<AutomationControl> mcontrol,
918                                    float                                value,
919                                    bool                                 is_input,
920                                    bool                                 use_knob)
921 {
922         ControlUI* control_ui = 0;
923
924         control_ui = manage (new ControlUI (param));
925         control_ui->combo = 0;
926         control_ui->control = mcontrol;
927         control_ui->label.set_text (desc.label);
928         control_ui->label.set_alignment (0.0, 0.5);
929         control_ui->label.set_name ("PluginParameterLabel");
930         control_ui->set_spacing (5);
931         set_short_autostate(control_ui, false);
932
933         if (is_input) {
934
935                 if (desc.datatype == Variant::PATH) {
936
937                         /* We shouldn't get that type for input ports */
938                         assert(param.type() == PluginPropertyAutomation);
939
940                         /* Build a file selector button */
941
942                         // Create/add controller
943                         control_ui->file_button = manage(new Gtk::FileChooserButton(Gtk::FILE_CHOOSER_ACTION_OPEN));
944                         control_ui->file_button->set_title(desc.label);
945
946                         if (use_knob) {
947                                 control_ui->knobtable = manage (new Table());
948                                 control_ui->pack_start(*control_ui->knobtable, true, false);
949                                 control_ui->knobtable->attach (control_ui->label, 0, 1, 0, 1);
950                                 control_ui->knobtable->attach (*control_ui->file_button, 0, 1, 1, 2);
951                         } else {
952                                 control_ui->pack_start (control_ui->label, false, true);
953                                 control_ui->pack_start (*control_ui->file_button, true, true);
954                         }
955
956                         // Monitor changes from the user.
957                         control_ui->file_button->signal_file_set().connect(
958                                 sigc::bind(sigc::mem_fun(*this, &GenericPluginUI::set_path_property),
959                                            desc, control_ui->file_button));
960
961                         /* Add the filebutton control to a map so that we can update it when
962                          * the corresponding property changes. This doesn't go through the usual
963                          * AutomationControls, because they don't support non-numeric values. */
964                         _filepath_controls.insert(std::make_pair(desc.key, control_ui->file_button));
965
966                         return control_ui;
967                 }
968
969                 assert(mcontrol);
970
971                 /* See if there any named values for our input value */
972                 control_ui->scale_points = desc.scale_points;
973
974                 /* If this parameter is an integer, work out the number of distinct values
975                    it can take on (assuming that lower and upper values are allowed).
976                 */
977                 int const steps = desc.integer_step ? (desc.upper - desc.lower + 1) / desc.step : 0;
978
979                 if (control_ui->scale_points && ((steps && int (control_ui->scale_points->size()) == steps) || desc.enumeration)) {
980
981                         /* Either:
982                          *   a) There is a label for each possible value of this input, or
983                          *   b) This port is marked as being an enumeration.
984                          */
985
986                         control_ui->combo = new ArdourDropdown();
987                         for (ARDOUR::ScalePoints::const_iterator i = control_ui->scale_points->begin();
988                              i != control_ui->scale_points->end();
989                              ++i) {
990                                 control_ui->combo->AddMenuElem(Menu_Helpers::MenuElem(
991                                                 i->first,
992                                                 sigc::bind(sigc::mem_fun(*this, &GenericPluginUI::control_combo_changed),
993                                                            control_ui,
994                                                            i->second)));
995                         }
996
997                         control_ui->combo->set_controllable (mcontrol);
998
999                         update_control_display(control_ui);
1000
1001                 } else {
1002
1003                         /* create the controller */
1004
1005                         /* XXX memory leak: SliderController not destroyed by ControlUI
1006                          * destructor, and manage() reports object hierarchy
1007                          * ambiguity.
1008                          */
1009                         control_ui->controller = AutomationController::create(mcontrol->parameter(), desc, mcontrol, use_knob);
1010
1011                         /* Control UI's don't need the rapid timer workaround */
1012                         control_ui->controller->stop_updating ();
1013
1014                         /* XXX this code is not right yet, because it doesn't handle
1015                            the absence of bounds in any sensible fashion.
1016                         */
1017
1018                         Adjustment* adj = control_ui->controller->adjustment();
1019
1020                         if (desc.toggled) {
1021                                 ArdourButton* but = dynamic_cast<ArdourButton*> (control_ui->controller->widget());
1022                                 assert(but);
1023                                 but->set_tweaks(ArdourButton::Square);
1024                         } else if (use_knob) {
1025                                 /* Delay size request so that styles are gotten right */
1026                                 control_ui->controller->widget()->signal_size_request().connect(
1027                                                 sigc::bind (sigc::mem_fun (*this, &GenericPluginUI::knob_size_request), control_ui));
1028                         } else {
1029                                 control_ui->controller->set_size_request (200, -1);
1030                                 control_ui->controller->set_name (X_("ProcessorControlSlider"));
1031                                 if (desc.integer_step) {
1032                                         AutomationBarController* abc = dynamic_cast <AutomationBarController*> (control_ui->controller->widget ());
1033                                         assert (abc);
1034                                         abc->set_digits (0);
1035                                 }
1036                         }
1037
1038                         if (!desc.integer_step && !desc.toggled && use_knob) {
1039                                 control_ui->spin_box = manage (new ArdourSpinner (mcontrol, adj));
1040                         }
1041
1042                         adj->set_value (mcontrol->internal_to_interface(value));
1043
1044                 }
1045
1046                 if (use_knob) {
1047                         set_short_autostate(control_ui, true);
1048
1049                         control_ui->label.set_alignment (0.5, 0.5);
1050                         control_ui->knobtable = manage (new Table());
1051                         control_ui->pack_start(*control_ui->knobtable, true, true);
1052
1053                         if (control_ui->combo) {
1054                                 control_ui->knobtable->attach (control_ui->label, 0, 1, 0, 1);
1055                                 control_ui->knobtable->attach (*control_ui->combo, 0, 1, 1, 2);
1056                         } else if (control_ui->spin_box) {
1057                                 ArdourKnob* knob = dynamic_cast<ArdourKnob*>(control_ui->controller->widget ());
1058                                 knob->set_tooltip_prefix (desc.label + ": ");
1059                                 Alignment *align = manage (new Alignment (.5, .5, 0, 0));
1060                                 align->add (*control_ui->controller);
1061                                 control_ui->knobtable->attach (*align, 0, 1, 0, 1, EXPAND, SHRINK, 1, 2);
1062                                 control_ui->knobtable->attach (*control_ui->spin_box, 0, 2, 1, 2);
1063                                 control_ui->knobtable->attach (control_ui->automate_button, 1, 2, 0, 1, SHRINK, SHRINK, 2, 0);
1064                         } else if (desc.toggled) {
1065                                 Alignment *align = manage (new Alignment (.5, .5, 0, 0));
1066                                 align->add (*control_ui->controller);
1067                                 control_ui->knobtable->attach (*align, 0, 2, 0, 1, EXPAND, SHRINK, 2, 2);
1068                                 control_ui->knobtable->attach (control_ui->label, 0, 1, 1, 2, FILL, SHRINK);
1069                                 control_ui->knobtable->attach (control_ui->automate_button, 1, 2, 1, 2, SHRINK, SHRINK, 2, 0);
1070                         } else {
1071                                 control_ui->knobtable->attach (*control_ui->controller, 0, 2, 0, 1);
1072                                 control_ui->knobtable->attach (control_ui->label, 0, 1, 1, 2, FILL, SHRINK);
1073                                 control_ui->knobtable->attach (control_ui->automate_button, 1, 2, 1, 2, SHRINK, SHRINK, 2, 0);
1074                         }
1075
1076                 } else {
1077
1078                         control_ui->pack_start (control_ui->label, true, true);
1079                         if (control_ui->combo) {
1080                                 control_ui->pack_start(*control_ui->combo, false, true);
1081                         } else if (control_ui->spin_box) {
1082                                 control_ui->pack_start (*control_ui->spin_box, false, false);
1083                                 control_ui->pack_start (*control_ui->controller, false, false);
1084                         } else {
1085                                 control_ui->pack_start (*control_ui->controller, false, false);
1086                         }
1087                         control_ui->pack_start (control_ui->automate_button, false, false);
1088                 }
1089
1090
1091                 if (mcontrol->flags () & Controllable::NotAutomatable) {
1092                         control_ui->automate_button.set_sensitive (false);
1093                         set_tooltip(control_ui->automate_button, _("This control cannot be automated"));
1094                 } else {
1095                         control_ui->automate_button.signal_button_press_event().connect (
1096                                         sigc::bind (sigc::mem_fun(*this, &GenericPluginUI::astate_button_event),
1097                                                     control_ui),
1098                                         false);
1099                         mcontrol->alist()->automation_state_changed.connect (
1100                                         control_connections,
1101                                         invalidator (*this),
1102                                         boost::bind (&GenericPluginUI::automation_state_changed, this, control_ui),
1103                                         gui_context());
1104                         input_controls_with_automation.push_back (control_ui);
1105                 }
1106
1107                 if (desc.toggled && !control_ui->combo) {
1108                         control_ui->button = true;
1109                         ArdourButton* but = dynamic_cast<ArdourButton*>(control_ui->controller->widget ());
1110                         assert (but);
1111                         but->set_name ("pluginui toggle");
1112                         update_control_display(control_ui);
1113                 }
1114
1115                 automation_state_changed (control_ui);
1116
1117                 /* Add to the list of CUIs that need manual update to workaround
1118                  * AutomationControl not knowing about preset loads */
1119                 input_controls.push_back (control_ui);
1120
1121         } else {
1122
1123                 control_ui->display = manage (new EventBox);
1124                 control_ui->display->set_name ("ParameterValueDisplay");
1125
1126                 control_ui->display_label = manage (new Label);
1127
1128                 control_ui->display_label->set_name ("ParameterValueDisplay");
1129
1130                 control_ui->display->add (*control_ui->display_label);
1131                 Gtkmm2ext::set_size_request_to_display_given_text (*control_ui->display, "-888.8g", 2, 6);
1132
1133                 control_ui->display->show_all ();
1134
1135                 control_ui->vbox = manage (new VBox);
1136                 control_ui->vbox->set_spacing(3);
1137
1138                 if (desc.unit == ParameterDescriptor::MIDI_NOTE) {
1139                         control_ui->vbox->pack_end (*control_ui->display, false, false);
1140                         control_ui->vbox->pack_end (control_ui->label, false, false);
1141                 } else if (desc.integer_step || desc.enumeration) {
1142                         control_ui->vbox->pack_end (*control_ui->display, false, false);
1143                         control_ui->vbox->pack_end (control_ui->label, false, false);
1144                 } else {
1145                         /* set up a meter for float ports */
1146
1147                         MeterInfo * info = new MeterInfo();
1148                         control_ui->meterinfo = info;
1149
1150                         info->meter = new FastMeter (
1151                                         5, 5, FastMeter::Vertical, 0,
1152                                         0x0000aaff,
1153                                         0x008800ff, 0x008800ff,
1154                                         0x00ff00ff, 0x00ff00ff,
1155                                         0xcccc00ff, 0xcccc00ff,
1156                                         0xffaa00ff, 0xffaa00ff,
1157                                         0xff0000ff,
1158                                         UIConfiguration::instance().color ("meter background bottom"),
1159                                         UIConfiguration::instance().color ("meter background top")
1160                                         );
1161
1162                         control_ui->label.set_angle(90);
1163
1164                         HBox* center =  manage (new HBox);
1165                         center->set_spacing(1);
1166                         center->pack_start (control_ui->label, false, false);
1167                         center->pack_start (*info->meter, false, false);
1168
1169                         control_ui->hbox = manage (new HBox);
1170                         control_ui->hbox->pack_start (*center, true, false);
1171
1172                         // horizontally center this hbox in the vbox
1173                         control_ui->vbox->pack_start (*control_ui->hbox, false, false);
1174
1175                         control_ui->meterinfo->meter->show_all();
1176                         control_ui->meterinfo->packed = true;
1177                         control_ui->vbox->pack_start (*control_ui->display, false, false);
1178                 }
1179
1180                 control_ui->pack_start (*control_ui->vbox);
1181
1182                 output_controls.push_back (control_ui);
1183         }
1184
1185         if (mcontrol) {
1186                 mcontrol->Changed.connect(control_connections, invalidator(*this),
1187                                           boost::bind(&GenericPluginUI::ui_parameter_changed,
1188                                                       this, control_ui),
1189                                           gui_context());
1190         }
1191
1192         return control_ui;
1193 }
1194
1195 void
1196 GenericPluginUI::knob_size_request(Gtk::Requisition* req, ControlUI* cui) {
1197         Gtk::Requisition astate_req (cui->automate_button.size_request());
1198         const int size = (int) (astate_req.height * 1.5);
1199         req->width = max(req->width, size);
1200         req->height = max(req->height, size);
1201 }
1202
1203
1204 bool
1205 GenericPluginUI::astate_button_event (GdkEventButton* ev, ControlUI* cui)
1206 {
1207         if (ev->button != 1) {
1208                 return true;
1209         }
1210
1211         using namespace Menu_Helpers;
1212
1213         if (automation_menu == 0) {
1214                 automation_menu = manage (new Menu);
1215                 automation_menu->set_name ("ArdourContextMenu");
1216                 automation_menu->set_reserve_toggle_size(false);
1217         }
1218
1219         MenuList& items (automation_menu->items());
1220
1221         items.clear ();
1222         items.push_back (MenuElem (S_("Automation|Manual"),
1223                                    sigc::bind (sigc::mem_fun(*this, &GenericPluginUI::set_automation_state), (AutoState) ARDOUR::Off, cui)));
1224         items.push_back (MenuElem (_("Play"),
1225                                    sigc::bind (sigc::mem_fun(*this, &GenericPluginUI::set_automation_state), (AutoState) Play, cui)));
1226         items.push_back (MenuElem (_("Write"),
1227                                    sigc::bind (sigc::mem_fun(*this, &GenericPluginUI::set_automation_state), (AutoState) Write, cui)));
1228         items.push_back (MenuElem (_("Touch"),
1229                                    sigc::bind (sigc::mem_fun(*this, &GenericPluginUI::set_automation_state), (AutoState) Touch, cui)));
1230         items.push_back (MenuElem (_("Latch"),
1231                                    sigc::bind (sigc::mem_fun(*this, &GenericPluginUI::set_automation_state), (AutoState) Latch, cui)));
1232
1233         anchored_menu_popup(automation_menu, &cui->automate_button, cui->automate_button.get_text(),
1234                             1, ev->time);
1235
1236         return true;
1237 }
1238
1239 void
1240 GenericPluginUI::set_all_automation (AutoState as)
1241 {
1242         for (vector<ControlUI*>::iterator i = input_controls_with_automation.begin(); i != input_controls_with_automation.end(); ++i) {
1243                 set_automation_state (as, (*i));
1244         }
1245 }
1246
1247 void
1248 GenericPluginUI::set_automation_state (AutoState state, ControlUI* cui)
1249 {
1250         insert->set_parameter_automation_state (cui->parameter(), state);
1251 }
1252
1253 void
1254 GenericPluginUI::ui_parameter_changed (ControlUI* cui)
1255 {
1256         if (!cui->update_pending) {
1257                 cui->update_pending = true;
1258                 Gtkmm2ext::UI::instance()->call_slot (MISSING_INVALIDATOR, boost::bind (&GenericPluginUI::update_control_display, this, cui));
1259         }
1260 }
1261
1262 void
1263 GenericPluginUI::update_control_display (ControlUI* cui)
1264 {
1265         /* XXX how do we handle logarithmic stuff here ? */
1266
1267         cui->update_pending = false;
1268
1269         float val = cui->control->get_value();
1270
1271         PBD::Unwinder<bool> (cui->ignore_change, true);
1272
1273         if (cui->combo && cui->scale_points) {
1274                 for (ARDOUR::ScalePoints::iterator it = cui->scale_points->begin(); it != cui->scale_points->end(); ++it) {
1275                         if (it->second == val) {
1276                                 cui->combo->set_text(it->first);
1277                                 break;
1278                         }
1279                 }
1280         } else if (cui->button) {
1281                 // AutomationController handles this
1282         }
1283
1284         if( cui->controller ) {
1285             cui->controller->display_effective_value();
1286         }
1287
1288
1289         /*} else {
1290                 if (cui->logarithmic) {
1291                         val = log(val);
1292                 }
1293                 if (val != cui->adjustment->get_value()) {
1294                         cui->adjustment->set_value (val);
1295                 }
1296         }*/
1297 }
1298
1299 void
1300 GenericPluginUI::update_input_displays ()
1301 {
1302         /* XXX This is a workaround for AutomationControl not knowing about preset loads */
1303         for (vector<ControlUI*>::iterator i = input_controls.begin();
1304              i != input_controls.end();
1305              ++i) {
1306                 update_control_display(*i);
1307         }
1308         return;
1309 }
1310
1311 void
1312 GenericPluginUI::control_combo_changed (ControlUI* cui, float value)
1313 {
1314         if (!cui->ignore_change) {
1315                 insert->automation_control (cui->parameter())->set_value (value, Controllable::NoGroup);
1316         }
1317 }
1318
1319 bool
1320 GenericPluginUI::start_updating (GdkEventAny*)
1321 {
1322         if (output_controls.size() > 0 ) {
1323                 screen_update_connection.disconnect();
1324                 screen_update_connection = Timers::super_rapid_connect (sigc::mem_fun(*this, &GenericPluginUI::output_update));
1325         }
1326         return false;
1327 }
1328
1329 bool
1330 GenericPluginUI::stop_updating (GdkEventAny*)
1331 {
1332         if (output_controls.size() > 0 ) {
1333                 screen_update_connection.disconnect();
1334         }
1335         return false;
1336 }
1337
1338 void
1339 GenericPluginUI::output_update ()
1340 {
1341         for (vector<ControlUI*>::iterator i = output_controls.begin(); i != output_controls.end(); ++i) {
1342                 float val = plugin->get_parameter ((*i)->parameter().id());
1343                 char buf[32];
1344                 boost::shared_ptr<ReadOnlyControl> c = insert->control_output ((*i)->parameter().id());
1345                 const std::string& str = ARDOUR::value_as_string(c->desc(), Variant(val));
1346                 size_t len = str.copy(buf, 31);
1347                 buf[len] = '\0';
1348                 (*i)->display_label->set_text (buf);
1349
1350                 if ((*i)->meterinfo && (*i)->meterinfo->packed) {
1351                         (*i)->meterinfo->meter->set (c->desc().to_interface (val));
1352                 }
1353         }
1354 }
1355
1356 void
1357 GenericPluginUI::set_path_property (const ParameterDescriptor& desc,
1358                                     Gtk::FileChooserButton*    widget)
1359 {
1360         plugin->set_property(desc.key, Variant(Variant::PATH, widget->get_filename()));
1361 }
1362
1363 void
1364 GenericPluginUI::path_property_changed (uint32_t key, const Variant& value)
1365 {
1366         FilePathControls::iterator c = _filepath_controls.find(key);
1367         if (c != _filepath_controls.end()) {
1368                 c->second->set_filename(value.get_path());
1369         } else {
1370                 std::cerr << "warning: property change for property with no control" << std::endl;
1371         }
1372 }
1373
1374 void
1375 GenericPluginUI::toggle_pianokeyboard ()
1376 {
1377         if (_plugin_pianokeyboard_expander.get_expanded()) {
1378                 _plugin_pianokeyboard_expander.add (_pianobox);
1379                 _pianobox.show_all ();
1380         } else {
1381                 const int child_height = _plugin_pianokeyboard_expander.get_child ()->get_height ();
1382                 _plugin_pianokeyboard_expander.get_child ()->hide ();
1383                 _plugin_pianokeyboard_expander.remove ();
1384
1385                 Gtk::Window *toplevel = (Gtk::Window*) _plugin_pianokeyboard_expander.get_ancestor (GTK_TYPE_WINDOW);
1386                 if (toplevel) {
1387                         Gtk::Requisition wr;
1388                         toplevel->get_size (wr.width, wr.height);
1389                         wr.height -= child_height;
1390                         toplevel->resize (wr.width, wr.height);
1391                 }
1392         }
1393 }
1394
1395 void
1396 GenericPluginUI::_note_on_event_handler(GtkWidget*, int note, gpointer arg)
1397 {
1398         ((GenericPluginUI*)arg)->note_on_event_handler(note);
1399 }
1400
1401 void
1402 GenericPluginUI::_note_off_event_handler(GtkWidget*, int note, gpointer arg)
1403 {
1404         ((GenericPluginUI*)arg)->note_off_event_handler(note);
1405 }
1406
1407 void
1408 GenericPluginUI::note_on_event_handler (int note)
1409 {
1410         MidiTrack* mt = dynamic_cast<MidiTrack*> (insert->owner());
1411         _pianomm->grab_focus ();
1412         uint8_t channel = _piano_channel.get_value_as_int () - 1;
1413         uint8_t event[3];
1414         event[0] = (MIDI_CMD_NOTE_ON | channel);
1415         event[1] = note;
1416         event[2] = _piano_velocity.get_value_as_int ();
1417         if (mt) {
1418                 mt->write_immediate_event (3, event);
1419         } else {
1420                 insert->write_immediate_event (3, event);
1421         }
1422 }
1423
1424 void
1425 GenericPluginUI::note_off_event_handler (int note)
1426 {
1427         MidiTrack* mt = dynamic_cast<MidiTrack*> (insert->owner());
1428         uint8_t channel = _piano_channel.get_value_as_int () - 1;
1429         uint8_t event[3];
1430         event[0] = (MIDI_CMD_NOTE_OFF | channel);
1431         event[1] = note;
1432         event[2] = 0;
1433         if (mt) {
1434                 mt->write_immediate_event (3, event);
1435         } else {
1436                 insert->write_immediate_event (3, event);
1437         }
1438 }