diff options
author | Carl Hetherington <carl@carlh.net> | 2009-01-20 14:46:00 +0000 |
---|---|---|
committer | Carl Hetherington <carl@carlh.net> | 2009-01-20 14:46:00 +0000 |
commit | 61db2175eb8b8fffd0c1796ace78ac33c9e1adf0 (patch) | |
tree | 6fdc23e7e0161ce54642b0bd44dc8e0a33f05efe | |
parent | ef038c1a84ecd541a540d5a5baa677d7663e535c (diff) |
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
32 files changed, 2160 insertions, 1196 deletions
diff --git a/gtk2_ardour/SConscript b/gtk2_ardour/SConscript index c6fb356cd9..d42edb1919 100644 --- a/gtk2_ardour/SConscript +++ b/gtk2_ardour/SConscript @@ -196,7 +196,6 @@ lineset.cc location_ui.cc main.cc marker.cc -matrix.cc midi_channel_selector.cc midi_port_dialog.cc midi_region_view.cc @@ -216,8 +215,13 @@ piano_roll_header.cc playlist_selector.cc plugin_selector.cc plugin_ui.cc -port_matrix.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 prompter.cc public_editor.cc diff --git a/gtk2_ardour/ardour.menus.in b/gtk2_ardour/ardour.menus.in index c6688e92a2..dbddc2d93c 100644 --- a/gtk2_ardour/ardour.menus.in +++ b/gtk2_ardour/ardour.menus.in @@ -423,6 +423,7 @@ <menuitem action='ToggleThemeManager'/> <menuitem action='ToggleBigClock'/> <menuitem action='toggle-rhythm-ferret'/> + <menuitem action='toggle-bundle-manager'/> <separator/> </menu> <menu name='Options' action='Options'> diff --git a/gtk2_ardour/bundle_manager.cc b/gtk2_ardour/bundle_manager.cc index e4e848557e..1b493348ae 100644 --- a/gtk2_ardour/bundle_manager.cc +++ b/gtk2_ardour/bundle_manager.cc @@ -38,71 +38,79 @@ BundleEditorMatrix::BundleEditorMatrix ( PortGroupList::Mask (PortGroupList::SYSTEM | PortGroupList::OTHER) ) { - _bundle = boost::dynamic_pointer_cast<ARDOUR::UserBundle> (bundle); - assert (_bundle != 0); + _our_bundle = bundle; } 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) { - _bundle->add_port_to_channel (r, p); - } else { - _bundle->remove_port_from_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 (s) { + ab->add_port_to_channel (ac, *i); + } else { + ab->remove_port_from_channel (ac, *i); + } } } 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 -BundleEditorMatrix::n_rows () const -{ - return _bundle->nchannels (); + return true; } -uint32_t -BundleEditorMatrix::maximum_rows () const +void +BundleEditorMatrix::add_channel (boost::shared_ptr<ARDOUR::Bundle> b) { - /* 65536 channels in a bundle ought to be enough for anyone (TM) */ - return 65536; -} + NameChannelDialog d; + d.set_position (Gtk::WIN_POS_MOUSE); -uint32_t -BundleEditorMatrix::minimum_rows () const -{ - return 0; -} + if (d.run () != Gtk::RESPONSE_ACCEPT) { + return; + } -std::string -BundleEditorMatrix::row_name (int r) const -{ - std::stringstream s; - s << r + 1; // 1-based counting - return s.str(); + _our_bundle->add_channel (d.get_name()); + setup (); } void -BundleEditorMatrix::add_row () +BundleEditorMatrix::remove_channel (boost::shared_ptr<ARDOUR::Bundle> b, uint32_t c) { - _bundle->add_channel (); + _our_bundle->remove_channel (c); setup (); } void -BundleEditorMatrix::remove_row (int r) +BundleEditorMatrix::rename_channel (boost::shared_ptr<ARDOUR::Bundle> b, uint32_t c) { - _bundle->remove_channel (r); - setup (); -} + NameChannelDialog d (b, c); + d.set_position (Gtk::WIN_POS_MOUSE); -std::string -BundleEditorMatrix::row_descriptor () const -{ - return _("channel"); + 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) @@ -111,21 +119,21 @@ BundleEditor::BundleEditor (ARDOUR::Session& session, boost::shared_ptr<ARDOUR:: Gtk::Table* t = new Gtk::Table (3, 2); t->set_spacings (4); + /* Bundle name */ Gtk::Alignment* a = new Gtk::Alignment (1, 0.5, 0, 1); a->add (*Gtk::manage (new Gtk::Label (_("Name:")))); t->attach (*Gtk::manage (a), 0, 1, 0, 1, Gtk::FILL, Gtk::FILL); t->attach (_name, 1, 2, 0, 1); - _name.set_text (_bundle->name ()); _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->add (*Gtk::manage (new Gtk::Label (_("Direction:")))); t->attach (*Gtk::manage (a), 0, 1, 1, 2, Gtk::FILL, Gtk::FILL); a = new Gtk::Alignment (0, 0.5, 0, 1); a->add (_input_or_output); t->attach (*Gtk::manage (a), 1, 2, 1, 2); - _input_or_output.append_text (_("Input")); _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)); + /* Type (audio or MIDI) */ a = new Gtk::Alignment (1, 0.5, 0, 1); a->add (*Gtk::manage (new Gtk::Label (_("Type:")))); 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)); get_vbox()->pack_start (*Gtk::manage (t), false, false); - get_vbox()->pack_start (_matrix); - 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) { add_button (Gtk::Stock::CANCEL, 1); add_button (Gtk::Stock::ADD, 0); @@ -269,7 +283,7 @@ BundleManager::new_clicked () boost::shared_ptr<ARDOUR::UserBundle> b (new ARDOUR::UserBundle ("")); /* Start off with a single channel */ - b->add_channel (); + b->add_channel (""); BundleEditor e (_session, b, true); 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 (); +} diff --git a/gtk2_ardour/bundle_manager.h b/gtk2_ardour/bundle_manager.h index 097328b602..18b0b29e92 100644 --- a/gtk2_ardour/bundle_manager.h +++ b/gtk2_ardour/bundle_manager.h @@ -22,6 +22,7 @@ #include <gtkmm/treeview.h> #include <gtkmm/liststore.h> +#include <gtkmm/entry.h> #include "ardour_dialog.h" #include "port_matrix.h" @@ -35,19 +36,28 @@ class BundleEditorMatrix : public PortMatrix public: BundleEditorMatrix (ARDOUR::Session &, boost::shared_ptr<ARDOUR::Bundle>); - void set_state (int, std::string const &, bool, uint32_t); - bool get_state (int, std::string const &) const; - uint32_t n_rows () const; - uint32_t maximum_rows () const; - uint32_t minimum_rows () const; - std::string row_name (int) const; - void add_row (); - void remove_row (int); - std::string row_descriptor () const; - - private: - - boost::shared_ptr<ARDOUR::UserBundle> _bundle; + 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 + ); + + bool get_state ( + boost::shared_ptr<ARDOUR::Bundle> ab, + uint32_t ac, + boost::shared_ptr<ARDOUR::Bundle> bb, + uint32_t bc + ) 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 true; + } + void rename_channel (boost::shared_ptr<ARDOUR::Bundle>, uint32_t); }; class BundleEditor : public ArdourDialog @@ -104,4 +114,22 @@ class BundleManager : public ArdourDialog 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 diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index 3eba17a06e..08be372b14 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -87,6 +87,7 @@ #include "actions.h" #include "tempo_lines.h" #include "analysis_window.h" +#include "bundle_manager.h" #include "i18n.h" @@ -347,6 +348,7 @@ Editor::Editor () select_new_marker = false; zoomed_to_region = false; rhythm_ferret = 0; + _bundle_manager = 0; allow_vertical_scroll = false; no_save_visual = false; need_resize_line = false; @@ -5128,6 +5130,16 @@ Editor::show_rhythm_ferret () } void +Editor::show_bundle_manager () +{ + if (_bundle_manager == 0) { + _bundle_manager = new BundleManager (*session); + } + + _bundle_manager->show (); +} + +void Editor::first_idle () { MessageDialog* dialog = 0; diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index 622d288213..87c2a1deb9 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -110,6 +110,7 @@ class ControlPoint; class SoundFileOmega; class RhythmFerret; class AnalysisWindow; +class BundleManager; /* <CMT Additions> */ class ImageFrameView; @@ -394,6 +395,7 @@ class Editor : public PublicEditor void toggle_meter_updating(); void show_rhythm_ferret(); + void show_bundle_manager (); void goto_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); RhythmFerret* rhythm_ferret; + BundleManager* _bundle_manager; void fit_tracks (); void set_track_height (uint32_t h); diff --git a/gtk2_ardour/editor_actions.cc b/gtk2_ardour/editor_actions.cc index 0e27aabaef..5c973034c4 100644 --- a/gtk2_ardour/editor_actions.cc +++ b/gtk2_ardour/editor_actions.cc @@ -563,6 +563,7 @@ Editor::register_actions () 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)); 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)); ActionManager::session_sensitive_actions.push_back (act); diff --git a/gtk2_ardour/io_selector.cc b/gtk2_ardour/io_selector.cc index 0e40aa6ad3..06274df95e 100644 --- a/gtk2_ardour/io_selector.cc +++ b/gtk2_ardour/io_selector.cc @@ -30,6 +30,7 @@ #include "ardour/audio_track.h" #include "ardour/midi_track.h" #include "ardour/data_type.h" +#include "ardour/bundle.h" #include "io_selector.h" #include "utils.h" @@ -44,80 +45,130 @@ IOSelector::IOSelector (ARDOUR::Session& session, boost::shared_ptr<ARDOUR::IO> PortGroupList::Mask (PortGroupList::BUSS | PortGroupList::SYSTEM | PortGroupList::OTHER)) + , _session (session) , _io (io) { - list<string> our_ports; - /* Listen for ports changing on the IO */ - if (_offer_inputs) { - _io->output_changed.connect (mem_fun(*this, &IOSelector::ports_changed)); + _io->PortCountChanged.connect (sigc::hide (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()); + int j = 0; 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 { - _io->input_changed.connect (mem_fun(*this, &IOSelector::ports_changed)); - + const PortSet& ps (_io->inputs()); + int j = 0; 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); - setup (); + PortMatrix::setup (); } void -IOSelector::ports_changed (ARDOUR::IOChange change, void *src) +IOSelector::ports_changed () { - ENSURE_GUI_THREAD (bind (mem_fun (*this, &IOSelector::ports_changed), change, src)); + ENSURE_GUI_THREAD (mem_fun (*this, &IOSelector::ports_changed)); setup (); } void -IOSelector::set_state (int r, std::string const & p, bool s, uint32_t keymod) +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 + ) { - if (s) { - if (!_offer_inputs) { - _io->connect_input (_io->input(r), p, 0); - } else { - _io->connect_output (_io->output(r), p, 0); - } - } else { - if (!_offer_inputs) { - _io->disconnect_input (_io->input(r), p, 0); - } else { - _io->disconnect_output (_io->output(r), p, 0); + ARDOUR::Bundle::PortList const& our_ports = ab->channel_ports (ac); + ARDOUR::Bundle::PortList const& other_ports = bb->channel_ports (bc); + + 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) { + + Port* f = _session.engine().get_port_by_name (*i); + if (!f) { + return; + } + + if (s) { + if (!offering_input()) { + _io->connect_input (f, *j, 0); + } else { + _io->connect_output (f, *j, 0); + } + } else { + if (!offering_input()) { + _io->disconnect_input (f, *j, 0); + } else { + _io->disconnect_output (f, *j, 0); + } + } } } } 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) { - _io->output(r)->get_connections (connections); - } else { - _io->input(r)->get_connections (connections); + 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) { + + 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 IOSelector::n_rows () const { - if (!_offer_inputs) { + if (!offering_input()) { return _io->inputs().num_ports (_io->default_type()); } else { return _io->outputs().num_ports (_io->default_type()); @@ -127,7 +178,7 @@ IOSelector::n_rows () const uint32_t IOSelector::maximum_rows () const { - if (!_offer_inputs) { + if (!offering_input()) { return _io->input_maximum ().get (_io->default_type()); } else { return _io->output_maximum ().get (_io->default_type()); @@ -138,39 +189,22 @@ IOSelector::maximum_rows () const uint32_t IOSelector::minimum_rows () const { - if (!_offer_inputs) { + if (!offering_input()) { return _io->input_minimum ().get (_io->default_type()); } else { 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 -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 const ARDOUR::DataType t = _io->default_type (); - if (!_offer_inputs) { + if (!offering_input()) { try { _io->add_input_port ("", this); @@ -195,24 +229,20 @@ IOSelector::add_row () } 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 - const ARDOUR::DataType t = _io->default_type (); + Port* f = _session.engine().get_port_by_name (b->channel_ports(c)[0]); + if (!f) { + return; + } - if (!_offer_inputs) { - _io->remove_input_port (_io->input (r), this); + if (offering_input()) { + _io->remove_output_port (f, this); } else { - _io->remove_output_port (_io->output (r), this); + _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) : ArdourDialog ("I/O selector") , _selector (session, io, !for_input) @@ -223,30 +253,34 @@ IOSelectorWindow::IOSelectorWindow (ARDOUR::Session& session, boost::shared_ptr< , rescan_button (_("Rescan")) { + /* XXX: what's this for? */ add_events (Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK); + set_name ("IOSelectorWindow2"); + /* Disconnect All button */ disconnect_button.set_name ("IOSelectorButton"); 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); + /* Add Port button */ if (_selector.maximum_rows() > _selector.n_rows()) { add_button.set_name ("IOSelectorButton"); 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); - 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) { - io->output_changed.connect (mem_fun(*this, &IOSelectorWindow::ports_changed)); - } else { - io->input_changed.connect (mem_fun(*this, &IOSelectorWindow::ports_changed)); - } - - rescan_button.set_name ("IOSelectorButton"); + /* Rescan button */ + rescan_button.set_name ("IOSelectorButton"); 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); + io->PortCountChanged.connect (sigc::hide (mem_fun (*this, &IOSelectorWindow::ports_changed))); + + /* Cancel button */ if (can_cancel) { cancel_button.set_name ("IOSelectorButton"); 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 { cancel_button.hide(); } - + cancel_button.signal_clicked().connect (mem_fun(*this, &IOSelectorWindow::cancel)); + + /* OK button */ ok_button.set_name ("IOSelectorButton"); if (!can_cancel) { 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_vbox()->set_spacing (8); - get_vbox()->pack_start (_selector, true, true); - suggestion.set_alignment (0.5, 0.5); - suggestion_box.pack_start (suggestion, true, true); - get_vbox()->pack_start (suggestion_box, false, false); - - ok_button.signal_clicked().connect (mem_fun(*this, &IOSelectorWindow::accept)); - cancel_button.signal_clicked().connect (mem_fun(*this, &IOSelectorWindow::cancel)); - rescan_button.signal_clicked().connect (mem_fun(*this, &IOSelectorWindow::rescan)); + /* XXX: do we still need the ScrolledWindow? */ + Gtk::ScrolledWindow* sel_scroll = Gtk::manage (new Gtk::ScrolledWindow); + sel_scroll->set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_NEVER); + sel_scroll->add (_selector); + get_vbox()->pack_start (*sel_scroll, true, true); set_position (Gtk::WIN_POS_MOUSE); io_name_changed (this); - ports_changed (IOChange (0), this); - leave_scroller ((GdkEventCrossing*) 0); + ports_changed (); show_all (); 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() @@ -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 -IOSelectorWindow::ports_changed (ARDOUR::IOChange change, void *src) +IOSelectorWindow::ports_changed () { if (_selector.maximum_rows() > _selector.n_rows()) { add_button.set_sensitive (true); @@ -319,12 +332,6 @@ IOSelectorWindow::ports_changed (ARDOUR::IOChange change, void *src) } void -IOSelectorWindow::rescan () -{ - _selector.setup (); -} - -void IOSelectorWindow::cancel () { _selector.Finished (IOSelector::Cancelled); @@ -365,10 +372,8 @@ PortInsertUI::PortInsertUI (ARDOUR::Session& sess, boost::shared_ptr<ARDOUR::Por : input_selector (sess, pi->io(), true), output_selector (sess, pi->io(), false) { - hbox.pack_start (output_selector, true, true); - hbox.pack_start (input_selector, true, true); - - pack_start (hbox); + pack_start (output_selector, true, true); + pack_start (input_selector, true, true); } void @@ -420,7 +425,6 @@ PortInsertWindow::PortInsertWindow (ARDOUR::Session& sess, boost::shared_ptr<ARD ok_button.signal_clicked().connect (mem_fun (*this, &PortInsertWindow::accept)); 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))); @@ -445,12 +449,6 @@ PortInsertWindow::on_map () void -PortInsertWindow::rescan () -{ - _portinsertui.redisplay (); -} - -void PortInsertWindow::cancel () { _portinsertui.finished (IOSelector::Cancelled); diff --git a/gtk2_ardour/io_selector.h b/gtk2_ardour/io_selector.h index 57c6e6db86..fb12de36ba 100644 --- a/gtk2_ardour/io_selector.h +++ b/gtk2_ardour/io_selector.h @@ -23,25 +23,47 @@ #include "ardour_dialog.h" #include "port_matrix.h" +namespace ARDOUR { + class PortInsert; +} + class IOSelector : public PortMatrix { public: IOSelector (ARDOUR::Session&, boost::shared_ptr<ARDOUR::IO>, bool); - void set_state (int, std::string const &, bool, uint32_t); - bool get_state (int, std::string const &) const; + void set_state ( + 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 maximum_rows () const; uint32_t minimum_rows () const; - std::string row_name (int) const; - void add_row (); - void remove_row (int); - std::string row_descriptor () const; - boost::shared_ptr<ARDOUR::IO> const io() { return _io; } + boost::shared_ptr<ARDOUR::IO> const io () { return _io; } + void setup (); private: + ARDOUR::Session& _session; boost::shared_ptr<ARDOUR::IO> _io; - void ports_changed (ARDOUR::IOChange, void*); + void ports_changed (); }; class IOSelectorWindow : public ArdourDialog @@ -66,17 +88,11 @@ class IOSelectorWindow : public ArdourDialog Gtk::Button cancel_button; Gtk::Button rescan_button; - Gtk::HBox suggestion_box; - Gtk::Label suggestion; - - void rescan (); void cancel (); void accept (); - void ports_changed (ARDOUR::IOChange change, void *src); + void ports_changed (); 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); private: - Gtk::HBox hbox; IOSelector input_selector; IOSelector output_selector; }; @@ -111,7 +126,6 @@ class PortInsertWindow : public ArdourDialog Gtk::Button rescan_button; Gtk::Frame button_frame; - void rescan (); void cancel (); void accept (); diff --git a/gtk2_ardour/matrix.cc b/gtk2_ardour/matrix.cc deleted file mode 100644 index b67081711a..0000000000 --- a/gtk2_ardour/matrix.cc +++ /dev/null @@ -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; -} diff --git a/gtk2_ardour/matrix.h b/gtk2_ardour/matrix.h deleted file mode 100644 index a02f2fb397..0000000000 --- a/gtk2_ardour/matrix.h +++ /dev/null @@ -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__ */ diff --git a/gtk2_ardour/option_editor.cc b/gtk2_ardour/option_editor.cc index 12f82ccbfd..935197a0ca 100644 --- a/gtk2_ardour/option_editor.cc +++ b/gtk2_ardour/option_editor.cc @@ -1120,11 +1120,11 @@ OptionEditor::setup_click_editor () click_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->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_packer.show_all (); @@ -1163,11 +1163,11 @@ OptionEditor::setup_auditioner_editor () void 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->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); auditioner_io_selector->show_all (); diff --git a/gtk2_ardour/port_group.cc b/gtk2_ardour/port_group.cc index 3de45eee73..4157348d8e 100644 --- a/gtk2_ardour/port_group.cc +++ b/gtk2_ardour/port_group.cc @@ -18,28 +18,42 @@ */ #include "port_group.h" +#include "port_matrix.h" #include "i18n.h" #include "ardour/session.h" #include "ardour/audio_track.h" #include "ardour/midi_track.h" #include "ardour/audioengine.h" +#include "ardour/bundle.h" #include <boost/shared_ptr.hpp> #include <cstring> using namespace std; using namespace Gtk; +/** Add a bundle to a group. + * @param b Bundle. + */ +void +PortGroup::add_bundle (boost::shared_ptr<ARDOUR::Bundle> b) +{ + bundles.push_back (b); +} + /** Add a port to a group. - * @param p Port name, with or without prefix. + * @param p Port. */ void -PortGroup::add (std::string const & p) +PortGroup::add_port (std::string const &p) { - if (prefix.empty() == false && p.substr (0, prefix.length()) == prefix) { - ports.push_back (p.substr (prefix.length())); - } else { - ports.push_back (p); - } + ports.push_back (p); +} + +void +PortGroup::clear () +{ + bundles.clear (); + ports.clear (); } /** PortGroupUI constructor. @@ -47,14 +61,14 @@ PortGroup::add (std::string const & p) * @Param g PortGroup to represent. */ -PortGroupUI::PortGroupUI (PortMatrix& m, PortGroup& g) +PortGroupUI::PortGroupUI (PortMatrix* m, PortGroup* g) : _port_matrix (m) , _port_group (g) - , _ignore_check_button_toggle (false) - , _visibility_checkbutton (g.name) + , _visibility_checkbutton (g->name) { - _port_group.visible = true; - _ignore_check_button_toggle = false; + _port_group->visible = true; + setup_visibility_checkbutton (); + _visibility_checkbutton.signal_toggled().connect (sigc::mem_fun (*this, &PortGroupUI::visibility_checkbutton_toggled)); } @@ -62,66 +76,49 @@ PortGroupUI::PortGroupUI (PortMatrix& m, PortGroup& g) void 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 */ -Widget& -PortGroupUI::get_visibility_checkbutton () -{ - return _visibility_checkbutton; -} - - -/** Handle a toggle of a port check button */ +/** Set up the visibility checkbutton according to PortGroup::visible */ void -PortGroupUI::port_checkbutton_toggled (CheckButton* b, int r, int c) +PortGroupUI::setup_visibility_checkbutton () { - if (_ignore_check_button_toggle == false) { - // _port_matrix.hide_group (_port_group); - } -} - -/** 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); + if (_visibility_checkbutton.get_active () != _port_group->visible) { + _visibility_checkbutton.set_active (_port_group->visible); } } /** PortGroupList constructor. - * @param session Session to get ports from. - * @param type Type of ports to offer (audio or MIDI) - * @param offer_inputs true to offer output ports, otherwise false. + * @param session Session to get bundles from. + * @param type Type of bundles to offer (audio or MIDI) + * @param offer_inputs true to offer output bundles, otherwise false. * @param mask Mask of groups to make visible by default. */ PortGroupList::PortGroupList (ARDOUR::Session & session, ARDOUR::DataType type, bool offer_inputs, Mask mask) : _session (session), _type (type), _offer_inputs (offer_inputs), - _buss (_("Bus"), "ardour:", mask & BUSS), - _track (_("Track"), "ardour:", mask & TRACK), - _system (_("System"), "system:", mask & SYSTEM), - _other (_("Other"), "", mask & OTHER) + _buss (_("Bus"), mask & BUSS), + _track (_("Track"), mask & TRACK), + _system (_("System"), mask & SYSTEM), + _other (_("Other"), mask & OTHER) { 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 PortGroupList::refresh () { 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 - names, so we'll have to be more devious. - */ + _buss.clear (); + _track.clear (); + _system.clear (); + _other.clear (); + + /* Find the bundles for routes */ boost::shared_ptr<ARDOUR::Session::RouteList> routes = _session.get_routes (); @@ -148,14 +145,12 @@ PortGroupList::refresh () } if (g) { - ARDOUR::PortSet const & p = _offer_inputs ? ((*i)->inputs()) : ((*i)->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()); + g->add_bundle (_offer_inputs ? (*i)->bundle_for_inputs() : (*i)->bundle_for_outputs ()); } } + + /* Bundles created by the session */ + _session.foreach_bundle (sigc::mem_fun (*this, &PortGroupList::maybe_add_session_bundle)); /* XXX: inserts, sends, plugin inserts? */ @@ -163,9 +158,9 @@ PortGroupList::refresh () 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) { + const char **ports = _session.engine().get_ports ("", _type.to_jack_type(), _offer_inputs ? + JackPortIsInput : JackPortIsOutput); + if (ports) { int n = 0; string client_matching_string; @@ -178,11 +173,11 @@ PortGroupList::refresh () if (p.substr(0, strlen ("system:")) == "system:") { /* system: prefix */ - _system.add (p); + _system.add_port (p); } else { if (p.substr(0, client_matching_string.length()) != client_matching_string) { /* other (non-ardour) prefix */ - _other.add (p); + _other.add_port (p); } } @@ -198,41 +193,6 @@ PortGroupList::refresh () 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 PortGroupList::set_type (ARDOUR::DataType t) { @@ -245,3 +205,10 @@ PortGroupList::set_offer_inputs (bool 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); + } +} diff --git a/gtk2_ardour/port_group.h b/gtk2_ardour/port_group.h index 9a38bfc39a..0641d231ef 100644 --- a/gtk2_ardour/port_group.h +++ b/gtk2_ardour/port_group.h @@ -22,37 +22,39 @@ #include <vector> #include <string> - #include <gtkmm/widget.h> #include <gtkmm/checkbutton.h> - +#include <boost/shared_ptr.hpp> #include <ardour/data_type.h> namespace ARDOUR { class Session; - class IO; - class PortInsert; + class Bundle; } 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 { public: /** PortGroup constructor. * @param n Name. - * @param p Port name prefix (including trailing :) * @param v true if group should be visible in the UI, otherwise false. */ - PortGroup (std::string const & n, std::string const & p, bool v) - : name (n), prefix (p), visible (v) {} + PortGroup (std::string const & n, bool 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 prefix; ///< prefix e.g. "ardour:" - std::vector<std::string> ports; ///< port names + std::vector<boost::shared_ptr<ARDOUR::Bundle> > bundles; + std::vector<std::string> ports; bool visible; ///< true if the group is visible in the UI }; @@ -60,20 +62,18 @@ class PortGroup class PortGroupUI { public: - PortGroupUI (PortMatrix&, PortGroup&); + PortGroupUI (PortMatrix*, PortGroup*); - Gtk::Widget& get_visibility_checkbutton (); - PortGroup& port_group () { return _port_group; } - void setup_visibility (); + Gtk::Widget& visibility_checkbutton () { + return _visibility_checkbutton; + } 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 setup_visibility_checkbutton (); - PortMatrix& _port_matrix; ///< the PortMatrix that we are working for - PortGroup& _port_group; ///< the PortGroup that we are representing - bool _ignore_check_button_toggle; + PortMatrix* _port_matrix; ///< the PortMatrix that we are working for + PortGroup* _port_group; ///< the PortGroup that we are representing Gtk::CheckButton _visibility_checkbutton; }; @@ -91,12 +91,12 @@ class PortGroupList : public std::list<PortGroup*> PortGroupList (ARDOUR::Session &, ARDOUR::DataType, bool, Mask); 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_offer_inputs (bool); private: + void maybe_add_session_bundle (boost::shared_ptr<ARDOUR::Bundle>); + ARDOUR::Session& _session; ARDOUR::DataType _type; bool _offer_inputs; diff --git a/gtk2_ardour/port_matrix.cc b/gtk2_ardour/port_matrix.cc index e04dd3b252..1e9227a525 100644 --- a/gtk2_ardour/port_matrix.cc +++ b/gtk2_ardour/port_matrix.cc @@ -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 it under the terms of the GNU General Public License as published by @@ -17,175 +17,205 @@ */ +#include <iostream> +#include <gtkmm/scrolledwindow.h> +#include <gtkmm/adjustment.h> #include <gtkmm/label.h> -#include <gtkmm/enums.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 "ardour/bundle.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) - : _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; - - _visibility_checkbutton_box.pack_start (*(manage (new Label (_("Connections displayed: ")))), false, false, 10); - pack_start (_visibility_checkbutton_box, false, false); - - _scrolled_window.set_policy (POLICY_ALWAYS, POLICY_AUTOMATIC); - _scrolled_window.set_shadow_type (SHADOW_NONE); + /* checkbuttons for visibility of groups */ + Gtk::HBox* visibility_buttons = Gtk::manage (new Gtk::HBox); - _scrolled_window.add (matrix); + visibility_buttons->pack_start (*Gtk::manage (new Gtk::Label (_("Show:"))), Gtk::PACK_SHRINK); + + for (std::list<PortGroup*>::iterator i = _port_group_list.begin(); i != _port_group_list.end(); ++i) { + _port_group_uis.push_back (new PortGroupUI (this, *i)); + } - 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); + 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 (_overall_hbox); -} + 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); -PortMatrix::~PortMatrix () -{ - clear (); -} + _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 (); -void -PortMatrix::set_ports (const std::list<std::string>& ports) -{ - matrix.set_ports (ports); + /* XXX hard-coded initial size suggestion */ + set_size_request (400, 200); + show_all (); } -/** Clear out the things that change when the number of source or destination ports changes */ -void -PortMatrix::clear () +PortMatrix::~PortMatrix () { - /* 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()); + for (std::list<PortGroupUI*>::iterator i = _port_group_uis.begin(); i != _port_group_uis.end(); ++i) { delete *i; } - - _port_group_ui.clear (); } -/** Set up the dialogue */ void PortMatrix::setup () { - /* sort out the ports that we'll offer to connect to */ _port_group_list.refresh (); - - clear (); - _side_vbox_pad = new Label (""); /* unmanaged, explicitly deleted */ - - _side_vbox.pack_start (*_side_vbox_pad, false, false); - _side_vbox.pack_start (*manage (new Label (""))); - - matrix.clear (); - - /* Matrix and visibility checkbuttons */ - for (PortGroupList::iterator i = _port_group_list.begin(); i != _port_group_list.end(); ++i) { - - PortGroupUI* t = new PortGroupUI (*this, **i); - - _port_group_ui.push_back (t); - - 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)); + std::vector<boost::shared_ptr<ARDOUR::Bundle> > column; + std::vector<boost::shared_ptr<ARDOUR::Bundle> > row; + + for (PortGroupList::iterator i = _port_group_list.begin (); i != _port_group_list.end (); ++i) { + 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()) { + + 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)); + } + + for (uint32_t j = 0; j < (*i)->ports.size(); ++j) { + std::string const p = (*i)->ports[j]; + b->add_channel (p.substr (pre.length())); + b->set_port (j, p); + } + + column.push_back (b); + } } } - show_all (); + row.push_back (_our_bundle); - reset_visibility (); + _body.setup (row, column); + setup_scrollbars (); + queue_draw (); } 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); + setup (); +} - (*i)->setup_visibility (); - - if ((*i)->port_group().visible) { - matrix.show_group ((*i)->port_group()); - } else { - matrix.hide_group ((*i)->port_group()); - } - } +void +PortMatrix::set_type (ARDOUR::DataType t) +{ + _type = t; + _port_group_list.set_type (t); + setup (); } +void +PortMatrix::hscroll_changed () +{ + _body.set_xoffset (_hscroll.get_adjustment()->get_value()); +} -/** Handle a button press on a row label */ -bool -PortMatrix::row_label_button_pressed (GdkEventButton* e, int r) +void +PortMatrix::vscroll_changed () { - if (e->type != GDK_BUTTON_PRESS || e->button != 3) { - return false; - } + _body.set_yoffset (_vscroll.get_adjustment()->get_value()); +} - Menu* menu = manage (new Menu); - Menu_Helpers::MenuList& items = menu->items (); - menu->set_name ("ArdourContextMenu"); +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); +} - bool const can_add = maximum_rows () > n_rows (); - bool const can_remove = minimum_rows () < n_rows (); - std::string const name = row_name (r); +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; + } + } - 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); + /* 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; + } + } - menu->popup (e->button, e->time); - - return true; + return ""; } void -PortMatrix::set_type (ARDOUR::DataType t) +PortMatrix::disassociate_all () { - _type = t; - _port_group_list.set_type (t); - setup (); -} + 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) { -void -PortMatrix::set_offer_inputs (bool i) -{ - _offer_inputs = i; - _port_group_list.set_offer_inputs (i); - setup (); -} + 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 (); +} diff --git a/gtk2_ardour/port_matrix.h b/gtk2_ardour/port_matrix.h index 27f295b89d..37c05553f1 100644 --- a/gtk2_ardour/port_matrix.h +++ b/gtk2_ardour/port_matrix.h @@ -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 it under the terms of the GNU General Public License as published by @@ -17,32 +17,43 @@ */ -#ifndef __ardour_ui_port_matrix_h__ -#define __ardour_ui_port_matrix_h__ +#ifndef __gtk_ardour_port_matrix_h__ +#define __gtk_ardour_port_matrix_h__ +#include <list> #include <gtkmm/box.h> -#include <gtkmm/checkbutton.h> -#include <gtkmm/table.h> -#include <gtkmm/frame.h> -#include <gtkmm/eventbox.h> -#include <gtkmm/scrolledwindow.h> - -#include "ardour_dialog.h" +#include <gtkmm/scrollbar.h> +#include <boost/shared_ptr.hpp> +#include "port_matrix_body.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 { - class Session; - class IO; - class PortInsert; + class Bundle; } -class PortMatrix : public Gtk::VBox { - public: +class PortMatrix : public Gtk::VBox +{ +public: PortMatrix (ARDOUR::Session&, ARDOUR::DataType, bool, PortGroupList::Mask); ~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 { Cancelled, @@ -51,55 +62,63 @@ class PortMatrix : public Gtk::VBox { sigc::signal<void, Result> Finished; - void set_type (ARDOUR::DataType); - void set_offer_inputs (bool); - bool offering_input() const { return _offer_inputs; } - - /** @param r Our row index. - * @param p Other port. + /** @param ab Our bundle. + * @param ac Channel on our bundle. + * @param bb Other bundle. + * @arapm bc Channel on other bundle. * @param s New state. * @param k XXX */ - virtual void set_state (int r, std::string const & p, bool s, uint32_t k) = 0; - - /** @param r Our row index. - * @param p Other port. + 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 ab Our bundle. + * @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. */ - 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; - 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; } + void setup_scrollbars (); - protected: +protected: + /// our bundle + 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; - void set_ports (const std::list<std::string>&); - - private: + /// list of port groups PortGroupList _port_group_list; + /// port type that we are working with 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 (); - bool row_label_button_pressed (GdkEventButton*, int); - void reset_visibility (); + + PortMatrixBody _body; + Gtk::HScrollbar _hscroll; + Gtk::VScrollbar _vscroll; + std::list<PortGroupUI*> _port_group_uis; }; #endif diff --git a/gtk2_ardour/port_matrix_body.cc b/gtk2_ardour/port_matrix_body.cc new file mode 100644 index 0000000000..d30dca5e85 --- /dev/null +++ b/gtk2_ardour/port_matrix_body.cc @@ -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 (); +} diff --git a/gtk2_ardour/port_matrix_body.h b/gtk2_ardour/port_matrix_body.h new file mode 100644 index 0000000000..503bb3ab62 --- /dev/null +++ b/gtk2_ardour/port_matrix_body.h @@ -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 diff --git a/gtk2_ardour/port_matrix_column_labels.cc b/gtk2_ardour/port_matrix_column_labels.cc new file mode 100644 index 0000000000..343d225b55 --- /dev/null +++ b/gtk2_ardour/port_matrix_column_labels.cc @@ -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(); + } + } +} + diff --git a/gtk2_ardour/port_matrix_column_labels.h b/gtk2_ardour/port_matrix_column_labels.h new file mode 100644 index 0000000000..3c9ff41838 --- /dev/null +++ b/gtk2_ardour/port_matrix_column_labels.h @@ -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 diff --git a/gtk2_ardour/port_matrix_component.cc b/gtk2_ardour/port_matrix_component.cc new file mode 100644 index 0000000000..e2d1e07027 --- /dev/null +++ b/gtk2_ardour/port_matrix_component.cc @@ -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); +} diff --git a/gtk2_ardour/port_matrix_component.h b/gtk2_ardour/port_matrix_component.h new file mode 100644 index 0000000000..a041c807e8 --- /dev/null +++ b/gtk2_ardour/port_matrix_component.h @@ -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 diff --git a/gtk2_ardour/port_matrix_grid.cc b/gtk2_ardour/port_matrix_grid.cc new file mode 100644 index 0000000000..461e9f5f72 --- /dev/null +++ b/gtk2_ardour/port_matrix_grid.cc @@ -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 (); + } +} + + diff --git a/gtk2_ardour/port_matrix_grid.h b/gtk2_ardour/port_matrix_grid.h new file mode 100644 index 0000000000..298d33c0b5 --- /dev/null +++ b/gtk2_ardour/port_matrix_grid.h @@ -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 diff --git a/gtk2_ardour/port_matrix_row_labels.cc b/gtk2_ardour/port_matrix_row_labels.cc new file mode 100644 index 0000000000..18e479942e --- /dev/null +++ b/gtk2_ardour/port_matrix_row_labels.cc @@ -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); +} diff --git a/gtk2_ardour/port_matrix_row_labels.h b/gtk2_ardour/port_matrix_row_labels.h new file mode 100644 index 0000000000..74af1092c2 --- /dev/null +++ b/gtk2_ardour/port_matrix_row_labels.h @@ -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 diff --git a/gtk2_ardour/route_params_ui.cc b/gtk2_ardour/route_params_ui.cc index 670635fb4d..13807a1521 100644 --- a/gtk2_ardour/route_params_ui.cc +++ b/gtk2_ardour/route_params_ui.cc @@ -320,13 +320,13 @@ RouteParams_UI::setup_io_frames() cleanup_io_frames(); // input - _input_iosel = new IOSelector (*session, _route, true); + _input_iosel = new IOSelector (*session, _route, false); _input_iosel->setup (); input_frame.add (*_input_iosel); input_frame.show_all(); // output - _output_iosel = new IOSelector (*session, _route, false); + _output_iosel = new IOSelector (*session, _route, true); _output_iosel->setup (); output_frame.add (*_output_iosel); output_frame.show_all(); diff --git a/libs/ardour/ardour/bundle.h b/libs/ardour/ardour/bundle.h index 1b029dc2b4..8b1af39e75 100644 --- a/libs/ardour/ardour/bundle.h +++ b/libs/ardour/ardour/bundle.h @@ -21,17 +21,21 @@ #define __ardour_bundle_h__ #include <string> +#include <vector> +#include <glibmm/thread.h> #include <sigc++/signal.h> #include "ardour/data_type.h" namespace ARDOUR { /** 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. * `Channel' is a rather overloaded term but I can't think of a better * one right now. */ -class Bundle : public sigc::trackable { +class Bundle : public sigc::trackable +{ public: /// 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) 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. * @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) {} + /** 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() {} /** @return Number of channels that this Bundle has */ @@ -60,13 +82,16 @@ class Bundle : public sigc::trackable { */ 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 set_port (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 uses_port (std::string) const; void remove_channel (uint32_t); + void remove_channels (); /** Set the name. * @param n New name. @@ -94,7 +119,7 @@ class Bundle : public sigc::trackable { 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; /** The number of channels has changed */ sigc::signal<void> ConfigurationChanged; @@ -103,10 +128,10 @@ class Bundle : public sigc::trackable { protected: - /// mutex for _ports; + /// mutex for _channel_ports and _channel_names /// XXX: is this necessary? - mutable Glib::Mutex _ports_mutex; - std::vector<PortList> _ports; + mutable Glib::Mutex _channel_mutex; + std::vector<Channel> _channel; private: int set_channels (std::string const &); diff --git a/libs/ardour/bundle.cc b/libs/ardour/bundle.cc index bdad9d364d..379a3d4c2b 100644 --- a/libs/ardour/bundle.cc +++ b/libs/ardour/bundle.cc @@ -32,8 +32,8 @@ using namespace PBD; uint32_t Bundle::nchannels () const { - Glib::Mutex::Lock lm (_ports_mutex); - return _ports.size (); + Glib::Mutex::Lock lm (_channel_mutex); + return _channel.size (); } Bundle::PortList const & @@ -41,8 +41,8 @@ Bundle::channel_ports (uint32_t c) const { assert (c < nchannels()); - Glib::Mutex::Lock lm (_ports_mutex); - return _ports[c]; + Glib::Mutex::Lock lm (_channel_mutex); + return _channel[c].ports; } /** 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()); { - Glib::Mutex::Lock lm (_ports_mutex); - _ports[ch].push_back (portname); + Glib::Mutex::Lock lm (_channel_mutex); + _channel[ch].ports.push_back (portname); } PortsChanged (ch); /* EMIT SIGNAL */ @@ -74,8 +74,8 @@ Bundle::remove_port_from_channel (uint32_t ch, string portname) bool changed = false; { - Glib::Mutex::Lock lm (_ports_mutex); - PortList& pl = _ports[ch]; + Glib::Mutex::Lock lm (_channel_mutex); + PortList& pl = _channel[ch].ports; PortList::iterator i = find (pl.begin(), pl.end(), portname); if (i != pl.end()) { @@ -95,48 +95,31 @@ Bundle::remove_port_from_channel (uint32_t ch, string portname) bool 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 Bundle::set_port (uint32_t ch, string portname) { assert (ch < nchannels()); { - Glib::Mutex::Lock lm (_ports_mutex); - _ports[ch].clear (); - _ports[ch].push_back (portname); + Glib::Mutex::Lock lm (_channel_mutex); + _channel[ch].ports.clear (); + _channel[ch].ports.push_back (portname); } PortsChanged (ch); /* EMIT SIGNAL */ } +/** @param n Channel name */ void -Bundle::add_channel () +Bundle::add_channel (std::string const & n) { { - Glib::Mutex::Lock lm (_ports_mutex); - _ports.push_back (PortList ()); + Glib::Mutex::Lock lm (_channel_mutex); + _channel.push_back (Channel (n)); } ConfigurationChanged (); /* EMIT SIGNAL */ @@ -147,8 +130,8 @@ Bundle::port_attached_to_channel (uint32_t ch, std::string portname) { assert (ch < nchannels()); - Glib::Mutex::Lock lm (_ports_mutex); - return (std::find (_ports[ch].begin (), _ports[ch].end (), portname) != _ports[ch].end ()); + Glib::Mutex::Lock lm (_channel_mutex); + return (std::find (_channel[ch].ports.begin (), _channel[ch].ports.end (), portname) != _channel[ch].ports.end ()); } void @@ -156,6 +139,52 @@ Bundle::remove_channel (uint32_t ch) { assert (ch < nchannels ()); - Glib::Mutex::Lock lm (_ports_mutex); - _ports.erase (_ports.begin () + ch); + Glib::Mutex::Lock lm (_channel_mutex); + _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 */ } diff --git a/libs/ardour/io.cc b/libs/ardour/io.cc index ed1064f0e8..2b8f12680f 100644 --- a/libs/ardour/io.cc +++ b/libs/ardour/io.cc @@ -593,7 +593,7 @@ IO::remove_output_port (Port* port, void* src) PortCountChanged (n_outputs()); /* EMIT SIGNAL */ } - if (change == ConnectionsChanged) { + if (change == ConfigurationChanged) { setup_bundles_for_inputs_and_outputs (); } @@ -2592,19 +2592,24 @@ IO::setup_bundles_for_inputs_and_outputs () { char buf[32]; + _bundle_for_inputs->remove_channels (); + _bundle_for_outputs->remove_channels (); + snprintf(buf, sizeof (buf), _("%s in"), _name.c_str()); _bundle_for_inputs->set_name (buf); uint32_t const ni = inputs().num_ports(); - _bundle_for_inputs->set_nchannels (ni); 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()); } snprintf(buf, sizeof (buf), _("%s out"), _name.c_str()); _bundle_for_outputs->set_name (buf); uint32_t const no = outputs().num_ports(); - _bundle_for_outputs->set_nchannels (no); 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()); } } diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index c020509924..2b4d264bbb 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -598,7 +598,7 @@ Session::when_engine_running () snprintf (buf, sizeof (buf), _("out %" PRIu32), np+1); 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)); add_bundle (c); @@ -609,7 +609,7 @@ Session::when_engine_running () snprintf (buf, sizeof (buf), _("in %" PRIu32), np+1); 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)); add_bundle (c); @@ -622,8 +622,9 @@ Session::when_engine_running () snprintf (buf, sizeof (buf), _("out %" PRIu32 "+%" PRIu32), np+1, np+2); 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->add_channel (_("right")); c->set_port (1, _engine.get_nth_physical_output (DataType::AUDIO, np + 1)); add_bundle (c); @@ -634,8 +635,9 @@ Session::when_engine_running () snprintf (buf, sizeof (buf), _("in %" PRIu32 "+%" PRIu32), np+1, np+2); 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->add_channel (_("right")); c->set_port (1, _engine.get_nth_physical_input (DataType::AUDIO, np + 1)); add_bundle (c); @@ -2003,13 +2005,6 @@ Session::add_routes (RouteList& new_routes, bool save) if ((*x)->is_control()) { _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) { diff --git a/libs/ardour/user_bundle.cc b/libs/ardour/user_bundle.cc index d53bf8b155..2dee0af01e 100644 --- a/libs/ardour/user_bundle.cc +++ b/libs/ardour/user_bundle.cc @@ -45,7 +45,7 @@ ARDOUR::UserBundle::set_state (XMLNode const & node) return -1; } - add_channel (); + add_channel ("XXX"); XMLNodeList const ports = (*i)->children (); @@ -83,13 +83,13 @@ ARDOUR::UserBundle::get_state () 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"); + 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"); p->add_property ("name", *j); c->add_child_nocopy (*p); |