summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2008-12-08 16:07:28 +0000
committerPaul Davis <paul@linuxaudiosystems.com>2008-12-08 16:07:28 +0000
commit3be16e8afbd891c0bfe7227158384ed0d127597f (patch)
tree73a8f761fc13cbd0613d3923a34748bb9aa761d2
parenta9bb336fc44ab4937978f5a0308e440ed632ea50 (diff)
partial patch/partial by-hand merge of 2.X commits 3169&3170 to 3.X codebase
git-svn-id: svn://localhost/ardour2/branches/3.0@4300 d708f5d6-7413-0410-9779-e7cbd77b26cf
-rw-r--r--gtk2_ardour/SConscript1
-rw-r--r--gtk2_ardour/ardour_ui.cc4
-rw-r--r--gtk2_ardour/audio_region_view.cc10
-rw-r--r--gtk2_ardour/editor.h1
-rw-r--r--gtk2_ardour/editor_mixer.cc159
-rw-r--r--gtk2_ardour/gain_meter.cc120
-rw-r--r--gtk2_ardour/gain_meter.h9
-rw-r--r--gtk2_ardour/level_meter.cc15
-rw-r--r--gtk2_ardour/level_meter.h4
-rw-r--r--gtk2_ardour/main.cc21
-rw-r--r--gtk2_ardour/matrix.cc427
-rw-r--r--gtk2_ardour/matrix.h106
-rw-r--r--gtk2_ardour/mixer_strip.cc337
-rw-r--r--gtk2_ardour/mixer_strip.h4
-rw-r--r--gtk2_ardour/option_editor.cc6
-rw-r--r--gtk2_ardour/panner_ui.cc115
-rw-r--r--gtk2_ardour/panner_ui.h11
-rw-r--r--gtk2_ardour/port_group.h90
-rw-r--r--gtk2_ardour/port_matrix.cc433
-rw-r--r--gtk2_ardour/port_matrix.h116
-rw-r--r--gtk2_ardour/processor_box.cc31
-rw-r--r--gtk2_ardour/processor_box.h8
-rw-r--r--gtk2_ardour/route_params_ui.cc7
-rw-r--r--gtk2_ardour/route_time_axis.cc4
-rw-r--r--gtk2_ardour/route_ui.cc140
-rw-r--r--gtk2_ardour/route_ui.h14
-rw-r--r--gtk2_ardour/send_ui.cc11
-rw-r--r--gtk2_ardour/sfdb_ui.cc3
-rw-r--r--libs/ardour/plugin_manager.cc45
-rw-r--r--libs/gtkmm2ext/binding_proxy.cc21
-rw-r--r--libs/gtkmm2ext/gtkmm2ext/barcontroller.h2
-rw-r--r--libs/gtkmm2ext/gtkmm2ext/bindable_button.h14
-rw-r--r--libs/gtkmm2ext/gtkmm2ext/binding_proxy.h2
-rw-r--r--libs/gtkmm2ext/gtkmm2ext/slider_controller.h7
-rw-r--r--libs/gtkmm2ext/slider_controller.cc9
35 files changed, 1387 insertions, 920 deletions
diff --git a/gtk2_ardour/SConscript b/gtk2_ardour/SConscript
index fef196e5ba..8bfccae2b4 100644
--- a/gtk2_ardour/SConscript
+++ b/gtk2_ardour/SConscript
@@ -194,6 +194,7 @@ lineset.cc
location_ui.cc
main.cc
marker.cc
+matrix.cc
midi_channel_selector.cc
midi_port_dialog.cc
midi_region_view.cc
diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc
index b3ac2e3db8..44280a3109 100644
--- a/gtk2_ardour/ardour_ui.cc
+++ b/gtk2_ardour/ardour_ui.cc
@@ -1736,8 +1736,8 @@ ARDOUR_UI::engine_halted ()
_("\
JACK has either been shutdown or it\n\
disconnected Ardour because Ardour\n\
-was not fast enough. You can save the\n\
-session and/or try to reconnect to JACK ."));
+was not fast enough. Try to restart\n\
+JACK, reconnect and save the session."));
pop_back_splash ();
msg.run ();
}
diff --git a/gtk2_ardour/audio_region_view.cc b/gtk2_ardour/audio_region_view.cc
index 40046cd9a1..e3dd980eee 100644
--- a/gtk2_ardour/audio_region_view.cc
+++ b/gtk2_ardour/audio_region_view.cc
@@ -196,6 +196,10 @@ AudioRegionView::init (Gdk::Color& basic_color, bool wfd)
setup_fade_handle_positions ();
+ if (!Config->get_show_region_fades()) {
+ set_fade_visibility (false);
+ }
+
string line_name = _region->name();
line_name += ':';
line_name += "gain";
@@ -410,8 +414,10 @@ AudioRegionView::reset_width_dependent_items (double pixel_width)
fade_in_handle->hide();
fade_out_handle->hide();
} else {
- fade_in_handle->show();
- fade_out_handle->show();
+ if (Config->get_show_region_fades()) {
+ fade_in_handle->show();
+ fade_out_handle->show();
+ }
}
}
}
diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h
index 279de99298..394c1ad121 100644
--- a/gtk2_ardour/editor.h
+++ b/gtk2_ardour/editor.h
@@ -294,6 +294,7 @@ class Editor : public PublicEditor
Width editor_mixer_strip_width;
void maybe_add_mixer_strip_width (XMLNode&);
void show_editor_mixer (bool yn);
+ void create_editor_mixer ();
void show_editor_list (bool yn);
void set_selected_mixer_strip (TimeAxisView&);
void hide_track_in_display (TimeAxisView& tv, bool temporary = false);
diff --git a/gtk2_ardour/editor_mixer.cc b/gtk2_ardour/editor_mixer.cc
index 6bfc325f6d..cb10b2f551 100644
--- a/gtk2_ardour/editor_mixer.cc
+++ b/gtk2_ardour/editor_mixer.cc
@@ -58,13 +58,6 @@ Editor::editor_list_button_toggled ()
}
void
-Editor::cms_new (boost::shared_ptr<ARDOUR::Route> r)
-{
- current_mixer_strip = new MixerStrip (*ARDOUR_UI::instance()->the_mixer(), *session, r);
- current_mixer_strip->GoingAway.connect (mem_fun (*this, &Editor::cms_deleted));
-}
-
-void
Editor::cms_deleted ()
{
current_mixer_strip = 0;
@@ -73,76 +66,108 @@ Editor::cms_deleted ()
void
Editor::show_editor_mixer (bool yn)
{
+ boost::shared_ptr<ARDOUR::Route> r;
+
show_editor_mixer_when_tracks_arrive = false;
+ if (!session) {
+ show_editor_mixer_when_tracks_arrive = yn;
+ return;
+ }
+
if (yn) {
- if (current_mixer_strip == 0) {
+ if (selection->tracks.empty()) {
+
+ if (track_views.empty()) {
+ show_editor_mixer_when_tracks_arrive = true;
+ return;
+ }
- if (selection->tracks.empty()) {
-
- if (track_views.empty()) {
- show_editor_mixer_when_tracks_arrive = true;
- return;
- }
+ for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
+ AudioTimeAxisView* atv;
- for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
- AudioTimeAxisView* atv;
-
- if ((atv = dynamic_cast<AudioTimeAxisView*> (*i)) != 0) {
- cms_new (atv->route ());
- break;
- }
+ if ((atv = dynamic_cast<AudioTimeAxisView*> (*i)) != 0) {
+ r = atv->route();
+ break;
}
+ }
- } else {
-
- sort_track_selection ();
-
- for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
- AudioTimeAxisView* atv;
+ } else {
- if ((atv = dynamic_cast<AudioTimeAxisView*> (*i)) != 0) {
- cms_new (atv->route ());
- break;
- }
+ sort_track_selection ();
+
+ for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
+ AudioTimeAxisView* atv;
+
+ if ((atv = dynamic_cast<AudioTimeAxisView*> (*i)) != 0) {
+ r = atv->route();
+ break;
}
-
}
+ }
+
+ if (r) {
+ bool created;
if (current_mixer_strip == 0) {
- return;
- }
+ create_editor_mixer ();
+ created = true;
+ } else {
+ created = false;
+ }
+
+ current_mixer_strip->set_route (r);
+
+ if (created) {
+ current_mixer_strip->set_width (editor_mixer_strip_width, (void*) this);
+ }
}
if (current_mixer_strip->get_parent() == 0) {
- current_mixer_strip->set_embedded (true);
- current_mixer_strip->Hiding.connect (mem_fun(*this, &Editor::current_mixer_strip_hidden));
- current_mixer_strip->GoingAway.connect (mem_fun(*this, &Editor::current_mixer_strip_removed));
- current_mixer_strip->set_width (editor_mixer_strip_width, (void*) this);
-
global_hpacker.pack_start (*current_mixer_strip, Gtk::PACK_SHRINK );
global_hpacker.reorder_child (*current_mixer_strip, 0);
-
current_mixer_strip->show_all ();
}
} else {
if (current_mixer_strip) {
- editor_mixer_strip_width = current_mixer_strip->get_width ();
if (current_mixer_strip->get_parent() != 0) {
global_hpacker.remove (*current_mixer_strip);
}
}
}
+
#ifdef GTKOSX
/* XXX gtk problem here */
+ ensure_all_elements_drawn();
+#endif
+}
+
+#ifdef GTKOSX
+void
+Editor::ensure_all_elements_drawn ()
+{
+ controls_layout.queue_draw ();
ruler_label_event_box.queue_draw ();
time_button_event_box.queue_draw ();
- controls_layout.queue_draw ();
-#endif
}
+#endif
+
+void
+Editor::create_editor_mixer ()
+{
+ current_mixer_strip = new MixerStrip (*ARDOUR_UI::instance()->the_mixer(),
+ *session,
+ false);
+ current_mixer_strip->Hiding.connect (mem_fun(*this, &Editor::current_mixer_strip_hidden));
+ current_mixer_strip->GoingAway.connect (mem_fun(*this, &Editor::current_mixer_strip_removed));
+#ifdef GTKOSX
+ current_mixer_strip->WidthChanged.connect (mem_fun(*this, &Editor::ensure_all_elements_drawn));
+#endif
+ current_mixer_strip->set_embedded (true);
+}
void
Editor::show_editor_list (bool yn)
@@ -157,38 +182,52 @@ Editor::show_editor_list (bool yn)
void
Editor::set_selected_mixer_strip (TimeAxisView& view)
{
- RouteTimeAxisView* rt;
+ AudioTimeAxisView* at;
bool show = false;
+ bool created;
- if (!session || (rt = dynamic_cast<RouteTimeAxisView*>(&view)) == 0) {
+ if (!session || (at = dynamic_cast<AudioTimeAxisView*>(&view)) == 0) {
return;
}
-
- if (current_mixer_strip) {
- /* might be nothing to do */
-
- if (current_mixer_strip->route() == rt->route()) {
+ Glib::RefPtr<Gtk::Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
+ if (act) {
+ Glib::RefPtr<Gtk::ToggleAction> tact = Glib::RefPtr<Gtk::ToggleAction>::cast_dynamic(act);
+ if (!tact || !tact->get_active()) {
+ /* not showing mixer strip presently */
return;
}
+ }
- if (current_mixer_strip->get_parent()) {
- show = true;
- }
- delete current_mixer_strip;
- current_mixer_strip = 0;
+ if (current_mixer_strip == 0) {
+ create_editor_mixer ();
+ created = true;
+ } else {
+ created = false;
}
- current_mixer_strip = new MixerStrip (*ARDOUR_UI::instance()->the_mixer(),
- *session,
- rt->route(), false);
- current_mixer_strip->GoingAway.connect (mem_fun(*this, &Editor::cms_deleted));
+ /* might be nothing to do */
+ if (current_mixer_strip->route() == at->route()) {
+ return;
+ }
+
+ if (current_mixer_strip->get_parent()) {
+ show = true;
+ }
+
+ current_mixer_strip->set_route (at->route());
+
+ if (created) {
+ current_mixer_strip->set_width (editor_mixer_strip_width, (void*) this);
+ }
+
if (show) {
show_editor_mixer (true);
}
}
+
double current = 0.0;
bool currentInitialized = 0;
@@ -276,7 +315,7 @@ void
Editor::current_mixer_strip_removed ()
{
if (current_mixer_strip) {
- /* it is being deleted */
+ /* it is being deleted elsewhere */
current_mixer_strip = 0;
}
}
diff --git a/gtk2_ardour/gain_meter.cc b/gtk2_ardour/gain_meter.cc
index a580ec6b38..b34c0b8d28 100644
--- a/gtk2_ardour/gain_meter.cc
+++ b/gtk2_ardour/gain_meter.cc
@@ -72,17 +72,18 @@ GainMeter::setup_slider_pix ()
}
}
-GainMeterBase::GainMeterBase (boost::shared_ptr<IO> io, Session& s,
+GainMeterBase::GainMeterBase (Session& s,
const Glib::RefPtr<Gdk::Pixbuf>& pix,
bool horizontal)
- : _io (io),
- _session (s),
+ : _session (s),
// 0.781787 is the value needed for gain to be set to 0.
gain_adjustment (0.781787, 0.0, 1.0, 0.01, 0.1),
gain_automation_style_button (""),
gain_automation_state_button ("")
{
+ using namespace Menu_Helpers;
+
ignore_toggle = false;
meter_menu = 0;
next_release_selects = false;
@@ -92,16 +93,14 @@ GainMeterBase::GainMeterBase (boost::shared_ptr<IO> io, Session& s,
if (horizontal) {
gain_slider = manage (new HSliderController (pix,
&gain_adjustment,
- _io->gain_control(),
false));
} else {
gain_slider = manage (new VSliderController (pix,
&gain_adjustment,
- _io->gain_control(),
false));
}
- level_meter = new LevelMeter(_io, _session);
+ level_meter = new LevelMeter(_session);
gain_slider->signal_button_press_event().connect (mem_fun(*this, &GainMeter::start_gain_touch));
gain_slider->signal_button_release_event().connect (mem_fun(*this, &GainMeter::end_gain_touch));
@@ -133,10 +132,40 @@ GainMeterBase::GainMeterBase (boost::shared_ptr<IO> io, Session& s,
gain_automation_state_button.set_size_request(15, 15);
gain_automation_style_button.set_size_request(15, 15);
+
+ gain_astyle_menu.items().push_back (MenuElem (_("Trim")));
+ gain_astyle_menu.items().push_back (MenuElem (_("Abs")));
+
+ gain_astate_menu.set_name ("ArdourContextMenu");
+ gain_astyle_menu.set_name ("ArdourContextMenu");
+ gain_adjustment.signal_value_changed().connect (mem_fun(*this, &GainMeterBase::gain_adjusted));
+ peak_display.signal_button_release_event().connect (mem_fun(*this, &GainMeterBase::peak_button_release), false);
+ gain_display.signal_key_press_event().connect (mem_fun(*this, &GainMeterBase::gain_key_press), false);
+ ResetAllPeakDisplays.connect (mem_fun(*this, &GainMeterBase::reset_peak_display));
+ ResetGroupPeakDisplays.connect (mem_fun(*this, &GainMeterBase::reset_group_peak_display));
+ UI::instance()->theme_changed.connect (mem_fun(*this, &GainMeterBase::on_theme_changed));
+ ColorsChanged.connect (bind(mem_fun (*this, &GainMeterBase::color_handler), false));
+ DPIReset.connect (bind(mem_fun (*this, &GainMeterBase::color_handler), true));
+}
+
+GainMeterBase::~GainMeterBase ()
+{
+ delete meter_menu;
+ delete level_meter;
+}
+void
+GainMeterBase::set_io (boost::shared_ptr<IO> io)
+{
+ connections.clear ();
+
+ _io = io;
+
+ level_meter->set_io (_io);
+ gain_slider->set_controllable (_io->gain_control());
boost::shared_ptr<Route> r;
@@ -146,6 +175,8 @@ GainMeterBase::GainMeterBase (boost::shared_ptr<IO> io, Session& s,
using namespace Menu_Helpers;
+ gain_astate_menu.items().clear ();
+
gain_astate_menu.items().push_back (MenuElem (_("Manual"),
bind (mem_fun (*_io, &IO::set_parameter_automation_state),
Evoral::Parameter(GainAutomation), (AutoState) Off)));
@@ -159,50 +190,21 @@ GainMeterBase::GainMeterBase (boost::shared_ptr<IO> io, Session& s,
bind (mem_fun (*_io, &IO::set_parameter_automation_state),
Evoral::Parameter(GainAutomation), (AutoState) Touch)));
- gain_astyle_menu.items().push_back (MenuElem (_("Trim")));
- gain_astyle_menu.items().push_back (MenuElem (_("Abs")));
-
- gain_astate_menu.set_name ("ArdourContextMenu");
- gain_astyle_menu.set_name ("ArdourContextMenu");
+ connections.push_back (gain_automation_style_button.signal_button_press_event().connect (mem_fun(*this, &GainMeterBase::gain_automation_style_button_event), false));
+ connections.push_back (gain_automation_state_button.signal_button_press_event().connect (mem_fun(*this, &GainMeterBase::gain_automation_state_button_event), false));
- gain_automation_style_button.signal_button_press_event().connect (mem_fun(*this, &GainMeterBase::gain_automation_style_button_event), false);
- gain_automation_state_button.signal_button_press_event().connect (mem_fun(*this, &GainMeterBase::gain_automation_state_button_event), false);
-
- r->gain_control()->alist()->automation_state_changed.connect (mem_fun(*this, &GainMeter::gain_automation_state_changed));
- r->gain_control()->alist()->automation_style_changed.connect (mem_fun(*this, &GainMeter::gain_automation_style_changed));
+ connections.push_back (r->gain_control()->alist()->automation_state_changed.connect (mem_fun(*this, &GainMeter::gain_automation_state_changed)));
+ connections.push_back (r->gain_control()->alist()->automation_style_changed.connect (mem_fun(*this, &GainMeter::gain_automation_style_changed)));
gain_automation_state_changed ();
}
}
- _io->gain_control()->Changed.connect (mem_fun(*this, &GainMeterBase::gain_changed));
-
- gain_adjustment.signal_value_changed().connect (mem_fun(*this, &GainMeterBase::gain_adjusted));
- peak_display.signal_button_release_event().connect (mem_fun(*this, &GainMeterBase::peak_button_release), false);
- gain_display.signal_key_press_event().connect (mem_fun(*this, &GainMeterBase::gain_key_press), false);
+ connections.push_back (_io->gain_control()->Changed.connect (mem_fun(*this, &GainMeterBase::gain_changed)));
gain_changed ();
show_gain ();
-
update_gain_sensitive ();
-
- ResetAllPeakDisplays.connect (mem_fun(*this, &GainMeterBase::reset_peak_display));
- ResetGroupPeakDisplays.connect (mem_fun(*this, &GainMeterBase::reset_group_peak_display));
-
- UI::instance()->theme_changed.connect (mem_fun(*this, &GainMeterBase::on_theme_changed));
- ColorsChanged.connect (bind(mem_fun (*this, &GainMeterBase::color_handler), false));
- DPIReset.connect (bind(mem_fun (*this, &GainMeterBase::color_handler), true));
-}
-
-GainMeterBase::~GainMeterBase ()
-{
- if (meter_menu) {
- delete meter_menu;
- }
-
- if (level_meter) {
- delete level_meter;
- }
}
void
@@ -752,10 +754,9 @@ GainMeterBase::on_theme_changed()
style_changed = true;
}
-GainMeter::GainMeter (boost::shared_ptr<IO> io, Session& s)
- : GainMeterBase (io, s, slider, false)
+GainMeter::GainMeter (Session& s)
+ : GainMeterBase (s, slider, false)
{
-
gain_display_box.set_homogeneous (true);
gain_display_box.set_spacing (2);
gain_display_box.pack_start (gain_display, true, true);
@@ -785,6 +786,31 @@ GainMeter::GainMeter (boost::shared_ptr<IO> io, Session& s)
hbox.set_spacing (2);
hbox.pack_start (*fader_vbox, true, true);
+ set_spacing (2);
+
+ pack_start (gain_display_box, Gtk::PACK_SHRINK);
+ pack_start (hbox, Gtk::PACK_SHRINK);
+
+ meter_metric_area.signal_expose_event().connect (mem_fun(*this, &GainMeter::meter_metrics_expose));
+}
+
+void
+GainMeter::set_io (boost::shared_ptr<IO> io)
+{
+ if (level_meter->get_parent()) {
+ hbox.remove (*level_meter);
+ }
+
+ if (peak_display.get_parent()) {
+ gain_display_box.remove (peak_display);
+ }
+
+ if (gain_automation_state_button.get_parent()) {
+ fader_vbox->remove (gain_automation_state_button);
+ }
+
+ GainMeterBase::set_io (io);
+
boost::shared_ptr<Route> r;
if ((r = boost::dynamic_pointer_cast<Route> (_io)) != 0) {
@@ -801,16 +827,8 @@ GainMeter::GainMeter (boost::shared_ptr<IO> io, Session& s)
fader_vbox->pack_start (gain_automation_state_button, false, false, 0);
}
}
-
- set_spacing (2);
-
- pack_start (gain_display_box, Gtk::PACK_SHRINK);
- pack_start (hbox, Gtk::PACK_SHRINK);
-
- meter_metric_area.signal_expose_event().connect (mem_fun(*this, &GainMeter::meter_metrics_expose));
}
-
int
GainMeter::get_gm_width ()
{
diff --git a/gtk2_ardour/gain_meter.h b/gtk2_ardour/gain_meter.h
index d493edf0fb..820e9ef0e7 100644
--- a/gtk2_ardour/gain_meter.h
+++ b/gtk2_ardour/gain_meter.h
@@ -58,10 +58,12 @@ namespace Gtk {
class GainMeterBase : virtual public sigc::trackable
{
public:
- GainMeterBase (boost::shared_ptr<ARDOUR::IO>, ARDOUR::Session&, const Glib::RefPtr<Gdk::Pixbuf>& pix,
+ GainMeterBase ( ARDOUR::Session&, const Glib::RefPtr<Gdk::Pixbuf>& pix,
bool horizontal);
virtual ~GainMeterBase ();
+ virtual void set_io (boost::shared_ptr<ARDOUR::IO>);
+
void update_gain_sensitive ();
void update_meters ();
@@ -82,6 +84,7 @@ class GainMeterBase : virtual public sigc::trackable
friend class MixerStrip;
boost::shared_ptr<ARDOUR::IO> _io;
ARDOUR::Session& _session;
+ std::vector<sigc::connection> connections;
bool ignore_toggle;
bool next_release_selects;
@@ -169,9 +172,11 @@ class GainMeterBase : virtual public sigc::trackable
class GainMeter : public GainMeterBase, public Gtk::VBox
{
public:
- GainMeter (boost::shared_ptr<ARDOUR::IO>, ARDOUR::Session&);
+ GainMeter (ARDOUR::Session&);
~GainMeter () {}
+ void set_io (boost::shared_ptr<ARDOUR::IO>);
+
int get_gm_width ();
void setup_meters (int len=0);
diff --git a/gtk2_ardour/level_meter.cc b/gtk2_ardour/level_meter.cc
index f6e3731aac..cd7308cfa2 100644
--- a/gtk2_ardour/level_meter.cc
+++ b/gtk2_ardour/level_meter.cc
@@ -58,9 +58,8 @@ using namespace std;
//sigc::signal<void,RouteGroup*> LevelMeter::ResetGroupPeakDisplays;
-LevelMeter::LevelMeter (boost::shared_ptr<IO> io, Session& s)
- : _io (io),
- _session (s)
+LevelMeter::LevelMeter (Session& s)
+ : _session (s)
{
set_spacing (1);
@@ -85,6 +84,12 @@ LevelMeter::~LevelMeter ()
}
}
+void
+LevelMeter::set_io (boost::shared_ptr<IO> io)
+{
+ _io = io;
+}
+
float
LevelMeter::update_meters ()
{
@@ -144,6 +149,10 @@ LevelMeter::hide_all_meters ()
void
LevelMeter::setup_meters (int len, int initial_width)
{
+ if (!_io) {
+ return; /* do it later */
+ }
+
uint32_t nmeters = _io->n_outputs().n_total();
regular_meter_width = initial_width;
diff --git a/gtk2_ardour/level_meter.h b/gtk2_ardour/level_meter.h
index ce0aa125cb..521b9d1d8f 100644
--- a/gtk2_ardour/level_meter.h
+++ b/gtk2_ardour/level_meter.h
@@ -56,9 +56,11 @@ namespace Gtk {
class LevelMeter : public Gtk::HBox
{
public:
- LevelMeter (boost::shared_ptr<ARDOUR::IO>, ARDOUR::Session&);
+ LevelMeter (ARDOUR::Session&);
~LevelMeter ();
+ virtual void set_io (boost::shared_ptr<ARDOUR::IO> io);
+
void update_gain_sensitive ();
float update_meters ();
diff --git a/gtk2_ardour/main.cc b/gtk2_ardour/main.cc
index edbdcd58cb..aecbc1f634 100644
--- a/gtk2_ardour/main.cc
+++ b/gtk2_ardour/main.cc
@@ -117,7 +117,10 @@ fixup_bundle_environment ()
Glib::ustring path;
const char *cstr = getenv ("PATH");
- /* ensure that we find any bundled executables (e.g. JACK) */
+ /* ensure that we find any bundled executables (e.g. JACK),
+ and find them before any instances of the same name
+ elsewhere in PATH
+ */
path = dir_path;
if (cstr) {
@@ -152,8 +155,10 @@ fixup_bundle_environment ()
if (cstr) {
path = cstr;
path += ':';
+ } else {
+ path = "";
}
- path = dir_path;
+ path += dir_path;
path += "/../Plugins";
setenv ("LADSPA_PATH", path.c_str(), 1);
@@ -162,8 +167,10 @@ fixup_bundle_environment ()
if (cstr) {
path = cstr;
path += ':';
+ } else {
+ path = "";
}
- path = dir_path;
+ path += dir_path;
path += "/../Frameworks";
setenv ("VAMP_PATH", path.c_str(), 1);
@@ -172,8 +179,10 @@ fixup_bundle_environment ()
if (cstr) {
path = cstr;
path += ':';
+ } else {
+ path = "";
}
- path = dir_path;
+ path += dir_path;
path += "/../Surfaces";
setenv ("ARDOUR_CONTROL_SURFACE_PATH", path.c_str(), 1);
@@ -182,8 +191,10 @@ fixup_bundle_environment ()
if (cstr) {
path = cstr;
path += ':';
+ } else {
+ path = "";
}
- path = dir_path;
+ path += dir_path;
path += "/../Plugins";
setenv ("LV2_PATH", path.c_str(), 1);
diff --git a/gtk2_ardour/matrix.cc b/gtk2_ardour/matrix.cc
new file mode 100644
index 0000000000..db16362ca0
--- /dev/null
+++ b/gtk2_ardour/matrix.cc
@@ -0,0 +1,427 @@
+#include <gtkmm.h>
+#include <cairo/cairo.h>
+#include <stdlib.h>
+#include <iostream>
+#include <algorithm>
+#include <stdint.h>
+#include <cmath>
+#include <map>
+#include <vector>
+
+#include "matrix.h"
+
+using namespace std;
+using namespace Gtk;
+using namespace ARDOUR;
+
+Matrix::Matrix ()
+{
+ alloc_width = 0;
+ alloc_height = 0;
+ line_width = 0;
+ line_height = 0;
+ labels_y_shift = 0;
+ labels_x_shift = 0;
+ arc_radius = 0;
+ xstep = 0;
+ ystep = 0;
+ pixmap = 0;
+ drawn = false;
+ angle_radians = M_PI/4.0;
+ motion_x = -1;
+ motion_y = -1;
+
+ add_events (Gdk::POINTER_MOTION_MASK|Gdk::LEAVE_NOTIFY_MASK);
+}
+
+void
+Matrix::set_ports (const list<string>& ports)
+{
+ ours = ports;
+ reset_size ();
+}
+
+void
+Matrix::add_group (PortGroup& pg)
+{
+ for (vector<string>::const_iterator s = pg.ports.begin(); s != pg.ports.end(); ++s) {
+ others.push_back (OtherPort (*s, pg));
+ }
+ reset_size ();
+}
+
+void
+Matrix::remove_group (PortGroup& pg)
+{
+ for (list<OtherPort>::iterator o = others.begin(); o != others.end(); ) {
+ if (&(*o).group() == &pg) {
+ o = others.erase (o);
+ } else {
+ ++o;
+ }
+ }
+ reset_size ();
+}
+
+void
+Matrix::hide_group (PortGroup& pg)
+{
+ reset_size();
+}
+
+void
+Matrix::show_group (PortGroup& pg)
+{
+ reset_size ();
+}
+
+void
+Matrix::setup_nodes ()
+{
+ int n, x, y;
+ list<string>::iterator m;
+ list<OtherPort>::iterator s;
+
+ for (vector<MatrixNode*>::iterator p = nodes.begin(); p != nodes.end(); ++p) {
+ delete *p;
+ }
+ nodes.clear ();
+
+ list<OtherPort>::size_type visible_others = 0;
+
+ for (list<OtherPort>::iterator s = others.begin(); s != others.end(); ++s) {
+ if ((*s).visible()) {
+ ++visible_others;
+ }
+ }
+
+ nodes.assign (ours.size() * visible_others, 0);
+
+ for (n = 0, y = 0, m = ours.begin(); m != ours.end(); ++m, ++y) {
+ for (x = 0, s = others.begin(); s != others.end(); ++s) {
+ if ((*s).visible()) {
+ nodes[n] = new MatrixNode (*m, *s, x, y);
+ n++;
+ x++;
+ }
+ }
+ }
+}
+
+void
+Matrix::reset_size ()
+{
+ list<OtherPort>::size_type visible_others = 0;
+
+ for (list<OtherPort>::iterator s = others.begin(); s != others.end(); ++s) {
+ if ((*s).visible()) {
+ ++visible_others;
+ }
+ }
+
+ border = 10;
+
+ if (alloc_width > line_width) {
+
+ xstep = (alloc_width - labels_x_shift - (2 * border) - (2 * arc_radius)) / visible_others;
+ line_width = xstep * (others.size() - 1);
+
+ ystep = (alloc_height - labels_y_shift - (2 * border) - (2 * arc_radius)) / (ours.size() - 1);
+ line_height = ystep * (ours.size() - 1);
+
+ } else {
+
+ xstep = 20;
+ ystep = 20;
+
+ line_height = (ours.size() - 1) * ystep;
+ line_width = visible_others * xstep;
+ }
+
+ int half_step = min (ystep/2,xstep/2);
+ if (half_step > 3) {
+ arc_radius = half_step - 5;
+ } else {
+ arc_radius = 3;
+ }
+
+ arc_radius = min (arc_radius, 10);
+
+ /* scan all the port names that will be rotated, and compute
+ how much space we need for them
+ */
+
+ float w = 0;
+ float w1;
+ float h = 0;
+ cairo_text_extents_t extents;
+ cairo_t* cr;
+ GdkPixmap* pm;
+
+ pm = gdk_pixmap_new (NULL, 1, 1, 24);
+ gdk_drawable_set_colormap (pm, gdk_colormap_get_system());
+
+ cr = gdk_cairo_create (pm);
+
+ for (list<OtherPort>::iterator s = others.begin(); s != others.end(); ++s) {
+ if ((*s).visible()) {
+ cairo_text_extents (cr, (*s).name().c_str(), &extents);
+ w = max ((float) extents.width, w);
+ h = max ((float) extents.height, h);
+ }
+ }
+
+ cairo_destroy (cr);
+ gdk_pixmap_unref (pm);
+
+ /* transform */
+
+ w = fabs (w * cos (angle_radians) + h * sin (angle_radians));
+ h = fabs (w * sin (angle_radians) + h * cos (angle_radians));
+
+ labels_y_shift = (int) ceil (h) + 10;
+ labels_x_shift = (int) ceil (w);
+
+ setup_nodes ();
+}
+
+bool
+Matrix::on_motion_notify_event (GdkEventMotion* ev)
+{
+ motion_x = ev->x;
+ motion_y = ev->y;
+ queue_draw ();
+ return false;
+}
+
+bool
+Matrix::on_leave_notify_event (GdkEventCrossing *ev)
+{
+ motion_x = -1;
+ motion_y = -1;
+ queue_draw ();
+ return false;
+}
+
+void
+Matrix::on_size_request (Requisition* req)
+{
+ req->width = labels_x_shift + line_width + (2*border) + (2*arc_radius);
+ req->height = labels_y_shift + line_height + (2*border) + (2*arc_radius);
+}
+
+MatrixNode*
+Matrix::get_node (int32_t x, int32_t y)
+{
+ int half_xstep = xstep / 2;
+ int half_ystep = ystep / 2;
+
+ x -= labels_x_shift - border;
+ if (x < half_xstep) {
+ return 0;
+ }
+
+ y -= labels_y_shift - border;
+ if (y < half_ystep) {
+ return 0;
+ }
+
+ x = (x - half_xstep) / xstep;
+ y = (y - half_ystep) / ystep;
+
+ x = y*ours.size() + x;
+
+ if (x >= nodes.size()) {
+ return 0;
+ }
+
+ return nodes[x];
+}
+
+bool
+Matrix::on_button_press_event (GdkEventButton* ev)
+{
+ MatrixNode* node;
+
+ if ((node = get_node (ev->x, ev->y)) != 0) {
+ cerr << "Event in node " << node->our_name() << " x " << node->their_name () << endl;
+ node->set_connected (!node->connected());
+ drawn = false;
+ queue_draw();
+ }
+}
+
+void
+Matrix::alloc_pixmap ()
+{
+ if (pixmap) {
+ gdk_pixmap_unref (pixmap);
+ }
+
+ pixmap = gdk_pixmap_new (get_window()->gobj(),
+ alloc_width,
+ alloc_height,
+ -1);
+
+ drawn = false;
+}
+
+void
+Matrix::on_size_allocate (Allocation& alloc)
+{
+ EventBox::on_size_allocate (alloc);
+
+ alloc_width = alloc.get_width();
+ alloc_height = alloc.get_height();
+
+ if (is_realized()) {
+ alloc_pixmap ();
+ reset_size ();
+#ifdef MATRIX_USE_BACKING_PIXMAP
+ redraw (pixmap, 0, 0, alloc_width, alloc_height);
+#endif
+ }
+}
+
+void
+Matrix::on_realize ()
+{
+ EventBox::on_realize ();
+ alloc_pixmap ();
+}
+
+void
+Matrix::redraw (GdkDrawable* drawable, GdkRectangle* rect)
+{
+ list<string>::iterator o;
+ list<OtherPort>::iterator t;
+ int x, y;
+ uint32_t top_shift, bottom_shift, left_shift, right_shift;
+ cairo_t* cr;
+
+ cr = gdk_cairo_create (drawable);
+
+ cairo_set_source_rgb (cr, 0.83, 0.83, 0.83);
+ cairo_rectangle (cr, rect->x, rect->y, rect->width, rect->height);
+ cairo_fill (cr);
+
+ cairo_set_line_width (cr, 0.5);
+
+ top_shift = labels_y_shift + border;
+ left_shift = labels_x_shift + border;
+ bottom_shift = 0;
+ right_shift = 0;
+
+ /* horizontal grid lines and side labels */
+
+ for (y = top_shift, o = ours.begin(); o != ours.end(); ++o, y += ystep) {
+
+ cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+ cairo_move_to (cr, left_shift, y);
+ cairo_line_to (cr, left_shift+line_width, y);
+ cairo_stroke (cr);
+#if 0
+
+ cairo_text_extents_t extents;
+ cairo_text_extents (cr, (*o).c_str(),&extents);
+ cairo_move_to (cr, border, y+extents.height/2);
+ cairo_show_text (cr, (*o).c_str());
+#endif
+
+ }
+
+ /* vertical grid lines and rotated labels*/
+
+ for (x = left_shift, t = others.begin(); t != others.end(); ++t, x += xstep) {
+
+ cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+ cairo_move_to (cr, x, top_shift);
+ cairo_line_to (cr, x, top_shift+line_height);
+ cairo_stroke (cr);
+
+ cairo_move_to (cr, x-left_shift+12, border);
+ cairo_set_source_rgb (cr, 0, 0, 1.0);
+
+ cairo_save (cr);
+ cairo_rotate (cr, angle_radians);
+ cairo_show_text (cr, (*t).name().c_str());
+ cairo_restore (cr);
+
+ }
+
+ /* nodes */
+
+ for (vector<MatrixNode*>::iterator n = nodes.begin(); n != nodes.end(); ++n) {
+
+ x = (*n)->x() * xstep;
+ y = (*n)->y() * ystep;
+
+ cairo_new_path (cr);
+
+ if (arc_radius) {
+ cairo_arc (cr, left_shift+x, top_shift+y, arc_radius, 0, 2.0 * M_PI);
+ if ((*n)->connected()) {
+ cairo_set_source_rgba (cr, 1.0, 0, 0, 1.0);
+ cairo_stroke (cr);
+ } else {
+ cairo_set_source_rgba (cr, 1.0, 0, 0, 0.7);
+ cairo_fill (cr);
+ }
+ }
+ }
+
+ /* motion indicators */
+
+ if (motion_x >= left_shift && motion_y >= top_shift) {
+
+ int col_left = left_shift + ((motion_x - left_shift) / xstep) * xstep;
+ int row_top = top_shift + ((motion_y - top_shift) / ystep) * ystep;
+
+ cairo_set_line_width (cr, 5);
+ cairo_set_source_rgba (cr, 1.0, 0.0, 0.0, 0.3);
+
+ /* horizontal (row) */
+
+ cairo_line_to (cr, left_shift, row_top);
+ cairo_line_to (cr, left_shift + line_width, row_top);
+ cairo_stroke (cr);
+
+ /* vertical (col) */
+
+ cairo_move_to (cr, col_left, top_shift);
+ cairo_line_to (cr, col_left, top_shift + line_height);
+ cairo_stroke (cr);
+ }
+
+ cairo_destroy (cr);
+
+#ifdef MATRIX_USE_BACKING_PIXMAP
+ drawn = true;
+#endif
+}
+
+bool
+Matrix::on_expose_event (GdkEventExpose* event)
+{
+#ifdef MATRIX_USE_BACKING_PIXMAP
+ if (!drawn) {
+ redraw (pixmap, 0, 0, alloc_width, alloc_height);
+ }
+
+ gdk_draw_drawable (get_window()->gobj(),
+ get_style()->get_fg_gc (STATE_NORMAL)->gobj(),
+ pixmap,
+ event->area.x,
+ event->area.y,
+ event->area.x,
+ event->area.y,
+ event->area.width,
+ event->area.height);
+#else
+ redraw (get_window()->gobj(), &event->area);
+#endif
+
+
+
+ return true;
+}
diff --git a/gtk2_ardour/matrix.h b/gtk2_ardour/matrix.h
new file mode 100644
index 0000000000..f859a881c7
--- /dev/null
+++ b/gtk2_ardour/matrix.h
@@ -0,0 +1,106 @@
+#ifndef __gtk_ardour_matrix_h__
+#define __gtk_ardour_matrix_h__
+
+#include <list>
+#include <vector>
+#include <string>
+#include <stdint.h>
+
+#include <gtkmm/eventbox.h>
+#include <gtkmm/widget.h>
+
+#include "port_group.h"
+
+class OtherPort {
+public:
+ OtherPort (const std::string& n, PortGroup& g)
+ : _name (n), _group (g) {}
+
+ std::string name() const { return _name; }
+ PortGroup& group() const { return _group; }
+ bool visible() const { return _group.visible; }
+
+public:
+ std::string _name;
+ PortGroup& _group;
+};
+
+class MatrixNode {
+ public:
+ MatrixNode (std::string a, OtherPort o, int32_t x, int32_t y)
+ : _name (a), them (o), _connected (random()%3), _x(x), _y(y) {}
+ ~MatrixNode() {}
+
+ PortGroup& get_group() const { return them.group(); }
+
+ std::string our_name() const { return _name; }
+ std::string their_name() const { return them.name(); }
+
+ bool connected() const { return _connected; }
+ void set_connected (bool yn) { _connected = yn; }
+ int32_t x() const { return _x; }
+ int32_t y() const { return _y; }
+
+ private:
+ std::string _name;
+ OtherPort them;
+ bool _connected;
+ int32_t _x;
+ int32_t _y;
+};
+
+class Matrix : public Gtk::EventBox
+{
+ public:
+ Matrix();
+
+ void set_ports (const std::list<std::string>&);
+ void add_group (PortGroup&);
+ void remove_group (PortGroup&);
+ void hide_group (PortGroup&);
+ void show_group (PortGroup&);
+
+ int row_spacing () const { return xstep; }
+
+ protected:
+ bool on_button_press_event (GdkEventButton* ev);
+ bool on_expose_event (GdkEventExpose* ev);
+ void on_size_allocate (Gtk::Allocation&);
+ void on_size_request (Gtk::Requisition*);
+ void on_realize ();
+ bool on_motion_notify_event (GdkEventMotion*);
+ bool on_leave_notify_event (GdkEventCrossing*);
+
+ MatrixNode* get_node (int32_t x, int32_t y);
+
+private:
+ int height;
+ int width;
+ int alloc_width;
+ int alloc_height;
+ bool drawn;
+ int labels_y_shift;
+ int labels_x_shift;
+ float angle_radians;
+ int border;
+ int ystep;
+ int xstep;
+ uint32_t line_height;
+ uint32_t line_width;
+ int arc_radius;
+ int32_t motion_x;
+ int32_t motion_y;
+
+ std::list<std::string> ours;
+ std::list<OtherPort> others;
+ std::vector<MatrixNode*> nodes;
+
+ void reset_size ();
+ void redraw (GdkDrawable*, GdkRectangle*);
+ void alloc_pixmap ();
+ void setup_nodes ();
+
+ GdkPixmap* pixmap;
+};
+
+#endif /* __gtk_ardour_matrix_h__ */
diff --git a/gtk2_ardour/mixer_strip.cc b/gtk2_ardour/mixer_strip.cc
index 373babf417..1a357dc002 100644
--- a/gtk2_ardour/mixer_strip.cc
+++ b/gtk2_ardour/mixer_strip.cc
@@ -82,34 +82,56 @@ speed_printer (char buf[32], Gtk::Adjustment& adj, void* arg)
}
#endif
-MixerStrip::MixerStrip (Mixer_UI& mx, Session& sess, boost::shared_ptr<Route> rt, bool in_mixer)
- : AxisView(sess),
- RouteUI (rt, sess, _("Mute"), _("Solo"), _("Record")),
- _mixer(mx),
- _mixer_owned (in_mixer),
- pre_processor_box (PreFader, sess, rt, mx.plugin_selector(), mx.selection(), in_mixer),
- post_processor_box (PostFader, sess, rt, mx.plugin_selector(), mx.selection(), in_mixer),
- gpm (_route, sess),
- panners (_route, sess),
- button_table (3, 2),
- middle_button_table (1, 2),
- bottom_button_table (1, 2),
- meter_point_label (_("pre")),
- comment_button (_("Comments")),
- speed_adjustment (1.0, 0.001, 4.0, 0.001, 0.1),
- speed_spinner (&speed_adjustment, "MixerStripSpeedBase", true)
+MixerStrip::MixerStrip (Mixer_UI& mx, Session& sess, bool in_mixer)
+ : AxisView(sess)
+ , RouteUI (sess, _("Mute"), _("Solo"), _("Record"))
+ ,_mixer(mx)
+ , _mixer_owned (in_mixer)
+ , pre_processor_box (PreFader, sess, mx.plugin_selector(), mx.selection(), in_mixer)
+ , post_processor_box (PostFader, sess, mx.plugin_selector(), mx.selection(), in_mixer)
+ , gpm (sess)
+ , panners (sess)
+ , button_table (3, 2)
+ , middle_button_table (1, 2)
+ , bottom_button_table (1, 2)
+ , meter_point_label (_("pre"))
+ , comment_button (_("Comments"))
+ , speed_adjustment (1.0, 0.001, 4.0, 0.001, 0.1)
+ , speed_spinner (&speed_adjustment, "MixerStripSpeedBase", true)
+
+{
+ init ();
+}
+MixerStrip::MixerStrip (Mixer_UI& mx, Session& sess, boost::shared_ptr<Route> rt, bool in_mixer)
+ : AxisView(sess)
+ , RouteUI (sess, _("Mute"), _("Solo"), _("Record"))
+ ,_mixer(mx)
+ , _mixer_owned (in_mixer)
+ , pre_processor_box (PreFader, sess, mx.plugin_selector(), mx.selection(), in_mixer)
+ , post_processor_box (PostFader, sess, mx.plugin_selector(), mx.selection(), in_mixer)
+ , gpm (sess)
+ , panners (sess)
+ , button_table (3, 2)
+ , middle_button_table (1, 2)
+ , bottom_button_table (1, 2)
+ , meter_point_label (_("pre"))
+ , comment_button (_("Comments"))
+ , speed_adjustment (1.0, 0.001, 4.0, 0.001, 0.1)
+ , speed_spinner (&speed_adjustment, "MixerStripSpeedBase", true)
+
{
- if (set_color_from_route()) {
- set_color (unique_random_color());
- }
+ init ();
+ set_route (rt);
+}
+void
+MixerStrip::init ()
+{
input_selector = 0;
output_selector = 0;
group_menu = 0;
- if (!_route->is_hidden()) {
- _marked_for_display = true;
- }
+ _marked_for_display = false;
route_ops_menu = 0;
ignore_comment_edit = false;
ignore_toggle = false;
@@ -118,40 +140,26 @@ MixerStrip::MixerStrip (Mixer_UI& mx, Session& sess, boost::shared_ptr<Route> rt
comment_area = 0;
_width_owner = 0;
- Gtk::Image *width_icon = manage (new Gtk::Image (::get_icon("strip_width")));
- Gtk::Image *hide_icon = manage (new Gtk::Image (::get_icon("hide")));
- width_button.add (*width_icon);
- hide_button.add (*hide_icon);
+ width_button.add (*(manage (new Gtk::Image (::get_icon("strip_width")))));
+ hide_button.add (*(manage (new Gtk::Image (::get_icon("hide")))));
input_label.set_text (_("Input"));
+ ARDOUR_UI::instance()->set_tip (&input_button, _("Click to choose inputs"), "");
input_button.add (input_label);
input_button.set_name ("MixerIOButton");
input_label.set_name ("MixerIOButtonLabel");
output_label.set_text (_("Output"));
+ ARDOUR_UI::instance()->set_tip (&output_button, _("Click to choose outputs"), "");
output_button.add (output_label);
output_button.set_name ("MixerIOButton");
output_label.set_name ("MixerIOButtonLabel");
- _route->meter_change.connect (mem_fun(*this, &MixerStrip::meter_changed));
+ ARDOUR_UI::instance()->set_tip (&meter_point_button, _("Select metering point"), "");
meter_point_button.add (meter_point_label);
meter_point_button.set_name ("MixerStripMeterPreButton");
meter_point_label.set_name ("MixerStripMeterPreButton");
- switch (_route->meter_point()) {
- case MeterInput:
- meter_point_label.set_text (_("input"));
- break;
-
- case MeterPreFader:
- meter_point_label.set_text (_("pre"));
- break;
-
- case MeterPostFader:
- meter_point_label.set_text (_("post"));
- break;
- }
-
/* TRANSLATORS: this string should be longest of the strings
used to describe meter points. In english, it's "input".
*/
@@ -183,56 +191,18 @@ MixerStrip::MixerStrip (Mixer_UI& mx, Session& sess, boost::shared_ptr<Route> rt
bottom_button_table.set_homogeneous (true);
bottom_button_table.attach (group_button, 0, 1, 0, 1);
- if (is_audio_track()) {
- boost::shared_ptr<AudioTrack> at = audio_track();
-
- at->FreezeChange.connect (mem_fun(*this, &MixerStrip::map_frozen));
-
-#ifdef VARISPEED_IN_MIXER_STRIP
- speed_adjustment.signal_value_changed().connect (mem_fun(*this, &MixerStrip::speed_adjustment_changed));
-
- speed_frame.set_name ("BaseFrame");
- speed_frame.set_shadow_type (Gtk::SHADOW_IN);
- speed_frame.add (speed_spinner);
-
- speed_spinner.set_print_func (speed_printer, 0);
-
- ARDOUR_UI::instance()->tooltips().set_tip (speed_spinner, _("Varispeed"));
-
- button_table.attach (speed_frame, 0, 2, 5, 6);
-#endif /* VARISPEED_IN_MIXER_STRIP */
-
- }
-
- if(rec_enable_button) {
- rec_enable_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::rec_enable_press), false);
- rec_enable_button->signal_button_release_event().connect (mem_fun(*this, &RouteUI::rec_enable_release));
-
- rec_enable_button->set_name ("MixerRecordEnableButton");
- button_table.attach (*rec_enable_button, 0, 2, 2, 3);
- }
-
name_button.add (name_label);
name_button.set_name ("MixerNameButton");
Gtkmm2ext::set_size_request_to_display_given_text (name_button, "longest label", 2, 2);
name_label.set_name ("MixerNameButtonLabel");
- if (_route->phase_invert()) {
- name_label.set_text (X_("Ø ") + name_label.get_text());
- } else {
- name_label.set_text (_route->name());
- }
-
+ ARDOUR_UI::instance()->set_tip (&group_button, _("Mix group"), "");
group_button.add (group_label);
group_button.set_name ("MixerGroupButton");
group_label.set_name ("MixerGroupButtonLabel");
comment_button.set_name ("MixerCommentButton");
- ARDOUR_UI::instance()->tooltips().set_tip (comment_button, _route->comment()=="" ?
- _("Click to Add/Edit Comments"):
- _route->comment());
-
comment_button.signal_clicked().connect (mem_fun(*this, &MixerStrip::comment_button_clicked));
global_vpacker.set_border_width (0);
@@ -269,19 +239,6 @@ MixerStrip::MixerStrip (Mixer_UI& mx, Session& sess, boost::shared_ptr<Route> rt
global_vpacker.pack_start (output_button, Gtk::PACK_SHRINK);
global_vpacker.pack_start (comment_button, Gtk::PACK_SHRINK);
- if (route()->is_master() || route()->is_control()) {
-
- if (scrollbar_height == 0) {
- HScrollbar scrollbar;
- Gtk::Requisition requisition(scrollbar.size_request ());
- scrollbar_height = requisition.height;
- }
-
- EventBox* spacer = manage (new EventBox);
- spacer->set_size_request (-1, scrollbar_height);
- global_vpacker.pack_start (*spacer, false, false);
- }
-
global_frame.add (global_vpacker);
global_frame.set_shadow_type (Gtk::SHADOW_IN);
global_frame.set_name ("BaseFrame");
@@ -298,22 +255,6 @@ MixerStrip::MixerStrip (Mixer_UI& mx, Session& sess, boost::shared_ptr<Route> rt
_session.engine().Stopped.connect (mem_fun(*this, &MixerStrip::engine_stopped));
_session.engine().Running.connect (mem_fun(*this, &MixerStrip::engine_running));
- _route->input_changed.connect (mem_fun(*this, &MixerStrip::input_changed));
- _route->output_changed.connect (mem_fun(*this, &MixerStrip::output_changed));
- _route->mute_changed.connect (mem_fun(*this, &RouteUI::mute_changed));
- _route->solo_changed.connect (mem_fun(*this, &RouteUI::solo_changed));
- _route->solo_safe_changed.connect (mem_fun(*this, &RouteUI::solo_changed));
- _route->mix_group_changed.connect (mem_fun(*this, &MixerStrip::mix_group_changed));
- _route->panner().Changed.connect (mem_fun(*this, &MixerStrip::connect_to_pan));
-
- if (is_audio_track()) {
- audio_track()->DiskstreamChanged.connect (mem_fun(*this, &MixerStrip::diskstream_changed));
- get_diskstream()->SpeedChanged.connect (mem_fun(*this, &MixerStrip::speed_changed));
- }
-
- _route->NameChanged.connect (mem_fun(*this, &RouteUI::name_changed));
- _route->comment_changed.connect (mem_fun(*this, &MixerStrip::comment_changed));
- _route->gui_changed.connect (mem_fun(*this, &MixerStrip::route_gui_changed));
input_button.signal_button_press_event().connect (mem_fun(*this, &MixerStrip::input_press), false);
output_button.signal_button_press_event().connect (mem_fun(*this, &MixerStrip::output_press), false);
@@ -323,11 +264,22 @@ MixerStrip::MixerStrip (Mixer_UI& mx, Session& sess, boost::shared_ptr<Route> rt
mute_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::mute_press), false);
mute_button->signal_button_release_event().connect (mem_fun(*this, &RouteUI::mute_release), false);
+ /* we don't need this if its not an audio track, but we don't know that yet and it doesn't
+ hurt (much).
+ */
+
+ rec_enable_button->set_name ("MixerRecordEnableButton");
+ rec_enable_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::rec_enable_press), false);
+ rec_enable_button->signal_button_release_event().connect (mem_fun(*this, &RouteUI::rec_enable_release));
+
name_button.signal_button_press_event().connect (mem_fun(*this, &MixerStrip::name_button_button_press), false);
group_button.signal_button_press_event().connect (mem_fun(*this, &MixerStrip::select_mix_group), false);
_width = (Width) -1;
- set_stuff_from_route ();
+
+ /* start off as a passthru strip. we'll correct this, if necessary,
+ in update_diskstream_display().
+ */
/* start off as a passthru strip. we'll correct this, if necessary,
in update_diskstream_display().
@@ -338,6 +290,126 @@ MixerStrip::MixerStrip (Mixer_UI& mx, Session& sess, boost::shared_ptr<Route> rt
else
set_name ("AudioTrackStripBase");
+ add_events (Gdk::BUTTON_RELEASE_MASK);
+}
+
+MixerStrip::~MixerStrip ()
+{
+ GoingAway(); /* EMIT_SIGNAL */
+
+ if (input_selector) {
+ delete input_selector;
+ }
+
+ if (output_selector) {
+ delete output_selector;
+ }
+}
+
+void
+MixerStrip::set_route (boost::shared_ptr<Route> rt)
+{
+ if (rec_enable_button->get_parent()) {
+ button_table.remove (*rec_enable_button);
+ }
+
+#ifdef VARISPEED_IN_MIXER_STRIP
+ if (speed_frame->get_parent()) {
+ button_table.remove (*speed_frame);
+ }
+#endif
+
+ RouteUI::set_route (rt);
+
+ panners.set_io (rt);
+ gpm.set_io (rt);
+ pre_processor_box.set_route (rt);
+ post_processor_box.set_route (rt);
+
+ if (set_color_from_route()) {
+ set_color (unique_random_color());
+ }
+
+ if (_mixer_owned && (route()->is_master() || route()->is_control())) {
+
+ if (scrollbar_height == 0) {
+ HScrollbar scrollbar;
+ Gtk::Requisition requisition(scrollbar.size_request ());
+ scrollbar_height = requisition.height;
+ }
+
+ EventBox* spacer = manage (new EventBox);
+ spacer->set_size_request (-1, scrollbar_height);
+ global_vpacker.pack_start (*spacer, false, false);
+ }
+
+ if (is_audio_track()) {
+
+ boost::shared_ptr<AudioTrack> at = audio_track();
+
+ connections.push_back (at->FreezeChange.connect (mem_fun(*this, &MixerStrip::map_frozen)));
+
+#ifdef VARISPEED_IN_MIXER_STRIP
+ speed_adjustment.signal_value_changed().connect (mem_fun(*this, &MixerStrip::speed_adjustment_changed));
+
+ speed_frame.set_name ("BaseFrame");
+ speed_frame.set_shadow_type (Gtk::SHADOW_IN);
+ speed_frame.add (speed_spinner);
+
+ speed_spinner.set_print_func (speed_printer, 0);
+
+ ARDOUR_UI::instance()->tooltips().set_tip (speed_spinner, _("Varispeed"));
+
+ button_table.attach (speed_frame, 0, 2, 5, 6);
+#endif /* VARISPEED_IN_MIXER_STRIP */
+
+ button_table.attach (*rec_enable_button, 0, 2, 2, 3);
+ }
+
+ if (_route->phase_invert()) {
+ name_label.set_text (X_("Ø ") + name_label.get_text());
+ } else {
+ name_label.set_text (_route->name());
+ }
+
+ switch (_route->meter_point()) {
+ case MeterInput:
+ meter_point_label.set_text (_("input"));
+ break;
+
+ case MeterPreFader:
+ meter_point_label.set_text (_("pre"));
+ break;
+
+ case MeterPostFader:
+ meter_point_label.set_text (_("post"));
+ break;
+ }
+
+ ARDOUR_UI::instance()->tooltips().set_tip (comment_button, _route->comment().empty() ?
+ _("Click to Add/Edit Comments"):
+ _route->comment());
+
+ connections.push_back (_route->meter_change.connect (mem_fun(*this, &MixerStrip::meter_changed)));
+ connections.push_back (_route->input_changed.connect (mem_fun(*this, &MixerStrip::input_changed)));
+ connections.push_back (_route->output_changed.connect (mem_fun(*this, &MixerStrip::output_changed)));
+ connections.push_back (_route->mute_changed.connect (mem_fun(*this, &RouteUI::mute_changed)));
+ connections.push_back (_route->solo_changed.connect (mem_fun(*this, &RouteUI::solo_changed)));
+ connections.push_back (_route->solo_safe_changed.connect (mem_fun(*this, &RouteUI::solo_changed)));
+ connections.push_back (_route->mix_group_changed.connect (mem_fun(*this, &MixerStrip::mix_group_changed)));
+ connections.push_back (_route->panner().Changed.connect (mem_fun(*this, &MixerStrip::connect_to_pan)));
+
+ if (is_audio_track()) {
+ connections.push_back (audio_track()->DiskstreamChanged.connect (mem_fun(*this, &MixerStrip::diskstream_changed)));
+ connections.push_back (get_diskstream()->SpeedChanged.connect (mem_fun(*this, &MixerStrip::speed_changed)));
+ }
+
+ connections.push_back (_route->NameChanged.connect (mem_fun(*this, &RouteUI::name_changed)));
+ connections.push_back (_route->comment_changed.connect (mem_fun(*this, &MixerStrip::comment_changed)));
+ connections.push_back (_route->gui_changed.connect (mem_fun(*this, &MixerStrip::route_gui_changed)));
+
+ set_stuff_from_route ();
+
/* now force an update of all the various elements */
pre_processor_box.update();
@@ -362,11 +434,6 @@ MixerStrip::MixerStrip (Mixer_UI& mx, Session& sess, boost::shared_ptr<Route> rt
add_events (Gdk::BUTTON_RELEASE_MASK);
- whvbox->show();
- hide_icon->show();
- width_icon->show();
- gain_meter_alignment->show_all();
-
pre_processor_box.show();
if (!route()->is_master() && !route()->is_control()) {
@@ -402,19 +469,6 @@ MixerStrip::MixerStrip (Mixer_UI& mx, Session& sess, boost::shared_ptr<Route> rt
show();
}
-MixerStrip::~MixerStrip ()
-{
- GoingAway(); /* EMIT_SIGNAL */
-
- if (input_selector) {
- delete input_selector;
- }
-
- if (output_selector) {
- delete output_selector;
- }
-}
-
void
MixerStrip::set_stuff_from_route ()
{
@@ -796,6 +850,7 @@ MixerStrip::update_output_display ()
break;
}
}
+
gpm.setup_meters ();
panners.setup_pan ();
}
@@ -1292,22 +1347,22 @@ MixerStrip::meter_changed (void *src)
ENSURE_GUI_THREAD (bind (mem_fun(*this, &MixerStrip::meter_changed), src));
switch (_route->meter_point()) {
- case MeterInput:
- meter_point_label.set_text (_("input"));
- break;
-
- case MeterPreFader:
- meter_point_label.set_text (_("pre"));
- break;
+ case MeterInput:
+ meter_point_label.set_text (_("input"));
+ break;
- case MeterPostFader:
- meter_point_label.set_text (_("post"));
- break;
+ case MeterPreFader:
+ meter_point_label.set_text (_("pre"));
+ break;
+
+ case MeterPostFader:
+ meter_point_label.set_text (_("post"));
+ break;
}
gpm.setup_meters ();
- // reset peak when meter point changes
- gpm.reset_peak_display();
- set_width(_width, this);
+ // reset peak when meter point changes
+ gpm.reset_peak_display();
+ set_width(_width, this);
}
diff --git a/gtk2_ardour/mixer_strip.h b/gtk2_ardour/mixer_strip.h
index 63a47974cc..c4c28a06e9 100644
--- a/gtk2_ardour/mixer_strip.h
+++ b/gtk2_ardour/mixer_strip.h
@@ -83,6 +83,7 @@ class MixerStrip : public RouteUI, public Gtk::EventBox
{
public:
MixerStrip (Mixer_UI&, ARDOUR::Session&, boost::shared_ptr<ARDOUR::Route>, bool in_mixer = true);
+ MixerStrip (Mixer_UI&, ARDOUR::Session&, bool in_mixer = true);
~MixerStrip ();
void set_width (Width, void* owner);
@@ -93,6 +94,7 @@ class MixerStrip : public RouteUI, public Gtk::EventBox
void set_embedded (bool);
ARDOUR::RouteGroup* mix_group() const;
+ void set_route (boost::shared_ptr<ARDOUR::Route>);
protected:
friend class Mixer_UI;
@@ -105,6 +107,8 @@ class MixerStrip : public RouteUI, public Gtk::EventBox
private:
Mixer_UI& _mixer;
+ void init ();
+
bool _embedded;
bool _packed;
bool _mixer_owned;
diff --git a/gtk2_ardour/option_editor.cc b/gtk2_ardour/option_editor.cc
index ca598a363f..c261aba649 100644
--- a/gtk2_ardour/option_editor.cc
+++ b/gtk2_ardour/option_editor.cc
@@ -1117,7 +1117,8 @@ OptionEditor::setup_click_editor ()
click_emphasis_path_entry.set_sensitive (true);
click_io_selector = new IOSelector (*session, session->click_io(), false);
- click_gpm = new GainMeter (session->click_io(), *session);
+ click_gpm = new GainMeter (*session);
+ click_gpm->set_io (session->click_io());
click_hpacker.pack_start (*click_io_selector, false, false);
click_hpacker.pack_start (*click_gpm, false, false);
@@ -1173,7 +1174,8 @@ void
OptionEditor::connect_audition_editor ()
{
auditioner_io_selector = new IOSelector (*session, session->the_auditioner(), false);
- auditioner_gpm = new GainMeter (session->the_auditioner(), *session);
+ auditioner_gpm = new GainMeter (*session);
+ auditioner_gpm->set_io (session->the_auditioner());
audition_hpacker.pack_start (*auditioner_io_selector, false, false);
audition_hpacker.pack_start (*auditioner_gpm, false, false);
diff --git a/gtk2_ardour/panner_ui.cc b/gtk2_ardour/panner_ui.cc
index 2be7be73ed..b1273aa460 100644
--- a/gtk2_ardour/panner_ui.cc
+++ b/gtk2_ardour/panner_ui.cc
@@ -47,9 +47,8 @@ using namespace sigc;
const int PannerUI::pan_bar_height = 30;
-PannerUI::PannerUI (boost::shared_ptr<IO> io, Session& s)
- : _io (io),
- _session (s),
+PannerUI::PannerUI (Session& s)
+ : _session (s),
hAdjustment(0.0, 0.0, 0.0),
vAdjustment(0.0, 0.0, 0.0),
panning_viewport(hAdjustment, vAdjustment),
@@ -61,6 +60,8 @@ PannerUI::PannerUI (boost::shared_ptr<IO> io, Session& s)
{
ignore_toggle = false;
pan_menu = 0;
+ pan_astate_menu = 0;
+ pan_astyle_menu = 0;
in_pan_update = false;
pan_automation_style_button.set_name ("MixerAutomationModeButton");
@@ -84,22 +85,6 @@ PannerUI::PannerUI (boost::shared_ptr<IO> io, Session& s)
pan_automation_style_button.unset_flags (Gtk::CAN_FOCUS);
pan_automation_state_button.unset_flags (Gtk::CAN_FOCUS);
- using namespace Menu_Helpers;
- pan_astate_menu.items().push_back (MenuElem (_("Manual"),
- bind (mem_fun (_io->panner(), &Panner::set_automation_state), (AutoState) Off)));
- pan_astate_menu.items().push_back (MenuElem (_("Play"),
- bind (mem_fun (_io->panner(), &Panner::set_automation_state), (AutoState) Play)));
- pan_astate_menu.items().push_back (MenuElem (_("Write"),
- bind (mem_fun (_io->panner(), &Panner::set_automation_state), (AutoState) Write)));
- pan_astate_menu.items().push_back (MenuElem (_("Touch"),
- bind (mem_fun (_io->panner(), &Panner::set_automation_state), (AutoState) Touch)));
-
- pan_astyle_menu.items().push_back (MenuElem (_("Trim")));
- pan_astyle_menu.items().push_back (MenuElem (_("Abs")));
-
- pan_astate_menu.set_name ("ArdourContextMenu");
- pan_astyle_menu.set_name ("ArdourContextMenu");
-
pan_automation_style_button.signal_button_press_event().connect (mem_fun(*this, &PannerUI::pan_automation_style_button_event), false);
pan_automation_state_button.signal_button_press_event().connect (mem_fun(*this, &PannerUI::pan_automation_state_button_event), false);
@@ -141,16 +126,41 @@ PannerUI::PannerUI (boost::shared_ptr<IO> io, Session& s)
panner = 0;
set_width(Narrow);
-
- _io->panner().Changed.connect (mem_fun(*this, &PannerUI::panner_changed));
- _io->panner().LinkStateChanged.connect (mem_fun(*this, &PannerUI::update_pan_linkage));
- _io->panner().StateChanged.connect (mem_fun(*this, &PannerUI::update_pan_state));
+}
+
+void
+PannerUI::set_io (boost::shared_ptr<IO> io)
+{
+ connections.clear ();
+
+ if (pan_astyle_menu) {
+ delete pan_astyle_menu;
+ pan_astyle_menu = 0;
+ }
+
+ if (pan_astate_menu) {
+ delete pan_astate_menu;
+ pan_astate_menu = 0;
+ }
+
+ _io = io;
+
+ connections.push_back (_io->panner().Changed.connect (mem_fun(*this, &PannerUI::panner_changed)));
+ connections.push_back (_io->panner().LinkStateChanged.connect (mem_fun(*this, &PannerUI::update_pan_linkage)));
+ connections.push_back (_io->panner().StateChanged.connect (mem_fun(*this, &PannerUI::update_pan_state)));
+
+ if (panner) {
+ delete panner;
+ panner = 0;
+ }
+
pan_changed (0);
update_pan_sensitive ();
update_pan_linkage ();
pan_automation_state_changed ();
+#if WHERE_DOES_THIS_LIVE
pan_bar_packer.show();
panning_viewport.show();
panning_up.show();
@@ -164,6 +174,46 @@ PannerUI::PannerUI (boost::shared_ptr<IO> io, Session& s)
pan_automation_style_button.show();
pan_automation_state_button.show();
show();
+#endif
+}
+
+void
+PannerUI::build_astate_menu ()
+{
+ using namespace Menu_Helpers;
+
+ if (pan_astate_menu == 0) {
+ pan_astate_menu = new Menu;
+ pan_astate_menu->set_name ("ArdourContextMenu");
+ } else {
+ pan_astate_menu->items().clear ();
+ }
+
+ pan_astate_menu->items().push_back (MenuElem (_("Manual"),
+ bind (mem_fun (_io->panner(), &Panner::set_automation_state), (AutoState) Off)));
+ pan_astate_menu->items().push_back (MenuElem (_("Play"),
+ bind (mem_fun (_io->panner(), &Panner::set_automation_state), (AutoState) Play)));
+ pan_astate_menu->items().push_back (MenuElem (_("Write"),
+ bind (mem_fun (_io->panner(), &Panner::set_automation_state), (AutoState) Write)));
+ pan_astate_menu->items().push_back (MenuElem (_("Touch"),
+ bind (mem_fun (_io->panner(), &Panner::set_automation_state), (AutoState) Touch)));
+
+}
+
+void
+PannerUI::build_astyle_menu ()
+{
+ using namespace Menu_Helpers;
+
+ if (pan_astyle_menu == 0) {
+ pan_astyle_menu = new Menu;
+ pan_astyle_menu->set_name ("ArdourContextMenu");
+ } else {
+ pan_astyle_menu->items().clear();
+ }
+
+ pan_astyle_menu->items().push_back (MenuElem (_("Trim")));
+ pan_astyle_menu->items().push_back (MenuElem (_("Abs")));
}
boost::shared_ptr<PBD::Controllable>
@@ -262,7 +312,14 @@ PannerUI::~PannerUI ()
if (pan_menu) {
delete pan_menu;
}
-
+
+ if (pan_astyle_menu) {
+ delete pan_astyle_menu;
+ }
+
+ if (pan_astate_menu) {
+ delete pan_astate_menu;
+ }
}
@@ -662,7 +719,10 @@ PannerUI::pan_automation_state_button_event (GdkEventButton *ev)
switch (ev->button) {
case 1:
- pan_astate_menu.popup (1, ev->time);
+ if (pan_astate_menu == 0) {
+ build_astate_menu ();
+ }
+ pan_astate_menu->popup (1, ev->time);
break;
default:
break;
@@ -680,7 +740,10 @@ PannerUI::pan_automation_style_button_event (GdkEventButton *ev)
switch (ev->button) {
case 1:
- pan_astyle_menu.popup (1, ev->time);
+ if (pan_astyle_menu == 0) {
+ build_astyle_menu ();
+ }
+ pan_astyle_menu->popup (1, ev->time);
break;
default:
break;
diff --git a/gtk2_ardour/panner_ui.h b/gtk2_ardour/panner_ui.h
index e10080a317..4501659750 100644
--- a/gtk2_ardour/panner_ui.h
+++ b/gtk2_ardour/panner_ui.h
@@ -54,9 +54,11 @@ namespace Gtk {
class PannerUI : public Gtk::HBox
{
public:
- PannerUI (boost::shared_ptr<ARDOUR::IO>, ARDOUR::Session&);
+ PannerUI (ARDOUR::Session&);
~PannerUI ();
+ virtual void set_io (boost::shared_ptr<ARDOUR::IO>);
+
void pan_changed (void *);
void update_pan_sensitive ();
@@ -75,6 +77,7 @@ class PannerUI : public Gtk::HBox
boost::shared_ptr<ARDOUR::IO> _io;
ARDOUR::Session& _session;
+ std::vector<sigc::connection> connections;
bool ignore_toggle;
bool in_pan_update;
@@ -101,8 +104,8 @@ class PannerUI : public Gtk::HBox
bool panning_link_button_press (GdkEventButton*);
bool panning_link_button_release (GdkEventButton*);
- Gtk::Menu pan_astate_menu;
- Gtk::Menu pan_astyle_menu;
+ Gtk::Menu* pan_astate_menu;
+ Gtk::Menu* pan_astyle_menu;
Gtk::Button pan_automation_style_button;
Gtk::ToggleButton pan_automation_state_button;
@@ -118,6 +121,8 @@ class PannerUI : public Gtk::HBox
void update_pan_bars (bool only_if_aplay);
void update_pan_linkage ();
void update_pan_state ();
+ void build_astate_menu ();
+ void build_astyle_menu ();
void panner_changed ();
diff --git a/gtk2_ardour/port_group.h b/gtk2_ardour/port_group.h
new file mode 100644
index 0000000000..d5334640b1
--- /dev/null
+++ b/gtk2_ardour/port_group.h
@@ -0,0 +1,90 @@
+#ifndef __gtk_ardour_port_group_h__
+#define __gtk_ardour_port_group_h__
+
+#include <vector>
+#include <string>
+
+#include <gtkmm/widget.h>
+#include <gtkmm/checkbutton.h>
+
+#include <ardour/data_type.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_visibility_checkbutton ();
+ PortGroup& port_group () { return _port_group; }
+ void setup_visibility ();
+
+ private:
+ void port_checkbutton_toggled (Gtk::CheckButton*, int, int);
+ bool port_checkbutton_release (GdkEventButton* ev, Gtk::CheckButton* b, int r, int c);
+ void visibility_checkbutton_toggled ();
+
+ 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::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;
+};
+
+#endif /* __gtk_ardour_port_group_h__ */
diff --git a/gtk2_ardour/port_matrix.cc b/gtk2_ardour/port_matrix.cc
index cff4a3328e..b1dd2a17ac 100644
--- a/gtk2_ardour/port_matrix.cc
+++ b/gtk2_ardour/port_matrix.cc
@@ -69,59 +69,8 @@ PortGroupUI::PortGroupUI (PortMatrix& m, PortGroup& 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) {
- CheckButton* b = new CheckButton;
-
- b->signal_toggled().connect (
- sigc::bind (sigc::mem_fun (*this, &PortGroupUI::port_checkbutton_toggled), b, i, j));
-
- b->signal_button_release_event().connect (
- sigc::bind (sigc::mem_fun (*this, &PortGroupUI::port_checkbutton_release), b, i, j), false);
-
- _port_checkbuttons[i][j] = b;
-
- cerr << this << " bind to " << &_port_checkbuttons << " via " << b
- << endl;
-
- _table.attach (*b, j, j + 1, i, i + 1);
- }
- }
-
- _table_box.add (_table);
-
- _ignore_check_button_toggle = true;
-
- /* 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;
- }
- }
- }
-
+ _port_group.visible = true;
_ignore_check_button_toggle = false;
-
_visibility_checkbutton.signal_toggled().connect (sigc::mem_fun (*this, &PortGroupUI::visibility_checkbutton_toggled));
}
@@ -132,34 +81,6 @@ PortGroupUI::visibility_checkbutton_toggled ()
_port_group.visible = _visibility_checkbutton.get_active ();
}
-/** @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 */
-Widget&
-PortGroupUI::get_table ()
-{
- return _table_box;
-}
-
/** @return Checkbutton used to toggle visibility */
Widget&
PortGroupUI::get_visibility_checkbutton ()
@@ -173,324 +94,37 @@ void
PortGroupUI::port_checkbutton_toggled (CheckButton* b, int r, int c)
{
if (_ignore_check_button_toggle == false) {
- _port_matrix.set_state (r, _port_group.prefix + _port_group.ports[c], b->get_active(), 0);
+ // _port_matrix.hide_group (_port_group);
}
}
-bool
-PortGroupUI::port_checkbutton_release (GdkEventButton* ev, CheckButton* b, int r, int c)
-{
- cerr << this << " RELEASE on " << b << " state = " << ev->state << endl;
-
- if (ev->state == 0) {
- /* let usual toggle handler take care of it */
- return false;
- }
-
- /* The fun starts here
- */
-
- const size_t ports = _port_group.ports.size();
- const size_t rows = _port_matrix.n_rows ();
-
- if (rows == 0 || ports == 0) {
- return true;
- }
-
- /* For each port in the group, change the state of
- the connection for the corresponding "connector" port.
- */
-
- for (size_t j = c; j < ports; ++j) {
-
- /* we've got a port to connect, now lets find the thing to
- connect it too ... (search "down" the rows)
- */
-
- cerr << "we're going to connect port " << j << " of " << ports << endl;
-
- for (size_t i = r; i < rows; ++i) {
-
- cerr << this << " going to connect to row " << i << " of " << rows << endl;
- cerr << "access [" << i << "][" << j << "]\n";
- cerr << " @ " << &_port_checkbuttons << endl;
-
- _port_checkbuttons[i][j]->set_active (!_port_checkbuttons[i][j]->get_active());
-
- /* next time, get at least as far as this port before looking
- for more.
- */
-
- r = i + 1;
-
- /* changed connection state, stop looking for more */
-
- break;
- }
- }
-
- return true;
-}
-
/** Set up visibility of the port group according to PortGroup::visible */
void
PortGroupUI::setup_visibility ()
{
- if (!_port_group.ports.empty() && _port_group.visible) {
- _table_box.show ();
- } else {
- _table_box.hide ();
- }
-
if (_visibility_checkbutton.get_active () != _port_group.visible) {
-
_visibility_checkbutton.set_active (_port_group.visible);
}
}
-RotatedLabelSet::RotatedLabelSet (PortGroupList& g)
- : Glib::ObjectBase ("RotatedLabelSet"), Widget (), _port_group_list (g), _base_width (128)
-{
- set_flags (NO_WINDOW);
- if (getenv ("AD_ANGLE") != 0) {
- set_angle (atoi (getenv ("AD_ANGLE")));
- } else {
- set_angle (45);
- }
-}
-
-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 (Requisition* requisition)
-{
- *requisition = 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;
- }
-
- cerr << "Labels will be " << requisition->width << " x " << requisition->height << endl;
-}
-
-void
-RotatedLabelSet::on_size_allocate (Allocation& allocation)
-{
- set_allocation (allocation);
-
- if (_gdk_window) {
- _gdk_window->move_resize (allocation.get_x(), allocation.get_y(), allocation.get_width(), allocation.get_height());
- }
-}
-
-void
-RotatedLabelSet::on_realize ()
-{
- Widget::on_realize ();
-
- Glib::RefPtr<Style> style = get_style ();
-
- if (!_gdk_window) {
- GdkWindowAttr attributes;
- memset (&attributes, 0, sizeof (attributes));
-
- 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 (NO_WINDOW);
- set_window (_gdk_window);
-
- _bg_colour = style->get_bg (STATE_NORMAL );
- modify_bg (STATE_NORMAL, _bg_colour);
- _fg_colour = style->get_fg (STATE_NORMAL);
-
- _gdk_window->set_user_data (gobj ());
-
- /* Set up Pango stuff */
- _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 ();
-
- 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;
-
- // cerr << "\"" << s << "\" was " << w << " x " << h << endl;
-
- d.first = int (fabs (w * cos (_angle_radians) + h * sin (_angle_radians)));
- d.second = int (fabs (w * sin (_angle_radians) + h * cos (_angle_radians)));
-
- // cerr << "\trotated by " << _angle_degrees << " = " << d.first << " x " << d.second << endl;
-
- return d;
-}
-
-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]);
- int x, y;
- if (getenv ("AD_X_SHIFT") != 0) {
- x = atoi (getenv ("AD_X_SHIFT"));
- } else {
- x = 0;
- }
- if (getenv ("AD_Y_SHIFT") != 0) {
- y = atoi (getenv ("AD_Y_SHIFT"));
- } else {
- y = 0;
- }
- get_window()->draw_layout (_gc, int ((n + 0.25) * spacing) + x, height - d.second + y, _pango_layout, _fg_colour, _bg_colour);
- ++n;
- }
- }
- }
-
- 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)
+ : _offer_inputs (offer_inputs), _port_group_list (session, type, offer_inputs, mask), _type (type)
{
_row_labels_vbox = 0;
_side_vbox_pad = 0;
_visibility_checkbutton_box.pack_start (*(manage (new Label (_("Connections displayed: ")))), false, false, 10);
pack_start (_visibility_checkbutton_box, false, false);
-
+
_scrolled_window.set_policy (POLICY_ALWAYS, POLICY_AUTOMATIC);
_scrolled_window.set_shadow_type (SHADOW_NONE);
VBox* b = manage (new VBox);
- if (offer_inputs) {
- b->pack_start (_port_group_hbox, false, false);
- b->pack_start (_column_labels, false, false);
- } else {
- b->pack_start (_column_labels, false, false);
- b->pack_start (_port_group_hbox, false, false);
- }
+ b->pack_start (_port_group_hbox, false, false);
+ b->pack_start (_port_group_hbox, false, false);
- Alignment* a;
-
- if (offer_inputs) {
- a = manage (new Alignment (1, 0, 0, 0));
- } else {
- a = manage (new Alignment (0, 1, 0, 0));
- }
-
- a->add (*b);
-
- _scrolled_window.add (*a);
+ _scrolled_window.add (matrix);
if (offer_inputs) {
_overall_hbox.pack_start (_side_vbox, false, false, 6);
@@ -535,7 +169,6 @@ PortMatrix::clear ()
}
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;
}
@@ -550,46 +183,16 @@ PortMatrix::clear ()
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.
- The constant value in the set_size_request() computation will control
- how big the scrolled window will be if we fill the port matrix will a gajillion
- ports.
- */
-
- _scrolled_window.set_size_request (
- std::min (_column_labels.get_width(), 400),
- _column_labels.get_height() + port_group_tables_height + scrollbar_height + 16
- );
-
/* Row labels */
for (std::vector<EventBox*>::iterator j = _row_labels.begin(); j != _row_labels.end(); ++j) {
- (*j)->get_child()->set_size_request (-1, unit_size.second);
+ (*j)->get_child()->set_size_request (-1, matrix.row_spacing());
}
if (_side_vbox_pad) {
if (_offer_inputs) {
- _side_vbox_pad->set_size_request (-1, unit_size.second / 4);
+ _side_vbox_pad->set_size_request (-1, matrix.row_spacing() / 4);
} else {
- _side_vbox_pad->set_size_request (-1, scrollbar_height + unit_size.second / 4);
+ _side_vbox_pad->set_size_request (-1, matrix.row_spacing() / 4);
}
}
}
@@ -647,7 +250,6 @@ PortMatrix::setup ()
PortGroupUI* t = new PortGroupUI (*this, **i);
_port_group_ui.push_back (t);
- _port_group_hbox.pack_start (t->get_table(), false, false);
_visibility_checkbutton_box.pack_start (t->get_visibility_checkbutton(), false, false);
@@ -666,23 +268,14 @@ PortMatrix::setup ()
void
PortMatrix::reset_visibility ()
{
- /* now adjust visibility and coloring */
-
- bool even = true;
- Gdk::Color odd_bg (color_from_style ("OddPortGroups", STATE_NORMAL, "fg"));
- Gdk::Color even_bg (color_from_style ("EvenPortGroups", STATE_NORMAL, "fg"));
-
for (std::vector<PortGroupUI*>::iterator i = _port_group_ui.begin(); i != _port_group_ui.end(); ++i) {
(*i)->setup_visibility ();
if ((*i)->port_group().visible) {
- if (even) {
- (*i)->get_table().modify_bg (STATE_NORMAL, even_bg);
- } else {
- (*i)->get_table().modify_bg (STATE_NORMAL, odd_bg);
- }
- even = !even;
+ matrix.show_group ((*i)->port_group());
+ } else {
+ matrix.hide_group ((*i)->port_group());
}
}
}
diff --git a/gtk2_ardour/port_matrix.h b/gtk2_ardour/port_matrix.h
index 89208f357a..b9a5f7e4c6 100644
--- a/gtk2_ardour/port_matrix.h
+++ b/gtk2_ardour/port_matrix.h
@@ -28,6 +28,8 @@
#include <gtkmm/scrolledwindow.h>
#include "ardour_dialog.h"
+#include "port_group.h"
+#include "matrix.h"
namespace ARDOUR {
class Session;
@@ -35,118 +37,6 @@ namespace ARDOUR {
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);
- bool port_checkbutton_release (GdkEventButton* ev, Gtk::CheckButton* b, int r, int c);
- 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);
@@ -184,10 +74,10 @@ class PortMatrix : public Gtk::VBox {
private:
PortGroupList _port_group_list;
ARDOUR::DataType _type;
+ Matrix matrix;
std::vector<PortGroupUI*> _port_group_ui;
std::vector<Gtk::EventBox*> _row_labels;
Gtk::VBox* _row_labels_vbox;
- RotatedLabelSet _column_labels;
Gtk::HBox _overall_hbox;
Gtk::VBox _side_vbox;
Gtk::HBox _port_group_hbox;
diff --git a/gtk2_ardour/processor_box.cc b/gtk2_ardour/processor_box.cc
index 370bb7548b..88bd7d0b15 100644
--- a/gtk2_ardour/processor_box.cc
+++ b/gtk2_ardour/processor_box.cc
@@ -82,10 +82,9 @@ bool ProcessorBox::get_colors = true;
Gdk::Color* ProcessorBox::active_processor_color;
Gdk::Color* ProcessorBox::inactive_processor_color;
-ProcessorBox::ProcessorBox (Placement pcmnt, Session& sess, boost::shared_ptr<Route> rt, PluginSelector &plugsel,
- RouteRedirectSelection & rsel, bool owner_is_mixer)
- : _route(rt),
- _session(sess),
+ProcessorBox::ProcessorBox (Placement pcmnt, Session& sess, PluginSelector &plugsel,
+ RouteRedirectSelection & rsel, bool owner_is_mixer)
+ : _session(sess),
_owner_is_mixer (owner_is_mixer),
_placement(pcmnt),
_plugin_selector(plugsel),
@@ -138,21 +137,10 @@ ProcessorBox::ProcessorBox (Placement pcmnt, Session& sess, boost::shared_ptr<Ro
pack_start (processor_eventbox, true, true);
- _route->processors_changed.connect (mem_fun(*this, &ProcessorBox::redisplay_processors));
- _route->GoingAway.connect (mem_fun (*this, &ProcessorBox::route_going_away));
-
processor_eventbox.signal_enter_notify_event().connect (bind (sigc::ptr_fun (ProcessorBox::enter_box), this));
processor_display.signal_button_press_event().connect (mem_fun(*this, &ProcessorBox::processor_button_press_event), false);
processor_display.signal_button_release_event().connect (mem_fun(*this, &ProcessorBox::processor_button_release_event));
-
- /* start off as a passthru strip. we'll correct this, if necessary,
- in update_diskstream_display().
- */
-
- /* now force an update of all the various elements */
-
- redisplay_processors ();
}
ProcessorBox::~ProcessorBox ()
@@ -160,6 +148,19 @@ ProcessorBox::~ProcessorBox ()
}
void
+ProcessorBox::set_route (boost::shared_ptr<Route> r)
+{
+ connections.clear ();
+
+ _route = r;
+
+ connections.push_back (_route->processors_changed.connect (mem_fun(*this, &ProcessorBox::redisplay_processors)));
+ connections.push_back (_route->GoingAway.connect (mem_fun (*this, &ProcessorBox::route_going_away)));
+
+ redisplay_processors ();
+}
+
+void
ProcessorBox::route_going_away ()
{
/* don't keep updating display as processors are deleted */
diff --git a/gtk2_ardour/processor_box.h b/gtk2_ardour/processor_box.h
index 5d3eb731ca..74d781a0d4 100644
--- a/gtk2_ardour/processor_box.h
+++ b/gtk2_ardour/processor_box.h
@@ -68,10 +68,10 @@ namespace ARDOUR {
class ProcessorBox : public Gtk::HBox, public PluginInterestedObject
{
public:
- ProcessorBox (ARDOUR::Placement, ARDOUR::Session&,
- boost::shared_ptr<ARDOUR::Route>, PluginSelector &, RouteRedirectSelection &, bool owner_is_mixer = false);
+ ProcessorBox (ARDOUR::Placement, ARDOUR::Session&, PluginSelector &, RouteRedirectSelection &, bool owner_is_mixer = false);
~ProcessorBox ();
+ void set_route (boost::shared_ptr<ARDOUR::Route>);
void set_width (Width);
void update();
@@ -87,14 +87,12 @@ class ProcessorBox : public Gtk::HBox, public PluginInterestedObject
static void register_actions();
- protected:
- void set_stuff_from_route ();
-
private:
boost::shared_ptr<ARDOUR::Route> _route;
ARDOUR::Session & _session;
bool _owner_is_mixer;
bool ab_direction;
+ std::vector<sigc::connection> connections;
ARDOUR::Placement _placement;
diff --git a/gtk2_ardour/route_params_ui.cc b/gtk2_ardour/route_params_ui.cc
index f39d41def5..00a4858c42 100644
--- a/gtk2_ardour/route_params_ui.cc
+++ b/gtk2_ardour/route_params_ui.cc
@@ -229,8 +229,11 @@ RouteParams_UI::setup_processor_boxes()
cleanup_processor_boxes();
// construct new redirect boxes
- pre_insert_box = new ProcessorBox(PreFader, *session, _route, *_plugin_selector, _rr_selection);
- post_insert_box = new ProcessorBox(PostFader, *session, _route, *_plugin_selector, _rr_selection);
+ pre_insert_box = new ProcessorBox(PreFader, *session, *_plugin_selector, _rr_selection);
+ post_insert_box = new ProcessorBox(PostFader, *session, *_plugin_selector, _rr_selection);
+
+ pre_insert_box->set_route (_route);
+ post_insert_box->set_route (_route);
pre_redir_hpane.pack1 (*pre_insert_box);
post_redir_hpane.pack1 (*post_insert_box);
diff --git a/gtk2_ardour/route_time_axis.cc b/gtk2_ardour/route_time_axis.cc
index 71ad6e73bd..8985e85071 100644
--- a/gtk2_ardour/route_time_axis.cc
+++ b/gtk2_ardour/route_time_axis.cc
@@ -106,9 +106,9 @@ RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session& sess, boost::sh
size_button (_("h")), // height
automation_button (_("a")),
visual_button (_("v")),
- gm (rt, sess, slider, true)
-
+ gm (sess, slider, true)
{
+ gm.set_io (rt);
gm.get_level_meter().set_no_show_all();
gm.get_level_meter().setup_meters(50);
diff --git a/gtk2_ardour/route_ui.cc b/gtk2_ardour/route_ui.cc
index 44032a4ea5..3019d954fc 100644
--- a/gtk2_ardour/route_ui.cc
+++ b/gtk2_ardour/route_ui.cc
@@ -54,13 +54,33 @@ using namespace Gtkmm2ext;
using namespace ARDOUR;
using namespace PBD;
-RouteUI::RouteUI (boost::shared_ptr<ARDOUR::Route> rt, ARDOUR::Session& sess, const char* m_name,
- const char* s_name, const char* r_name)
- : AxisView(sess),
- _route(rt),
- mute_button(0),
- solo_button(0),
- rec_enable_button(0)
+RouteUI::RouteUI (ARDOUR::Session& sess, const char* mute_name, const char* solo_name, const char* rec_name)
+ : AxisView(sess)
+{
+ init ();
+ set_button_names (mute_name, solo_name, rec_name);
+}
+
+RouteUI::RouteUI (boost::shared_ptr<ARDOUR::Route> rt,
+ ARDOUR::Session& sess, const char* mute_name, const char* solo_name, const char* rec_name)
+ : AxisView(sess)
+{
+ init ();
+ set_button_names (mute_name, solo_name, rec_name);
+ set_route (rt);
+}
+
+RouteUI::~RouteUI()
+{
+ GoingAway (); /* EMIT SIGNAL */
+
+ delete solo_menu;
+ delete mute_menu;
+ delete remote_control_menu;
+}
+
+void
+RouteUI::init ()
{
xml_node = 0;
mute_menu = 0;
@@ -73,27 +93,87 @@ RouteUI::RouteUI (boost::shared_ptr<ARDOUR::Route> rt, ARDOUR::Session& sess, co
polarity_menu_item = 0;
denormal_menu_item = 0;
- if (set_color_from_route()) {
- set_color (unique_random_color());
+ mute_button = manage (new BindableToggleButton (""));
+ mute_button->set_self_managed (true);
+ mute_button->set_name ("MuteButton");
+
+ solo_button = manage (new BindableToggleButton (""));
+ solo_button->set_self_managed (true);
+ solo_button->set_name ("SoloButton");
+
+ rec_enable_button = manage (new BindableToggleButton (""));
+ rec_enable_button->set_name ("RecordEnableButton");
+ rec_enable_button->set_self_managed (true);
+
+ _session.SoloChanged.connect (mem_fun(*this, &RouteUI::solo_changed_so_update_mute));
+}
+
+void
+RouteUI::reset ()
+{
+ connections.clear ();
+
+ if (solo_menu) {
+ delete solo_menu;
+ solo_menu = 0;
}
- new PairedShiva<Route,RouteUI> (*_route, *this);
+ if (mute_menu) {
+ delete mute_menu;
+ mute_menu = 0;
+ }
+
+ if (remote_control_menu) {
+ delete remote_control_menu;
+ remote_control_menu = 0;
+ }
- _route->active_changed.connect (mem_fun (*this, &RouteUI::route_active_changed));
+ if (xml_node) {
+ /* do not delete the node - its owned by the route */
+ xml_node = 0;
+ }
- mute_button = manage (new BindableToggleButton (_route->mute_control(), m_name ));
- mute_button->set_self_managed (true);
+ route_active_menu_item = 0;
+ polarity_menu_item = 0;
+ denormal_menu_item = 0;
+}
- solo_button = manage (new BindableToggleButton (_route->solo_control(), s_name ));
- solo_button->set_self_managed (true);
+void
+RouteUI::set_button_names (const char* mute, const char* solo, const char* rec)
+{
+ m_name = mute;
+ s_name = solo;
+ r_name = rec;
+}
- mute_button->set_name ("MuteButton");
- solo_button->set_name ("SoloButton");
+void
+RouteUI::set_route (boost::shared_ptr<Route> rp)
+{
+ reset ();
+
+ _route = rp;
+
+ if (set_color_from_route()) {
+ set_color (unique_random_color());
+ }
- _route->mute_changed.connect (mem_fun(*this, &RouteUI::mute_changed));
- _route->solo_changed.connect (mem_fun(*this, &RouteUI::solo_changed));
- _route->solo_safe_changed.connect (mem_fun(*this, &RouteUI::solo_changed));
+ /* no, there is no memory leak here. This object cleans itself (and other stuff)
+ up when the route is destroyed.
+ */
+ new PairedShiva<Route,RouteUI> (*_route, *this);
+
+ mute_button->set_controllable (_route->mute_control());
+ mute_button->set_label (m_name);
+
+ solo_button->set_controllable (_route->solo_control());
+ solo_button->set_label (s_name);
+
+ connections.push_back (_route->active_changed.connect (mem_fun (*this, &RouteUI::route_active_changed)));
+ connections.push_back (_route->mute_changed.connect (mem_fun(*this, &RouteUI::mute_changed)));
+ connections.push_back (_route->solo_changed.connect (mem_fun(*this, &RouteUI::solo_changed)));
+ connections.push_back (_route->solo_safe_changed.connect (mem_fun(*this, &RouteUI::solo_changed)));
+
/* when solo changes, update mute state too, in case the user wants us to display it */
_session.SoloChanged.connect (mem_fun(*this, &RouteUI::solo_changed_so_update_mute));
@@ -101,15 +181,13 @@ RouteUI::RouteUI (boost::shared_ptr<ARDOUR::Route> rt, ARDOUR::Session& sess, co
if (is_track()) {
boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(_route);
- t->diskstream()->RecordEnableChanged.connect (mem_fun (*this, &RouteUI::route_rec_enable_changed));
-
- _session.RecordStateChanged.connect (mem_fun (*this, &RouteUI::session_rec_enable_changed));
+ connections.push_back (t->diskstream()->RecordEnableChanged.connect (mem_fun (*this, &RouteUI::route_rec_enable_changed)));
+ connections.push_back (_session.RecordStateChanged.connect (mem_fun (*this, &RouteUI::session_rec_enable_changed)));
- rec_enable_button = manage (new BindableToggleButton (t->rec_enable_control(), r_name ));
- rec_enable_button->set_name ("RecordEnableButton");
- rec_enable_button->set_self_managed (true);
-
rec_enable_button->show();
+ rec_enable_button->set_controllable (t->rec_enable_control());
+ rec_enable_button->set_label (r_name);
+
update_rec_display ();
}
@@ -119,19 +197,13 @@ RouteUI::RouteUI (boost::shared_ptr<ARDOUR::Route> rt, ARDOUR::Session& sess, co
mute_button->show();
solo_button->show();
- _route->RemoteControlIDChanged.connect (mem_fun(*this, &RouteUI::refresh_remote_control_menu));
+ connections.push_back (_route->RemoteControlIDChanged.connect (mem_fun(*this, &RouteUI::refresh_remote_control_menu)));
/* map the current state */
map_frozen ();
}
-RouteUI::~RouteUI()
-{
- GoingAway (); /* EMIT SIGNAL */
- delete mute_menu;
-}
-
bool
RouteUI::mute_press(GdkEventButton* ev)
{
diff --git a/gtk2_ardour/route_ui.h b/gtk2_ardour/route_ui.h
index e18eda6871..67aa409dda 100644
--- a/gtk2_ardour/route_ui.h
+++ b/gtk2_ardour/route_ui.h
@@ -45,9 +45,14 @@ class BindableToggleButton;
class RouteUI : public virtual AxisView
{
public:
+ RouteUI(ARDOUR::Session&, const char*, const char*, const char*);
RouteUI(boost::shared_ptr<ARDOUR::Route>, ARDOUR::Session&, const char*, const char*, const char*);
+
virtual ~RouteUI();
+ virtual void set_route (boost::shared_ptr<ARDOUR::Route>);
+ void set_button_names (const char*, const char*, const char*);
+
bool is_track() const;
bool is_audio_track() const;
bool is_midi_track() const;
@@ -161,6 +166,15 @@ class RouteUI : public virtual AxisView
void reversibly_apply_track_boolean (string name, void (ARDOUR::Track::*func)(bool, void*), bool, void *);
void adjust_latency ();
+
+ protected:
+ std::vector<sigc::connection> connections;
+ std::string s_name;
+ std::string m_name;
+ std::string r_name;
+
+ void init ();
+ void reset ();
};
#endif /* __ardour_route_ui__ */
diff --git a/gtk2_ardour/send_ui.cc b/gtk2_ardour/send_ui.cc
index 90ff3de717..6ce6edd303 100644
--- a/gtk2_ardour/send_ui.cc
+++ b/gtk2_ardour/send_ui.cc
@@ -30,11 +30,14 @@ using namespace ARDOUR;
using namespace PBD;
SendUI::SendUI (boost::shared_ptr<Send> s, Session& se)
- : _send (s),
- _session (se),
- gpm (s->io(), se),
- panners (s->io(), se)
+ : _send (s)
+ , _session (se)
+ , gpm (se)
+ , panners (se)
{
+ panners.set_io (s->io());
+ gpm.set_io (s->io());
+
hbox.pack_start (gpm, true, true);
set_name ("SendUIFrame");
diff --git a/gtk2_ardour/sfdb_ui.cc b/gtk2_ardour/sfdb_ui.cc
index 601b83f0c0..915c3c6d3c 100644
--- a/gtk2_ardour/sfdb_ui.cc
+++ b/gtk2_ardour/sfdb_ui.cc
@@ -596,7 +596,8 @@ SoundFileBrowser::add_gain_meter ()
delete gm;
}
- gm = new GainMeter (session->the_auditioner(), *session);
+ gm = new GainMeter (*session);
+ gm->set_io (session->the_auditioner());
meter_packer.set_border_width (12);
meter_packer.pack_start (*gm, false, true);
diff --git a/libs/ardour/plugin_manager.cc b/libs/ardour/plugin_manager.cc
index d4e995f0bf..0b5a14bf56 100644
--- a/libs/ardour/plugin_manager.cc
+++ b/libs/ardour/plugin_manager.cc
@@ -153,12 +153,45 @@ PluginManager::refresh ()
void
PluginManager::ladspa_refresh ()
{
- _ladspa_plugin_info.clear ();
-
- if (ladspa_path.length() == 0) {
- ladspa_path = "/usr/local/lib64/ladspa:/usr/local/lib/ladspa:/usr/lib64/ladspa:/usr/lib/ladspa:/Library/Audio/Plug-Ins/LADSPA";
- }
-
+ _ladspa_plugin_info.clear ();
+
+ static const char *standard_paths[] = {
+ "/usr/local/lib64/ladspa",
+ "/usr/local/lib/ladspa",
+ "/usr/lib64/ladspa",
+ "/usr/lib/ladspa",
+ "/Library/Audio/Plug-Ins/LADSPA",
+ ""
+ };
+
+ /* allow LADSPA_PATH to augment, not override standard locations */
+
+ /* Only add standard locations to ladspa_path if it doesn't
+ * already contain them. Check for trailing '/'s too.
+ */
+
+ int i;
+ for (i = 0; standard_paths[i][0]; i++) {
+ size_t found = ladspa_path.find(standard_paths[i]);
+ if (found != ladspa_path.npos) {
+ switch (ladspa_path[found + strlen(standard_paths[i])]) {
+ case ':' :
+ case '\0':
+ continue;
+ case '/' :
+ if (ladspa_path[found + strlen(standard_paths[i]) + 1] == ':' ||
+ ladspa_path[found + strlen(standard_paths[i]) + 1] == '\0') {
+ continue;
+ }
+ }
+ }
+ if (!ladspa_path.empty())
+ ladspa_path += ":";
+
+ ladspa_path += standard_paths[i];
+
+ }
+
ladspa_discover_from_path (ladspa_path);
}
diff --git a/libs/gtkmm2ext/binding_proxy.cc b/libs/gtkmm2ext/binding_proxy.cc
index 6c60deaa8f..845146239d 100644
--- a/libs/gtkmm2ext/binding_proxy.cc
+++ b/libs/gtkmm2ext/binding_proxy.cc
@@ -40,6 +40,14 @@ BindingProxy::BindingProxy (boost::shared_ptr<Controllable> c)
{
}
+BindingProxy::BindingProxy ()
+ : prompter (0),
+ bind_button (2),
+ bind_statemask (Gdk::CONTROL_MASK)
+
+{
+}
+
BindingProxy::~BindingProxy ()
{
if (prompter) {
@@ -48,6 +56,13 @@ BindingProxy::~BindingProxy ()
}
void
+BindingProxy::set_controllable (boost::shared_ptr<Controllable> c)
+{
+ learning_finished ();
+ controllable = c;
+}
+
+void
BindingProxy::set_bind_button_state (guint button, guint statemask)
{
bind_button = button;
@@ -64,7 +79,7 @@ BindingProxy::get_bind_button_state (guint &button, guint &statemask)
bool
BindingProxy::button_press_handler (GdkEventButton *ev)
{
- if ((ev->state & bind_statemask) && ev->button == bind_button) {
+ if (controllable && (ev->state & bind_statemask) && ev->button == bind_button) {
if (Controllable::StartLearning (controllable.get())) {
string prompt = _("operate controller now");
if (prompter == 0) {
@@ -95,7 +110,9 @@ bool
BindingProxy::prompter_hiding (GdkEventAny *ev)
{
learning_connection.disconnect ();
- Controllable::StopLearning (controllable.get());
+ if (controllable) {
+ Controllable::StopLearning (controllable.get());
+ }
return false;
}
diff --git a/libs/gtkmm2ext/gtkmm2ext/barcontroller.h b/libs/gtkmm2ext/gtkmm2ext/barcontroller.h
index 5e9a6fed90..2c0ca948d9 100644
--- a/libs/gtkmm2ext/gtkmm2ext/barcontroller.h
+++ b/libs/gtkmm2ext/gtkmm2ext/barcontroller.h
@@ -63,7 +63,9 @@ class BarController : public Gtk::Frame
/* export this to allow direct connection to button events */
Gtk::Widget& event_widget() { return darea; }
+
boost::shared_ptr<PBD::Controllable> get_controllable() { return binding_proxy.get_controllable(); }
+ void set_controllable(boost::shared_ptr<PBD::Controllable> c) { binding_proxy.set_controllable(c); }
protected:
Gtk::Adjustment& adjustment;
diff --git a/libs/gtkmm2ext/gtkmm2ext/bindable_button.h b/libs/gtkmm2ext/gtkmm2ext/bindable_button.h
index 844dc27d08..3fad7d1faa 100644
--- a/libs/gtkmm2ext/gtkmm2ext/bindable_button.h
+++ b/libs/gtkmm2ext/gtkmm2ext/bindable_button.h
@@ -32,10 +32,8 @@ namespace PBD {
class BindableToggleButton : public Gtkmm2ext::StatefulToggleButton
{
public:
- BindableToggleButton (boost::shared_ptr<PBD::Controllable> c) : binding_proxy (c) {}
-
- explicit BindableToggleButton (boost::shared_ptr<PBD::Controllable> c, const std::string &label)
- : Gtkmm2ext::StatefulToggleButton (label), binding_proxy (c) {}
+ BindableToggleButton (const std::string &label)
+ : Gtkmm2ext::StatefulToggleButton (label) {}
virtual ~BindableToggleButton() {}
@@ -49,7 +47,8 @@ class BindableToggleButton : public Gtkmm2ext::StatefulToggleButton
}
boost::shared_ptr<PBD::Controllable> get_controllable() { return binding_proxy.get_controllable(); }
-
+ void set_controllable (boost::shared_ptr<PBD::Controllable> c) { binding_proxy.set_controllable (c); }
+
private:
BindingProxy binding_proxy;
};
@@ -58,10 +57,6 @@ class BindableButton : public Gtkmm2ext::StatefulButton
{
public:
BindableButton (boost::shared_ptr<PBD::Controllable> c) : binding_proxy (c) {}
-
- explicit BindableButton (boost::shared_ptr<PBD::Controllable> c, const std::string &label)
- : Gtkmm2ext::StatefulButton (label), binding_proxy (c) {}
-
~BindableButton() {}
bool on_button_press_event (GdkEventButton *ev) {
@@ -74,6 +69,7 @@ class BindableButton : public Gtkmm2ext::StatefulButton
}
boost::shared_ptr<PBD::Controllable> get_controllable() { return binding_proxy.get_controllable(); }
+ void set_controllable (boost::shared_ptr<PBD::Controllable> c) { binding_proxy.set_controllable (c); }
private:
BindingProxy binding_proxy;
diff --git a/libs/gtkmm2ext/gtkmm2ext/binding_proxy.h b/libs/gtkmm2ext/gtkmm2ext/binding_proxy.h
index ecb95bf815..f684cce5c3 100644
--- a/libs/gtkmm2ext/gtkmm2ext/binding_proxy.h
+++ b/libs/gtkmm2ext/gtkmm2ext/binding_proxy.h
@@ -34,6 +34,7 @@ class BindingProxy : public sigc::trackable
{
public:
BindingProxy (boost::shared_ptr<PBD::Controllable>);
+ BindingProxy ();
virtual ~BindingProxy();
void set_bind_button_state (guint button, guint statemask);
@@ -42,6 +43,7 @@ class BindingProxy : public sigc::trackable
bool button_press_handler (GdkEventButton *);
boost::shared_ptr<PBD::Controllable> get_controllable() { return controllable; }
+ void set_controllable (boost::shared_ptr<PBD::Controllable>);
protected:
Gtkmm2ext::PopUp* prompter;
diff --git a/libs/gtkmm2ext/gtkmm2ext/slider_controller.h b/libs/gtkmm2ext/gtkmm2ext/slider_controller.h
index 46f70dacd8..7fea4a40d7 100644
--- a/libs/gtkmm2ext/gtkmm2ext/slider_controller.h
+++ b/libs/gtkmm2ext/gtkmm2ext/slider_controller.h
@@ -41,7 +41,6 @@ class SliderController : public Gtkmm2ext::PixFader
public:
SliderController (Glib::RefPtr<Gdk::Pixbuf> image,
Gtk::Adjustment* adj, int orientation,
- boost::shared_ptr<PBD::Controllable>,
bool with_numeric = true);
virtual ~SliderController () {}
@@ -52,6 +51,8 @@ class SliderController : public Gtkmm2ext::PixFader
bool on_button_press_event (GdkEventButton *ev);
+ void set_controllable (boost::shared_ptr<PBD::Controllable> c) { binding_proxy.set_controllable (c); }
+
protected:
BindingProxy binding_proxy;
Glib::RefPtr<Gdk::Pixbuf> slider;
@@ -59,6 +60,8 @@ class SliderController : public Gtkmm2ext::PixFader
Gtk::SpinButton spin;
Gtk::Frame spin_frame;
Gtk::HBox spin_hbox;
+
+ void init ();
};
class VSliderController : public SliderController
@@ -66,7 +69,6 @@ class VSliderController : public SliderController
public:
VSliderController (Glib::RefPtr<Gdk::Pixbuf> image,
Gtk::Adjustment *adj,
- boost::shared_ptr<PBD::Controllable>,
bool with_numeric = true);
};
@@ -75,7 +77,6 @@ class HSliderController : public SliderController
public:
HSliderController (Glib::RefPtr<Gdk::Pixbuf> image,
Gtk::Adjustment *adj,
- boost::shared_ptr<PBD::Controllable>,
bool with_numeric = true);
};
diff --git a/libs/gtkmm2ext/slider_controller.cc b/libs/gtkmm2ext/slider_controller.cc
index 92fd9d078b..4eee6a6f36 100644
--- a/libs/gtkmm2ext/slider_controller.cc
+++ b/libs/gtkmm2ext/slider_controller.cc
@@ -30,11 +30,9 @@ using namespace PBD;
SliderController::SliderController (Glib::RefPtr<Gdk::Pixbuf> image,
Gtk::Adjustment *adj, int orientation,
- boost::shared_ptr<Controllable> c,
bool with_numeric)
: PixFader (image, *adj, orientation),
- binding_proxy (c),
spin (*adj, 0, 2)
{
spin.set_name ("SliderControllerValue");
@@ -55,15 +53,15 @@ SliderController::on_button_press_event (GdkEventButton *ev)
if (binding_proxy.button_press_handler (ev)) {
return true;
}
+
return PixFader::on_button_press_event (ev);
}
VSliderController::VSliderController (Glib::RefPtr<Gdk::Pixbuf> image,
Gtk::Adjustment *adj,
- boost::shared_ptr<Controllable> control,
bool with_numeric)
- : SliderController (image, adj, VERT, control, with_numeric)
+ : SliderController (image, adj, VERT, with_numeric)
{
if (with_numeric) {
spin_frame.add (spin);
@@ -76,10 +74,9 @@ VSliderController::VSliderController (Glib::RefPtr<Gdk::Pixbuf> image,
HSliderController::HSliderController (Glib::RefPtr<Gdk::Pixbuf> image,
Gtk::Adjustment *adj,
- boost::shared_ptr<Controllable> control,
bool with_numeric)
- : SliderController (image, adj, HORIZ, control, with_numeric)
+ : SliderController (image, adj, HORIZ, with_numeric)
{
if (with_numeric) {
spin_frame.add (spin);