New matrix-based editor for connections and bundles, based on thorwil's design.

Add Bundle Manager dialog.


git-svn-id: svn://localhost/ardour2/branches/3.0@4415 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Carl Hetherington 2009-01-20 14:46:00 +00:00
parent ef038c1a84
commit 61db2175eb
32 changed files with 2165 additions and 1201 deletions

View file

@ -196,7 +196,6 @@ lineset.cc
location_ui.cc location_ui.cc
main.cc main.cc
marker.cc marker.cc
matrix.cc
midi_channel_selector.cc midi_channel_selector.cc
midi_port_dialog.cc midi_port_dialog.cc
midi_region_view.cc midi_region_view.cc
@ -216,8 +215,13 @@ piano_roll_header.cc
playlist_selector.cc playlist_selector.cc
plugin_selector.cc plugin_selector.cc
plugin_ui.cc plugin_ui.cc
port_matrix.cc
port_group.cc port_group.cc
port_matrix.cc
port_matrix_body.cc
port_matrix_column_labels.cc
port_matrix_component.cc
port_matrix_grid.cc
port_matrix_row_labels.cc
processor_box.cc processor_box.cc
prompter.cc prompter.cc
public_editor.cc public_editor.cc

View file

@ -423,6 +423,7 @@
<menuitem action='ToggleThemeManager'/> <menuitem action='ToggleThemeManager'/>
<menuitem action='ToggleBigClock'/> <menuitem action='ToggleBigClock'/>
<menuitem action='toggle-rhythm-ferret'/> <menuitem action='toggle-rhythm-ferret'/>
<menuitem action='toggle-bundle-manager'/>
<separator/> <separator/>
</menu> </menu>
<menu name='Options' action='Options'> <menu name='Options' action='Options'>

View file

@ -38,71 +38,79 @@ BundleEditorMatrix::BundleEditorMatrix (
PortGroupList::Mask (PortGroupList::SYSTEM | PortGroupList::OTHER) PortGroupList::Mask (PortGroupList::SYSTEM | PortGroupList::OTHER)
) )
{ {
_bundle = boost::dynamic_pointer_cast<ARDOUR::UserBundle> (bundle); _our_bundle = bundle;
assert (_bundle != 0);
} }
void void
BundleEditorMatrix::set_state (int r, std::string const & p, bool s, uint32_t keymod) BundleEditorMatrix::set_state (
boost::shared_ptr<ARDOUR::Bundle> ab,
uint32_t ac,
boost::shared_ptr<ARDOUR::Bundle> bb,
uint32_t bc,
bool s,
uint32_t k
)
{ {
if (s) { ARDOUR::Bundle::PortList const& pl = bb->channel_ports (bc);
_bundle->add_port_to_channel (r, p); for (ARDOUR::Bundle::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
} else { if (s) {
_bundle->remove_port_from_channel (r, p); ab->add_port_to_channel (ac, *i);
} else {
ab->remove_port_from_channel (ac, *i);
}
} }
} }
bool bool
BundleEditorMatrix::get_state (int r, std::string const & p) const BundleEditorMatrix::get_state (
boost::shared_ptr<ARDOUR::Bundle> ab,
uint32_t ac,
boost::shared_ptr<ARDOUR::Bundle> bb,
uint32_t bc
) const
{ {
return _bundle->port_attached_to_channel (r, p); ARDOUR::Bundle::PortList const& pl = bb->channel_ports (bc);
} for (ARDOUR::Bundle::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
if (!ab->port_attached_to_channel (ac, *i)) {
return false;
}
}
uint32_t return true;
BundleEditorMatrix::n_rows () const
{
return _bundle->nchannels ();
}
uint32_t
BundleEditorMatrix::maximum_rows () const
{
/* 65536 channels in a bundle ought to be enough for anyone (TM) */
return 65536;
}
uint32_t
BundleEditorMatrix::minimum_rows () const
{
return 0;
}
std::string
BundleEditorMatrix::row_name (int r) const
{
std::stringstream s;
s << r + 1; // 1-based counting
return s.str();
} }
void void
BundleEditorMatrix::add_row () BundleEditorMatrix::add_channel (boost::shared_ptr<ARDOUR::Bundle> b)
{ {
_bundle->add_channel (); NameChannelDialog d;
d.set_position (Gtk::WIN_POS_MOUSE);
if (d.run () != Gtk::RESPONSE_ACCEPT) {
return;
}
_our_bundle->add_channel (d.get_name());
setup (); setup ();
} }
void void
BundleEditorMatrix::remove_row (int r) BundleEditorMatrix::remove_channel (boost::shared_ptr<ARDOUR::Bundle> b, uint32_t c)
{ {
_bundle->remove_channel (r); _our_bundle->remove_channel (c);
setup (); setup ();
} }
std::string void
BundleEditorMatrix::row_descriptor () const BundleEditorMatrix::rename_channel (boost::shared_ptr<ARDOUR::Bundle> b, uint32_t c)
{ {
return _("channel"); NameChannelDialog d (b, c);
d.set_position (Gtk::WIN_POS_MOUSE);
if (d.run () != Gtk::RESPONSE_ACCEPT) {
return;
}
b->set_channel_name (c, d.get_name ());
} }
BundleEditor::BundleEditor (ARDOUR::Session& session, boost::shared_ptr<ARDOUR::UserBundle> bundle, bool add) BundleEditor::BundleEditor (ARDOUR::Session& session, boost::shared_ptr<ARDOUR::UserBundle> bundle, bool add)
@ -111,21 +119,21 @@ BundleEditor::BundleEditor (ARDOUR::Session& session, boost::shared_ptr<ARDOUR::
Gtk::Table* t = new Gtk::Table (3, 2); Gtk::Table* t = new Gtk::Table (3, 2);
t->set_spacings (4); t->set_spacings (4);
/* Bundle name */
Gtk::Alignment* a = new Gtk::Alignment (1, 0.5, 0, 1); Gtk::Alignment* a = new Gtk::Alignment (1, 0.5, 0, 1);
a->add (*Gtk::manage (new Gtk::Label (_("Name:")))); a->add (*Gtk::manage (new Gtk::Label (_("Name:"))));
t->attach (*Gtk::manage (a), 0, 1, 0, 1, Gtk::FILL, Gtk::FILL); t->attach (*Gtk::manage (a), 0, 1, 0, 1, Gtk::FILL, Gtk::FILL);
t->attach (_name, 1, 2, 0, 1); t->attach (_name, 1, 2, 0, 1);
_name.set_text (_bundle->name ()); _name.set_text (_bundle->name ());
_name.signal_changed().connect (sigc::mem_fun (*this, &BundleEditor::name_changed)); _name.signal_changed().connect (sigc::mem_fun (*this, &BundleEditor::name_changed));
/* Direction (input or output) */
a = new Gtk::Alignment (1, 0.5, 0, 1); a = new Gtk::Alignment (1, 0.5, 0, 1);
a->add (*Gtk::manage (new Gtk::Label (_("Direction:")))); a->add (*Gtk::manage (new Gtk::Label (_("Direction:"))));
t->attach (*Gtk::manage (a), 0, 1, 1, 2, Gtk::FILL, Gtk::FILL); t->attach (*Gtk::manage (a), 0, 1, 1, 2, Gtk::FILL, Gtk::FILL);
a = new Gtk::Alignment (0, 0.5, 0, 1); a = new Gtk::Alignment (0, 0.5, 0, 1);
a->add (_input_or_output); a->add (_input_or_output);
t->attach (*Gtk::manage (a), 1, 2, 1, 2); t->attach (*Gtk::manage (a), 1, 2, 1, 2);
_input_or_output.append_text (_("Input")); _input_or_output.append_text (_("Input"));
_input_or_output.append_text (_("Output")); _input_or_output.append_text (_("Output"));
@ -137,6 +145,7 @@ BundleEditor::BundleEditor (ARDOUR::Session& session, boost::shared_ptr<ARDOUR::
_input_or_output.signal_changed().connect (sigc::mem_fun (*this, &BundleEditor::input_or_output_changed)); _input_or_output.signal_changed().connect (sigc::mem_fun (*this, &BundleEditor::input_or_output_changed));
/* Type (audio or MIDI) */
a = new Gtk::Alignment (1, 0.5, 0, 1); a = new Gtk::Alignment (1, 0.5, 0, 1);
a->add (*Gtk::manage (new Gtk::Label (_("Type:")))); a->add (*Gtk::manage (new Gtk::Label (_("Type:"))));
t->attach (*Gtk::manage (a), 0, 1, 2, 3, Gtk::FILL, Gtk::FILL); t->attach (*Gtk::manage (a), 0, 1, 2, 3, Gtk::FILL, Gtk::FILL);
@ -159,11 +168,16 @@ BundleEditor::BundleEditor (ARDOUR::Session& session, boost::shared_ptr<ARDOUR::
_type.signal_changed().connect (sigc::mem_fun (*this, &BundleEditor::type_changed)); _type.signal_changed().connect (sigc::mem_fun (*this, &BundleEditor::type_changed));
get_vbox()->pack_start (*Gtk::manage (t), false, false); get_vbox()->pack_start (*Gtk::manage (t), false, false);
get_vbox()->pack_start (_matrix); get_vbox()->pack_start (_matrix);
get_vbox()->set_spacing (4); get_vbox()->set_spacing (4);
/* Add Channel button */
Gtk::Button* add_channel_button = Gtk::manage (new Gtk::Button (_("Add Channel")));
add_channel_button->set_name ("IOSelectorButton");
add_channel_button->set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::ADD, Gtk::ICON_SIZE_BUTTON)));
get_action_area()->pack_start (*add_channel_button, false, false);
add_channel_button->signal_clicked().connect (sigc::bind (sigc::mem_fun (_matrix, &BundleEditorMatrix::add_channel), boost::shared_ptr<ARDOUR::Bundle> ()));
if (add) { if (add) {
add_button (Gtk::Stock::CANCEL, 1); add_button (Gtk::Stock::CANCEL, 1);
add_button (Gtk::Stock::ADD, 0); add_button (Gtk::Stock::ADD, 0);
@ -269,7 +283,7 @@ BundleManager::new_clicked ()
boost::shared_ptr<ARDOUR::UserBundle> b (new ARDOUR::UserBundle ("")); boost::shared_ptr<ARDOUR::UserBundle> b (new ARDOUR::UserBundle (""));
/* Start off with a single channel */ /* Start off with a single channel */
b->add_channel (); b->add_channel ("");
BundleEditor e (_session, b, true); BundleEditor e (_session, b, true);
if (e.run () == 0) { if (e.run () == 0) {
@ -333,3 +347,47 @@ BundleManager::bundle_name_changed (boost::shared_ptr<ARDOUR::UserBundle> b)
} }
} }
NameChannelDialog::NameChannelDialog ()
: ArdourDialog (_("Add channel")),
_adding (true)
{
setup ();
}
NameChannelDialog::NameChannelDialog (boost::shared_ptr<ARDOUR::Bundle> b, uint32_t c)
: ArdourDialog (_("Rename channel")),
_bundle (b),
_channel (c),
_adding (false)
{
_name.set_text (b->channel_name (c));
setup ();
}
void
NameChannelDialog::setup ()
{
Gtk::HBox* box = Gtk::manage (new Gtk::HBox ());
box->pack_start (*Gtk::manage (new Gtk::Label (_("Name"))));
box->pack_start (_name);
get_vbox ()->pack_end (*box);
box->show_all ();
add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
if (_adding) {
add_button (Gtk::Stock::ADD, Gtk::RESPONSE_ACCEPT);
} else {
add_button (Gtk::Stock::APPLY, Gtk::RESPONSE_ACCEPT);
}
set_default_response (Gtk::RESPONSE_ACCEPT);
}
std::string
NameChannelDialog::get_name () const
{
return _name.get_text ();
}

View file

@ -22,6 +22,7 @@
#include <gtkmm/treeview.h> #include <gtkmm/treeview.h>
#include <gtkmm/liststore.h> #include <gtkmm/liststore.h>
#include <gtkmm/entry.h>
#include "ardour_dialog.h" #include "ardour_dialog.h"
#include "port_matrix.h" #include "port_matrix.h"
@ -35,19 +36,28 @@ class BundleEditorMatrix : public PortMatrix
public: public:
BundleEditorMatrix (ARDOUR::Session &, boost::shared_ptr<ARDOUR::Bundle>); BundleEditorMatrix (ARDOUR::Session &, boost::shared_ptr<ARDOUR::Bundle>);
void set_state (int, std::string const &, bool, uint32_t); void set_state (
bool get_state (int, std::string const &) const; boost::shared_ptr<ARDOUR::Bundle> ab,
uint32_t n_rows () const; uint32_t ac,
uint32_t maximum_rows () const; boost::shared_ptr<ARDOUR::Bundle> bb,
uint32_t minimum_rows () const; uint32_t bc,
std::string row_name (int) const; bool s,
void add_row (); uint32_t k
void remove_row (int); );
std::string row_descriptor () const;
bool get_state (
boost::shared_ptr<ARDOUR::Bundle> ab,
uint32_t ac,
boost::shared_ptr<ARDOUR::Bundle> bb,
uint32_t bc
) const;
private: void add_channel (boost::shared_ptr<ARDOUR::Bundle>);
void remove_channel (boost::shared_ptr<ARDOUR::Bundle>, uint32_t);
boost::shared_ptr<ARDOUR::UserBundle> _bundle; bool can_rename_channels () const {
return true;
}
void rename_channel (boost::shared_ptr<ARDOUR::Bundle>, uint32_t);
}; };
class BundleEditor : public ArdourDialog class BundleEditor : public ArdourDialog
@ -104,4 +114,22 @@ class BundleManager : public ArdourDialog
Gtk::Button delete_button; Gtk::Button delete_button;
}; };
class NameChannelDialog : public ArdourDialog
{
public:
NameChannelDialog ();
NameChannelDialog (boost::shared_ptr<ARDOUR::Bundle>, uint32_t);
std::string get_name () const;
private:
void setup ();
boost::shared_ptr<ARDOUR::Bundle> _bundle;
uint32_t _channel;
Gtk::Entry _name;
bool _adding;
};
#endif #endif

View file

