summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gtk2_ardour/audio_region_view.cc28
-rw-r--r--gtk2_ardour/audio_region_view.h2
-rw-r--r--gtk2_ardour/automation_region_view.h4
-rw-r--r--gtk2_ardour/automation_time_axis.cc43
-rw-r--r--gtk2_ardour/automation_time_axis.h9
-rw-r--r--gtk2_ardour/canvas_vars.h5
-rw-r--r--gtk2_ardour/editor.cc7
-rw-r--r--gtk2_ardour/editor.h12
-rw-r--r--gtk2_ardour/editor_canvas.cc23
-rw-r--r--gtk2_ardour/editor_ops.cc42
-rw-r--r--gtk2_ardour/editor_route_list.cc17
-rw-r--r--gtk2_ardour/ghostregion.cc259
-rw-r--r--gtk2_ardour/ghostregion.h102
-rw-r--r--gtk2_ardour/lineset.cc6
-rw-r--r--gtk2_ardour/midi_region_view.cc61
-rw-r--r--gtk2_ardour/midi_region_view.h2
-rw-r--r--gtk2_ardour/midi_scroomer.cc47
-rw-r--r--gtk2_ardour/midi_streamview.cc7
-rw-r--r--gtk2_ardour/midi_streamview.h1
-rw-r--r--gtk2_ardour/midi_time_axis.cc24
-rw-r--r--gtk2_ardour/midi_time_axis.h2
-rw-r--r--gtk2_ardour/piano_roll_header.cc28
-rw-r--r--gtk2_ardour/piano_roll_header.h1
-rw-r--r--gtk2_ardour/public_editor.h2
-rw-r--r--gtk2_ardour/region_view.cc10
-rw-r--r--gtk2_ardour/region_view.h5
-rw-r--r--gtk2_ardour/route_time_axis.cc188
-rw-r--r--gtk2_ardour/route_time_axis.h15
-rw-r--r--gtk2_ardour/streamview.cc1
-rw-r--r--gtk2_ardour/time_axis_view.cc55
-rw-r--r--gtk2_ardour/time_axis_view.h11
-rw-r--r--libs/gtkmm2ext/scroomer.cc2
-rw-r--r--libs/pbd/pbd/xml++.h2
-rw-r--r--libs/pbd/xml++.cc21
34 files changed, 852 insertions, 192 deletions
diff --git a/gtk2_ardour/audio_region_view.cc b/gtk2_ardour/audio_region_view.cc
index d19abf2ce5..ea98bae1ed 100644
--- a/gtk2_ardour/audio_region_view.cc
+++ b/gtk2_ardour/audio_region_view.cc
@@ -348,6 +348,8 @@ AudioRegionView::region_renamed ()
void
AudioRegionView::region_resized (Change what_changed)
{
+ AudioGhostRegion* agr;
+
RegionView::region_resized(what_changed);
if (what_changed & Change (StartChanged|LengthChanged)) {
@@ -357,10 +359,12 @@ AudioRegionView::region_resized (Change what_changed)
}
for (vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
+ if((agr = dynamic_cast<AudioGhostRegion*>(*i)) != 0) {
- for (vector<WaveView*>::iterator w = (*i)->waves.begin(); w != (*i)->waves.end(); ++w) {
- (*w)->property_region_start() = _region->start();
- }
+ for (vector<WaveView*>::iterator w = agr->waves.begin(); w != agr->waves.end(); ++w) {
+ (*w)->property_region_start() = _region->start();
+ }
+ }
}
}
}
@@ -1078,13 +1082,13 @@ AudioRegionView::set_waveform_scale (WaveformScale scale)
GhostRegion*
-AudioRegionView::add_ghost (AutomationTimeAxisView& atv)
+AudioRegionView::add_ghost (TimeAxisView& tv)
{
RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&trackview);
assert(rtv);
double unit_position = _region->position () / samples_per_unit;
- GhostRegion* ghost = new GhostRegion (atv, unit_position);
+ AudioGhostRegion* ghost = new AudioGhostRegion (tv, trackview, unit_position);
uint32_t nchans;
nchans = rtv->get_diskstream()->n_channels().n_audio();
@@ -1107,10 +1111,7 @@ AudioRegionView::add_ghost (AutomationTimeAxisView& atv)
wave->property_x() = 0.0;
wave->property_samples_per_unit() = samples_per_unit;
wave->property_amplitude_above_axis() = _amplitude_above_axis;
- wave->property_wave_color() = ARDOUR_UI::config()->canvasvar_GhostTrackWave.get();
- wave->property_fill_color() = ARDOUR_UI::config()->canvasvar_GhostTrackWave.get();
- wave->property_clip_color() = ARDOUR_UI::config()->canvasvar_GhostTrackWaveClip.get();
- wave->property_zero_color() = ARDOUR_UI::config()->canvasvar_GhostTrackZeroLine.get();
+
wave->property_region_start() = _region->start();
ghost->waves.push_back(wave);
@@ -1118,6 +1119,7 @@ AudioRegionView::add_ghost (AutomationTimeAxisView& atv)
ghost->set_height ();
ghost->set_duration (_region->length() / samples_per_unit);
+ ghost->set_colors();
ghosts.push_back (ghost);
ghost->GoingAway.connect (mem_fun(*this, &AudioRegionView::remove_ghost));
@@ -1170,7 +1172,7 @@ AudioRegionView::envelope_active_changed ()
void
AudioRegionView::set_waveview_data_src()
{
-
+ AudioGhostRegion* agr;
double unit_length= _region->length() / samples_per_unit;
for (uint32_t n = 0; n < waves.size(); ++n) {
@@ -1182,8 +1184,10 @@ AudioRegionView::set_waveview_data_src()
(*i)->set_duration (unit_length);
- for (vector<WaveView*>::iterator w = (*i)->waves.begin(); w != (*i)->waves.end(); ++w) {
- (*w)->property_data_src() = _region.get();
+ if((agr = dynamic_cast<AudioGhostRegion*>(*i)) != 0) {
+ for (vector<WaveView*>::iterator w = agr->waves.begin(); w != agr->waves.end(); ++w) {
+ (*w)->property_data_src() = _region.get();
+ }
}
}
diff --git a/gtk2_ardour/audio_region_view.h b/gtk2_ardour/audio_region_view.h
index f5110e6a72..30ab8304a6 100644
--- a/gtk2_ardour/audio_region_view.h
+++ b/gtk2_ardour/audio_region_view.h
@@ -90,7 +90,7 @@ class AudioRegionView : public RegionView
void region_changed (ARDOUR::Change);
void envelope_active_changed ();
- GhostRegion* add_ghost (AutomationTimeAxisView&);
+ GhostRegion* add_ghost (TimeAxisView&);
void reset_fade_in_shape_width (nframes_t);
void reset_fade_out_shape_width (nframes_t);
diff --git a/gtk2_ardour/automation_region_view.h b/gtk2_ardour/automation_region_view.h
index dc0cc8f9ee..e69845461c 100644
--- a/gtk2_ardour/automation_region_view.h
+++ b/gtk2_ardour/automation_region_view.h
@@ -33,7 +33,7 @@ namespace ARDOUR {
class AutomationList;
};
-class AutomationTimeAxisView;
+class TimeAxisView;
class AutomationRegionView : public RegionView
{
@@ -55,7 +55,7 @@ public:
boost::shared_ptr<AutomationLine> line() { return _line; }
// We are a ghost. Meta ghosts? Crazy talk.
- virtual GhostRegion* add_ghost(AutomationTimeAxisView&) { return NULL; }
+ virtual GhostRegion* add_ghost(TimeAxisView&) { return NULL; }
void reset_width_dependent_items(double pixel_width);
diff --git a/gtk2_ardour/automation_time_axis.cc b/gtk2_ardour/automation_time_axis.cc
index 844d90095f..518f4ed41f 100644
--- a/gtk2_ardour/automation_time_axis.cc
+++ b/gtk2_ardour/automation_time_axis.cc
@@ -31,7 +31,6 @@
#include "public_editor.h"
#include "simplerect.h"
#include "selection.h"
-#include "ghostregion.h"
#include "rgb_macros.h"
#include "automation_selectable.h"
#include "point_selection.h"
@@ -74,7 +73,6 @@ AutomationTimeAxisView::AutomationTimeAxisView (Session& s, boost::shared_ptr<Ro
}
automation_menu = 0;
- in_destructor = false;
auto_off_item = 0;
auto_touch_item = 0;
auto_write_item = 0;
@@ -228,11 +226,6 @@ AutomationTimeAxisView::AutomationTimeAxisView (Session& s, boost::shared_ptr<Ro
AutomationTimeAxisView::~AutomationTimeAxisView ()
{
- in_destructor = true;
-
- for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
- delete *i;
- }
}
void
@@ -399,10 +392,6 @@ AutomationTimeAxisView::set_height (TrackHeight ht)
_view->update_contents_y_position_and_height();
}
- for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
- (*i)->set_height ();
- }
-
TimeAxisView* state_parent = get_parent_with_state ();
assert(state_parent);
@@ -802,30 +791,6 @@ AutomationTimeAxisView::paste_one (AutomationLine& line, nframes_t pos, float ti
}
void
-AutomationTimeAxisView::add_ghost (GhostRegion* gr)
-{
- ghosts.push_back (gr);
- gr->GoingAway.connect (mem_fun(*this, &AutomationTimeAxisView::remove_ghost));
-}
-
-void
-AutomationTimeAxisView::remove_ghost (GhostRegion* gr)
-{
- if (in_destructor) {
- return;
- }
-
- list<GhostRegion*>::iterator i;
-
- for (i = ghosts.begin(); i != ghosts.end(); ++i) {
- if ((*i) == gr) {
- ghosts.erase (i);
- break;
- }
- }
-}
-
-void
AutomationTimeAxisView::get_selectables (nframes_t start, nframes_t end, double top, double bot, list<Selectable*>& results)
{
if (_line && touched (top, bot)) {
@@ -916,7 +881,7 @@ AutomationTimeAxisView::exited ()
_line->track_exited();
}
-void
+/*void
AutomationTimeAxisView::set_colors ()
{
for (list<GhostRegion*>::iterator i=ghosts.begin(); i != ghosts.end(); i++ ) {
@@ -925,12 +890,14 @@ AutomationTimeAxisView::set_colors ()
if (_line)
_line->set_colors();
-}
+ }*/
void
AutomationTimeAxisView::color_handler ()
{
- set_colors ();
+ if (_line) {
+ _line->set_colors();
+ }
}
void
diff --git a/gtk2_ardour/automation_time_axis.h b/gtk2_ardour/automation_time_axis.h
index 4e21d281a3..8ddd007a3c 100644
--- a/gtk2_ardour/automation_time_axis.h
+++ b/gtk2_ardour/automation_time_axis.h
@@ -50,7 +50,6 @@ class TimeSelection;
class RegionSelection;
class PointSelection;
class AutomationLine;
-class GhostRegion;
class Selection;
class Selectable;
class AutomationStreamView;
@@ -95,9 +94,6 @@ class AutomationTimeAxisView : public TimeAxisView {
bool paste (nframes_t, float times, Selection&, size_t nth);
void reset_objects (PointSelection&);
- void add_ghost (GhostRegion*);
- void remove_ghost (GhostRegion*);
-
void set_state (const XMLNode&);
guint32 show_at (double y, int& nth, Gtk::VBox *parent);
@@ -121,7 +117,6 @@ class AutomationTimeAxisView : public TimeAxisView {
AutomationStreamView* _view;
string _name;
- bool in_destructor;
bool ignore_toggle;
bool first_call_to_set_height;
@@ -151,8 +146,6 @@ class AutomationTimeAxisView : public TimeAxisView {
void build_display_menu ();
- list<GhostRegion*> ghosts;
-
bool cut_copy_clear_one (AutomationLine&, Selection&, Editing::CutCopyOp);
bool cut_copy_clear_objects_one (AutomationLine&, PointSelection&, Editing::CutCopyOp);
bool paste_one (AutomationLine&, nframes_t, float times, Selection&, size_t nth);
@@ -173,7 +166,7 @@ class AutomationTimeAxisView : public TimeAxisView {
void entered ();
void exited ();
- void set_colors ();
+ //void set_colors ();
void color_handler ();
static Pango::FontDescription* name_font;
diff --git a/gtk2_ardour/canvas_vars.h b/gtk2_ardour/canvas_vars.h
index 5f2f5ca2c6..c152ebbc38 100644
--- a/gtk2_ardour/canvas_vars.h
+++ b/gtk2_ardour/canvas_vars.h
@@ -15,6 +15,7 @@ CANVAS_VARIABLE(canvasvar_CrossfadeEditorLineShading, "crossfade editor line sha
CANVAS_VARIABLE(canvasvar_CrossfadeEditorPointFill, "crossfade editor point fill")
CANVAS_VARIABLE(canvasvar_CrossfadeEditorPointOutline, "crossfade editor point outline")
CANVAS_VARIABLE(canvasvar_CrossfadeEditorWave, "crossfade editor wave")
+CANVAS_VARIABLE(canvasvar_SelectedCrossfadeEditorWaveFill, "selected crossfade editor wave fill")
CANVAS_VARIABLE(canvasvar_CrossfadeLine, "crossfade line")
CANVAS_VARIABLE(canvasvar_EditPoint, "edit point")
CANVAS_VARIABLE(canvasvar_EnteredAutomationLine, "entered automation line")
@@ -28,8 +29,10 @@ CANVAS_VARIABLE(canvasvar_FrameHandle, "frame handle")
CANVAS_VARIABLE(canvasvar_GainLine, "gain line")
CANVAS_VARIABLE(canvasvar_GainLineInactive, "gain line inactive")
CANVAS_VARIABLE(canvasvar_GhostTrackBase, "ghost track base")
-CANVAS_VARIABLE(canvasvar_GhostTrackWaveClip, "ghost track wave clip")
+CANVAS_VARIABLE(canvasvar_GhostTrackMidiOutline, "ghost track midi outline")
CANVAS_VARIABLE(canvasvar_GhostTrackWave, "ghost track wave")
+CANVAS_VARIABLE(canvasvar_GhostTrackWaveFill, "ghost track wave fill")
+CANVAS_VARIABLE(canvasvar_GhostTrackWaveClip, "ghost track wave clip")
CANVAS_VARIABLE(canvasvar_GhostTrackZeroLine, "ghost track zero line")
CANVAS_VARIABLE(canvasvar_ImageTrack, "image track")
CANVAS_VARIABLE(canvasvar_InactiveCrossfade, "inactive crossfade")
diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc
index d26f01f76b..6d7e291b0e 100644
--- a/gtk2_ardour/editor.cc
+++ b/gtk2_ardour/editor.cc
@@ -521,9 +521,10 @@ Editor::Editor ()
route_list_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
route_list_display.set_headers_visible (true);
route_list_display.set_name ("TrackListDisplay");
- route_list_display.get_selection()->set_mode (SELECTION_NONE);
+ route_list_display.get_selection()->set_mode (SELECTION_SINGLE);
route_list_display.set_reorderable (true);
route_list_display.set_size_request (100,-1);
+ route_list_display.add_object_drag (route_display_columns.route.index(), "routes");
CellRendererToggle* route_list_visible_cell = dynamic_cast<CellRendererToggle*>(route_list_display.get_column_cell_renderer (0));
route_list_visible_cell->property_activatable() = true;
@@ -1537,6 +1538,10 @@ Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type,
}
+ if (item_type == StreamItem && clicked_routeview) {
+ clicked_routeview->build_underlay_menu(menu);
+ }
+
menu->popup (button, time);
}
diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h
index 5b4e54f4f6..acd18c211e 100644
--- a/gtk2_ardour/editor.h
+++ b/gtk2_ardour/editor.h
@@ -165,6 +165,8 @@ class Editor : public PublicEditor
TimeAxisView* get_named_time_axis(const std::string & name) ;
#endif
+ RouteTimeAxisView* get_route_view_by_id (PBD::ID& id);
+
void consider_auditioning (boost::shared_ptr<ARDOUR::Region>);
void hide_a_region (boost::shared_ptr<ARDOUR::Region>);
void remove_a_region (boost::shared_ptr<ARDOUR::Region>);
@@ -313,6 +315,7 @@ class Editor : public PublicEditor
bool dragging_playhead () const { return _dragging_playhead; }
void toggle_waveform_visibility ();
+ void toggle_zero_line_visibility ();
void toggle_waveforms_while_recording ();
void toggle_measure_visibility ();
void toggle_logo_visibility ();
@@ -1111,6 +1114,8 @@ class Editor : public PublicEditor
void insert_region_list_drag (boost::shared_ptr<ARDOUR::Region>, int x, int y);
void insert_region_list_selection (float times);
+ void insert_route_list_drag (boost::shared_ptr<ARDOUR::Route>, int x, int y);
+
/* import & embed */
void add_external_audio_action (Editing::ImportMode);
@@ -1870,6 +1875,13 @@ class Editor : public PublicEditor
guint info,
guint time);
+ void drop_routes (const Glib::RefPtr<Gdk::DragContext>& context,
+ gint x,
+ gint y,
+ const Gtk::SelectionData& data,
+ guint info,
+ guint time);
+
/* audio export */
ExportDialog *export_dialog;
diff --git a/gtk2_ardour/editor_canvas.cc b/gtk2_ardour/editor_canvas.cc
index aeff20f0bc..a2df1783aa 100644
--- a/gtk2_ardour/editor_canvas.cc
+++ b/gtk2_ardour/editor_canvas.cc
@@ -127,6 +127,7 @@ Editor::initialize_canvas ()
// Drag-N-Drop from the region list can generate this target
target_table.push_back (TargetEntry ("regions"));
+ target_table.push_back (TargetEntry ("routes"));
target_table.push_back (TargetEntry ("text/plain"));
target_table.push_back (TargetEntry ("text/uri-list"));
@@ -457,7 +458,11 @@ Editor::track_canvas_drag_data_received (const RefPtr<Gdk::DragContext>& context
if (data.get_target() == "regions") {
drop_regions (context, x, y, data, info, time);
- } else {
+ }
+ else if(data.get_target() == "routes") {
+ drop_routes (context, x, y, data, info, time);
+ }
+ else {
drop_paths (context, x, y, data, info, time);
}
}
@@ -541,6 +546,22 @@ Editor::drop_regions (const RefPtr<Gdk::DragContext>& context,
}
void
+Editor::drop_routes (const Glib::RefPtr<Gdk::DragContext>& context,
+ int x, int y,
+ const Gtk::SelectionData& data,
+ guint info, guint time) {
+ const SerializedObjectPointers<boost::shared_ptr<Route> >* sr =
+ reinterpret_cast<const SerializedObjectPointers<boost::shared_ptr<Route> > *> (data.get_data());
+
+ for (uint32_t i = 0; i < sr->cnt; ++i) {
+ boost::shared_ptr<Route> r = sr->data[i];
+ insert_route_list_drag (r, x, y);
+ }
+
+ context->drag_finish (true, false, time);
+}
+
+void
Editor::maybe_autoscroll (GdkEvent* event)
{
nframes_t rightmost_frame = leftmost_frame + current_page_frames();
diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc
index 77b0883fc1..ea9f059fae 100644
--- a/gtk2_ardour/editor_ops.cc
+++ b/gtk2_ardour/editor_ops.cc
@@ -2096,6 +2096,48 @@ Editor::insert_region_list_drag (boost::shared_ptr<Region> region, int x, int y)
}
void
+Editor::insert_route_list_drag (boost::shared_ptr<Route> route, int x, int y) {
+ double wx, wy;
+ double cx, cy;
+ TimeAxisView *tv;
+ nframes_t where;
+ RouteTimeAxisView *dest_rtv = 0;
+ RouteTimeAxisView *source_rtv = 0;
+
+ track_canvas.window_to_world (x, y, wx, wy);
+ wx += horizontal_adjustment.get_value();
+ wy += vertical_adjustment.get_value();
+
+ GdkEvent event;
+ event.type = GDK_BUTTON_RELEASE;
+ event.button.x = wx;
+ event.button.y = wy;
+
+ where = event_frame (&event, &cx, &cy);
+
+ if ((tv = trackview_by_y_position (cy)) == 0) {
+ return;
+ }
+
+ if ((dest_rtv = dynamic_cast<RouteTimeAxisView*>(tv)) == 0) {
+ return;
+ }
+
+ /* use this drag source to add underlay to a track. But we really don't care
+ about the Route, only the view of the route, so find it first */
+ for(TrackViewList::iterator it = track_views.begin(); it != track_views.end(); ++it) {
+ if((source_rtv = dynamic_cast<RouteTimeAxisView*>(*it)) == 0) {
+ continue;
+ }
+
+ if(source_rtv->route() == route && source_rtv != dest_rtv) {
+ dest_rtv->add_underlay(source_rtv->view());
+ break;
+ }
+ }
+}
+
+void
Editor::insert_region_list_selection (float times)
{
RouteTimeAxisView *tv = 0;
diff --git a/gtk2_ardour/editor_route_list.cc b/gtk2_ardour/editor_route_list.cc
index c422a43859..c2966d506c 100644
--- a/gtk2_ardour/editor_route_list.cc
+++ b/gtk2_ardour/editor_route_list.cc
@@ -613,3 +613,20 @@ Editor::route_list_display_drag_data_received (const RefPtr<Gdk::DragContext>& c
cerr << "some other kind of drag\n";
context->drag_finish (true, false, time);
}
+
+
+RouteTimeAxisView*
+Editor::get_route_view_by_id (PBD::ID& id)
+{
+ RouteTimeAxisView* v;
+
+ for(TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
+ if((v = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
+ if(v->route()->id() == id) {
+ return v;
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/gtk2_ardour/ghostregion.cc b/gtk2_ardour/ghostregion.cc
index b14872b357..e57e3b6375 100644
--- a/gtk2_ardour/ghostregion.cc
+++ b/gtk2_ardour/ghostregion.cc
@@ -20,23 +20,23 @@
#include "simplerect.h"
#include "waveview.h"
#include "ghostregion.h"
+#include "midi_time_axis.h"
#include "automation_time_axis.h"
+#include "midi_streamview.h"
#include "rgb_macros.h"
#include "ardour_ui.h"
+#include "canvas-hit.h"
+#include "canvas-note.h"
using namespace Editing;
using namespace ArdourCanvas;
using namespace ARDOUR;
-GhostRegion::GhostRegion (AutomationTimeAxisView& atv, double initial_pos)
- : trackview (atv)
+GhostRegion::GhostRegion (ArdourCanvas::Group* parent, TimeAxisView& tv, TimeAxisView& source_tv, double initial_pos)
+ : trackview (tv)
+ , source_trackview (source_tv)
{
- //group = gnome_canvas_item_new (GNOME_CANVAS_GROUP(trackview.canvas_display),
- // gnome_canvas_group_get_type(),
- // "x", initial_pos,
- // "y", 0.0,
- // NULL);
- group = new ArdourCanvas::Group (*trackview.canvas_display);
+ group = new ArdourCanvas::Group (*parent);
group->property_x() = initial_pos;
group->property_y() = 0.0;
@@ -45,11 +45,16 @@ GhostRegion::GhostRegion (AutomationTimeAxisView& atv, double initial_pos)
base_rect->property_y1() = (double) 0.0;
base_rect->property_y2() = (double) trackview.height;
base_rect->property_outline_what() = (guint32) 0;
- base_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_GhostTrackBase.get();
- base_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_GhostTrackBase.get();
- group->lower_to_bottom ();
- atv.add_ghost (this);
+ if(!is_automation_ghost()) {
+ base_rect->hide();
+ }
+
+ GhostRegion::set_colors();
+
+ /* the parent group of a ghostregion is a dedicated group for ghosts,
+ so the new ghost would want to get to the top of that group*/
+ group->raise_to_top ();
}
GhostRegion::~GhostRegion ()
@@ -60,27 +65,62 @@ GhostRegion::~GhostRegion ()
}
void
-GhostRegion::set_samples_per_unit (double spu)
+GhostRegion::set_duration (double units)
{
- for (vector<WaveView*>::iterator i = waves.begin(); i != waves.end(); ++i) {
- (*i)->property_samples_per_unit() = spu;
- }
+ base_rect->property_x2() = units;
}
void
-GhostRegion::set_duration (double units)
+GhostRegion::set_height ()
{
- base_rect->property_x2() = units;
+ base_rect->property_y2() = (double) trackview.height;
}
void
-GhostRegion::set_height ()
+GhostRegion::set_colors ()
+{
+ if(is_automation_ghost()) {
+ base_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_GhostTrackBase.get();
+ base_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_GhostTrackBase.get();
+ }
+}
+
+guint
+GhostRegion::source_track_color(unsigned char alpha) {
+ Gdk::Color color = source_trackview.color();
+ unsigned char r,g,b ;
+ r = color.get_red()/256;
+ g = color.get_green()/256;
+ b = color.get_blue()/256;
+ return RGBA_TO_UINT(r, g, b, alpha);
+}
+
+bool
+GhostRegion::is_automation_ghost() {
+ return (dynamic_cast<AutomationTimeAxisView*>(&trackview)) != 0;
+}
+
+AudioGhostRegion::AudioGhostRegion(TimeAxisView& tv, TimeAxisView& source_tv, double initial_unit_pos)
+ : GhostRegion(tv.ghost_group, tv, source_tv, initial_unit_pos) {
+}
+
+void
+AudioGhostRegion::set_samples_per_unit (double spu)
+{
+ for (vector<WaveView*>::iterator i = waves.begin(); i != waves.end(); ++i) {
+ (*i)->property_samples_per_unit() = spu;
+ }
+}
+
+void
+AudioGhostRegion::set_height ()
{
gdouble ht;
vector<WaveView*>::iterator i;
uint32_t n;
- base_rect->property_y2() = (double) trackview.height;
+ GhostRegion::set_height();
+
ht = ((trackview.height) / (double) waves.size());
for (n = 0, i = waves.begin(); i != waves.end(); ++i, ++n) {
@@ -91,16 +131,177 @@ GhostRegion::set_height ()
}
void
-GhostRegion::set_colors ()
+AudioGhostRegion::set_colors ()
{
- base_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_GhostTrackBase.get();
- base_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_GhostTrackBase.get();
+ GhostRegion::set_colors();
+ guint fill_color;
+
+ if(is_automation_ghost()) {
+ fill_color = ARDOUR_UI::config()->canvasvar_GhostTrackWaveFill.get();
+ }
+ else {
+ fill_color = source_track_color(200);
+ }
+
+ for (uint32_t n=0; n < waves.size(); ++n) {
+ waves[n]->property_wave_color() = ARDOUR_UI::config()->canvasvar_GhostTrackWave.get();
+ waves[n]->property_fill_color() = fill_color;
+ waves[n]->property_clip_color() = ARDOUR_UI::config()->canvasvar_GhostTrackWaveClip.get();
+ waves[n]->property_zero_color() = ARDOUR_UI::config()->canvasvar_GhostTrackZeroLine.get();
+ }
+}
+
+/*
+ * This is the general constructor, and is called when the destination timeaxisview doesn't have
+ * a midistreamview. But what to do when positioning the midi ghost here? For example, there is
+ * no range controller in these tracks. maybe show the whole range.
+ */
+MidiGhostRegion::MidiGhostRegion(TimeAxisView& tv, TimeAxisView& source_tv, double initial_unit_pos)
+ : GhostRegion(tv.ghost_group, tv, source_tv, initial_unit_pos) {
+
+ base_rect->lower_to_bottom();
+}
+
+MidiGhostRegion::MidiGhostRegion(MidiStreamView& msv, TimeAxisView& source_tv, double initial_unit_pos)
+ : GhostRegion(msv.midi_underlay_group, msv.trackview(), source_tv, initial_unit_pos) {
+
+ base_rect->lower_to_bottom();
+}
+
+MidiGhostRegion::Event::Event(ArdourCanvas::CanvasMidiEvent* e)
+ : event(e) {
+}
+
+MidiGhostRegion::Note::Note(ArdourCanvas::CanvasNote* n, ArdourCanvas::Group* g)
+ : Event(n) {
+
+ rect = new ArdourCanvas::SimpleRect(*g, n->x1(), n->y1(), n->x2(), n->y2());
+}
- for (uint32_t n=0; n < waves.size(); ++n) {
- waves[n]->property_wave_color() = ARDOUR_UI::config()->canvasvar_GhostTrackWave.get();
- waves[n]->property_fill_color() = ARDOUR_UI::config()->canvasvar_GhostTrackWave.get();
+MidiGhostRegion::Note::~Note() {
+ delete rect;
+}
+
+void
+MidiGhostRegion::Note::x_changed() {
+ rect->property_x1() = event->x1();
+ rect->property_x2() = event->x2();
+}
+
+MidiGhostRegion::Hit::Hit(ArdourCanvas::CanvasHit* h, ArdourCanvas::Group*)
+ : Event(h) {
+ cerr << "Hit ghost item does not work yet" << endl;
+}
+
+MidiGhostRegion::Hit::~Hit() {
+}
+
+void
+MidiGhostRegion::Hit::x_changed() {
+}
+
+void
+MidiGhostRegion::set_samples_per_unit (double spu)
+{
+}
+
+MidiStreamView*
+MidiGhostRegion::midi_view() {
+ MidiTimeAxisView* mtv;
+
+ if((mtv = dynamic_cast<MidiTimeAxisView*>(&trackview)) != 0) {
+ return mtv->midi_view();
+ }
+ else {
+ return 0;
+ }
+}
+
+void
+MidiGhostRegion::set_height() {
+ GhostRegion::set_height();
+ update_range();
+}
+
+void
+MidiGhostRegion::set_colors() {
+ MidiGhostRegion::Note* note;
+ guint fill = source_track_color(200);
+
+ GhostRegion::set_colors();
+
+ for(EventList::iterator it = events.begin(); it != events.end(); ++it) {
+ if((note = dynamic_cast<MidiGhostRegion::Note*>(*it)) != 0) {
+ note->rect->property_fill_color_rgba() = fill;
+ note->rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_GhostTrackMidiOutline.get();
+ }
+ }
+}
+
+void
+MidiGhostRegion::update_range() {
+ MidiStreamView* mv = midi_view();
+
+ if(!mv) {
+ return;
+ }
+
+ MidiGhostRegion::Note* note;
+ uint8_t note_num;
+ double y;
+
+ for(EventList::iterator it = events.begin(); it != events.end(); ++it) {
+ if((note = dynamic_cast<MidiGhostRegion::Note*>(*it)) != 0) {
+ note_num = note->event->note()->note();
+
+ if(note_num < mv->lowest_note() || note_num > mv->highest_note()) {
+ note->rect->hide();
+ }
+ else {
+ note->rect->show();
+ y = mv->note_to_y(note_num);
+ note->rect->property_y1() = y;
+ note->rect->property_y2() = y + mv->note_height();
+ }
+ }
+ }
+}
+
+void
+MidiGhostRegion::add_note(ArdourCanvas::CanvasNote* n) {
+ Note* note = new Note(n, group);
+ events.push_back(note);
+
+ note->rect->property_fill_color_rgba() = source_track_color(200);
+ note->rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_GhostTrackMidiOutline.get();
+
+ MidiStreamView* mv = midi_view();
+
+ if(mv) {
+ uint8_t note_num = n->note()->note();
+ double y;
+
+ if(note_num < mv->lowest_note() || note_num > mv->highest_note()) {
+ note->rect->hide();
+ }
+ else {
+ y = mv->note_to_y(note_num);
+ note->rect->property_y1() = y;
+ note->rect->property_y2() = y + mv->note_height();
+ }
+ }
+}
+
+void
+MidiGhostRegion::add_hit(ArdourCanvas::CanvasHit* h) {
+ //events.push_back(new Hit(h, group));
+}
+
+void
+MidiGhostRegion::clear_events() {
+ for(EventList::iterator it = events.begin(); it != events.end(); ++it) {
+ delete *it;
+ }
- waves[n]->property_clip_color() = ARDOUR_UI::config()->canvasvar_GhostTrackWaveClip.get();
- waves[n]->property_zero_color() = ARDOUR_UI::config()->canvasvar_GhostTrackZeroLine.get();
- }
+ events.clear();
}
diff --git a/gtk2_ardour/ghostregion.h b/gtk2_ardour/ghostregion.h
index 16d945f543..ba02762d0b 100644
--- a/gtk2_ardour/ghostregion.h
+++ b/gtk2_ardour/ghostregion.h
@@ -25,26 +25,100 @@
#include <libgnomecanvasmm.h>
#include <ardour/configuration.h>
#include "canvas.h"
-#include "simplerect.h"
-class AutomationTimeAxisView;
+namespace Gnome {
+ namespace Canvas {
+ class CanvasMidiEvent;
+ class CanvasNote;
+ class CanvasHit;
+ class Diamond;
+ }
+}
-struct GhostRegion : public sigc::trackable
+class MidiStreamView;
+class TimeAxisView;
+
+class GhostRegion : public sigc::trackable
{
- AutomationTimeAxisView& trackview;
- ArdourCanvas::Group* group;
- ArdourCanvas::SimpleRect* base_rect;
- std::vector<ArdourCanvas::WaveView*> waves;
+public:
+ GhostRegion(ArdourCanvas::Group* parent, TimeAxisView& tv, TimeAxisView& source_tv, double initial_unit_pos);
+ virtual ~GhostRegion();
+
+ virtual void set_samples_per_unit(double spu) = 0;
+ virtual void set_height();
+ virtual void set_colors();
+
+ void set_duration(double units);
+
+ guint source_track_color(unsigned char alpha = 0xff);
+ bool is_automation_ghost();
+
+ sigc::signal<void,GhostRegion*> GoingAway;
+
+ TimeAxisView& trackview;
+ TimeAxisView& source_trackview;
+ ArdourCanvas::Group* group;
+ ArdourCanvas::SimpleRect* base_rect;
+};
+
+class AudioGhostRegion : public GhostRegion {
+public:
+ AudioGhostRegion(TimeAxisView& tv, TimeAxisView& source_tv, double initial_unit_pos);
+
+ void set_samples_per_unit(double spu);
+ void set_height();
+ void set_colors();
+
+ std::vector<ArdourCanvas::WaveView*> waves;
+};
+
+class MidiGhostRegion : public GhostRegion {
+public:
+ class Event : public sigc::trackable {
+ public:
+ Event(ArdourCanvas::CanvasMidiEvent*);
+ virtual ~Event() {}
+
+ virtual void x_changed() = 0;
+ ArdourCanvas::CanvasMidiEvent* event;
+ };
+
+ class Note : public Event {
+ public:
+ Note(ArdourCanvas::CanvasNote*, ArdourCanvas::Group*);
+ ~Note();
+
+ void x_changed();
+ ArdourCanvas::SimpleRect* rect;
+ };
+
+ class Hit : public Event {
+ public:
+ Hit(ArdourCanvas::CanvasHit*, ArdourCanvas::Group*);
+ ~Hit();
+
+ void x_changed();
+ ArdourCanvas::Diamond* diamond;
+ };
+
+ MidiGhostRegion(TimeAxisView& tv, TimeAxisView& source_tv, double initial_unit_pos);
+ MidiGhostRegion(MidiStreamView& msv, TimeAxisView& source_tv, double initial_unit_pos);
+
+ MidiStreamView* midi_view();
+
+ void set_height();
+ void set_samples_per_unit(double spu);
+ void set_colors();
+
+ void update_range();
- GhostRegion (AutomationTimeAxisView& tv, double initial_unit_pos);
- ~GhostRegion ();
+ void add_note(ArdourCanvas::CanvasNote*);
+ void add_hit(ArdourCanvas::CanvasHit*);
- void set_samples_per_unit (double spu);
- void set_duration (double units);
- void set_height ();
- void set_colors ();
+ void clear_events();
- sigc::signal<void,GhostRegion*> GoingAway;
+ typedef std::list<MidiGhostRegion::Event*> EventList;
+ EventList events;
};
#endif /* __ardour_gtk_ghost_region_h__ */
diff --git a/gtk2_ardour/lineset.cc b/gtk2_ardour/lineset.cc
index 46c06705bb..75fdb938ae 100644
--- a/gtk2_ardour/lineset.cc
+++ b/gtk2_ardour/lineset.cc
@@ -489,12 +489,6 @@ Lineset::update_bounds() {
}
/*
- * Some key concepts
- * don't allow modifying line data outside the update function. We don't want any line data outside the visible view range,
- * and view range is only "known" in the update function
- */
-
-/*
* what to do here?
* 1. find out if any line data has been modified since last update.
* N. find out if the item moved. if it moved, the old bbox and the new bbox need to be updated.
diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc
index a16da1448f..a8692af77a 100644
--- a/gtk2_ardour/midi_region_view.cc
+++ b/gtk2_ardour/midi_region_view.cc
@@ -380,6 +380,14 @@ MidiRegionView::clear_events()
for (std::vector<CanvasMidiEvent*>::iterator i = _events.begin(); i != _events.end(); ++i)
delete *i;
+ MidiGhostRegion* gr;
+
+ for(vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
+ if((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
+ gr->clear_events();
+ }
+ }
+
_events.clear();
}
@@ -505,11 +513,23 @@ MidiRegionView::set_y_position_and_height (double y, double h)
for (std::vector<CanvasMidiEvent*>::const_iterator i = _events.begin(); i != _events.end(); ++i) {
CanvasNote* note = dynamic_cast<CanvasNote*>(*i);
if (note && note->note()) {
- const double y1 = midi_stream_view()->note_to_y(note->note()->note());
- const double y2 = y1 + floor(midi_stream_view()->note_height());
-
- note->property_y1() = y1;
- note->property_y2() = y2;
+ if(note->note()->note() < midi_stream_view()->lowest_note() ||
+ note->note()->note() > midi_stream_view()->highest_note()) {
+ if(canvas_item_visible(note)) {
+ note->hide();
+ }
+ }
+ else {
+ const double y1 = midi_stream_view()->note_to_y(note->note()->note());
+ const double y2 = y1 + floor(midi_stream_view()->note_height());
+
+ if(!canvas_item_visible(note)) {
+ note->show();
+ }
+
+ note->property_y1() = y1;
+ note->property_y2() = y2;
+ }
}
}
@@ -522,18 +542,37 @@ MidiRegionView::set_y_position_and_height (double y, double h)
}
GhostRegion*
-MidiRegionView::add_ghost (AutomationTimeAxisView& atv)
+MidiRegionView::add_ghost (TimeAxisView& tv)
{
RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&trackview);
+ CanvasNote* note;
assert(rtv);
double unit_position = _region->position () / samples_per_unit;
- GhostRegion* ghost = new GhostRegion (atv, unit_position);
+ MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
+ MidiGhostRegion* ghost;
+
+ if(mtv && mtv->midi_view()) {
+ /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group.
+ this is because it's nice to have midi notes on top of the note lines and
+ audio waveforms under it.
+ */
+ ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
+ }
+ else {
+ ghost = new MidiGhostRegion (tv, trackview, unit_position);
+ }
ghost->set_height ();
ghost->set_duration (_region->length() / samples_per_unit);
ghosts.push_back (ghost);
+ for (std::vector<CanvasMidiEvent*>::iterator i = _events.begin(); i != _events.end(); ++i) {
+ if((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
+ ghost->add_note(note);
+ }
+ }
+
ghost->GoingAway.connect (mem_fun(*this, &MidiRegionView::remove_ghost));
return ghost;
@@ -638,6 +677,14 @@ MidiRegionView::add_note(const boost::shared_ptr<Note> note)
ev_rect->show();
_events.push_back(ev_rect);
+ MidiGhostRegion* gr;
+
+ for(vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
+ if((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
+ gr->add_note(ev_rect);
+ }
+ }
+
} else if (midi_view()->note_mode() == Percussive) {
//cerr << "MRV::add_note percussive " << note->note() << " @ " << note->time()
diff --git a/gtk2_ardour/midi_region_view.h b/gtk2_ardour/midi_region_view.h
index a709f523a3..2f4e4fcb01 100644
--- a/gtk2_ardour/midi_region_view.h
+++ b/gtk2_ardour/midi_region_view.h
@@ -76,7 +76,7 @@ class MidiRegionView : public RegionView
void redisplay_model();
- GhostRegion* add_ghost (AutomationTimeAxisView&);
+ GhostRegion* add_ghost (TimeAxisView&);
void add_note(const boost::shared_ptr<ARDOUR::Note> note);
void resolve_note(uint8_t note_num, double end_time);
diff --git a/gtk2_ardour/midi_scroomer.cc b/gtk2_ardour/midi_scroomer.cc
index a38911d98c..fc63f89611 100644
--- a/gtk2_ardour/midi_scroomer.cc
+++ b/gtk2_ardour/midi_scroomer.cc
@@ -54,7 +54,7 @@ MidiScroomer::on_expose_event(GdkEventExpose* ev) {
double note_width = 0.8 * get_width();
double note_height = 1.4 * note2y;
double black_shift = 0.1 * note2y;
- double colors[6];
+ double colors[6] = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
//cerr << ev->area.y << " " << ev->area.height << endl;
@@ -71,8 +71,8 @@ MidiScroomer::on_expose_event(GdkEventExpose* ev) {
cc->rectangle(clip_rect.x, clip_rect.y, clip_rect.width, clip_rect.height);
cc->set_source_rgb (colors[3], colors[4], colors[5]);
cc->fill_preserve();
-
cc->clip();
+
cc->set_source_rgb(colors[0], colors[1], colors[2]);
cc->set_line_width(note_height);
@@ -109,6 +109,13 @@ MidiScroomer::on_expose_event(GdkEventExpose* ev) {
}
}
+ if(i == Handle1 || i == Handle2) {
+ cc->rectangle(comp_rect.x + 0.5f, comp_rect.y + 0.5f, comp_rect.width - 1.0f, comp_rect.height - 1.0f);
+ cc->set_line_width(1.0f);
+ cc->set_source_rgb (1.0f, 1.0f, 1.0f);
+ cc->stroke();
+ }
+
cc->reset_clip();
}
}
@@ -121,29 +128,29 @@ MidiScroomer::get_colors(double color[], Component comp) {
switch (comp) {
case TopBase:
case BottomBase:
- color[0] = 0.24;
- color[1] = 0.24;
- color[2] = 0.24;
- color[3] = 0.33;
- color[4] = 0.33;
- color[5] = 0.33;
+ color[0] = 0.24f;
+ color[1] = 0.24f;
+ color[2] = 0.24f;
+ color[3] = 0.33f;
+ color[4] = 0.33f;
+ color[5] = 0.33f;
break;
case Handle1:
case Handle2:
- color[0] = 0.38;
- color[1] = 0.38;
- color[2] = 0.38;
- color[3] = 0.91;
- color[4] = 0.91;
- color[5] = 0.91;
+ color[0] = 0.91f;
+ color[1] = 0.91f;
+ color[2] = 0.91f;
+ color[3] = 0.0f;
+ color[4] = 0.0f;
+ color[5] = 0.0f;
break;
case Slider:
- color[0] = 0.38;
- color[1] = 0.38;
- color[2] = 0.38;
- color[3] = 0.77;
- color[4] = 0.77;
- color[5] = 0.77;
+ color[0] = 0.38f;
+ color[1] = 0.38f;
+ color[2] = 0.38f;
+ color[3] = 0.77f;
+ color[4] = 0.77f;
+ color[5] = 0.77f;
break;
default:
break;
diff --git a/gtk2_ardour/midi_streamview.cc b/gtk2_ardour/midi_streamview.cc
index a1deba9ba9..81be583496 100644
--- a/gtk2_ardour/midi_streamview.cc
+++ b/gtk2_ardour/midi_streamview.cc
@@ -68,6 +68,12 @@ MidiStreamView::MidiStreamView (MidiTimeAxisView& tv)
use_rec_regions = tv.editor.show_waveforms_recording ();
+ /* use a group dedicated to MIDI underlays. Audio underlays are not in this group. */
+ midi_underlay_group = new ArdourCanvas::Group (*canvas_group);
+ midi_underlay_group->lower_to_bottom();
+
+ /* put the note lines in the timeaxisview's group, so it
+ can be put below ghost regions from MIDI underlays*/
_note_lines = new ArdourCanvas::Lineset(*canvas_group, ArdourCanvas::Lineset::Horizontal);
_note_lines->property_x1() = 0;
@@ -76,6 +82,7 @@ MidiStreamView::MidiStreamView (MidiTimeAxisView& tv)
_note_lines->property_y2() = 0;
_note_lines->signal_event().connect (bind (mem_fun (_trackview.editor, &PublicEditor::canvas_stream_view_event), _note_lines, &_trackview));
+ _note_lines->lower_to_bottom();
note_range_adjustment.signal_value_changed().connect (mem_fun (*this, &MidiStreamView::note_range_adjustment_changed));
ColorsChanged.connect(mem_fun(*this, &MidiStreamView::draw_note_lines));
diff --git a/gtk2_ardour/midi_streamview.h b/gtk2_ardour/midi_streamview.h
index 5c0ee47c30..60dde8cc85 100644
--- a/gtk2_ardour/midi_streamview.h
+++ b/gtk2_ardour/midi_streamview.h
@@ -67,6 +67,7 @@ class MidiStreamView : public StreamView
};
Gtk::Adjustment note_range_adjustment;
+ ArdourCanvas::Group* midi_underlay_group;
VisibleNoteRange note_range() { return _range; }
void set_note_range(VisibleNoteRange r);
diff --git a/gtk2_ardour/midi_time_axis.cc b/gtk2_ardour/midi_time_axis.cc
index 534305eaa6..cb72363f88 100644
--- a/gtk2_ardour/midi_time_axis.cc
+++ b/gtk2_ardour/midi_time_axis.cc
@@ -69,6 +69,7 @@
#include "utils.h"
#include "midi_scroomer.h"
#include "piano_roll_header.h"
+#include "ghostregion.h"
#include <ardour/midi_track.h>
@@ -126,13 +127,25 @@ MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session& sess, boost::shar
controls_base_selected_name = "MidiTrackControlsBaseSelected";
controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
+ midi_view()->NoteRangeChanged.connect (mem_fun(*this, &MidiTimeAxisView::update_range));
+
/* ask for notifications of any new RegionViews */
+ _view->RegionViewAdded.connect (mem_fun(*this, &MidiTimeAxisView::region_view_added));
_view->attach ();
}
}
MidiTimeAxisView::~MidiTimeAxisView ()
{
+ if(_piano_roll_header) {
+ delete _piano_roll_header;
+ _piano_roll_header = 0;
+ }
+
+ if(_range_scroomer) {
+ delete _range_scroomer;
+ _range_scroomer = 0;
+ }
}
MidiStreamView*
@@ -248,6 +261,17 @@ MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
}
+void
+MidiTimeAxisView::update_range() {
+ MidiGhostRegion* mgr;
+
+ for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
+ if((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
+ mgr->update_range();
+ }
+ }
+}
+
/** Prompt for a controller with a dialog and add an automation track for it
*/
void
diff --git a/gtk2_ardour/midi_time_axis.h b/gtk2_ardour/midi_time_axis.h
index 15e487e00e..efc987760b 100644
--- a/gtk2_ardour/midi_time_axis.h
+++ b/gtk2_ardour/midi_time_axis.h
@@ -70,6 +70,8 @@ class MidiTimeAxisView : public RouteTimeAxisView
ARDOUR::NoteMode note_mode() const { return _note_mode; }
+ void update_range();
+
private:
void append_extra_display_menu_items ();
diff --git a/gtk2_ardour/piano_roll_header.cc b/gtk2_ardour/piano_roll_header.cc
index 95ebba4aeb..5032e97e91 100644
--- a/gtk2_ardour/piano_roll_header.cc
+++ b/gtk2_ardour/piano_roll_header.cc
@@ -60,7 +60,8 @@ PianoRollHeader::Color::set(const PianoRollHeader::Color& c) {
PianoRollHeader::PianoRollHeader(MidiStreamView& v)
: _view(v)
, _highlighted_note(no_note)
- , _clicked_note(no_note) {
+ , _clicked_note(no_note)
+ , _dragging(false) {
add_events (Gdk::BUTTON_PRESS_MASK |
Gdk::BUTTON_RELEASE_MASK |
@@ -295,7 +296,7 @@ PianoRollHeader::on_expose_event (GdkEventExpose* ev) {
}
cr->select_font_face ("Georgia", Cairo::FONT_SLANT_NORMAL, Cairo::FONT_WEIGHT_BOLD);
- font_size = min(10.0, _note_height);
+ font_size = min((double) 10.0f, _note_height - 4.0f);
cr->set_font_size(font_size);
/* fill the entire rect with the color for non-highlighted white notes.
@@ -445,7 +446,7 @@ PianoRollHeader::on_expose_event (GdkEventExpose* ev) {
//cr->get_text_extents(s.str(), te);
cr->set_source_rgb(0.30f, 0.30f, 0.30f);
- cr->move_to(0, y + font_size + (note_height - font_size) / 2.0f);
+ cr->move_to(2.0f, y + note_height - 1.0f - (note_height - font_size) / 2.0f);
cr->show_text(s.str());
}
}
@@ -491,6 +492,9 @@ PianoRollHeader::on_button_press_event (GdkEventButton* ev) {
int note = _view.y_to_note(ev->y);
if(ev->type == GDK_BUTTON_PRESS && note >= 0 && note < 128) {
+ add_modal_grab();
+ _dragging = true;
+
if(!_active_notes[note]) {
_active_notes[note] = true;
_clicked_note = note;
@@ -510,12 +514,17 @@ bool
PianoRollHeader::on_button_release_event (GdkEventButton* ev) {
int note = _view.y_to_note(ev->y);
- if(note == _clicked_note) {
- _active_notes[note] = false;
- _clicked_note = no_note;
- send_note_off(note);
-
- invalidate_note_range(note, note);
+ if(_dragging) {
+ remove_modal_grab();
+ _dragging = false;
+
+ if(note == _clicked_note) {
+ _active_notes[note] = false;
+ _clicked_note = no_note;
+ send_note_off(note);
+
+ invalidate_note_range(note, note);
+ }
}
return true;
@@ -524,6 +533,7 @@ PianoRollHeader::on_button_release_event (GdkEventButton* ev) {
bool
PianoRollHeader::on_enter_notify_event (GdkEventCrossing* ev) {
_highlighted_note = _view.y_to_note(ev->y);
+
invalidate_note_range(_highlighted_note, _highlighted_note);
return true;
}
diff --git a/gtk2_ardour/piano_roll_header.h b/gtk2_ardour/piano_roll_header.h
index 141e7663f5..8333284ad9 100644
--- a/gtk2_ardour/piano_roll_header.h
+++ b/gtk2_ardour/piano_roll_header.h
@@ -96,6 +96,7 @@ private:
uint8_t _highlighted_note;
uint8_t _clicked_note;
double _grab_y;
+ bool _dragging;
double _note_height;
double _black_note_width;
diff --git a/gtk2_ardour/public_editor.h b/gtk2_ardour/public_editor.h
index 3f2c90e05a..5081a207cf 100644
--- a/gtk2_ardour/public_editor.h
+++ b/gtk2_ardour/public_editor.h
@@ -264,6 +264,8 @@ class PublicEditor : public Gtk::Window, public PBD::StatefulThingWithGoingAway
virtual TimeAxisView* get_named_time_axis(const std::string & name) = 0;
#endif
+ virtual RouteTimeAxisView* get_route_view_by_id (PBD::ID& id) = 0;
+
virtual void get_equivalent_regions (RegionView* rv, std::vector<RegionView*>&) const = 0;
sigc::signal<void> ZoomFocusChanged;
diff --git a/gtk2_ardour/region_view.cc b/gtk2_ardour/region_view.cc
index fa9ec5969b..fe15fc9668 100644
--- a/gtk2_ardour/region_view.cc
+++ b/gtk2_ardour/region_view.cc
@@ -530,6 +530,16 @@ RegionView::move (double x_delta, double y_delta)
}
void
+RegionView::remove_ghost_in (TimeAxisView& tv) {
+ for (vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
+ if (&(*i)->trackview == &tv) {
+ delete *i;
+ break;
+ }
+ }
+}
+
+void
RegionView::remove_ghost (GhostRegion* ghost)
{
if (in_destructor) {
diff --git a/gtk2_ardour/region_view.h b/gtk2_ardour/region_view.h
index e5822715ca..33b4a7a5e9 100644
--- a/gtk2_ardour/region_view.h
+++ b/gtk2_ardour/region_view.h
@@ -77,8 +77,9 @@ class RegionView : public TimeAxisViewItem
virtual void region_changed (ARDOUR::Change);
- virtual GhostRegion* add_ghost (AutomationTimeAxisView&) = 0;
- void remove_ghost (GhostRegion*);
+ virtual GhostRegion* add_ghost (TimeAxisView&) = 0;
+ void remove_ghost_in (TimeAxisView&);
+ void remove_ghost (GhostRegion*);
uint32_t get_fill_color ();
diff --git a/gtk2_ardour/route_time_axis.cc b/gtk2_ardour/route_time_axis.cc
index 12a8ee67bb..bd6f8db95e 100644
--- a/gtk2_ardour/route_time_axis.cc
+++ b/gtk2_ardour/route_time_axis.cc
@@ -95,7 +95,8 @@ RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session& sess, boost::sh
size_button (_("h")), // height
automation_button (_("a")),
visual_button (_("v")),
- lm (rt, sess)
+ lm (rt, sess),
+ underlay_xml_node (0)
{
lm.set_no_show_all();
lm.setup_meters(50);
@@ -302,28 +303,34 @@ RouteTimeAxisView::set_state (const XMLNode& node)
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
child_node = *niter;
- if (child_node->name() != AutomationTimeAxisView::state_node_name)
- continue;
-
- XMLProperty* prop = child_node->property ("automation-id");
- if (!prop)
- continue;
-
- Parameter param(prop->value());
- if (!param)
- continue;
-
- bool show = false;
-
- prop = child_node->property ("shown");
-
- if (prop && prop->value() == "yes") {
- show = true;
- _show_automation.insert(param);
+ if (child_node->name() == AutomationTimeAxisView::state_node_name) {
+ XMLProperty* prop = child_node->property ("automation-id");
+ if (!prop)
+ continue;
+
+ Parameter param(prop->value());
+ if (!param)
+ continue;
+
+ bool show = false;
+
+ prop = child_node->property ("shown");
+
+ if (prop && prop->value() == "yes") {
+ show = true;
+ _show_automation.insert(param);
+ }
+
+ if (_automation_tracks.find(param) == _automation_tracks.end()) {
+ create_automation_child(param, show);
+ }
+ }
+ else if (child_node->name() == "Underlays") {
+ underlay_xml_node = child_node;
+
+ /* Wait for all gui tracks to be loaded as underlays are cross referencing tracks*/
+ Glib::signal_idle().connect(mem_fun(*this, &RouteTimeAxisView::set_underlay_state));
}
-
- if (_automation_tracks.find(param) == _automation_tracks.end())
- create_automation_child(param, show);
}
}
@@ -1694,19 +1701,20 @@ RouteTimeAxisView::hide_all_automation ()
void
RouteTimeAxisView::region_view_added (RegionView* rv)
{
- for (Children::iterator i = children.begin(); i != children.end(); ++i) {
- boost::shared_ptr<AutomationTimeAxisView> atv;
-
- if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
- rv->add_ghost (*atv.get());
+ /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
+ if(is_audio_track()) {
+ for (Children::iterator i = children.begin(); i != children.end(); ++i) {
+ boost::shared_ptr<AutomationTimeAxisView> atv;
+
+ if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
+ atv->add_ghost(rv);
+ }
}
}
-}
-void
-RouteTimeAxisView::add_ghost_to_processor (RegionView* rv, boost::shared_ptr<AutomationTimeAxisView> atv)
-{
- rv->add_ghost (*atv.get());
+ for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
+ (*i)->add_ghost(rv);
+ }
}
RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
@@ -1812,7 +1820,7 @@ RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor>
add_child (pan->view);
if (_view) {
- _view->foreach_regionview (bind (mem_fun(*this, &RouteTimeAxisView::add_ghost_to_processor), pan->view));
+ _view->foreach_regionview (mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
}
processor->mark_automation_visible (what, true);
@@ -2133,3 +2141,117 @@ RouteTimeAxisView::io_changed (IOChange change, void *src)
{
reset_meter ();
}
+
+void
+RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu) {
+ using namespace Menu_Helpers;
+
+ if(!_underlay_streams.empty()) {
+ MenuList& parent_items = parent_menu->items();
+ Menu* gs_menu = manage (new Menu);
+ gs_menu->set_name ("ArdourContextMenu");
+ MenuList& gs_items = gs_menu->items();
+
+ parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
+
+ for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
+ gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
+ bind(mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
+ }
+ }
+}
+
+bool
+RouteTimeAxisView::set_underlay_state()
+{
+ if(!underlay_xml_node) {
+ return false;
+ }
+
+ XMLNodeList nlist = underlay_xml_node->children();
+ XMLNodeConstIterator niter;
+ XMLNode *child_node;
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+ child_node = *niter;
+
+ if(child_node->name() != "Underlay") {
+ continue;
+ }
+
+ XMLProperty* prop = child_node->property ("id");
+ if(prop) {
+ PBD::ID id(prop->value());
+
+ RouteTimeAxisView* v = editor.get_route_view_by_id(id);
+
+ if(v) {
+ add_underlay(v->view(), false);
+ }
+ }
+ }
+
+ return false;
+}
+
+void
+RouteTimeAxisView::add_underlay(StreamView* v, bool update_xml)
+{
+ if(!v) {
+ return;
+ }
+
+ RouteTimeAxisView& other = v->trackview();
+
+ if(find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
+ if(find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
+ fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
+ /*NOTREACHED*/
+ }
+
+ _underlay_streams.push_back(v);
+ other._underlay_mirrors.push_back(this);
+
+ v->foreach_regionview(mem_fun(*this, &RouteTimeAxisView::add_ghost));
+
+ if(update_xml) {
+ if(!underlay_xml_node) {
+ ensure_xml_node();
+ underlay_xml_node = xml_node->add_child("Underlays");
+ }
+
+ XMLNode* node = underlay_xml_node->add_child("Underlay");
+ XMLProperty* prop = node->add_property("id");
+ prop->set_value(v->trackview().route()->id().to_s());
+ }
+ }
+}
+
+void
+RouteTimeAxisView::remove_underlay(StreamView* v)
+{
+ if(!v) {
+ return;
+ }
+
+ UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
+ RouteTimeAxisView& other = v->trackview();
+
+ if(it != _underlay_streams.end()) {
+ UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
+
+ if(gm == other._underlay_mirrors.end()) {
+ fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
+ /*NOTREACHED*/
+ }
+
+ v->foreach_regionview(mem_fun(*this, &RouteTimeAxisView::remove_ghost));
+
+ _underlay_streams.erase(it);
+ other._underlay_mirrors.erase(gm);
+
+ if(underlay_xml_node) {
+ underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
+ }
+ }
+}
diff --git a/gtk2_ardour/route_time_axis.h b/gtk2_ardour/route_time_axis.h
index 294a0e73ff..6978524246 100644
--- a/gtk2_ardour/route_time_axis.h
+++ b/gtk2_ardour/route_time_axis.h
@@ -98,6 +98,10 @@ public:
void clear_playlist ();
void build_playlist_menu (Gtk::Menu *);
+
+ void add_underlay (StreamView*, bool update_xml = true);
+ void remove_underlay (StreamView*);
+ void build_underlay_menu(Gtk::Menu*);
/* This is a bit nasty to expose :/ */
struct RouteAutomationNode {
@@ -239,8 +243,7 @@ protected:
void color_handler ();
void region_view_added (RegionView*);
- void add_ghost_to_processor (RegionView*, boost::shared_ptr<AutomationTimeAxisView>);
-
+
StreamView* _view;
ArdourCanvas::Canvas& parent_canvas;
bool no_redraw;
@@ -296,6 +299,14 @@ protected:
XMLNode* get_automation_child_xml_node (ARDOUR::Parameter param);
LevelMeter lm;
+
+ XMLNode* underlay_xml_node;
+ bool set_underlay_state();
+
+ typedef list<StreamView*> UnderlayList;
+ UnderlayList _underlay_streams;
+ typedef list<RouteTimeAxisView*> UnderlayMirrorList;
+ UnderlayMirrorList _underlay_mirrors;
};
#endif /* __ardour_route_time_axis_h__ */
diff --git a/gtk2_ardour/streamview.cc b/gtk2_ardour/streamview.cc
index 633e60e252..d1ed929f46 100644
--- a/gtk2_ardour/streamview.cc
+++ b/gtk2_ardour/streamview.cc
@@ -62,7 +62,6 @@ StreamView::StreamView (RouteTimeAxisView& tv, ArdourCanvas::Group* group)
{
/* set_position() will position the group */
- canvas_rect = new ArdourCanvas::SimpleRect (*canvas_group);
canvas_rect->property_x1() = 0.0;
canvas_rect->property_y1() = 0.0;
canvas_rect->property_x2() = _trackview.editor.frame_to_pixel (max_frames);
diff --git a/gtk2_ardour/time_axis_view.cc b/gtk2_ardour/time_axis_view.cc
index abe4e93043..f64ee3f4e9 100644
--- a/gtk2_ardour/time_axis_view.cc
+++ b/gtk2_ardour/time_axis_view.cc
@@ -42,6 +42,8 @@
#include "ardour_ui.h"
#include "public_editor.h"
#include "time_axis_view.h"
+#include "region_view.h"
+#include "ghostregion.h"
#include "simplerect.h"
#include "simpleline.h"
#include "selection.h"
@@ -83,7 +85,11 @@ TimeAxisView::TimeAxisView (ARDOUR::Session& sess, PublicEditor& ed, TimeAxisVie
}
canvas_display = new Group (*canvas.root(), 0.0, 0.0);
-
+
+ ghost_group = new Group (*canvas_display);
+ ghost_group->lower_to_bottom();
+ ghost_group->show();
+
selection_group = new Group (*canvas_display);
selection_group->hide();
@@ -92,6 +98,7 @@ TimeAxisView::TimeAxisView (ARDOUR::Session& sess, PublicEditor& ed, TimeAxisVie
size_menu = 0;
_marked_for_display = false;
_hidden = false;
+ in_destructor = false;
height = 0;
effective_height = 0;
parent = rent;
@@ -156,6 +163,12 @@ TimeAxisView::TimeAxisView (ARDOUR::Session& sess, PublicEditor& ed, TimeAxisVie
TimeAxisView::~TimeAxisView()
{
+ in_destructor = true;
+
+ for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
+ delete *i;
+ }
+
for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
delete (*i)->rect;
delete (*i)->start_trim;
@@ -356,6 +369,10 @@ TimeAxisView::set_height (TrackHeight h)
{
height_style = h;
set_height_pixels (height_to_pixels (h));
+
+ for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
+ (*i)->set_height ();
+ }
}
void
@@ -846,6 +863,37 @@ TimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& resul
return;
}
+void
+TimeAxisView::add_ghost (RegionView* rv) {
+ GhostRegion* gr = rv->add_ghost (*this);
+
+ if(gr) {
+ ghosts.push_back(gr);
+ gr->GoingAway.connect (mem_fun(*this, &TimeAxisView::erase_ghost));
+ }
+}
+
+void
+TimeAxisView::remove_ghost (RegionView* rv) {
+ rv->remove_ghost_in (*this);
+}
+
+void
+TimeAxisView::erase_ghost (GhostRegion* gr) {
+ if(in_destructor) {
+ return;
+ }
+
+ list<GhostRegion*>::iterator i;
+
+ for (i = ghosts.begin(); i != ghosts.end(); ++i) {
+ if ((*i) == gr) {
+ ghosts.erase (i);
+ break;
+ }
+ }
+}
+
bool
TimeAxisView::touched (double top, double bot)
{
@@ -1055,7 +1103,10 @@ TimeAxisView::hide_name_entry ()
void
TimeAxisView::color_handler ()
{
-
+ for (list<GhostRegion*>::iterator i=ghosts.begin(); i != ghosts.end(); i++ ) {
+ (*i)->set_colors();
+ }
+
for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
(*i)->rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectionRect.get();
diff --git a/gtk2_ardour/time_axis_view.h b/gtk2_ardour/time_axis_view.h
index c210d6deb0..048c60dce6 100644
--- a/gtk2_ardour/time_axis_view.h
+++ b/gtk2_ardour/time_axis_view.h
@@ -61,6 +61,8 @@ class PointSelection;
class TimeAxisViewItem;
class Selection;
class Selectable;
+class RegionView;
+class GhostRegion;
/** Abstract base class for time-axis views (horizontal editor 'strips')
*
@@ -202,6 +204,12 @@ class TimeAxisView : public virtual AxisView
virtual void get_selectables (nframes_t start, nframes_t end, double top, double bot, list<Selectable*>& results);
virtual void get_inverted_selectables (Selection&, list<Selectable *>& results);
+ ArdourCanvas::Group* ghost_group;
+
+ void add_ghost (RegionView*);
+ void remove_ghost (RegionView*);
+ void erase_ghost (GhostRegion*);
+
/* state/serialization management */
TimeAxisView* get_parent () { return parent; }
@@ -296,6 +304,8 @@ class TimeAxisView : public virtual AxisView
ArdourCanvas::Group *selection_group;
+ list<GhostRegion*> ghosts;
+
list<SelectionRect*> free_selection_rects;
list<SelectionRect*> used_selection_rects;
@@ -305,6 +315,7 @@ class TimeAxisView : public virtual AxisView
bool _hidden;
bool _has_state;
+ bool in_destructor;
NamePackingBits name_packing;
static void compute_controls_size_info ();
diff --git a/libs/gtkmm2ext/scroomer.cc b/libs/gtkmm2ext/scroomer.cc
index b9be460de3..6572c8b51c 100644
--- a/libs/gtkmm2ext/scroomer.cc
+++ b/libs/gtkmm2ext/scroomer.cc
@@ -202,8 +202,6 @@ Scroomer::on_button_press_event (GdkEventButton* ev) {
if(ev->button == 1) {
Component comp = point_in(ev->y);
- cerr << get_comp_name(comp) << " pressed" << endl;
-
if(comp == Total || comp == None) {
return false;
}
diff --git a/libs/pbd/pbd/xml++.h b/libs/pbd/pbd/xml++.h
index bf26a6f685..7171fce6cc 100644
--- a/libs/pbd/pbd/xml++.h
+++ b/libs/pbd/pbd/xml++.h
@@ -109,6 +109,8 @@ public:
void remove_nodes(const string &);
/** Remove and delete all nodes with the name passed to remove_nodes */
void remove_nodes_and_delete(const string &);
+ /** Remove and delete all nodes with property prop matching val */
+ void remove_nodes_and_delete(const string& propname, const string& val);
};
class XMLProperty {
diff --git a/libs/pbd/xml++.cc b/libs/pbd/xml++.cc
index 5507f658e9..e603b4c50f 100644
--- a/libs/pbd/xml++.cc
+++ b/libs/pbd/xml++.cc
@@ -379,6 +379,27 @@ XMLNode::remove_nodes_and_delete(const string & n)
}
}
+void
+XMLNode::remove_nodes_and_delete(const string& propname, const string& val)
+{
+ XMLNodeIterator i = _children.begin();
+ XMLNodeIterator tmp;
+ XMLProperty* prop;
+
+ while (i != _children.end()) {
+ tmp = i;
+ ++tmp;
+
+ prop = (*i)->property(propname);
+ if(prop && prop->value() == val) {
+ delete *i;
+ _children.erase(i);
+ }
+
+ i = tmp;
+ }
+}
+
XMLProperty::XMLProperty(const string &n, const string &v)
: _name(n),
_value(v)