summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gtk2_ardour/SConscript2
-rw-r--r--gtk2_ardour/ardour.menus1
-rw-r--r--gtk2_ardour/ardour_ui.cc2
-rw-r--r--gtk2_ardour/ardour_ui.h5
-rw-r--r--gtk2_ardour/ardour_ui_dialogs.cc28
-rw-r--r--gtk2_ardour/ardour_ui_ed.cc2
-rw-r--r--gtk2_ardour/bundle_manager.cc335
-rw-r--r--gtk2_ardour/bundle_manager.h107
-rw-r--r--gtk2_ardour/io_selector.cc747
-rw-r--r--gtk2_ardour/io_selector.h149
-rw-r--r--gtk2_ardour/mixer_strip.cc61
-rw-r--r--gtk2_ardour/mixer_strip.h4
-rw-r--r--gtk2_ardour/port_matrix.cc730
-rw-r--r--gtk2_ardour/port_matrix.h200
-rw-r--r--libs/ardour/SConscript5
-rw-r--r--libs/ardour/ardour/audioengine.h4
-rw-r--r--libs/ardour/ardour/auto_bundle.h50
-rw-r--r--libs/ardour/ardour/bundle.h120
-rw-r--r--libs/ardour/ardour/data_type.h1
-rw-r--r--libs/ardour/ardour/io.h58
-rw-r--r--libs/ardour/ardour/session.h7
-rw-r--r--libs/ardour/ardour/user_bundle.h72
-rw-r--r--libs/ardour/audioengine.cc2
-rw-r--r--libs/ardour/auto_bundle.cc47
-rw-r--r--libs/ardour/io.cc705
-rw-r--r--libs/ardour/session.cc89
-rw-r--r--libs/ardour/session_state.cc56
-rw-r--r--libs/ardour/user_bundle.cc198
28 files changed, 2382 insertions, 1405 deletions
diff --git a/gtk2_ardour/SConscript b/gtk2_ardour/SConscript
index 61d653bed7..f1309b8c00 100644
--- a/gtk2_ardour/SConscript
+++ b/gtk2_ardour/SConscript
@@ -115,6 +115,7 @@ automation_time_axis.cc
automation_streamview.cc
automation_controller.cc
automation_region_view.cc
+bundle_manager.cc
midi_port_dialog.cc
midi_time_axis.cc
midi_streamview.cc
@@ -166,6 +167,7 @@ ghostregion.cc
gtk-custom-hruler.c
gtk-custom-ruler.c
io_selector.cc
+port_matrix.cc
keyboard.cc
keyeditor.cc
ladspa_pluginui.cc
diff --git a/gtk2_ardour/ardour.menus b/gtk2_ardour/ardour.menus
index 871aa15aee..6f11572151 100644
--- a/gtk2_ardour/ardour.menus
+++ b/gtk2_ardour/ardour.menus
@@ -237,6 +237,7 @@
<menuitem action='ToggleKeyEditor'/>
<menuitem action='ToggleThemeManager'/>
<menuitem action='ToggleBigClock'/>
+ <menuitem action='ToggleBundleManager'/>
<separator/>
</menu>
<menu name='Options' action='Options'>
diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc
index 491939bd97..1182f39792 100644
--- a/gtk2_ardour/ardour_ui.cc
+++ b/gtk2_ardour/ardour_ui.cc
@@ -85,7 +85,7 @@
#include "utils.h"
#include "gui_thread.h"
#include "theme_manager.h"
-
+#include "bundle_manager.h"
#include "i18n.h"
diff --git a/gtk2_ardour/ardour_ui.h b/gtk2_ardour/ardour_ui.h
index 9061f789a1..74400396af 100644
--- a/gtk2_ardour/ardour_ui.h
+++ b/gtk2_ardour/ardour_ui.h
@@ -78,6 +78,7 @@ class AddRouteDialog;
class NewSessionDialog;
class LocationUI;
class ThemeManager;
+class BundleManager;
namespace Gtkmm2ext {
class TearOff;
@@ -158,6 +159,7 @@ class ARDOUR_UI : public Gtkmm2ext::UI
void toggle_key_editor ();
void toggle_location_window ();
void toggle_theme_manager ();
+ void toggle_bundle_manager ();
void toggle_big_clock_window ();
void toggle_connection_editor ();
void toggle_route_params_window ();
@@ -612,6 +614,9 @@ class ARDOUR_UI : public Gtkmm2ext::UI
RouteParams_UI *route_params;
int create_route_params ();
+ BundleManager *bundle_manager;
+ void create_bundle_manager ();
+
ConnectionEditor *connection_editor;
int create_connection_editor ();
diff --git a/gtk2_ardour/ardour_ui_dialogs.cc b/gtk2_ardour/ardour_ui_dialogs.cc
index 89ab470d9b..34104016d7 100644
--- a/gtk2_ardour/ardour_ui_dialogs.cc
+++ b/gtk2_ardour/ardour_ui_dialogs.cc
@@ -35,6 +35,7 @@
#include "route_params_ui.h"
#include "sfdb_ui.h"
#include "theme_manager.h"
+#include "bundle_manager.h"
#include "keyeditor.h"
#include "i18n.h"
@@ -363,6 +364,33 @@ ARDOUR_UI::toggle_theme_manager ()
}
}
+void
+ARDOUR_UI::create_bundle_manager ()
+{
+ if (bundle_manager == 0) {
+ bundle_manager = new BundleManager (*session);
+ bundle_manager->signal_unmap().connect (sigc::bind (sigc::ptr_fun (&ActionManager::uncheck_toggleaction), X_("<Actions>/Common/ToggleBundleManager")));
+ }
+}
+
+void
+ARDOUR_UI::toggle_bundle_manager ()
+{
+ create_bundle_manager ();
+
+ RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("ToggleBundleManager"));
+ if (act) {
+ RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic (act);
+
+ if (tact->get_active()) {
+ bundle_manager->show_all ();
+ bundle_manager->present ();
+ } else {
+ bundle_manager->hide ();
+ }
+ }
+}
+
int
ARDOUR_UI::create_route_params ()
{
diff --git a/gtk2_ardour/ardour_ui_ed.cc b/gtk2_ardour/ardour_ui_ed.cc
index 8ba5e3790a..74b5a70738 100644
--- a/gtk2_ardour/ardour_ui_ed.cc
+++ b/gtk2_ardour/ardour_ui_ed.cc
@@ -219,6 +219,8 @@ ARDOUR_UI::install_actions ()
ActionManager::register_action (common_actions, X_("About"), _("About"), mem_fun(*this, &ARDOUR_UI::show_splash));
ActionManager::register_toggle_action (common_actions, X_("ToggleThemeManager"), _("Theme Manager"), mem_fun(*this, &ARDOUR_UI::toggle_theme_manager));
+ ActionManager::register_toggle_action (common_actions, X_("ToggleBundleManager"), _("Bundle Manager"), mem_fun(*this, &ARDOUR_UI::toggle_bundle_manager));
+
ActionManager::register_toggle_action (common_actions, X_("ToggleKeyEditor"), _("Keybindings"), mem_fun(*this, &ARDOUR_UI::toggle_key_editor));
Glib::RefPtr<ActionGroup> transport_actions = ActionGroup::create (X_("Transport"));
diff --git a/gtk2_ardour/bundle_manager.cc b/gtk2_ardour/bundle_manager.cc
new file mode 100644
index 0000000000..4dacce782f
--- /dev/null
+++ b/gtk2_ardour/bundle_manager.cc
@@ -0,0 +1,335 @@
+/*
+ Copyright (C) 2007 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ 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 <gtkmm/stock.h>
+#include <gtkmm/button.h>
+#include <gtkmm/label.h>
+#include <gtkmm/entry.h>
+#include <gtkmm/table.h>
+#include <gtkmm/comboboxtext.h>
+#include <gtkmm/alignment.h>
+#include "ardour/session.h"
+#include "ardour/user_bundle.h"
+#include "ardour/audioengine.h"
+#include "bundle_manager.h"
+#include "i18n.h"
+
+BundleEditorMatrix::BundleEditorMatrix (
+ ARDOUR::Session& session, boost::shared_ptr<ARDOUR::Bundle> bundle
+ )
+ : PortMatrix (
+ session, bundle->type(), bundle->ports_are_inputs(),
+ PortGroupList::Mask (PortGroupList::SYSTEM | PortGroupList::OTHER)
+ )
+{
+ _bundle = boost::dynamic_pointer_cast<ARDOUR::UserBundle> (bundle);
+ assert (_bundle != 0);
+}
+
+void
+BundleEditorMatrix::set_state (int r, std::string const & p, bool s)
+{
+ if (s) {
+ _bundle->add_port_to_channel (r, p);
+ } else {
+ _bundle->remove_port_from_channel (r, p);
+ }
+}
+
+bool
+BundleEditorMatrix::get_state (int r, std::string const & p) const
+{
+ return _bundle->port_attached_to_channel (r, p);
+}
+
+uint32_t
+BundleEditorMatrix::n_rows () const
+{
+ return _bundle->nchannels ();
+}
+
+uint32_t
+BundleEditorMatrix::maximum_rows () const
+{
+ /* 65536 channels in a bundle ought to be enough for anyone (TM) */
+ return 65536;
+}
+
+uint32_t
+BundleEditorMatrix::minimum_rows () const
+{
+ return 0;
+}
+
+std::string
+BundleEditorMatrix::row_name (int r) const
+{
+ std::stringstream s;
+ s << r;
+ return s.str();
+}
+
+void
+BundleEditorMatrix::add_row ()
+{
+ _bundle->add_channel ();
+ redisplay ();
+}
+
+void
+BundleEditorMatrix::remove_row (int r)
+{
+ _bundle->remove_channel (r);
+ redisplay ();
+}
+
+std::string
+BundleEditorMatrix::row_descriptor () const
+{
+ return _("channel");
+}
+
+BundleEditor::BundleEditor (ARDOUR::Session& session, boost::shared_ptr<ARDOUR::UserBundle> bundle, bool add)
+ : ArdourDialog (_("Edit Bundle")), _matrix (session, bundle), _bundle (bundle)
+{
+ Gtk::Table* t = new Gtk::Table (3, 2);
+ t->set_spacings (4);
+
+ 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));
+
+ 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"));
+
+ if (bundle->ports_are_inputs()) {
+ _input_or_output.set_active_text (_("Output"));
+ } else {
+ _input_or_output.set_active_text (_("Input"));
+ }
+
+ _input_or_output.signal_changed().connect (sigc::mem_fun (*this, &BundleEditor::input_or_output_changed));
+
+ 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);
+ a = new Gtk::Alignment (0, 0.5, 0, 1);
+ a->add (_type);
+ t->attach (*Gtk::manage (a), 1, 2, 2, 3);
+
+ _type.append_text (_("Audio"));
+ _type.append_text (_("MIDI"));
+
+ switch (bundle->type ()) {
+ case ARDOUR::DataType::AUDIO:
+ _type.set_active_text (_("Audio"));
+ break;
+ case ARDOUR::DataType::MIDI:
+ _type.set_active_text (_("MIDI"));
+ break;
+ }
+
+ _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);
+
+ if (add) {
+ add_button (Gtk::Stock::CANCEL, 1);
+ add_button (Gtk::Stock::ADD, 0);
+ } else {
+ add_button (Gtk::Stock::CLOSE, 0);
+ }
+
+ show_all ();
+}
+
+void
+BundleEditor::name_changed ()
+{
+ _bundle->set_name (_name.get_text ());
+}
+
+void
+BundleEditor::input_or_output_changed ()
+{
+ if (_input_or_output.get_active_text() == _("Output")) {
+ _bundle->set_ports_are_inputs ();
+ _matrix.set_offer_inputs (true);
+ } else {
+ _bundle->set_ports_are_outputs ();
+ _matrix.set_offer_inputs (false);
+ }
+}
+
+void
+BundleEditor::type_changed ()
+{
+ ARDOUR::DataType const t = _type.get_active_text() == _("Audio") ?
+ ARDOUR::DataType::AUDIO : ARDOUR::DataType::MIDI;
+
+ _bundle->set_type (t);
+ _matrix.set_type (t);
+}
+
+void
+BundleEditor::on_map ()
+{
+ _matrix.redisplay ();
+ Window::on_map ();
+}
+
+
+BundleManager::BundleManager (ARDOUR::Session& session)
+ : ArdourDialog (_("Bundle manager")), _session (session), edit_button (_("Edit")), delete_button (_("Delete"))
+{
+ _list_model = Gtk::ListStore::create (_list_model_columns);
+ _tree_view.set_model (_list_model);
+ _tree_view.append_column (_("Name"), _list_model_columns.name);
+ _tree_view.set_headers_visible (false);
+
+ _session.foreach_bundle (sigc::mem_fun (*this, &BundleManager::add_bundle));
+
+ /* New / Edit / Delete buttons */
+ Gtk::VBox* buttons = new Gtk::VBox;
+ buttons->set_spacing (8);
+ Gtk::Button* b = new Gtk::Button (_("New"));
+ b->set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::NEW, Gtk::ICON_SIZE_BUTTON)));
+ b->signal_clicked().connect (sigc::mem_fun (*this, &BundleManager::new_clicked));
+ buttons->pack_start (*Gtk::manage (b), false, false);
+ edit_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::EDIT, Gtk::ICON_SIZE_BUTTON)));
+ edit_button.signal_clicked().connect (sigc::mem_fun (*this, &BundleManager::edit_clicked));
+ buttons->pack_start (edit_button, false, false);
+ delete_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::DELETE, Gtk::ICON_SIZE_BUTTON)));
+ delete_button.signal_clicked().connect (sigc::mem_fun (*this, &BundleManager::delete_clicked));
+ buttons->pack_start (delete_button, false, false);
+
+ Gtk::HBox* h = new Gtk::HBox;
+ h->set_spacing (8);
+ h->set_border_width (8);
+ h->pack_start (_tree_view);
+ h->pack_start (*Gtk::manage (buttons), false, false);
+
+ get_vbox()->set_spacing (8);
+ get_vbox()->pack_start (*Gtk::manage (h));
+
+ set_default_size (480, 240);
+
+ _tree_view.get_selection()->signal_changed().connect (
+ sigc::mem_fun (*this, &BundleManager::set_button_sensitivity)
+ );
+
+ set_button_sensitivity ();
+
+ show_all ();
+}
+
+void
+BundleManager::set_button_sensitivity ()
+{
+ bool const sel = (_tree_view.get_selection()->get_selected() != 0);
+ edit_button.set_sensitive (sel);
+ delete_button.set_sensitive (sel);
+}
+
+
+void
+BundleManager::new_clicked ()
+{
+ boost::shared_ptr<ARDOUR::UserBundle> b (new ARDOUR::UserBundle (""));
+
+ /* Start off with a single channel */
+ b->add_channel ();
+
+ BundleEditor e (_session, b, true);
+ if (e.run () == 0) {
+ _session.add_bundle (b);
+ add_bundle (b);
+ }
+}
+
+void
+BundleManager::edit_clicked ()
+{
+ Gtk::TreeModel::iterator i = _tree_view.get_selection()->get_selected();
+ if (i) {
+ boost::shared_ptr<ARDOUR::UserBundle> b = (*i)[_list_model_columns.bundle];
+ BundleEditor e (_session, b, false);
+ e.run ();
+ }
+
+}
+
+void
+BundleManager::delete_clicked ()
+{
+ Gtk::TreeModel::iterator i = _tree_view.get_selection()->get_selected();
+ if (i) {
+ boost::shared_ptr<ARDOUR::UserBundle> b = (*i)[_list_model_columns.bundle];
+ _session.remove_bundle (b);
+ _list_model->erase (i);
+ }
+}
+
+void
+BundleManager::add_bundle (boost::shared_ptr<ARDOUR::Bundle> b)
+{
+ boost::shared_ptr<ARDOUR::UserBundle> u = boost::dynamic_pointer_cast<ARDOUR::UserBundle> (b);
+ if (u == 0) {
+ return;
+ }
+
+ Gtk::TreeModel::iterator i = _list_model->append ();
+ (*i)[_list_model_columns.name] = u->name ();
+ (*i)[_list_model_columns.bundle] = u;
+
+ u->NameChanged.connect (sigc::bind (sigc::mem_fun (*this, &BundleManager::bundle_name_changed), u));
+}
+
+void
+BundleManager::bundle_name_changed (boost::shared_ptr<ARDOUR::UserBundle> b)
+{
+ Gtk::TreeModel::iterator i = _list_model->children().begin ();
+ while (i != _list_model->children().end()) {
+ boost::shared_ptr<ARDOUR::UserBundle> t = (*i)[_list_model_columns.bundle];
+ if (t == b) {
+ break;
+ }
+ ++i;
+ }
+
+ if (i != _list_model->children().end()) {
+ (*i)[_list_model_columns.name] = b->name ();
+ }
+}
+
diff --git a/gtk2_ardour/bundle_manager.h b/gtk2_ardour/bundle_manager.h
new file mode 100644
index 0000000000..4d4d44074f
--- /dev/null
+++ b/gtk2_ardour/bundle_manager.h
@@ -0,0 +1,107 @@
+/*
+ Copyright (C) 2007 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ 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 __ardour_ui_bundle_manager_h__
+#define __ardour_ui_bundle_manager_h__
+
+#include <gtkmm/treeview.h>
+#include <gtkmm/liststore.h>
+#include "ardour_dialog.h"
+#include "port_matrix.h"
+
+namespace ARDOUR {
+ class Session;
+ class Bundle;
+}
+
+class BundleEditorMatrix : public PortMatrix
+{
+ public:
+ BundleEditorMatrix (ARDOUR::Session &, boost::shared_ptr<ARDOUR::Bundle>);
+
+ void set_state (int, std::string const &, bool);
+ 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;
+};
+
+class BundleEditor : public ArdourDialog
+{
+ public:
+ BundleEditor (ARDOUR::Session &, boost::shared_ptr<ARDOUR::UserBundle>, bool);
+
+ protected:
+ void on_map ();
+
+ private:
+ void name_changed ();
+ void input_or_output_changed ();
+ void type_changed ();
+
+ BundleEditorMatrix _matrix;
+ boost::shared_ptr<ARDOUR::UserBundle> _bundle;
+ Gtk::Entry _name;
+ Gtk::ComboBoxText _input_or_output;
+ Gtk::ComboBoxText _type;
+};
+
+class BundleManager : public ArdourDialog
+{
+ public:
+ BundleManager (ARDOUR::Session &);
+
+ private:
+
+ void new_clicked ();
+ void edit_clicked ();
+ void delete_clicked ();
+ void add_bundle (boost::shared_ptr<ARDOUR::Bundle>);
+ void bundle_name_changed (boost::shared_ptr<ARDOUR::UserBundle>);
+ void set_button_sensitivity ();
+
+ class ModelColumns : public Gtk::TreeModelColumnRecord
+ {
+ public:
+ ModelColumns () {
+ add (name);
+ add (bundle);
+ }
+
+ Gtk::TreeModelColumn<Glib::ustring> name;
+ Gtk::TreeModelColumn<boost::shared_ptr<ARDOUR::UserBundle> > bundle;
+ };
+
+ Gtk::TreeView _tree_view;
+ Glib::RefPtr<Gtk::ListStore> _list_model;
+ ModelColumns _list_model_columns;
+ ARDOUR::Session& _session;
+ Gtk::Button edit_button;
+ Gtk::Button delete_button;
+};
+
+#endif
diff --git a/gtk2_ardour/io_selector.cc b/gtk2_ardour/io_selector.cc
index d0f236f84a..b8598659d7 100644
--- a/gtk2_ardour/io_selector.cc
+++ b/gtk2_ardour/io_selector.cc
@@ -17,15 +17,6 @@
*/
-#include <gtkmm/label.h>
-#include <gtkmm/enums.h>
-#include <gtkmm/image.h>
-#include <gtkmm/stock.h>
-#include <gtkmm/messagedialog.h>
-#include <gtkmm/menu.h>
-#include <gtkmm/menu_elems.h>
-#include <gtkmm/menuitem.h>
-#include <gtkmm/menushell.h>
#include <glibmm/objectbase.h>
#include <gtkmm2ext/doi.h>
#include <ardour/port_insert.h>
@@ -41,575 +32,116 @@
#include "gui_thread.h"
#include "i18n.h"
-/** Add a port to a group.
- * @param p Port name, with or without prefix.
- */
-
-void
-PortGroup::add (std::string const & p)
-{
- if (prefix.empty() == false && p.substr (0, prefix.length()) == prefix) {
- ports.push_back (p.substr (prefix.length()));
- } else {
- ports.push_back (p);
- }
-}
-
-
-PortGroupTable::PortGroupTable (
- PortGroup& g, boost::shared_ptr<ARDOUR::IO> io, bool for_input
- )
- : _port_group (g), _ignore_check_button_toggle (false),
- _io (io), _for_input (for_input)
+IOSelector::IOSelector (ARDOUR::Session& session, boost::shared_ptr<ARDOUR::IO> io, bool offer_inputs)
+ : PortMatrix (
+ session, io->default_type(), offer_inputs,
+ PortGroupList::Mask (PortGroupList::BUSS | PortGroupList::SYSTEM | PortGroupList::OTHER)
+ ),
+ _io (io)
{
- ARDOUR::DataType const t = _io->default_type();
-
- int rows;
- if (_for_input) {
- rows = _io->n_inputs().get(t);
+ /* Listen for ports changing on the IO */
+ if (!offer_inputs) {
+ _io->input_changed.connect (mem_fun(*this, &IOSelector::ports_changed));
} else {
- rows = _io->n_outputs().get(t);
- }
-
- int const ports = _port_group.ports.size();
-
- if (rows == 0 || ports == 0) {
- return;
- }
-
- /* Sort out the table and the checkbuttons inside it */
-
- _table.resize (rows, ports);
- _check_buttons.resize (rows);
- for (int i = 0; i < rows; ++i) {
- _check_buttons[i].resize (ports);
- }
-
- for (int i = 0; i < rows; ++i) {
- for (uint32_t j = 0; j < _port_group.ports.size(); ++j) {
- Gtk::CheckButton* b = new Gtk::CheckButton;
- b->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &PortGroupTable::check_button_toggled), b, i, _port_group.prefix + _port_group.ports[j]));
- _check_buttons[i][j] = b;
- _table.attach (*b, j, j + 1, i, i + 1);
- }
- }
-
- _box.add (_table);
-
- _ignore_check_button_toggle = true;
-
- /* Set the state of the check boxes according to current connections */
- for (int i = 0; i < rows; ++i) {
- const char **connections = _for_input ? _io->input(i)->get_connections() : _io->output(i)->get_connections();
- for (uint32_t j = 0; j < _port_group.ports.size(); ++j) {
-
- std::string const t = _port_group.prefix + _port_group.ports[j];
- int k = 0;
- bool required_state = false;
-
- while (connections && connections[k]) {
- if (std::string(connections[k]) == t) {
- required_state = true;
- break;
- }
- ++k;
- }
-
- _check_buttons[i][j]->set_active (required_state);
- }
- }
-
- _ignore_check_button_toggle = false;
-}
-
-/** @return Width and height of a single check button in a port group table */
-std::pair<int, int>
-PortGroupTable::unit_size () const
-{
- if (_check_buttons.empty() || _check_buttons[0].empty()) {
- return std::pair<int, int> (0, 0);
+ _io->output_changed.connect (mem_fun(*this, &IOSelector::ports_changed));
}
-
- return std::make_pair (
- _check_buttons[0][0]->get_width() + _table.get_col_spacing (0),
- _check_buttons[0][0]->get_height() + _table.get_row_spacing (0)
- );
-}
-
-Gtk::Widget&
-PortGroupTable::get_widget ()
-{
- return _box;
-}
-
-
-/** Handle a toggle of a check button */
-void
-PortGroupTable::check_button_toggled (Gtk::CheckButton* b, int r, std::string const & p)
-{
- if (_ignore_check_button_toggle) {
- return;
- }
-
- bool const new_state = b->get_active ();
-
- if (new_state) {
- if (_for_input) {
- _io->connect_input (_io->input(r), p, 0);
- } else {
- _io->connect_output (_io->output(r), p, 0);
- }
- } else {
- if (_for_input) {
- _io->disconnect_input (_io->input(r), p, 0);
- } else {
- _io->disconnect_output (_io->output(r), p, 0);
- }
- }
-}
-
-
-RotatedLabelSet::RotatedLabelSet (PortGroupList& g)
- : Glib::ObjectBase ("RotatedLabelSet"), Gtk::Widget (), _port_group_list (g), _base_width (128)
-{
- set_flags (Gtk::NO_WINDOW);
- set_angle (30);
-}
-
-RotatedLabelSet::~RotatedLabelSet ()
-{
-
-}
-
-
-/** Set the angle that the labels are drawn at.
- * @param degrees New angle in degrees.
- */
-
-void
-RotatedLabelSet::set_angle (int degrees)
-{
- _angle_degrees = degrees;
- _angle_radians = M_PI * _angle_degrees / 180;
-
- queue_resize ();
}
-void
-RotatedLabelSet::on_size_request (Gtk::Requisition* requisition)
-{
- *requisition = Gtk::Requisition ();
-
- if (_pango_layout == 0) {
- return;
- }
-
- /* Our height is the highest label */
- requisition->height = 0;
- for (PortGroupList::const_iterator i = _port_group_list.begin(); i != _port_group_list.end(); ++i) {
- for (std::vector<std::string>::const_iterator j = (*i)->ports.begin(); j != (*i)->ports.end(); ++j) {
- std::pair<int, int> const d = setup_layout (*j);
- if (d.second > requisition->height) {
- requisition->height = d.second;
- }
- }
- }
-
- /* And our width is the base plus the width of the last label */
- requisition->width = _base_width;
- int const n = _port_group_list.n_visible_ports ();
- if (n > 0) {
- std::pair<int, int> const d = setup_layout (_port_group_list.get_port_by_index (n - 1, false));
- requisition->width += d.first;
- }
-}
void
-RotatedLabelSet::on_size_allocate (Gtk::Allocation& allocation)
+IOSelector::ports_changed (ARDOUR::IOChange change, void *src)
{
- set_allocation (allocation);
+ ENSURE_GUI_THREAD (bind (mem_fun (*this, &IOSelector::ports_changed), change, src));
- if (_gdk_window) {
- _gdk_window->move_resize (
- allocation.get_x(), allocation.get_y(), allocation.get_width(), allocation.get_height()
- );
- }
+ redisplay ();
}
-void
-RotatedLabelSet::on_realize ()
-{
- Gtk::Widget::on_realize ();
-
- Glib::RefPtr<Gtk::Style> style = get_style ();
-
- if (!_gdk_window) {
- GdkWindowAttr attributes;
- memset (&attributes, 0, sizeof (attributes));
-
- Gtk::Allocation allocation = get_allocation ();
- attributes.x = allocation.get_x ();
- attributes.y = allocation.get_y ();
- attributes.width = allocation.get_width ();
- attributes.height = allocation.get_height ();
-
- attributes.event_mask = get_events () | Gdk::EXPOSURE_MASK;
- attributes.window_type = GDK_WINDOW_CHILD;
- attributes.wclass = GDK_INPUT_OUTPUT;
- _gdk_window = Gdk::Window::create (get_window (), &attributes, GDK_WA_X | GDK_WA_Y);
- unset_flags (Gtk::NO_WINDOW);
- set_window (_gdk_window);
-
- _bg_colour = style->get_bg (Gtk::STATE_NORMAL );
- modify_bg (Gtk::STATE_NORMAL, _bg_colour);
- _fg_colour = style->get_fg (Gtk::STATE_NORMAL);
-;
- _gdk_window->set_user_data (gobj ());
-
- /* Set up Pango stuff */
- _pango_context = create_pango_context ();
-
- Pango::Matrix matrix = PANGO_MATRIX_INIT;
- pango_matrix_rotate (&matrix, _angle_degrees);
- _pango_context->set_matrix (matrix);
-
- _pango_layout = Pango::Layout::create (_pango_context);
- _gc = Gdk::GC::create (get_window ());
- }
-}
void
-RotatedLabelSet::on_unrealize()
-{
- _gdk_window.clear ();
-
- Gtk::Widget::on_unrealize ();
-}
-
-
-/** Set up our Pango layout to plot a given string, and compute its dimensions once
- * it has been rotated.
- * @param s String to use.
- * @return width and height of the rotated string, in pixels.
- */
-
-std::pair<int, int>
-RotatedLabelSet::setup_layout (std::string const & s)
+IOSelector::set_state (int r, std::string const & p, bool s)
{
- _pango_layout->set_text (s);
-
- /* Here's the unrotated size */
- int w;
- int h;
- _pango_layout->get_pixel_size (w, h);
-
- /* Rotate the width and height as appropriate. I thought Pango might be able
- to do this for us, but I can't find out how... */
- std::pair<int, int> d;
- d.first = int (w * cos (_angle_radians) - h * sin (_angle_radians));
- d.second = int (w * sin (_angle_radians) + h * cos (_angle_radians));
-
- return d;
+ 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);
+ }
+ }
}
bool
-RotatedLabelSet::on_expose_event (GdkEventExpose* event)
+IOSelector::get_state (int r, std::string const & p) const
{
- if (!_gdk_window) {
- return true;
- }
+ const char **connections = _offer_inputs ? _io->output(r)->get_connections() : _io->input(r)->get_connections();
- int const height = get_allocation().get_height ();
- double const spacing = double (_base_width) / _port_group_list.n_visible_ports();
-
- /* Plot all the visible labels; really we should clip for efficiency */
- int n = 0;
- for (PortGroupList::const_iterator i = _port_group_list.begin(); i != _port_group_list.end(); ++i) {
- if ((*i)->visible) {
- for (uint32_t j = 0; j < (*i)->ports.size(); ++j) {
- std::pair<int, int> const d = setup_layout ((*i)->ports[j]);
- get_window()->draw_layout (_gc, int ((n + 0.25) * spacing), height - d.second, _pango_layout, _fg_colour, _bg_colour);
- ++n;
- }
+ int k = 0;
+ while (connections && connections[k]) {
+ if (std::string (connections[k]) == p) {
+ return true;
}
- }
-
- return true;
-}
-
-/** Set the `base width'. This is the width of the base of the label set, ie:
- *
- * L L L L
- * E E E E
- * B B B B
- * A A A A
- * L L L L
- * <--w-->
- */
-
-void
-RotatedLabelSet::set_base_width (int w)
-{
- _base_width = w;
- queue_resize ();
-}
-
-
-/** Construct an IOSelector.
- * @param session Session to operate on.
- * @param io IO to operate on.
- * @param for_input true if the selector is for an input, otherwise false.
- */
-
-IOSelector::IOSelector (ARDOUR::Session& session, boost::shared_ptr<ARDOUR::IO> io, bool for_input)
- : _port_group_list (session, io, for_input), _io (io), _for_input (for_input),
- _column_labels (_port_group_list)
-{
- _row_labels_vbox[0] = _row_labels_vbox[1] = 0;
- _side_vbox_pad[0] = _side_vbox_pad[1] = 0;
-
- Gtk::HBox* c = new Gtk::HBox;
- for (PortGroupList::iterator i = _port_group_list.begin(); i != _port_group_list.end(); ++i) {
- Gtk::CheckButton* b = new Gtk::CheckButton ((*i)->name);
- b->set_active (true);
- b->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &IOSelector::group_visible_toggled), b, (*i)->name));
- c->pack_start (*Gtk::manage (b), false, false);
- }
- pack_start (*Gtk::manage (c));
-
- _side_vbox[0].pack_start (*Gtk::manage (new Gtk::Label ("")));
- _overall_hbox.pack_start (_side_vbox[0], false, false);
- _scrolled_window.set_policy (Gtk::POLICY_ALWAYS, Gtk::POLICY_NEVER);
- _scrolled_window.set_shadow_type (Gtk::SHADOW_NONE);
- Gtk::VBox* b = new Gtk::VBox;
- b->pack_start (_column_labels, false, false);
- b->pack_start (_port_group_hbox, false, false);
- Gtk::Alignment* a = new Gtk::Alignment (0, 1, 0, 0);
- a->add (*Gtk::manage (b));
- _scrolled_window.add (*Gtk::manage (a));
- _overall_hbox.pack_start (_scrolled_window);
- _side_vbox[1].pack_start (*Gtk::manage (new Gtk::Label ("")));
- _overall_hbox.pack_start (_side_vbox[1]);
- pack_start (_overall_hbox);
-
- _port_group_hbox.signal_size_allocate().connect (sigc::hide (sigc::mem_fun (*this, &IOSelector::setup_dimensions)));
- /* Listen for ports changing on the IO */
- if (_for_input) {
- _io->input_changed.connect (mem_fun(*this, &IOSelector::ports_changed));
- } else {
- _io->output_changed.connect (mem_fun(*this, &IOSelector::ports_changed));
+ ++k;
}
-
-}
-IOSelector::~IOSelector ()
-{
- clear ();
+ return false;
}
-/** Clear out the things that change when the number of source or destination ports changes */
-void
-IOSelector::clear ()
+uint32_t
+IOSelector::n_rows () const
{
- for (int i = 0; i < 2; ++i) {
-
- for (std::vector<Gtk::EventBox*>::iterator j = _row_labels[i].begin(); j != _row_labels[i].end(); ++j) {
- delete *j;
- }
- _row_labels[i].clear ();
-
- if (_row_labels_vbox[i]) {
- _side_vbox[i].remove (*_row_labels_vbox[i]);
- }
- delete _row_labels_vbox[i];
- _row_labels_vbox[i] = 0;
-
- if (_side_vbox_pad[i]) {
- _side_vbox[i].remove (*_side_vbox_pad[i]);
- }
- delete _side_vbox_pad[i];
- _side_vbox_pad[i] = 0;
- }
-
- for (std::vector<PortGroupTable*>::iterator i = _port_group_tables.begin(); i != _port_group_tables.end(); ++i) {
- _port_group_hbox.remove ((*i)->get_widget());
- delete *i;
+ if (!_offer_inputs) {
+ return _io->inputs().num_ports (_io->default_type());
+ } else {
+ return _io->outputs().num_ports (_io->default_type());
}
-
- _port_group_tables.clear ();
}
-
-/** Set up dimensions of some of our widgets which depend on other dimensions
- * within the dialogue.
- */
-void
-IOSelector::setup_dimensions ()
+uint32_t
+IOSelector::maximum_rows () const
{
- /* Get some dimensions from various places */
- int const scrollbar_height = _scrolled_window.get_hscrollbar()->get_height();
-
- std::pair<int, int> unit_size (0, 0);
- int port_group_tables_height = 0;
- for (std::vector<PortGroupTable*>::iterator i = _port_group_tables.begin(); i != _port_group_tables.end(); ++i) {
- std::pair<int, int> const u = (*i)->unit_size ();
- unit_size.first = std::max (unit_size.first, u.first);
- unit_size.second = std::max (unit_size.second, u.second);
- port_group_tables_height = std::max (
- port_group_tables_height, (*i)->get_widget().get_height()
- );
- }
-
- /* Column labels */
- _column_labels.set_base_width (_port_group_list.n_visible_ports () * unit_size.first);
-
- /* Scrolled window */
- /* XXX: really shouldn't set a minimum horizontal size here, but if we don't
- the window starts up very small */
- _scrolled_window.set_size_request (
- std::min (_column_labels.get_width(), 640),
- _column_labels.get_height() + port_group_tables_height + scrollbar_height + 16
- );
-
- /* Row labels */
- for (int i = 0; i < 2; ++i) {
- for (std::vector<Gtk::EventBox*>::iterator j = _row_labels[i].begin(); j != _row_labels[i].end(); ++j) {
- (*j)->get_child()->set_size_request (-1, unit_size.second);
- }
-
- if (_side_vbox_pad[i]) {
- _side_vbox_pad[i]->set_size_request (-1, scrollbar_height + unit_size.second / 4);
- }
+ if (!_offer_inputs) {
+ return _io->input_maximum ().get (_io->default_type());
+ } else {
+ return _io->output_maximum ().get (_io->default_type());
}
}
-/** Set up the dialogue */
-void
-IOSelector::setup ()
+uint32_t
+IOSelector::minimum_rows () const
{
- clear ();
-
- /* Work out how many rows we have */
- ARDOUR::DataType const t = _io->default_type();
-
- int rows;
- if (_for_input) {
- rows = _io->n_inputs().get(t);
- } else {
- rows = _io->n_outputs().get(t);
- }
-
- /* Row labels */
- for (int i = 0; i < 2; ++i) {
- _row_labels_vbox[i] = new Gtk::VBox;
- for (int j = 0; j < rows; ++j) {
- Gtk::Label* label = new Gtk::Label (_for_input ? _io->input(j)->name() : _io->output(j)->name());
- Gtk::EventBox* b = new Gtk::EventBox;
- b->set_events (Gdk::BUTTON_PRESS_MASK);
- b->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &IOSelector::row_label_button_pressed), j));
- b->add (*Gtk::manage (label));
- _row_labels[i].push_back (b);
- _row_labels_vbox[i]->pack_start (*b, false, false);
- }
-
- _side_vbox[i].pack_start (*_row_labels_vbox[i], false, false);
- _side_vbox_pad[i] = new Gtk::Label ("");
- _side_vbox[i].pack_start (*_side_vbox_pad[i], false, false);
+ if (!_offer_inputs) {
+ return _io->input_minimum ().get (_io->default_type());
+ } else {
+ return _io->output_minimum ().get (_io->default_type());
}
-
- /* Checkbutton tables */
- int n = 0;
- for (PortGroupList::iterator i = _port_group_list.begin(); i != _port_group_list.end(); ++i) {
- PortGroupTable* t = new PortGroupTable (**i, _io, _for_input);
-
- /* XXX: this is a bit of a hack; should probably use a configurable colour here */
- Gdk::Color alt_bg = get_style()->get_bg (Gtk::STATE_NORMAL);
- alt_bg.set_rgb (alt_bg.get_red() + 4096, alt_bg.get_green() + 4096, alt_bg.get_blue () + 4096);
- if ((n % 2) == 0) {
- t->get_widget().modify_bg (Gtk::STATE_NORMAL, alt_bg);
- }
-
- _port_group_tables.push_back (t);
- _port_group_hbox.pack_start (t->get_widget(), false, false);
- ++n;
- }
-
- show_all ();
-
- set_port_group_table_visibility ();
-}
-
-void
-IOSelector::ports_changed (ARDOUR::IOChange change, void *src)
-{
- ENSURE_GUI_THREAD (bind (mem_fun (*this, &IOSelector::ports_changed), change, src));
-
- redisplay ();
-}
-
-
-void
-IOSelector::redisplay ()
-{
- _port_group_list.refresh ();
- setup ();
}
-
-/** Handle a button press on a row label */
-bool
-IOSelector::row_label_button_pressed (GdkEventButton* e, int r)
+std::string
+IOSelector::row_name (int r) const
{
- if (e->type != GDK_BUTTON_PRESS || e->button != 3) {
- return false;
- }
-
- Gtk::Menu* menu = Gtk::manage (new Gtk::Menu);
- Gtk::Menu_Helpers::MenuList& items = menu->items ();
- menu->set_name ("ArdourContextMenu");
-
- bool can_add;
- bool can_remove;
- std::string name;
- ARDOUR::DataType const t = _io->default_type();
-
- if (_for_input) {
- can_add = _io->input_maximum().get(t) > _io->n_inputs().get(t);
- can_remove = _io->input_minimum().get(t) < _io->n_inputs().get(t);
- name = _io->input(r)->name();
+ if (!_offer_inputs) {
+ return _io->input(r)->name();
} else {
- can_add = _io->output_maximum().get(t) > _io->n_outputs().get(t);
- can_remove = _io->output_minimum().get(t) < _io->n_outputs().get(t);
- name = _io->output(r)->name();
+ return _io->output(r)->name();
}
-
- items.push_back (
- Gtk::Menu_Helpers::MenuElem (_("Add port"), sigc::mem_fun (*this, &IOSelector::add_port))
- );
-
- items.back().set_sensitive (can_add);
-
- items.push_back (
- Gtk::Menu_Helpers::MenuElem (_("Remove port '") + name + _("'"), sigc::bind (sigc::mem_fun (*this, &IOSelector::remove_port), r))
- );
-
- items.back().set_sensitive (can_remove);
-
- menu->popup (e->button, e->time);
-
- return true;
+
}
void
-IOSelector::add_port ()
+IOSelector::add_row ()
{
// The IO selector only works for single typed IOs
const ARDOUR::DataType t = _io->default_type ();
- if (_for_input) {
+ if (!_offer_inputs) {
try {
_io->add_input_port ("", this);
@@ -633,176 +165,33 @@ IOSelector::add_port ()
}
}
+
void
-IOSelector::remove_port (int r)
+IOSelector::remove_row (int r)
{
// The IO selector only works for single typed IOs
const ARDOUR::DataType t = _io->default_type ();
- if (_for_input) {
+ if (!_offer_inputs) {
_io->remove_input_port (_io->input (r), this);
} else {
_io->remove_output_port (_io->output (r), this);
}
}
-void
-IOSelector::group_visible_toggled (Gtk::CheckButton* b, std::string const & n)
-{
- PortGroupList::iterator i = _port_group_list.begin();
- while (i != _port_group_list.end() & (*i)->name != n) {
- ++i;
- }
-
- if (i == _port_group_list.end()) {
- return;
- }
-
- (*i)->visible = b->get_active ();
-
- set_port_group_table_visibility ();
-
- _column_labels.queue_draw ();
-}
-
-void
-IOSelector::set_port_group_table_visibility ()
-{
- for (std::vector<PortGroupTable*>::iterator j = _port_group_tables.begin(); j != _port_group_tables.end(); ++j) {
- if ((*j)->port_group().visible) {
- (*j)->get_widget().show();
- } else {
- (*j)->get_widget().hide();
- }
- }
-}
-
-
-PortGroupList::PortGroupList (ARDOUR::Session & session, boost::shared_ptr<ARDOUR::IO> io, bool for_input)
- : _session (session), _io (io), _for_input (for_input),
- buss (_("Buss"), "ardour:"),
- track (_("Track"), "ardour:"),
- system (_("System"), "system:"),
- other (_("Other"), "")
-{
- refresh ();
-}
-
-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. */
-
- boost::shared_ptr<ARDOUR::Session::RouteList> routes = _session.get_routes ();
-
- for (ARDOUR::Session::RouteList::const_iterator i = routes->begin(); i != routes->end(); ++i) {
-
- PortGroup* g = 0;
- if (_io->default_type() == ARDOUR::DataType::AUDIO && dynamic_cast<ARDOUR::AudioTrack*> ((*i).get())) {
- /* Audio track for an audio IO */
- g = &track;
- } else if (_io->default_type() == ARDOUR::DataType::MIDI && dynamic_cast<ARDOUR::MidiTrack*> ((*i).get())) {
- /* Midi track for a MIDI IO */
- g = &track;
- } else if (_io->default_type() == ARDOUR::DataType::AUDIO && dynamic_cast<ARDOUR::MidiTrack*> ((*i).get()) == 0) {
- /* Non-MIDI track for an Audio IO; must be an audio buss */
- g = &buss;
- }
-
- if (g) {
- ARDOUR::PortSet const & p = _for_input ? ((*i)->outputs()) : ((*i)->inputs());
- for (uint32_t j = 0; j < p.num_ports(); ++j) {
- g->add (p.port(j)->name ());
- }
-
- std::sort (g->ports.begin(), g->ports.end());
- }
- }
-
-
- /* XXX: inserts, sends, plugin inserts? */
-
- /* Now we need to find the non-ardour ports; we do this by first
- finding all the ports that we can connect to. */
- const char **ports = _session.engine().get_ports (
- "", _io->default_type().to_jack_type(), _for_input ? JackPortIsOutput : JackPortIsInput
- );
-
- if (ports) {
-
- int n = 0;
- while (ports[n]) {
- std::string const p = ports[n];
-
- if (p.substr(0, strlen ("system:")) == "system:") {
- /* system: prefix */
- system.add (p);
- } else {
- if (p.substr(0, strlen("ardour:")) != "ardour:") {
- /* other (non-ardour) prefix */
- other.add (p);
- }
- }
-
- ++n;
- }
- }
-
- push_back (&buss);
- push_back (&track);
- push_back (&system);
- 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
+IOSelector::row_descriptor () 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 "";
+ 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),
+ _selector (session, io, !for_input),
ok_button (can_cancel ? _("OK"): _("Close")),
cancel_button (_("Cancel")),
rescan_button (_("Rescan"))
diff --git a/gtk2_ardour/io_selector.h b/gtk2_ardour/io_selector.h
index 92c3af9b48..92a112dc1d 100644
--- a/gtk2_ardour/io_selector.h
+++ b/gtk2_ardour/io_selector.h
@@ -20,153 +20,30 @@
#ifndef __ardour_ui_io_selector_h__
#define __ardour_ui_io_selector_h__
-#include <gtkmm/box.h>
-#include <gtkmm/checkbutton.h>
-#include <gtkmm/table.h>
-#include <gtkmm/frame.h>
-
#include "ardour_dialog.h"
+#include "port_matrix.h"
-namespace ARDOUR {
- class Session;
- class IO;
- class PortInsert;
-}
-
-/// A group of port names
-class PortGroup
-{
- public:
- PortGroup (std::string const & n, std::string const & p) : name (n), prefix (p), visible (true) {}
-
- void add (std::string const & p);
-
- std::string name;
- std::string prefix; ///< prefix (before colon) e.g. "ardour:"
- std::vector<std::string> ports; ///< port names
- bool visible;
-};
-
-/// A table of checkbuttons to provide the GUI for connecting to a PortGroup
-class PortGroupTable
-{
- public:
- PortGroupTable (PortGroup&, boost::shared_ptr<ARDOUR::IO>, bool);
-
- Gtk::Widget& get_widget ();
- std::pair<int, int> unit_size () const;
- PortGroup& port_group () { return _port_group; }
-
- private:
- void check_button_toggled (Gtk::CheckButton*, int, std::string const &);
-
- Gtk::Table _table;
- Gtk::EventBox _box;
- PortGroup& _port_group;
- std::vector<std::vector<Gtk::CheckButton* > > _check_buttons;
- bool _ignore_check_button_toggle;
- boost::shared_ptr<ARDOUR::IO> _io;
- bool _for_input;
-};
-
-/// A list of PortGroups
-class PortGroupList : public std::list<PortGroup*>
-{
- public:
- PortGroupList (ARDOUR::Session &, boost::shared_ptr<ARDOUR::IO>, bool);
-
- void refresh ();
- int n_visible_ports () const;
- std::string get_port_by_index (int, bool with_prefix = true) const;
-
- private:
- ARDOUR::Session& _session;
- boost::shared_ptr<ARDOUR::IO> _io;
- bool _for_input;
-
- PortGroup buss;
- PortGroup track;
- PortGroup system;
- PortGroup other;
-};
-
-
-/// A widget which provides a set of rotated text labels
-class RotatedLabelSet : public Gtk::Widget {
- public:
- RotatedLabelSet (PortGroupList&);
- virtual ~RotatedLabelSet ();
-
- void set_angle (int);
- void set_base_width (int);
- void update_visibility ();
-
- protected:
- virtual void on_size_request (Gtk::Requisition*);
- virtual void on_size_allocate (Gtk::Allocation&);
- virtual void on_realize ();
- virtual void on_unrealize ();
- virtual bool on_expose_event (GdkEventExpose*);
-
- Glib::RefPtr<Gdk::Window> _gdk_window;
-
- private:
- std::pair<int, int> setup_layout (std::string const &);
-
- PortGroupList& _port_group_list; ///< list of ports to display
- int _angle_degrees; ///< label rotation angle in degrees
- double _angle_radians; ///< label rotation angle in radians
- int _base_width; ///< width of labels; see set_base_width() for more details
- Glib::RefPtr<Pango::Context> _pango_context;
- Glib::RefPtr<Pango::Layout> _pango_layout;
- Glib::RefPtr<Gdk::GC> _gc;
- Gdk::Color _fg_colour;
- Gdk::Color _bg_colour;
-};
-
-
-
-/// Widget for selecting what an IO is connected to
-class IOSelector : public Gtk::VBox {
+class IOSelector : public PortMatrix {
public:
IOSelector (ARDOUR::Session&, boost::shared_ptr<ARDOUR::IO>, bool);
- ~IOSelector ();
-
- void redisplay ();
- enum Result {
- Cancelled,
- Accepted
- };
-
- sigc::signal<void, Result> Finished;
+ void set_state (int, std::string const &, bool);
+ 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:
- void setup ();
- void clear ();
- void setup_dimensions ();
+
void ports_changed (ARDOUR::IOChange, void*);
- bool row_label_button_pressed (GdkEventButton*, int);
- void add_port ();
- void remove_port (int);
- void group_visible_toggled (Gtk::CheckButton*, std::string const &);
- void set_port_group_table_visibility ();
-
- PortGroupList _port_group_list;
+
boost::shared_ptr<ARDOUR::IO> _io;
- bool _for_input;
- std::vector<PortGroupTable*> _port_group_tables;
- std::vector<Gtk::EventBox*> _row_labels[2];
- Gtk::VBox* _row_labels_vbox[2];
- RotatedLabelSet _column_labels;
- Gtk::HBox _overall_hbox;
- Gtk::VBox _side_vbox[2];
- Gtk::HBox _port_group_hbox;
- Gtk::ScrolledWindow _scrolled_window;
- Gtk::Label* _side_vbox_pad[2];
};
-
class IOSelectorWindow : public ArdourDialog
{
public:
diff --git a/gtk2_ardour/mixer_strip.cc b/gtk2_ardour/mixer_strip.cc
index f791314d75..62aefba27f 100644
--- a/gtk2_ardour/mixer_strip.cc
+++ b/gtk2_ardour/mixer_strip.cc
@@ -42,7 +42,8 @@
#include <ardour/send.h>
#include <ardour/processor.h>
#include <ardour/ladspa_plugin.h>
-#include <ardour/bundle.h>
+#include <ardour/auto_bundle.h>
+#include <ardour/user_bundle.h>
#include "ardour_ui.h"
#include "ardour_dialog.h"
@@ -540,6 +541,7 @@ MixerStrip::output_press (GdkEventButton *ev)
switch (ev->button) {
case 1:
+ {
output_menu.set_name ("ArdourContextMenu");
citems.clear();
@@ -547,13 +549,16 @@ MixerStrip::output_press (GdkEventButton *ev)
citems.push_back (SeparatorElem());
citems.push_back (MenuElem (_("Disconnect"), mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
citems.push_back (SeparatorElem());
-
- _session.foreach_bundle (
- bind (mem_fun (*this, &MixerStrip::add_bundle_to_output_menu), _route->output_bundle ())
- );
+
+ std::vector<boost::shared_ptr<Bundle> > current = _route->bundles_connected_to_outputs ();
+
+ _session.foreach_bundle (
+ bind (mem_fun (*this, &MixerStrip::add_bundle_to_output_menu), current)
+ );
output_menu.popup (1, ev->time);
break;
+ }
default:
break;
@@ -607,18 +612,21 @@ MixerStrip::input_press (GdkEventButton *ev)
switch (ev->button) {
case 1:
+ {
citems.push_back (MenuElem (_("Edit"), mem_fun(*this, &MixerStrip::edit_input_configuration)));
citems.push_back (SeparatorElem());
citems.push_back (MenuElem (_("Disconnect"), mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
citems.push_back (SeparatorElem());
-
+
+ std::vector<boost::shared_ptr<Bundle> > current = _route->bundles_connected_to_inputs ();
+
_session.foreach_bundle (
- bind (mem_fun (*this, &MixerStrip::add_bundle_to_input_menu), _route->input_bundle ())
+ bind (mem_fun (*this, &MixerStrip::add_bundle_to_input_menu), current)
);
input_menu.popup (1, ev->time);
break;
-
+ }
default:
break;
}
@@ -658,23 +666,23 @@ MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
}
void
-MixerStrip::add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, boost::shared_ptr<Bundle> current)
+MixerStrip::add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, std::vector<boost::shared_ptr<Bundle> > const & current)
{
using namespace Menu_Helpers;
/* the input menu needs to contain only output bundles (that we
can connect inputs to */
- if (boost::dynamic_pointer_cast<OutputBundle, Bundle> (b) == 0) {
- return;
- }
+ if (b->ports_are_outputs() == false) {
+ return;
+ }
MenuList& citems = input_menu.items();
if (b->nchannels() == _route->n_inputs().n_total()) {
citems.push_back (CheckMenuElem (b->name(), bind (mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
-
- if (current == b) {
+
+ if (std::find (current.begin(), current.end(), b) != current.end()) {
ignore_toggle = true;
dynamic_cast<CheckMenuItem *> (&citems.back())->set_active (true);
ignore_toggle = false;
@@ -683,23 +691,22 @@ MixerStrip::add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, boost::shared
}
void
-MixerStrip::add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, boost::shared_ptr<Bundle> current)
+MixerStrip::add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, std::vector<boost::shared_ptr<Bundle> > const & current)
{
using namespace Menu_Helpers;
/* the output menu needs to contain only input bundles (that we
can connect outputs to */
- if (boost::dynamic_pointer_cast<InputBundle, Bundle> (b) == 0) {
- return;
- }
-
+ if (b->ports_are_inputs() == false) {
+ return;
+ }
if (b->nchannels() == _route->n_outputs().n_total()) {
MenuList& citems = output_menu.items();
citems.push_back (CheckMenuElem (b->name(), bind (mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
- if (current == b) {
+ if (std::find (current.begin(), current.end(), b) != current.end()) {
ignore_toggle = true;
dynamic_cast<CheckMenuItem *> (&citems.back())->set_active (true);
ignore_toggle = false;
@@ -752,10 +759,11 @@ MixerStrip::connect_to_pan ()
void
MixerStrip::update_input_display ()
{
- boost::shared_ptr<ARDOUR::Bundle> c;
+ std::vector<boost::shared_ptr<ARDOUR::Bundle> > c = _route->bundles_connected_to_inputs ();
- if ((c = _route->input_bundle()) != 0) {
- input_label.set_text (c->name());
+ /* XXX: how do we represent >1 connected bundle? */
+ if (c.empty() == false) {
+ input_label.set_text (c[0]->name());
} else {
switch (_width) {
case Wide:
@@ -772,10 +780,11 @@ MixerStrip::update_input_display ()
void
MixerStrip::update_output_display ()
{
- boost::shared_ptr<ARDOUR::Bundle> c;
+ std::vector<boost::shared_ptr<ARDOUR::Bundle> > c = _route->bundles_connected_to_outputs ();
- if ((c = _route->output_bundle()) != 0) {
- output_label.set_text (c->name());
+ /* XXX: how do we represent >1 connected bundle? */
+ if (c.empty() == false) {
+ output_label.set_text (c[0]->name());
} else {
switch (_width) {
case Wide:
diff --git a/gtk2_ardour/mixer_strip.h b/gtk2_ardour/mixer_strip.h
index c3cf68a9ce..f134897fc5 100644
--- a/gtk2_ardour/mixer_strip.h
+++ b/gtk2_ardour/mixer_strip.h
@@ -168,10 +168,10 @@ class MixerStrip : public RouteUI, public Gtk::EventBox
gint output_press (GdkEventButton *);
Gtk::Menu input_menu;
- void add_bundle_to_input_menu (boost::shared_ptr<ARDOUR::Bundle>, boost::shared_ptr<ARDOUR::Bundle>);
+ void add_bundle_to_input_menu (boost::shared_ptr<ARDOUR::Bundle>, std::vector<boost::shared_ptr<ARDOUR::Bundle> > const &);
Gtk::Menu output_menu;
- void add_bundle_to_output_menu (boost::shared_ptr<ARDOUR::Bundle>, boost::shared_ptr<ARDOUR::Bundle>);
+ void add_bundle_to_output_menu (boost::shared_ptr<ARDOUR::Bundle>, std::vector<boost::shared_ptr<ARDOUR::Bundle> > const &);
void bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle>);
void bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle>);
diff --git a/gtk2_ardour/port_matrix.cc b/gtk2_ardour/port_matrix.cc
new file mode 100644
index 0000000000..19c0dc9d7f
--- /dev/null
+++ b/gtk2_ardour/port_matrix.cc
@@ -0,0 +1,730 @@
+/*
+ Copyright (C) 2002-2007 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ 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 <gtkmm/label.h>
+#include <gtkmm/enums.h>
+#include <gtkmm/image.h>
+#include <gtkmm/stock.h>
+#include <gtkmm/messagedialog.h>
+#include <gtkmm/menu.h>
+#include <gtkmm/menu_elems.h>
+#include <gtkmm/menuitem.h>
+#include <gtkmm/menushell.h>
+#include <glibmm/objectbase.h>
+#include <gtkmm2ext/doi.h>
+#include <ardour/port_insert.h>
+#include "ardour/session.h"
+#include "ardour/io.h"
+#include "ardour/audioengine.h"
+#include "ardour/track.h"
+#include "ardour/audio_track.h"
+#include "ardour/midi_track.h"
+#include "ardour/data_type.h"
+#include "io_selector.h"
+#include "utils.h"
+#include "gui_thread.h"
+#include "i18n.h"
+
+/** Add a port to a group.
+ * @param p Port name, with or without prefix.
+ */
+
+void
+PortGroup::add (std::string const & p)
+{
+ if (prefix.empty() == false && p.substr (0, prefix.length()) == prefix) {
+ ports.push_back (p.substr (prefix.length()));
+ } else {
+ ports.push_back (p);
+ }
+}
+
+/** PortGroupUI constructor.
+ * @param m PortMatrix to work for.
+ * @Param g PortGroup to represent.
+ */
+
+PortGroupUI::PortGroupUI (PortMatrix& m, PortGroup& g)
+ : _port_matrix (m), _port_group (g), _ignore_check_button_toggle (false),
+ _visibility_checkbutton (g.name)
+{
+ int const ports = _port_group.ports.size();
+ int const rows = _port_matrix.n_rows ();
+
+ if (rows == 0 || ports == 0) {
+ return;
+ }
+
+ /* Sort out the table and the checkbuttons inside it */
+
+ _table.resize (rows, ports);
+ _port_checkbuttons.resize (rows);
+ for (int i = 0; i < rows; ++i) {
+ _port_checkbuttons[i].resize (ports);
+ }
+
+ for (int i = 0; i < rows; ++i) {
+ for (uint32_t j = 0; j < _port_group.ports.size(); ++j) {
+ Gtk::CheckButton* b = new Gtk::CheckButton;
+
+ b->signal_toggled().connect (
+ sigc::bind (sigc::mem_fun (*this, &PortGroupUI::port_checkbutton_toggled), b, i, j)
+ );
+
+ _port_checkbuttons[i][j] = b;
+ _table.attach (*b, j, j + 1, i, i + 1);
+ }
+ }
+
+ _table_box.add (_table);
+
+ _ignore_check_button_toggle = true;
+
+ /* Set the state of the check boxes according to current connections */
+ for (int i = 0; i < rows; ++i) {
+ for (uint32_t j = 0; j < _port_group.ports.size(); ++j) {
+ std::string const t = _port_group.prefix + _port_group.ports[j];
+ bool const s = _port_matrix.get_state (i, t);
+ _port_checkbuttons[i][j]->set_active (s);
+ if (s) {
+ _port_group.visible = true;
+ }
+ }
+ }
+
+ _ignore_check_button_toggle = false;
+
+ _visibility_checkbutton.signal_toggled().connect (sigc::mem_fun (*this, &PortGroupUI::visibility_checkbutton_toggled));
+}
+
+/** The visibility of a PortGroupUI has been toggled */
+void
+PortGroupUI::visibility_checkbutton_toggled ()
+{
+ _port_group.visible = _visibility_checkbutton.get_active ();
+ setup_visibility ();
+}
+
+/** @return Width and height of a single checkbutton in a port group table */
+std::pair<int, int>
+PortGroupUI::unit_size () const
+{
+ if (_port_checkbuttons.empty() || _port_checkbuttons[0].empty())
+ {
+ return std::pair<int, int> (0, 0);
+ }
+
+ int r = 0;
+ /* We can't ask for row spacing unless there >1 rows, otherwise we get a warning */
+ if (_table.property_n_rows() > 1) {
+ r = _table.get_row_spacing (0);
+ }
+
+ return std::make_pair (
+ _port_checkbuttons[0][0]->get_width() + _table.get_col_spacing (0),
+ _port_checkbuttons[0][0]->get_height() + r
+ );
+}
+
+/** @return Table widget containing the port checkbuttons */
+Gtk::Widget&
+PortGroupUI::get_table ()
+{
+ return _table_box;
+}
+
+/** @return Checkbutton used to toggle visibility */
+Gtk::Widget&
+PortGroupUI::get_visibility_checkbutton ()
+{
+ return _visibility_checkbutton;
+}
+
+
+/** Handle a toggle of a port check button */
+void
+PortGroupUI::port_checkbutton_toggled (Gtk::CheckButton* b, int r, int c)
+{
+ if (_ignore_check_button_toggle == false) {
+ _port_matrix.set_state (r, _port_group.prefix + _port_group.ports[c], b->get_active());
+ }
+}
+
+/** Set up visibility of the port group according to PortGroup::visible */
+void
+PortGroupUI::setup_visibility ()
+{
+ if (_port_group.visible) {
+ _table_box.show ();
+ } else {
+ _table_box.hide ();
+ }
+
+ if (_visibility_checkbutton.get_active () != _port_group.visible) {
+ _visibility_checkbutton.set_active (_port_group.visible);
+ }
+}
+
+
+RotatedLabelSet::RotatedLabelSet (PortGroupList& g)
+ : Glib::ObjectBase ("RotatedLabelSet"), Gtk::Widget (), _port_group_list (g), _base_width (128)
+{
+ set_flags (Gtk::NO_WINDOW);
+ set_angle (30);
+}
+
+RotatedLabelSet::~RotatedLabelSet ()
+{
+
+}
+
+
+/** Set the angle that the labels are drawn at.
+ * @param degrees New angle in degrees.
+ */
+
+void
+RotatedLabelSet::set_angle (int degrees)
+{
+ _angle_degrees = degrees;
+ _angle_radians = M_PI * _angle_degrees / 180;
+
+ queue_resize ();
+}
+
+void
+RotatedLabelSet::on_size_request (Gtk::Requisition* requisition)
+{
+ *requisition = Gtk::Requisition ();
+
+ if (_pango_layout == 0) {
+ return;
+ }
+
+ /* Our height is the highest label */
+ requisition->height = 0;
+ for (PortGroupList::const_iterator i = _port_group_list.begin(); i != _port_group_list.end(); ++i) {
+ for (std::vector<std::string>::const_iterator j = (*i)->ports.begin(); j != (*i)->ports.end(); ++j) {
+ std::pair<int, int> const d = setup_layout (*j);
+ if (d.second > requisition->height) {
+ requisition->height = d.second;
+ }
+ }
+ }
+
+ /* And our width is the base plus the width of the last label */
+ requisition->width = _base_width;
+ int const n = _port_group_list.n_visible_ports ();
+ if (n > 0) {
+ std::pair<int, int> const d = setup_layout (_port_group_list.get_port_by_index (n - 1, false));
+ requisition->width += d.first;
+ }
+}
+
+void
+RotatedLabelSet::on_size_allocate (Gtk::Allocation& allocation)
+{
+ set_allocation (allocation);
+
+ if (_gdk_window) {
+ _gdk_window->move_resize (
+ allocation.get_x(), allocation.get_y(), allocation.get_width(), allocation.get_height()
+ );
+ }
+}
+
+void
+RotatedLabelSet::on_realize ()
+{
+ Gtk::Widget::on_realize ();
+
+ Glib::RefPtr<Gtk::Style> style = get_style ();
+
+ if (!_gdk_window) {
+ GdkWindowAttr attributes;
+ memset (&attributes, 0, sizeof (attributes));
+
+ Gtk::Allocation allocation = get_allocation ();
+ attributes.x = allocation.get_x ();
+ attributes.y = allocation.get_y ();
+ attributes.width = allocation.get_width ();
+ attributes.height = allocation.get_height ();
+
+ attributes.event_mask = get_events () | Gdk::EXPOSURE_MASK;
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.wclass = GDK_INPUT_OUTPUT;
+
+ _gdk_window = Gdk::Window::create (get_window (), &attributes, GDK_WA_X | GDK_WA_Y);
+ unset_flags (Gtk::NO_WINDOW);
+ set_window (_gdk_window);
+
+ _bg_colour = style->get_bg (Gtk::STATE_NORMAL );
+ modify_bg (Gtk::STATE_NORMAL, _bg_colour);
+ _fg_colour = style->get_fg (Gtk::STATE_NORMAL);
+;
+ _gdk_window->set_user_data (gobj ());
+
+ /* Set up Pango stuff */
+ _pango_context = create_pango_context ();
+
+ Pango::Matrix matrix = PANGO_MATRIX_INIT;
+ pango_matrix_rotate (&matrix, _angle_degrees);
+ _pango_context->set_matrix (matrix);
+
+ _pango_layout = Pango::Layout::create (_pango_context);
+ _gc = Gdk::GC::create (get_window ());
+ }
+}
+
+void
+RotatedLabelSet::on_unrealize()
+{
+ _gdk_window.clear ();
+
+ Gtk::Widget::on_unrealize ();
+}
+
+
+/** Set up our Pango layout to plot a given string, and compute its dimensions once
+ * it has been rotated.
+ * @param s String to use.
+ * @return width and height of the rotated string, in pixels.
+ */
+
+std::pair<int, int>
+RotatedLabelSet::setup_layout (std::string const & s)
+{
+ _pango_layout->set_text (s);
+
+ /* Here's the unrotated size */
+ int w;
+ int h;
+ _pango_layout->get_pixel_size (w, h);
+
+ /* Rotate the width and height as appropriate. I thought Pango might be able
+ to do this for us, but I can't find out how... */
+ std::pair<int, int> d;
+ d.first = int (w * cos (_angle_radians) - h * sin (_angle_radians));
+ d.second = int (w * sin (_angle_radians) + h * cos (_angle_radians));
+
+ return d;
+}
+
+bool
+RotatedLabelSet::on_expose_event (GdkEventExpose* event)
+{
+ if (!_gdk_window) {
+ return true;
+ }
+
+ int const height = get_allocation().get_height ();
+ double const spacing = double (_base_width) / _port_group_list.n_visible_ports();
+
+ /* Plot all the visible labels; really we should clip for efficiency */
+ int n = 0;
+ for (PortGroupList::const_iterator i = _port_group_list.begin(); i != _port_group_list.end(); ++i) {
+ if ((*i)->visible) {
+ for (uint32_t j = 0; j < (*i)->ports.size(); ++j) {
+ std::pair<int, int> const d = setup_layout ((*i)->ports[j]);
+ get_window()->draw_layout (_gc, int ((n + 0.25) * spacing), height - d.second, _pango_layout, _fg_colour, _bg_colour);
+ ++n;
+ }
+ }
+ }
+
+ return true;
+}
+
+/** Set the `base width'. This is the width of the base of the label set, ie:
+ *
+ * L L L L
+ * E E E E
+ * B B B B
+ * A A A A
+ * L L L L
+ * <--w-->
+ */
+
+void
+RotatedLabelSet::set_base_width (int w)
+{
+ _base_width = w;
+ queue_resize ();
+}
+
+
+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),
+ _column_labels (_port_group_list)
+{
+ _row_labels_vbox[0] = _row_labels_vbox[1] = 0;
+ _side_vbox_pad[0] = _side_vbox_pad[1] = 0;
+
+ pack_start (_visibility_checkbutton_box, false, false);
+
+ _side_vbox[0].pack_start (*Gtk::manage (new Gtk::Label ("")));
+ _overall_hbox.pack_start (_side_vbox[0], false, false);
+ _scrolled_window.set_policy (Gtk::POLICY_ALWAYS, Gtk::POLICY_NEVER);
+ _scrolled_window.set_shadow_type (Gtk::SHADOW_NONE);
+ Gtk::VBox* b = new Gtk::VBox;
+ b->pack_start (_column_labels, false, false);
+ b->pack_start (_port_group_hbox, false, false);
+ Gtk::Alignment* a = new Gtk::Alignment (0, 1, 0, 0);
+ a->add (*Gtk::manage (b));
+ _scrolled_window.add (*Gtk::manage (a));
+ _overall_hbox.pack_start (_scrolled_window);
+ _side_vbox[1].pack_start (*Gtk::manage (new Gtk::Label ("")));
+ _overall_hbox.pack_start (_side_vbox[1]);
+ pack_start (_overall_hbox);
+
+ _port_group_hbox.signal_size_allocate().connect (sigc::hide (sigc::mem_fun (*this, &IOSelector::setup_dimensions)));
+}
+
+PortMatrix::~PortMatrix ()
+{
+ clear ();
+}
+
+/** Clear out the things that change when the number of source or destination ports changes */
+void
+PortMatrix::clear ()
+{
+ for (int i = 0; i < 2; ++i) {
+
+ for (std::vector<Gtk::EventBox*>::iterator j = _row_labels[i].begin(); j != _row_labels[i].end(); ++j) {
+ delete *j;
+ }
+ _row_labels[i].clear ();
+
+ if (_row_labels_vbox[i]) {
+ _side_vbox[i].remove (*_row_labels_vbox[i]);
+ }
+ delete _row_labels_vbox[i];
+ _row_labels_vbox[i] = 0;
+
+ if (_side_vbox_pad[i]) {
+ _side_vbox[i].remove (*_side_vbox_pad[i]);
+ }
+ delete _side_vbox_pad[i];
+ _side_vbox_pad[i] = 0;
+ }
+
+ for (std::vector<PortGroupUI*>::iterator i = _port_group_ui.begin(); i != _port_group_ui.end(); ++i) {
+ _port_group_hbox.remove ((*i)->get_table());
+ _visibility_checkbutton_box.remove ((*i)->get_visibility_checkbutton());
+ delete *i;
+ }
+
+ _port_group_ui.clear ();
+}
+
+
+/** Set up dimensions of some of our widgets which depend on other dimensions
+ * within the dialogue.
+ */
+void
+PortMatrix::setup_dimensions ()
+{
+ /* Get some dimensions from various places */
+ int const scrollbar_height = _scrolled_window.get_hscrollbar()->get_height();
+
+ std::pair<int, int> unit_size (0, 0);
+ int port_group_tables_height = 0;
+ for (std::vector<PortGroupUI*>::iterator i = _port_group_ui.begin(); i != _port_group_ui.end(); ++i) {
+ std::pair<int, int> const u = (*i)->unit_size ();
+ unit_size.first = std::max (unit_size.first, u.first);
+ unit_size.second = std::max (unit_size.second, u.second);
+ port_group_tables_height = std::max (
+ port_group_tables_height, (*i)->get_table().get_height()
+ );
+ }
+
+ /* Column labels */
+ _column_labels.set_base_width (_port_group_list.n_visible_ports () * unit_size.first);
+
+ /* Scrolled window */
+ /* XXX: really shouldn't set a minimum horizontal size here, but if we don't
+ the window starts up very small */
+ _scrolled_window.set_size_request (
+ std::min (_column_labels.get_width(), 640),
+ _column_labels.get_height() + port_group_tables_height + scrollbar_height + 16
+ );
+
+ /* Row labels */
+ for (int i = 0; i < 2; ++i) {
+ for (std::vector<Gtk::EventBox*>::iterator j = _row_labels[i].begin(); j != _row_labels[i].end(); ++j) {
+ (*j)->get_child()->set_size_request (-1, unit_size.second);
+ }
+
+ if (_side_vbox_pad[i]) {
+ _side_vbox_pad[i]->set_size_request (-1, scrollbar_height + unit_size.second / 4);
+ }
+ }
+}
+
+
+/** Set up the dialogue */
+void
+PortMatrix::setup ()
+{
+ clear ();
+
+ int const rows = n_rows ();
+
+ /* Row labels */
+ for (int i = 0; i < 2; ++i) {
+ _row_labels_vbox[i] = new Gtk::VBox;
+ int const run_rows = std::max (1, rows);
+ for (int j = 0; j < run_rows; ++j) {
+ Gtk::Label* label = new Gtk::Label (rows == 0 ? "Quim" : row_name (j));
+ Gtk::EventBox* b = new Gtk::EventBox;
+ b->set_events (Gdk::BUTTON_PRESS_MASK);
+ b->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &IOSelector::row_label_button_pressed), j));
+ b->add (*Gtk::manage (label));
+ _row_labels[i].push_back (b);
+ _row_labels_vbox[i]->pack_start (*b, false, false);
+ }
+
+ _side_vbox[i].pack_start (*_row_labels_vbox[i], false, false);
+ _side_vbox_pad[i] = new Gtk::Label ("");
+ _side_vbox[i].pack_start (*_side_vbox_pad[i], false, false);
+ }
+
+ /* Checkbutton tables and visibility checkbuttons */
+ int n = 0;
+ for (PortGroupList::iterator i = _port_group_list.begin(); i != _port_group_list.end(); ++i) {
+ PortGroupUI* t = new PortGroupUI (*this, **i);
+
+ /* XXX: this is a bit of a hack; should probably use a configurable colour here */
+ Gdk::Color alt_bg = get_style()->get_bg (Gtk::STATE_NORMAL);
+ alt_bg.set_rgb (alt_bg.get_red() + 4096, alt_bg.get_green() + 4096, alt_bg.get_blue () + 4096);
+ if ((n % 2) == 0) {
+ t->get_table().modify_bg (Gtk::STATE_NORMAL, alt_bg);
+ }
+
+ _port_group_ui.push_back (t);
+ _port_group_hbox.pack_start (t->get_table(), false, false);
+
+ _visibility_checkbutton_box.pack_start (t->get_visibility_checkbutton(), false, false);
+ ++n;
+ }
+
+ show_all ();
+
+ for (std::vector<PortGroupUI*>::iterator i = _port_group_ui.begin(); i != _port_group_ui.end(); ++i) {
+ (*i)->setup_visibility ();
+ }
+
+}
+
+void
+PortMatrix::redisplay ()
+{
+ _port_group_list.refresh ();
+ setup ();
+}
+
+
+/** Handle a button press on a row label */
+bool
+PortMatrix::row_label_button_pressed (GdkEventButton* e, int r)
+{
+ if (e->type != GDK_BUTTON_PRESS || e->button != 3) {
+ return false;
+ }
+
+ Gtk::Menu* menu = Gtk::manage (new Gtk::Menu);
+ Gtk::Menu_Helpers::MenuList& items = menu->items ();
+ menu->set_name ("ArdourContextMenu");
+
+ bool const can_add = maximum_rows () > n_rows ();
+ bool const can_remove = minimum_rows () < n_rows ();
+ std::string const name = row_name (r);
+
+ items.push_back (
+ Gtk::Menu_Helpers::MenuElem (string_compose(_("Add %1"), row_descriptor()), sigc::mem_fun (*this, &PortMatrix::add_row))
+ );
+
+ items.back().set_sensitive (can_add);
+
+ items.push_back (
+ Gtk::Menu_Helpers::MenuElem (string_compose(_("Remove %1 \"%2\""), row_descriptor(), name), sigc::bind (sigc::mem_fun (*this, &PortMatrix::remove_row), r))
+ );
+
+ items.back().set_sensitive (can_remove);
+
+ menu->popup (e->button, e->time);
+
+ return true;
+}
+
+void
+PortMatrix::set_type (ARDOUR::DataType t)
+{
+ _type = t;
+ _port_group_list.set_type (t);
+ redisplay ();
+}
+
+void
+PortMatrix::set_offer_inputs (bool i)
+{
+ _offer_inputs = i;
+ _port_group_list.set_offer_inputs (i);
+ redisplay ();
+}
+
+/** 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 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 (_("Buss"), "ardour:", mask & BUSS),
+ track (_("Track"), "ardour:", mask & TRACK),
+ system (_("System"), "system:", mask & SYSTEM),
+ other (_("Other"), "", mask & OTHER)
+{
+ refresh ();
+}
+
+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. */
+
+ boost::shared_ptr<ARDOUR::Session::RouteList> routes = _session.get_routes ();
+
+ for (ARDOUR::Session::RouteList::const_iterator i = routes->begin(); i != routes->end(); ++i) {
+
+ PortGroup* g = 0;
+ if (_type == ARDOUR::DataType::AUDIO && dynamic_cast<ARDOUR::AudioTrack*> ((*i).get())) {
+ /* Audio track for an audio IO */
+ g = &track;
+ } else if (_type == ARDOUR::DataType::MIDI && dynamic_cast<ARDOUR::MidiTrack*> ((*i).get())) {
+ /* Midi track for a MIDI IO */
+ g = &track;
+ } else if (_type == ARDOUR::DataType::AUDIO && dynamic_cast<ARDOUR::MidiTrack*> ((*i).get()) == 0) {
+ /* Non-MIDI track for an Audio IO; must be an audio buss */
+ g = &buss;
+ }
+
+ if (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());
+ }
+ }
+
+
+ /* XXX: inserts, sends, plugin inserts? */
+
+ /* Now we need to find the non-ardour ports; we do this by first
+ finding all the ports that we can connect to. */
+ const char **ports = _session.engine().get_ports (
+ "", _type.to_jack_type(), _offer_inputs ? JackPortIsInput : JackPortIsOutput
+ );
+
+ if (ports) {
+
+ int n = 0;
+ while (ports[n]) {
+ std::string const p = ports[n];
+
+ if (p.substr(0, strlen ("system:")) == "system:") {
+ /* system: prefix */
+ system.add (p);
+ } else {
+ if (p.substr(0, strlen("ardour:")) != "ardour:") {
+ /* other (non-ardour) prefix */
+ other.add (p);
+ }
+ }
+
+ ++n;
+ }
+ }
+
+ push_back (&buss);
+ push_back (&track);
+ push_back (&system);
+ 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)
+{
+ _type = t;
+}
+
+void
+PortGroupList::set_offer_inputs (bool i)
+{
+ _offer_inputs = i;
+}
+
diff --git a/gtk2_ardour/port_matrix.h b/gtk2_ardour/port_matrix.h
new file mode 100644
index 0000000000..a945d00496
--- /dev/null
+++ b/gtk2_ardour/port_matrix.h
@@ -0,0 +1,200 @@
+/*
+ Copyright (C) 2002-2007 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ 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 __ardour_ui_port_matrix_h__
+#define __ardour_ui_port_matrix_h__
+
+#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"
+
+namespace ARDOUR {
+ class Session;
+ class IO;
+ class PortInsert;
+}
+
+class PortMatrix;
+
+/// A list of port names, grouped by some aspect of their type e.g. busses, tracks, system
+class PortGroup
+{
+ public:
+ /** PortGroup constructor.
+ * @param n Name.
+ * @param p Port name prefix.
+ * @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) {}
+
+ void add (std::string const & p);
+
+ std::string name; ///< name for the group
+ std::string prefix; ///< prefix (before colon) e.g. "ardour:"
+ std::vector<std::string> ports; ///< port names
+ bool visible; ///< true if the group is visible in the UI
+};
+
+/// The UI for a PortGroup
+class PortGroupUI
+{
+ public:
+ PortGroupUI (PortMatrix&, PortGroup&);
+
+ Gtk::Widget& get_table ();
+ Gtk::Widget& get_visibility_checkbutton ();
+ std::pair<int, int> unit_size () const;
+ PortGroup& port_group () { return _port_group; }
+ void setup_visibility ();
+
+ private:
+ void port_checkbutton_toggled (Gtk::CheckButton*, int, int);
+ void visibility_checkbutton_toggled ();
+
+ PortMatrix& _port_matrix; ///< the PortMatrix that we are working for
+ PortGroup& _port_group; ///< the PortGroup that we are representing
+ bool _ignore_check_button_toggle;
+ Gtk::Table _table;
+ Gtk::EventBox _table_box;
+ std::vector<std::vector<Gtk::CheckButton* > > _port_checkbuttons;
+ Gtk::CheckButton _visibility_checkbutton;
+};
+
+/// A list of PortGroups
+class PortGroupList : public std::list<PortGroup*>
+{
+ public:
+ enum Mask {
+ BUSS = 0x1,
+ TRACK = 0x2,
+ SYSTEM = 0x4,
+ OTHER = 0x8
+ };
+
+ 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:
+ ARDOUR::Session& _session;
+ ARDOUR::DataType _type;
+ bool _offer_inputs;
+
+ PortGroup buss;
+ PortGroup track;
+ PortGroup system;
+ PortGroup other;
+};
+
+
+/// A widget which provides a set of rotated text labels
+class RotatedLabelSet : public Gtk::Widget {
+ public:
+ RotatedLabelSet (PortGroupList&);
+ virtual ~RotatedLabelSet ();
+
+ void set_angle (int);
+ void set_base_width (int);
+ void update_visibility ();
+
+ protected:
+ virtual void on_size_request (Gtk::Requisition*);
+ virtual void on_size_allocate (Gtk::Allocation&);
+ virtual void on_realize ();
+ virtual void on_unrealize ();
+ virtual bool on_expose_event (GdkEventExpose*);
+
+ Glib::RefPtr<Gdk::Window> _gdk_window;
+
+ private:
+ std::pair<int, int> setup_layout (std::string const &);
+
+ PortGroupList& _port_group_list; ///< list of ports to display
+ int _angle_degrees; ///< label rotation angle in degrees
+ double _angle_radians; ///< label rotation angle in radians
+ int _base_width; ///< width of labels; see set_base_width() for more details
+ Glib::RefPtr<Pango::Context> _pango_context;
+ Glib::RefPtr<Pango::Layout> _pango_layout;
+ Glib::RefPtr<Gdk::GC> _gc;
+ Gdk::Color _fg_colour;
+ Gdk::Color _bg_colour;
+};
+
+
+class PortMatrix : public Gtk::VBox {
+ public:
+ PortMatrix (ARDOUR::Session&, ARDOUR::DataType, bool, PortGroupList::Mask);
+ ~PortMatrix ();
+
+ void redisplay ();
+
+ enum Result {
+ Cancelled,
+ Accepted
+ };
+
+ sigc::signal<void, Result> Finished;
+
+ void set_type (ARDOUR::DataType);
+ void set_offer_inputs (bool);
+
+ virtual void set_state (int, std::string const &, bool) = 0;
+ virtual bool get_state (int, std::string const &) const = 0;
+ 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;
+
+ protected:
+
+ bool _offer_inputs;
+
+ private:
+ void setup ();
+ void clear ();
+ void setup_dimensions ();
+ bool row_label_button_pressed (GdkEventButton*, int);
+
+ PortGroupList _port_group_list;
+ ARDOUR::DataType _type;
+ std::vector<PortGroupUI*> _port_group_ui;
+ std::vector<Gtk::EventBox*> _row_labels[2];
+ Gtk::VBox* _row_labels_vbox[2];
+ RotatedLabelSet _column_labels;
+ Gtk::HBox _overall_hbox;
+ Gtk::VBox _side_vbox[2];
+ Gtk::HBox _port_group_hbox;
+ Gtk::ScrolledWindow _scrolled_window;
+ Gtk::Label* _side_vbox_pad[2];
+ Gtk::HBox _visibility_checkbutton_box;
+};
+
+#endif
diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript
index 4d68dc7725..af08d4569c 100644
--- a/libs/ardour/SConscript
+++ b/libs/ardour/SConscript
@@ -12,7 +12,7 @@ ardour = env.Copy()
# this defines the version number of libardour
#
-domain = 'libardour2'
+domain = 'libardour'
ardour.Append(DOMAIN = domain, MAJOR = 2, MINOR = 0, MICRO = 0)
ardour.Append(CXXFLAGS = "-DPACKAGE=\\\"" + domain + "\\\"")
@@ -29,6 +29,8 @@ ardour.Append(CPPPATH = '#libs/surfaces/control_protocol')
ardour_files=Split("""
amp.cc
audio_buffer.cc
+auto_bundle.cc
+user_bundle.cc
audio_diskstream.cc
audio_library.cc
audio_playlist.cc
@@ -45,7 +47,6 @@ automation_control.cc
automation_event.cc
buffer.cc
buffer_set.cc
-bundle.cc
chan_count.cc
configuration.cc
control_protocol_manager.cc
diff --git a/libs/ardour/ardour/audioengine.h b/libs/ardour/ardour/audioengine.h
index 34f7eb8c22..503b8166f0 100644
--- a/libs/ardour/ardour/audioengine.h
+++ b/libs/ardour/ardour/audioengine.h
@@ -144,7 +144,7 @@ class AudioEngine : public sigc::trackable
/** Caller may not delete the object pointed to by the return value
*/
- Port *get_port_by_name (const std::string& name, bool keep = true);
+ Port *get_port_by_name (const std::string& name, bool keep = true) const;
enum TransportState {
TransportStopped = JackTransportStopped,
@@ -199,7 +199,7 @@ class AudioEngine : public sigc::trackable
ARDOUR::Session *session;
jack_client_t *_jack;
std::string jack_client_name;
- Glib::Mutex _process_lock;
+ mutable Glib::Mutex _process_lock;
Glib::Cond session_removed;
bool session_remove_pending;
bool _running;
diff --git a/libs/ardour/ardour/auto_bundle.h b/libs/ardour/ardour/auto_bundle.h
new file mode 100644
index 0000000000..685a083e8d
--- /dev/null
+++ b/libs/ardour/ardour/auto_bundle.h
@@ -0,0 +1,50 @@
+/*
+ Copyright (C) 2007 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ 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 __ardour_auto_bundle_h__
+#define __ardour_auto_bundle_h__
+
+#include <vector>
+#include <glibmm/thread.h>
+#include "ardour/bundle.h"
+
+namespace ARDOUR {
+
+class AutoBundle : public Bundle {
+
+ public:
+ AutoBundle (bool i = true);
+ AutoBundle (std::string const &, bool i = true);
+
+ uint32_t nchannels () const;
+ const PortList& channel_ports (uint32_t) const;
+
+ void set_channels (uint32_t);
+ void set_port (uint32_t, std::string const &);
+
+ private:
+ /// mutex for _ports;
+ /// XXX: is this necessary?
+ mutable Glib::Mutex _ports_mutex;
+ std::vector<PortList> _ports;
+};
+
+}
+
+#endif /* __ardour_auto_bundle_h__ */
diff --git a/libs/ardour/ardour/bundle.h b/libs/ardour/ardour/bundle.h
index 9c5f3cb21a..ba92063b30 100644
--- a/libs/ardour/ardour/bundle.h
+++ b/libs/ardour/ardour/bundle.h
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2002 Paul Davis
+ Copyright (C) 2002-2007 Paul Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,118 +20,54 @@
#ifndef __ardour_bundle_h__
#define __ardour_bundle_h__
-#include <vector>
#include <string>
#include <sigc++/signal.h>
-#include <glibmm/thread.h>
-#include <pbd/stateful.h>
-
-using std::vector;
-using std::string;
+#include "ardour/data_type.h"
namespace ARDOUR {
+typedef std::vector<std::string> PortList;
+
/**
- * A set of `channels', each of which is associated with 0 or more
- * JACK ports.
+ * A set of `channels', each of which is associated with 0 or more JACK ports.
*/
-class Bundle : public PBD::Stateful, public sigc::trackable {
+class Bundle {
public:
- /**
- * Bundle constructor.
- * @param name Name for this Bundle.
- * @param dy true if this Bundle is `dynamic', ie it is created on-the-fly
- * and should not be written to the session file.
- */
- Bundle (string name, bool dy = false) : _name (name), _dynamic(dy) {}
- ~Bundle() {}
-
- /// A vector of JACK port names
- typedef vector<string> PortList;
-
- void set_name (string name, void *src);
-
- /**
- * @return name of this Bundle.
- */
- string name() const { return _name; }
-
- /**
- * @return true if this Bundle is marked as `dynamic', meaning
- * that it won't be written to the session file.
- */
- bool dynamic() const { return _dynamic; }
+ Bundle () : _type (DataType::AUDIO) {}
+ Bundle (bool i) : _type (DataType::AUDIO), _ports_are_inputs (i) {}
+ Bundle (std::string const & n, bool i = true) : _name (n), _type (DataType::AUDIO), _ports_are_inputs (i) {}
+ virtual ~Bundle() {}
/**
* @return Number of channels that this Bundle has.
*/
- uint32_t nchannels () const { return _channels.size(); }
- const PortList& channel_ports (int ch) const;
+ virtual uint32_t nchannels () const = 0;
+ virtual const PortList& channel_ports (uint32_t) const = 0;
- void set_nchannels (int n);
+ void set_name (std::string const & n) {
+ _name = n;
+ NameChanged ();
+ }
+
+ std::string name () const { return _name; }
- void add_port_to_channel (int ch, string portname);
- void remove_port_from_channel (int ch, string portname);
+ sigc::signal<void> NameChanged;
- /// Our name changed
- sigc::signal<void, void*> NameChanged;
- /// The number of channels changed
- sigc::signal<void> ConfigurationChanged;
- /// The ports associated with one of our channels changed
- sigc::signal<void, int> PortsChanged;
+ void set_type (DataType t) { _type = t; }
+ DataType type () const { return _type; }
- bool operator==(const Bundle& other) const;
-
- XMLNode& get_state (void);
- int set_state (const XMLNode&);
-
- protected:
- Bundle (const XMLNode&);
+ void set_ports_are_inputs () { _ports_are_inputs = true; }
+ void set_ports_are_outputs () { _ports_are_inputs = false; }
+ bool ports_are_inputs () const { return _ports_are_inputs; }
+ bool ports_are_outputs () const { return !_ports_are_inputs; }
private:
- mutable Glib::Mutex channels_lock; ///< mutex for _channels
- vector<PortList> _channels; ///< list of JACK ports associated with each of our channels
- string _name; ///< name
- bool _dynamic; ///< true if `dynamic', ie not to be written to the session file
-
- int set_channels (const string& str);
- int parse_io_string (const string& str, vector<string>& ports);
-};
-
-/**
- * Bundle in which the JACK ports are inputs.
- */
-
-class InputBundle : public Bundle {
- public:
- /**
- * InputBundle constructor.
- * \param name Name.
- * \param dy true if this Bundle is `dynamic'; ie it is created on-the-fly
- * and should not be written to the session file.
- */
- InputBundle (string name, bool dy = false) : Bundle (name, dy) {}
- InputBundle (const XMLNode&);
-};
-
-/**
- * Bundle in which the JACK ports are outputs.
- */
-
-class OutputBundle : public Bundle {
- public:
- /**
- * OutputBundle constructor.
- * \param name Name.
- * \param dy true if this Bundle is `dynamic'; ie it is created on-the-fly
- * and should not be written to the session file.
- */
- OutputBundle (string name, bool dy = false) : Bundle (name, dy) {}
- OutputBundle (const XMLNode&);
+ std::string _name;
+ ARDOUR::DataType _type;
+ bool _ports_are_inputs;
};
}
#endif /* __ardour_bundle_h__ */
-
diff --git a/libs/ardour/ardour/data_type.h b/libs/ardour/ardour/data_type.h
index 68d9554904..854f52acba 100644
--- a/libs/ardour/ardour/data_type.h
+++ b/libs/ardour/ardour/data_type.h
@@ -21,7 +21,6 @@
#define __ardour_data_type_h__
#include <string>
-#include <ardour/data_type.h>
#include <jack/jack.h>
namespace ARDOUR {
diff --git a/libs/ardour/ardour/io.h b/libs/ardour/ardour/io.h
index 6e68c01d8c..888b770250 100644
--- a/libs/ardour/ardour/io.h
+++ b/libs/ardour/ardour/io.h
@@ -43,6 +43,7 @@
#include <ardour/chan_count.h>
#include <ardour/latent.h>
#include <ardour/automation_control.h>
+#include <ardour/user_bundle.h>
using std::string;
using std::vector;
@@ -54,6 +55,7 @@ namespace ARDOUR {
class Session;
class AudioEngine;
class Bundle;
+class AutoBundle;
class Panner;
class PeakMeter;
class Port;
@@ -123,9 +125,12 @@ class IO : public Automatable, public Latent
int connect_input_ports_to_bundle (boost::shared_ptr<Bundle>, void *src);
int connect_output_ports_to_bundle (boost::shared_ptr<Bundle>, void *src);
- boost::shared_ptr<Bundle> input_bundle();
- boost::shared_ptr<Bundle> output_bundle();
+ std::vector<boost::shared_ptr<Bundle> > bundles_connected_to_inputs ();
+ std::vector<boost::shared_ptr<Bundle> > bundles_connected_to_outputs ();
+ boost::shared_ptr<AutoBundle> bundle_for_inputs () { return _bundle_for_inputs; }
+ boost::shared_ptr<AutoBundle> bundle_for_outputs () { return _bundle_for_outputs; }
+
int add_input_port (string source, void *src, DataType type = DataType::NIL);
int add_output_port (string destination, void *src, DataType type = DataType::NIL);
@@ -179,9 +184,6 @@ class IO : public Automatable, public Latent
void attach_buffers(ChanCount ignored);
- boost::shared_ptr<Bundle> bundle_for_inputs () const { return _bundle_for_inputs; }
- boost::shared_ptr<Bundle> bundle_for_outputs () const { return _bundle_for_outputs; }
-
sigc::signal<void,IOChange,void*> input_changed;
sigc::signal<void,IOChange,void*> output_changed;
@@ -272,8 +274,6 @@ class IO : public Automatable, public Latent
PortSet _outputs;
PortSet _inputs;
PeakMeter* _meter;
- boost::shared_ptr<Bundle> _input_bundle; ///< bundle connected to our inputs
- boost::shared_ptr<Bundle> _output_bundle; ///< bundle connected to our outputs
bool no_panner_reset;
bool _phase_invert;
bool _denormal_protection;
@@ -310,13 +310,6 @@ class IO : public Automatable, public Latent
friend class Send;
- /* are these the best variable names ever, or what? */
-
- sigc::connection input_bundle_configuration_connection;
- sigc::connection output_bundle_configuration_connection;
- sigc::connection input_bundle_connection_connection;
- sigc::connection output_bundle_connection_connection;
-
static bool panners_legal;
int connecting_became_legal ();
@@ -330,8 +323,21 @@ class IO : public Automatable, public Latent
ChanCount _output_minimum; ///< minimum number of output channels (0 for no minimum)
ChanCount _output_maximum; ///< maximum number of output channels (ChanCount::INFINITE for no maximum)
- boost::shared_ptr<Bundle> _bundle_for_inputs;
- boost::shared_ptr<Bundle> _bundle_for_outputs;
+ boost::shared_ptr<AutoBundle> _bundle_for_inputs; ///< a bundle representing our inputs
+ boost::shared_ptr<AutoBundle> _bundle_for_outputs; ///< a bundle representing our outputs
+
+ struct UserBundleInfo {
+ UserBundleInfo (IO*, boost::shared_ptr<UserBundle> b);
+
+ boost::shared_ptr<UserBundle> bundle;
+ sigc::connection configuration_will_change;
+ sigc::connection configuration_has_changed;
+ sigc::connection ports_will_change;
+ sigc::connection ports_have_changed;
+ };
+
+ std::vector<UserBundleInfo> _bundles_connected_to_outputs; ///< user bundles connected to our outputs
+ std::vector<UserBundleInfo> _bundles_connected_to_inputs; ///< user bundles connected to our inputs
static int parse_io_string (const string&, vector<string>& chns);
@@ -343,13 +349,14 @@ class IO : public Automatable, public Latent
int ensure_inputs (ChanCount, bool clear, bool lockit, void *src);
int ensure_outputs (ChanCount, bool clear, bool lockit, void *src);
- void drop_input_bundle ();
- void drop_output_bundle ();
+ void check_bundles_connected_to_inputs ();
+ void check_bundles_connected_to_outputs ();
+ void check_bundles (std::vector<UserBundleInfo>&, const PortSet&);
- void input_bundle_configuration_changed ();
- void input_bundle_connection_changed (int);
- void output_bundle_configuration_changed ();
- void output_bundle_connection_changed (int);
+ void bundle_configuration_will_change ();
+ void bundle_configuration_has_changed ();
+ void bundle_ports_will_change (int);
+ void bundle_ports_have_changed (int);
int create_ports (const XMLNode&);
int make_connections (const XMLNode&);
@@ -363,8 +370,11 @@ class IO : public Automatable, public Latent
int32_t find_input_port_hole ();
int32_t find_output_port_hole ();
- void create_bundles ();
- void setup_bundles ();
+ void create_bundles_for_inputs_and_outputs ();
+ void setup_bundles_for_inputs_and_outputs ();
+
+ void maybe_add_input_bundle_to_list (boost::shared_ptr<Bundle>, std::vector<boost::shared_ptr<Bundle> >*);
+ void maybe_add_output_bundle_to_list (boost::shared_ptr<Bundle>, std::vector<boost::shared_ptr<Bundle> >*);
};
} // namespace ARDOUR
diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h
index bbcae6e91d..e153914a09 100644
--- a/libs/ardour/ardour/session.h
+++ b/libs/ardour/ardour/session.h
@@ -489,7 +489,8 @@ class Session : public PBD::StatefulDestructible
void set_remote_control_ids();
- AudioEngine &engine() { return _engine; };
+ AudioEngine & engine() { return _engine; }
+ AudioEngine const & engine () const { return _engine; }
int32_t max_level;
int32_t min_level;
@@ -716,7 +717,6 @@ class Session : public PBD::StatefulDestructible
void add_bundle (boost::shared_ptr<Bundle>);
void remove_bundle (boost::shared_ptr<Bundle>);
boost::shared_ptr<Bundle> bundle_by_name (string) const;
- boost::shared_ptr<Bundle> bundle_by_ports (vector<string> const &) const;
sigc::signal<void,boost::shared_ptr<Bundle> > BundleAdded;
sigc::signal<void,boost::shared_ptr<Bundle> > BundleRemoved;
@@ -1564,7 +1564,8 @@ class Session : public PBD::StatefulDestructible
typedef list<boost::shared_ptr<Bundle> > BundleList;
mutable Glib::Mutex bundle_lock;
BundleList _bundles;
- int load_bundles (const XMLNode&);
+ XMLNode* _bundle_xml_node;
+ int load_bundles (XMLNode const &);
void reverse_diskstream_buffers ();
diff --git a/libs/ardour/ardour/user_bundle.h b/libs/ardour/ardour/user_bundle.h
new file mode 100644
index 0000000000..954e93d5d1
--- /dev/null
+++ b/libs/ardour/ardour/user_bundle.h
@@ -0,0 +1,72 @@
+/*
+ Copyright (C) 2007 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ 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 __ardour_user_bundle_h__
+#define __ardour_user_bundle_h__
+
+#include <vector>
+#include <glibmm/thread.h>
+#include "pbd/stateful.h"
+#include "ardour/bundle.h"
+
+namespace ARDOUR {
+
+class Session;
+
+class UserBundle : public Bundle, public PBD::Stateful {
+
+ public:
+ UserBundle (std::string const &);
+ UserBundle (XMLNode const &, bool);
+
+ uint32_t nchannels () const;
+ const ARDOUR::PortList& channel_ports (uint32_t) const;
+
+ void add_channel ();
+ void set_channels (uint32_t);
+ void remove_channel (uint32_t);
+ void add_port_to_channel (uint32_t, std::string const &);
+ void remove_port_from_channel (uint32_t, std::string const &);
+ bool port_attached_to_channel (uint32_t, std::string const &) const;
+ XMLNode& get_state ();
+
+ /// The number of channels is about to change
+ sigc::signal<void> ConfigurationWillChange;
+ /// The number of channels has changed
+ sigc::signal<void> ConfigurationHasChanged;
+ /// The port set associated with one of our channels is about to change
+ /// Parameter is the channel number
+ sigc::signal<void, int> PortsWillChange;
+ /// The port set associated with one of our channels has changed
+ /// Parameter is the channel number
+ sigc::signal<void, int> PortsHaveChanged;
+
+ private:
+
+ int set_state (const XMLNode &);
+
+ /// mutex for _ports;
+ /// XXX: is this necessary?
+ mutable Glib::Mutex _ports_mutex;
+ std::vector<PortList> _ports;
+};
+
+}
+
+#endif
diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc
index 4552de1186..2e1b790848 100644
--- a/libs/ardour/audioengine.cc
+++ b/libs/ardour/audioengine.cc
@@ -763,7 +763,7 @@ AudioEngine::frames_per_cycle ()
* Note this can return NULL, it will NOT create a port if it is not found (any more).
*/
Port *
-AudioEngine::get_port_by_name (const string& portname, bool keep)
+AudioEngine::get_port_by_name (const string& portname, bool keep) const
{
Glib::Mutex::Lock lm (_process_lock);
diff --git a/libs/ardour/auto_bundle.cc b/libs/ardour/auto_bundle.cc
new file mode 100644
index 0000000000..9da32bbb7a
--- /dev/null
+++ b/libs/ardour/auto_bundle.cc
@@ -0,0 +1,47 @@
+#include <cassert>
+#include "ardour/auto_bundle.h"
+
+ARDOUR::AutoBundle::AutoBundle (bool i)
+ : Bundle (i)
+{
+
+}
+
+ARDOUR::AutoBundle::AutoBundle (std::string const & n, bool i)
+ : Bundle (n, i)
+{
+
+}
+
+uint32_t
+ARDOUR::AutoBundle::nchannels () const
+{
+ Glib::Mutex::Lock lm (_ports_mutex);
+ return _ports.size ();
+}
+
+const ARDOUR::PortList&
+ARDOUR::AutoBundle::channel_ports (uint32_t c) const
+{
+ assert (c < nchannels());
+
+ Glib::Mutex::Lock lm (_ports_mutex);
+ return _ports[c];
+}
+
+void
+ARDOUR::AutoBundle::set_channels (uint32_t n)
+{
+ Glib::Mutex::Lock lm (_ports_mutex);
+ _ports.resize (n);
+}
+
+void
+ARDOUR::AutoBundle::set_port (uint32_t c, std::string const & p)
+{
+ assert (c < nchannels ());
+
+ Glib::Mutex::Lock lm (_ports_mutex);
+ _ports[c].resize (1);
+ _ports[c][0] = p;
+}
diff --git a/libs/ardour/io.cc b/libs/ardour/io.cc
index 95f40f1f80..d0a233a356 100644
--- a/libs/ardour/io.cc
+++ b/libs/ardour/io.cc
@@ -34,7 +34,7 @@
#include <ardour/port.h>
#include <ardour/audio_port.h>
#include <ardour/midi_port.h>
-#include <ardour/bundle.h>
+#include <ardour/auto_bundle.h>
#include <ardour/session.h>
#include <ardour/cycle_timer.h>
#include <ardour/panner.h>
@@ -153,7 +153,7 @@ IO::IO (Session& s, const string& name,
_session.add_controllable (_gain_control);
- create_bundles ();
+ create_bundles_for_inputs_and_outputs ();
}
IO::IO (Session& s, const XMLNode& node, DataType dt)
@@ -193,7 +193,7 @@ IO::IO (Session& s, const XMLNode& node, DataType dt)
_session.add_controllable (_gain_control);
- create_bundles ();
+ create_bundles_for_inputs_and_outputs ();
}
IO::~IO ()
@@ -334,24 +334,62 @@ IO::just_meter_input (nframes_t start_frame, nframes_t end_frame,
_meter->run(bufs, start_frame, end_frame, nframes, offset);
}
+
void
-IO::drop_input_bundle ()
+IO::check_bundles_connected_to_inputs ()
{
- _input_bundle.reset ();
- input_bundle_configuration_connection.disconnect();
- input_bundle_connection_connection.disconnect();
- _session.set_dirty ();
+ check_bundles (_bundles_connected_to_inputs, inputs());
}
void
-IO::drop_output_bundle ()
+IO::check_bundles_connected_to_outputs ()
{
- _output_bundle.reset ();
- output_bundle_configuration_connection.disconnect();
- output_bundle_connection_connection.disconnect();
- _session.set_dirty ();
+ check_bundles (_bundles_connected_to_outputs, outputs());
}
+void
+IO::check_bundles (std::vector<UserBundleInfo>& list, const PortSet& ports)
+{
+ std::vector<UserBundleInfo> new_list;
+
+ for (std::vector<UserBundleInfo>::iterator i = list.begin(); i != list.end(); ++i) {
+
+ uint32_t const N = i->bundle->nchannels ();
+
+ if (ports.num_ports() < N) {
+ continue;
+ }
+
+ bool ok = true;
+ for (uint32_t j = 0; j < N; ++j) {
+ /* Every port on bundle channel j must be connected to our input j */
+ PortList const pl = i->bundle->channel_ports (j);
+ for (uint32_t k = 0; k < pl.size(); ++k) {
+ if (ports.port(j)->connected_to (pl[k]) == false) {
+ ok = false;
+ break;
+ }
+ }
+
+ if (ok == false) {
+ break;
+ }
+ }
+
+ if (ok) {
+ new_list.push_back (*i);
+ } else {
+ i->configuration_will_change.disconnect ();
+ i->configuration_has_changed.disconnect ();
+ i->ports_will_change.disconnect ();
+ i->ports_have_changed.disconnect ();
+ }
+ }
+
+ list = new_list;
+}
+
+
int
IO::disconnect_input (Port* our_port, string other_port, void* src)
{
@@ -378,7 +416,7 @@ IO::disconnect_input (Port* our_port, string other_port, void* src)
return -1;
}
- drop_input_bundle ();
+ check_bundles_connected_to_inputs ();
}
}
@@ -412,8 +450,6 @@ IO::connect_input (Port* our_port, string other_port, void* src)
if (_session.engine().connect (other_port, our_port->name())) {
return -1;
}
-
- drop_input_bundle ();
}
}
@@ -448,7 +484,7 @@ IO::disconnect_output (Port* our_port, string other_port, void* src)
return -1;
}
- drop_output_bundle ();
+ check_bundles_connected_to_outputs ();
}
}
@@ -482,8 +518,6 @@ IO::connect_output (Port* our_port, string other_port, void* src)
if (_session.engine().connect (our_port->name(), other_port)) {
return -1;
}
-
- drop_output_bundle ();
}
}
@@ -544,7 +578,7 @@ IO::remove_output_port (Port* port, void* src)
}
_session.engine().unregister_port (*port);
- drop_output_bundle ();
+ check_bundles_connected_to_outputs ();
setup_peak_meters ();
reset_panner ();
@@ -555,7 +589,7 @@ IO::remove_output_port (Port* port, void* src)
}
if (change == ConnectionsChanged) {
- setup_bundles ();
+ setup_bundles_for_inputs_and_outputs ();
}
if (change != NoChange) {
@@ -608,7 +642,6 @@ IO::add_output_port (string destination, void* src, DataType type)
}
_outputs.add (our_port);
- drop_output_bundle ();
setup_peak_meters ();
reset_panner ();
}
@@ -624,7 +657,7 @@ IO::add_output_port (string destination, void* src, DataType type)
// pan_changed (src); /* EMIT SIGNAL */
output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
- setup_bundles ();
+ setup_bundles_for_inputs_and_outputs ();
_session.set_dirty ();
return 0;
@@ -655,7 +688,7 @@ IO::remove_input_port (Port* port, void* src)
}
_session.engine().unregister_port (*port);
- drop_input_bundle ();
+ check_bundles_connected_to_inputs ();
setup_peak_meters ();
reset_panner ();
@@ -666,7 +699,7 @@ IO::remove_input_port (Port* port, void* src)
}
if (change == ConfigurationChanged) {
- setup_bundles ();
+ setup_bundles_for_inputs_and_outputs ();
}
if (change != NoChange) {
@@ -719,7 +752,6 @@ IO::add_input_port (string source, void* src, DataType type)
}
_inputs.add (our_port);
- drop_input_bundle ();
setup_peak_meters ();
reset_panner ();
}
@@ -736,7 +768,7 @@ IO::add_input_port (string source, void* src, DataType type)
// pan_changed (src); /* EMIT SIGNAL */
input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
- setup_bundles ();
+ setup_bundles_for_inputs_and_outputs ();
_session.set_dirty ();
return 0;
@@ -755,7 +787,7 @@ IO::disconnect_inputs (void* src)
_session.engine().disconnect (*i);
}
- drop_input_bundle ();
+ check_bundles_connected_to_inputs ();
}
}
@@ -777,7 +809,7 @@ IO::disconnect_outputs (void* src)
_session.engine().disconnect (*i);
}
- drop_output_bundle ();
+ check_bundles_connected_to_outputs ();
}
}
@@ -841,7 +873,7 @@ IO::ensure_inputs_locked (ChanCount count, bool clear, void* src)
}
if (changed) {
- drop_input_bundle ();
+ check_bundles_connected_to_inputs ();
setup_peak_meters ();
reset_panner ();
PortCountChanged (n_inputs()); /* EMIT SIGNAL */
@@ -1008,18 +1040,18 @@ IO::ensure_io (ChanCount in, ChanCount out, bool clear, void* src)
}
if (out_changed) {
- drop_output_bundle ();
+ check_bundles_connected_to_outputs ();
output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
}
if (in_changed) {
- drop_input_bundle ();
+ check_bundles_connected_to_inputs ();
input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
}
if (in_changed || out_changed) {
PortCountChanged (max (n_outputs(), n_inputs())); /* EMIT SIGNAL */
- setup_bundles ();
+ setup_bundles_for_inputs_and_outputs ();
_session.set_dirty ();
}
@@ -1047,7 +1079,7 @@ IO::ensure_inputs (ChanCount count, bool clear, bool lockit, void* src)
if (changed) {
input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
- setup_bundles ();
+ setup_bundles_for_inputs_and_outputs ();
_session.set_dirty ();
}
return 0;
@@ -1106,7 +1138,7 @@ IO::ensure_outputs_locked (ChanCount count, bool clear, void* src)
}
if (changed) {
- drop_output_bundle ();
+ check_bundles_connected_to_outputs ();
PortCountChanged (n_outputs()); /* EMIT SIGNAL */
_session.set_dirty ();
}
@@ -1145,7 +1177,7 @@ IO::ensure_outputs (ChanCount count, bool clear, bool lockit, void* src)
if (changed) {
output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
- setup_bundles ();
+ setup_bundles_for_inputs_and_outputs ();
}
return 0;
@@ -1209,8 +1241,6 @@ IO::state (bool full_state)
XMLNode* node = new XMLNode (state_node_name);
char buf[64];
string str;
- bool need_ins = true;
- bool need_outs = true;
LocaleGuard lg (X_("POSIX"));
Glib::Mutex::Lock lm (io_lock);
@@ -1218,83 +1248,91 @@ IO::state (bool full_state)
id().print (buf, sizeof (buf));
node->add_property("id", buf);
- str = "";
-
- if (_input_bundle && !_input_bundle->dynamic()) {
- node->add_property ("input-connection", _input_bundle->name());
- need_ins = false;
+ for (
+ std::vector<UserBundleInfo>::iterator i = _bundles_connected_to_inputs.begin();
+ i != _bundles_connected_to_inputs.end();
+ ++i
+ )
+ {
+ XMLNode* n = new XMLNode ("InputBundle");
+ n->add_property ("name", i->bundle->name ());
+ node->add_child_nocopy (*n);
}
- if (_output_bundle && !_output_bundle->dynamic()) {
- node->add_property ("output-connection", _output_bundle->name());
- need_outs = false;
+ for (
+ std::vector<UserBundleInfo>::iterator i = _bundles_connected_to_outputs.begin();
+ i != _bundles_connected_to_outputs.end();
+ ++i
+ )
+ {
+ XMLNode* n = new XMLNode ("OutputBundle");
+ n->add_property ("name", i->bundle->name ());
+ node->add_child_nocopy (*n);
}
+
+ str = "";
- if (need_ins) {
- for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
+ for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
- const char **connections = i->get_connections();
+ const char **connections = i->get_connections();
+
+ if (connections && connections[0]) {
+ str += '{';
- if (connections && connections[0]) {
- str += '{';
+ for (int n = 0; connections && connections[n]; ++n) {
+ if (n) {
+ str += ',';
+ }
- for (int n = 0; connections && connections[n]; ++n) {
- if (n) {
- str += ',';
- }
-
- /* if its a connection to our own port,
- return only the port name, not the
- whole thing. this allows connections
- to be re-established even when our
- client name is different.
- */
-
- str += _session.engine().make_port_name_relative (connections[n]);
- }
-
- str += '}';
+ /* if its a connection to our own port,
+ return only the port name, not the
+ whole thing. this allows connections
+ to be re-established even when our
+ client name is different.
+ */
- free (connections);
- }
- else {
- str += "{}";
- }
+ str += _session.engine().make_port_name_relative (connections[n]);
+ }
+
+ str += '}';
+
+ free (connections);
+ }
+ else {
+ str += "{}";
}
-
- node->add_property ("inputs", str);
}
+
+ node->add_property ("inputs", str);
- if (need_outs) {
- str = "";
+ str = "";
+
+ for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
- for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
+ const char **connections = i->get_connections();
+
+ if (connections && connections[0]) {
- const char **connections = i->get_connections();
+ str += '{';
- if (connections && connections[0]) {
-
- str += '{';
-
- for (int n = 0; connections[n]; ++n) {
- if (n) {
- str += ',';
- }
-
- str += _session.engine().make_port_name_relative (connections[n]);
+ for (int n = 0; connections[n]; ++n) {
+ if (n) {
+ str += ',';
}
-
- str += '}';
- free (connections);
- }
- else {
- str += "{}";
+ str += _session.engine().make_port_name_relative (connections[n]);
}
+
+ str += '}';
+
+ free (connections);
+ }
+ else {
+ str += "{}";
}
-
- node->add_property ("outputs", str);
}
+
+ node->add_property ("outputs", str);
node->add_child_nocopy (_panner->state (full_state));
node->add_child_nocopy (_gain_control->get_state ());
@@ -1575,55 +1613,12 @@ IO::ports_became_legal ()
int
IO::create_ports (const XMLNode& node)
{
- const XMLProperty* prop;
+ XMLProperty const * prop;
int num_inputs = 0;
int num_outputs = 0;
- /* XXX: we could change *-connection to *-bundle, but it seems a bit silly to
- * break the session file format.
- */
- if ((prop = node.property ("input-connection")) != 0) {
-
- boost::shared_ptr<Bundle> c = _session.bundle_by_name (prop->value());
-
- if (c == 0) {
- error << string_compose(_("Unknown bundle \"%1\" listed for input of %2"), prop->value(), _name) << endmsg;
-
- if ((c = _session.bundle_by_name (_("in 1"))) == 0) {
- error << _("No input bundles available as a replacement")
- << endmsg;
- return -1;
- } else {
- info << string_compose (_("Bundle %1 was not available - \"in 1\" used instead"), prop->value())
- << endmsg;
- }
- }
-
- num_inputs = c->nchannels();
-
- } else if ((prop = node.property ("inputs")) != 0) {
-
+ if ((prop = node.property ("inputs")) != 0) {
num_inputs = count (prop->value().begin(), prop->value().end(), '{');
- }
-
- if ((prop = node.property ("output-connection")) != 0) {
- boost::shared_ptr<Bundle> c = _session.bundle_by_name (prop->value());
-
- if (c == 0) {
- error << string_compose(_("Unknown bundle \"%1\" listed for output of %2"), prop->value(), _name) << endmsg;
-
- if ((c = _session.bundle_by_name (_("out 1"))) == 0) {
- error << _("No output bundles available as a replacement")
- << endmsg;
- return -1;
- } else {
- info << string_compose (_("Bundle %1 was not available - \"out 1\" used instead"), prop->value())
- << endmsg;
- }
- }
-
- num_outputs = c->nchannels ();
-
} else if ((prop = node.property ("outputs")) != 0) {
num_outputs = count (prop->value().begin(), prop->value().end(), '{');
}
@@ -1648,57 +1643,48 @@ IO::create_ports (const XMLNode& node)
int
IO::make_connections (const XMLNode& node)
{
- const XMLProperty* prop;
-
- if ((prop = node.property ("input-connection")) != 0) {
- boost::shared_ptr<Bundle> c = _session.bundle_by_name (prop->value());
-
- if (c == 0) {
- error << string_compose(_("Unknown connection \"%1\" listed for input of %2"), prop->value(), _name) << endmsg;
-
- if ((c = _session.bundle_by_name (_("in 1"))) == 0) {
- error << _("No input connections available as a replacement")
- << endmsg;
- return -1;
- } else {
- info << string_compose (_("Bundle %1 was not available - \"in 1\" used instead"), prop->value())
- << endmsg;
- }
- }
-
- connect_input_ports_to_bundle (c, this);
-
- } else if ((prop = node.property ("inputs")) != 0) {
+ XMLProperty const * prop;
+
+ if ((prop = node.property ("inputs")) != 0) {
if (set_inputs (prop->value())) {
error << string_compose(_("improper input channel list in XML node (%1)"), prop->value()) << endmsg;
return -1;
}
}
-
- if ((prop = node.property ("output-bundle")) != 0) {
- boost::shared_ptr<Bundle> c = _session.bundle_by_name (prop->value());
-
- if (c == 0) {
- error << string_compose(_("Unknown bundle \"%1\" listed for output of %2"), prop->value(), _name) << endmsg;
- if ((c = _session.bundle_by_name (_("out 1"))) == 0) {
- error << _("No output bundles available as a replacement")
- << endmsg;
- return -1;
- } else {
- info << string_compose (_("Bundle %1 was not available - \"out 1\" used instead"), prop->value())
- << endmsg;
- }
- }
-
- connect_output_ports_to_bundle (c, this);
-
- } else if ((prop = node.property ("outputs")) != 0) {
+
+ if ((prop = node.property ("outputs")) != 0) {
if (set_outputs (prop->value())) {
error << string_compose(_("improper output channel list in XML node (%1)"), prop->value()) << endmsg;
return -1;
}
}
+
+ for (XMLNodeConstIterator i = node.children().begin(); i != node.children().end(); ++i) {
+
+ if ((*i)->name() == "InputBundle") {
+ XMLProperty const * prop = (*i)->property ("name");
+ if (prop) {
+ boost::shared_ptr<Bundle> b = _session.bundle_by_name (prop->value());
+ if (b) {
+ connect_input_ports_to_bundle (b, this);
+ } else {
+ error << string_compose(_("Unknown bundle \"%1\" listed for input of %2"), prop->value(), _name) << endmsg;
+ }
+ }
+
+ } else if ((*i)->name() == "OutputBundle") {
+ XMLProperty const * prop = (*i)->property ("name");
+ if (prop) {
+ boost::shared_ptr<Bundle> b = _session.bundle_by_name (prop->value());
+ if (b) {
+ connect_output_ports_to_bundle (b, this);
+ } else {
+ error << string_compose(_("Unknown bundle \"%1\" listed for output of %2"), prop->value(), _name) << endmsg;
+ }
+ }
+ }
+ }
return 0;
}
@@ -1880,7 +1866,7 @@ IO::set_name (const string& str)
bool const r = SessionObject::set_name(name);
- setup_bundles ();
+ setup_bundles_for_inputs_and_outputs ();
return r;
}
@@ -1960,61 +1946,21 @@ IO::input_latency () const
int
IO::connect_input_ports_to_bundle (boost::shared_ptr<Bundle> c, void* src)
{
- uint32_t limit;
-
{
BLOCK_PROCESS_CALLBACK ();
Glib::Mutex::Lock lm2 (io_lock);
-
- limit = c->nchannels();
-
- drop_input_bundle ();
-
- // FIXME bundles only work for audio-only
- if (ensure_inputs (ChanCount(DataType::AUDIO, limit), false, false, src)) {
- return -1;
- }
- /* first pass: check the current state to see what's correctly
- connected, and drop anything that we don't want.
- */
-
- for (uint32_t n = 0; n < limit; ++n) {
- const Bundle::PortList& pl = c->channel_ports (n);
-
- for (Bundle::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
-
- if (!_inputs.port(n)->connected_to ((*i))) {
-
- /* clear any existing connections */
-
- _session.engine().disconnect (*_inputs.port(n));
-
- } else if (_inputs.port(n)->connected() > 1) {
-
- /* OK, it is connected to the port we want,
- but its also connected to other ports.
- Change that situation.
- */
-
- /* XXX could be optimized to not drop
- the one we want.
- */
-
- _session.engine().disconnect (*_inputs.port(n));
-
- }
- }
- }
-
- /* second pass: connect all requested ports where necessary */
+ /* Connect to the bundle, not worrying about any connections
+ that are already made. */
+
+ uint32_t const channels = c->nchannels ();
- for (uint32_t n = 0; n < limit; ++n) {
- const Bundle::PortList& pl = c->channel_ports (n);
-
- for (Bundle::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
-
- if (!_inputs.port(n)->connected_to ((*i))) {
+ for (uint32_t n = 0; n < channels; ++n) {
+ const PortList& pl = c->channel_ports (n);
+
+ for (PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
+
+ if (!_inputs.port(n)->connected_to (*i)) {
if (_session.engine().connect (*i, _inputs.port(n)->name())) {
return -1;
@@ -2023,13 +1969,23 @@ IO::connect_input_ports_to_bundle (boost::shared_ptr<Bundle> c, void* src)
}
}
-
- _input_bundle = c;
-
- input_bundle_configuration_connection = c->ConfigurationChanged.connect
- (mem_fun (*this, &IO::input_bundle_configuration_changed));
- input_bundle_connection_connection = c->PortsChanged.connect
- (mem_fun (*this, &IO::input_bundle_connection_changed));
+
+ /* If this is a UserBundle, make a note of what we've done */
+
+ boost::shared_ptr<UserBundle> ub = boost::dynamic_pointer_cast<UserBundle> (c);
+ if (ub) {
+
+ /* See if we already know about this one */
+ std::vector<UserBundleInfo>::iterator i = _bundles_connected_to_inputs.begin();
+ while (i != _bundles_connected_to_inputs.end() && i->bundle != ub) {
+ ++i;
+ }
+
+ if (i == _bundles_connected_to_inputs.end()) {
+ /* We don't, so make a note */
+ _bundles_connected_to_inputs.push_back (UserBundleInfo (this, ub));
+ }
+ }
}
input_changed (IOChange (ConfigurationChanged|ConnectionsChanged), src); /* EMIT SIGNAL */
@@ -2039,62 +1995,22 @@ IO::connect_input_ports_to_bundle (boost::shared_ptr<Bundle> c, void* src)
int
IO::connect_output_ports_to_bundle (boost::shared_ptr<Bundle> c, void* src)
{
- uint32_t limit;
-
{
BLOCK_PROCESS_CALLBACK ();
Glib::Mutex::Lock lm2 (io_lock);
- limit = c->nchannels();
-
- drop_output_bundle ();
-
- // FIXME: audio-only
- if (ensure_outputs (ChanCount(DataType::AUDIO, limit), false, false, src)) {
- return -1;
- }
-
- /* first pass: check the current state to see what's correctly
- connected, and drop anything that we don't want.
- */
-
- for (uint32_t n = 0; n < limit; ++n) {
-
- const Bundle::PortList& pl = c->channel_ports (n);
-
- for (Bundle::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
-
- if (!_outputs.port(n)->connected_to ((*i))) {
-
- /* clear any existing connections */
-
- _session.engine().disconnect (*_outputs.port(n));
+ /* Connect to the bundle, not worrying about any connections
+ that are already made. */
- } else if (_outputs.port(n)->connected() > 1) {
+ uint32_t const channels = c->nchannels ();
- /* OK, it is connected to the port we want,
- but its also connected to other ports.
- Change that situation.
- */
+ for (uint32_t n = 0; n < channels; ++n) {
- /* XXX could be optimized to not drop
- the one we want.
- */
-
- _session.engine().disconnect (*_outputs.port(n));
- }
- }
- }
+ const PortList& pl = c->channel_ports (n);
- /* second pass: connect all requested ports where necessary */
+ for (PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
- for (uint32_t n = 0; n < limit; ++n) {
-
- const Bundle::PortList& pl = c->channel_ports (n);
-
- for (Bundle::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
-
- if (!_outputs.port(n)->connected_to ((*i))) {
+ if (!_outputs.port(n)->connected_to (*i)) {
if (_session.engine().connect (_outputs.port(n)->name(), *i)) {
return -1;
@@ -2103,12 +2019,22 @@ IO::connect_output_ports_to_bundle (boost::shared_ptr<Bundle> c, void* src)
}
}
- _output_bundle = c;
+ /* If this is a UserBundle, make a note of what we've done */
- output_bundle_configuration_connection = c->ConfigurationChanged.connect
- (mem_fun (*this, &IO::output_bundle_configuration_changed));
- output_bundle_connection_connection = c->PortsChanged.connect
- (mem_fun (*this, &IO::output_bundle_connection_changed));
+ boost::shared_ptr<UserBundle> ub = boost::dynamic_pointer_cast<UserBundle> (c);
+ if (ub) {
+
+ /* See if we already know about this one */
+ std::vector<UserBundleInfo>::iterator i = _bundles_connected_to_outputs.begin();
+ while (i != _bundles_connected_to_outputs.end() && i->bundle != ub) {
+ ++i;
+ }
+
+ if (i == _bundles_connected_to_outputs.end()) {
+ /* We don't, so make a note */
+ _bundles_connected_to_outputs.push_back (UserBundleInfo (this, ub));
+ }
+ }
}
output_changed (IOChange (ConnectionsChanged|ConfigurationChanged), src); /* EMIT SIGNAL */
@@ -2159,27 +2085,31 @@ IO::reset_panners ()
}
void
-IO::input_bundle_connection_changed (int ignored)
+IO::bundle_configuration_will_change ()
{
- connect_input_ports_to_bundle (_input_bundle, this);
+ //XXX
+// connect_input_ports_to_bundle (_input_bundle, this);
}
void
-IO::input_bundle_configuration_changed ()
+IO::bundle_configuration_has_changed ()
{
- connect_input_ports_to_bundle (_input_bundle, this);
+ //XXX
+// connect_input_ports_to_bundle (_input_bundle, this);
}
void
-IO::output_bundle_connection_changed (int ignored)
+IO::bundle_ports_will_change (int ignored)
{
- connect_output_ports_to_bundle (_output_bundle, this);
+//XXX
+// connect_output_ports_to_bundle (_output_bundle, this);
}
void
-IO::output_bundle_configuration_changed ()
+IO::bundle_ports_have_changed (int ignored)
{
- connect_output_ports_to_bundle (_output_bundle, this);
+ //XXX
+// connect_output_ports_to_bundle (_output_bundle, this);
}
void
@@ -2483,27 +2413,25 @@ IO::update_port_total_latencies ()
*/
void
-IO::setup_bundles ()
+IO::setup_bundles_for_inputs_and_outputs ()
{
char buf[32];
snprintf(buf, sizeof (buf), _("%s in"), _name.c_str());
- _bundle_for_inputs->set_name (buf, 0);
- int const ins = n_inputs().n_total();
- _bundle_for_inputs->set_nchannels (ins);
-
- for (int i = 0; i < ins; ++i) {
- _bundle_for_inputs->add_port_to_channel (i, _inputs.port(i)->name ());
- }
+ _bundle_for_inputs->set_name (buf);
+ uint32_t const ni = inputs().num_ports();
+ _bundle_for_inputs->set_channels (ni);
+ for (uint32_t i = 0; i < ni; ++i) {
+ _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, 0);
- int const outs = n_outputs().n_total();
- _bundle_for_outputs->set_nchannels (outs);
-
- for (int i = 0; i < outs; ++i) {
- _bundle_for_outputs->add_port_to_channel (i, _outputs.port(i)->name ());
- }
+ _bundle_for_outputs->set_name (buf);
+ uint32_t const no = outputs().num_ports();
+ _bundle_for_outputs->set_channels (no);
+ for (uint32_t i = 0; i < no; ++i) {
+ _bundle_for_outputs->set_port (i, outputs().port(i)->name());
+ }
}
@@ -2512,64 +2440,131 @@ IO::setup_bundles ()
*/
void
-IO::create_bundles ()
+IO::create_bundles_for_inputs_and_outputs ()
{
- _bundle_for_inputs = boost::shared_ptr<Bundle> (
- new InputBundle ("", true)
- );
-
- _bundle_for_outputs = boost::shared_ptr<Bundle> (
- new OutputBundle ("", true)
- );
-
- setup_bundles ();
+ _bundle_for_inputs = boost::shared_ptr<AutoBundle> (new AutoBundle (true));
+ _bundle_for_outputs = boost::shared_ptr<AutoBundle> (new AutoBundle (false));
+ setup_bundles_for_inputs_and_outputs ();
}
-boost::shared_ptr<Bundle>
-IO::input_bundle()
+/** Add a bundle to a list if is connected to our inputs.
+ * @param b Bundle to check.
+ * @param bundles List to add to.
+ */
+void
+IO::maybe_add_input_bundle_to_list (boost::shared_ptr<Bundle> b, std::vector<boost::shared_ptr<Bundle> >* bundles)
{
- if (_input_bundle) {
- return _input_bundle;
+ boost::shared_ptr<AutoBundle> ab = boost::dynamic_pointer_cast<AutoBundle, Bundle> (b);
+ if (ab == 0 || ab->ports_are_outputs() == false) {
+ return;
+ }
+
+ if (ab->nchannels () != n_inputs().n_total ()) {
+ return;
}
- /* XXX: will only report the first bundle found; should really return a list, I think */
-
- /* check that _input_bundle is right wrt the connections that are currently made */
+ for (uint32_t i = 0; i < n_inputs().n_total (); ++i) {
- /* make a vector of the first output connected to each of our inputs */
- std::vector<std::string> connected;
- for (uint32_t i = 0; i < _inputs.num_ports(); ++i) {
- const char** c = _inputs.port(i)->get_connections ();
- if (c) {
- connected.push_back (c[0]);
+ PortList const & pl = b->channel_ports (i);
+
+ if (pl.empty()) {
+ return;
+ }
+
+ if (!input(i)->connected_to (pl[0])) {
+ return;
}
}
- _input_bundle = _session.bundle_by_ports (connected);
- return _input_bundle;
+ bundles->push_back (b);
+}
+
+/** @return Bundles connected to our inputs */
+std::vector<boost::shared_ptr<Bundle> >
+IO::bundles_connected_to_inputs ()
+{
+ std::vector<boost::shared_ptr<Bundle> > bundles;
+
+ /* User bundles */
+ for (std::vector<UserBundleInfo>::iterator i = _bundles_connected_to_inputs.begin(); i != _bundles_connected_to_inputs.end(); ++i) {
+ bundles.push_back (i->bundle);
+ }
+
+ /* Auto bundles */
+ _session.foreach_bundle (
+ sigc::bind (sigc::mem_fun (*this, &IO::maybe_add_input_bundle_to_list), &bundles)
+ );
+
+ return bundles;
}
-boost::shared_ptr<Bundle>
-IO::output_bundle()
+/** Add a bundle to a list if is connected to our outputs.
+ * @param b Bundle to check.
+ * @param bundles List to add to.
+ */
+void
+IO::maybe_add_output_bundle_to_list (boost::shared_ptr<Bundle> b, std::vector<boost::shared_ptr<Bundle> >* bundles)
{
- if (_output_bundle) {
- return _output_bundle;
+ boost::shared_ptr<AutoBundle> ab = boost::dynamic_pointer_cast<AutoBundle, Bundle> (b);
+ if (ab == 0 || ab->ports_are_inputs() == false) {
+ return;
+ }
+
+ if (ab->nchannels () != n_outputs().n_total ()) {
+ return;
}
-
- /* XXX: will only report the first bundle found; should really return a list, I think */
-
- /* check that _output_bundle is right wrt the connections that are currently made */
- /* make a vector of the first input connected to each of our outputs */
- std::vector<std::string> connected;
- for (uint32_t i = 0; i < _outputs.num_ports(); ++i) {
- const char** c = _outputs.port(i)->get_connections ();
- if (c) {
- connected.push_back (c[0]);
+ for (uint32_t i = 0; i < n_outputs().n_total (); ++i) {
+
+ PortList const & pl = b->channel_ports (i);
+
+ if (pl.empty()) {
+ return;
+ }
+
+ if (!output(i)->connected_to (pl[0])) {
+ return;
}
}
- _output_bundle = _session.bundle_by_ports (connected);
- return _output_bundle;
+ bundles->push_back (b);
+}
+
+
+/* @return Bundles connected to our outputs */
+std::vector<boost::shared_ptr<Bundle> >
+IO::bundles_connected_to_outputs ()
+{
+ std::vector<boost::shared_ptr<Bundle> > bundles;
+
+ /* User bundles */
+ for (std::vector<UserBundleInfo>::iterator i = _bundles_connected_to_outputs.begin(); i != _bundles_connected_to_outputs.end(); ++i) {
+ bundles.push_back (i->bundle);
+ }
+
+ /* Auto bundles */
+ _session.foreach_bundle (
+ sigc::bind (sigc::mem_fun (*this, &IO::maybe_add_output_bundle_to_list), &bundles)
+ );
+
+ return bundles;
+}
+
+
+IO::UserBundleInfo::UserBundleInfo (IO* io, boost::shared_ptr<UserBundle> b)
+{
+ bundle = b;
+ configuration_will_change = b->ConfigurationWillChange.connect (
+ sigc::mem_fun (*io, &IO::bundle_configuration_will_change)
+ );
+ configuration_has_changed = b->ConfigurationHasChanged.connect (
+ sigc::mem_fun (*io, &IO::bundle_configuration_has_changed)
+ );
+ ports_will_change = b->PortsWillChange.connect (
+ sigc::mem_fun (*io, &IO::bundle_ports_will_change)
+ );
+ ports_have_changed = b->PortsHaveChanged.connect (
+ sigc::mem_fun (*io, &IO::bundle_ports_have_changed)
+ );
}
diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc
index 1382fa3f0a..e155800d23 100644
--- a/libs/ardour/session.cc
+++ b/libs/ardour/session.cc
@@ -62,7 +62,7 @@
#include <ardour/processor.h>
#include <ardour/plugin_insert.h>
#include <ardour/port_insert.h>
-#include <ardour/bundle.h>
+#include <ardour/auto_bundle.h>
#include <ardour/slave.h>
#include <ardour/tempo.h>
#include <ardour/audio_track.h>
@@ -123,6 +123,7 @@ Session::Session (AudioEngine &eng,
diskstreams (new DiskstreamList),
routes (new RouteList),
auditioner ((Auditioner*) 0),
+ _bundle_xml_node (0),
_click_io ((IO*) 0),
main_outs (0)
{
@@ -223,6 +224,7 @@ Session::Session (AudioEngine &eng,
_send_smpte_update (false),
diskstreams (new DiskstreamList),
routes (new RouteList),
+ _bundle_xml_node (0),
main_outs (0)
{
@@ -599,22 +601,22 @@ Session::when_engine_running ()
char buf[32];
snprintf (buf, sizeof (buf), _("out %" PRIu32), np+1);
- shared_ptr<Bundle> c (new InputBundle (buf, true));
- c->set_nchannels (1);
- c->add_port_to_channel (0, _engine.get_nth_physical_output (DataType::AUDIO, np));
+ shared_ptr<AutoBundle> c (new AutoBundle (buf, true));
+ c->set_channels (1);
+ c->set_port (0, _engine.get_nth_physical_output (DataType::AUDIO, np));
- add_bundle (c);
+ add_bundle (c);
}
for (uint32_t np = 0; np < n_physical_inputs; ++np) {
char buf[32];
snprintf (buf, sizeof (buf), _("in %" PRIu32), np+1);
- shared_ptr<Bundle> c (new OutputBundle (buf, true));
- c->set_nchannels (1);
- c->add_port_to_channel (0, _engine.get_nth_physical_input (DataType::AUDIO, np));
+ shared_ptr<AutoBundle> c (new AutoBundle (buf, false));
+ c->set_channels (1);
+ c->set_port (0, _engine.get_nth_physical_input (DataType::AUDIO, np));
- add_bundle (c);
+ add_bundle (c);
}
/* TWO: STEREO */
@@ -623,24 +625,24 @@ Session::when_engine_running ()
char buf[32];
snprintf (buf, sizeof (buf), _("out %" PRIu32 "+%" PRIu32), np+1, np+2);
- shared_ptr<Bundle> c (new InputBundle (buf, true));
- c->set_nchannels (2);
- c->add_port_to_channel (0, _engine.get_nth_physical_output (DataType::AUDIO, np));
- c->add_port_to_channel (1, _engine.get_nth_physical_output (DataType::AUDIO, np+1));
+ shared_ptr<AutoBundle> c (new AutoBundle (buf, true));
+ c->set_channels (2);
+ c->set_port (0, _engine.get_nth_physical_output (DataType::AUDIO, np));
+ c->set_port (1, _engine.get_nth_physical_output (DataType::AUDIO, np + 1));
- add_bundle (c);
+ add_bundle (c);
}
for (uint32_t np = 0; np < n_physical_inputs; np +=2) {
char buf[32];
snprintf (buf, sizeof (buf), _("in %" PRIu32 "+%" PRIu32), np+1, np+2);
- shared_ptr<Bundle> c (new OutputBundle (buf, true));
- c->set_nchannels (2);
- c->add_port_to_channel (0, _engine.get_nth_physical_input (DataType::AUDIO, np));
- c->add_port_to_channel (1, _engine.get_nth_physical_input (DataType::AUDIO, np+1));
+ shared_ptr<AutoBundle> c (new AutoBundle (buf, false));
+ c->set_channels (2);
+ c->set_port (0, _engine.get_nth_physical_input (DataType::AUDIO, np));
+ c->set_port (1, _engine.get_nth_physical_input (DataType::AUDIO, np + 1));
- add_bundle (c);
+ add_bundle (c);
}
/* THREE MASTER */
@@ -685,13 +687,13 @@ Session::when_engine_running ()
}
- shared_ptr<Bundle> c (new OutputBundle (_("Master Out"), true));
+ shared_ptr<AutoBundle> c (new AutoBundle (_("Master Out"), true));
- c->set_nchannels (_master_out->n_inputs().n_total());
- for (uint32_t n = 0; n < _master_out->n_inputs ().n_total(); ++n) {
- c->add_port_to_channel ((int) n, _master_out->input(n)->name());
- }
- add_bundle (c);
+ c->set_channels (_master_out->n_inputs().n_total());
+ for (uint32_t n = 0; n < _master_out->n_inputs ().n_total(); ++n) {
+ c->set_port (n, _master_out->input(n)->name());
+ }
+ add_bundle (c);
}
hookup_io ();
@@ -802,7 +804,13 @@ Session::hookup_io ()
for (RouteList::iterator x = r->begin(); x != r->end(); ++x) {
(*x)->set_control_outs (cports);
}
- }
+ }
+
+ /* load bundles, which we may have postponed earlier on */
+ if (_bundle_xml_node) {
+ load_bundles (*_bundle_xml_node);
+ delete _bundle_xml_node;
+ }
/* Tell all IO objects to connect themselves together */
@@ -3739,35 +3747,6 @@ Session::bundle_by_name (string name) const
return boost::shared_ptr<Bundle> ();
}
-boost::shared_ptr<Bundle>
-Session::bundle_by_ports (std::vector<std::string> const & wanted_ports) const
-{
- Glib::Mutex::Lock lm (bundle_lock);
-
- for (BundleList::const_iterator i = _bundles.begin(); i != _bundles.end(); ++i) {
- if ((*i)->nchannels() != wanted_ports.size()) {
- continue;
- }
-
- bool match = true;
- for (uint32_t j = 0; j < (*i)->nchannels(); ++j) {
- Bundle::PortList const p = (*i)->channel_ports (j);
- if (p.empty() || p[0] != wanted_ports[j]) {
- /* not this bundle */
- match = false;
- break;
- }
- }
-
- if (match) {
- /* matched bundle */
- return *i;
- }
- }
-
- return boost::shared_ptr<Bundle> ();
-}
-
void
Session::tempo_map_changed (Change ignored)
{
diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc
index 0534da6c89..c711b227e7 100644
--- a/libs/ardour/session_state.cc
+++ b/libs/ardour/session_state.cc
@@ -953,12 +953,13 @@ Session::state(bool full_state)
node->add_child_nocopy (loc.get_state());
}
- child = node->add_child ("Connections");
+ child = node->add_child ("Bundles");
{
Glib::Mutex::Lock lm (bundle_lock);
for (BundleList::iterator i = _bundles.begin(); i != _bundles.end(); ++i) {
- if (!(*i)->dynamic()) {
- child->add_child_nocopy ((*i)->get_state());
+ boost::shared_ptr<UserBundle> b = boost::dynamic_pointer_cast<UserBundle> (*i);
+ if (b) {
+ child->add_child_nocopy (b->get_state());
}
}
}
@@ -1197,13 +1198,16 @@ Session::set_state (const XMLNode& node)
goto out;
}
- if ((child = find_named_node (node, "Connections")) == 0) {
- error << _("Session: XML state has no connections section") << endmsg;
- goto out;
- } else if (load_bundles (*child)) {
+ if ((child = find_named_node (node, "Bundles")) == 0) {
+ error << _("Session: XML state has no bundles section") << endmsg;
goto out;
+ } else {
+ /* We can't load Bundles yet as they need to be able
+ to convert from port names to Port objects, which can't happen until
+ later */
+ _bundle_xml_node = new XMLNode (*child);
}
-
+
if ((child = find_named_node (node, "EditGroups")) == 0) {
error << _("Session: XML state has no edit groups section") << endmsg;
goto out;
@@ -1237,7 +1241,7 @@ Session::set_state (const XMLNode& node)
} else if (_click_io) {
_click_io->set_state (*child);
}
-
+
if ((child = find_named_node (node, "ControlProtocols")) != 0) {
ControlProtocolManager::instance().set_protocol_states (*child);
}
@@ -1876,23 +1880,23 @@ Session::automation_dir () const
}
int
-Session::load_bundles (const XMLNode& node)
-{
- XMLNodeList nlist = node.children();
- XMLNodeConstIterator niter;
-
- set_dirty();
-
- for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
- if ((*niter)->name() == "InputConnection") {
- add_bundle (boost::shared_ptr<Bundle> (new InputBundle (**niter)));
- } else if ((*niter)->name() == "OutputConnection") {
- add_bundle (boost::shared_ptr<Bundle> (new OutputBundle (**niter)));
- } else {
- error << string_compose(_("Unknown node \"%1\" found in Connections list from state file"), (*niter)->name()) << endmsg;
- return -1;
- }
- }
+Session::load_bundles (XMLNode const & node)
+{
+ XMLNodeList nlist = node.children();
+ XMLNodeConstIterator niter;
+
+ set_dirty();
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+ if ((*niter)->name() == "InputBundle") {
+ add_bundle (boost::shared_ptr<UserBundle> (new UserBundle (**niter, true)));
+ } else if ((*niter)->name() == "OutputBundle") {
+ add_bundle (boost::shared_ptr<UserBundle> (new UserBundle (**niter, false)));
+ } else {
+ error << string_compose(_("Unknown node \"%1\" found in Bundles list from state file"), (*niter)->name()) << endmsg;
+ return -1;
+ }
+ }
return 0;
}
diff --git a/libs/ardour/user_bundle.cc b/libs/ardour/user_bundle.cc
new file mode 100644
index 0000000000..471d823496
--- /dev/null
+++ b/libs/ardour/user_bundle.cc
@@ -0,0 +1,198 @@
+#include <cassert>
+#include <pbd/failed_constructor.h>
+#include <pbd/compose.h>
+#include <pbd/xml++.h>
+#include "ardour/user_bundle.h"
+#include "ardour/port_set.h"
+#include "ardour/io.h"
+#include "ardour/session.h"
+#include "ardour/audioengine.h"
+#include "i18n.h"
+
+ARDOUR::UserBundle::UserBundle (std::string const & n)
+ : Bundle (n)
+{
+
+}
+
+ARDOUR::UserBundle::UserBundle (XMLNode const & x, bool i)
+ : Bundle (i)
+{
+ if (set_state (x)) {
+ throw failed_constructor ();
+ }
+}
+
+uint32_t
+ARDOUR::UserBundle::nchannels () const
+{
+ Glib::Mutex::Lock lm (_ports_mutex);
+ return _ports.size ();
+}
+
+const ARDOUR::PortList&
+ARDOUR::UserBundle::channel_ports (uint32_t n) const
+{
+ assert (n < nchannels ());
+
+ Glib::Mutex::Lock lm (_ports_mutex);
+ return _ports[n];
+}
+
+void
+ARDOUR::UserBundle::add_port_to_channel (uint32_t c, std::string const & p)
+{
+ assert (c < nchannels ());
+
+ PortsWillChange (c);
+
+ {
+ Glib::Mutex::Lock lm (_ports_mutex);
+ _ports[c].push_back (p);
+ }
+
+ PortsHaveChanged (c);
+}
+
+void
+ARDOUR::UserBundle::remove_port_from_channel (uint32_t c, std::string const & p)
+{
+ assert (c < nchannels ());
+
+ PortsWillChange (c);
+
+ {
+ Glib::Mutex::Lock lm (_ports_mutex);
+ PortList::iterator i = std::find (_ports[c].begin(), _ports[c].end(), p);
+ if (i != _ports[c].end()) {
+ _ports[c].erase (i);
+ }
+ }
+
+ PortsHaveChanged (c);
+}
+
+bool
+ARDOUR::UserBundle::port_attached_to_channel (uint32_t c, std::string const & p) const
+{
+ assert (c < nchannels ());
+
+ Glib::Mutex::Lock lm (_ports_mutex);
+ return std::find (_ports[c].begin(), _ports[c].end(), p) != _ports[c].end();
+}
+
+void
+ARDOUR::UserBundle::add_channel ()
+{
+ ConfigurationWillChange ();
+
+ {
+ Glib::Mutex::Lock lm (_ports_mutex);
+ _ports.resize (_ports.size() + 1);
+ }
+
+ ConfigurationHasChanged ();
+}
+
+void
+ARDOUR::UserBundle::set_channels (uint32_t n)
+{
+ ConfigurationWillChange ();
+
+ {
+ Glib::Mutex::Lock lm (_ports_mutex);
+ _ports.resize (n);
+ }
+
+ ConfigurationHasChanged ();
+}
+
+void
+ARDOUR::UserBundle::remove_channel (uint32_t r)
+{
+ assert (r < nchannels ());
+
+ ConfigurationWillChange ();
+
+ {
+ Glib::Mutex::Lock lm (_ports_mutex);
+ _ports.erase (_ports.begin() + r, _ports.begin() + r + 1);
+ }
+
+ ConfigurationHasChanged ();
+}
+
+int
+ARDOUR::UserBundle::set_state (XMLNode const & node)
+{
+ XMLProperty const * name;
+
+ if ((name = node.property ("name")) == 0) {
+ PBD::error << _("Node for Bundle has no \"name\" property") << endmsg;
+ return -1;
+ }
+
+ set_name (name->value ());
+
+ XMLNodeList const channels = node.children ();
+
+ int n = 0;
+ for (XMLNodeConstIterator i = channels.begin(); i != channels.end(); ++i) {
+
+ if ((*i)->name() != "Channel") {
+ PBD::error << string_compose (_("Unknown node \"%s\" in Bundle"), (*i)->name()) << endmsg;
+ return -1;
+ }
+
+ add_channel ();
+
+ XMLNodeList const ports = (*i)->children ();
+
+ for (XMLNodeConstIterator j = ports.begin(); j != ports.end(); ++j) {
+ if ((*j)->name() != "Port") {
+ PBD::error << string_compose (_("Unknown node \"%s\" in Bundle"), (*j)->name()) << endmsg;
+ return -1;
+ }
+
+ if ((name = (*j)->property ("name")) == 0) {
+ PBD::error << _("Node for Port has no \"name\" property") << endmsg;
+ return -1;
+ }
+
+ add_port_to_channel (n, name->value ());
+ }
+
+ ++n;
+ }
+
+ return 0;
+}
+
+XMLNode&
+ARDOUR::UserBundle::get_state ()
+{
+ XMLNode *node;
+
+ if (ports_are_inputs ()) {
+ node = new XMLNode ("InputBundle");
+ } else {
+ node = new XMLNode ("OutputBundle");
+ }
+
+ node->add_property ("name", name ());
+
+ for (std::vector<PortList>::iterator i = _ports.begin(); i != _ports.end(); ++i) {
+
+ XMLNode* c = new XMLNode ("Channel");
+
+ for (PortList::iterator j = i->begin(); j != i->end(); ++j) {
+ XMLNode* p = new XMLNode ("Port");
+ p->add_property ("name", *j);
+ c->add_child_nocopy (*p);
+ }
+
+ node->add_child_nocopy (*c);
+ }
+
+ return *node;
+}