@ -87,6 +87,7 @@
#include "actions.h" #include "actions.h"
#include "tempo_lines.h" #include "tempo_lines.h"
#include "analysis_window.h" #include "analysis_window.h"
#include "bundle_manager.h"
#include "i18n.h" #include "i18n.h"
@ -347,6 +348,7 @@ Editor::Editor ()
select_new_marker = false; select_new_marker = false;
zoomed_to_region = false; zoomed_to_region = false;
rhythm_ferret = 0; rhythm_ferret = 0;
_bundle_manager = 0;
allow_vertical_scroll = false; allow_vertical_scroll = false;
no_save_visual = false; no_save_visual = false;
need_resize_line = false; need_resize_line = false;
@ -5127,6 +5129,16 @@ Editor::show_rhythm_ferret ()
rhythm_ferret->present (); rhythm_ferret->present ();
} }
void
Editor::show_bundle_manager ()
{
if (_bundle_manager == 0) {
_bundle_manager = new BundleManager (*session);
}
_bundle_manager->show ();
}
void void
Editor::first_idle () Editor::first_idle ()
{ {

View file

@ -110,6 +110,7 @@ class ControlPoint;
class SoundFileOmega; class SoundFileOmega;
class RhythmFerret; class RhythmFerret;
class AnalysisWindow; class AnalysisWindow;
class BundleManager;
/* <CMT Additions> */ /* <CMT Additions> */
class ImageFrameView; class ImageFrameView;
@ -394,6 +395,7 @@ class Editor : public PublicEditor
void toggle_meter_updating(); void toggle_meter_updating();
void show_rhythm_ferret(); void show_rhythm_ferret();
void show_bundle_manager ();
void goto_visual_state (uint32_t); void goto_visual_state (uint32_t);
void save_visual_state (uint32_t); void save_visual_state (uint32_t);
@ -2347,6 +2349,7 @@ public:
void snap_to_internal (nframes64_t& first, int32_t direction = 0, bool for_mark = false); void snap_to_internal (nframes64_t& first, int32_t direction = 0, bool for_mark = false);
RhythmFerret* rhythm_ferret; RhythmFerret* rhythm_ferret;
BundleManager* _bundle_manager;
void fit_tracks (); void fit_tracks ();
void set_track_height (uint32_t h); void set_track_height (uint32_t h);

View file

@ -563,6 +563,7 @@ Editor::register_actions ()
ActionManager::region_selection_sensitive_actions.push_back (act); ActionManager::region_selection_sensitive_actions.push_back (act);
act = ActionManager::register_action (editor_actions, "toggle-rhythm-ferret", _("Rhythm Ferret"), mem_fun(*this, &Editor::show_rhythm_ferret)); act = ActionManager::register_action (editor_actions, "toggle-rhythm-ferret", _("Rhythm Ferret"), mem_fun(*this, &Editor::show_rhythm_ferret));
ActionManager::session_sensitive_actions.push_back (act); ActionManager::session_sensitive_actions.push_back (act);
act = ActionManager::register_action (editor_actions, "toggle-bundle-manager", _("Bundle Manager"), mem_fun (*this, &Editor::show_bundle_manager));
act = ActionManager::register_action (editor_actions, "tab-to-transient-forwards", _("Move Forward to Transient"), bind (mem_fun(*this, &Editor::tab_to_transient), true)); act = ActionManager::register_action (editor_actions, "tab-to-transient-forwards", _("Move Forward to Transient"), bind (mem_fun(*this, &Editor::tab_to_transient), true));
ActionManager::session_sensitive_actions.push_back (act); ActionManager::session_sensitive_actions.push_back (act);

View file

@ -30,6 +30,7 @@
#include "ardour/audio_track.h" #include "ardour/audio_track.h"
#include "ardour/midi_track.h" #include "ardour/midi_track.h"
#include "ardour/data_type.h" #include "ardour/data_type.h"
#include "ardour/bundle.h"
#include "io_selector.h" #include "io_selector.h"
#include "utils.h" #include "utils.h"
@ -44,80 +45,130 @@ IOSelector::IOSelector (ARDOUR::Session& session, boost::shared_ptr<ARDOUR::IO>
PortGroupList::Mask (PortGroupList::BUSS | PortGroupList::Mask (PortGroupList::BUSS |
PortGroupList::SYSTEM | PortGroupList::SYSTEM |
PortGroupList::OTHER)) PortGroupList::OTHER))
, _session (session)
, _io (io) , _io (io)
{ {
list<string> our_ports;
/* Listen for ports changing on the IO */ /* Listen for ports changing on the IO */
if (_offer_inputs) { _io->PortCountChanged.connect (sigc::hide (mem_fun (*this, &IOSelector::ports_changed)));
_io->output_changed.connect (mem_fun(*this, &IOSelector::ports_changed));
setup ();
}
void
IOSelector::setup ()
{
_our_bundle = boost::shared_ptr<ARDOUR::Bundle> (new ARDOUR::Bundle);
_our_bundle->set_name (_io->name());
if (offering_input ()) {
const PortSet& ps (_io->outputs()); const PortSet& ps (_io->outputs());
int j = 0;
for (PortSet::const_iterator i = ps.begin(); i != ps.end(); ++i) { for (PortSet::const_iterator i = ps.begin(); i != ps.end(); ++i) {
our_ports.push_back (i->name()); char buf[32];
snprintf (buf, sizeof(buf), _("out %d"), j + 1);
_our_bundle->add_channel (buf);
_our_bundle->add_port_to_channel (j, i->name());
++j;
} }
} else { } else {
_io->input_changed.connect (mem_fun(*this, &IOSelector::ports_changed));
const PortSet& ps (_io->inputs()); const PortSet& ps (_io->inputs());
int j = 0;
for (PortSet::const_iterator i = ps.begin(); i != ps.end(); ++i) { for (PortSet::const_iterator i = ps.begin(); i != ps.end(); ++i) {
our_ports.push_back (i->name()); char buf[32];
snprintf (buf, sizeof(buf), _("in %d"), j + 1);
_our_bundle->add_channel (buf);
_our_bundle->add_port_to_channel (j, i->name());
++j;
} }
} }
set_ports (our_ports); PortMatrix::setup ();
}
void
IOSelector::ports_changed ()
{
ENSURE_GUI_THREAD (mem_fun (*this, &IOSelector::ports_changed));
setup (); setup ();
} }
void void
IOSelector::ports_changed (ARDOUR::IOChange change, void *src) IOSelector::set_state (
boost::shared_ptr<ARDOUR::Bundle> ab,
uint32_t ac,
boost::shared_ptr<ARDOUR::Bundle> bb,
uint32_t bc,
bool s,
uint32_t k
)
{ {
ENSURE_GUI_THREAD (bind (mem_fun (*this, &IOSelector::ports_changed), change, src)); ARDOUR::Bundle::PortList const& our_ports = ab->channel_ports (ac);
ARDOUR::Bundle::PortList const& other_ports = bb->channel_ports (bc);
setup (); for (ARDOUR::Bundle::PortList::const_iterator i = our_ports.begin(); i != our_ports.end(); ++i) {
} for (ARDOUR::Bundle::PortList::const_iterator j = other_ports.begin(); j != other_ports.end(); ++j) {
void Port* f = _session.engine().get_port_by_name (*i);
IOSelector::set_state (int r, std::string const & p, bool s, uint32_t keymod) if (!f) {
{ return;
if (s) { }
if (!_offer_inputs) {
_io->connect_input (_io->input(r), p, 0); if (s) {
} else { if (!offering_input()) {
_io->connect_output (_io->output(r), p, 0); _io->connect_input (f, *j, 0);
} } else {
} else { _io->connect_output (f, *j, 0);
if (!_offer_inputs) { }
_io->disconnect_input (_io->input(r), p, 0); } else {
} else { if (!offering_input()) {
_io->disconnect_output (_io->output(r), p, 0); _io->disconnect_input (f, *j, 0);
} else {
_io->disconnect_output (f, *j, 0);
}
}
} }
} }
} }
bool bool
IOSelector::get_state (int r, std::string const & p) const IOSelector::get_state (
boost::shared_ptr<ARDOUR::Bundle> ab,
uint32_t ac,
boost::shared_ptr<ARDOUR::Bundle> bb,
uint32_t bc
) const
{ {
vector<string> connections; ARDOUR::Bundle::PortList const& our_ports = ab->channel_ports (ac);
ARDOUR::Bundle::PortList const& other_ports = bb->channel_ports (bc);
if (_offer_inputs) { for (ARDOUR::Bundle::PortList::const_iterator i = our_ports.begin(); i != our_ports.end(); ++i) {
_io->output(r)->get_connections (connections); for (ARDOUR::Bundle::PortList::const_iterator j = other_ports.begin(); j != other_ports.end(); ++j) {
} else {
_io->input(r)->get_connections (connections); Port* f = _session.engine().get_port_by_name (*i);
if (!f) {
return false;
}
if (!f->connected_to (*j)) {
/* if any one thing is not connected, all bets are off */
return false;
}
}
} }
return (std::find (connections.begin (), connections.end (), p) != connections.end ()); return true;
} }
uint32_t uint32_t
IOSelector::n_rows () const IOSelector::n_rows () const
{ {
if (!_offer_inputs) { if (!offering_input()) {
return _io->inputs().num_ports (_io->default_type()); return _io->inputs().num_ports (_io->default_type());
} else { } else {
return _io->outputs().num_ports (_io->default_type()); return _io->outputs().num_ports (_io->default_type());
@ -127,7 +178,7 @@ IOSelector::n_rows () const
uint32_t uint32_t
IOSelector::maximum_rows () const IOSelector::maximum_rows () const
{ {
if (!_offer_inputs) { if (!offering_input()) {
return _io->input_maximum ().get (_io->default_type()); return _io->input_maximum ().get (_io->default_type());
} else { } else {
return _io->output_maximum ().get (_io->default_type()); return _io->output_maximum ().get (_io->default_type());
@ -138,39 +189,22 @@ IOSelector::maximum_rows () const
uint32_t uint32_t
IOSelector::minimum_rows () const IOSelector::minimum_rows () const
{ {
if (!_offer_inputs) { if (!offering_input()) {
return _io->input_minimum ().get (_io->default_type()); return _io->input_minimum ().get (_io->default_type());
} else { } else {
return _io->output_minimum ().get (_io->default_type()); return _io->output_minimum ().get (_io->default_type());
} }
} }
std::string
IOSelector::row_name (int r) const
{
string n;
string::size_type pos;
if (!_offer_inputs) {
n = _io->input(r)->name();
} else {
n = _io->output(r)->name();
}
if ((pos = n.find ('/')) != string::npos) {
return n.substr (pos+1);
} else {
return n;
}
}
void void
IOSelector::add_row () IOSelector::add_channel (boost::shared_ptr<ARDOUR::Bundle> b)
{ {
/* we ignore the bundle parameter, as we know what it is that we're adding to */
// The IO selector only works for single typed IOs // The IO selector only works for single typed IOs
const ARDOUR::DataType t = _io->default_type (); const ARDOUR::DataType t = _io->default_type ();
if (!_offer_inputs) { if (!offering_input()) {
try { try {
_io->add_input_port ("", this); _io->add_input_port ("", this);
@ -195,22 +229,18 @@ IOSelector::add_row ()
} }
void void
IOSelector::remove_row (int r) IOSelector::remove_channel (boost::shared_ptr<ARDOUR::Bundle> b, uint32_t c)
{ {
// The IO selector only works for single typed IOs Port* f = _session.engine().get_port_by_name (b->channel_ports(c)[0]);
const ARDOUR::DataType t = _io->default_type (); if (!f) {
return;
if (!_offer_inputs) { }
_io->remove_input_port (_io->input (r), this);
} else { if (offering_input()) {
_io->remove_output_port (_io->output (r), this); _io->remove_output_port (f, this);
} else {
_io->remove_input_port (f, this);
} }
}
std::string
IOSelector::row_descriptor () const
{
return _("port");
} }
IOSelectorWindow::IOSelectorWindow (ARDOUR::Session& session, boost::shared_ptr<ARDOUR::IO> io, bool for_input, bool can_cancel) IOSelectorWindow::IOSelectorWindow (ARDOUR::Session& session, boost::shared_ptr<ARDOUR::IO> io, bool for_input, bool can_cancel)
@ -223,30 +253,34 @@ IOSelectorWindow::IOSelectorWindow (ARDOUR::Session& session, boost::shared_ptr<
, rescan_button (_("Rescan")) , rescan_button (_("Rescan"))
{ {
/* XXX: what's this for? */
add_events (Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK); add_events (Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK);
set_name ("IOSelectorWindow2"); set_name ("IOSelectorWindow2");
/* Disconnect All button */
disconnect_button.set_name ("IOSelectorButton"); disconnect_button.set_name ("IOSelectorButton");
disconnect_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::DISCONNECT, Gtk::ICON_SIZE_BUTTON))); disconnect_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::DISCONNECT, Gtk::ICON_SIZE_BUTTON)));
disconnect_button.signal_clicked().connect (sigc::mem_fun (_selector, &IOSelector::disassociate_all));
get_action_area()->pack_start (disconnect_button, false, false); get_action_area()->pack_start (disconnect_button, false, false);
/* Add Port button */
if (_selector.maximum_rows() > _selector.n_rows()) { if (_selector.maximum_rows() > _selector.n_rows()) {
add_button.set_name ("IOSelectorButton"); add_button.set_name ("IOSelectorButton");
add_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::ADD, Gtk::ICON_SIZE_BUTTON))); add_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::ADD, Gtk::ICON_SIZE_BUTTON)));
get_action_area()->pack_start (add_button, false, false); get_action_area()->pack_start (add_button, false, false);
add_button.signal_clicked().connect (sigc::mem_fun (_selector, &IOSelector::add_row)); add_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (_selector, &IOSelector::add_channel), boost::shared_ptr<Bundle> ()));
} }
if (!for_input) { /* Rescan button */
io->output_changed.connect (mem_fun(*this, &IOSelectorWindow::ports_changed)); rescan_button.set_name ("IOSelectorButton");
} else {
io->input_changed.connect (mem_fun(*this, &IOSelectorWindow::ports_changed));
}
rescan_button.set_name ("IOSelectorButton");
rescan_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::REFRESH, Gtk::ICON_SIZE_BUTTON))); rescan_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::REFRESH, Gtk::ICON_SIZE_BUTTON)));
rescan_button.signal_clicked().connect (sigc::mem_fun (_selector, &IOSelector::setup));
get_action_area()->pack_start (rescan_button, false, false); get_action_area()->pack_start (rescan_button, false, false);
io->PortCountChanged.connect (sigc::hide (mem_fun (*this, &IOSelectorWindow::ports_changed)));
/* Cancel button */
if (can_cancel) { if (can_cancel) {
cancel_button.set_name ("IOSelectorButton"); cancel_button.set_name ("IOSelectorButton");
cancel_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::CANCEL, Gtk::ICON_SIZE_BUTTON))); cancel_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::CANCEL, Gtk::ICON_SIZE_BUTTON)));
@ -254,37 +288,32 @@ IOSelectorWindow::IOSelectorWindow (ARDOUR::Session& session, boost::shared_ptr<
} else { } else {
cancel_button.hide(); cancel_button.hide();
} }
cancel_button.signal_clicked().connect (mem_fun(*this, &IOSelectorWindow::cancel));
/* OK button */
ok_button.set_name ("IOSelectorButton"); ok_button.set_name ("IOSelectorButton");
if (!can_cancel) { if (!can_cancel) {
ok_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::CLOSE, Gtk::ICON_SIZE_BUTTON))); ok_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::CLOSE, Gtk::ICON_SIZE_BUTTON)));
} }
ok_button.signal_clicked().connect (mem_fun(*this, &IOSelectorWindow::accept));
get_action_area()->pack_start (ok_button, false, false); get_action_area()->pack_start (ok_button, false, false);
get_vbox()->set_spacing (8); get_vbox()->set_spacing (8);
get_vbox()->pack_start (_selector, true, true);
suggestion.set_alignment (0.5, 0.5); /* XXX: do we still need the ScrolledWindow? */
suggestion_box.pack_start (suggestion, true, true); Gtk::ScrolledWindow* sel_scroll = Gtk::manage (new Gtk::ScrolledWindow);
get_vbox()->pack_start (suggestion_box, false, false); sel_scroll->set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_NEVER);
sel_scroll->add (_selector);
ok_button.signal_clicked().connect (mem_fun(*this, &IOSelectorWindow::accept)); get_vbox()->pack_start (*sel_scroll, true, true);
cancel_button.signal_clicked().connect (mem_fun(*this, &IOSelectorWindow::cancel));
rescan_button.signal_clicked().connect (mem_fun(*this, &IOSelectorWindow::rescan));
set_position (Gtk::WIN_POS_MOUSE); set_position (Gtk::WIN_POS_MOUSE);
io_name_changed (this); io_name_changed (this);
ports_changed (IOChange (0), this); ports_changed ();
leave_scroller ((GdkEventCrossing*) 0);
show_all (); show_all ();
signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), this)); signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), this));
_selector.scrolled_window().add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
_selector.scrolled_window().signal_enter_notify_event().connect (mem_fun (*this, &IOSelectorWindow::enter_scroller), false);
_selector.scrolled_window().signal_leave_notify_event().connect (mem_fun (*this, &IOSelectorWindow::leave_scroller), false);
} }
IOSelectorWindow::~IOSelectorWindow() IOSelectorWindow::~IOSelectorWindow()
@ -292,24 +321,8 @@ IOSelectorWindow::~IOSelectorWindow()
} }
bool
IOSelectorWindow::enter_scroller (GdkEventCrossing* ignored)
{
cerr << "IN\n";
suggestion.set_text (_("Click to connect. Ctrl-click to disconnect. Shift-click for cross-connect"));
return false;
}
bool
IOSelectorWindow::leave_scroller (GdkEventCrossing* ignored)
{
cerr << "OUT, ev = " << ignored << "\n";
suggestion.set_text (_("Right-click on individual port names for per-port operations"));
return false;
}
void void
IOSelectorWindow::ports_changed (ARDOUR::IOChange change, void *src) IOSelectorWindow::ports_changed ()
{ {
if (_selector.maximum_rows() > _selector.n_rows()) { if (_selector.maximum_rows() > _selector.n_rows()) {
add_button.set_sensitive (true); add_button.set_sensitive (true);
@ -318,12 +331,6 @@ IOSelectorWindow::ports_changed (ARDOUR::IOChange change, void *src)
} }
} }
void
IOSelectorWindow::rescan ()
{
_selector.setup ();
}
void void
IOSelectorWindow::cancel () IOSelectorWindow::cancel ()
{ {
@ -365,10 +372,8 @@ PortInsertUI::PortInsertUI (ARDOUR::Session& sess, boost::shared_ptr<ARDOUR::Por
: input_selector (sess, pi->io(), true), : input_selector (sess, pi->io(), true),
output_selector (sess, pi->io(), false) output_selector (sess, pi->io(), false)
{ {
hbox.pack_start (output_selector, true, true); pack_start (output_selector, true, true);
hbox.pack_start (input_selector, true, true); pack_start (input_selector, true, true);
pack_start (hbox);
} }
void void
@ -420,7 +425,6 @@ PortInsertWindow::PortInsertWindow (ARDOUR::Session& sess, boost::shared_ptr<ARD
ok_button.signal_clicked().connect (mem_fun (*this, &PortInsertWindow::accept)); ok_button.signal_clicked().connect (mem_fun (*this, &PortInsertWindow::accept));
cancel_button.signal_clicked().connect (mem_fun (*this, &PortInsertWindow::cancel)); cancel_button.signal_clicked().connect (mem_fun (*this, &PortInsertWindow::cancel));
rescan_button.signal_clicked().connect (mem_fun (*this, &PortInsertWindow::rescan));
signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), reinterpret_cast<Window *> (this))); signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), reinterpret_cast<Window *> (this)));
@ -444,12 +448,6 @@ PortInsertWindow::on_map ()
} }
void
PortInsertWindow::rescan ()
{
_portinsertui.redisplay ();
}
void void
PortInsertWindow::cancel () PortInsertWindow::cancel ()
{ {

View file

@ -23,25 +23,47 @@
#include "ardour_dialog.h" #include "ardour_dialog.h"
#include "port_matrix.h" #include "port_matrix.h"
namespace ARDOUR {
class PortInsert;
}
class IOSelector : public PortMatrix { class IOSelector : public PortMatrix {
public: public:
IOSelector (ARDOUR::Session&, boost::shared_ptr<ARDOUR::IO>, bool); IOSelector (ARDOUR::Session&, boost::shared_ptr<ARDOUR::IO>, bool);
void set_state (int, std::string const &, bool, uint32_t); void set_state (
bool get_state (int, std::string const &) const; boost::shared_ptr<ARDOUR::Bundle>,
uint32_t,
boost::shared_ptr<ARDOUR::Bundle>,
uint32_t,
bool,
uint32_t
);
bool get_state (
boost::shared_ptr<ARDOUR::Bundle>,
uint32_t,
boost::shared_ptr<ARDOUR::Bundle>,
uint32_t
) const;
void add_channel (boost::shared_ptr<ARDOUR::Bundle>);
void remove_channel (boost::shared_ptr<ARDOUR::Bundle>, uint32_t);
bool can_rename_channels () const {
return false;
}
uint32_t n_rows () const; uint32_t n_rows () const;
uint32_t maximum_rows () const; uint32_t maximum_rows () const;
uint32_t minimum_rows () const; uint32_t minimum_rows () const;
std::string row_name (int) const; boost::shared_ptr<ARDOUR::IO> const io () { return _io; }
void add_row (); void setup ();
void remove_row (int);
std::string row_descriptor () const;
boost::shared_ptr<ARDOUR::IO> const io() { return _io; }
private: private:
ARDOUR::Session& _session;
boost::shared_ptr<ARDOUR::IO> _io; boost::shared_ptr<ARDOUR::IO> _io;
void ports_changed (ARDOUR::IOChange, void*); void ports_changed ();
}; };
class IOSelectorWindow : public ArdourDialog class IOSelectorWindow : public ArdourDialog
@ -66,17 +88,11 @@ class IOSelectorWindow : public ArdourDialog
Gtk::Button cancel_button; Gtk::Button cancel_button;
Gtk::Button rescan_button; Gtk::Button rescan_button;
Gtk::HBox suggestion_box;
Gtk::Label suggestion;
void rescan ();
void cancel (); void cancel ();
void accept (); void accept ();
void ports_changed (ARDOUR::IOChange change, void *src); void ports_changed ();
void io_name_changed (void *src); void io_name_changed (void *src);
bool enter_scroller (GdkEventCrossing*);
bool leave_scroller (GdkEventCrossing*);
}; };
@ -89,7 +105,6 @@ class PortInsertUI : public Gtk::VBox
void finished (IOSelector::Result); void finished (IOSelector::Result);
private: private:
Gtk::HBox hbox;
IOSelector input_selector; IOSelector input_selector;
IOSelector output_selector; IOSelector output_selector;
}; };
@ -111,7 +126,6 @@ class PortInsertWindow : public ArdourDialog
Gtk::Button rescan_button; Gtk::Button rescan_button;
Gtk::Frame button_frame; Gtk::Frame button_frame;
void rescan ();
void cancel (); void cancel ();
void accept (); void accept ();

