13
0

Group destination ports by track / buss / system etc.

git-svn-id: svn://localhost/ardour2/trunk@2545 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Carl Hetherington 2007-10-12 12:13:11 +00:00
parent 40d8d96785
commit a57a62b925
2 changed files with 214 additions and 91 deletions

View File

@ -28,13 +28,14 @@
#include "ardour/session.h"
#include "ardour/io.h"
#include "ardour/audioengine.h"
#include "ardour/track.h"
#include "io_selector.h"
#include "utils.h"
#include "gui_thread.h"
#include "i18n.h"
RotatedLabelSet::RotatedLabelSet ()
: Glib::ObjectBase ("RotatedLabelSet"), Gtk::Widget (), _base_start (64), _base_width (128)
RotatedLabelSet::RotatedLabelSet (GroupedPortList& g)
: Glib::ObjectBase ("RotatedLabelSet"), Gtk::Widget (), _port_list (g), _base_start (64), _base_width (128)
{
set_flags (Gtk::NO_WINDOW);
set_angle (30);
@ -45,6 +46,11 @@ RotatedLabelSet::~RotatedLabelSet ()
}
/** Set the angle that the labels are drawn at.
* @param degrees New angle in degrees.
*/
void
RotatedLabelSet::set_angle (int degrees)
{
@ -65,17 +71,20 @@ RotatedLabelSet::on_size_request (Gtk::Requisition* requisition)
/* Our height is the highest label */
requisition->height = 0;
for (std::vector<std::string>::const_iterator i = _labels.begin(); i != _labels.end(); ++i) {
std::pair<int, int> const d = setup_layout (*i);
if (d.second > requisition->height) {
requisition->height = d.second;
for (GroupedPortList::const_iterator i = _port_list.begin(); i != _port_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) {
requisition->height = d.second;
}
}
}
/* And our width is the base plus the width of the last label */
requisition->width = _base_start + _base_width;
if (_labels.empty() == false) {
std::pair<int, int> const d = setup_layout (_labels.back());
int const n = _port_list.n_ports ();
if (n > 0) {
std::pair<int, int> const d = setup_layout (_port_list.get_port_by_index (n - 1, false));
requisition->width += d.first;
}
}
@ -169,44 +178,29 @@ RotatedLabelSet::setup_layout (std::string const & s)
return d;
}
bool RotatedLabelSet::on_expose_event (GdkEventExpose* event)
bool
RotatedLabelSet::on_expose_event (GdkEventExpose* event)
{
if (!_gdk_window) {
return true;
}
int const height = get_allocation().get_height ();
double const spacing = double (_base_width) / _labels.size();
double const spacing = double (_base_width) / _port_list.n_ports();
/* Plot all the labels; really we should clip for efficiency */
for (uint32_t i = 0; i < _labels.size(); ++i) {
std::pair<int, int> const d = setup_layout (_labels[i]);
get_window()->draw_layout (_gc, _base_start + int ((i + 0.25) * spacing), height - d.second, _pango_layout, _fg_colour, _bg_colour);
int n = 0;
for (GroupedPortList::const_iterator i = _port_list.begin(); i != _port_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);
++n;
}
}
return true;
}
void
RotatedLabelSet::set_n_labels (int n)
{
_labels.resize (n);
queue_resize ();
}
void
RotatedLabelSet::set_label (int n, std::string const & l)
{
_labels[n] = l;
queue_resize ();
}
std::string
RotatedLabelSet::get_label (int n) const
{
return _labels[n];
}
/**
* 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.
@ -238,7 +232,8 @@ RotatedLabelSet::set_base_dimensions (int s, int w)
*/
IOSelector::IOSelector (ARDOUR::Session& session, boost::shared_ptr<ARDOUR::IO> io, bool for_input)
: _session (session), _io (io), _for_input (for_input), _width (0), _height (0),
: _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)
{
/* Column labels */
@ -257,9 +252,9 @@ IOSelector::IOSelector (ARDOUR::Session& session, boost::shared_ptr<ARDOUR::IO>
/* 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_size ();
setup_table ();
setup_row_labels ();
setup_column_labels ();
setup_check_button_states ();
_table_hbox.pack_start (_table, false, false);
_table_hbox.pack_start (_dummy);
@ -313,26 +308,23 @@ IOSelector::update_column_label_dimensions ()
}
void
IOSelector::setup_table_size ()
IOSelector::setup_table ()
{
if (_add_remove_box_added) {
_table.remove (_add_remove_box);
}
for (std::vector<Gtk::EventBox*>::iterator i = _group_labels.begin(); i != _group_labels.end(); ++i) {
_table.remove (**i);
delete *i;
}
_group_labels.clear ();
/* New width */
int const old_width = _width;
_width = 0;
const char **ports = _session.engine().get_ports (
"", _io->default_type().to_jack_type(), _for_input ? JackPortIsOutput : JackPortIsInput
);
if (ports) {
while (ports[_width]) {
++_width;
}
}
_width = _port_list.n_ports ();
/* New height */
@ -348,7 +340,7 @@ IOSelector::setup_table_size ()
_table.resize (_width + 1, _height + 1);
/* Add checkboxes where required, and remove those that aren't */
/* 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];
@ -390,6 +382,29 @@ IOSelector::setup_table_size ()
_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;
}
}
show_all ();
}
@ -406,34 +421,6 @@ IOSelector::setup_row_labels ()
}
}
/**
* Write the correct text to the column labels.
*/
void
IOSelector::setup_column_labels ()
{
/* Find the ports that we can connect to */
const char **ports = _session.engine().get_ports (
"", _io->default_type().to_jack_type(), _for_input ? JackPortIsOutput : JackPortIsInput
);
if (ports == 0) {
return;
}
int n = 0;
while (ports[n]) {
++n;
}
_column_labels.set_n_labels (n);
for (int i = 0; i < n; ++i) {
_column_labels.set_label (i, ports[i]);
}
}
/**
* Set up the state of each check button according to what connections are made.
@ -449,7 +436,7 @@ IOSelector::setup_check_button_states ()
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 = _column_labels.get_label (j);
std::string const t = _port_list.get_port_by_index (j);
int k = 0;
bool required_state = false;
@ -481,7 +468,7 @@ IOSelector::check_button_toggled (int x, int y)
}
bool const new_state = _check_buttons[x][y]->get_active ();
std::string const port = _column_labels.get_label (x);
std::string const port = _port_list.get_port_by_index (x);
if (new_state) {
if (_for_input) {
@ -599,8 +586,8 @@ IOSelector::ports_changed (ARDOUR::IOChange change, void *src)
void
IOSelector::redisplay ()
{
setup_table_size ();
setup_column_labels ();
_port_list.refresh ();
setup_table ();
setup_row_labels ();
setup_check_button_states ();
update_column_label_dimensions ();
@ -608,6 +595,116 @@ IOSelector::redisplay ()
GroupedPortList::GroupedPortList (ARDOUR::Session & session, boost::shared_ptr<ARDOUR::IO> io, bool for_input)
: _session (session), _io (io), _for_input (for_input)
{
refresh ();
}
void
GroupedPortList::refresh ()
{
clear ();
/* Find the ports provided by ardour; we can't derive their type just from their
names, so we'll have to be more devious. */
boost::shared_ptr<ARDOUR::Session::RouteList> routes = _session.get_routes ();
PortGroup buss (_("Buss"), "ardour:");
PortGroup track (_("Track"), "ardour:");
for (ARDOUR::Session::RouteList::const_iterator i = routes->begin(); i != routes->end(); ++i) {
PortGroup& g = dynamic_cast<ARDOUR::Track*> ((*i).get()) == 0 ? buss : track;
ARDOUR::PortSet const & p = _for_input ? ((*i)->outputs()) : ((*i)->inputs());
for (uint32_t j = 0; j < p.num_ports(); ++j) {
std::string const n = p.port(j)->name ();
g.ports.push_back (n.substr(strlen ("ardour:")));
}
std::sort (g.ports.begin(), g.ports.end());
}
/* XXX: inserts, sends, plugin inserts? */
/* Now we need to find the non-ardour ports; we do this by first
finding all the ports that we can connect to. */
const char **ports = _session.engine().get_ports (
"", _io->default_type().to_jack_type(), _for_input ? JackPortIsOutput : JackPortIsInput
);
PortGroup system (_("System"), "system:");
PortGroup other (_("Other"), "");
if (ports) {
/* Count them */
int n = 0;
while (ports[n]) {
++n;
}
for (int i = 0; i < n; ++i) {
std::string const p = ports[i];
if (p.substr(0, strlen ("system:")) == "system:") {
/* system: prefix */
system.ports.push_back (p.substr (strlen ("system:")));
} else {
if (p.substr(0, strlen("ardour:")) != "ardour:") {
/* other (non-ardour) prefix */
other.ports.push_back (p);
}
}
}
}
push_back (buss);
push_back (track);
push_back (system);
push_back (other);
}
int
GroupedPortList::n_ports () const
{
int n = 0;
for (const_iterator i = begin(); i != end(); ++i) {
for (std::vector<std::string>::const_iterator j = i->ports.begin(); j != i->ports.end(); ++j) {
++n;
}
}
return n;
}
std::string
GroupedPortList::get_port_by_index (int n, bool with_prefix) const
{
/* XXX: slightly inefficient algorithm */
for (const_iterator i = begin(); i != end(); ++i) {
for (std::vector<std::string>::const_iterator j = i->ports.begin(); j != i->ports.end(); ++j) {
if (n == 0) {
if (with_prefix) {
return i->prefix + *j;
} else {
return *j;
}
}
--n;
}
}
return "";
}
IOSelectorWindow::IOSelectorWindow (
ARDOUR::Session& session, boost::shared_ptr<ARDOUR::IO> io, bool for_input, bool can_cancel
)

View File

@ -33,15 +33,38 @@ namespace ARDOUR {
class PortInsert;
}
struct PortGroup
{
PortGroup (std::string const & n, std::string const & p) : name (n), prefix (p) {}
std::string name;
std::string prefix;
std::vector<std::string> ports;
};
class GroupedPortList : public std::list<PortGroup>
{
public:
GroupedPortList (ARDOUR::Session &, boost::shared_ptr<ARDOUR::IO>, bool);
void refresh ();
int n_ports () const;
std::string get_port_by_index (int, bool with_prefix = true) const;
private:
ARDOUR::Session& _session;
boost::shared_ptr<ARDOUR::IO> _io;
bool _for_input;
};
/// A widget which provides a set of rotated text labels
class RotatedLabelSet : public Gtk::Widget {
public:
RotatedLabelSet ();
RotatedLabelSet (GroupedPortList&);
virtual ~RotatedLabelSet ();
void set_angle (int);
void set_n_labels (int);
void set_label (int, std::string const &);
std::string get_label (int) const;
void set_base_dimensions (int, int);
protected:
@ -55,8 +78,8 @@ class RotatedLabelSet : public Gtk::Widget {
private:
std::pair<int, int> setup_layout (std::string const &);
std::vector<std::string> _labels; ///< text for our labels
GroupedPortList& _port_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
@ -68,6 +91,9 @@ class RotatedLabelSet : public Gtk::Widget {
Gdk::Color _bg_colour;
};
/// Widget for selecting what an IO is connected to
class IOSelector : public Gtk::VBox {
public:
IOSelector (ARDOUR::Session&, boost::shared_ptr<ARDOUR::IO>, bool);
@ -86,9 +112,8 @@ class IOSelector : public Gtk::VBox {
ARDOUR::Session& _session;
private:
void setup_table_size ();
void setup_table ();
void setup_row_labels ();
void setup_column_labels ();
void setup_check_button_states ();
void check_button_toggled (int, int);
void add_port_button_clicked ();
@ -96,12 +121,14 @@ class IOSelector : public Gtk::VBox {
void set_button_sensitivity ();
void ports_changed (ARDOUR::IOChange, void *);
void update_column_label_dimensions ();
GroupedPortList _port_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;
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
@ -111,7 +138,6 @@ class IOSelector : public Gtk::VBox {
Gtk::HBox _table_hbox;
Gtk::Label _dummy;
bool _add_remove_box_added;
Gtk::Table _table;
};