#include "evoral/Parameter.hpp"
#include "evoral/Control.hpp"
-#include "streamview.h"
-#include "midi_region_view.h"
-#include "midi_streamview.h"
-#include "midi_time_axis.h"
-#include "simpleline.h"
+#include "automation_region_view.h"
+#include "automation_time_axis.h"
#include "canvas-hit.h"
#include "canvas-note.h"
#include "canvas-program-change.h"
-#include "public_editor.h"
#include "ghostregion.h"
-#include "midi_time_axis.h"
-#include "automation_time_axis.h"
-#include "automation_region_view.h"
-#include "utils.h"
-#include "midi_util.h"
#include "gui_thread.h"
#include "keyboard.h"
+#include "midi_cut_buffer.h"
+#include "midi_region_view.h"
+#include "midi_streamview.h"
+#include "midi_time_axis.h"
+#include "midi_time_axis.h"
+#include "midi_util.h"
+#include "public_editor.h"
+#include "selection.h"
+#include "simpleline.h"
+#include "streamview.h"
+#include "utils.h"
#include "i18n.h"
using namespace ArdourCanvas;
MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
- boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color)
+ boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & basic_color)
: RegionView (parent, tv, r, spu, basic_color)
, _force_channel(-1)
, _last_channel_selection(0xFFFF)
}
void
-MidiRegionView::init (Gdk::Color& basic_color, bool wfd)
+MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
{
if (wfd) {
midi_region()->midi_source(0)->load_model();
bool
MidiRegionView::canvas_event(GdkEvent* ev)
{
+ PublicEditor& editor (trackview.editor());
+
+ if (!editor.internal_editing()) {
+ return false;
+ }
+
static double drag_start_x, drag_start_y;
static double last_x, last_y;
double event_x, event_y;
static ArdourCanvas::SimpleRect* drag_rect = NULL;
- if (trackview.editor().current_mouse_mode() != MouseNote)
- return false;
-
- const Editing::MidiEditMode midi_edit_mode = trackview.editor().current_midi_edit_mode();
-
switch (ev->type) {
case GDK_KEY_PRESS:
if (ev->key.keyval == GDK_Shift_L || ev->key.keyval == GDK_Control_L) {
case Pressed: // Drag start
// Select drag start
- if (_pressed_button == 1 && midi_edit_mode == MidiEditSelect) {
+ if (_pressed_button == 1 && editor.current_mouse_mode() == MouseRange) {
group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
Gdk::Cursor(Gdk::FLEUR), ev->motion.time);
last_x = event_x;
return true;
// Add note drag start
- } else if (midi_edit_mode == MidiEditPencil) {
+ } else if (editor.current_mouse_mode() == MouseObject) {
group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
Gdk::Cursor(Gdk::FLEUR), ev->motion.time);
last_x = event_x;
switch (_mouse_state) {
case Pressed: // Clicked
- switch (midi_edit_mode) {
- case MidiEditSelect:
- case MidiEditResize:
+ switch (editor.current_mouse_mode()) {
+ case MouseRange:
+ case MouseTimeFX:
clear_selection();
break;
- case MidiEditPencil:
+ case MouseObject:
create_note_at(event_x, event_y, _default_note_length);
default: break;
}
assert(note <= 127.0);
// Start of note in frames relative to region start
- nframes64_t start_frames = snap_to_frame(trackview.editor().pixel_to_frame(x));
+ nframes64_t start_frames = snap_frame_to_frame(trackview.editor().pixel_to_frame(x));
assert(start_frames >= 0);
// Snap length
length = frames_to_beats(
- snap_to_frame(start_frames + beats_to_frames(length)) - start_frames);
+ snap_frame_to_frame(start_frames + beats_to_frames(length)) - start_frames);
const boost::shared_ptr<NoteType> new_note(new NoteType(0,
frames_to_beats(start_frames + _region->start()), length,
delete _delta_command;
}
-
void
MidiRegionView::region_resized (Change what_changed)
{
midi_stream_view()->highest_note(),
height != old_height + FUDGE);
- if (name_text) {
- name_text->raise_to_top();
+ if (name_pixbuf) {
+ name_pixbuf->raise_to_top();
}
}
GhostRegion*
MidiRegionView::add_ghost (TimeAxisView& tv)
{
- RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&trackview);
CanvasNote* note;
- assert(rtv);
double unit_position = _region->position () / samples_per_unit;
MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
void
MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
{
- for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
+ for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
+
+ Selection::iterator tmp = i;
+ ++tmp;
+
if ((*i) != ev) {
- (*i)->selected(false);
- (*i)->hide_velocity();
- }
- }
+ remove_from_selection (*i);
+ }
- _selection.clear();
- _selection.insert(ev);
+ i = tmp;
+ }
- if ( ! ev->selected()) {
- ev->selected(true);
+ if (!ev->selected()) {
+ add_to_selection (ev);
}
}
void
MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add)
{
- if ( ! add) {
+ if (!add) {
clear_selection_except(ev);
}
- if (_selection.insert(ev).second) {
- play_midi_note(ev->note());
- }
-
- if ( ! ev->selected()) {
- ev->selected(true);
+ if (!ev->selected()) {
+ add_to_selection (ev);
}
}
void
MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev, bool add)
{
- if ( ! add) {
+ if (!add) {
clear_selection_except(ev);
}
- _selection.erase(ev);
-
- if (ev->selected()) {
- ev->selected(false);
- }
+ remove_from_selection (ev);
}
assert((*i)->x1() >= last_x1);
last_x1 = (*i)->x1();
#endif
- // Inside rectangle
if ((*i)->x1() >= x1 && (*i)->x1() <= x2 && (*i)->y1() >= last_y && (*i)->y1() <= y) {
- if (!(*i)->selected()) {
- (*i)->selected(true);
- _selection.insert(*i);
- play_midi_note((*i)->note());
- }
- // Not inside rectangle
+ // Inside rectangle
+ add_to_selection (*i);
} else if ((*i)->selected()) {
- (*i)->selected(false);
- _selection.erase(*i);
+ // Not inside rectangle
+ remove_from_selection (*i);
}
}
} else {
assert((*i)->x1() >= last_x1);
last_x1 = (*i)->x1();
#endif
- // Inside rectangle
if ((*i)->x2() <= x1 && (*i)->x2() >= x2 && (*i)->y1() >= last_y && (*i)->y1() <= y) {
- if (!(*i)->selected()) {
- (*i)->selected(true);
- _selection.insert(*i);
- play_midi_note((*i)->note());
- }
- // Not inside rectangle
+ // Inside rectangle
+ add_to_selection (*i);
} else if ((*i)->selected()) {
- (*i)->selected(false);
- _selection.erase(*i);
+ // Not inside rectangle
+ remove_from_selection (*i);
}
}
}
}
+void
+MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
+{
+ Selection::iterator i = _selection.find (ev);
+
+ if (i != _selection.end()) {
+ _selection.erase (i);
+ }
+
+ ev->selected (false);
+ ev->hide_velocity ();
+
+ if (_selection.empty()) {
+ PublicEditor& editor (trackview.editor());
+ editor.get_selection().remove (this);
+ }
+}
+
+void
+MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
+{
+ bool add_mrv_selection = false;
+
+ if (_selection.empty()) {
+ add_mrv_selection = true;
+ }
+
+ if (_selection.insert (ev).second) {
+ ev->selected (true);
+ play_midi_note ((ev)->note());
+ }
+
+ if (add_mrv_selection) {
+ PublicEditor& editor (trackview.editor());
+ editor.get_selection().add (this);
+ }
+}
void
MidiRegionView::move_selection(double dx, double dy)
}
}
-
void
MidiRegionView::note_dropped(CanvasNoteEvent* ev, double dt, uint8_t dnote)
{
const boost::shared_ptr<NoteType> copy(new NoteType(*(*i)->note().get()));
- // we need to snap here again in nframes64_t in order to be sample accurate
- double start_frames = snap_to_frame(beats_to_frames((*i)->note()->time()) + dt);
-
- // keep notes inside region if dragged beyond left region bound
- if (start_frames < _region->start()) {
- start_frames = _region->start();
+ nframes64_t start_frames = beats_to_frames((*i)->note()->time());
+ if (dt >= 0) {
+ start_frames += snap_frame_to_frame(trackview.editor().pixel_to_frame(dt));
+ } else {
+ start_frames -= snap_frame_to_frame(trackview.editor().pixel_to_frame(-dt));
}
-
+
copy->set_time(frames_to_beats(start_frames));
uint8_t original_pitch = (*i)->note()->note();
- uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
+ uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
// keep notes in standard midi range
clamp_to_0_127(new_pitch);
}
nframes64_t
-MidiRegionView::snap_to_frame(double x)
+MidiRegionView::snap_pixel_to_frame(double x)
{
- PublicEditor &editor = trackview.editor();
+ PublicEditor& editor = trackview.editor();
// x is region relative, convert it to global absolute frames
nframes64_t frame = editor.pixel_to_frame(x) + _region->position();
editor.snap_to(frame);
}
nframes64_t
-MidiRegionView::snap_to_frame(nframes64_t x)
+MidiRegionView::snap_frame_to_frame(nframes64_t x)
{
- PublicEditor &editor = trackview.editor();
- // x is region relative
- // convert x to global frame
+ PublicEditor& editor = trackview.editor();
+ // x is region relative, convert it to global absolute frames
nframes64_t frame = x + _region->position();
editor.snap_to(frame);
- // convert event_frame back to local coordinates relative to position
- frame -= _region->position();
- return frame;
+ return frame - _region->position(); // convert back to region relative
}
double
MidiRegionView::snap_to_pixel(double x)
{
- return (double) trackview.editor().frame_to_pixel(snap_to_frame(x));
+ return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
}
double
// because snapping works on world coordinates we have to transform current_x
// to world coordinates before snapping and transform it back afterwards
- nframes64_t current_frame = snap_to_frame(current_x);
+ nframes64_t current_frame = snap_pixel_to_frame(current_x);
// transform to region start relative
current_frame += _region->start();
redisplay_model();
}
+void
+MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
+{
+ if (_selection.empty()) {
+ return;
+ }
+
+ PublicEditor& editor (trackview.editor());
+
+ switch (op) {
+ case Cut:
+ case Copy:
+ editor.get_cut_buffer().add (selection_as_cut_buffer());
+ break;
+ default:
+ break;
+ }
+
+ start_delta_command();
+
+ for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
+ switch (op) {
+ case Copy:
+ break;
+ case Cut:
+ command_remove_note (*i);
+ break;
+ case Clear:
+ break;
+ }
+ }
+
+ apply_command();
+}
+
+MidiCutBuffer*
+MidiRegionView::selection_as_cut_buffer () const
+{
+ Evoral::Sequence<MidiModel::TimeType>::Notes notes;
+
+ for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
+ notes.push_back (boost::shared_ptr<NoteType> (new NoteType (*((*i)->note().get()))));
+ }
+
+ /* sort them into time order */
+
+ sort (notes.begin(), notes.end(), Evoral::Sequence<MidiModel::TimeType>::note_time_comparator);
+
+ MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
+ cb->set (notes);
+
+ return cb;
+}
+
+void
+MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb)
+{
+ if (mcb.empty()) {
+ return;
+ }
+
+ start_delta_command (_("paste"));
+
+ MidiModel::TimeType beat_delta;
+ MidiModel::TimeType paste_pos_beats;
+ MidiModel::TimeType duration;
+
+ duration = mcb.notes().back()->end_time() - mcb.notes().front()->time();
+ paste_pos_beats = frames_to_beats (pos);
+ beat_delta = mcb.notes().front()->time() - paste_pos_beats;
+ paste_pos_beats = 0;
+
+ _selection.clear ();
+
+ for (int n = 0; n < (int) times; ++n) {
+
+ for (Evoral::Sequence<MidiModel::TimeType>::Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
+
+ boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
+ copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
+
+ /* make all newly added notes selected */
+
+ command_add_note (copied_note, true);
+ }
+
+ paste_pos_beats += duration;
+ }
+
+ apply_command ();
+}