View file

@ -1,511 +0,0 @@
#include <gtkmm.h>
#include <cairo/cairo.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
#include <stdint.h>
#include <cmath>
#include <map>
#include <vector>
#include "matrix.h"
#include "port_matrix.h"
using namespace std;
using namespace Gtk;
using namespace ARDOUR;
Matrix::Matrix (PortMatrix* p) : _port_matrix (p)
{
alloc_width = 0;
alloc_height = 0;
line_width = 0;
line_height = 0;
labels_y_shift = 0;
labels_x_shift = 0;
arc_radius = 0;
xstep = 0;
ystep = 0;
pixmap = 0;
drawn = false;
angle_radians = M_PI / 4.0;
motion_x = -1;
motion_y = -1;
border = 10;
add_events (Gdk::POINTER_MOTION_MASK|Gdk::LEAVE_NOTIFY_MASK);
}
void
Matrix::set_ports (const list<string>& ports)
{
ours = ports;
reset_size ();
}
void
Matrix::add_group (PortGroup& pg)
{
for (vector<string>::const_iterator s = pg.ports.begin(); s != pg.ports.end(); ++s) {
others.push_back (OtherPort (*s, pg));
}
if (pg.visible) {
reset_size ();
}
}
void
Matrix::clear ()
{
others.clear ();
reset_size ();
}
void
Matrix::remove_group (PortGroup& pg)
{
for (list<OtherPort>::iterator o = others.begin(); o != others.end(); ) {
if (&(*o).group() == &pg) {
o = others.erase (o);
} else {
++o;
}
}
if (pg.visible) {
reset_size ();
}
}
void
Matrix::hide_group (PortGroup& pg)
{
reset_size();
}
void
Matrix::show_group (PortGroup& pg)
{
reset_size ();
}
void
Matrix::setup_nodes ()
{
for (vector<MatrixNode*>::iterator p = nodes.begin(); p != nodes.end(); ++p) {
delete *p;
}
nodes.clear ();
nodes.assign (ours.size() * get_visible_others (), 0);
int n, x, y;
list<string>::iterator m;
list<OtherPort>::iterator s;
for (n = 0, y = 0, m = ours.begin(); m != ours.end(); ++m, ++y) {
for (x = 0, s = others.begin(); s != others.end(); ++s) {
if (s->visible ()) {
bool const c = _port_matrix->get_state (y, s->name());
nodes[n] = new MatrixNode (*m, *s, c, x, y);
n++;
x++;
}
}
}
}
void
Matrix::other_name_size_information (double* rotated_width, double* rotated_height, double* typical_height) const
{
double w = 0;
double h = 0;
GdkPixmap* pm = gdk_pixmap_new (NULL, 1, 1, 24);
gdk_drawable_set_colormap (pm, gdk_colormap_get_system());
cairo_t* cr = gdk_cairo_create (pm);
for (list<OtherPort>::const_iterator s = others.begin(); s != others.end(); ++s) {
if (s->visible()) {
cairo_text_extents_t extents;
cairo_text_extents (cr, s->short_name().c_str(), &extents);
if (extents.width > w) {
w = extents.width;
h = extents.height;
}
}
}
cairo_destroy (cr);
gdk_pixmap_unref (pm);
/* transform */
*rotated_width = fabs (w * cos (angle_radians) + h * sin (angle_radians));
*rotated_height = fabs (w * sin (angle_radians) + h * cos (angle_radians));
*typical_height = h;
}
std::pair<int, int>
Matrix::ideal_size () const
{
double rw;
double rh;
double th;
other_name_size_information (&rw, &rh, &th);
double const ideal_xstep = th * 2;
double const ideal_ystep = 16;
uint32_t const visible_others = get_visible_others ();
return std::make_pair (
int (rw + (2 * border) + ideal_xstep * visible_others),
int (rh + (2 * border) + ideal_ystep * ours.size ())
);
}
void
Matrix::reset_size ()
{
double rw;
double rh;
double th;
other_name_size_information (&rw, &rh, &th);
/* y shift is the largest transformed text height plus a bit for luck */
labels_y_shift = int (ceil (rh) + 10);
/* x shift is the width of the leftmost label */
labels_x_shift = int (ceil (rw));
uint32_t const visible_others = get_visible_others ();
if (!visible_others) {
xstep = 1;
ystep = 1;
line_width = 1;
line_height = 1;
arc_radius = 3;
return;
}
if (ours.size () > 1) {
xstep = (alloc_width - labels_x_shift - (2 * border)) / visible_others;
line_width = xstep * (visible_others - 1);
ystep = (alloc_height - labels_y_shift - (2 * border)) / (ours.size() - 1);
line_height = ystep * (ours.size() - 1);
} else {
/* we have <= 1 of our ports, so steps don't matter */
xstep = 20;
ystep = 20;
line_height = (ours.size() - 1) * ystep;
line_width = visible_others * xstep;
}
int half_step = min (ystep / 2, xstep / 2);
if (half_step > 3) {
arc_radius = half_step - 5;
} else {
arc_radius = 3;
}
arc_radius = min (arc_radius, 10);
setup_nodes ();
// cerr << "Based on ours = " << ours.size() << " others = " << others.size()
// << " dimens = "
// << " xstep " << xstep << endl
// << " ystep " << ystep << endl
// << " line_width " << line_width << endl
// << " line_height " << line_height << endl
// << " border " << border << endl
// << " arc_radius " << arc_radius << endl
// << " labels_x_shift " << labels_x_shift << endl
// << " labels_y_shift " << labels_y_shift << endl;
}
bool
Matrix::on_motion_notify_event (GdkEventMotion* ev)
{
motion_x = ev->x;
motion_y = ev->y;
queue_draw ();
return false;
}
bool
Matrix::on_leave_notify_event (GdkEventCrossing *ev)
{
motion_x = -1;
motion_y = -1;
queue_draw ();
return false;
}
void
Matrix::on_size_request (Requisition* req)
{
std::pair<int, int> const is = ideal_size ();
req->width = is.first;
req->height = is.second;
}
MatrixNode*
Matrix::get_node (int32_t x, int32_t y)
{
int const half_xstep = xstep / 2;
int const half_ystep = ystep / 2;
x -= labels_x_shift + border;
if (x < -half_xstep) {
return 0;
}
y -= labels_y_shift + border;
if (y < -half_ystep) {
return 0;
}
x = (x + half_xstep) / xstep;
y = (y + half_ystep) / ystep;
x = y * get_visible_others () + x;
if (x >= int32_t (nodes.size())) {
return 0;
}
return nodes[x];
}
bool
Matrix::on_button_press_event (GdkEventButton* ev)
{
MatrixNode* node;
if ((node = get_node (ev->x, ev->y)) != 0) {
node->set_connected (!node->connected());
_port_matrix->set_state (node->y (), node->their_name (), node->connected (), 0);
drawn = false;
queue_draw();
return true;
}
return false;
}
void
Matrix::alloc_pixmap ()
{
if (pixmap) {
gdk_pixmap_unref (pixmap);
}
pixmap = gdk_pixmap_new (get_window()->gobj(),
alloc_width,
alloc_height,
-1);
drawn = false;
}
void
Matrix::on_size_allocate (Allocation& alloc)
{
EventBox::on_size_allocate (alloc);
alloc_width = alloc.get_width();
alloc_height = alloc.get_height();
if (is_realized()) {
alloc_pixmap ();
reset_size ();
#ifdef MATRIX_USE_BACKING_PIXMAP
redraw (pixmap, 0, 0, alloc_width, alloc_height);
#endif
}
}
void
Matrix::on_realize ()
{
EventBox::on_realize ();
alloc_pixmap ();
}
void
Matrix::redraw (GdkDrawable* drawable, GdkRectangle* rect)
{
list<string>::iterator o;
list<OtherPort>::iterator t;
int x, y;
cairo_t* cr = gdk_cairo_create (drawable);
cairo_set_source_rgb (cr, 0.83, 0.83, 0.83);
cairo_rectangle (cr, rect->x, rect->y, rect->width, rect->height);
cairo_fill (cr);
cairo_set_line_width (cr, 0.5);
int32_t const top_shift = labels_y_shift + border;
int32_t const left_shift = labels_x_shift + border;
/* horizontal grid lines and side labels */
for (y = top_shift, o = ours.begin(); o != ours.end(); ++o, y += ystep) {
cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
cairo_move_to (cr, left_shift, y);
cairo_line_to (cr, left_shift+line_width, y);
cairo_stroke (cr);
#if 0
cairo_text_extents_t extents;
cairo_text_extents (cr, (*o).c_str(),&extents);
cairo_move_to (cr, border, y+extents.height/2);
cairo_show_text (cr, (*o).c_str());
#endif
}
/* vertical grid lines and rotated labels*/
for (x = left_shift, t = others.begin(); t != others.end(); ++t, x += xstep) {
cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
cairo_move_to (cr, x, top_shift);
cairo_line_to (cr, x, top_shift+line_height);
cairo_stroke (cr);
cairo_move_to (cr, x-left_shift+12, border);
cairo_set_source_rgb (cr, 0, 0, 1.0);
cairo_save (cr);
cairo_rotate (cr, angle_radians);
cairo_show_text (cr, t->short_name().c_str());
cairo_restore (cr);
}
/* nodes */
for (vector<MatrixNode*>::iterator n = nodes.begin(); n != nodes.end(); ++n) {
x = (*n)->x() * xstep;
y = (*n)->y() * ystep;
cairo_new_path (cr);
if (arc_radius) {
cairo_arc (cr, left_shift+x, top_shift+y, arc_radius, 0, 2.0 * M_PI);
if ((*n)->connected()) {
cairo_set_source_rgba (cr, 1.0, 0, 0, 1.0);
cairo_fill (cr);
} else {
cairo_set_source_rgba (cr, 1.0, 0, 0, 0.7);
cairo_stroke (cr);
}
}
}
/* motion indicators */
if (motion_x >= left_shift && motion_y >= top_shift) {
int col_left = left_shift + ((motion_x + (xstep / 2) + - left_shift) / xstep) * xstep;
int row_top = top_shift + ((motion_y + (ystep / 2) - top_shift) / ystep) * ystep;
cairo_set_line_width (cr, 5);
cairo_set_source_rgba (cr, 1.0, 0.0, 0.0, 0.3);
/* horizontal (row) */
cairo_line_to (cr, left_shift, row_top);
cairo_line_to (cr, left_shift + line_width, row_top);
cairo_stroke (cr);
/* vertical (col) */
cairo_move_to (cr, col_left, top_shift);
cairo_line_to (cr, col_left, top_shift + line_height);
cairo_stroke (cr);
}
cairo_destroy (cr);
#ifdef MATRIX_USE_BACKING_PIXMAP
drawn = true;
#endif
}
bool
Matrix::on_expose_event (GdkEventExpose* event)
{
#ifdef MATRIX_USE_BACKING_PIXMAP
if (!drawn) {
redraw (pixmap, 0, 0, alloc_width, alloc_height);
}
gdk_draw_drawable (get_window()->gobj(),
get_style()->get_fg_gc (STATE_NORMAL)->gobj(),
pixmap,
event->area.x,
event->area.y,
event->area.x,
event->area.y,
event->area.width,
event->area.height);
#else
redraw (get_window()->gobj(), &event->area);
#endif
return true;
}
uint32_t
Matrix::get_visible_others () const
{
uint32_t v = 0;
for (list<OtherPort>::const_iterator s = others.begin(); s != others.end(); ++s) {
if (s->visible()) {
++v;
}
}
return v;
}
MatrixNode::MatrixNode (std::string a, OtherPort o, bool c, int32_t x, int32_t y)
: _name (a), them (o), _connected (c), _x(x), _y(y)
{
}
std::string
OtherPort::name () const
{
return _group.prefix + _short_name;
}

View file

