re-use canvas note items when the model changes ; slightly more efficient (probably) and avoids invalidating references to said items in, for example, ResizeData in a copied region

git-svn-id: svn://localhost/ardour2/branches/3.0@5650 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2009-09-09 16:46:18 +00:00
parent 58f5ad6406
commit 5f319d0a08
5 changed files with 142 additions and 43 deletions

View File

@ -47,6 +47,7 @@ CanvasNoteEvent::CanvasNoteEvent(MidiRegionView& region, Item* item,
, _state(None)
, _note(note)
, _selected(false)
, _valid (true)
{
}
@ -60,6 +61,18 @@ CanvasNoteEvent::~CanvasNoteEvent()
delete _channel_selector_widget;
}
void
CanvasNoteEvent::invalidate ()
{
_valid = false;
}
void
CanvasNoteEvent::validate ()
{
_valid = true;
}
void
CanvasNoteEvent::move_event(double dx, double dy)
{

View File

@ -66,6 +66,10 @@ public:
virtual void hide() = 0;
virtual bool on_event(GdkEvent* ev);
bool valid() const { return _valid; }
void invalidate ();
void validate ();
bool selected() const { return _selected; }
void selected(bool yn);
@ -129,6 +133,7 @@ protected:
const boost::shared_ptr<NoteType> _note;
bool _own_note;
bool _selected;
bool _valid;
};
} // namespace Gnome

View File

@ -1457,6 +1457,7 @@ NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
MidiRegionSelection::iterator next;
next = r;
++next;
cerr << "Working on MRV " << (*r)->midi_region()->name() << endl;
(*r)->commit_resizing (at_front, _current_pointer_x - _grab_x, relative);
r = next;
}

View File

