From 1b657585572298d1a69a7b43e611f59b7e185df3 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sun, 10 Feb 2008 18:16:25 +0000 Subject: Committed underlay support (from Audun). git-svn-id: svn://localhost/ardour2/branches/3.0@3037 d708f5d6-7413-0410-9779-e7cbd77b26cf --- gtk2_ardour/audio_region_view.cc | 28 ++-- gtk2_ardour/audio_region_view.h | 2 +- gtk2_ardour/automation_region_view.h | 4 +- gtk2_ardour/automation_time_axis.cc | 43 +----- gtk2_ardour/automation_time_axis.h | 9 +- gtk2_ardour/canvas_vars.h | 5 +- gtk2_ardour/editor.cc | 7 +- gtk2_ardour/editor.h | 12 ++ gtk2_ardour/editor_canvas.cc | 23 +++- gtk2_ardour/editor_ops.cc | 42 ++++++ gtk2_ardour/editor_route_list.cc | 17 +++ gtk2_ardour/ghostregion.cc | 259 +++++++++++++++++++++++++++++++---- gtk2_ardour/ghostregion.h | 102 ++++++++++++-- gtk2_ardour/lineset.cc | 6 - gtk2_ardour/midi_region_view.cc | 61 ++++++++- gtk2_ardour/midi_region_view.h | 2 +- gtk2_ardour/midi_scroomer.cc | 47 ++++--- gtk2_ardour/midi_streamview.cc | 7 + gtk2_ardour/midi_streamview.h | 1 + gtk2_ardour/midi_time_axis.cc | 24 ++++ gtk2_ardour/midi_time_axis.h | 2 + gtk2_ardour/piano_roll_header.cc | 28 ++-- gtk2_ardour/piano_roll_header.h | 1 + gtk2_ardour/public_editor.h | 2 + gtk2_ardour/region_view.cc | 10 ++ gtk2_ardour/region_view.h | 5 +- gtk2_ardour/route_time_axis.cc | 188 ++++++++++++++++++++----- gtk2_ardour/route_time_axis.h | 15 +- gtk2_ardour/streamview.cc | 1 - gtk2_ardour/time_axis_view.cc | 55 +++++++- gtk2_ardour/time_axis_view.h | 11 ++ libs/gtkmm2ext/scroomer.cc | 2 - libs/pbd/pbd/xml++.h | 2 + libs/pbd/xml++.cc | 21 +++ 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::iterator i = ghosts.begin(); i != ghosts.end(); ++i) { + if((agr = dynamic_cast(*i)) != 0) { - for (vector::iterator w = (*i)->waves.begin(); w != (*i)->waves.end(); ++w) { - (*w)->property_region_start() = _region->start(); - } + for (vector::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(&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::iterator w = (*i)->waves.begin(); w != (*i)->waves.end(); ++w) { - (*w)->property_data_src() = _region.get(); + if((agr = dynamic_cast(*i)) != 0) { + for (vector::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 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::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::iterator i = ghosts.begin(); i != ghosts.end(); ++i) { - (*i)->set_height (); - } - TimeAxisView* state_parent = get_parent_with_state (); assert(state_parent); @@ -801,30 +790,6 @@ AutomationTimeAxisView::paste_one (AutomationLine& line, nframes_t pos, float ti return true; } -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::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& results) { @@ -916,7 +881,7 @@ AutomationTimeAxisView::exited () _line->track_exited(); } -void +/*void AutomationTimeAxisView::set_colors () { for (list::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 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(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); void hide_a_region (boost::shared_ptr); void remove_a_region (boost::shared_ptr); @@ -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, int x, int y); void insert_region_list_selection (float times); + void insert_route_list_drag (boost::shared_ptr, 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& 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& 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); } } @@ -540,6 +545,22 @@ Editor::drop_regions (const RefPtr& context, context->drag_finish (true, false, time); } +void +Editor::drop_routes (const Glib::RefPtr& context, + int x, int y, + const Gtk::SelectionData& data, + guint info, guint time) { + const SerializedObjectPointers >* sr = + reinterpret_cast > *> (data.get_data()); + + for (uint32_t i = 0; i < sr->cnt; ++i) { + boost::shared_ptr r = sr->data[i]; + insert_route_list_drag (r, x, y); + } + + context->drag_finish (true, false, time); +} + void Editor::maybe_autoscroll (GdkEvent* event) { 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 @@ -2095,6 +2095,48 @@ Editor::insert_region_list_drag (boost::shared_ptr region, int x, int y) commit_reversible_command (); } +void +Editor::insert_route_list_drag (boost::shared_ptr 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(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(*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) { 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& 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(*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::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(&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::iterator i = waves.begin(); i != waves.end(); ++i) { + (*i)->property_samples_per_unit() = spu; + } +} + +void +AudioGhostRegion::set_height () { gdouble ht; vector::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(&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(*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(*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 #include #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 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 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 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 GoingAway; + typedef std::list 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 @@ -488,12 +488,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. 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::iterator i = _events.begin(); i != _events.end(); ++i) delete *i; + MidiGhostRegion* gr; + + for(vector::iterator g = ghosts.begin(); g != ghosts.end(); ++g) { + if((gr = dynamic_cast(*g)) != 0) { + gr->clear_events(); + } + } + _events.clear(); } @@ -505,11 +513,23 @@ MidiRegionView::set_y_position_and_height (double y, double h) for (std::vector::const_iterator i = _events.begin(); i != _events.end(); ++i) { CanvasNote* note = dynamic_cast(*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(&trackview); + CanvasNote* note; assert(rtv); double unit_position = _region->position () / samples_per_unit; - GhostRegion* ghost = new GhostRegion (atv, unit_position); + MidiTimeAxisView* mtv = dynamic_cast(&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::iterator i = _events.begin(); i != _events.end(); ++i) { + if((note = dynamic_cast(*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) ev_rect->show(); _events.push_back(ev_rect); + MidiGhostRegion* gr; + + for(vector::iterator g = ghosts.begin(); g != ghosts.end(); ++g) { + if((gr = dynamic_cast(*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 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 @@ -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::iterator i = ghosts.begin(); i != ghosts.end(); ++i) { + if((mgr = dynamic_cast(*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&) const = 0; sigc::signal 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 @@ -529,6 +529,16 @@ RegionView::move (double x_delta, double y_delta) } } +void +RegionView::remove_ghost_in (TimeAxisView& tv) { + for (vector::iterator i = ghosts.begin(); i != ghosts.end(); ++i) { + if (&(*i)->trackview == &tv) { + delete *i; + break; + } + } +} + void RegionView::remove_ghost (GhostRegion* ghost) { 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 atv; - - if ((atv = boost::dynamic_pointer_cast (*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 atv; + + if ((atv = boost::dynamic_pointer_cast (*i)) != 0) { + atv->add_ghost(rv); + } } } -} -void -RouteTimeAxisView::add_ghost_to_processor (RegionView* rv, boost::shared_ptr 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 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); - + 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 UnderlayList; + UnderlayList _underlay_streams; + typedef list 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::iterator i = ghosts.begin(); i != ghosts.end(); ++i) { + delete *i; + } + for (list::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::iterator i = ghosts.begin(); i != ghosts.end(); ++i) { + (*i)->set_height (); + } } void @@ -846,6 +863,37 @@ TimeAxisView::get_inverted_selectables (Selection& sel, list& 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::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::iterator i=ghosts.begin(); i != ghosts.end(); i++ ) { + (*i)->set_colors(); + } + for (list::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& results); virtual void get_inverted_selectables (Selection&, list& 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 ghosts; + list free_selection_rects; list 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) -- cgit v1.2.3