@ -1,113 +0,0 @@
#ifndef __gtk_ardour_matrix_h__
#define __gtk_ardour_matrix_h__
#include <list>
#include <vector>
#include <string>
#include <stdint.h>
#include <gtkmm/eventbox.h>
#include <gtkmm/widget.h>
#include "port_group.h"
/// One of the other ports that we're connecting ours to
class OtherPort {
public:
OtherPort (const std::string& n, PortGroup& g)
: _short_name (n), _group (g) {}
std::string name () const;
std::string short_name () const { return _short_name; }
PortGroup& group() const { return _group; }
bool visible() const { return _group.visible; }
public:
std::string _short_name;
PortGroup& _group;
};
/// A node on the matrix
class MatrixNode {
public:
MatrixNode (std::string, OtherPort, bool, int32_t, int32_t);
~MatrixNode() {}
PortGroup& get_group() const { return them.group(); }
std::string our_name() const { return _name; }
std::string their_name() const { return them.name(); }
bool connected() const { return _connected; }
void set_connected (bool yn) { _connected = yn; }
int32_t x() const { return _x; }
int32_t y() const { return _y; }
private:
std::string _name;
OtherPort them;
bool _connected;
int32_t _x;
int32_t _y;
};
class Matrix : public Gtk::EventBox
{
public:
Matrix (PortMatrix*);
void set_ports (const std::list<std::string>&);
void add_group (PortGroup&);
void remove_group (PortGroup&);
void hide_group (PortGroup&);
void show_group (PortGroup&);
void clear ();
int row_spacing () const { return xstep; }
protected:
bool on_button_press_event (GdkEventButton* ev);
bool on_expose_event (GdkEventExpose* ev);
void on_size_allocate (Gtk::Allocation&);
void on_size_request (Gtk::Requisition*);
void on_realize ();
bool on_motion_notify_event (GdkEventMotion*);
bool on_leave_notify_event (GdkEventCrossing*);
MatrixNode* get_node (int32_t x, int32_t y);
private:
PortMatrix* _port_matrix; ///< the PortMatrix that we're working for
int height;
int width;
int alloc_width;
int alloc_height;
bool drawn;
int labels_y_shift;
int labels_x_shift;
float angle_radians;
int border;
int ystep;
int xstep;
uint32_t line_height;
uint32_t line_width;
int arc_radius;
int32_t motion_x;
int32_t motion_y;
std::list<std::string> ours;
std::list<OtherPort> others;
std::vector<MatrixNode*> nodes;
void reset_size ();
void redraw (GdkDrawable*, GdkRectangle*);
void alloc_pixmap ();
void setup_nodes ();
uint32_t get_visible_others () const;
void other_name_size_information (double *, double *, double *) const;
std::pair<int, int> ideal_size () const;
GdkPixmap* pixmap;
};
#endif /* __gtk_ardour_matrix_h__ */

View file

@ -1120,11 +1120,11 @@ OptionEditor::setup_click_editor ()
click_path_entry.set_sensitive (true); click_path_entry.set_sensitive (true);
click_emphasis_path_entry.set_sensitive (true); click_emphasis_path_entry.set_sensitive (true);
click_io_selector = new IOSelector (*session, session->click_io(), false); click_io_selector = new IOSelector (*session, session->click_io(), true);
click_gpm = new GainMeter (*session); click_gpm = new GainMeter (*session);
click_gpm->set_io (session->click_io()); click_gpm->set_io (session->click_io());
click_hpacker.pack_start (*click_io_selector, false, false); click_hpacker.pack_start (*click_io_selector, true, true);
click_hpacker.pack_start (*click_gpm, false, false); click_hpacker.pack_start (*click_gpm, false, false);
click_packer.show_all (); click_packer.show_all ();
@ -1163,11 +1163,11 @@ OptionEditor::setup_auditioner_editor ()
void void
OptionEditor::connect_audition_editor () OptionEditor::connect_audition_editor ()
{ {
auditioner_io_selector = new IOSelector (*session, session->the_auditioner(), false); auditioner_io_selector = new IOSelector (*session, session->the_auditioner(), true);
auditioner_gpm = new GainMeter (*session); auditioner_gpm = new GainMeter (*session);
auditioner_gpm->set_io (session->the_auditioner()); auditioner_gpm->set_io (session->the_auditioner());
audition_hpacker.pack_start (*auditioner_io_selector, false, false); audition_hpacker.pack_start (*auditioner_io_selector, true, true);
audition_hpacker.pack_start (*auditioner_gpm, false, false); audition_hpacker.pack_start (*auditioner_gpm, false, false);
auditioner_io_selector->show_all (); auditioner_io_selector->show_all ();

View file

@ -18,28 +18,42 @@
*/ */
#include "port_group.h" #include "port_group.h"
#include "port_matrix.h"
#include "i18n.h" #include "i18n.h"
#include "ardour/session.h" #include "ardour/session.h"
#include "ardour/audio_track.h" #include "ardour/audio_track.h"
#include "ardour/midi_track.h" #include "ardour/midi_track.h"
#include "ardour/audioengine.h" #include "ardour/audioengine.h"
#include "ardour/bundle.h"
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
#include <cstring> #include <cstring>
using namespace std; using namespace std;
using namespace Gtk; using namespace Gtk;
/** Add a port to a group. /** Add a bundle to a group.
* @param p Port name, with or without prefix. * @param b Bundle.
*/ */
void void
PortGroup::add (std::string const & p) PortGroup::add_bundle (boost::shared_ptr<ARDOUR::Bundle> b)
{ {
if (prefix.empty() == false && p.substr (0, prefix.length()) == prefix) { bundles.push_back (b);
ports.push_back (p.substr (prefix.length())); }
} else {
ports.push_back (p); /** Add a port to a group.
} * @param p Port.
*/
void
PortGroup::add_port (std::string const &p)
{
ports.push_back (p);
}
void
PortGroup::clear ()
{
bundles.clear ();
ports.clear ();
} }
/** PortGroupUI constructor. /** PortGroupUI constructor.
@ -47,14 +61,14 @@ PortGroup::add (std::string const & p)
* @Param g PortGroup to represent. * @Param g PortGroup to represent.
*/ */
PortGroupUI::PortGroupUI (PortMatrix& m, PortGroup& g) PortGroupUI::PortGroupUI (PortMatrix* m, PortGroup* g)
: _port_matrix (m) : _port_matrix (m)
, _port_group (g) , _port_group (g)
, _ignore_check_button_toggle (false) , _visibility_checkbutton (g->name)
, _visibility_checkbutton (g.name)
{ {
_port_group.visible = true; _port_group->visible = true;
_ignore_check_button_toggle = false; setup_visibility_checkbutton ();
_visibility_checkbutton.signal_toggled().connect (sigc::mem_fun (*this, &PortGroupUI::visibility_checkbutton_toggled)); _visibility_checkbutton.signal_toggled().connect (sigc::mem_fun (*this, &PortGroupUI::visibility_checkbutton_toggled));
} }
@ -62,66 +76,49 @@ PortGroupUI::PortGroupUI (PortMatrix& m, PortGroup& g)
void void
PortGroupUI::visibility_checkbutton_toggled () PortGroupUI::visibility_checkbutton_toggled ()
{ {
_port_group.visible = _visibility_checkbutton.get_active (); _port_group->visible = _visibility_checkbutton.get_active ();
setup_visibility_checkbutton ();
_port_matrix->setup ();
} }
/** @return Checkbutton used to toggle visibility */ /** Set up the visibility checkbutton according to PortGroup::visible */
Widget&
PortGroupUI::get_visibility_checkbutton ()
{
return _visibility_checkbutton;
}
/** Handle a toggle of a port check button */
void void
PortGroupUI::port_checkbutton_toggled (CheckButton* b, int r, int c) PortGroupUI::setup_visibility_checkbutton ()
{ {
if (_ignore_check_button_toggle == false) { if (_visibility_checkbutton.get_active () != _port_group->visible) {
// _port_matrix.hide_group (_port_group); _visibility_checkbutton.set_active (_port_group->visible);
}
}
/** Set up visibility of the port group according to PortGroup::visible */
void
PortGroupUI::setup_visibility ()
{
if (_visibility_checkbutton.get_active () != _port_group.visible) {
_visibility_checkbutton.set_active (_port_group.visible);
} }
} }
/** PortGroupList constructor. /** PortGroupList constructor.
* @param session Session to get ports from. * @param session Session to get bundles from.
* @param type Type of ports to offer (audio or MIDI) * @param type Type of bundles to offer (audio or MIDI)
* @param offer_inputs true to offer output ports, otherwise false. * @param offer_inputs true to offer output bundles, otherwise false.
* @param mask Mask of groups to make visible by default. * @param mask Mask of groups to make visible by default.
*/ */
PortGroupList::PortGroupList (ARDOUR::Session & session, ARDOUR::DataType type, bool offer_inputs, Mask mask) PortGroupList::PortGroupList (ARDOUR::Session & session, ARDOUR::DataType type, bool offer_inputs, Mask mask)
: _session (session), _type (type), _offer_inputs (offer_inputs), : _session (session), _type (type), _offer_inputs (offer_inputs),
_buss (_("Bus"), "ardour:", mask & BUSS), _buss (_("Bus"), mask & BUSS),
_track (_("Track"), "ardour:", mask & TRACK), _track (_("Track"), mask & TRACK),
_system (_("System"), "system:", mask & SYSTEM), _system (_("System"), mask & SYSTEM),
_other (_("Other"), "", mask & OTHER) _other (_("Other"), mask & OTHER)
{ {
refresh (); refresh ();
} }
/** Find or re-find all our ports and set up our lists */ /** Find or re-find all our bundles and set up our lists */
void void
PortGroupList::refresh () PortGroupList::refresh ()
{ {
clear (); clear ();
_buss.ports.clear ();
_track.ports.clear ();
_system.ports.clear ();
_other.ports.clear ();
/* Find the ports provided by ardour; we can't derive their type just from their _buss.clear ();
names, so we'll have to be more devious. _track.clear ();
*/ _system.clear ();
_other.clear ();
/* Find the bundles for routes */
boost::shared_ptr<ARDOUR::Session::RouteList> routes = _session.get_routes (); boost::shared_ptr<ARDOUR::Session::RouteList> routes = _session.get_routes ();
@ -148,14 +145,12 @@ PortGroupList::refresh ()
} }
if (g) { if (g) {
ARDOUR::PortSet const & p = _offer_inputs ? ((*i)->inputs()) : ((*i)->outputs()); g->add_bundle (_offer_inputs ? (*i)->bundle_for_inputs() : (*i)->bundle_for_outputs ());
for (uint32_t j = 0; j < p.num_ports(); ++j) {
g->add (p.port(j)->name ());
}
std::sort (g->ports.begin(), g->ports.end());
} }
} }
/* Bundles created by the session */
_session.foreach_bundle (sigc::mem_fun (*this, &PortGroupList::maybe_add_session_bundle));
/* XXX: inserts, sends, plugin inserts? */ /* XXX: inserts, sends, plugin inserts? */
@ -163,9 +158,9 @@ PortGroupList::refresh ()
finding all the ports that we can connect to. finding all the ports that we can connect to.
*/ */
const char **ports = _session.engine().get_ports ("", _type.to_jack_type(), _offer_inputs ? const char **ports = _session.engine().get_ports ("", _type.to_jack_type(), _offer_inputs ?
JackPortIsInput : JackPortIsOutput); JackPortIsInput : JackPortIsOutput);
if (ports) { if (ports) {
int n = 0; int n = 0;
string client_matching_string; string client_matching_string;
@ -178,11 +173,11 @@ PortGroupList::refresh ()
if (p.substr(0, strlen ("system:")) == "system:") { if (p.substr(0, strlen ("system:")) == "system:") {
/* system: prefix */ /* system: prefix */
_system.add (p); _system.add_port (p);
} else { } else {
if (p.substr(0, client_matching_string.length()) != client_matching_string) { if (p.substr(0, client_matching_string.length()) != client_matching_string) {
/* other (non-ardour) prefix */ /* other (non-ardour) prefix */
_other.add (p); _other.add_port (p);
} }
} }
@ -198,41 +193,6 @@ PortGroupList::refresh ()
push_back (&_other); push_back (&_other);
} }
int
PortGroupList::n_visible_ports () const
{
int n = 0;
for (const_iterator i = begin(); i != end(); ++i) {
if ((*i)->visible) {
n += (*i)->ports.size();
}
}
return n;
}
std::string
PortGroupList::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 "";
}
void void
PortGroupList::set_type (ARDOUR::DataType t) PortGroupList::set_type (ARDOUR::DataType t)
{ {
@ -245,3 +205,10 @@ PortGroupList::set_offer_inputs (bool i)
_offer_inputs = i; _offer_inputs = i;
} }
void
PortGroupList::maybe_add_session_bundle (boost::shared_ptr<ARDOUR::Bundle> b)
{
if (b->ports_are_inputs () == _offer_inputs) {
_system.bundles.push_back (b);
}
}

View file

@ -22,37 +22,39 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include <gtkmm/widget.h> #include <gtkmm/widget.h>
#include <gtkmm/checkbutton.h> #include <gtkmm/checkbutton.h>
#include <boost/shared_ptr.hpp>
#include <ardour/data_type.h> #include <ardour/data_type.h>
namespace ARDOUR { namespace ARDOUR {
class Session; class Session;
class IO; class Bundle;
class PortInsert;
} }
class PortMatrix; class PortMatrix;
/// A list of port names, grouped by some aspect of their type e.g. busses, tracks, system /** A list of bundles and ports, grouped by some aspect of their
* type e.g. busses, tracks, system. Each group has 0 or more bundles
* and 0 or more ports, where the ports are not in the bundles.
*/
class PortGroup class PortGroup
{ {
public: public:
/** PortGroup constructor. /** PortGroup constructor.
* @param n Name. * @param n Name.
* @param p Port name prefix (including trailing :)
* @param v true if group should be visible in the UI, otherwise false. * @param v true if group should be visible in the UI, otherwise false.
*/ */
PortGroup (std::string const & n, std::string const & p, bool v) PortGroup (std::string const & n, bool v)
: name (n), prefix (p), visible (v) {} : name (n), visible (v) {}
void add (std::string const & p); void add_bundle (boost::shared_ptr<ARDOUR::Bundle>);
void add_port (std::string const &);
void clear ();
std::string name; ///< name for the group std::string name; ///< name for the group
std::string prefix; ///< prefix e.g. "ardour:" std::vector<boost::shared_ptr<ARDOUR::Bundle> > bundles;
std::vector<std::string> ports; ///< port names std::vector<std::string> ports;
bool visible; ///< true if the group is visible in the UI bool visible; ///< true if the group is visible in the UI
}; };
@ -60,20 +62,18 @@ class PortGroup
class PortGroupUI class PortGroupUI
{ {
public: public:
PortGroupUI (PortMatrix&, PortGroup&); PortGroupUI (PortMatrix*, PortGroup*);
Gtk::Widget& get_visibility_checkbutton (); Gtk::Widget& visibility_checkbutton () {
PortGroup& port_group () { return _port_group; } return _visibility_checkbutton;
void setup_visibility (); }
private: private:
void port_checkbutton_toggled (Gtk::CheckButton*, int, int);
bool port_checkbutton_release (GdkEventButton* ev, Gtk::CheckButton* b, int r, int c);
void visibility_checkbutton_toggled (); void visibility_checkbutton_toggled ();
void setup_visibility_checkbutton ();
PortMatrix& _port_matrix; ///< the PortMatrix that we are working for PortMatrix* _port_matrix; ///< the PortMatrix that we are working for
PortGroup& _port_group; ///< the PortGroup that we are representing PortGroup* _port_group; ///< the PortGroup that we are representing
bool _ignore_check_button_toggle;
Gtk::CheckButton _visibility_checkbutton; Gtk::CheckButton _visibility_checkbutton;
}; };
@ -91,12 +91,12 @@ class PortGroupList : public std::list<PortGroup*>
PortGroupList (ARDOUR::Session &, ARDOUR::DataType, bool, Mask); PortGroupList (ARDOUR::Session &, ARDOUR::DataType, bool, Mask);
void refresh (); void refresh ();
int n_visible_ports () const;
std::string get_port_by_index (int, bool with_prefix = true) const;
void set_type (ARDOUR::DataType); void set_type (ARDOUR::DataType);
void set_offer_inputs (bool); void set_offer_inputs (bool);
private: private:
void maybe_add_session_bundle (boost::shared_ptr<ARDOUR::Bundle>);
ARDOUR::Session& _session; ARDOUR::Session& _session;
ARDOUR::DataType _type; ARDOUR::DataType _type;
bool _offer_inputs; bool _offer_inputs;

View file

