+ wxCoord label_width;
+ wxCoord label_height;
+
+ /* Row channel labels */
+
+ for (auto i = 0U; i < _input_channels.size(); ++i) {
+ auto const name = std_to_wx(_input_channels[i].name);
+ dc.GetTextExtent (name, &label_width, &label_height);
+ dc.DrawText (name, left_width() - MINIMUM_COLUMN_WIDTH + (MINIMUM_COLUMN_WIDTH - label_width) / 2, TOP_HEIGHT + ROW_HEIGHT * i + (ROW_HEIGHT - label_height) / 2);
+ }
+
+ /* Vertical lines on the left */
+
+ for (int i = 1; i < 3; ++i) {
+ dc.DrawLine (
+ wxPoint(MINIMUM_COLUMN_WIDTH * i, TOP_HEIGHT),
+ wxPoint(MINIMUM_COLUMN_WIDTH * i, TOP_HEIGHT + _input_channels.size() * ROW_HEIGHT)
+ );
+ }
+
+ int y = TOP_HEIGHT;
+ for (auto const& i: _input_groups) {
+ dc.DrawLine (wxPoint(MINIMUM_COLUMN_WIDTH, y), wxPoint(MINIMUM_COLUMN_WIDTH * 2, y));
+ y += (i.to - i.from + 1) * ROW_HEIGHT;
+ }
+ dc.DrawLine (wxPoint(MINIMUM_COLUMN_WIDTH, y), wxPoint(MINIMUM_COLUMN_WIDTH * 2, y));
+
+ if (_input_groups.empty()) {
+ auto const bottom = TOP_HEIGHT + _input_channels.size() * ROW_HEIGHT;
+ dc.DrawLine(wxPoint(MINIMUM_COLUMN_WIDTH, bottom), wxPoint(MINIMUM_COLUMN_WIDTH * 2, bottom));
+ }
+
+ /* Group labels and lines; be careful here as wxDCClipper does not restore the old
+ * clipping rectangle after it is destroyed
+ */
+ y = TOP_HEIGHT;
+ for (auto const& i: _input_groups) {
+ int const height = (i.to - i.from + 1) * ROW_HEIGHT;
+ dc.GetTextExtent (std_to_wx(i.name), &label_width, &label_height);
+ if (label_width > height) {
+ label_width = height - 8;
+ }
+
+ {
+ wxDCClipper clip (dc, wxRect(MINIMUM_COLUMN_WIDTH, y, ROW_HEIGHT, height));
+ int yp = y;
+ if ((yp - 2 * ROW_HEIGHT) < dc.GetLogicalOrigin().y) {
+ yp += dc.GetLogicalOrigin().y;
+ }
+
+ dc.DrawRotatedText (
+ std_to_wx(i.name),
+ ROW_HEIGHT + (ROW_HEIGHT - label_height) / 2,
+ y + (height + label_width) / 2,
+ 90
+ );
+ }
+
+ y += height;
+ }