summaryrefslogtreecommitdiff
path: root/gtk2_ardour/generic_pluginui.cc
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2011-07-11 12:34:20 +0000
committerPaul Davis <paul@linuxaudiosystems.com>2011-07-11 12:34:20 +0000
commitd314c96e33f053c9197f728c4f8ba6b39dd39bd3 (patch)
treeed8f1abd8ebcc14ca9d3f5048bafbd5a0e3d6a10 /gtk2_ardour/generic_pluginui.cc
parent31d0eca3077938372d5dad027b1e6270b0ee6e4c (diff)
heuristic grouping of plugin controls in the generic plugin UI window, from colin f. (#2482)
git-svn-id: svn://localhost/ardour2/branches/3.0@9835 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'gtk2_ardour/generic_pluginui.cc')
-rw-r--r--gtk2_ardour/generic_pluginui.cc182
1 files changed, 138 insertions, 44 deletions
diff --git a/gtk2_ardour/generic_pluginui.cc b/gtk2_ardour/generic_pluginui.cc
index a44b54c9b0..64b1ac344c 100644
--- a/gtk2_ardour/generic_pluginui.cc
+++ b/gtk2_ardour/generic_pluginui.cc
@@ -128,6 +128,7 @@ GenericPluginUI::GenericPluginUI (boost::shared_ptr<PluginInsert> pi, bool scrol
bypass_button.set_active (!pi->active());
+ prefheight = 0;
build ();
}
@@ -138,6 +139,55 @@ GenericPluginUI::~GenericPluginUI ()
}
}
+// Some functions for calculating the 'similarity' of two plugin
+// control labels.
+
+static int get_number(string label) {
+static const char *digits = "0123456789";
+int value = -1;
+
+ std::size_t first_digit_pos = label.find_first_of(digits);
+ if (first_digit_pos != string::npos) {
+ // found some digits: there's a number in there somewhere
+ stringstream s;
+ s << label.substr(first_digit_pos);
+ s >> value;
+ }
+ return value;
+}
+
+static int match_or_digit(char c1, char c2) {
+ return c1 == c2 || (isdigit(c1) && isdigit(c2));
+}
+
+static std::size_t matching_chars_at_head(const string s1, const string s2) {
+std::size_t length, n = 0;
+
+ length = min(s1.length(), s2.length());
+ while (n < length) {
+ if (!match_or_digit(s1[n], s2[n]))
+ break;
+ n++;
+ }
+ return n;
+}
+
+static std::size_t matching_chars_at_tail(const string s1, const string s2) {
+std::size_t s1pos, s2pos, n = 0;
+
+ s1pos = s1.length();
+ s2pos = s2.length();
+ while (s1pos-- > 0 && s2pos-- > 0) {
+ if (!match_or_digit(s1[s1pos], s2[s2pos]) )
+ break;
+ n++;
+ }
+ return n;
+}
+
+static const guint32 min_controls_per_column = 17, max_controls_per_column = 24;
+static const float default_similarity_threshold = 0.3;
+
void
GenericPluginUI::build ()
{
@@ -151,7 +201,6 @@ GenericPluginUI::build ()
int output_rows, output_cols;
int button_rows, button_cols;
- prefheight = 30;
hpacker.set_spacing (10);
output_rows = initial_output_rows;
@@ -176,6 +225,7 @@ GenericPluginUI::build ()
bt_frame = manage (new Frame);
bt_frame->set_name ("BaseFrame");
+ bt_frame->set_label (_("Switches"));
bt_frame->add (button_table);
hpacker.pack_start(*bt_frame, true, true);
@@ -190,6 +240,7 @@ GenericPluginUI::build ()
hpacker.pack_start(*frame, true, true);
/* find all ports. build control elements for all appropriate control ports */
+ std::vector<ControlUI *> cui_controls_list;
for (i = 0; i < plugin->parameter_count(); ++i) {
@@ -203,24 +254,6 @@ GenericPluginUI::build ()
ControlUI* cui;
- /* if we are scrollable, just use one long column */
-
- if (!is_scrollable) {
- if (x++ > 20){
- frame = manage (new Frame);
- frame->set_name ("BaseFrame");
- box = manage (new VBox);
-
- box->set_border_width (5);
- box->set_spacing (1);
-
- frame->add (*box);
- hpacker.pack_start(*frame, true, true);
-
- x = 1;
- }
- }
-
boost::shared_ptr<ARDOUR::AutomationControl> c
= boost::dynamic_pointer_cast<ARDOUR::AutomationControl>(
insert->control(Evoral::Parameter(PluginAutomation, 0, i)));
@@ -231,12 +264,12 @@ GenericPluginUI::build ()
}
if (cui->controller || cui->clickbox || cui->combo) {
-
- box->pack_start (*cui, false, false);
-
+ // Get all of the controls into a list, so that
+ // we can lay them out a bit more nicely later.
+ cui_controls_list.push_back(cui);
} else if (cui->button) {
- if (button_row == button_rows) {
+ if (!is_scrollable && button_row == button_rows) {
button_row = 0;
if (++button_col == button_cols) {
button_cols += 2;
@@ -259,33 +292,93 @@ GenericPluginUI::build ()
output_cols ++;
output_table.resize (output_rows, output_cols);
}
+ }
+ }
+ }
- /* old code, which divides meters into
- * columns first, rows later. New code divides into one row
-
- if (output_row == output_rows) {
- output_row = 0;
- if (++output_col == output_cols) {
- output_cols += 2;
- output_table.resize (output_rows, output_cols);
- }
- }
-
- output_table.attach (*cui, output_col, output_col + 1, output_row, output_row+1,
- FILL|EXPAND, FILL);
-
- output_row++;
- */
+ // Iterate over the list of controls to find which adjacent controls
+ // are similar enough to be grouped together.
+
+ string label, previous_label = "";
+ int numbers_in_labels[cui_controls_list.size()];
+
+ float similarity_scores[cui_controls_list.size()];
+ float most_similar = 0.0, least_similar = 1.0;
+
+ i = 0;
+ for (vector<ControlUI*>::iterator cuip = cui_controls_list.begin(); cuip != cui_controls_list.end(); ++cuip, ++i) {
+ label = (*cuip)->label.get_text();
+ numbers_in_labels[i] = get_number(label);
+
+ if (i > 0) {
+ // A hand-wavy calculation of how similar this control's
+ // label is to the previous.
+ similarity_scores[i] =
+ (float) (
+ ( matching_chars_at_head(label, previous_label) +
+ matching_chars_at_tail(label, previous_label) +
+ 1
+ )
+ ) / (label.length() + previous_label.length());
+ if (numbers_in_labels[i] >= 0) {
+ similarity_scores[i] += (numbers_in_labels[i] == numbers_in_labels[i-1]);
}
+ least_similar = min(least_similar, similarity_scores[i]);
+ most_similar = max(most_similar, similarity_scores[i]);
+ } else {
+ similarity_scores[0] = 1.0;
+ }
- /* HACK: ideally the preferred height would be queried from
- the complete hpacker, but I can't seem to get that
- information in time, so this is an estimation
- */
+ // cerr << "label: " << label << " sim: " << fixed << setprecision(3) << similarity_scores[i] << " num: " << numbers_in_labels[i] << endl;
+ previous_label = label;
+ }
- prefheight += 30;
+
+ // cerr << "most similar: " << most_similar << ", least similar: " << least_similar << endl;
+ float similarity_threshold;
+
+ if (most_similar > 1.0) {
+ similarity_threshold = default_similarity_threshold;
+ } else {
+ similarity_threshold = most_similar - (1 - default_similarity_threshold);
+ }
+
+ // Now iterate over the list of controls to display them, placing an
+ // HSeparator between controls of less than a certain similarity, and
+ // starting a new column when necessary.
+
+ i = 0;
+ for (vector<ControlUI*>::iterator cuip = cui_controls_list.begin(); cuip != cui_controls_list.end(); ++cuip, ++i) {
+
+ ControlUI* cui = *cuip;
+
+ if (!is_scrollable) {
+ x++;
+ }
+
+ if (x > max_controls_per_column || similarity_scores[i] <= similarity_threshold) {
+ if (x > min_controls_per_column) {
+ frame = manage (new Frame);
+ frame->set_name ("BaseFrame");
+ frame->set_label (_("Controls"));
+ box = manage (new VBox);
+ box->set_border_width (5);
+ box->set_spacing (1);
+ frame->add (*box);
+ hpacker.pack_start(*frame, true, true);
+ x = 0;
+ } else {
+ HSeparator *split = new HSeparator();
+ split->set_size_request(-1, 5);
+ box->pack_start(*split, false, false, 0);
+ }
}
+ box->pack_start (*cui, false, false);
+ }
+
+ if (is_scrollable) {
+ prefheight = 30 * i;
}
if (box->children().empty()) {
@@ -299,6 +392,7 @@ GenericPluginUI::build ()
if (!output_table.children().empty()) {
frame = manage (new Frame);
frame->set_name ("BaseFrame");
+ frame->set_label(_("Meters"));
frame->add (output_table);
hpacker.pack_end (*frame, true, true);
}