@ -1,5 +1,5 @@
/* /*
Copyright (C) 2002-2007 Paul Davis Copyright (C) 2002-2009 Paul Davis
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -17,160 +17,109 @@
*/ */
#include <iostream>
#include <gtkmm/scrolledwindow.h>
#include <gtkmm/adjustment.h>
#include <gtkmm/label.h> #include <gtkmm/label.h>
#include <gtkmm/enums.h> #include "ardour/bundle.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/data_type.h"
#include "i18n.h"
#include "port_matrix.h" #include "port_matrix.h"
#include "i18n.h"
using namespace Gtk; /** PortMatrix constructor.
* @param session Our session.
* @param type Port type that we are handling.
* @param offer_inputs true to offer inputs, otherwise false.
* @param mask Mask of port groups to offer.
*/
PortMatrix::PortMatrix (ARDOUR::Session& session, ARDOUR::DataType type, bool offer_inputs, PortGroupList::Mask mask) PortMatrix::PortMatrix (ARDOUR::Session& session, ARDOUR::DataType type, bool offer_inputs, PortGroupList::Mask mask)
: _offer_inputs (offer_inputs), _port_group_list (session, type, offer_inputs, mask), _type (type), matrix (this) : _offer_inputs (offer_inputs),
_port_group_list (session, type, offer_inputs, mask),
_type (type),
_body (this)
{ {
_side_vbox_pad = 0; /* checkbuttons for visibility of groups */
Gtk::HBox* visibility_buttons = Gtk::manage (new Gtk::HBox);
_visibility_checkbutton_box.pack_start (*(manage (new Label (_("Connections displayed: ")))), false, false, 10); visibility_buttons->pack_start (*Gtk::manage (new Gtk::Label (_("Show:"))), Gtk::PACK_SHRINK);
pack_start (_visibility_checkbutton_box, false, false);
for (std::list<PortGroup*>::iterator i = _port_group_list.begin(); i != _port_group_list.end(); ++i) {
_scrolled_window.set_policy (POLICY_ALWAYS, POLICY_AUTOMATIC); _port_group_uis.push_back (new PortGroupUI (this, *i));
_scrolled_window.set_shadow_type (SHADOW_NONE);
_scrolled_window.add (matrix);
if (offer_inputs) {
_overall_hbox.pack_start (_side_vbox, false, false, 6);
_overall_hbox.pack_start (_scrolled_window, true, true);
} else {
_overall_hbox.pack_start (_scrolled_window, true, true, 6);
_overall_hbox.pack_start (_side_vbox, false, false);
} }
pack_start (_overall_hbox); for (std::list<PortGroupUI*>::iterator i = _port_group_uis.begin(); i != _port_group_uis.end(); ++i) {
visibility_buttons->pack_start ((*i)->visibility_checkbutton(), Gtk::PACK_SHRINK);
}
pack_start (*visibility_buttons, Gtk::PACK_SHRINK);
pack_start (_hscroll, Gtk::PACK_SHRINK);
Gtk::HBox* hbox = Gtk::manage (new Gtk::HBox);
hbox->pack_start (_body);
hbox->pack_start (_vscroll, Gtk::PACK_SHRINK);
pack_start (*hbox);
_hscroll.signal_value_changed().connect (sigc::mem_fun (*this, &PortMatrix::hscroll_changed));
_vscroll.signal_value_changed().connect (sigc::mem_fun (*this, &PortMatrix::vscroll_changed));
setup_scrollbars ();
/* XXX hard-coded initial size suggestion */
set_size_request (400, 200);
show_all ();
} }
PortMatrix::~PortMatrix () PortMatrix::~PortMatrix ()
{ {
clear (); for (std::list<PortGroupUI*>::iterator i = _port_group_uis.begin(); i != _port_group_uis.end(); ++i) {
}
void
PortMatrix::set_ports (const std::list<std::string>& ports)
{
matrix.set_ports (ports);
}
/** Clear out the things that change when the number of source or destination ports changes */
void
PortMatrix::clear ()
{
/* remove lurking, invisible label and padding */
_side_vbox.children().clear ();
delete _side_vbox_pad;
_side_vbox_pad = 0;
for (std::vector<PortGroupUI*>::iterator i = _port_group_ui.begin(); i != _port_group_ui.end(); ++i) {
_visibility_checkbutton_box.remove ((*i)->get_visibility_checkbutton());
delete *i; delete *i;
} }
_port_group_ui.clear ();
} }
/** Set up the dialogue */
void void
PortMatrix::setup () PortMatrix::setup ()
{ {
/* sort out the ports that we'll offer to connect to */
_port_group_list.refresh (); _port_group_list.refresh ();
clear ();
_side_vbox_pad = new Label (""); /* unmanaged, explicitly deleted */ std::vector<boost::shared_ptr<ARDOUR::Bundle> > column;
std::vector<boost::shared_ptr<ARDOUR::Bundle> > row;
_side_vbox.pack_start (*_side_vbox_pad, false, false); for (PortGroupList::iterator i = _port_group_list.begin (); i != _port_group_list.end (); ++i) {
_side_vbox.pack_start (*manage (new Label (""))); if ((*i)->visible) {
std::copy ((*i)->bundles.begin(), (*i)->bundles.end(), std::back_inserter (column));
/* make a bundle for the ports, if there are any */
if (!(*i)->ports.empty()) {
matrix.clear (); boost::shared_ptr<ARDOUR::Bundle> b (new ARDOUR::Bundle ("", _type, !_offer_inputs));
std::string const pre = common_prefix ((*i)->ports);
if (!pre.empty()) {
b->set_name (pre.substr (0, pre.length() - 1));
}
/* Matrix and visibility checkbuttons */ for (uint32_t j = 0; j < (*i)->ports.size(); ++j) {
for (PortGroupList::iterator i = _port_group_list.begin(); i != _port_group_list.end(); ++i) { std::string const p = (*i)->ports[j];
b->add_channel (p.substr (pre.length()));
PortGroupUI* t = new PortGroupUI (*this, **i); b->set_port (j, p);
}
_port_group_ui.push_back (t);
column.push_back (b);
matrix.add_group (**i); }
_visibility_checkbutton_box.pack_start (t->get_visibility_checkbutton(), false, false);
CheckButton* chk = dynamic_cast<CheckButton*>(&t->get_visibility_checkbutton());
if (chk) {
chk->signal_toggled().connect (sigc::mem_fun (*this, &PortMatrix::reset_visibility));
} }
} }
show_all (); row.push_back (_our_bundle);
reset_visibility (); _body.setup (row, column);
setup_scrollbars ();
queue_draw ();
} }
void void
PortMatrix::reset_visibility () PortMatrix::set_offer_inputs (bool s)
{ {
for (std::vector<PortGroupUI*>::iterator i = _port_group_ui.begin(); i != _port_group_ui.end(); ++i) { _offer_inputs = s;
_port_group_list.set_offer_inputs (s);
(*i)->setup_visibility (); setup ();
if ((*i)->port_group().visible) {
matrix.show_group ((*i)->port_group());
} else {
matrix.hide_group ((*i)->port_group());
}
}
}
/** Handle a button press on a row label */
bool
PortMatrix::row_label_button_pressed (GdkEventButton* e, int r)
{
if (e->type != GDK_BUTTON_PRESS || e->button != 3) {
return false;
}
Menu* menu = manage (new Menu);
Menu_Helpers::MenuList& items = menu->items ();
menu->set_name ("ArdourContextMenu");
bool const can_add = maximum_rows () > n_rows ();
bool const can_remove = minimum_rows () < n_rows ();
std::string const name = row_name (r);
items.push_back (
Menu_Helpers::MenuElem (string_compose(_("Add %1"), row_descriptor()), sigc::mem_fun (*this, &PortMatrix::add_row))
);
items.back().set_sensitive (can_add);
items.push_back (
Menu_Helpers::MenuElem (string_compose(_("Remove %1 \"%2\""), row_descriptor(), name), sigc::bind (sigc::mem_fun (*this, &PortMatrix::remove_row), r))
);
items.back().set_sensitive (can_remove);
menu->popup (e->button, e->time);
return true;
} }
void void
@ -182,10 +131,91 @@ PortMatrix::set_type (ARDOUR::DataType t)
} }
void void
PortMatrix::set_offer_inputs (bool i) PortMatrix::hscroll_changed ()
{ {
_offer_inputs = i; _body.set_xoffset (_hscroll.get_adjustment()->get_value());
_port_group_list.set_offer_inputs (i);
setup ();
} }
void
PortMatrix::vscroll_changed ()
{
_body.set_yoffset (_vscroll.get_adjustment()->get_value());
}
void
PortMatrix::setup_scrollbars ()
{
Gtk::Adjustment* a = _hscroll.get_adjustment ();
a->set_lower (0);
a->set_upper (_body.full_scroll_width());
a->set_page_size (_body.alloc_scroll_width());
a->set_step_increment (32);
a->set_page_increment (128);
a = _vscroll.get_adjustment ();
a->set_lower (0);
a->set_upper (_body.full_scroll_height());
a->set_page_size (_body.alloc_scroll_height());
a->set_step_increment (32);
a->set_page_increment (128);
}
std::string
PortMatrix::common_prefix (std::vector<std::string> const & p) const
{
/* common prefix before '/' ? */
if (p[0].find_first_of ("/") != std::string::npos) {
std::string const fp = p[0].substr (0, (p[0].find_first_of ("/") + 1));
uint32_t j = 1;
while (j < p.size()) {
if (p[j].substr (0, fp.length()) != fp) {
break;
}
++j;
}
if (j == p.size()) {
return fp;
}
}
/* or before ':' ? */
if (p[0].find_first_of (":") != std::string::npos) {
std::string const fp = p[0].substr (0, (p[0].find_first_of (":") + 1));
uint32_t j = 1;
while (j < p.size()) {
if (p[j].substr (0, fp.length()) != fp) {
break;
}
++j;
}
if (j == p.size()) {
return fp;
}
}
return "";
}
void
PortMatrix::disassociate_all ()
{
for (PortGroupList::iterator i = _port_group_list.begin(); i != _port_group_list.end(); ++i) {
for (std::vector<boost::shared_ptr<ARDOUR::Bundle> >::iterator j = (*i)->bundles.begin(); j != (*i)->bundles.end(); ++j) {
for (uint32_t k = 0; k < (*j)->nchannels(); ++k) {
for (uint32_t l = 0; l < _our_bundle->nchannels(); ++l) {
set_state (
_our_bundle, l, *j, k, false, 0
);
}
}
}
}
_body.repaint_grid ();
}

View file

