13
0

Considerable re-work to allow scrolling of the checkbutton area of the dialogue. Add/remove port is now on a context menu over the row labels.

git-svn-id: svn://localhost/ardour2/trunk@2550 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Carl Hetherington 2007-10-13 00:58:03 +00:00
parent cded4193fe
commit 465a64520f
2 changed files with 371 additions and 347 deletions

View File

@ -1,5 +1,5 @@
/*
Copyright (C) 2002-2003 Paul Davis
Copyright (C) 2002-2007 Paul Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -22,6 +22,10 @@
#include <gtkmm/image.h>
#include <gtkmm/stock.h>
#include <gtkmm/messagedialog.h>
#include <gtkmm/menu.h>
#include <gtkmm/menu_elems.h>
#include <gtkmm/menuitem.h>
#include <gtkmm/menushell.h>
#include <glibmm/objectbase.h>
#include <gtkmm2ext/doi.h>
#include <ardour/port_insert.h>
@ -37,6 +41,10 @@
#include "gui_thread.h"
#include "i18n.h"
/** Add a port to a group.
* @param p Port name, with or without prefix.
*/
void
PortGroup::add (std::string const & p)
{
@ -47,8 +55,122 @@ PortGroup::add (std::string const & p)
}
}
RotatedLabelSet::RotatedLabelSet (GroupedPortList& g)
: Glib::ObjectBase ("RotatedLabelSet"), Gtk::Widget (), _port_list (g), _base_start (64), _base_width (128)
PortGroupTable::PortGroupTable (
PortGroup& g, boost::shared_ptr<ARDOUR::IO> io, bool for_input
)
: _port_group (g), _ignore_check_button_toggle (false),
_io (io), _for_input (for_input)
{
ARDOUR::DataType const t = _io->default_type();
int rows;
if (_for_input) {
rows = _io->n_inputs().get(t);
} else {
rows = _io->n_outputs().get(t);
}
int const ports = _port_group.ports.size();
if (rows == 0 || ports == 0) {
return;
}
/* Sort out the table and the checkbuttons inside it */
_table.resize (rows, ports);
_check_buttons.resize (rows);
for (int i = 0; i < rows; ++i) {
_check_buttons[i].resize (ports);
}
for (int i = 0; i < rows; ++i) {
for (uint32_t j = 0; j < _port_group.ports.size(); ++j) {
Gtk::CheckButton* b = new Gtk::CheckButton;
b->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &PortGroupTable::check_button_toggled), b, i, _port_group.prefix + _port_group.ports[j]));
_check_buttons[i][j] = b;
_table.attach (*b, j, j + 1, i, i + 1);
}
}
_box.add (_table);
_ignore_check_button_toggle = true;
/* Set the state of the check boxes according to current connections */
for (int i = 0; i < rows; ++i) {
const char **connections = _for_input ? _io->input(i)->get_connections() : _io->output(i)->get_connections();
for (uint32_t j = 0; j < _port_group.ports.size(); ++j) {
std::string const t = _port_group.prefix + _port_group.ports[j];
int k = 0;
bool required_state = false;
while (connections && connections[k]) {
if (std::string(connections[k]) == t) {
required_state = true;
break;
}
++k;
}
_check_buttons[i][j]->set_active (required_state);
}
}
_ignore_check_button_toggle = false;
}
/** @return Width and height of a single check button in a port group table */
std::pair<int, int>
PortGroupTable::unit_size () const
{
if (_check_buttons.empty() || _check_buttons[0].empty()) {
return std::pair<int, int> (0, 0);
}
return std::make_pair (
_check_buttons[0][0]->get_width() + _table.get_col_spacing (0),
_check_buttons[0][0]->get_height() + _table.get_row_spacing (0)
);
}
Gtk::Widget&
PortGroupTable::get_widget ()
{
return _box;
}
/** Handle a toggle of a check button */
void
PortGroupTable::check_button_toggled (Gtk::CheckButton* b, int r, std::string const & p)
{
if (_ignore_check_button_toggle) {
return;
}
bool const new_state = b->get_active ();
if (new_state) {
if (_for_input) {
_io->connect_input (_io->input(r), p, 0);
} else {
_io->connect_output (_io->output(r), p, 0);
}
} else {
if (_for_input) {
_io->disconnect_input (_io->input(r), p, 0);
} else {
_io->disconnect_output (_io->output(r), p, 0);
}
}
}
RotatedLabelSet::RotatedLabelSet (PortGroupList& g)
: Glib::ObjectBase ("RotatedLabelSet"), Gtk::Widget (), _port_group_list (g), _base_width (128)
{
set_flags (Gtk::NO_WINDOW);
set_angle (30);
@ -84,7 +206,7 @@ RotatedLabelSet::on_size_request (Gtk::Requisition* requisition)
/* Our height is the highest label */
requisition->height = 0;
for (GroupedPortList::const_iterator i = _port_list.begin(); i != _port_list.end(); ++i) {
for (PortGroupList::const_iterator i = _port_group_list.begin(); i != _port_group_list.end(); ++i) {
for (std::vector<std::string>::const_iterator j = i->ports.begin(); j != i->ports.end(); ++j) {
std::pair<int, int> const d = setup_layout (*j);
if (d.second > requisition->height) {
@ -94,10 +216,10 @@ RotatedLabelSet::on_size_request (Gtk::Requisition* requisition)
}
/* And our width is the base plus the width of the last label */
requisition->width = _base_start + _base_width;
int const n = _port_list.n_ports ();
requisition->width = _base_width;
int const n = _port_group_list.n_ports ();
if (n > 0) {
std::pair<int, int> const d = setup_layout (_port_list.get_port_by_index (n - 1, false));
std::pair<int, int> const d = setup_layout (_port_group_list.get_port_by_index (n - 1, false));
requisition->width += d.first;
}
}
@ -166,10 +288,10 @@ RotatedLabelSet::on_unrealize()
}
/**
* Set up our Pango layout to plot a given string, and compute its dimensions once it has been rotated.
* @param s String to use.
* @return width and height of the rotated string, in pixels.
/** Set up our Pango layout to plot a given string, and compute its dimensions once
* it has been rotated.
* @param s String to use.
* @return width and height of the rotated string, in pixels.
*/
std::pair<int, int>
@ -182,8 +304,8 @@ RotatedLabelSet::setup_layout (std::string const & s)
int h;
_pango_layout->get_pixel_size (w, h);
/* Rotate the width and height as appropriate. I thought Pango might be able to do this for us,
but I can't find out how... */
/* Rotate the width and height as appropriate. I thought Pango might be able
to do this for us, but I can't find out how... */
std::pair<int, int> d;
d.first = int (w * cos (_angle_radians) - h * sin (_angle_radians));
d.second = int (w * sin (_angle_radians) + h * cos (_angle_radians));
@ -199,14 +321,14 @@ RotatedLabelSet::on_expose_event (GdkEventExpose* event)
}
int const height = get_allocation().get_height ();
double const spacing = double (_base_width) / _port_list.n_ports();
double const spacing = double (_base_width) / _port_group_list.n_ports();
/* Plot all the labels; really we should clip for efficiency */
int n = 0;
for (GroupedPortList::const_iterator i = _port_list.begin(); i != _port_list.end(); ++i) {
for (PortGroupList::const_iterator i = _port_group_list.begin(); i != _port_group_list.end(); ++i) {
for (uint32_t j = 0; j < i->ports.size(); ++j) {
std::pair<int, int> const d = setup_layout (i->ports[j]);
get_window()->draw_layout (_gc, _base_start + int ((n + 0.25) * spacing), height - d.second, _pango_layout, _fg_colour, _bg_colour);
get_window()->draw_layout (_gc, int ((n + 0.25) * spacing), height - d.second, _pango_layout, _fg_colour, _bg_colour);
++n;
}
}
@ -214,69 +336,48 @@ RotatedLabelSet::on_expose_event (GdkEventExpose* event)
return true;
}
/**
* Set the `base dimensions'. These are the dimensions of the area at which the labels start, and
* have to be set up to match whatever they are labelling.
/** Set the `base width'. This is the width of the base of the label set, ie:
*
* Roughly speaking, we have
*
* L L L L
* E E E E
* B B B B
* A A A A
* L L L L
* <--s--><--w--->
* L L L L
* E E E E
* B B B B
* A A A A
* L L L L
* <--w-->
*/
void
RotatedLabelSet::set_base_dimensions (int s, int w)
RotatedLabelSet::set_base_width (int w)
{
_base_start = s;
_base_width = w;
queue_resize ();
}
/**
* Construct an IOSelector.
/** Construct an IOSelector.
* @param session Session to operate on.
* @param io IO to operate on.
* @param for_input true if the selector is for an input, otherwise false.
*/
IOSelector::IOSelector (ARDOUR::Session& session, boost::shared_ptr<ARDOUR::IO> io, bool for_input)
: _session (session), _port_list (session, io, for_input), _io (io), _for_input (for_input),
_width (0), _height (0), _column_labels (_port_list),
_ignore_check_button_toggle (false), _add_remove_box_added (false)
: _port_group_list (session, io, for_input), _io (io), _for_input (for_input),
_row_labels_vbox (0), _column_labels (_port_group_list), _left_vbox_pad (0)
{
/* Column labels */
pack_start (_column_labels, true, true);
_left_vbox.pack_start (*Gtk::manage (new Gtk::Label ("")));
_overall_hbox.pack_start (_left_vbox, false, false);
_scrolled_window.set_policy (Gtk::POLICY_ALWAYS, Gtk::POLICY_NEVER);
_scrolled_window.set_shadow_type (Gtk::SHADOW_NONE);
Gtk::VBox* b = new Gtk::VBox;
b->pack_start (_column_labels, false, false);
b->pack_start (_port_group_hbox, false, false);
Gtk::Alignment* a = new Gtk::Alignment (0, 1, 0, 0);
a->add (*Gtk::manage (b));
_scrolled_window.add (*Gtk::manage (a));
_overall_hbox.pack_start (_scrolled_window);
pack_start (_overall_hbox);
/* Buttons for adding and removing ports */
_add_port_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::ADD, Gtk::ICON_SIZE_MENU)));
_add_port_button.set_label (_("Add port"));
_add_port_button.signal_clicked().connect (mem_fun (*this, &IOSelector::add_port_button_clicked));
_add_remove_box.pack_start (_add_port_button);
_remove_port_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::REMOVE, Gtk::ICON_SIZE_MENU)));
_remove_port_button.set_label (_("Remove port"));
_remove_port_button.signal_clicked().connect (mem_fun (*this, &IOSelector::remove_port_button_clicked));
_add_remove_box.pack_start (_remove_port_button);
set_button_sensitivity ();
/* Table. We need to put in a HBox, with a dummy label to its right,
so that the rotated column labels can overhang the right hand side of the table. */
setup_table ();
setup_row_labels ();
setup_check_button_states ();
_table_hbox.pack_start (_table, false, false);
_table_hbox.pack_start (_dummy);
_table.set_col_spacings (4);
pack_start (_table_hbox);
show_all ();
update_column_label_dimensions ();
_port_group_hbox.signal_size_allocate().connect (sigc::hide (sigc::mem_fun (*this, &IOSelector::setup_dimensions)));
/* Listen for ports changing on the IO */
if (_for_input) {
@ -289,222 +390,199 @@ IOSelector::IOSelector (ARDOUR::Session& session, boost::shared_ptr<ARDOUR::IO>
IOSelector::~IOSelector ()
{
for (std::vector<Gtk::Label*>::iterator i = _row_labels.begin(); i != _row_labels.end(); ++i) {
clear ();
}
/** Clear out the things that change when the number of source or destination ports changes */
void
IOSelector::clear ()
{
for (std::vector<Gtk::EventBox*>::iterator i = _row_labels.begin(); i != _row_labels.end(); ++i) {
delete *i;
}
_row_labels.clear ();
if (_row_labels_vbox) {
_left_vbox.remove (*_row_labels_vbox);
}
delete _row_labels_vbox;
_row_labels_vbox = 0;
if (_left_vbox_pad) {
_left_vbox.remove (*_left_vbox_pad);
}
delete _left_vbox_pad;
_left_vbox_pad = 0;
for (std::vector<PortGroupTable*>::iterator i = _port_group_tables.begin(); i != _port_group_tables.end(); ++i) {
_port_group_hbox.remove ((*i)->get_widget());
delete *i;
}
for (uint32_t x = 0; x < _check_buttons.size(); ++x) {
for (uint32_t y = 0; y < _check_buttons[x].size(); ++y) {
delete _check_buttons[x][y];
}
}
_port_group_tables.clear ();
}
/**
* Sets up the sizing of the column label widget to match the table that's underneath it.
/** Set up dimensions of some of our widgets which depend on other dimensions
* within the dialogue.
*/
void
IOSelector::update_column_label_dimensions ()
IOSelector::setup_dimensions ()
{
if (_row_labels.empty() || _check_buttons.empty() || _check_buttons[0].empty()) {
return;
/* Get some dimensions from various places */
int const scrollbar_height = _scrolled_window.get_hscrollbar()->get_height();
std::pair<int, int> unit_size (0, 0);
int port_group_tables_height = 0;
for (std::vector<PortGroupTable*>::iterator i = _port_group_tables.begin(); i != _port_group_tables.end(); ++i) {
std::pair<int, int> const u = (*i)->unit_size ();
unit_size.first = std::max (unit_size.first, u.first);
unit_size.second = std::max (unit_size.second, u.second);
port_group_tables_height = std::max (
port_group_tables_height, (*i)->get_widget().get_height()
);
}
_column_labels.set_base_dimensions (
/* width of the row label + a column spacing */
_row_labels.front()->get_width() + _table.get_col_spacing (0),
/* width of a check button + a column spacing for each column */
_check_buttons.size() * ( _check_buttons[0][0]->get_width() + _table.get_col_spacing(1))
/* Column labels */
_column_labels.set_base_width (_port_group_list.n_ports () * unit_size.first);
/* Scrolled window */
/* XXX: really shouldn't set a minimum horizontal size here, but if we don't
the window starts up very small */
_scrolled_window.set_size_request (
std::min (_column_labels.get_width(), 640),
_column_labels.get_height() + port_group_tables_height + scrollbar_height + 16
);
/* Row labels */
for (std::vector<Gtk::EventBox*>::iterator i = _row_labels.begin(); i != _row_labels.end(); ++i) {
(*i)->get_child()->set_size_request (-1, unit_size.second);
}
if (_left_vbox_pad) {
_left_vbox_pad->set_size_request (-1, scrollbar_height + unit_size.second / 4);
}
}
/** Set up the dialogue */
void
IOSelector::setup_table ()
IOSelector::setup ()
{
if (_add_remove_box_added) {
_table.remove (_add_remove_box);
}
clear ();
for (std::vector<Gtk::EventBox*>::iterator i = _group_labels.begin(); i != _group_labels.end(); ++i) {
_table.remove (**i);
delete *i;
}
/* Work out how many rows we have */
ARDOUR::DataType const t = _io->default_type();
_group_labels.clear ();
int rows;
if (_for_input) {
rows = _io->n_inputs().get(t);
} else {
rows = _io->n_outputs().get(t);
}
/* New width */
int const old_width = _width;
_width = _port_list.n_ports ();
/* Row labels */
_row_labels_vbox = new Gtk::VBox;
for (int i = 0; i < rows; ++i) {
Gtk::Label* label = new Gtk::Label (_for_input ? _io->input(i)->name() : _io->output(i)->name());
Gtk::EventBox* b = new Gtk::EventBox;
b->set_events (Gdk::BUTTON_PRESS_MASK);
b->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &IOSelector::row_label_button_pressed), i));
b->add (*Gtk::manage (label));
_row_labels.push_back (b);
_row_labels_vbox->pack_start (*b, false, false);
}
_left_vbox.pack_start (*_row_labels_vbox, false, false);
_left_vbox_pad = new Gtk::Label ("");
_left_vbox.pack_start (*_left_vbox_pad, false, false);
/* New height */
/* Checkbutton tables */
int n = 0;
for (PortGroupList::iterator i = _port_group_list.begin(); i != _port_group_list.end(); ++i) {
PortGroupTable* t = new PortGroupTable (*i, _io, _for_input);
int const old_height = _height;
/* XXX: this is a bit of a hack; should probably use a configurable colour here */
Gdk::Color alt_bg = get_style()->get_bg (Gtk::STATE_NORMAL);
alt_bg.set_rgb (alt_bg.get_red() + 4096, alt_bg.get_green() + 4096, alt_bg.get_blue () + 4096);
if ((n % 2) == 0) {
t->get_widget().modify_bg (Gtk::STATE_NORMAL, alt_bg);
}
ARDOUR::DataType const t = _io->default_type();
if (_for_input) {
_height = _io->n_inputs().get(t);
} else {
_height = _io->n_outputs().get(t);
}
_table.resize (_width + 1, _height + 1);
/* Add checkbuttons where required, and remove those that aren't */
for (int x = _width; x < old_width; ++x) {
for (int y = 0; y < old_height; ++y) {
delete _check_buttons[x][y];
}
}
_check_buttons.resize (_width);
for (int x = 0; x < _width; x++) {
for (int y = _height; y < old_height; ++y) {
delete _check_buttons[x][y];
}
_check_buttons[x].resize (_height);
for (int y = 0; y < _height; y++) {
if (x >= old_width || y >= old_height) {
Gtk::CheckButton* button = new Gtk::CheckButton;
button->signal_toggled().connect (
sigc::bind (sigc::mem_fun (*this, &IOSelector::check_button_toggled), x, y)
);
_check_buttons[x][y] = button;
_table.attach (*button, x + 1, x + 2, y, y + 1);
}
}
}
/* Add more row labels where required, and remove those that aren't */
for (int y = _height; y < old_height; ++y) {
delete _row_labels[y];
}
_row_labels.resize (_height);
for (int y = old_height; y < _height; ++y) {
Gtk::Label* label = new Gtk::Label;
_row_labels[y] = label;
_table.attach (*label, 0, 1, y, y + 1);
}
_table.attach (_add_remove_box, 0, 1, _height, _height + 1);
_add_remove_box_added = true;
/* Add group labels */
int n = 1;
int m = 0;
/* XXX: this is a bit of a hack; should probably use a configurable colour here */
Gdk::Color alt_bg = get_style()->get_bg (Gtk::STATE_NORMAL);
alt_bg.set_rgb (alt_bg.get_red() + 4096, alt_bg.get_green() + 4096, alt_bg.get_blue () + 4096);
for (GroupedPortList::iterator i = _port_list.begin(); i != _port_list.end(); ++i) {
if (i->ports.empty() == false) {
Gtk::Label* label = new Gtk::Label ("<b>" + i->name + "</b>");
label->set_use_markup (true);
Gtk::EventBox* box = new Gtk::EventBox ();
box->add (*Gtk::manage (label));
if (m % 2 == 0) {
box->modify_bg (Gtk::STATE_NORMAL, alt_bg);
}
_group_labels.push_back (box);
_table.attach (*box, n, n + i->ports.size(), _height, _height + 1);
n += i->ports.size();
++m;
}
}
_port_group_tables.push_back (t);
_port_group_hbox.pack_start (t->get_widget(), false, false);
++n;
}
show_all ();
}
/**
* Write the correct text to the row labels.
*/
void
IOSelector::setup_row_labels ()
IOSelector::ports_changed (ARDOUR::IOChange change, void *src)
{
for (int y = 0; y < _height; y++) {
_row_labels[y]->set_text (_for_input ? _io->input(y)->name() : _io->output(y)->name());
}
ENSURE_GUI_THREAD (bind (mem_fun (*this, &IOSelector::ports_changed), change, src));
redisplay ();
}
/**
* Set up the state of each check button according to what connections are made.
*/
void
IOSelector::setup_check_button_states ()
IOSelector::redisplay ()
{
_ignore_check_button_toggle = true;
/* Set the state of the check boxes according to current connections */
for (int i = 0; i < _height; ++i) {
const char **connections = _for_input ? _io->input(i)->get_connections() : _io->output(i)->get_connections();
for (int j = 0; j < _width; ++j) {
std::string const t = _port_list.get_port_by_index (j);
int k = 0;
bool required_state = false;
while (connections && connections[k]) {
if (std::string(connections[k]) == t) {
required_state = true;
break;
}
++k;
}
_check_buttons[j][i]->set_active (required_state);
}
}
_ignore_check_button_toggle = false;
_port_group_list.refresh ();
setup ();
}
/**
* Handle a toggle of a check button.
*/
void
IOSelector::check_button_toggled (int x, int y)
/** Handle a button press on a row label */
bool
IOSelector::row_label_button_pressed (GdkEventButton* e, int r)
{
if (_ignore_check_button_toggle) {
return;
if (e->type != GDK_BUTTON_PRESS || e->button != 3) {
return false;
}
Gtk::Menu* menu = Gtk::manage (new Gtk::Menu);
Gtk::Menu_Helpers::MenuList& items = menu->items ();
menu->set_name ("ArdourContextMenu");
bool can_add;
bool can_remove;
std::string name;
ARDOUR::DataType const t = _io->default_type();
if (_for_input) {
can_add = _io->input_maximum().get(t) > _io->n_inputs().get(t);
can_remove = _io->input_minimum().get(t) < _io->n_inputs().get(t);
name = _io->input(r)->name();
} else {
can_add = _io->output_maximum().get(t) > _io->n_outputs().get(t);
can_remove = _io->output_minimum().get(t) < _io->n_outputs().get(t);
name = _io->output(r)->name();
}
bool const new_state = _check_buttons[x][y]->get_active ();
std::string const port = _port_list.get_port_by_index (x);
items.push_back (
Gtk::Menu_Helpers::MenuElem (_("Add port"), sigc::mem_fun (*this, &IOSelector::add_port))
);
if (new_state) {
if (_for_input) {
_io->connect_input (_io->input(y), port, 0);
} else {
_io->connect_output (_io->output(y), port, 0);
}
} else {
if (_for_input) {
_io->disconnect_input (_io->input(y), port, 0);
} else {
_io->disconnect_output (_io->output(y), port, 0);
}
}
items.back().set_sensitive (can_add);
items.push_back (
Gtk::Menu_Helpers::MenuElem (_("Remove port '") + name + _("'"), sigc::bind (sigc::mem_fun (*this, &IOSelector::remove_port), r))
);
items.back().set_sensitive (can_remove);
menu->popup (e->button, e->time);
return true;
}
void
IOSelector::add_port_button_clicked ()
IOSelector::add_port ()
{
/* add a new port, then hide the button if we're up to the maximum allowed */
// The IO selector only works for single typed IOs
const ARDOUR::DataType t = _io->default_type();
const ARDOUR::DataType t = _io->default_type ();
if (_for_input) {
@ -528,95 +606,30 @@ IOSelector::add_port_button_clicked ()
msg.run ();
}
}
set_button_sensitivity ();
}
void
IOSelector::remove_port_button_clicked ()
IOSelector::remove_port (int r)
{
uint32_t nports;
// The IO selector only works for single typed IOs
const ARDOUR::DataType t = _io->default_type();
// always remove last port
const ARDOUR::DataType t = _io->default_type ();
if (_for_input) {
if ((nports = _io->n_inputs().get(t)) > 0) {
_io->remove_input_port (_io->input(nports - 1), this);
}
_io->remove_input_port (_io->input (r), this);
} else {
if ((nports = _io->n_outputs().get(t)) > 0) {
_io->remove_output_port (_io->output(nports - 1), this);
}
}
set_button_sensitivity ();
}
void
IOSelector::set_button_sensitivity ()
{
ARDOUR::DataType const t = _io->default_type();
if (_for_input) {
_add_port_button.set_sensitive (
_io->input_maximum().get(t) > _io->n_inputs().get(t)
);
} else {
_add_port_button.set_sensitive (
_io->output_maximum().get(t) > _io->n_outputs().get(t)
);
}
if (_for_input) {
_remove_port_button.set_sensitive (
_io->n_inputs().get(t) && _io->input_minimum().get(t) < _io->n_inputs().get(t)
);
} else {
_remove_port_button.set_sensitive (
_io->n_outputs().get(t) && _io->output_minimum().get(t) < _io->n_outputs().get(t)
);
_io->remove_output_port (_io->output (r), this);
}
}
void
IOSelector::ports_changed (ARDOUR::IOChange change, void *src)
{
ENSURE_GUI_THREAD (bind (mem_fun (*this, &IOSelector::ports_changed), change, src));
redisplay ();
}
void
IOSelector::redisplay ()
{
_port_list.refresh ();
setup_table ();
setup_row_labels ();
setup_check_button_states ();
update_column_label_dimensions ();
}
GroupedPortList::GroupedPortList (ARDOUR::Session & session, boost::shared_ptr<ARDOUR::IO> io, bool for_input)
PortGroupList::PortGroupList (ARDOUR::Session & session, boost::shared_ptr<ARDOUR::IO> io, bool for_input)
: _session (session), _io (io), _for_input (for_input)
{
refresh ();
}
void
GroupedPortList::refresh ()
PortGroupList::refresh ()
{
clear ();
@ -625,8 +638,8 @@ GroupedPortList::refresh ()
boost::shared_ptr<ARDOUR::Session::RouteList> routes = _session.get_routes ();
PortGroup buss (_("Buss"), "ardour:");
PortGroup track (_("Track"), "ardour:");
PortGroup buss ("ardour:");
PortGroup track ("ardour:");
for (ARDOUR::Session::RouteList::const_iterator i = routes->begin(); i != routes->end(); ++i) {
@ -661,8 +674,8 @@ GroupedPortList::refresh ()
"", _io->default_type().to_jack_type(), _for_input ? JackPortIsOutput : JackPortIsInput
);
PortGroup system (_("System"), "system:");
PortGroup other (_("Other"), "");
PortGroup system ("system:");
PortGroup other ("");
if (ports) {
@ -691,7 +704,7 @@ GroupedPortList::refresh ()
}
int
GroupedPortList::n_ports () const
PortGroupList::n_ports () const
{
int n = 0;
@ -705,7 +718,7 @@ GroupedPortList::n_ports () const
}
std::string
GroupedPortList::get_port_by_index (int n, bool with_prefix) const
PortGroupList::get_port_by_index (int n, bool with_prefix) const
{
/* XXX: slightly inefficient algorithm */

View File

@ -33,22 +33,44 @@ namespace ARDOUR {
class PortInsert;
}
/// A group of port names
class PortGroup
{
public:
PortGroup (std::string const & n, std::string const & p) : name (n), prefix (p) {}
PortGroup (std::string const & p) : prefix (p) {}
void add (std::string const & p);
std::string name;
std::string prefix;
std::vector<std::string> ports;
std::string prefix; ///< prefix (before colon) e.g. "ardour:"
std::vector<std::string> ports; ///< port names
};
class GroupedPortList : public std::list<PortGroup>
/// A table of checkbuttons to provide the GUI for connecting to a PortGroup
class PortGroupTable
{
public:
GroupedPortList (ARDOUR::Session &, boost::shared_ptr<ARDOUR::IO>, bool);
PortGroupTable (PortGroup&, boost::shared_ptr<ARDOUR::IO>, bool);
Gtk::Widget& get_widget ();
std::pair<int, int> unit_size () const;
private:
void check_button_toggled (Gtk::CheckButton*, int, std::string const &);
Gtk::Table _table;
Gtk::EventBox _box;
PortGroup& _port_group;
std::vector<std::vector<Gtk::CheckButton* > > _check_buttons;
bool _ignore_check_button_toggle;
boost::shared_ptr<ARDOUR::IO> _io;
bool _for_input;
};
/// A list of PortGroups
class PortGroupList : public std::list<PortGroup>
{
public:
PortGroupList (ARDOUR::Session &, boost::shared_ptr<ARDOUR::IO>, bool);
void refresh ();
int n_ports () const;
@ -64,11 +86,11 @@ class GroupedPortList : public std::list<PortGroup>
/// A widget which provides a set of rotated text labels
class RotatedLabelSet : public Gtk::Widget {
public:
RotatedLabelSet (GroupedPortList&);
RotatedLabelSet (PortGroupList&);
virtual ~RotatedLabelSet ();
void set_angle (int);
void set_base_dimensions (int, int);
void set_base_width (int);
protected:
virtual void on_size_request (Gtk::Requisition*);
@ -82,11 +104,10 @@ class RotatedLabelSet : public Gtk::Widget {
private:
std::pair<int, int> setup_layout (std::string const &);
GroupedPortList& _port_list; ///< list of ports to display
PortGroupList& _port_group_list; ///< list of ports to display
int _angle_degrees; ///< label rotation angle in degrees
double _angle_radians; ///< label rotation angle in radians
int _base_start; ///< offset to start of labels; see set_base_dimensions() for more details
int _base_width; ///< width of labels; see set_base_dimensions() for more details
int _base_width; ///< width of labels; see set_base_width() for more details
Glib::RefPtr<Pango::Context> _pango_context;
Glib::RefPtr<Pango::Layout> _pango_layout;
Glib::RefPtr<Gdk::GC> _gc;
@ -111,37 +132,27 @@ class IOSelector : public Gtk::VBox {
sigc::signal<void, Result> Finished;
protected:
ARDOUR::Session& _session;
private:
void setup_table ();
void setup_row_labels ();
void setup_check_button_states ();
void check_button_toggled (int, int);
void add_port_button_clicked ();
void remove_port_button_clicked ();
void set_button_sensitivity ();
void ports_changed (ARDOUR::IOChange, void *);
void update_column_label_dimensions ();
void setup ();
void clear ();
void setup_dimensions ();
void ports_changed (ARDOUR::IOChange, void*);
bool row_label_button_pressed (GdkEventButton*, int);
void add_port ();
void remove_port (int);
GroupedPortList _port_list;
PortGroupList _port_group_list;
boost::shared_ptr<ARDOUR::IO> _io;
bool _for_input;
int _width;
int _height;
std::vector<Gtk::Label*> _row_labels;
std::vector<Gtk::EventBox*> _group_labels;
std::vector<PortGroupTable*> _port_group_tables;
std::vector<Gtk::EventBox*> _row_labels;
Gtk::VBox* _row_labels_vbox;
RotatedLabelSet _column_labels;
std::vector<std::vector<Gtk::CheckButton*> > _check_buttons;
bool _ignore_check_button_toggle; ///< check button toggle events are ignored when this is true
Gtk::Button _add_port_button;
Gtk::Button _remove_port_button;
Gtk::VBox _add_remove_box;
Gtk::HBox _table_hbox;
Gtk::Label _dummy;
bool _add_remove_box_added;
Gtk::Table _table;
Gtk::HBox _overall_hbox;
Gtk::VBox _left_vbox;
Gtk::HBox _port_group_hbox;
Gtk::ScrolledWindow _scrolled_window;
Gtk::Label* _left_vbox_pad;
};