#include "ardour/midi_region.h"
#include "ardour/midi_source.h"
#include "ardour/midi_track.h"
+#include "ardour/operations.h"
#include "ardour/session.h"
#include "evoral/Parameter.hpp"
-#include "evoral/MIDIParameters.hpp"
#include "evoral/MIDIEvent.hpp"
#include "evoral/Control.hpp"
#include "evoral/midi_util.h"
#include "canvas/debug.h"
+#include "canvas/text.h"
#include "automation_region_view.h"
#include "automation_time_axis.h"
+#include "control_point.h"
#include "debug.h"
#include "editor.h"
#include "editor_drag.h"
#include "ghostregion.h"
#include "gui_thread.h"
+#include "item_counts.h"
#include "keyboard.h"
#include "midi_channel_dialog.h"
#include "midi_cut_buffer.h"
#include "rgb_macros.h"
#include "selection.h"
#include "streamview.h"
-#include "utils.h"
#include "patch_change_dialog.h"
#include "verbose_cursor.h"
#include "ardour_ui.h"
using namespace ARDOUR;
using namespace PBD;
using namespace Editing;
+using namespace std;
using Gtkmm2ext::Keyboard;
PBD::Signal1<void, MidiRegionView *> MidiRegionView::SelectionCleared;
#define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1)
-MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
- boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & basic_color)
+MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
+ RouteTimeAxisView& tv,
+ boost::shared_ptr<MidiRegion> r,
+ double spu,
+ uint32_t basic_color)
: RegionView (parent, tv, r, spu, basic_color)
, _current_range_min(0)
, _current_range_max(0)
+ , _region_relative_time_converter(r->session().tempo_map(), r->position())
+ , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
, _active_notes(0)
- , _note_group (new ArdourCanvas::Group (group))
+ , _note_group (new ArdourCanvas::Container (group))
, _note_diff_command (0)
, _ghost_note(0)
, _step_edit_cursor (0)
, _last_event_y (0)
, pre_enter_cursor (0)
, pre_press_cursor (0)
+ , pre_note_enter_cursor (0)
, _note_player (0)
{
CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
}
-MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
- boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color,
- TimeAxisViewItem::Visibility visibility)
+MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
+ RouteTimeAxisView& tv,
+ boost::shared_ptr<MidiRegion> r,
+ double spu,
+ uint32_t basic_color,
+ TimeAxisViewItem::Visibility visibility)
: RegionView (parent, tv, r, spu, basic_color, false, visibility)
, _current_range_min(0)
, _current_range_max(0)
+ , _region_relative_time_converter(r->session().tempo_map(), r->position())
+ , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
, _active_notes(0)
- , _note_group (new ArdourCanvas::Group (parent))
+ , _note_group (new ArdourCanvas::Container (parent))
, _note_diff_command (0)
, _ghost_note(0)
, _step_edit_cursor (0)
, _last_event_y (0)
, pre_enter_cursor (0)
, pre_press_cursor (0)
+ , pre_note_enter_cursor (0)
, _note_player (0)
{
CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
void
MidiRegionView::parameter_changed (std::string const & p)
{
- if (p == "diplay-first-midi-bank-as-zero") {
+ if (p == "display-first-midi-bank-as-zero") {
if (_enable_display) {
redisplay_model();
}
, RegionView (other)
, _current_range_min(0)
, _current_range_max(0)
+ , _region_relative_time_converter(other.region_relative_time_converter())
+ , _source_relative_time_converter(other.source_relative_time_converter())
, _active_notes(0)
- , _note_group (new ArdourCanvas::Group (get_canvas_group()))
+ , _note_group (new ArdourCanvas::Container (get_canvas_group()))
, _note_diff_command (0)
, _ghost_note(0)
, _step_edit_cursor (0)
, _last_event_y (0)
, pre_enter_cursor (0)
, pre_press_cursor (0)
+ , pre_note_enter_cursor (0)
, _note_player (0)
{
- Gdk::Color c;
- int r,g,b,a;
-
- UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
- c.set_rgb_p (r/255.0, g/255.0, b/255.0);
-
- init (c, false);
+ init (false);
}
MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
: RegionView (other, boost::shared_ptr<Region> (region))
, _current_range_min(0)
, _current_range_max(0)
+ , _region_relative_time_converter(other.region_relative_time_converter())
+ , _source_relative_time_converter(other.source_relative_time_converter())
, _active_notes(0)
- , _note_group (new ArdourCanvas::Group (get_canvas_group()))
+ , _note_group (new ArdourCanvas::Container (get_canvas_group()))
, _note_diff_command (0)
, _ghost_note(0)
, _step_edit_cursor (0)
, _last_event_y (0)
, pre_enter_cursor (0)
, pre_press_cursor (0)
+ , pre_note_enter_cursor (0)
, _note_player (0)
{
- Gdk::Color c;
- int r,g,b,a;
-
- UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
- c.set_rgb_p (r/255.0, g/255.0, b/255.0);
-
- init (c, true);
+ init (true);
}
void
-MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
+MidiRegionView::init (bool wfd)
{
PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
_model = midi_region()->midi_source(0)->model();
_enable_display = false;
- RegionView::init (basic_color, false);
-
- compute_colors (basic_color);
+ RegionView::init (false);
set_height (trackview.current_height());
bool
MidiRegionView::canvas_group_event(GdkEvent* ev)
{
+ if (in_destructor) {
+ return false;
+ }
+
bool r;
switch (ev->type) {
}
if ((!trackview.editor().internal_editing() && trackview.editor().current_mouse_mode() != MouseGain) ||
- (trackview.editor().current_mouse_mode() == MouseTimeFX) ||
- (trackview.editor().current_mouse_mode() == MouseZoom)) {
+ (trackview.editor().current_mouse_mode() == MouseTimeFX)) {
// handle non-internal-edit/non-draw modes elsewhere
return RegionView::canvas_group_event (ev);
}
switch (ev->type) {
case GDK_SCROLL:
- return scroll (&ev->scroll);
+ if (scroll (&ev->scroll)) {
+ return true;
+ }
+ break;
case GDK_KEY_PRESS:
return key_press (&ev->key);
return r;
case GDK_ENTER_NOTIFY:
+ // set entered_regionview (among other things)
+ trackview.editor().canvas_region_view_event (ev, group, this);
return enter_notify (&ev->crossing);
case GDK_LEAVE_NOTIFY:
+ // reset entered_regionview (among other things)
+ trackview.editor().canvas_region_view_event (ev, group, this);
return leave_notify (&ev->crossing);
case GDK_MOTION_NOTIFY:
trackview.editor().verbose_cursor()->hide ();
remove_ghost_note ();
+ if (trackview.editor().internal_editing()) {
+ Keyboard::magic_widget_drop_focus();
+ }
+
if (pre_enter_cursor) {
Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
editor->set_canvas_cursor(pre_enter_cursor);
+ pre_enter_cursor = 0;
}
return false;
if (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
pre_press_cursor = editor->get_canvas_cursor ();
editor->set_canvas_cursor (editor->cursors()->midi_pencil);
- }
+ }
if (_mouse_state != SelectTouchDragging) {
event_y = ev->y;
group->canvas_to_item (event_x, event_y);
- bool success;
- Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_sample (event_x));
-
- if (!success) {
- beats = 1;
- }
+ Evoral::MusicalTime beats = get_grid_beats(editor.pixel_to_sample(event_x));
/* Shorten the length by 1 tick so that we can add a new note at the next
grid snap without it overlapping this one.
*/
- beats -= 1.0 / Timecode::BBT_Time::ticks_per_beat;
+ beats -= Evoral::MusicalTime::tick();
create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
}
}
case MouseDraw:
{
- bool success;
- Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_sample (event_x));
-
- if (!success) {
- beats = 1;
- }
+ Evoral::MusicalTime beats = get_grid_beats(editor.pixel_to_sample(event_x));
/* Shorten the length by 1 tick so that we can add a new note at the next
grid snap without it overlapping this one.
*/
- beats -= 1.0 / Timecode::BBT_Time::ticks_per_beat;
+ beats -= Evoral::MusicalTime::tick();
create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
default:
break;
+
}
- return false;
+ /* we may be dragging some non-note object (eg. patch-change, sysex)
+ */
+
+ return editor.drags()->motion_handler ((GdkEvent *) ev, false);
}
bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
- change_note_lengths (fine, shorter, 0.0, start, end);
+ change_note_lengths (fine, shorter, Evoral::MusicalTime(), start, end);
return true;
* \param snap_t true to snap t to the grid, otherwise false.
*/
void
-MidiRegionView::create_note_at (framepos_t t, double y, double length, bool snap_t)
+MidiRegionView::create_note_at (framepos_t t, double y, Evoral::MusicalTime length, bool snap_t)
{
if (length < 2 * DBL_EPSILON) {
return;
MidiModel::Notes& notes (_model->notes());
_optimization_iterator = _events.begin();
- bool empty_when_starting = !_events.empty();
+ bool empty_when_starting = _events.empty();
for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
bool visible;
if (note_in_region_range (note, visible)) {
-
- if (empty_when_starting && (cne = find_canvas_note (note)) != 0) {
+
+ if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
cne->validate ();
}
} else {
-
- if (empty_when_starting && (cne = find_canvas_note (note)) != 0) {
+
+ if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
cne->validate ();
cne->hide ();
}
/* remove note items that are no longer valid */
- if (empty_when_starting) {
+ if (!empty_when_starting) {
for (Events::iterator i = _events.begin(); i != _events.end(); ) {
if (!(*i)->valid ()) {
RegionView::region_resized(what_changed);
if (what_changed.contains (ARDOUR::Properties::position)) {
+ _region_relative_time_converter.set_origin_b(_region->position());
set_duration(_region->length(), 0);
if (_enable_display) {
redisplay_model();
}
}
+
+ if (what_changed.contains (ARDOUR::Properties::start) ||
+ what_changed.contains (ARDOUR::Properties::position)) {
+ _source_relative_time_converter.set_origin_b (_region->position() - _region->start());
+ }
}
void
void
MidiRegionView::set_height (double height)
{
- static const double FUDGE = 2.0;
- const double old_height = _height;
+ double old_height = _height;
RegionView::set_height(height);
- _height = height - FUDGE;
- apply_note_range(midi_stream_view()->lowest_note(),
- midi_stream_view()->highest_note(),
- height != old_height + FUDGE);
+ apply_note_range (midi_stream_view()->lowest_note(),
+ midi_stream_view()->highest_note(),
+ height != old_height);
if (name_text) {
name_text->raise_to_top();
if (Note* cnote = dynamic_cast<Note*>(event)) {
- const double y0 = midi_stream_view()->note_to_y(note->note());
- const double y1 = y0 + floor(midi_stream_view()->note_height());
+ const double y0 = 1. + floor (midi_stream_view()->note_to_y(note->note()));
+ const double y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1.);
cnote->set_y0 (y0);
cnote->set_y1 (y1);
} else if (Hit* chit = dynamic_cast<Hit*>(event)) {
-
- const double diamond_size = update_hit (chit);
-
- chit->set_height (diamond_size);
+ update_hit (chit);
}
}
}
/** Resolve an active MIDI note (while recording).
*/
void
-MidiRegionView::resolve_note(uint8_t note, double end_time)
+MidiRegionView::resolve_note(uint8_t note, Evoral::MusicalTime end_time)
{
if (midi_view()->note_mode() != Sustained) {
return;
const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
_active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_frames));
- _active_notes[note]->set_outline_what (0xf);
+ _active_notes[note]->set_outline_all ();
_active_notes[note] = 0;
+
}
}
{
boost::shared_ptr<NoteType> note = ev->note();
const double x = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time()));
- const double y0 = midi_stream_view()->note_to_y(note->note());
+ const double y0 = 1 + floor(midi_stream_view()->note_to_y(note->note()));
ev->set_x0 (x);
ev->set_y0 (y0);
ev->set_x1 (trackview.editor().sample_to_pixel (_region->length()));
}
- ev->set_y1 (y0 + floor(midi_stream_view()->note_height()));
+ ev->set_y1 (y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1));
- if (note->length() == 0) {
+ if (!note->length()) {
if (_active_notes && note->note() < 128) {
// If this note is already active there's a stuck note,
// finish the old note rectangle
Note* const old_rect = _active_notes[note->note()];
boost::shared_ptr<NoteType> old_note = old_rect->note();
old_rect->set_x1 (x);
- old_rect->set_outline_what (0xF);
+ old_rect->set_outline_all ();
}
_active_notes[note->note()] = ev;
}
/* outline all but right edge */
- ev->set_outline_what (0x1 & 0x4 & 0x8);
+ ev->set_outline_what (ArdourCanvas::Rectangle::What (
+ ArdourCanvas::Rectangle::TOP|
+ ArdourCanvas::Rectangle::LEFT|
+ ArdourCanvas::Rectangle::BOTTOM));
} else {
/* outline all edges */
- ev->set_outline_what (0xF);
+ ev->set_outline_all ();
}
if (update_ghost_regions) {
}
}
-double
+void
MidiRegionView::update_hit (Hit* ev)
{
boost::shared_ptr<NoteType> note = ev->note();
const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
const double x = trackview.editor().sample_to_pixel(note_start_frames);
- 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);
+ const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
+ const double y = 1.5 + floor(midi_stream_view()->note_to_y(note->note())) + diamond_size * .5;
ev->set_position (ArdourCanvas::Duple (x, y));
-
- return diamond_size;
+ ev->set_height (diamond_size);
}
/** Add a MIDI note to the view (with length).
{
NoteBase* event = 0;
- //ArdourCanvas::Group* const group = (ArdourCanvas::Group*) get_canvas_group();
-
if (midi_view()->note_mode() == Sustained) {
Note* ev_rect = new Note (*this, _note_group, note);
} else if (midi_view()->note_mode() == Percussive) {
- const double diamond_size = midi_stream_view()->note_height() / 2.0;
+ const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note);
/// Return true iff @p pc applies to the given time on the given channel.
static bool
-patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, double time, uint8_t channel)
+patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, Evoral::MusicalTime time, uint8_t channel)
{
return pc->time() <= time && pc->channel() == channel;
}
void
-MidiRegionView::get_patch_key_at (double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
+MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
{
// The earliest event not before time
MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
/* find end of latest note selected, select all between that and the start of "ev" */
Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
- Evoral::MusicalTime latest = 0;
+ Evoral::MusicalTime latest = Evoral::MusicalTime();
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
if ((*i)->note()->end_time() > latest) {
}
void
-MidiRegionView::update_drag_selection(double x0, double x1, double y0, double y1, bool extend)
+MidiRegionView::update_drag_selection(framepos_t start, framepos_t end, double gy0, double gy1, bool extend)
{
+ PublicEditor& editor = trackview.editor();
+
+ // Convert to local coordinates
+ const framepos_t p = _region->position();
+ const double y = midi_view()->y_position();
+ const double x0 = editor.sample_to_pixel(max((framepos_t)0, start - p));
+ const double x1 = editor.sample_to_pixel(max((framepos_t)0, end - p));
+ const double y0 = max(0.0, gy0 - y);
+ const double y1 = max(0.0, gy1 - y);
+
// TODO: Make this faster by storing the last updated selection rect, and only
// adjusting things that are in the area that appears/disappeared.
// We probably need a tree to be able to find events in O(log(n)) time.
remove_from_selection (*i);
}
}
+
+ typedef RouteTimeAxisView::AutomationTracks ATracks;
+ typedef std::list<Selectable*> Selectables;
+
+ /* Add control points to selection. */
+ const ATracks& atracks = midi_view()->automation_tracks();
+ Selectables selectables;
+ editor.get_selection().clear_points();
+ for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
+ a->second->get_selectables(start, end, gy0, gy1, selectables);
+ for (Selectables::const_iterator s = selectables.begin(); s != selectables.end(); ++s) {
+ ControlPoint* cp = dynamic_cast<ControlPoint*>(*s);
+ if (cp) {
+ editor.get_selection().add(cp);
+ }
+ }
+ a->second->set_selected_points(editor.get_selection().points);
+ }
}
void
}
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
- if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
+ if ((*i)->note()->time() == earliest) {
to_play.push_back ((*i)->note());
}
(*i)->move_event(dx, dy);
}
framepos_t
-MidiRegionView::source_beats_to_absolute_frames(double beats) const
+MidiRegionView::source_beats_to_absolute_frames(Evoral::MusicalTime beats) const
{
/* the time converter will return the frame corresponding to `beats'
relative to the start of the source. The start of the source
return source_start + _source_relative_time_converter.to (beats);
}
-double
+Evoral::MusicalTime
MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
{
/* the `frames' argument needs to be converted into a frame count
}
framepos_t
-MidiRegionView::region_beats_to_region_frames(double beats) const
+MidiRegionView::region_beats_to_region_frames(Evoral::MusicalTime beats) const
{
return _region_relative_time_converter.to(beats);
}
-double
+Evoral::MusicalTime
MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
{
return _region_relative_time_converter.from(frames);
// calculate the colors: get the color settings
uint32_t fill_color = UINT_RGBA_CHANGE_A(
- ARDOUR_UI::config()->get_canvasvar_MidiNoteSelected(),
+ ARDOUR_UI::config()->get_MidiNoteSelected(),
128);
// make the resize preview notes more transparent and bright
0.85));
resize_rect->set_outline_color (NoteBase::calculate_outline (
- ARDOUR_UI::config()->get_canvasvar_MidiNoteSelected()));
+ ARDOUR_UI::config()->get_MidiNoteSelected()));
resize_data->resize_rect = resize_rect;
_resize_data.push_back(resize_data);
}
}
+ if (current_x < 0) {
+ // This works even with snapping because RegionView::snap_frame_to_frame()
+ // snaps forward if the snapped sample is before the beginning of the region
+ current_x = 0;
+ }
+ if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
+ current_x = trackview.editor().sample_to_pixel(_region->length());
+ }
+
if (at_front) {
resize_rect->set_x0 (snap_to_pixel(current_x));
resize_rect->set_x1 (canvas_note->x1());
}
if (!cursor_set) {
- double beats;
-
- beats = snap_pixel_to_sample (current_x);
- beats = region_frames_to_region_beats (beats);
-
- double len;
+ const double snapped_x = snap_pixel_to_sample (current_x);
+ Evoral::MusicalTime beats = region_frames_to_region_beats (snapped_x);
+ Evoral::MusicalTime len = Evoral::MusicalTime();
if (at_front) {
if (beats < canvas_note->note()->end_time()) {
len = canvas_note->note()->time() - beats;
len += canvas_note->note()->length();
- } else {
- len = 0;
}
} else {
if (beats >= canvas_note->note()->time()) {
len = beats - canvas_note->note()->time();
- } else {
- len = 0;
}
}
char buf[16];
- snprintf (buf, sizeof (buf), "%.3g beats", len);
+ snprintf (buf, sizeof (buf), "%.3g beats", len.to_double());
show_verbose_cursor (buf, 0, 0);
cursor_set = true;
}
}
+ if (current_x < 0) {
+ current_x = 0;
+ }
+ if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
+ current_x = trackview.editor().sample_to_pixel(_region->length());
+ }
+
/* Convert that to a frame within the source */
current_x = snap_pixel_to_sample (current_x) + _region->start ();
/* and then to beats */
- current_x = region_frames_to_region_beats (current_x);
+ const Evoral::MusicalTime x_beats = region_frames_to_region_beats (current_x);
- if (at_front && current_x < canvas_note->note()->end_time()) {
- note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, current_x);
+ if (at_front && x_beats < canvas_note->note()->end_time()) {
+ note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats);
- double len = canvas_note->note()->time() - current_x;
+ Evoral::MusicalTime len = canvas_note->note()->time() - x_beats;
len += canvas_note->note()->length();
- if (len > 0) {
- /* XXX convert to beats */
+ if (!!len) {
note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
}
}
if (!at_front) {
- double len = current_x - canvas_note->note()->time();
+ const Evoral::MusicalTime len = x_beats - canvas_note->note()->time();
- if (len > 0) {
+ if (!!len) {
/* XXX convert to beats */
note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
}
{
bool change_start = false;
bool change_length = false;
- Evoral::MusicalTime new_start = 0;
- Evoral::MusicalTime new_length = 0;
+ Evoral::MusicalTime new_start;
+ Evoral::MusicalTime new_length;
/* NOTE: the semantics of the two delta arguments are slightly subtle:
if negative - move the end of the note earlier in time (shortening it)
*/
- if (front_delta) {
+ if (!!front_delta) {
if (front_delta < 0) {
if (event->note()->time() < -front_delta) {
- new_start = 0;
+ new_start = Evoral::MusicalTime();
} else {
new_start = event->note()->time() + front_delta; // moves earlier
}
}
- if (end_delta) {
+ if (!!end_delta) {
bool can_change = true;
if (end_delta < 0) {
if (event->note()->length() < -end_delta) {
if (relative) {
if (delta < 0.0) {
if (event->note()->time() < -delta) {
- new_time = 0;
+ new_time = Evoral::MusicalTime();
} else {
new_time = event->note()->time() + delta;
}
void
MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
{
- if (delta == 0.0) {
+ if (!delta) {
if (fine) {
- delta = 1.0/128.0;
+ delta = Evoral::MusicalTime(1.0/128.0);
} else {
/* grab the current grid distance */
- bool success;
- delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
- if (!success) {
- /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
- error << string_compose (_("programming error: %1"), "Grid type not available as beats - TO BE FIXED") << endmsg;
- return;
- }
+ delta = get_grid_beats(_region->position());
}
}
/* note the negation of the delta for start */
- trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
+ trim_note (*i,
+ (start ? -delta : Evoral::MusicalTime()),
+ (end ? delta : Evoral::MusicalTime()));
i = next;
}
next_pos -= 1;
}
- trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
+ trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), false);
distance = ref_point - next_pos;
}
{
Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
- pre_enter_cursor = editor->get_canvas_cursor ();
+ pre_note_enter_cursor = editor->get_canvas_cursor ();
if (_mouse_state == SelectTouchDragging) {
note_selected (ev, true);
editor->verbose_cursor()->hide ();
- if (pre_enter_cursor) {
- editor->set_canvas_cursor (pre_enter_cursor);
- pre_enter_cursor = 0;
+ if (pre_note_enter_cursor) {
+ editor->set_canvas_cursor (pre_note_enter_cursor);
+ pre_note_enter_cursor = 0;
}
}
Editing::MouseMode mm = editor->current_mouse_mode();
bool trimmable = (mm == MouseObject || mm == MouseTimeFX || mm == MouseDraw);
- if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
- editor->set_canvas_cursor (editor->cursors()->left_side_trim);
- } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
- editor->set_canvas_cursor (editor->cursors()->right_side_trim);
- } else {
- if (pre_enter_cursor && can_set_cursor) {
- editor->set_canvas_cursor (pre_enter_cursor);
+ if (can_set_cursor) {
+ if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
+ editor->set_canvas_cursor (editor->cursors()->left_side_trim);
+ } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
+ editor->set_canvas_cursor (editor->cursors()->right_side_trim);
+ } else if (pre_note_enter_cursor) {
+ editor->set_canvas_cursor (pre_note_enter_cursor);
}
}
}
}
if (_selected) {
- f = ARDOUR_UI::config()->get_canvasvar_SelectedFrameBase();
+ f = ARDOUR_UI::config()->get_SelectedFrameBase();
} else if (high_enough_for_name) {
- f= ARDOUR_UI::config()->get_canvasvar_MidiFrameBase();
+ f= ARDOUR_UI::config()->get_MidiFrameBase();
} else {
f = fill_color;
}
if (!rect_visible) {
- f = UINT_RGBA_CHANGE_A (f, 0);
+ f = UINT_RGBA_CHANGE_A (f, 80);
}
frame->set_fill_color (f);
}
/** This method handles undo */
-void
-MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
+bool
+MidiRegionView::paste (framepos_t pos, unsigned paste_count, float times, const ::Selection& selection, ItemCounts& counts)
{
- if (mcb.empty()) {
- return;
+ // Get our set of notes from the selection
+ MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(counts.n_notes());
+ if (m == selection.midi_notes.end()) {
+ return false;
}
+ counts.increase_n_notes();
- DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("MIDI paste @ %1 times %2\n", pos, times));
+ trackview.session()->begin_reversible_command (Operations::paste);
- trackview.session()->begin_reversible_command (_("paste"));
+ // Paste notes
+ paste_internal(pos, paste_count, times, **m);
- start_note_diff_command (_("paste"));
+ // Paste control points to automation children
+ typedef RouteTimeAxisView::AutomationTracks ATracks;
+ const ATracks& atracks = midi_view()->automation_tracks();
+ for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
+ a->second->paste(pos, paste_count, times, selection, counts);
+ }
- Evoral::MusicalTime beat_delta;
- Evoral::MusicalTime paste_pos_beats;
- Evoral::MusicalTime duration;
- Evoral::MusicalTime end_point = 0;
+ trackview.session()->commit_reversible_command ();
+
+ return true;
+}
- duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
- paste_pos_beats = absolute_frames_to_source_beats (pos);
- beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
- paste_pos_beats = 0;
+/** This method handles undo */
+void
+MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
+{
+ if (mcb.empty()) {
+ return;
+ }
- DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6 ; beat delta = %7\n",
- (*mcb.notes().begin())->time(),
- (*mcb.notes().rbegin())->end_time(),
+ start_note_diff_command (_("paste"));
+
+ const Evoral::MusicalTime snap_beats = get_grid_beats(pos);
+ const Evoral::MusicalTime first_time = (*mcb.notes().begin())->time();
+ const Evoral::MusicalTime last_time = (*mcb.notes().rbegin())->end_time();
+ const Evoral::MusicalTime duration = last_time - first_time;
+ const Evoral::MusicalTime snap_duration = duration.snap_to(snap_beats);
+ const Evoral::MusicalTime paste_offset = snap_duration * paste_count;
+ const Evoral::MusicalTime pos_beats = absolute_frames_to_source_beats(pos) + paste_offset;
+ Evoral::MusicalTime end_point = Evoral::MusicalTime();
+
+ DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n",
+ first_time,
+ last_time,
duration, pos, _region->position(),
- paste_pos_beats, beat_delta));
+ pos_beats));
clear_selection ();
for (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);
+ copied_note->set_time (pos_beats + copied_note->time() - first_time);
/* make all newly added notes selected */
note_diff_add_note (copied_note, true);
end_point = copied_note->end_time();
}
-
- paste_pos_beats += duration;
}
/* if we pasted past the current end of the region, extend the region */
}
apply_diff (true);
-
- trackview.session()->commit_reversible_command ();
}
struct EventNoteTimeEarlyFirstComparator {
/* use region_frames... because we are converting a delta within the region
*/
- bool success;
- double length = editor.get_grid_type_as_beats (success, unsnapped_frame);
-
- if (!success) {
- length = 1;
- }
+ const Evoral::MusicalTime length = get_grid_beats(unsnapped_frame);
/* note that this sets the time of the ghost note in beats relative to
the start of the source; that is how all note times are stored.
*/
- _ghost_note->note()->set_time (absolute_frames_to_source_beats (f + _region->position ()));
+ _ghost_note->note()->set_time (
+ std::max(Evoral::MusicalTime(),
+ absolute_frames_to_source_beats (f + _region->position ())));
_ghost_note->note()->set_length (length);
_ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
_ghost_note->note()->set_channel (mtv->get_channel_for_add ());
_ghost_note = new Note (*this, _note_group, g);
_ghost_note->set_ignore_events (true);
_ghost_note->set_outline_color (0x000000aa);
+ if (x < 0) { x = 0; }
update_ghost_note (x, y);
_ghost_note->show ();
MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
{
if (_step_edit_cursor == 0) {
- ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
+ ArdourCanvas::Item* const group = get_canvas_group();
_step_edit_cursor = new ArdourCanvas::Rectangle (group);
_step_edit_cursor->set_y0 (0);
if (ev.type() == MIDI_CMD_NOTE_ON) {
boost::shared_ptr<NoteType> note (
- new NoteType (ev.channel(), time_beats, 0, ev.note(), ev.velocity()));
+ new NoteType (ev.channel(), time_beats, Evoral::MusicalTime(), ev.note(), ev.velocity()));
add_note (note, true);
/* Reparent the note group to the region view's parent, so that it doesn't change
when the region view is trimmed.
*/
- _temporary_note_group = new ArdourCanvas::Group (group->parent ());
+ _temporary_note_group = new ArdourCanvas::Container (group->parent ());
_temporary_note_group->move (group->position ());
_note_group->reparent (_temporary_note_group);
}
void
MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
{
- double wx, wy;
-
- trackview.editor().verbose_cursor()->set_text (text);
- trackview.editor().get_pointer_position (wx, wy);
-
- wx += xoffset;
- wy += yoffset;
-
- /* Flip the cursor above the mouse pointer if it would overlap the bottom of the canvas */
-
- boost::optional<ArdourCanvas::Rect> bbo = trackview.editor().verbose_cursor()->item().bounding_box();
-
- assert (bbo);
-
- ArdourCanvas::Rect bb = bbo.get();
-
- if ((wy + bb.y1 - bb.y0) > trackview.editor().visible_canvas_height()) {
- wy -= (bb.y1 - bb.y0) + 2 * yoffset;
- }
-
- trackview.editor().verbose_cursor()->set_position (wx, wy);
+ trackview.editor().verbose_cursor()->set (text);
trackview.editor().verbose_cursor()->show ();
+ trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
}
/** @param p A session framepos.
{
PublicEditor& editor = trackview.editor ();
- bool success;
- Evoral::MusicalTime grid_beats = editor.get_grid_type_as_beats (success, p);
+ const Evoral::MusicalTime grid_beats = get_grid_beats(p);
- if (!success) {
- grid_beats = 1;
- }
-
grid_frames = region_beats_to_region_frames (grid_beats);
/* Hack so that we always snap to the note that we are over, instead of snapping
return rtav->midi_track()->get_playback_channel_mask();
}
+
+Evoral::MusicalTime
+MidiRegionView::get_grid_beats(framepos_t pos) const
+{
+ PublicEditor& editor = trackview.editor();
+ bool success = false;
+ Evoral::MusicalTime beats = editor.get_grid_type_as_beats(success, pos);
+ if (!success) {
+ beats = Evoral::MusicalTime(1);
+ }
+ return beats;
+}