@ -1,5 +1,5 @@
/* /*
Copyright (C) 2002-2007 Paul Davis Copyright (C) 2002-2009 Paul Davis
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -17,32 +17,43 @@
*/ */
#ifndef __ardour_ui_port_matrix_h__ #ifndef __gtk_ardour_port_matrix_h__
#define __ardour_ui_port_matrix_h__ #define __gtk_ardour_port_matrix_h__
#include <list>
#include <gtkmm/box.h> #include <gtkmm/box.h>
#include <gtkmm/checkbutton.h> #include <gtkmm/scrollbar.h>
#include <gtkmm/table.h> #include <boost/shared_ptr.hpp>
#include <gtkmm/frame.h> #include "port_matrix_body.h"
#include <gtkmm/eventbox.h>
#include <gtkmm/scrolledwindow.h>
#include "ardour_dialog.h"
#include "port_group.h" #include "port_group.h"
#include "matrix.h"
/** The `port matrix' UI. This is a widget which lets the user alter
* associations between one set of ports and another. e.g. to connect
* things together.
*
* The columns are labelled with various ports from around Ardour and the
* system.
*
* It is made up of a body, PortMatrixBody, which is rendered using cairo,
* and some scrollbars. All of this is arranged inside the VBox that we
* inherit from.
*/
namespace ARDOUR { namespace ARDOUR {
class Session; class Bundle;
class IO;
class PortInsert;
} }
class PortMatrix : public Gtk::VBox { class PortMatrix : public Gtk::VBox
public: {
public:
PortMatrix (ARDOUR::Session&, ARDOUR::DataType, bool, PortGroupList::Mask); PortMatrix (ARDOUR::Session&, ARDOUR::DataType, bool, PortGroupList::Mask);
~PortMatrix (); ~PortMatrix ();
void setup (); virtual void setup ();
void set_offer_inputs (bool);
void set_type (ARDOUR::DataType);
bool offering_input () const { return _offer_inputs; }
void disassociate_all ();
enum Result { enum Result {
Cancelled, Cancelled,
@ -51,55 +62,63 @@ class PortMatrix : public Gtk::VBox {
sigc::signal<void, Result> Finished; sigc::signal<void, Result> Finished;
void set_type (ARDOUR::DataType); /** @param ab Our bundle.
void set_offer_inputs (bool); * @param ac Channel on our bundle.
bool offering_input() const { return _offer_inputs; } * @param bb Other bundle.
* @arapm bc Channel on other bundle.
/** @param r Our row index.
* @param p Other port.
* @param s New state. * @param s New state.
* @param k XXX * @param k XXX
*/ */
virtual void set_state (int r, std::string const & p, bool s, uint32_t k) = 0; virtual void set_state (
boost::shared_ptr<ARDOUR::Bundle> ab,
uint32_t ac,
boost::shared_ptr<ARDOUR::Bundle> bb,
uint32_t bc,
bool s,
uint32_t k
) = 0;
/** @param r Our row index. /** @param ab Our bundle.
* @param p Other port. * @param ac Channel on our bundle.
* @param bb Other bundle.
* @arapm bc Channel on other bundle.
* @return true if r is connected to p, otherwise false. * @return true if r is connected to p, otherwise false.
*/ */
virtual bool get_state (int r, std::string const &p) const = 0; virtual bool get_state (
boost::shared_ptr<ARDOUR::Bundle> ab,
uint32_t ac,
boost::shared_ptr<ARDOUR::Bundle> bb,
uint32_t bc
) const = 0;
virtual void add_channel (boost::shared_ptr<ARDOUR::Bundle>) = 0;
virtual void remove_channel (boost::shared_ptr<ARDOUR::Bundle>, uint32_t) = 0;
virtual bool can_rename_channels () const = 0;
virtual void rename_channel (boost::shared_ptr<ARDOUR::Bundle>, uint32_t) {}
virtual uint32_t n_rows () const = 0; void setup_scrollbars ();
virtual uint32_t maximum_rows () const = 0;
virtual uint32_t minimum_rows () const = 0;
virtual std::string row_name (int) const = 0;
virtual void add_row () = 0;
virtual void remove_row (int) = 0;
virtual std::string row_descriptor () const = 0;
Gtk::Widget& scrolled_window() { return _scrolled_window; } protected:
/// our bundle
protected: boost::shared_ptr<ARDOUR::Bundle> _our_bundle;
private:
void hscroll_changed ();
void vscroll_changed ();
std::string common_prefix (std::vector<std::string> const &) const;
/// true to offer inputs, otherwise false
bool _offer_inputs; bool _offer_inputs;
void set_ports (const std::list<std::string>&); /// list of port groups
private:
PortGroupList _port_group_list; PortGroupList _port_group_list;
/// port type that we are working with
ARDOUR::DataType _type; ARDOUR::DataType _type;
Matrix matrix;
std::vector<PortGroupUI*> _port_group_ui;
std::vector<Gtk::EventBox*> _row_labels;
Gtk::VBox* _row_labels_vbox;
Gtk::HBox _overall_hbox;
Gtk::VBox _side_vbox;
Gtk::HBox _port_group_hbox;
Gtk::ScrolledWindow _scrolled_window;
Gtk::Label* _side_vbox_pad;
Gtk::HBox _visibility_checkbutton_box;
void clear (); PortMatrixBody _body;
bool row_label_button_pressed (GdkEventButton*, int); Gtk::HScrollbar _hscroll;
void reset_visibility (); Gtk::VScrollbar _vscroll;
std::list<PortGroupUI*> _port_group_uis;
}; };
#endif #endif

View file

@ -0,0 +1,269 @@
/*
Copyright (C) 2002-2009 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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <iostream>
#include "ardour/bundle.h"
#include "port_matrix_body.h"
#include "port_matrix.h"
PortMatrixBody::PortMatrixBody (PortMatrix* p)
: _port_matrix (p),
_column_labels (this),
_row_labels (p, this),
_grid (p, this),
_alloc_width (0),
_alloc_height (0),
_alloc_xdiv (0),
_alloc_ydiv (0),
_xoffset (0),
_yoffset (0)
{
}
bool
PortMatrixBody::on_expose_event (GdkEventExpose* event)
{
Gdk::Rectangle const exposure (
event->area.x, event->area.y, event->area.width, event->area.height
);
Gdk::Rectangle const col (0, 0, _alloc_width, _alloc_ydiv);
Gdk::Rectangle const row (_alloc_xdiv, _alloc_ydiv, _alloc_width - _alloc_xdiv, _alloc_height - _alloc_ydiv);
Gdk::Rectangle const grid (0, _alloc_ydiv, _alloc_xdiv, _alloc_height - _alloc_ydiv);
bool intersects;
Gdk::Rectangle r = exposure;
r.intersect (col, intersects);
if (intersects) {
gdk_draw_drawable (
get_window()->gobj(),
get_style()->get_fg_gc (Gtk::STATE_NORMAL)->gobj(),
_column_labels.get_pixmap (get_window()->gobj()),
r.get_x() + _xoffset,
r.get_y(),
r.get_x(),
r.get_y(),
r.get_width(),
r.get_height()
);
}
r = exposure;
r.intersect (row, intersects);
if (intersects) {
gdk_draw_drawable (
get_window()->gobj(),
get_style()->get_fg_gc (Gtk::STATE_NORMAL)->gobj(),
_row_labels.get_pixmap (get_window()->gobj()),
r.get_x() - _alloc_xdiv,
r.get_y() + _yoffset - _alloc_ydiv,
r.get_x(),
r.get_y(),
r.get_width(),
r.get_height()
);
}
r = exposure;
r.intersect (grid, intersects);
if (intersects) {
gdk_draw_drawable (
get_window()->gobj(),
get_style()->get_fg_gc (Gtk::STATE_NORMAL)->gobj(),
_grid.get_pixmap (get_window()->gobj()),
r.get_x() + _xoffset,
r.get_y() + _yoffset - _alloc_ydiv,
r.get_x(),
r.get_y(),
r.get_width(),
r.get_height()
);
}
return true;
}
void
PortMatrixBody::on_size_request (Gtk::Requisition *req)
{
std::pair<int, int> const col = _column_labels.dimensions ();
std::pair<int, int> const row = _row_labels.dimensions ();
std::pair<int, int> const grid = _grid.dimensions ();
req->width = std::max (col.first, grid.first + row.first);
req->height = col.second + grid.second;
}
void
PortMatrixBody::on_size_allocate (Gtk::Allocation& alloc)
{
Gtk::EventBox::on_size_allocate (alloc);
set_allocation (alloc);
_alloc_width = alloc.get_width ();
_alloc_height = alloc.get_height ();
compute_divs ();
_port_matrix->setup_scrollbars ();
}
void
PortMatrixBody::compute_divs ()
{
std::pair<uint32_t, uint32_t> const col = _column_labels.dimensions ();
if (_alloc_height > col.second) {
/* allocated height is enough for the column labels */
_alloc_ydiv = col.second;
} else {
/* not enough space for the column labels */
_alloc_ydiv = _alloc_height;
}
std::pair<uint32_t, uint32_t> const grid = _grid.dimensions ();
std::pair<uint32_t, uint32_t> const row = _row_labels.dimensions ();
if (_alloc_width > (grid.first + row.first)) {
/* allocated width is larger than we need, so
put the x division at the extent of the grid */
_alloc_xdiv = grid.first;
} else if (_alloc_width > row.first) {
/* allocated width is large enough for the row labels
but not for the whole grid, so display the whole
row label section and cut part of the grid off */
_alloc_xdiv = _alloc_width - row.first;
} else {
/* allocated width isn't even enough for the row labels */
_alloc_xdiv = 0;
}
}
void
PortMatrixBody::setup (
std::vector<boost::shared_ptr<ARDOUR::Bundle> > const & row,
std::vector<boost::shared_ptr<ARDOUR::Bundle> > const & column
)
{
for (std::list<sigc::connection>::iterator i = _bundle_connections.begin(); i != _bundle_connections.end(); ++i) {
i->disconnect ();
}
_bundle_connections.clear ();
_row_bundles = row;
_column_bundles = column;
for (std::vector<boost::shared_ptr<ARDOUR::Bundle> >::iterator i = _row_bundles.begin(); i != _row_bundles.end(); ++i) {
_bundle_connections.push_back (
(*i)->NameChanged.connect (sigc::mem_fun (*this, &PortMatrixBody::repaint_row_labels))
);
}
for (std::vector<boost::shared_ptr<ARDOUR::Bundle> >::iterator i = _column_bundles.begin(); i != _column_bundles.end(); ++i) {
_bundle_connections.push_back (
(*i)->NameChanged.connect (sigc::mem_fun (*this, &PortMatrixBody::repaint_column_labels))
);
}
_column_labels.setup ();
_row_labels.setup ();
_grid.setup ();
compute_divs ();
}
uint32_t
PortMatrixBody::full_scroll_width ()
{
return _grid.dimensions().first;
}
uint32_t
PortMatrixBody::alloc_scroll_width ()
{
return _alloc_xdiv;
}
uint32_t
PortMatrixBody::full_scroll_height ()
{
return _grid.dimensions().second;
}
uint32_t
PortMatrixBody::alloc_scroll_height ()
{
return _alloc_height - _alloc_ydiv;
}
void
PortMatrixBody::set_xoffset (uint32_t xo)
{
_xoffset = xo;
queue_draw ();
}
void
PortMatrixBody::set_yoffset (uint32_t yo)
{
_yoffset = yo;
queue_draw ();
}
bool
PortMatrixBody::on_button_press_event (GdkEventButton* ev)
{
if (ev->x < _alloc_xdiv && ev->y > _alloc_ydiv) {
_grid.button_press (ev->x + _xoffset, ev->y + _yoffset - _alloc_ydiv, ev->button);
} else if (ev->x > _alloc_xdiv && ev->y > _alloc_ydiv) {
_row_labels.button_press (ev->x - _alloc_xdiv, ev->y + _yoffset - _alloc_ydiv, ev->button, ev->time);
} else {
return false;
}
return true;
}
void
PortMatrixBody::repaint_grid ()
{
_grid.require_render ();
queue_draw ();
}
void
PortMatrixBody::repaint_column_labels ()
{
_column_labels.require_render ();
queue_draw ();
}
void
PortMatrixBody::repaint_row_labels ()
{
_row_labels.require_render ();
queue_draw ();
}

View file

@ -0,0 +1,94 @@
/*
Copyright (C) 2002-2009 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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __gtk_ardour_port_matrix_body_h__
#define __gtk_ardour_port_matrix_body_h__
#include "port_matrix_column_labels.h"
#include "port_matrix_row_labels.h"
#include "port_matrix_grid.h"
class PortMatrix;
/** The main body of the port matrix. It is made up of three parts:
* column labels, grid and row labels, each drawn using cairo.
* This class handles the arrangement of these parts.
*/
class PortMatrixBody : public Gtk::EventBox
{
public:
PortMatrixBody (PortMatrix *);
/** @return bundles to offer for columns */
std::vector<boost::shared_ptr<ARDOUR::Bundle> > const & column_bundles () {
return _column_bundles;
}
/** @return bundles to offer for rows */
std::vector<boost::shared_ptr<ARDOUR::Bundle> > const & row_bundles () {
return _row_bundles;
}
void setup (
std::vector<boost::shared_ptr<ARDOUR::Bundle> > const &,
std::vector<boost::shared_ptr<ARDOUR::Bundle> > const &
);
uint32_t full_scroll_width ();
uint32_t alloc_scroll_width ();
uint32_t full_scroll_height ();
uint32_t alloc_scroll_height ();
void set_xoffset (uint32_t);
void set_yoffset (uint32_t);
void repaint_grid ();
protected:
bool on_expose_event (GdkEventExpose *);
void on_size_request (Gtk::Requisition *);
void on_size_allocate (Gtk::Allocation &);
bool on_button_press_event (GdkEventButton *);
private:
void compute_divs ();
void repaint_column_labels ();
void repaint_row_labels ();
PortMatrix* _port_matrix;
PortMatrixColumnLabels _column_labels;
PortMatrixRowLabels _row_labels;
PortMatrixGrid _grid;
uint32_t _alloc_width; ///< allocated width
uint32_t _alloc_height; ///< allocated height
uint32_t _alloc_xdiv; ///< position of the division between grid and row labels
uint32_t _alloc_ydiv; ///< position of the division between column labels and grid
uint32_t _xoffset;
uint32_t _yoffset;
/// bundles to offer for columns
std::vector<boost::shared_ptr<ARDOUR::Bundle> > _column_bundles;
/// bundles to offer for rows
std::vector<boost::shared_ptr<ARDOUR::Bundle> > _row_bundles;
std::list<sigc::connection> _bundle_connections;
};
#endif

View file

@ -0,0 +1,199 @@
/*
Copyright (C) 2002-2009 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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <iostream>
#include "ardour/bundle.h"
#include "port_matrix_column_labels.h"
#include "port_matrix.h"
PortMatrixColumnLabels::PortMatrixColumnLabels (PortMatrixBody* b)
: PortMatrixComponent (b)
{
}
void
PortMatrixColumnLabels::compute_dimensions ()
{
GdkPixmap* pm = gdk_pixmap_new (NULL, 1, 1, 24);
gdk_drawable_set_colormap (pm, gdk_colormap_get_system());
cairo_t* cr = gdk_cairo_create (pm);
/* width of the longest bundle name */
_longest_bundle_name = 0;
/* width of the longest channel name */
_longest_channel_name = 0;
/* height of highest bit of text */
_highest_text = 0;
for (std::vector<boost::shared_ptr<ARDOUR::Bundle> >::const_iterator i = _body->column_bundles().begin (); i != _body->column_bundles().end(); ++i) {
cairo_text_extents_t ext;
cairo_text_extents (cr, (*i)->name().c_str(), &ext);
if (ext.width > _longest_bundle_name) {
_longest_bundle_name = ext.width;
}
if (ext.height > _highest_text) {
_highest_text = ext.height;
}
for (uint32_t j = 0; j < (*i)->nchannels (); ++j) {
cairo_text_extents (
cr,
(*i)->channel_name (j).c_str(),
&ext
);
if (ext.width > _longest_channel_name) {
_longest_channel_name = ext.width;
}
if (ext.height > _highest_text) {
_highest_text = ext.height;
}
}
}
cairo_destroy (cr);
gdk_pixmap_unref (pm);
/* width and height of the whole thing */
_width = 0;
for (std::vector<boost::shared_ptr<ARDOUR::Bundle> >::const_iterator i = _body->column_bundles().begin (); i != _body->column_bundles().end(); ++i) {
_width += (*i)->nchannels() * column_width();
}
_height =
(_longest_bundle_name + _longest_channel_name + 4 * name_pad()) * sin (angle())
+ _highest_text * cos (angle());
_width += _height / tan (angle ());
}
uint32_t
PortMatrixColumnLabels::basic_text_x_pos (int c) const
{
return column_width() / 2 +
_highest_text / (2 * sin (angle ()));
}
void
PortMatrixColumnLabels::render (cairo_t* cr)
{
/* BACKGROUND */
set_source_rgb (cr, background_colour());
cairo_rectangle (cr, 0, 0, _width, _height);
cairo_fill (cr);
/* BUNDLE PARALLELOGRAM-TYPE-THING AND NAME */
uint32_t x = 0;
for (std::vector<boost::shared_ptr<ARDOUR::Bundle> >::const_iterator i = _body->column_bundles().begin (); i != _body->column_bundles().end(); ++i) {
Gdk::Color colour = get_a_bundle_colour (i - _body->column_bundles().begin ());
set_source_rgb (cr, colour);
uint32_t const w = (*i)->nchannels() * column_width();
uint32_t x_ = x;
uint32_t y_ = _height;
cairo_move_to (cr, x_, y_);
x_ += w;
cairo_line_to (cr, x_, y_);
x_ += _height / tan (angle ());
y_ -= _height;
cairo_line_to (cr, x_, y_);
x_ -= w;
cairo_line_to (cr, x_, y_);
cairo_line_to (cr, x, _height);
cairo_fill_preserve (cr);
set_source_rgb (cr, background_colour());
cairo_set_line_width (cr, label_border_width());
cairo_stroke (cr);
set_source_rgb (cr, text_colour());
uint32_t const rl = 3 * name_pad() + _longest_channel_name;
cairo_move_to (
cr,
x + basic_text_x_pos (0) + rl * cos (angle()),
_height - rl * sin (angle())
);
cairo_save (cr);
cairo_rotate (cr, -angle());
cairo_show_text (cr, (*i)->name().c_str());
cairo_restore (cr);
x += (*i)->nchannels () * column_width();
}
/* PORT NAMES */
x = 0;
for (std::vector<boost::shared_ptr<ARDOUR::Bundle> >::const_iterator i = _body->column_bundles().begin(); i != _body->column_bundles().end(); ++i) {
for (uint32_t j = 0; j < (*i)->nchannels(); ++j) {
uint32_t const p = _longest_channel_name + (2 * name_pad());
uint32_t const w = column_width();
uint32_t x_ = x;
uint32_t y_ = _height;
cairo_move_to (cr, x_, y_);
x_ += w;
cairo_line_to (cr, x_, y_);
x_ += p * cos (angle());
y_ -= p * sin (angle());
cairo_line_to (cr, x_, y_);
x_ -= column_width() * pow (sin (angle()), 2);
y_ -= column_width() * sin (angle()) * cos (angle());
cairo_line_to (cr, x_, y_);
cairo_line_to (cr, x, _height);
Gdk::Color colour = get_a_bundle_colour (i - _body->column_bundles().begin());
set_source_rgb (cr, colour);
cairo_fill_preserve (cr);
set_source_rgb (cr, background_colour());
cairo_set_line_width (cr, label_border_width());
cairo_stroke (cr);
set_source_rgb (cr, text_colour());
cairo_move_to (cr, x + basic_text_x_pos(j), _height - name_pad() * sin (angle()));
cairo_save (cr);
cairo_rotate (cr, -angle());
cairo_show_text (
cr,
(*i)->channel_name(j).c_str()
);
cairo_restore (cr);
x += column_width();
}
}
}

View file

@ -0,0 +1,49 @@
/*
Copyright (C) 2002-2009 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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __port_matrix_column_labels_h__
#define __port_matrix_column_labels_h__
#include <boost/shared_ptr.hpp>
#include "port_matrix_component.h"
class PortMatrixBody;
namespace ARDOUR {
class Bundle;
}
/** The column labels part of the port matrix */
class PortMatrixColumnLabels : public PortMatrixComponent
{
public:
PortMatrixColumnLabels (PortMatrixBody *);
private:
void render (cairo_t *);
void compute_dimensions ();
uint32_t basic_text_x_pos (int) const;
std::vector<boost::shared_ptr<ARDOUR::Bundle> > _bundles;
uint32_t _longest_bundle_name;
uint32_t _longest_channel_name;
uint32_t _highest_text;
};
#endif

View file

@ -0,0 +1,107 @@
/*
Copyright (C) 2002-2009 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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <iostream>
#include "port_matrix_component.h"
/** Constructor.
* @param p Port matrix that we're in.
*/
PortMatrixComponent::PortMatrixComponent (PortMatrixBody* b)
: _body (b),
_pixmap (0),
_render_required (true),
_dimension_computation_required (true)
{
}
/** Destructor */
PortMatrixComponent::~PortMatrixComponent ()
{
if (_pixmap) {
gdk_pixmap_unref (_pixmap);
}
}
void
PortMatrixComponent::setup ()
{
_dimension_computation_required = true;
_render_required = true;
}
GdkPixmap *
PortMatrixComponent::get_pixmap (GdkDrawable *drawable)
{
if (_render_required) {
if (_dimension_computation_required) {
compute_dimensions ();
_dimension_computation_required = false;
}
/* we may be zero width or height; if so, just
use the smallest allowable pixmap */
if (_width == 0) {
_width = 1;
}
if (_height == 0) {
_height = 1;
}
/* make a pixmap of the right size */
if (_pixmap) {
gdk_pixmap_unref (_pixmap);
}
_pixmap = gdk_pixmap_new (drawable, _width, _height, -1);
/* render */
cairo_t* cr = gdk_cairo_create (_pixmap);
render (cr);
cairo_destroy (cr);
_render_required = false;
}
return _pixmap;
}
void
PortMatrixComponent::set_source_rgb (cairo_t *cr, Gdk::Color const & c)
{
cairo_set_source_rgb (cr, c.get_red_p(), c.get_green_p(), c.get_blue_p());
}
void
PortMatrixComponent::set_source_rgba (cairo_t *cr, Gdk::Color const & c, double a)
{
cairo_set_source_rgba (cr, c.get_red_p(), c.get_green_p(), c.get_blue_p(), a);
}
std::pair<uint32_t, uint32_t>
PortMatrixComponent::dimensions ()
{
if (_dimension_computation_required) {
compute_dimensions ();
_dimension_computation_required = false;
}
return std::make_pair (_width, _height);
}

View file