@ -724,6 +724,20 @@ MidiRegionView::abort_command()
clear_selection();
}
CanvasNoteEvent*
MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
{
/* XXX optimize the crap out of this SOON */
for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
if ((*i)->note() == note) {
return *i;
}
}
return 0;
}
void
MidiRegionView::redisplay_model()
{
@ -738,17 +752,51 @@ MidiRegionView::redisplay_model()
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
_marked_for_selection.insert((*i)->note());
}
for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
(*i)->invalidate ();
}
clear_events();
_model->read_lock();
MidiModel::Notes notes = _model->notes();
for (size_t i = 0; i < _model->n_notes(); ++i) {
boost::shared_ptr<NoteType> note (_model->note_at (i));
if (note_in_visible_range (note)) {
add_note (note);
CanvasNoteEvent* cne;
if ((cne = find_canvas_note (note)) != 0) {
cne->validate ();
CanvasNote* cn;
CanvasHit* ch;
if ((cn = dynamic_cast<CanvasNote*>(cne)) != 0) {
update_note (cn);
} else if ((ch = dynamic_cast<CanvasHit*>(cne)) != 0) {
update_hit (ch);
}
} else {
add_note (note);
}
}
}
/* remove note items that are no longer valid */
for (Events::iterator i = _events.begin(); i != _events.end(); ) {
if (!(*i)->valid ()) {
cerr << "Canvas note " << *i << " is invalid, deleting\n";
delete *i;
i = _events.erase (i);
} else {
++i;
}
}
@ -1100,6 +1148,64 @@ MidiRegionView::note_in_visible_range(const boost::shared_ptr<NoteType> note) co
return !outside;
}
void
MidiRegionView::update_note (CanvasNote* ev)
{
boost::shared_ptr<NoteType> note = ev->note();
const nframes64_t note_start_frames = beats_to_frames(note->time());
const nframes64_t note_end_frames = beats_to_frames(note->end_time());
const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
const double y1 = midi_stream_view()->note_to_y(note->note());
const double note_endpixel =
trackview.editor().frame_to_pixel(note_end_frames - _region->start());
ev->property_x1() = x;
ev->property_y1() = y1;
if (note->length() > 0) {
ev->property_x2() = note_endpixel;
} else {
ev->property_x2() = trackview.editor().frame_to_pixel(_region->length());
}
ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
if (note->length() == 0) {
if (_active_notes) {
assert(note->note() < 128);
// If this note is already active there's a stuck note,
// finish the old note rectangle
if (_active_notes[note->note()]) {
CanvasNote* const old_rect = _active_notes[note->note()];
boost::shared_ptr<NoteType> old_note = old_rect->note();
old_rect->property_x2() = x;
old_rect->property_outline_what() = (guint32) 0xF;
}
_active_notes[note->note()] = ev;
}
/* outline all but right edge */
ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
} else {
/* outline all edges */
ev->property_outline_what() = (guint32) 0xF;
}
}
void
MidiRegionView::update_hit (CanvasHit* ev)
{
boost::shared_ptr<NoteType> note = ev->note();
const nframes64_t note_start_frames = beats_to_frames(note->time());
const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
const double diamond_size = midi_stream_view()->note_height() / 2.0;
const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
ev->move(x, y);
}
/** Add a MIDI note to the view (with length).
*
* If in sustained mode, notes with length 0 will be considered active
@ -1109,56 +1215,23 @@ MidiRegionView::note_in_visible_range(const boost::shared_ptr<NoteType> note) co
void
MidiRegionView::add_note(const boost::shared_ptr<NoteType> note)
{
CanvasNoteEvent* event = 0;
assert(note->time() >= 0);
assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
const nframes64_t note_start_frames = beats_to_frames(note->time());
const nframes64_t note_end_frames = beats_to_frames(note->end_time());
ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
CanvasNoteEvent* event = 0;
const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
if (midi_view()->note_mode() == Sustained) {
const double y1 = midi_stream_view()->note_to_y(note->note());
const double note_endpixel =
trackview.editor().frame_to_pixel(note_end_frames - _region->start());
CanvasNote* ev_rect = new CanvasNote(*this, *group, note);
ev_rect->property_x1() = x;
ev_rect->property_y1() = y1;
if (note->length() > 0) {
ev_rect->property_x2() = note_endpixel;
} else {
ev_rect->property_x2() = trackview.editor().frame_to_pixel(_region->length());
}
ev_rect->property_y2() = y1 + floor(midi_stream_view()->note_height());
if (note->length() == 0) {
if (_active_notes) {
assert(note->note() < 128);
// If this note is already active there's a stuck note,
// finish the old note rectangle
if (_active_notes[note->note()]) {
CanvasNote* const old_rect = _active_notes[note->note()];
boost::shared_ptr<NoteType> old_note = old_rect->note();
old_rect->property_x2() = x;
old_rect->property_outline_what() = (guint32) 0xF;
}
_active_notes[note->note()] = ev_rect;
}
/* outline all but right edge */
ev_rect->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
} else {
/* outline all edges */
ev_rect->property_outline_what() = (guint32) 0xF;
}
update_note (ev_rect);
event = ev_rect;
MidiGhostRegion* gr;
for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
gr->add_note(ev_rect);
@ -1166,12 +1239,15 @@ MidiRegionView::add_note(const boost::shared_ptr<NoteType> note)
}
} else if (midi_view()->note_mode() == Percussive) {
const double diamond_size = midi_stream_view()->note_height() / 2.0;
const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
CanvasHit* ev_diamond = new CanvasHit(*this, *group, diamond_size, note);
ev_diamond->move(x, y);
update_hit (ev_diamond);
event = ev_diamond;
} else {
event = 0;
}
@ -1860,8 +1936,6 @@ MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_del
if negative - move the end of the note earlier in time (shortening it)
*/
cerr << "Trim front by " << front_delta << " end by " << end_delta << endl;
if (front_delta) {
if (front_delta < 0) {

View File

@ -37,6 +37,7 @@
#include "automation_line.h"
#include "enums.h"
#include "canvas.h"
#include "canvas-hit.h"
#include "canvas-note.h"
#include "canvas-note-event.h"
#include "canvas-program-change.h"
@ -381,6 +382,11 @@ class MidiRegionView : public RegionView
/* connection used to connect to model's ContentChanged signal */
sigc::connection content_connection;
ArdourCanvas::CanvasNoteEvent* find_canvas_note (boost::shared_ptr<NoteType>);
void update_note (ArdourCanvas::CanvasNote*);
void update_hit (ArdourCanvas::CanvasHit*);
};