Index: gtk2_ardour/generic_pluginui.cc
===================================================================
--- gtk2_ardour/generic_pluginui.cc	(revision 4281)
+++ gtk2_ardour/generic_pluginui.cc	(working copy)
@@ -121,9 +121,119 @@
 	}
 }
 
+
+static int numeric_value(string label, int pos) {
+int value = -1;
+
+	stringstream s;
+	s << label.substr(pos);
+	s >> value;
+	
+	return value;
+
+}
+
+static void split_label(string label, string &first, string &last, int &num) {
+static const char *digits = "0123456789";
+static const char *whitespace = " \t\r\n";
+ 
+
+	unsigned int first_digit_pos = label.find_first_of(digits);
+	
+
+	if (first_digit_pos != string::npos) {
+		// found some digits
+		num = numeric_value(label, first_digit_pos);
+
+		if (first_digit_pos > 0) {
+			// some text preceding number
+			first = label.substr(0, first_digit_pos);
+		
+		} else {
+			first = "";
+		
+		}
+		
+		unsigned int last_digit_pos = label.find_last_of(digits);
+		if (last_digit_pos < label.length()) {
+			// some text following number
+			last = label.substr(last_digit_pos + 1);
+		
+		} else {
+			last = "";
+		}	
+	} else {
+		// no digits found
+		num = -1;
+		unsigned int first_whitespace_pos = label.find_first_of(whitespace);
+		if (first_whitespace_pos != string::npos) {
+			// found some whitespace
+			if (first_whitespace_pos > 0) {
+				// some text before whitespace
+				first = label.substr(0, first_whitespace_pos);
+			} else {
+				// whitespace at start of label: what to do?
+				first = "";
+			}
+			
+			unsigned int last_whitespace_pos = label.find_last_of(whitespace);
+			if (last_whitespace_pos < label.length()) {
+				// some text following whitespace
+				last = label.substr(last_whitespace_pos + 1);
+			} else {
+				// whitespace at end of label: what to do?
+				last = "";	
+			}			
+		} else {
+			// no whitespace
+			first = label;
+			last = "";
+		}
+	
+	}
+}
+
+
+static bool possible_split(string label, string previous) {
+/* See how similar label is to previous.
+ *
+ * If they are sufficiently different, (according to a hand-wavy definition of
+ * "sufficiently"), return true to indicate that we might wish to start a new 
+ * column, or display some visual demarcation.
+ *
+ * Otherwise, return false to indicate that this label and the previous one
+ * should probably be grouped together.
+ */
+
+ 
+	int section_number, previous_section_number;
+
+	string first_word, last_word,
+	       previous_first_word, previous_last_word;
+
+	if (previous == "") 
+		return false;
+
+	split_label(label, first_word, last_word, section_number);
+	split_label(previous, previous_first_word, previous_last_word, previous_section_number);
+
+	// cerr << "label: " << label << ", num: " << section_number << endl;
+
+	if (section_number == previous_section_number) {
+		return false;
+	} else {
+		if (first_word == previous_first_word &&
+		    last_word  == previous_last_word ) {
+			return false;
+		}
+	}
+	return true;
+
+}
+
+
 void
 GenericPluginUI::build ()
-
 {
 	guint32 i = 0;
 	guint32 x = 0;
@@ -173,8 +283,10 @@
 	frame->set_label (_("Controls"));
 	frame->add (*box);
 	hpacker.pack_start(*frame, true, true);
-
+	
 	/* find all ports. build control elements for all appropriate control ports */
+	
+	string previous_label = "";
 
 	for (i = 0; i < plugin->parameter_count(); ++i) {
 
@@ -188,24 +300,7 @@
 
 			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;
-				}
-			}
-
 			if ((cui = build_control_ui (i, plugin->get_nth_control (i))) == 0) {
 				error << string_compose(_("Plugin Editor: could not build control element for port %1"), i) << endmsg;
 				continue;
@@ -213,10 +308,34 @@
 				
 			if (cui->control || cui->clickbox || cui->combo) {
 
+				/* if we are scrollable, just use one long column */
+				if (!is_scrollable) {
+					x++;
+					if (possible_split(cui->label.get_text(), previous_label) || x > 20) {
+						if (x > 16) {
+							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 {
+							Label *split = new Label(" ");
+							box->pack_start(*split, false, false, 0);
+						}
+					}
+				}
+
 				box->pack_start (*cui, false, false);
-
+				previous_label = cui->label.get_text();
 			} else if (cui->button) {
-
+			
 				if (button_row == button_rows) {
 					button_row = 0;
 					if (++button_col == button_cols) {
@@ -227,6 +346,7 @@
 
 				button_table.attach (*cui, button_col, button_col + 1, button_row, button_row+1, 
 						     FILL|EXPAND, FILL);
+
 				button_row++;
 
 			} else if (cui->display) {