@ -0,0 +1,136 @@
/*
Copyright (C) 2002-2009 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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __gtk_ardour_port_matrix_component_h__
#define __gtk_ardour_port_matrix_component_h__
#include <gtkmm/eventbox.h>
class PortMatrixBody;
/** One component of the PortMatrix. This is a cairo-rendered
* Pixmap.
*/
class PortMatrixComponent
{
public:
PortMatrixComponent (PortMatrixBody *);
virtual ~PortMatrixComponent ();
void setup ();
GdkPixmap* get_pixmap (GdkDrawable *);
std::pair<uint32_t, uint32_t> dimensions ();
void require_render () {
_render_required = true;
}
/** @return width of columns in the grid */
static uint32_t column_width () {
return 32;
}
/** @return height of rows in the grid */
static uint32_t row_height () {
return 32;
}
protected:
/** @return width of borders drawn around labels */
static uint32_t label_border_width () {
return 1;
}
/** @return padding between a name and the nearest line */
static uint32_t name_pad () {
return 8;
}
/** @return width of thin lines in the grid */
static uint32_t thin_grid_line_width () {
return 1;
}
/** @return width of thick lines in the grid */
static uint32_t thick_grid_line_width () {
return 2;
}
/** @return space around the connection indicator */
static uint32_t connection_indicator_pad () {
return 8;
}
/** @return angle of column labels, in radians */
static double angle () {
return M_PI / 4;
}
/* XXX I guess these colours should come from a theme, or something */
/* @return background colour */
static Gdk::Color background_colour () {
return Gdk::Color ("#000000");
}
/* @return text colour */
static Gdk::Color text_colour () {
return Gdk::Color ("#ffffff");
}
/* @return grid line colour */
static Gdk::Color grid_colour () {
return Gdk::Color ("#333333");
}
/* @return colour of association blobs */
static Gdk::Color association_colour () {
return Gdk::Color ("#00ff00");
}
/* XXX */
static Gdk::Color get_a_bundle_colour (int x) {
if ((x % 2) == 0) {
return Gdk::Color ("#547027");
} else {
return Gdk::Color ("#3552a6");
}
}
void set_source_rgb (cairo_t *, Gdk::Color const &);
void set_source_rgba (cairo_t *, Gdk::Color const &, double);
/** Render the complete component to a cairo context. */
virtual void render (cairo_t *) = 0;
/** Compute any required dimensions. This must set up
* _width and _height.
*/
virtual void compute_dimensions () = 0;
PortMatrixBody* _body; ///< the PortMatrixBody that we're in
uint32_t _width; ///< full width of the contents
uint32_t _height; ///< full height of the contents
private:
GdkPixmap* _pixmap; ///< pixmap
bool _render_required; ///< true if the rendered pixmap is out of date
bool _dimension_computation_required; ///< true if the dimensions are out of date
};
#endif

View file

@ -0,0 +1,195 @@
/*
Copyright (C) 2002-2009 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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <iostream>
#include <cairo/cairo.h>
#include "ardour/bundle.h"
#include "port_matrix_grid.h"
#include "port_matrix.h"
PortMatrixGrid::PortMatrixGrid (PortMatrix* p, PortMatrixBody* b)
: PortMatrixComponent (b),
_port_matrix (p)
{
}
void
PortMatrixGrid::compute_dimensions ()
{
_width = 0;
for (uint32_t i = 0; i < _body->column_bundles().size(); ++i) {
_width += _body->column_bundles()[i]->nchannels() * column_width();
}
_height = 0;
for (uint32_t i = 0; i < _body->row_bundles().size(); ++i) {
_height += _body->row_bundles()[i]->nchannels() * row_height();
}
}
void
PortMatrixGrid::render (cairo_t* cr)
{
/* BACKGROUND */
set_source_rgb (cr, background_colour());
cairo_rectangle (cr, 0, 0, _width, _height);
cairo_fill (cr);
/* VERTICAL GRID LINES */
set_source_rgb (cr, grid_colour());
uint32_t x = 0;
for (std::vector<boost::shared_ptr<ARDOUR::Bundle> >::size_type i = 0; i < _body->column_bundles().size(); ++i) {
cairo_set_line_width (cr, thin_grid_line_width());
for (uint32_t j = 1; j < _body->column_bundles()[i]->nchannels(); ++j) {
x += column_width();
cairo_move_to (cr, x, 0);
cairo_line_to (cr, x, _height);
cairo_stroke (cr);
}
if (i < (_body->column_bundles().size() - 1)) {
x += column_width();
cairo_set_line_width (cr, thick_grid_line_width());
cairo_move_to (cr, x, 0);
cairo_line_to (cr, x, _height);
cairo_stroke (cr);
}
}
uint32_t grid_width = x + column_width();
/* HORIZONTAL GRID LINES */
uint32_t y = 0;
for (std::vector<boost::shared_ptr<ARDOUR::Bundle> >::size_type i = 0; i < _body->row_bundles().size(); ++i) {
cairo_set_line_width (cr, thin_grid_line_width());
for (uint32_t j = 1; j < _body->row_bundles()[i]->nchannels(); ++j) {
y += row_height();
cairo_move_to (cr, 0, y);
cairo_line_to (cr, grid_width, y);
cairo_stroke (cr);
}
if (i < (_body->row_bundles().size() - 1)) {
y += row_height();
cairo_set_line_width (cr, thick_grid_line_width());
cairo_move_to (cr, 0, y);
cairo_line_to (cr, grid_width, y);
cairo_stroke (cr);
}
}
/* ASSOCIATION INDICATORS */
uint32_t bx = 0;
uint32_t by = 0;
for (std::vector<boost::shared_ptr<ARDOUR::Bundle> >::const_iterator i = _body->column_bundles().begin(); i < _body->column_bundles().end(); ++i) {
by = 0;
for (std::vector<boost::shared_ptr<ARDOUR::Bundle> >::const_iterator j = _body->row_bundles().begin(); j < _body->row_bundles().end(); ++j) {
x = bx;
for (uint32_t k = 0; k < (*i)->nchannels (); k++) {
y = by;
for (uint32_t l = 0; l < (*j)->nchannels (); ++l) {
if (_port_matrix->get_state (*j, l, *i, k)) {
set_source_rgba (cr, association_colour(), 0.5);
cairo_arc (
cr,
x + column_width() / 2,
y + column_width() / 2,
(column_width() - (2 * connection_indicator_pad())) / 2,
0,
2 * M_PI
);
cairo_fill (cr);
}
y += row_height();
}
x += column_width();
}
by += (*j)->nchannels () * row_height();
}
bx += (*i)->nchannels () * column_width();
}
}
void
PortMatrixGrid::button_press (double x, double y, int b)
{
uint32_t grid_column = x / column_width ();
uint32_t grid_row = y / row_height ();
boost::shared_ptr<ARDOUR::Bundle> our_bundle;
uint32_t our_channel = 0;
boost::shared_ptr<ARDOUR::Bundle> other_bundle;
uint32_t other_channel = 0;
for (std::vector<boost::shared_ptr<ARDOUR::Bundle> >::const_iterator i = _body->row_bundles().begin(); i != _body->row_bundles().end(); ++i) {
if (grid_row < (*i)->nchannels ()) {
our_bundle = *i;
our_channel = grid_row;
break;
} else {
grid_row -= (*i)->nchannels ();
}
}
for (std::vector<boost::shared_ptr<ARDOUR::Bundle> >::const_iterator i = _body->column_bundles().begin(); i != _body->column_bundles().end(); ++i) {
if (grid_column < (*i)->nchannels ()) {
other_bundle = *i;
other_channel = grid_column;
break;
} else {
grid_column -= (*i)->nchannels ();
}
}
if (our_bundle && other_bundle) {
bool const s = _port_matrix->get_state (
our_bundle, our_channel, other_bundle, other_channel
);
_port_matrix->set_state (
our_bundle, our_channel, other_bundle, other_channel,
!s, 0
);
require_render ();
_body->queue_draw ();
}
}

View file

@ -0,0 +1,53 @@
/*
Copyright (C) 2002-2009 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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __gtk_ardour_port_matrix_grid_h__
#define __gtk_ardour_port_matrix_grid_h__
#include <string>
#include <vector>
#include <boost/shared_ptr.hpp>
#include "port_matrix_component.h"
class PortMatrix;
class PortMatrixBody;
namespace ARDOUR {
class Bundle;
}
/// The grid part of the port matrix
class PortMatrixGrid : public PortMatrixComponent
{
public:
PortMatrixGrid (PortMatrix *, PortMatrixBody *);
void button_press (double, double, int);
private:
void compute_dimensions ();
void render (cairo_t *);
std::vector<boost::shared_ptr<ARDOUR::Bundle> > _column_bundles;
std::vector<boost::shared_ptr<ARDOUR::Bundle> > _row_bundles;
PortMatrix* _port_matrix;
};
#endif

View file

@ -0,0 +1,240 @@
/*
Copyright (C) 2002-2009 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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <iostream>
#include <boost/weak_ptr.hpp>
#include <gtkmm/menu.h>
#include <gtkmm/menushell.h>
#include <gtkmm/menu_elems.h>
#include <cairo/cairo.h>
#include "ardour/bundle.h"
#include "port_matrix_row_labels.h"
#include "port_matrix.h"
#include "i18n.h"
PortMatrixRowLabels::PortMatrixRowLabels (PortMatrix* p, PortMatrixBody* b)
: PortMatrixComponent (b), _port_matrix (p), _menu (0)
{
}
PortMatrixRowLabels::~PortMatrixRowLabels ()
{
delete _menu;
}
void
PortMatrixRowLabels::compute_dimensions ()
{
GdkPixmap* pm = gdk_pixmap_new (NULL, 1, 1, 24);
gdk_drawable_set_colormap (pm, gdk_colormap_get_system());
cairo_t* cr = gdk_cairo_create (pm);
_longest_port_name = 0;
for (std::vector<boost::shared_ptr<ARDOUR::Bundle> >::const_iterator i = _body->row_bundles().begin(); i != _body->row_bundles().end(); ++i) {
for (uint32_t j = 0; j < (*i)->nchannels(); ++j) {
cairo_text_extents_t ext;
cairo_text_extents (cr, (*i)->channel_name(j).c_str(), &ext);
if (ext.width > _longest_port_name) {
_longest_port_name = ext.width;
}
}
}
_longest_bundle_name = 0;
for (std::vector<boost::shared_ptr<ARDOUR::Bundle> >::const_iterator i = _body->row_bundles().begin(); i != _body->row_bundles().end(); ++i) {
cairo_text_extents_t ext;
cairo_text_extents (cr, (*i)->name().c_str(), &ext);
if (ext.width > _longest_bundle_name) {
_longest_bundle_name = ext.width;
}
}
cairo_destroy (cr);
gdk_pixmap_unref (pm);
_height = 0;
for (std::vector<boost::shared_ptr<ARDOUR::Bundle> >::const_iterator i = _body->row_bundles().begin(); i != _body->row_bundles().end(); ++i) {
_height += (*i)->nchannels() * row_height();
}
_width = _longest_port_name +
name_pad() * 4 +
_longest_bundle_name + name_pad() * 2;
}
void
PortMatrixRowLabels::render (cairo_t* cr)
{
/* BACKGROUND */
set_source_rgb (cr, background_colour());
cairo_rectangle (cr, 0, 0, _width, _height);
cairo_fill (cr);
/* SIDE BUNDLE NAMES */
uint32_t x = _longest_port_name + name_pad() * 3;
uint32_t y = 0;
for (std::vector<boost::shared_ptr<ARDOUR::Bundle> >::const_iterator i = _body->row_bundles().begin(); i != _body->row_bundles().end(); ++i) {
Gdk::Color const colour = get_a_bundle_colour (i - _body->row_bundles().begin ());
set_source_rgb (cr, colour);
cairo_rectangle (
cr,
0,
y,
_longest_port_name + name_pad() * 4 + _longest_bundle_name + name_pad() * 2,
row_height() * (*i)->nchannels()
);
cairo_fill_preserve (cr);
set_source_rgb (cr, background_colour());
cairo_set_line_width (cr, label_border_width ());
cairo_stroke (cr);
uint32_t off = 0;
if ((*i)->nchannels () > 0) {
/* use the extent of our first channel name so that the bundle name is vertically aligned with it */
cairo_text_extents_t ext;
cairo_text_extents (cr, (*i)->channel_name(0).c_str(), &ext);
off = (row_height() - ext.height) / 2;
} else {
off = row_height() / 2;
}
set_source_rgb (cr, text_colour());
cairo_move_to (cr, x, y + name_pad() + off);
cairo_show_text (cr, (*i)->name().c_str());
y += row_height() * (*i)->nchannels ();
}
/* SIDE PORT NAMES */
y = 0;
for (std::vector<boost::shared_ptr<ARDOUR::Bundle> >::const_iterator i = _body->row_bundles().begin(); i != _body->row_bundles().end(); ++i) {
for (uint32_t j = 0; j < (*i)->nchannels(); ++j) {
Gdk::Color const colour = get_a_bundle_colour (i - _body->row_bundles().begin ());
set_source_rgb (cr, colour);
cairo_rectangle (
cr,
0,
y,
_longest_port_name + (name_pad() * 2),
row_height()
);
cairo_fill_preserve (cr);
set_source_rgb (cr, background_colour());
cairo_set_line_width (cr, label_border_width ());
cairo_stroke (cr);
cairo_text_extents_t ext;
cairo_text_extents (cr, (*i)->channel_name(j).c_str(), &ext);
uint32_t const off = (row_height() - ext.height) / 2;
set_source_rgb (cr, text_colour());
cairo_move_to (cr, name_pad(), y + name_pad() + off);
cairo_show_text (cr, (*i)->channel_name(j).c_str());
y += row_height();
}
}
}
void
PortMatrixRowLabels::button_press (double x, double y, int b, uint32_t t)
{
if (b == 3 && x < (_longest_port_name + name_pad() * 2) ) {
delete _menu;
_menu = new Gtk::Menu;
_menu->set_name ("ArdourContextMenu");
Gtk::Menu_Helpers::MenuList& items = _menu->items ();
uint32_t row = y / row_height ();
boost::shared_ptr<ARDOUR::Bundle> bundle;
uint32_t channel;
for (std::vector<boost::shared_ptr<ARDOUR::Bundle> >::const_iterator i = _body->row_bundles().begin(); i != _body->row_bundles().end(); ++i) {
if (row < (*i)->nchannels ()) {
bundle = *i;
channel = row;
break;
} else {
row -= (*i)->nchannels ();
}
}
if (bundle) {
char buf [64];
if (_port_matrix->can_rename_channels ()) {
snprintf (buf, sizeof (buf), _("Rename '%s'..."), bundle->channel_name (channel).c_str());
items.push_back (
Gtk::Menu_Helpers::MenuElem (
buf,
sigc::bind (sigc::mem_fun (*this, &PortMatrixRowLabels::rename_channel_proxy), bundle, channel)
)
);
}
snprintf (buf, sizeof (buf), _("Remove '%s'"), bundle->channel_name (channel).c_str());
items.push_back (
Gtk::Menu_Helpers::MenuElem (
buf,
sigc::bind (sigc::mem_fun (*this, &PortMatrixRowLabels::remove_channel_proxy), bundle, channel)
)
);
_menu->popup (1, t);
}
}
}
void
PortMatrixRowLabels::remove_channel_proxy (boost::weak_ptr<ARDOUR::Bundle> b, uint32_t c)
{
boost::shared_ptr<ARDOUR::Bundle> sb = b.lock ();
if (!sb) {
return;
}
_port_matrix->remove_channel (sb, c);
}
void
PortMatrixRowLabels::rename_channel_proxy (boost::weak_ptr<ARDOUR::Bundle> b, uint32_t c)
{
boost::shared_ptr<ARDOUR::Bundle> sb = b.lock ();
if (!sb) {
return;
}
_port_matrix->rename_channel (sb, c);
}

View file

