#include "ardour/midi_track.h"
#include "ardour/data_type.h"
#include "io_selector.h"
+#include "keyboard.h"
#include "utils.h"
#include "gui_thread.h"
#include "i18n.h"
+using namespace Gtk;
+
/** Add a port to a group.
* @param p Port name, with or without prefix.
*/
*/
PortGroupUI::PortGroupUI (PortMatrix& m, PortGroup& g)
- : _port_matrix (m), _port_group (g), _ignore_check_button_toggle (false),
- _visibility_checkbutton (g.name)
+ : _port_matrix (m)
+ , _port_group (g)
+ , _ignore_check_button_toggle (false)
+ , _visibility_checkbutton (g.name)
{
int const ports = _port_group.ports.size();
int const rows = _port_matrix.n_rows ();
_port_checkbuttons[i].resize (ports);
}
+
for (int i = 0; i < rows; ++i) {
for (uint32_t j = 0; j < _port_group.ports.size(); ++j) {
- Gtk::CheckButton* b = new Gtk::CheckButton;
+ CheckButton* b = new CheckButton;
b->signal_toggled().connect (
- sigc::bind (sigc::mem_fun (*this, &PortGroupUI::port_checkbutton_toggled), b, i, j)
- );
+ sigc::bind (sigc::mem_fun (*this, &PortGroupUI::port_checkbutton_toggled), b, i, j));
+ b->signal_button_release_event().connect (
+ sigc::bind (sigc::mem_fun (*this, &PortGroupUI::port_checkbutton_release), b, i, j), false);
+
_port_checkbuttons[i][j] = b;
+
+ cerr << this << " bind to " << &_port_checkbuttons << " via " << b
+ << endl;
+
_table.attach (*b, j, j + 1, i, i + 1);
}
}
-
+
_table_box.add (_table);
_ignore_check_button_toggle = true;
PortGroupUI::visibility_checkbutton_toggled ()
{
_port_group.visible = _visibility_checkbutton.get_active ();
- setup_visibility ();
}
/** @return Width and height of a single checkbutton in a port group table */
}
/** @return Table widget containing the port checkbuttons */
-Gtk::Widget&
+Widget&
PortGroupUI::get_table ()
{
return _table_box;
}
/** @return Checkbutton used to toggle visibility */
-Gtk::Widget&
+Widget&
PortGroupUI::get_visibility_checkbutton ()
{
return _visibility_checkbutton;
/** Handle a toggle of a port check button */
void
-PortGroupUI::port_checkbutton_toggled (Gtk::CheckButton* b, int r, int c)
+PortGroupUI::port_checkbutton_toggled (CheckButton* b, int r, int c)
{
if (_ignore_check_button_toggle == false) {
- _port_matrix.set_state (r, _port_group.prefix + _port_group.ports[c], b->get_active());
+ _port_matrix.set_state (r, _port_group.prefix + _port_group.ports[c], b->get_active(), 0);
}
}
+bool
+PortGroupUI::port_checkbutton_release (GdkEventButton* ev, CheckButton* b, int r, int c)
+{
+ cerr << this << " RELEASE on " << b << " state = " << ev->state << endl;
+
+ if (ev->state == 0) {
+ /* let usual toggle handler take care of it */
+ return false;
+ }
+
+ /* The fun starts here
+ */
+
+ const size_t ports = _port_group.ports.size();
+ const size_t rows = _port_matrix.n_rows ();
+
+ if (rows == 0 || ports == 0) {
+ return true;
+ }
+
+ /* For each port in the group, change the state of
+ the connection for the corresponding "connector" port.
+ */
+
+ for (size_t j = c; j < ports; ++j) {
+
+ /* we've got a port to connect, now lets find the thing to
+ connect it too ... (search "down" the rows)
+ */
+
+ cerr << "we're going to connect port " << j << " of " << ports << endl;
+
+ for (size_t i = r; i < rows; ++i) {
+
+ cerr << this << " going to connect to row " << i << " of " << rows << endl;
+ cerr << "access [" << i << "][" << j << "]\n";
+ cerr << " @ " << &_port_checkbuttons << endl;
+
+ _port_checkbuttons[i][j]->set_active (!_port_checkbuttons[i][j]->get_active());
+
+ /* next time, get at least as far as this port before looking
+ for more.
+ */
+
+ r = i + 1;
+
+ /* changed connection state, stop looking for more */
+
+ break;
+ }
+ }
+
+ return true;
+}
+
/** Set up visibility of the port group according to PortGroup::visible */
void
PortGroupUI::setup_visibility ()
{
- if (_port_group.visible) {
+ if (!_port_group.ports.empty() && _port_group.visible) {
_table_box.show ();
} else {
_table_box.hide ();
}
if (_visibility_checkbutton.get_active () != _port_group.visible) {
+
_visibility_checkbutton.set_active (_port_group.visible);
}
}
-
RotatedLabelSet::RotatedLabelSet (PortGroupList& g)
- : Glib::ObjectBase ("RotatedLabelSet"), Gtk::Widget (), _port_group_list (g), _base_width (128)
+ : Glib::ObjectBase ("RotatedLabelSet"), Widget (), _port_group_list (g), _base_width (128)
{
- set_flags (Gtk::NO_WINDOW);
- set_angle (30);
+ set_flags (NO_WINDOW);
+ if (getenv ("AD_ANGLE") != 0) {
+ set_angle (atoi (getenv ("AD_ANGLE")));
+ } else {
+ set_angle (45);
+ }
}
RotatedLabelSet::~RotatedLabelSet ()
}
void
-RotatedLabelSet::on_size_request (Gtk::Requisition* requisition)
+RotatedLabelSet::on_size_request (Requisition* requisition)
{
- *requisition = Gtk::Requisition ();
+ *requisition = Requisition ();
if (_pango_layout == 0) {
return;
std::pair<int, int> const d = setup_layout (_port_group_list.get_port_by_index (n - 1, false));
requisition->width += d.first;
}
+
+ cerr << "Labels will be " << requisition->width << " x " << requisition->height << endl;
}
void
-RotatedLabelSet::on_size_allocate (Gtk::Allocation& allocation)
+RotatedLabelSet::on_size_allocate (Allocation& allocation)
{
set_allocation (allocation);
if (_gdk_window) {
- _gdk_window->move_resize (
- allocation.get_x(), allocation.get_y(), allocation.get_width(), allocation.get_height()
- );
+ _gdk_window->move_resize (allocation.get_x(), allocation.get_y(), allocation.get_width(), allocation.get_height());
}
}
void
RotatedLabelSet::on_realize ()
{
- Gtk::Widget::on_realize ();
+ Widget::on_realize ();
- Glib::RefPtr<Gtk::Style> style = get_style ();
+ Glib::RefPtr<Style> style = get_style ();
if (!_gdk_window) {
GdkWindowAttr attributes;
memset (&attributes, 0, sizeof (attributes));
- Gtk::Allocation allocation = get_allocation ();
+ Allocation allocation = get_allocation ();
attributes.x = allocation.get_x ();
attributes.y = allocation.get_y ();
attributes.width = allocation.get_width ();
attributes.wclass = GDK_INPUT_OUTPUT;
_gdk_window = Gdk::Window::create (get_window (), &attributes, GDK_WA_X | GDK_WA_Y);
- unset_flags (Gtk::NO_WINDOW);
+ unset_flags (NO_WINDOW);
set_window (_gdk_window);
- _bg_colour = style->get_bg (Gtk::STATE_NORMAL );
- modify_bg (Gtk::STATE_NORMAL, _bg_colour);
- _fg_colour = style->get_fg (Gtk::STATE_NORMAL);
-;
+ _bg_colour = style->get_bg (STATE_NORMAL );
+ modify_bg (STATE_NORMAL, _bg_colour);
+ _fg_colour = style->get_fg (STATE_NORMAL);
+
_gdk_window->set_user_data (gobj ());
/* Set up Pango stuff */
{
_gdk_window.clear ();
- Gtk::Widget::on_unrealize ();
+ Widget::on_unrealize ();
}
_pango_layout->get_pixel_size (w, h);
/* Rotate the width and height as appropriate. I thought Pango might be able
- to do this for us, but I can't find out how... */
+ to do this for us, but I can't find out how...
+ */
+
std::pair<int, int> d;
- d.first = int (w * cos (_angle_radians) - h * sin (_angle_radians));
- d.second = int (w * sin (_angle_radians) + h * cos (_angle_radians));
+
+ // cerr << "\"" << s << "\" was " << w << " x " << h << endl;
+
+ d.first = int (fabs (w * cos (_angle_radians) + h * sin (_angle_radians)));
+ d.second = int (fabs (w * sin (_angle_radians) + h * cos (_angle_radians)));
+
+ // cerr << "\trotated by " << _angle_degrees << " = " << d.first << " x " << d.second << endl;
return d;
}
if ((*i)->visible) {
for (uint32_t j = 0; j < (*i)->ports.size(); ++j) {
std::pair<int, int> const d = setup_layout ((*i)->ports[j]);
- get_window()->draw_layout (_gc, int ((n + 0.25) * spacing), height - d.second, _pango_layout, _fg_colour, _bg_colour);
+ int x, y;
+ if (getenv ("AD_X_SHIFT") != 0) {
+ x = atoi (getenv ("AD_X_SHIFT"));
+ } else {
+ x = 0;
+ }
+ if (getenv ("AD_Y_SHIFT") != 0) {
+ y = atoi (getenv ("AD_Y_SHIFT"));
+ } else {
+ y = 0;
+ }
+ get_window()->draw_layout (_gc, int ((n + 0.25) * spacing) + x, height - d.second + y, _pango_layout, _fg_colour, _bg_colour);
++n;
}
}
: _offer_inputs (offer_inputs), _port_group_list (session, type, offer_inputs, mask), _type (type),
_column_labels (_port_group_list)
{
- _row_labels_vbox[0] = _row_labels_vbox[1] = 0;
- _side_vbox_pad[0] = _side_vbox_pad[1] = 0;
+ _row_labels_vbox = 0;
+ _side_vbox_pad = 0;
+ _visibility_checkbutton_box.pack_start (*(manage (new Label (_("Connections displayed: ")))), false, false, 10);
pack_start (_visibility_checkbutton_box, false, false);
- _side_vbox[0].pack_start (*Gtk::manage (new Gtk::Label ("")));
- _overall_hbox.pack_start (_side_vbox[0], false, false);
- _scrolled_window.set_policy (Gtk::POLICY_ALWAYS, Gtk::POLICY_NEVER);
- _scrolled_window.set_shadow_type (Gtk::SHADOW_NONE);
- Gtk::VBox* b = new Gtk::VBox;
- b->pack_start (_column_labels, false, false);
- b->pack_start (_port_group_hbox, false, false);
- Gtk::Alignment* a = new Gtk::Alignment (0, 1, 0, 0);
- a->add (*Gtk::manage (b));
- _scrolled_window.add (*Gtk::manage (a));
- _overall_hbox.pack_start (_scrolled_window);
- _side_vbox[1].pack_start (*Gtk::manage (new Gtk::Label ("")));
- _overall_hbox.pack_start (_side_vbox[1]);
+ _scrolled_window.set_policy (POLICY_ALWAYS, POLICY_AUTOMATIC);
+ _scrolled_window.set_shadow_type (SHADOW_NONE);
+
+ VBox* b = manage (new VBox);
+
+ if (offer_inputs) {
+ b->pack_start (_port_group_hbox, false, false);
+ b->pack_start (_column_labels, false, false);
+ } else {
+ b->pack_start (_column_labels, false, false);
+ b->pack_start (_port_group_hbox, false, false);
+ }
+
+ Alignment* a;
+
+ if (offer_inputs) {
+ a = manage (new Alignment (1, 0, 0, 0));
+ } else {
+ a = manage (new Alignment (0, 1, 0, 0));
+ }
+
+ a->add (*b);
+
+ _scrolled_window.add (*a);
+
+ 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);
_port_group_hbox.signal_size_allocate().connect (sigc::hide (sigc::mem_fun (*this, &IOSelector::setup_dimensions)));
void
PortMatrix::clear ()
{
- for (int i = 0; i < 2; ++i) {
-
- for (std::vector<Gtk::EventBox*>::iterator j = _row_labels[i].begin(); j != _row_labels[i].end(); ++j) {
- delete *j;
- }
- _row_labels[i].clear ();
-
- if (_row_labels_vbox[i]) {
- _side_vbox[i].remove (*_row_labels_vbox[i]);
- }
- delete _row_labels_vbox[i];
- _row_labels_vbox[i] = 0;
+ for (std::vector<EventBox*>::iterator j = _row_labels.begin(); j != _row_labels.end(); ++j) {
+ delete *j;
+ }
+ _row_labels.clear ();
- if (_side_vbox_pad[i]) {
- _side_vbox[i].remove (*_side_vbox_pad[i]);
- }
- delete _side_vbox_pad[i];
- _side_vbox_pad[i] = 0;
+ if (_row_labels_vbox) {
+ _side_vbox.remove (*_row_labels_vbox);
+ delete _row_labels_vbox;
+ _row_labels_vbox = 0;
+ }
+
+ /* remove lurking, invisible label and padding */
+
+ _side_vbox.children().clear ();
+
+ if (_side_vbox_pad) {
+ delete _side_vbox_pad;
+ _side_vbox_pad = 0;
}
for (std::vector<PortGroupUI*>::iterator i = _port_group_ui.begin(); i != _port_group_ui.end(); ++i) {
/* Scrolled window */
/* XXX: really shouldn't set a minimum horizontal size here, but if we don't
- the window starts up very small */
+ the window starts up very small.
+ The constant value in the set_size_request() computation will control
+ how big the scrolled window will be if we fill the port matrix will a gajillion
+ ports.
+ */
+
_scrolled_window.set_size_request (
- std::min (_column_labels.get_width(), 640),
+ std::min (_column_labels.get_width(), 400),
_column_labels.get_height() + port_group_tables_height + scrollbar_height + 16
);
/* Row labels */
- for (int i = 0; i < 2; ++i) {
- for (std::vector<Gtk::EventBox*>::iterator j = _row_labels[i].begin(); j != _row_labels[i].end(); ++j) {
- (*j)->get_child()->set_size_request (-1, unit_size.second);
- }
-
- if (_side_vbox_pad[i]) {
- _side_vbox_pad[i]->set_size_request (-1, scrollbar_height + unit_size.second / 4);
- }
+ for (std::vector<EventBox*>::iterator j = _row_labels.begin(); j != _row_labels.end(); ++j) {
+ (*j)->get_child()->set_size_request (-1, unit_size.second);
}
+
+ if (_side_vbox_pad) {
+ if (_offer_inputs) {
+ _side_vbox_pad->set_size_request (-1, unit_size.second / 4);
+ } else {
+ _side_vbox_pad->set_size_request (-1, scrollbar_height + unit_size.second / 4);
+ }
+ }
}
int const rows = n_rows ();
/* Row labels */
- for (int i = 0; i < 2; ++i) {
- _row_labels_vbox[i] = new Gtk::VBox;
- int const run_rows = std::max (1, rows);
- for (int j = 0; j < run_rows; ++j) {
- Gtk::Label* label = new Gtk::Label (rows == 0 ? "Quim" : row_name (j));
- Gtk::EventBox* b = new Gtk::EventBox;
- b->set_events (Gdk::BUTTON_PRESS_MASK);
- b->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &IOSelector::row_label_button_pressed), j));
- b->add (*Gtk::manage (label));
- _row_labels[i].push_back (b);
- _row_labels_vbox[i]->pack_start (*b, false, false);
- }
- _side_vbox[i].pack_start (*_row_labels_vbox[i], false, false);
- _side_vbox_pad[i] = new Gtk::Label ("");
- _side_vbox[i].pack_start (*_side_vbox_pad[i], false, false);
+ _row_labels_vbox = new VBox;
+ int const run_rows = std::max (1, rows);
+
+ for (int j = 0; j < run_rows; ++j) {
+
+ /* embolden the port/channel name */
+
+ string s = "<b>";
+ s += row_name (j);
+ s += "</b>";
+
+ Label* label = manage (new Label (s));
+ EventBox* b = manage (new EventBox);
+
+ label->set_use_markup (true);
+
+ b->set_events (Gdk::BUTTON_PRESS_MASK);
+ b->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &IOSelector::row_label_button_pressed), j));
+ b->add (*label);
+
+ _row_labels.push_back (b);
+ _row_labels_vbox->pack_start (*b, false, false);
+ }
+
+ _side_vbox_pad = new Label (""); /* unmanaged, explicitly deleted */
+
+ if (_offer_inputs) {
+ _side_vbox.pack_start (*_side_vbox_pad, false, false);
+ _side_vbox.pack_start (*_row_labels_vbox, false, false);
+ _side_vbox.pack_start (*manage (new Label ("")));
+ } else {
+ _side_vbox.pack_start (*manage (new Label ("")));
+ _side_vbox.pack_start (*_row_labels_vbox, false, false);
+ _side_vbox.pack_start (*_side_vbox_pad, false, false);
}
/* Checkbutton tables and visibility checkbuttons */
- int n = 0;
for (PortGroupList::iterator i = _port_group_list.begin(); i != _port_group_list.end(); ++i) {
- if ((*i)->visible) {
- PortGroupUI* t = new PortGroupUI (*this, **i);
- /* XXX: this is a bit of a hack; should probably use a configurable colour here */
- Gdk::Color alt_bg = get_style()->get_bg (Gtk::STATE_NORMAL);
- alt_bg.set_rgb (alt_bg.get_red() + 4096, alt_bg.get_green() + 4096, alt_bg.get_blue () + 4096);
- if ((n % 2) == 0) {
- t->get_table().modify_bg (Gtk::STATE_NORMAL, alt_bg);
- }
+ PortGroupUI* t = new PortGroupUI (*this, **i);
+
+ _port_group_ui.push_back (t);
+ _port_group_hbox.pack_start (t->get_table(), false, false);
+
+ _visibility_checkbutton_box.pack_start (t->get_visibility_checkbutton(), false, false);
- _port_group_ui.push_back (t);
- _port_group_hbox.pack_start (t->get_table(), false, false);
+ CheckButton* chk = dynamic_cast<CheckButton*>(&t->get_visibility_checkbutton());
- _visibility_checkbutton_box.pack_start (t->get_visibility_checkbutton(), false, false);
- ++n;
- }
- }
+ if (chk) {
+ chk->signal_toggled().connect (sigc::mem_fun (*this, &PortMatrix::reset_visibility));
+ }
+ }
show_all ();
+ reset_visibility ();
+}
+
+void
+PortMatrix::reset_visibility ()
+{
+ /* now adjust visibility and coloring */
+
+ bool even = true;
+ Gdk::Color odd_bg (color_from_style ("OddPortGroups", STATE_NORMAL, "fg"));
+ Gdk::Color even_bg (color_from_style ("EvenPortGroups", STATE_NORMAL, "fg"));
+
for (std::vector<PortGroupUI*>::iterator i = _port_group_ui.begin(); i != _port_group_ui.end(); ++i) {
+
(*i)->setup_visibility ();
+
+ if ((*i)->port_group().visible) {
+ if (even) {
+ (*i)->get_table().modify_bg (STATE_NORMAL, even_bg);
+ } else {
+ (*i)->get_table().modify_bg (STATE_NORMAL, odd_bg);
+ }
+ even = !even;
+ }
}
-
}
void
return false;
}
- Gtk::Menu* menu = Gtk::manage (new Gtk::Menu);
- Gtk::Menu_Helpers::MenuList& items = menu->items ();
+ Menu* menu = manage (new Menu);
+ Menu_Helpers::MenuList& items = menu->items ();
menu->set_name ("ArdourContextMenu");
bool const can_add = maximum_rows () > n_rows ();
std::string const name = row_name (r);
items.push_back (
- Gtk::Menu_Helpers::MenuElem (string_compose(_("Add %1"), row_descriptor()), sigc::mem_fun (*this, &PortMatrix::add_row))
+ 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 (
- Gtk::Menu_Helpers::MenuElem (string_compose(_("Remove %1 \"%2\""), row_descriptor(), name), sigc::bind (sigc::mem_fun (*this, &PortMatrix::remove_row), r))
+ 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);
PortGroupList::PortGroupList (ARDOUR::Session & session, ARDOUR::DataType type, bool offer_inputs, Mask mask)
: _session (session), _type (type), _offer_inputs (offer_inputs),
- buss (_("Buss"), "ardour:", mask & BUSS),
+ buss (_("Bus"), "ardour:", mask & BUSS),
track (_("Track"), "ardour:", mask & TRACK),
system (_("System"), "system:", mask & SYSTEM),
other (_("Other"), "", mask & OTHER)
other.ports.clear ();
/* Find the ports provided by ardour; we can't derive their type just from their
- names, so we'll have to be more devious. */
+ names, so we'll have to be more devious.
+ */
boost::shared_ptr<ARDOUR::Session::RouteList> routes = _session.get_routes ();
for (ARDOUR::Session::RouteList::const_iterator i = routes->begin(); i != routes->end(); ++i) {
PortGroup* g = 0;
- if (_type == ARDOUR::DataType::AUDIO && dynamic_cast<ARDOUR::AudioTrack*> ((*i).get())) {
- /* Audio track for an audio IO */
- g = &track;
- } else if (_type == ARDOUR::DataType::MIDI && dynamic_cast<ARDOUR::MidiTrack*> ((*i).get())) {
- /* Midi track for a MIDI IO */
- g = &track;
- } else if (_type == ARDOUR::DataType::AUDIO && dynamic_cast<ARDOUR::MidiTrack*> ((*i).get()) == 0) {
- /* Non-MIDI track for an Audio IO; must be an audio buss */
- g = &buss;
- }
+ if (_type == ARDOUR::DataType::AUDIO) {
+
+ if (boost::dynamic_pointer_cast<ARDOUR::AudioTrack> (*i)) {
+ g = &track;
+ } else if (!boost::dynamic_pointer_cast<ARDOUR::MidiTrack>(*i)) {
+ g = &buss;
+ }
+
+
+ } else if (_type == ARDOUR::DataType::MIDI) {
+
+ if (boost::dynamic_pointer_cast<ARDOUR::MidiTrack> (*i)) {
+ g = &track;
+ }
+
+ /* No MIDI busses yet */
+ }
+
if (g) {
ARDOUR::PortSet const & p = _offer_inputs ? ((*i)->inputs()) : ((*i)->outputs());
for (uint32_t j = 0; j < p.num_ports(); ++j) {
}
}
-
/* XXX: inserts, sends, plugin inserts? */
/* Now we need to find the non-ardour ports; we do this by first
- finding all the ports that we can connect to. */
- const char **ports = _session.engine().get_ports (
- "", _type.to_jack_type(), _offer_inputs ? JackPortIsInput : JackPortIsOutput
- );
+ finding all the ports that we can connect to.
+ */
+ const char **ports = _session.engine().get_ports ("", _type.to_jack_type(), _offer_inputs ?
+ JackPortIsInput : JackPortIsOutput);
if (ports) {
int n = 0;
+ string client_matching_string;
+
+ client_matching_string = _session.engine().client_name();
+ client_matching_string += ':';
+
while (ports[n]) {
std::string const p = ports[n];
/* system: prefix */
system.add (p);
} else {
- if (p.substr(0, strlen("ardour:")) != "ardour:") {
+ if (p.substr(0, client_matching_string.length()) != client_matching_string) {
/* other (non-ardour) prefix */
other.add (p);
}
++n;
}
+
+ free (ports);
}
+ push_back (&system);
push_back (&buss);
push_back (&track);
- push_back (&system);
push_back (&other);
}