refactor MidiRegionView event handling to avoid too much spaghetti as we move along

git-svn-id: svn://localhost/ardour2/branches/3.0@7182 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis
2010-05-27 15:07:15 +00:00
parent d15082d828
commit 8689ae88f7
2 changed files with 379 additions and 317 deletions

View File

@@ -86,6 +86,7 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &
, _delta_command(0)
, _diff_command(0)
, _ghost_note(0)
, _drag_rect (0)
, _mouse_state(None)
, _pressed_button(0)
, _sort_needed (true)
@@ -109,13 +110,13 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &
, _delta_command(0)
, _diff_command(0)
, _ghost_note(0)
, _drag_rect (0)
, _mouse_state(None)
, _pressed_button(0)
, _sort_needed (true)
, _optimization_iterator (_events.end())
, _list_editor (0)
, no_sound_notes (false)
{
_note_group->raise_to_top();
}
@@ -133,6 +134,7 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other)
, _delta_command(0)
, _diff_command(0)
, _ghost_note(0)
, _drag_rect (0)
, _mouse_state(None)
, _pressed_button(0)
, _sort_needed (true)
@@ -160,6 +162,7 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<M
, _delta_command(0)
, _diff_command(0)
, _ghost_note(0)
, _drag_rect (0)
, _mouse_state(None)
, _pressed_button(0)
, _sort_needed (true)
@@ -227,351 +230,396 @@ MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
bool
MidiRegionView::canvas_event(GdkEvent* ev)
{
PublicEditor& editor (trackview.editor());
if (!editor.internal_editing()) {
if (!trackview.editor().internal_editing()) {
return false;
}
static double drag_start_x, drag_start_y;
static double last_x, last_y;
double event_x, event_y;
nframes64_t event_frame = 0;
bool fine;
static ArdourCanvas::SimpleRect* drag_rect = 0;
/* XXX: note that as of August 2009, the GnomeCanvas does not propagate scroll events
/* XXX: note that until version 2.30, the GnomeCanvas did not propagate scroll events
to its items, which means that ev->type == GDK_SCROLL will never be seen
*/
switch (ev->type) {
case GDK_SCROLL:
fine = Keyboard::modifier_state_equals (ev->scroll.state, Keyboard::Level4Modifier);
if (ev->scroll.direction == GDK_SCROLL_UP) {
change_velocities (true, fine, false);
return true;
} else if (ev->scroll.direction == GDK_SCROLL_DOWN) {
change_velocities (false, fine, false);
return true;
} else {
return false;
}
break;
return scroll (&ev->scroll);
case GDK_KEY_PRESS:
/* since GTK bindings are generally activated on press, and since
detectable auto-repeat is the name of the game and only sends
repeated presses, carry out key actions at key press, not release.
*/
if (ev->key.keyval == GDK_Alt_L || ev->key.keyval == GDK_Alt_R){
_mouse_state = SelectTouchDragging;
return true;
} else if (ev->key.keyval == GDK_Escape) {
clear_selection();
_mouse_state = None;
} else if (ev->key.keyval == GDK_comma || ev->key.keyval == GDK_period) {
bool start = (ev->key.keyval == GDK_comma);
bool end = (ev->key.keyval == GDK_period);
bool shorter = Keyboard::modifier_state_contains (ev->key.state, Keyboard::PrimaryModifier);
fine = Keyboard::modifier_state_contains (ev->key.state, Keyboard::SecondaryModifier);
change_note_lengths (fine, shorter, start, end);
return true;
} else if (ev->key.keyval == GDK_Delete) {
delete_selection();
return true;
} else if (ev->key.keyval == GDK_Tab) {
if (Keyboard::modifier_state_equals (ev->key.state, Keyboard::PrimaryModifier)) {
goto_previous_note ();
} else {
goto_next_note ();
}
return true;
} else if (ev->key.keyval == GDK_Up) {
bool allow_smush = Keyboard::modifier_state_contains (ev->key.state, Keyboard::SecondaryModifier);
bool fine = Keyboard::modifier_state_contains (ev->key.state, Keyboard::TertiaryModifier);
if (Keyboard::modifier_state_contains (ev->key.state, Keyboard::PrimaryModifier)) {
change_velocities (true, fine, allow_smush);
} else {
transpose (true, fine, allow_smush);
}
return true;
} else if (ev->key.keyval == GDK_Down) {
bool allow_smush = Keyboard::modifier_state_contains (ev->key.state, Keyboard::SecondaryModifier);
fine = Keyboard::modifier_state_contains (ev->key.state, Keyboard::TertiaryModifier);
if (Keyboard::modifier_state_contains (ev->key.state, Keyboard::PrimaryModifier)) {
change_velocities (false, fine, allow_smush);
} else {
transpose (false, fine, allow_smush);
}
return true;
} else if (ev->key.keyval == GDK_Left) {
nudge_notes (false);
return true;
} else if (ev->key.keyval == GDK_Right) {
nudge_notes (true);
return true;
} else if (ev->key.keyval == GDK_Control_L) {
return true;
} else if (ev->key.keyval == GDK_r) {
/* if we're not step editing, this really doesn't matter */
midi_view()->step_edit_rest ();
return true;
}
return false;
return key_press (&ev->key);
case GDK_KEY_RELEASE:
if (ev->key.keyval == GDK_Alt_L || ev->key.keyval == GDK_Alt_R) {
_mouse_state = None;
return true;
}
return false;
return key_release (&ev->key);
case GDK_BUTTON_PRESS:
last_x = ev->button.x;
last_y = ev->button.y;
group->w2i (last_x, last_y);
if (_mouse_state != SelectTouchDragging && ev->button.button == 1) {
_pressed_button = ev->button.button;
_mouse_state = Pressed;
return true;
}
_pressed_button = ev->button.button;
return true;
return button_press (&ev->button);
case GDK_2BUTTON_PRESS:
return true;
case GDK_BUTTON_RELEASE:
return button_release (&ev->button);
case GDK_ENTER_NOTIFY:
{
/* FIXME: do this on switch to note tool, too, if the pointer is already in */
Keyboard::magic_widget_grab_focus();
group->grab_focus();
if (editor.current_mouse_mode() == MouseRange) {
create_ghost_note (ev->crossing.x, ev->crossing.y);
}
break;
}
return enter_notify (&ev->crossing);
case GDK_LEAVE_NOTIFY:
{
trackview.editor().hide_verbose_canvas_cursor ();
delete _ghost_note;
_ghost_note = 0;
break;
}
return leave_notify (&ev->crossing);
case GDK_MOTION_NOTIFY:
{
event_x = ev->motion.x;
event_y = ev->motion.y;
group->w2i(event_x, event_y);
return motion (&ev->motion);
// convert event_x to global frame
event_frame = trackview.editor().pixel_to_frame(event_x) + _region->position();
trackview.editor().snap_to(event_frame);
// convert event_frame back to local coordinates relative to position
event_frame -= _region->position();
if (_ghost_note) {
update_ghost_note (ev->motion.x, ev->motion.y);
}
switch (_mouse_state) {
case Pressed: // Maybe start a drag, if we've moved a bit
if (fabs (event_x - last_x) < 1 && fabs (event_y - last_y) < 1) {
/* no appreciable movement since the button was pressed */
return false;
}
// Select drag start
if (_pressed_button == 1 && 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;
last_y = event_y;
drag_start_x = event_x;
drag_start_y = event_y;
drag_rect = new ArdourCanvas::SimpleRect(*group);
drag_rect->property_x1() = event_x;
drag_rect->property_y1() = event_y;
drag_rect->property_x2() = event_x;
drag_rect->property_y2() = event_y;
drag_rect->property_outline_what() = 0xFF;
drag_rect->property_outline_color_rgba()
= ARDOUR_UI::config()->canvasvar_MidiSelectRectOutline.get();
drag_rect->property_fill_color_rgba()
= ARDOUR_UI::config()->canvasvar_MidiSelectRectFill.get();
_mouse_state = SelectRectDragging;
return true;
// Add note drag start
} else if (editor.internal_editing()) {
delete _ghost_note;
_ghost_note = 0;
group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
Gdk::Cursor(Gdk::FLEUR), ev->motion.time);
last_x = event_x;
last_y = event_y;
drag_start_x = event_x;
drag_start_y = event_y;
drag_rect = new ArdourCanvas::SimpleRect(*group);
drag_rect->property_x1() = trackview.editor().frame_to_pixel(event_frame);
drag_rect->property_y1() = midi_stream_view()->note_to_y(
midi_stream_view()->y_to_note(event_y));
drag_rect->property_x2() = trackview.editor().frame_to_pixel(event_frame);
drag_rect->property_y2() = drag_rect->property_y1()
+ floor(midi_stream_view()->note_height());
drag_rect->property_outline_what() = 0xFF;
drag_rect->property_outline_color_rgba() = 0xFFFFFF99;
drag_rect->property_fill_color_rgba() = 0xFFFFFF66;
_mouse_state = AddDragging;
return true;
}
return false;
case SelectRectDragging: // Select drag motion
case AddDragging: // Add note drag motion
if (ev->motion.is_hint) {
int t_x;
int t_y;
GdkModifierType state;
gdk_window_get_pointer(ev->motion.window, &t_x, &t_y, &state);
event_x = t_x;
event_y = t_y;
}
if (_mouse_state == AddDragging)
event_x = trackview.editor().frame_to_pixel(event_frame);
if (drag_rect) {
if (event_x > drag_start_x)
drag_rect->property_x2() = event_x;
else
drag_rect->property_x1() = event_x;
}
if (drag_rect && _mouse_state == SelectRectDragging) {
if (event_y > drag_start_y)
drag_rect->property_y2() = event_y;
else
drag_rect->property_y1() = event_y;
update_drag_selection(drag_start_x, event_x, drag_start_y, event_y);
}
last_x = event_x;
last_y = event_y;
case SelectTouchDragging:
return false;
default:
break;
}
break;
}
case GDK_BUTTON_RELEASE:
event_x = ev->motion.x;
event_y = ev->motion.y;
group->w2i(event_x, event_y);
group->ungrab(ev->button.time);
event_frame = trackview.editor().pixel_to_frame(event_x);
if (ev->button.button == 3) {
return false;
} else if (_pressed_button != 1) {
return false;
}
switch (_mouse_state) {
case Pressed: // Clicked
switch (editor.current_mouse_mode()) {
case MouseObject:
case MouseTimeFX:
clear_selection();
break;
case MouseRange:
{
bool success;
Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, trackview.editor().pixel_to_frame (event_x));
if (!success) {
beats = 1;
}
create_note_at (event_x, event_y, beats);
break;
}
default:
break;
}
_mouse_state = None;
break;
case SelectRectDragging: // Select drag done
_mouse_state = None;
delete drag_rect;
drag_rect = 0;
break;
case AddDragging: // Add drag done
_mouse_state = None;
if (drag_rect->property_x2() > drag_rect->property_x1() + 2) {
const double x = drag_rect->property_x1();
const double length = trackview.editor().pixel_to_frame(
drag_rect->property_x2() - drag_rect->property_x1());
create_note_at(x, drag_rect->property_y1(), frames_to_beats(length));
}
delete drag_rect;
drag_rect = 0;
create_ghost_note (ev->button.x, ev->button.y);
default: break;
}
default: break;
default:
break;
}
return false;
}
bool
MidiRegionView::enter_notify (GdkEventCrossing* ev)
{
/* FIXME: do this on switch to note tool, too, if the pointer is already in */
Keyboard::magic_widget_grab_focus();
group->grab_focus();
if (trackview.editor().current_mouse_mode() == MouseRange) {
create_ghost_note (ev->x, ev->y);
}
return false;
}
bool
MidiRegionView::leave_notify (GdkEventCrossing* ev)
{
trackview.editor().hide_verbose_canvas_cursor ();
delete _ghost_note;
_ghost_note = 0;
return false;
}
bool
MidiRegionView::button_press (GdkEventButton* ev)
{
_last_x = ev->x;
_last_y = ev->y;
group->w2i (_last_x, _last_y);
if (_mouse_state != SelectTouchDragging && ev->button == 1) {
_pressed_button = ev->button;
_mouse_state = Pressed;
return true;
}
_pressed_button = ev->button;
return true;
}
bool
MidiRegionView::button_release (GdkEventButton* ev)
{
double event_x, event_y;
nframes64_t event_frame = 0;
event_x = ev->x;
event_y = ev->y;
group->w2i(event_x, event_y);
group->ungrab(ev->time);
event_frame = trackview.editor().pixel_to_frame(event_x);
if (ev->button == 3) {
return false;
} else if (_pressed_button != 1) {
return false;
}
switch (_mouse_state) {
case Pressed: // Clicked
switch (trackview.editor().current_mouse_mode()) {
case MouseObject:
case MouseTimeFX:
clear_selection();
break;
case MouseRange:
{
bool success;
Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, trackview.editor().pixel_to_frame (event_x));
if (!success) {
beats = 1;
}
create_note_at (event_x, event_y, beats);
break;
}
default:
break;
}
_mouse_state = None;
break;
case SelectRectDragging: // Select drag done
_mouse_state = None;
delete _drag_rect;
_drag_rect = 0;
break;
case AddDragging: // Add drag done
_mouse_state = None;
if (_drag_rect->property_x2() > _drag_rect->property_x1() + 2) {
const double x = _drag_rect->property_x1();
const double length = trackview.editor().pixel_to_frame
(_drag_rect->property_x2() - _drag_rect->property_x1());
create_note_at (x, _drag_rect->property_y1(), frames_to_beats(length));
}
delete _drag_rect;
_drag_rect = 0;
create_ghost_note (ev->x, ev->y);
default:
break;
}
return false;
}
bool
MidiRegionView::motion (GdkEventMotion* ev)
{
double event_x, event_y;
nframes64_t event_frame = 0;
event_x = ev->x;
event_y = ev->y;
group->w2i(event_x, event_y);
// convert event_x to global frame
event_frame = trackview.editor().pixel_to_frame(event_x) + _region->position();
trackview.editor().snap_to(event_frame);
// convert event_frame back to local coordinates relative to position
event_frame -= _region->position();
if (_ghost_note) {
update_ghost_note (ev->x, ev->y);
}
switch (_mouse_state) {
case Pressed: // Maybe start a drag, if we've moved a bit
if (fabs (event_x - _last_x) < 1 && fabs (event_y - _last_y) < 1) {
/* no appreciable movement since the button was pressed */
return false;
}
// Select drag start
if (_pressed_button == 1 && trackview.editor().current_mouse_mode() == MouseObject) {
group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
Gdk::Cursor(Gdk::FLEUR), ev->time);
_last_x = event_x;
_last_y = event_y;
_drag_start_x = event_x;
_drag_start_y = event_y;
_drag_rect = new ArdourCanvas::SimpleRect(*group);
_drag_rect->property_x1() = event_x;
_drag_rect->property_y1() = event_y;
_drag_rect->property_x2() = event_x;
_drag_rect->property_y2() = event_y;
_drag_rect->property_outline_what() = 0xFF;
_drag_rect->property_outline_color_rgba()
= ARDOUR_UI::config()->canvasvar_MidiSelectRectOutline.get();
_drag_rect->property_fill_color_rgba()
= ARDOUR_UI::config()->canvasvar_MidiSelectRectFill.get();
_mouse_state = SelectRectDragging;
return true;
// Add note drag start
} else if (trackview.editor().internal_editing()) {
delete _ghost_note;
_ghost_note = 0;
group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
Gdk::Cursor(Gdk::FLEUR), ev->time);
_last_x = event_x;
_last_y = event_y;
_drag_start_x = event_x;
_drag_start_y = event_y;
_drag_rect = new ArdourCanvas::SimpleRect(*group);
_drag_rect->property_x1() = trackview.editor().frame_to_pixel(event_frame);
_drag_rect->property_y1() = midi_stream_view()->note_to_y(
midi_stream_view()->y_to_note(event_y));
_drag_rect->property_x2() = trackview.editor().frame_to_pixel(event_frame);
_drag_rect->property_y2() = _drag_rect->property_y1()
+ floor(midi_stream_view()->note_height());
_drag_rect->property_outline_what() = 0xFF;
_drag_rect->property_outline_color_rgba() = 0xFFFFFF99;
_drag_rect->property_fill_color_rgba() = 0xFFFFFF66;
_mouse_state = AddDragging;
return true;
}
return false;
case SelectRectDragging: // Select drag motion
case AddDragging: // Add note drag motion
if (ev->is_hint) {
int t_x;
int t_y;
GdkModifierType state;
gdk_window_get_pointer(ev->window, &t_x, &t_y, &state);
event_x = t_x;
event_y = t_y;
}
if (_mouse_state == AddDragging)
event_x = trackview.editor().frame_to_pixel(event_frame);
if (_drag_rect) {
if (event_x > _drag_start_x)
_drag_rect->property_x2() = event_x;
else
_drag_rect->property_x1() = event_x;
}
if (_drag_rect && _mouse_state == SelectRectDragging) {
if (event_y > _drag_start_y)
_drag_rect->property_y2() = event_y;
else
_drag_rect->property_y1() = event_y;
update_drag_selection(_drag_start_x, event_x, _drag_start_y, event_y);
}
_last_x = event_x;
_last_y = event_y;
case SelectTouchDragging:
return false;
default:
break;
}
return false;
}
bool
MidiRegionView::scroll (GdkEventScroll* ev)
{
bool fine = Keyboard::modifier_state_equals (ev->state, Keyboard::Level4Modifier);
if (ev->direction == GDK_SCROLL_UP) {
change_velocities (true, fine, false);
return true;
} else if (ev->direction == GDK_SCROLL_DOWN) {
change_velocities (false, fine, false);
return true;
}
return false;
}
bool
MidiRegionView::key_press (GdkEventKey* ev)
{
/* since GTK bindings are generally activated on press, and since
detectable auto-repeat is the name of the game and only sends
repeated presses, carry out key actions at key press, not release.
*/
if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R){
_mouse_state = SelectTouchDragging;
return true;
} else if (ev->keyval == GDK_Escape) {
clear_selection();
_mouse_state = None;
} else if (ev->keyval == GDK_comma || ev->keyval == GDK_period) {
bool start = (ev->keyval == GDK_comma);
bool end = (ev->keyval == GDK_period);
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, start, end);
return true;
} else if (ev->keyval == GDK_Delete) {
delete_selection();
return true;
} else if (ev->keyval == GDK_Tab) {
if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
goto_previous_note ();
} else {
goto_next_note ();
}
return true;
} else if (ev->keyval == GDK_Up) {
bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
change_velocities (true, fine, allow_smush);
} else {
transpose (true, fine, allow_smush);
}
return true;
} else if (ev->keyval == GDK_Down) {
bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
change_velocities (false, fine, allow_smush);
} else {
transpose (false, fine, allow_smush);
}
return true;
} else if (ev->keyval == GDK_Left) {
nudge_notes (false);
return true;
} else if (ev->keyval == GDK_Right) {
nudge_notes (true);
return true;
} else if (ev->keyval == GDK_Control_L) {
return true;
} else if (ev->keyval == GDK_r) {
/* if we're not step editing, this really doesn't matter */
midi_view()->step_edit_rest ();
return true;
}
return false;
}
bool
MidiRegionView::key_release (GdkEventKey* ev)
{
if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R) {
_mouse_state = None;
return true;
}
return false;
}
void
MidiRegionView::show_list_editor ()
{

View File

@@ -372,6 +372,11 @@ class MidiRegionView : public RegionView
ArdourCanvas::CanvasNote* _ghost_note;
double _last_ghost_x;
double _last_ghost_y;
double _drag_start_x;
double _drag_start_y;
double _last_x;
double _last_y;
ArdourCanvas::SimpleRect* _drag_rect;
MouseState _mouse_state;
int _pressed_button;
@@ -416,6 +421,15 @@ class MidiRegionView : public RegionView
PBD::ScopedConnection snap_changed_connection;
void show_verbose_canvas_cursor (boost::shared_ptr<NoteType>) const;
bool motion (GdkEventMotion*);
bool scroll (GdkEventScroll*);
bool key_press (GdkEventKey*);
bool key_release (GdkEventKey*);
bool button_press (GdkEventButton*);
bool button_release (GdkEventButton*);
bool enter_notify (GdkEventCrossing*);
bool leave_notify (GdkEventCrossing*);
};