@ -0,0 +1,57 @@
/*
Copyright (C) 2002-2009 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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __port_matrix_row_labels_h__
#define __port_matrix_row_labels_h__
#include <boost/shared_ptr.hpp>
#include "port_matrix_component.h"
class PortMatrix;
class PortMatrixBody;
namespace ARDOUR {
class Bundle;
}
namespace Gtk {
class Menu;
}
class PortMatrixRowLabels : public PortMatrixComponent
{
public:
PortMatrixRowLabels (PortMatrix *, PortMatrixBody *);
~PortMatrixRowLabels ();
void button_press (double, double, int, uint32_t);
private:
void render (cairo_t *);
void compute_dimensions ();
void remove_channel_proxy (boost::weak_ptr<ARDOUR::Bundle>, uint32_t);
void rename_channel_proxy (boost::weak_ptr<ARDOUR::Bundle>, uint32_t);
PortMatrix* _port_matrix;
uint32_t _longest_port_name;
uint32_t _longest_bundle_name;
Gtk::Menu* _menu;
};
#endif

View file

@ -320,13 +320,13 @@ RouteParams_UI::setup_io_frames()
cleanup_io_frames(); cleanup_io_frames();
// input // input
_input_iosel = new IOSelector (*session, _route, true); _input_iosel = new IOSelector (*session, _route, false);
_input_iosel->setup (); _input_iosel->setup ();
input_frame.add (*_input_iosel); input_frame.add (*_input_iosel);
input_frame.show_all(); input_frame.show_all();
// output // output
_output_iosel = new IOSelector (*session, _route, false); _output_iosel = new IOSelector (*session, _route, true);
_output_iosel->setup (); _output_iosel->setup ();
output_frame.add (*_output_iosel); output_frame.add (*_output_iosel);
output_frame.show_all(); output_frame.show_all();

View file

@ -21,17 +21,21 @@
#define __ardour_bundle_h__ #define __ardour_bundle_h__
#include <string> #include <string>
#include <vector>
#include <glibmm/thread.h>
#include <sigc++/signal.h> #include <sigc++/signal.h>
#include "ardour/data_type.h" #include "ardour/data_type.h"
namespace ARDOUR { namespace ARDOUR {
/** A set of `channels', each of which is associated with 0 or more ports. /** A set of `channels', each of which is associated with 0 or more ports.
* Each channel has a name which can be anything useful.
* Intended for grouping things like, for example, a buss' outputs. * Intended for grouping things like, for example, a buss' outputs.
* `Channel' is a rather overloaded term but I can't think of a better * `Channel' is a rather overloaded term but I can't think of a better
* one right now. * one right now.
*/ */
class Bundle : public sigc::trackable { class Bundle : public sigc::trackable
{
public: public:
/// List of ports associated with a channel. We can't use a /// List of ports associated with a channel. We can't use a
@ -39,6 +43,17 @@ class Bundle : public sigc::trackable {
/// (ie those without a Port object) /// (ie those without a Port object)
typedef std::vector<std::string> PortList; typedef std::vector<std::string> PortList;
struct Channel {
Channel (std::string n) : name (n) {}
bool operator== (Channel const &o) const {
return name == o.name && ports == o.ports;
}
std::string name;
PortList ports;
};
/** Construct an audio bundle. /** Construct an audio bundle.
* @param i true if ports are inputs, otherwise false. * @param i true if ports are inputs, otherwise false.
*/ */
@ -50,6 +65,13 @@ class Bundle : public sigc::trackable {
*/ */
Bundle (std::string const & n, bool i = true) : _name (n), _type (DataType::AUDIO), _ports_are_inputs (i) {} Bundle (std::string const & n, bool i = true) : _name (n), _type (DataType::AUDIO), _ports_are_inputs (i) {}
/** Construct a bundle.
* @param n Name.
* @param t Type.
* @param i true if ports are inputs, otherwise false.
*/
Bundle (std::string const & n, DataType t, bool i = true) : _name (n), _type (t), _ports_are_inputs (i) {}
virtual ~Bundle() {} virtual ~Bundle() {}
/** @return Number of channels that this Bundle has */ /** @return Number of channels that this Bundle has */
@ -60,13 +82,16 @@ class Bundle : public sigc::trackable {
*/ */
PortList const & channel_ports (uint32_t) const; PortList const & channel_ports (uint32_t) const;
void add_channel (); void add_channel (std::string const &);
std::string channel_name (uint32_t) const;
void set_channel_name (uint32_t, std::string const &);
void add_port_to_channel (uint32_t, std::string); void add_port_to_channel (uint32_t, std::string);
void set_port (uint32_t, std::string); void set_port (uint32_t, std::string);
void remove_port_from_channel (uint32_t, std::string); void remove_port_from_channel (uint32_t, std::string);
void set_nchannels (uint32_t);
bool port_attached_to_channel (uint32_t, std::string); bool port_attached_to_channel (uint32_t, std::string);
bool uses_port (std::string) const;
void remove_channel (uint32_t); void remove_channel (uint32_t);
void remove_channels ();
/** Set the name. /** Set the name.
* @param n New name. * @param n New name.
@ -94,7 +119,7 @@ class Bundle : public sigc::trackable {
bool operator== (Bundle const &) const; bool operator== (Bundle const &) const;
/** Emitted when the name changes */ /** Emitted when the bundle name or a channel name has changed */
sigc::signal<void> NameChanged; sigc::signal<void> NameChanged;
/** The number of channels has changed */ /** The number of channels has changed */
sigc::signal<void> ConfigurationChanged; sigc::signal<void> ConfigurationChanged;
@ -103,10 +128,10 @@ class Bundle : public sigc::trackable {
protected: protected:
/// mutex for _ports; /// mutex for _channel_ports and _channel_names
/// XXX: is this necessary? /// XXX: is this necessary?
mutable Glib::Mutex _ports_mutex; mutable Glib::Mutex _channel_mutex;
std::vector<PortList> _ports; std::vector<Channel> _channel;
private: private:
int set_channels (std::string const &); int set_channels (std::string const &);

View file

@ -32,8 +32,8 @@ using namespace PBD;
uint32_t uint32_t
Bundle::nchannels () const Bundle::nchannels () const
{ {
Glib::Mutex::Lock lm (_ports_mutex); Glib::Mutex::Lock lm (_channel_mutex);
return _ports.size (); return _channel.size ();
} }
Bundle::PortList const & Bundle::PortList const &
@ -41,8 +41,8 @@ Bundle::channel_ports (uint32_t c) const
{ {
assert (c < nchannels()); assert (c < nchannels());
Glib::Mutex::Lock lm (_ports_mutex); Glib::Mutex::Lock lm (_channel_mutex);
return _ports[c]; return _channel[c].ports;
} }
/** Add an association between one of our channels and a port. /** Add an association between one of our channels and a port.
@ -55,8 +55,8 @@ Bundle::add_port_to_channel (uint32_t ch, string portname)
assert (ch < nchannels()); assert (ch < nchannels());
{ {
Glib::Mutex::Lock lm (_ports_mutex); Glib::Mutex::Lock lm (_channel_mutex);
_ports[ch].push_back (portname); _channel[ch].ports.push_back (portname);
} }
PortsChanged (ch); /* EMIT SIGNAL */ PortsChanged (ch); /* EMIT SIGNAL */
@ -74,8 +74,8 @@ Bundle::remove_port_from_channel (uint32_t ch, string portname)
bool changed = false; bool changed = false;
{ {
Glib::Mutex::Lock lm (_ports_mutex); Glib::Mutex::Lock lm (_channel_mutex);
PortList& pl = _ports[ch]; PortList& pl = _channel[ch].ports;
PortList::iterator i = find (pl.begin(), pl.end(), portname); PortList::iterator i = find (pl.begin(), pl.end(), portname);
if (i != pl.end()) { if (i != pl.end()) {
@ -95,48 +95,31 @@ Bundle::remove_port_from_channel (uint32_t ch, string portname)
bool bool
Bundle::operator== (const Bundle& other) const Bundle::operator== (const Bundle& other) const
{ {
return other._ports == _ports; return other._channel == _channel;
} }
/** Set the number of channels.
* @param n New number of channels.
*/
void
Bundle::set_nchannels (uint32_t n)
{
{
Glib::Mutex::Lock lm (_ports_mutex);
_ports.clear ();
for (uint32_t i = 0; i < n; ++i) {
_ports.push_back (PortList());
}
}
ConfigurationChanged (); /* EMIT SIGNAL */
}
void void
Bundle::set_port (uint32_t ch, string portname) Bundle::set_port (uint32_t ch, string portname)
{ {
assert (ch < nchannels()); assert (ch < nchannels());
{ {
Glib::Mutex::Lock lm (_ports_mutex); Glib::Mutex::Lock lm (_channel_mutex);
_ports[ch].clear (); _channel[ch].ports.clear ();
_ports[ch].push_back (portname); _channel[ch].ports.push_back (portname);
} }
PortsChanged (ch); /* EMIT SIGNAL */ PortsChanged (ch); /* EMIT SIGNAL */
} }
/** @param n Channel name */
void void
Bundle::add_channel () Bundle::add_channel (std::string const & n)
{ {
{ {
Glib::Mutex::Lock lm (_ports_mutex); Glib::Mutex::Lock lm (_channel_mutex);
_ports.push_back (PortList ()); _channel.push_back (Channel (n));
} }
ConfigurationChanged (); /* EMIT SIGNAL */ ConfigurationChanged (); /* EMIT SIGNAL */
@ -147,8 +130,8 @@ Bundle::port_attached_to_channel (uint32_t ch, std::string portname)
{ {
assert (ch < nchannels()); assert (ch < nchannels());
Glib::Mutex::Lock lm (_ports_mutex); Glib::Mutex::Lock lm (_channel_mutex);
return (std::find (_ports[ch].begin (), _ports[ch].end (), portname) != _ports[ch].end ()); return (std::find (_channel[ch].ports.begin (), _channel[ch].ports.end (), portname) != _channel[ch].ports.end ());
} }
void void
@ -156,6 +139,52 @@ Bundle::remove_channel (uint32_t ch)
{ {
assert (ch < nchannels ()); assert (ch < nchannels ());
Glib::Mutex::Lock lm (_ports_mutex); Glib::Mutex::Lock lm (_channel_mutex);
_ports.erase (_ports.begin () + ch); _channel.erase (_channel.begin () + ch);
}
void
Bundle::remove_channels ()
{
Glib::Mutex::Lock lm (_channel_mutex);
_channel.clear ();
}
bool
Bundle::uses_port (std::string p) const
{
Glib::Mutex::Lock lm (_channel_mutex);
for (std::vector<Channel>::const_iterator i = _channel.begin(); i != _channel.end(); ++i) {
for (PortList::const_iterator j = i->ports.begin(); j != i->ports.end(); ++j) {
if (*j == p) {
return true;
}
}
}
return false;
}
std::string
Bundle::channel_name (uint32_t ch) const
{
assert (ch < nchannels());
Glib::Mutex::Lock lm (_channel_mutex);
return _channel[ch].name;
}
void
Bundle::set_channel_name (uint32_t ch, std::string const & n)
{
assert (ch < nchannels());
{
Glib::Mutex::Lock lm (_channel_mutex);
_channel[ch].name = n;
}
NameChanged (); /* EMIT SIGNAL */
} }

View file

@ -593,7 +593,7 @@ IO::remove_output_port (Port* port, void* src)
PortCountChanged (n_outputs()); /* EMIT SIGNAL */ PortCountChanged (n_outputs()); /* EMIT SIGNAL */
} }
if (change == ConnectionsChanged) { if (change == ConfigurationChanged) {
setup_bundles_for_inputs_and_outputs (); setup_bundles_for_inputs_and_outputs ();
} }
@ -2592,19 +2592,24 @@ IO::setup_bundles_for_inputs_and_outputs ()
{ {
char buf[32]; char buf[32];
_bundle_for_inputs->remove_channels ();
_bundle_for_outputs->remove_channels ();
snprintf(buf, sizeof (buf), _("%s in"), _name.c_str()); snprintf(buf, sizeof (buf), _("%s in"), _name.c_str());
_bundle_for_inputs->set_name (buf); _bundle_for_inputs->set_name (buf);
uint32_t const ni = inputs().num_ports(); uint32_t const ni = inputs().num_ports();
_bundle_for_inputs->set_nchannels (ni);
for (uint32_t i = 0; i < ni; ++i) { for (uint32_t i = 0; i < ni; ++i) {
snprintf (buf, sizeof(buf), _("in %d"), (i + 1));
_bundle_for_inputs->add_channel (buf);
_bundle_for_inputs->set_port (i, inputs().port(i)->name()); _bundle_for_inputs->set_port (i, inputs().port(i)->name());
} }
snprintf(buf, sizeof (buf), _("%s out"), _name.c_str()); snprintf(buf, sizeof (buf), _("%s out"), _name.c_str());
_bundle_for_outputs->set_name (buf); _bundle_for_outputs->set_name (buf);
uint32_t const no = outputs().num_ports(); uint32_t const no = outputs().num_ports();
_bundle_for_outputs->set_nchannels (no);
for (uint32_t i = 0; i < no; ++i) { for (uint32_t i = 0; i < no; ++i) {
snprintf (buf, sizeof(buf), _("out %d"), (i + 1));
_bundle_for_outputs->add_channel (buf);
_bundle_for_outputs->set_port (i, outputs().port(i)->name()); _bundle_for_outputs->set_port (i, outputs().port(i)->name());
} }
} }

View file

@ -598,7 +598,7 @@ Session::when_engine_running ()
snprintf (buf, sizeof (buf), _("out %" PRIu32), np+1); snprintf (buf, sizeof (buf), _("out %" PRIu32), np+1);
shared_ptr<Bundle> c (new Bundle (buf, true)); shared_ptr<Bundle> c (new Bundle (buf, true));
c->set_nchannels (1); c->add_channel (_("mono"));
c->set_port (0, _engine.get_nth_physical_output (DataType::AUDIO, np)); c->set_port (0, _engine.get_nth_physical_output (DataType::AUDIO, np));
add_bundle (c); add_bundle (c);
@ -609,7 +609,7 @@ Session::when_engine_running ()
snprintf (buf, sizeof (buf), _("in %" PRIu32), np+1); snprintf (buf, sizeof (buf), _("in %" PRIu32), np+1);
shared_ptr<Bundle> c (new Bundle (buf, false)); shared_ptr<Bundle> c (new Bundle (buf, false));
c->set_nchannels (1); c->add_channel (_("mono"));
c->set_port (0, _engine.get_nth_physical_input (DataType::AUDIO, np)); c->set_port (0, _engine.get_nth_physical_input (DataType::AUDIO, np));
add_bundle (c); add_bundle (c);
@ -622,8 +622,9 @@ Session::when_engine_running ()
snprintf (buf, sizeof (buf), _("out %" PRIu32 "+%" PRIu32), np+1, np+2); snprintf (buf, sizeof (buf), _("out %" PRIu32 "+%" PRIu32), np+1, np+2);
shared_ptr<Bundle> c (new Bundle (buf, true)); shared_ptr<Bundle> c (new Bundle (buf, true));
c->set_nchannels (2); c->add_channel (_("left"));
c->set_port (0, _engine.get_nth_physical_output (DataType::AUDIO, np)); c->set_port (0, _engine.get_nth_physical_output (DataType::AUDIO, np));
c->add_channel (_("right"));
c->set_port (1, _engine.get_nth_physical_output (DataType::AUDIO, np + 1)); c->set_port (1, _engine.get_nth_physical_output (DataType::AUDIO, np + 1));
add_bundle (c); add_bundle (c);
@ -634,8 +635,9 @@ Session::when_engine_running ()
snprintf (buf, sizeof (buf), _("in %" PRIu32 "+%" PRIu32), np+1, np+2); snprintf (buf, sizeof (buf), _("in %" PRIu32 "+%" PRIu32), np+1, np+2);
shared_ptr<Bundle> c (new Bundle (buf, false)); shared_ptr<Bundle> c (new Bundle (buf, false));
c->set_nchannels (2); c->add_channel (_("left"));
c->set_port (0, _engine.get_nth_physical_input (DataType::AUDIO, np)); c->set_port (0, _engine.get_nth_physical_input (DataType::AUDIO, np));
c->add_channel (_("right"));
c->set_port (1, _engine.get_nth_physical_input (DataType::AUDIO, np + 1)); c->set_port (1, _engine.get_nth_physical_input (DataType::AUDIO, np + 1));
add_bundle (c); add_bundle (c);
@ -2003,13 +2005,6 @@ Session::add_routes (RouteList& new_routes, bool save)
if ((*x)->is_control()) { if ((*x)->is_control()) {
_control_out = (*x); _control_out = (*x);
} }
/* only busses get automatic bundles formed */
if (!boost::dynamic_pointer_cast<Track> (*x)) {
add_bundle ((*x)->bundle_for_inputs());
add_bundle ((*x)->bundle_for_outputs());
}
} }
if (_control_out && IO::connecting_legal) { if (_control_out && IO::connecting_legal) {

View file

@ -45,7 +45,7 @@ ARDOUR::UserBundle::set_state (XMLNode const & node)
return -1; return -1;
} }
add_channel (); add_channel ("XXX");
XMLNodeList const ports = (*i)->children (); XMLNodeList const ports = (*i)->children ();
@ -83,13 +83,13 @@ ARDOUR::UserBundle::get_state ()
node->add_property ("name", name ()); node->add_property ("name", name ());
{ {
Glib::Mutex::Lock lm (_ports_mutex); Glib::Mutex::Lock lm (_channel_mutex);
for (std::vector<PortList>::iterator i = _ports.begin(); i != _ports.end(); ++i) { for (std::vector<Channel>::iterator i = _channel.begin(); i != _channel.end(); ++i) {
XMLNode* c = new XMLNode ("Channel"); XMLNode* c = new XMLNode ("Channel");
c->add_property ("name", i->name);
for (PortList::iterator j = i->begin(); j != i->end(); ++j) { for (PortList::iterator j = i->ports.begin(); j != i->ports.end(); ++j) {
XMLNode* p = new XMLNode ("Port"); XMLNode* p = new XMLNode ("Port");
p->add_property ("name", *j); p->add_property ("name", *j);
c->add_child_nocopy (*p); c->add_child_nocopy (*p);