Committed underlay support (from Audun).

git-svn-id: svn://localhost/ardour2/branches/3.0@3037 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
David Robillard 2008-02-10 18:16:25 +00:00
parent e76b028ffd
commit 1b65758557
34 changed files with 852 additions and 192 deletions

View File

@ -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();
}
}
}

View File

@ -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);

View File

@ -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);

View File

@ -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);
@ -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<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)
{
@ -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

View File

@ -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;

View File

@ -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")

View File

@ -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);
}

View File

@ -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;

View File

@ -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);
}
}
@ -540,6 +545,22 @@ Editor::drop_regions (const RefPtr<Gdk::DragContext>& context,
context->drag_finish (true, false, time);
}
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)
{

View File

@ -2095,6 +2095,48 @@ Editor::insert_region_list_drag (boost::shared_ptr<Region> region, int x, int y)
commit_reversible_command ();
}
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)
{

View File

@ -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;
}

View File

@ -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 ()
@ -59,14 +64,6 @@ GhostRegion::~GhostRegion ()
delete group;
}
void
GhostRegion::set_samples_per_unit (double spu)
{
for (vector<WaveView*>::iterator i = waves.begin(); i != waves.end(); ++i) {
(*i)->property_samples_per_unit() = spu;
}
}
void
GhostRegion::set_duration (double units)
{
@ -75,12 +72,55 @@ GhostRegion::set_duration (double units)
void
GhostRegion::set_height ()
{
base_rect->property_y2() = (double) trackview.height;
}
void
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;
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();
if(is_automation_ghost()) {
fill_color = ARDOUR_UI::config()->canvasvar_GhostTrackWaveFill.get();
}
else {
fill_color = source_track_color(200);
}
waves[n]->property_clip_color() = ARDOUR_UI::config()->canvasvar_GhostTrackWaveClip.get();
waves[n]->property_zero_color() = ARDOUR_UI::config()->canvasvar_GhostTrackZeroLine.get();
}
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());
}
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;
}
events.clear();
}

View File

@ -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();
GhostRegion (AutomationTimeAxisView& tv, double initial_unit_pos);
~GhostRegion ();
virtual void set_samples_per_unit(double spu) = 0;
virtual void set_height();
virtual void set_colors();
void set_samples_per_unit (double spu);
void set_duration (double units);
void set_height ();
void set_colors ();
void set_duration(double units);
sigc::signal<void,GhostRegion*> GoingAway;
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();
void add_note(ArdourCanvas::CanvasNote*);
void add_hit(ArdourCanvas::CanvasHit*);
void clear_events();
typedef std::list<MidiGhostRegion::Event*> EventList;
EventList events;
};
#endif /* __ardour_gtk_ghost_region_h__ */

View File

@ -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.

View File

@ -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());
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;
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()

View File

@ -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);

View File

@ -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;

View File

@ -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));

View File

@ -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);

View File

@ -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

View File

@ -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 ();

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -529,6 +529,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)
{

View File

@ -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 ();

View File

@ -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());
}
}
}

View File

@ -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__ */

View File

@ -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);

View File

@ -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();

View File

@ -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 ();

View File

@ -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;
}

View File

@ -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 {

View File

@ -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)