X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Feditor_canvas_events.cc;h=adb896af8b8235b46e08998f87f56338a8767cd3;hb=6129b3492d3e47f7c027278f795ebb5cbea53cca;hp=f821220cf38a2229ebef88650891cbc91aa8f6e7;hpb=f03a87a132feb525a743de675d0439e981ab329a;p=ardour.git diff --git a/gtk2_ardour/editor_canvas_events.cc b/gtk2_ardour/editor_canvas_events.cc index f821220cf3..adb896af8b 100644 --- a/gtk2_ardour/editor_canvas_events.cc +++ b/gtk2_ardour/editor_canvas_events.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2000 Paul Davis + Copyright (C) 2000 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,138 +20,125 @@ #include #include #include +#include -#include +#include "pbd/stacktrace.h" -#include -#include +#include "ardour/audio_track.h" +#include "ardour/midi_track.h" +#include "ardour/midi_region.h" +#include "ardour/region_factory.h" +#include "ardour/profile.h" + +#include "canvas/canvas.h" +#include "canvas/text.h" +#include "canvas/scroll_group.h" #include "editor.h" #include "keyboard.h" #include "public_editor.h" +#include "ardour_ui.h" #include "audio_region_view.h" #include "audio_streamview.h" -#include "crossfade_view.h" #include "audio_time_axis.h" #include "region_gain_line.h" #include "automation_line.h" #include "automation_time_axis.h" #include "automation_line.h" #include "control_point.h" -#include "canvas_impl.h" -#include "simplerect.h" -#include "canvas-note-event.h" -#include "canvas-program-change.h" +#include "editor_drag.h" +#include "midi_time_axis.h" +#include "editor_regions.h" +#include "verbose_cursor.h" #include "i18n.h" -using namespace sigc; using namespace std; using namespace ARDOUR; using namespace PBD; using namespace Gtk; using namespace ArdourCanvas; +using Gtkmm2ext::Keyboard; + bool Editor::track_canvas_scroll (GdkEventScroll* ev) { - int x, y; - double wx, wy; - nframes64_t xdelta; + if (Keyboard::some_magic_widget_has_focus()) { + return false; + } + + framepos_t xdelta; int direction = ev->direction; - CanvasNoteEvent *midi_event = dynamic_cast(track_canvas->get_item_at(ev->x, ev->y)); - CanvasFlagRect *flag_rect = dynamic_cast(track_canvas->get_item_at(ev->x, ev->y)); - CanvasFlagText *flag_text = dynamic_cast(track_canvas->get_item_at(ev->x, ev->y)); + + /* this event arrives without transformation by the canvas, so we have + * to transform the coordinates to be able to look things up. + */ + + Duple event_coords = _track_canvas->window_to_canvas (Duple (ev->x, ev->y)); retry: switch (direction) { case GDK_SCROLL_UP: - if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) { - //if (ev->state == GDK_CONTROL_MASK) { - /* XXX - the ev->x will be out of step with the canvas - if we're in mid zoom, so we have to get the damn mouse - pointer again - */ - track_canvas->get_pointer (x, y); - track_canvas->window_to_world (x, y, wx, wy); - - GdkEvent event; - event.type = GDK_BUTTON_RELEASE; - event.button.x = wx; - event.button.y = wy; - - nframes64_t where = event_frame (&event, 0, 0); - temporal_zoom_to_frame (false, where); + if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) { + //for mouse-wheel zoom, force zoom-focus to mouse + Editing::ZoomFocus temp_focus = zoom_focus; + zoom_focus = Editing::ZoomFocusMouse; + temporal_zoom_step (false); + zoom_focus = temp_focus; return true; - } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier)) { + } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollHorizontalModifier)) { direction = GDK_SCROLL_LEFT; goto retry; - } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) { + } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) { if (!current_stepping_trackview) { - step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500); - if (!(current_stepping_trackview = trackview_by_y_position (ev->y + vertical_adjustment.get_value() - canvas_timebars_vsize))) { + step_timeout = Glib::signal_timeout().connect (sigc::mem_fun(*this, &Editor::track_height_step_timeout), 500); + std::pair const p = trackview_by_y_position (event_coords.y, false); + current_stepping_trackview = p.first; + if (!current_stepping_trackview) { return false; } } last_track_height_step_timestamp = get_microseconds(); - current_stepping_trackview->step_height (true); + current_stepping_trackview->step_height (false); return true; } else { - if(midi_event) { - return midi_event->on_event(reinterpret_cast(ev)); - } else if (flag_rect) { - return flag_rect->on_event(reinterpret_cast(ev)); - } else if (flag_text) { - return flag_text->on_event(reinterpret_cast(ev)); - } - scroll_tracks_up_line (); + scroll_up_one_track (); return true; } break; case GDK_SCROLL_DOWN: - if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) { - //if (ev->state == GDK_CONTROL_MASK) { - track_canvas->get_pointer (x, y); - track_canvas->window_to_world (x, y, wx, wy); - - GdkEvent event; - event.type = GDK_BUTTON_RELEASE; - event.button.x = wx; - event.button.y = wy; - - nframes64_t where = event_frame (&event, 0, 0); - temporal_zoom_to_frame (true, where); + if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) { + //for mouse-wheel zoom, force zoom-focus to mouse + Editing::ZoomFocus temp_focus = zoom_focus; + zoom_focus = Editing::ZoomFocusMouse; + temporal_zoom_step (true); + zoom_focus = temp_focus; return true; - } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier)) { + } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollHorizontalModifier)) { direction = GDK_SCROLL_RIGHT; goto retry; - } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) { + } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) { if (!current_stepping_trackview) { - step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500); - if (!(current_stepping_trackview = trackview_by_y_position (ev->y + vertical_adjustment.get_value() - canvas_timebars_vsize))) { + step_timeout = Glib::signal_timeout().connect (sigc::mem_fun(*this, &Editor::track_height_step_timeout), 500); + std::pair const p = trackview_by_y_position (event_coords.y, false); + current_stepping_trackview = p.first; + if (!current_stepping_trackview) { return false; } } last_track_height_step_timestamp = get_microseconds(); - current_stepping_trackview->step_height (false); + current_stepping_trackview->step_height (true); return true; } else { - if(midi_event) { - return midi_event->on_event(reinterpret_cast(ev)); - } else if (flag_rect) { - return flag_rect->on_event(reinterpret_cast(ev)); - } else if (flag_text) { - return flag_text->on_event(reinterpret_cast(ev)); - } - scroll_tracks_down_line (); + scroll_down_one_track (); return true; } - break; + break; case GDK_SCROLL_LEFT: - xdelta = (current_page_frames() / 8); + xdelta = (current_page_samples() / 8); if (leftmost_frame > xdelta) { reset_x_origin (leftmost_frame - xdelta); } else { @@ -160,11 +147,11 @@ Editor::track_canvas_scroll (GdkEventScroll* ev) break; case GDK_SCROLL_RIGHT: - xdelta = (current_page_frames() / 8); - if (max_frames - xdelta > leftmost_frame) { + xdelta = (current_page_samples() / 8); + if (max_framepos - xdelta > leftmost_frame) { reset_x_origin (leftmost_frame + xdelta); } else { - reset_x_origin (max_frames - current_page_frames()); + reset_x_origin (max_framepos - current_page_samples()); } break; @@ -172,52 +159,53 @@ Editor::track_canvas_scroll (GdkEventScroll* ev) /* what? */ break; } - + return false; } bool -Editor::track_canvas_scroll_event (GdkEventScroll *event) +Editor::canvas_scroll_event (GdkEventScroll *event, bool from_canvas) { - track_canvas->grab_focus(); - track_canvas_scroll (event); - return false; + if (from_canvas) { + boost::optional rulers = _time_markers_group->bounding_box(); + if (rulers && rulers->contains (Duple (event->x, event->y))) { + return canvas_ruler_event ((GdkEvent*) event, timecode_ruler, TimecodeRulerItem); + } + } + + _track_canvas->grab_focus(); + return track_canvas_scroll (event); } bool Editor::track_canvas_button_press_event (GdkEventButton *event) { - selection->clear (); - track_canvas->grab_focus(); + _track_canvas->grab_focus(); + if (!Keyboard::is_context_menu_event (event)) { + begin_reversible_selection_op (X_("Clear Selection Click (track canvas)")); + selection->clear (); + commit_reversible_selection_op(); + } return false; } bool Editor::track_canvas_button_release_event (GdkEventButton *event) { - if (drag_info.item) { - end_grab (drag_info.item, (GdkEvent*) event); - } + if (!Keyboard::is_context_menu_event (event)) { + if (_drags->active ()) { + _drags->end_grab ((GdkEvent*) event); + } + } return false; } bool -Editor::track_canvas_motion_notify_event (GdkEventMotion *event) +Editor::track_canvas_motion_notify_event (GdkEventMotion */*event*/) { int x, y; /* keep those motion events coming */ - track_canvas->get_pointer (x, y); - return false; -} - -bool -Editor::track_canvas_motion (GdkEvent *ev) -{ - if (verbose_cursor_visible) { - verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (ev->motion.x + 10); - verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (ev->motion.y + 10); - } - + _track_canvas->get_pointer (x, y); return false; } @@ -225,7 +213,7 @@ bool Editor::typed_event (ArdourCanvas::Item* item, GdkEvent *event, ItemType type) { gint ret = FALSE; - + switch (event->type) { case GDK_BUTTON_PRESS: case GDK_2BUTTON_PRESS: @@ -236,7 +224,7 @@ Editor::typed_event (ArdourCanvas::Item* item, GdkEvent *event, ItemType type) ret = button_release_handler (item, event, type); break; case GDK_MOTION_NOTIFY: - ret = motion_handler (item, event, type); + ret = motion_handler (item, event); break; case GDK_ENTER_NOTIFY: @@ -247,6 +235,14 @@ Editor::typed_event (ArdourCanvas::Item* item, GdkEvent *event, ItemType type) ret = leave_handler (item, event, type); break; + case GDK_KEY_PRESS: + ret = key_press_handler (item, event, type); + break; + + case GDK_KEY_RELEASE: + ret = key_release_handler (item, event, type); + break; + default: break; } @@ -262,7 +258,6 @@ Editor::canvas_region_view_event (GdkEvent *event, ArdourCanvas::Item* item, Reg return false; } - switch (event->type) { case GDK_BUTTON_PRESS: case GDK_2BUTTON_PRESS: @@ -279,17 +274,19 @@ Editor::canvas_region_view_event (GdkEvent *event, ArdourCanvas::Item* item, Reg break; case GDK_MOTION_NOTIFY: - ret = motion_handler (item, event, RegionItem); + ret = motion_handler (item, event); break; case GDK_ENTER_NOTIFY: - set_entered_track (&rv->get_time_axis_view ()); set_entered_regionview (rv); + ret = enter_handler (item, event, RegionItem); break; case GDK_LEAVE_NOTIFY: - set_entered_track (0); - set_entered_regionview (0); + if (event->crossing.detail != GDK_NOTIFY_INFERIOR) { + set_entered_regionview (0); + ret = leave_handler (item, event, RegionItem); + } break; default: @@ -299,11 +296,47 @@ Editor::canvas_region_view_event (GdkEvent *event, ArdourCanvas::Item* item, Reg return ret; } +bool +Editor::canvas_wave_view_event (GdkEvent *event, ArdourCanvas::Item* item, RegionView* rv) +{ + /* we only care about enter events here, required for mouse/cursor + * tracking. there is a non-linear (non-child/non-parent) relationship + * between various components of a regionview and so when we leave one + * of them (e.g. a trim handle) and enter another (e.g. the waveview) + * no other items get notified. enter/leave handling does not propagate + * in the same way as other events, so we need to catch this because + * entering (and leaving) the waveview is equivalent to + * entering/leaving the regionview (which is why it is passed in as a + * third argument). + * + * And in fact, we really only care about enter events. + */ + + bool ret = false; + + if (!rv->sensitive ()) { + return false; + } + + switch (event->type) { + case GDK_ENTER_NOTIFY: + set_entered_regionview (rv); + ret = enter_handler (item, event, WaveItem); + break; + + default: + break; + } + + return ret; +} + + bool Editor::canvas_stream_view_event (GdkEvent *event, ArdourCanvas::Item* item, RouteTimeAxisView *tv) { bool ret = FALSE; - + switch (event->type) { case GDK_BUTTON_PRESS: case GDK_2BUTTON_PRESS: @@ -311,7 +344,7 @@ Editor::canvas_stream_view_event (GdkEvent *event, ArdourCanvas::Item* item, Rou clicked_regionview = 0; clicked_control_point = 0; clicked_axisview = tv; - clicked_routeview = dynamic_cast(tv); + clicked_routeview = dynamic_cast(clicked_axisview); ret = button_press_handler (item, event, StreamItem); break; @@ -320,15 +353,19 @@ Editor::canvas_stream_view_event (GdkEvent *event, ArdourCanvas::Item* item, Rou break; case GDK_MOTION_NOTIFY: - ret = motion_handler (item, event, StreamItem); + ret = motion_handler (item, event); break; case GDK_ENTER_NOTIFY: set_entered_track (tv); + ret = enter_handler (item, event, StreamItem); break; case GDK_LEAVE_NOTIFY: - set_entered_track (0); + if (event->crossing.detail != GDK_NOTIFY_INFERIOR) { + set_entered_track (0); + } + ret = leave_handler (item, event, StreamItem); break; default: @@ -343,7 +380,6 @@ Editor::canvas_automation_track_event (GdkEvent *event, ArdourCanvas::Item* item { bool ret = false; - switch (event->type) { case GDK_BUTTON_PRESS: case GDK_2BUTTON_PRESS: @@ -360,7 +396,7 @@ Editor::canvas_automation_track_event (GdkEvent *event, ArdourCanvas::Item* item break; case GDK_MOTION_NOTIFY: - ret = motion_handler (item, event, AutomationTrackItem); + ret = motion_handler (item, event); break; case GDK_ENTER_NOTIFY: @@ -379,10 +415,8 @@ Editor::canvas_automation_track_event (GdkEvent *event, ArdourCanvas::Item* item } bool -Editor::canvas_fade_in_event (GdkEvent *event, ArdourCanvas::Item* item, AudioRegionView *rv) +Editor::canvas_start_xfade_event (GdkEvent *event, ArdourCanvas::Item* item, AudioRegionView *rv) { - /* we handle only button 3 press/release events */ - if (!rv->sensitive()) { return false; } @@ -394,71 +428,64 @@ Editor::canvas_fade_in_event (GdkEvent *event, ArdourCanvas::Item* item, AudioRe clicked_axisview = &rv->get_time_axis_view(); clicked_routeview = dynamic_cast(clicked_axisview); if (event->button.button == 3) { - return button_press_handler (item, event, FadeInItem); + return button_press_handler (item, event, StartCrossFadeItem); } break; case GDK_BUTTON_RELEASE: if (event->button.button == 3) { - return button_release_handler (item, event, FadeInItem); + return button_release_handler (item, event, StartCrossFadeItem); } break; default: break; - + } - /* proxy for the regionview */ - - return canvas_region_view_event (event, rv->get_canvas_group(), rv); + /* In Mixbus, the crossfade area is used to trim the region while leaving the fade anchor intact (see preserve_fade_anchor)*/ + /* however in A3 this feature is unfinished, and it might be better to do it with a modifier-trim instead, anyway */ + /* if we return RegionItem here then we avoid the issue until it is resolved later */ + return typed_event (item, event, RegionItem); // StartCrossFadeItem); } bool -Editor::canvas_fade_in_handle_event (GdkEvent *event, ArdourCanvas::Item* item, AudioRegionView *rv) +Editor::canvas_end_xfade_event (GdkEvent *event, ArdourCanvas::Item* item, AudioRegionView *rv) { - bool ret = false; - if (!rv->sensitive()) { return false; } switch (event->type) { case GDK_BUTTON_PRESS: - case GDK_2BUTTON_PRESS: - case GDK_3BUTTON_PRESS: clicked_regionview = rv; clicked_control_point = 0; clicked_axisview = &rv->get_time_axis_view(); clicked_routeview = dynamic_cast(clicked_axisview); - ret = button_press_handler (item, event, FadeInHandleItem); + if (event->button.button == 3) { + return button_press_handler (item, event, EndCrossFadeItem); + } break; case GDK_BUTTON_RELEASE: - ret = button_release_handler (item, event, FadeInHandleItem); - break; - - case GDK_MOTION_NOTIFY: - ret = motion_handler (item, event, FadeInHandleItem); - break; - - case GDK_ENTER_NOTIFY: - ret = enter_handler (item, event, FadeInHandleItem); - break; - - case GDK_LEAVE_NOTIFY: - ret = leave_handler (item, event, FadeInHandleItem); + if (event->button.button == 3) { + return button_release_handler (item, event, EndCrossFadeItem); + } break; default: break; + } - return ret; + /* In Mixbus, the crossfade area is used to trim the region while leaving the fade anchor intact (see preserve_fade_anchor)*/ + /* however in A3 this feature is unfinished, and it might be better to do it with a modifier-trim instead, anyway */ + /* if we return RegionItem here then we avoid the issue until it is resolved later */ + return typed_event (item, event, RegionItem); // EndCrossFadeItem); } bool -Editor::canvas_fade_out_event (GdkEvent *event, ArdourCanvas::Item* item, AudioRegionView *rv) +Editor::canvas_fade_in_event (GdkEvent *event, ArdourCanvas::Item* item, AudioRegionView *rv) { /* we handle only button 3 press/release events */ @@ -473,31 +500,35 @@ Editor::canvas_fade_out_event (GdkEvent *event, ArdourCanvas::Item* item, AudioR clicked_axisview = &rv->get_time_axis_view(); clicked_routeview = dynamic_cast(clicked_axisview); if (event->button.button == 3) { - return button_press_handler (item, event, FadeOutItem); + return button_press_handler (item, event, FadeInItem); } break; case GDK_BUTTON_RELEASE: if (event->button.button == 3) { - return button_release_handler (item, event, FadeOutItem); + return button_release_handler (item, event, FadeInItem); } break; default: break; - + } - /* proxy for the regionview */ - - return canvas_region_view_event (event, rv->get_canvas_group(), rv); + /* proxy for the regionview, except enter/leave events */ + + if (event->type == GDK_ENTER_NOTIFY || event->type == GDK_LEAVE_NOTIFY) { + return true; + } else { + return canvas_region_view_event (event, rv->get_canvas_group(), rv); + } } bool -Editor::canvas_fade_out_handle_event (GdkEvent *event, ArdourCanvas::Item* item, AudioRegionView *rv) +Editor::canvas_fade_in_handle_event (GdkEvent *event, ArdourCanvas::Item* item, AudioRegionView *rv, bool trim) { bool ret = false; - + if (!rv->sensitive()) { return false; } @@ -510,23 +541,24 @@ Editor::canvas_fade_out_handle_event (GdkEvent *event, ArdourCanvas::Item* item, clicked_control_point = 0; clicked_axisview = &rv->get_time_axis_view(); clicked_routeview = dynamic_cast(clicked_axisview); - ret = button_press_handler (item, event, FadeOutHandleItem); + ret = button_press_handler (item, event, trim ? FadeInTrimHandleItem : FadeInHandleItem); break; case GDK_BUTTON_RELEASE: - ret = button_release_handler (item, event, FadeOutHandleItem); + ret = button_release_handler (item, event, trim ? FadeInTrimHandleItem : FadeInHandleItem); + maybe_locate_with_edit_preroll ( rv->region()->position() ); break; case GDK_MOTION_NOTIFY: - ret = motion_handler (item, event, FadeOutHandleItem); + ret = motion_handler (item, event); break; case GDK_ENTER_NOTIFY: - ret = enter_handler (item, event, FadeOutHandleItem); + ret = enter_handler (item, event, trim ? FadeInTrimHandleItem : FadeInHandleItem); break; case GDK_LEAVE_NOTIFY: - ret = leave_handler (item, event, FadeOutHandleItem); + ret = leave_handler (item, event, trim ? FadeInTrimHandleItem : FadeInHandleItem); break; default: @@ -536,83 +568,96 @@ Editor::canvas_fade_out_handle_event (GdkEvent *event, ArdourCanvas::Item* item, return ret; } -struct DescendingRegionLayerSorter { - bool operator()(boost::shared_ptr a, boost::shared_ptr b) { - return a->layer() > b->layer(); - } -}; - bool -Editor::canvas_crossfade_view_event (GdkEvent* event, ArdourCanvas::Item* item, CrossfadeView* xfv) +Editor::canvas_fade_out_event (GdkEvent *event, ArdourCanvas::Item* item, AudioRegionView *rv) { /* we handle only button 3 press/release events */ + if (!rv->sensitive()) { + return false; + } + switch (event->type) { case GDK_BUTTON_PRESS: - clicked_crossfadeview = xfv; - clicked_axisview = &clicked_crossfadeview->get_time_axis_view(); + clicked_regionview = rv; + clicked_control_point = 0; + clicked_axisview = &rv->get_time_axis_view(); + clicked_routeview = dynamic_cast(clicked_axisview); if (event->button.button == 3) { - return button_press_handler (item, event, CrossfadeViewItem); - } + return button_press_handler (item, event, FadeOutItem); + } break; case GDK_BUTTON_RELEASE: if (event->button.button == 3) { - bool ret = button_release_handler (item, event, CrossfadeViewItem); - return ret; + return button_release_handler (item, event, FadeOutItem); } break; default: break; - - } - - /* XXX do not forward double clicks */ - if (event->type == GDK_2BUTTON_PRESS) { - return false; } - - /* proxy for the upper most regionview */ - /* XXX really need to check if we are in the name highlight, - and proxy to that when required. - */ - - TimeAxisView& tv (xfv->get_time_axis_view()); - AudioTimeAxisView* atv; + /* proxy for the regionview, except enter/leave events */ - if ((atv = dynamic_cast(&tv)) != 0) { + if (event->type == GDK_ENTER_NOTIFY || event->type == GDK_LEAVE_NOTIFY) { + return true; + } else { + return canvas_region_view_event (event, rv->get_canvas_group(), rv); + } +} - if (atv->is_audio_track()) { +bool +Editor::canvas_fade_out_handle_event (GdkEvent *event, ArdourCanvas::Item* item, AudioRegionView *rv, bool trim) +{ + bool ret = false; - boost::shared_ptr pl; - if ((pl = boost::dynamic_pointer_cast (atv->get_diskstream()->playlist())) != 0) { + if (!rv->sensitive()) { + return false; + } - Playlist::RegionList* rl = pl->regions_at (event_frame (event)); + switch (event->type) { + case GDK_BUTTON_PRESS: + case GDK_2BUTTON_PRESS: + case GDK_3BUTTON_PRESS: + clicked_regionview = rv; + clicked_control_point = 0; + clicked_axisview = &rv->get_time_axis_view(); + clicked_routeview = dynamic_cast(clicked_axisview); + ret = button_press_handler (item, event, trim ? FadeOutTrimHandleItem : FadeOutHandleItem); + break; - if (!rl->empty()) { - DescendingRegionLayerSorter cmp; - rl->sort (cmp); + case GDK_BUTTON_RELEASE: + ret = button_release_handler (item, event, trim ? FadeOutTrimHandleItem : FadeOutHandleItem); + maybe_locate_with_edit_preroll ( rv->region()->last_frame() - rv->get_fade_out_shape_width() ); + break; - RegionView* rv = atv->view()->find_view (rl->front()); + case GDK_MOTION_NOTIFY: + ret = motion_handler (item, event); + break; - delete rl; + case GDK_ENTER_NOTIFY: + ret = enter_handler (item, event, trim ? FadeOutTrimHandleItem : FadeOutHandleItem); + break; - /* proxy */ - - return canvas_region_view_event (event, rv->get_canvas_group(), rv); - } + case GDK_LEAVE_NOTIFY: + ret = leave_handler (item, event, trim ? FadeOutTrimHandleItem : FadeOutHandleItem); + break; - delete rl; - } - } + default: + break; } - return TRUE; + return ret; } +struct DescendingRegionLayerSorter { + bool operator()(boost::shared_ptr a, boost::shared_ptr b) { + return a->layer() > b->layer(); + } +}; + bool Editor::canvas_control_point_event (GdkEvent *event, ArdourCanvas::Item* item, ControlPoint* cp) { @@ -650,6 +695,11 @@ Editor::canvas_line_event (GdkEvent *event, ArdourCanvas::Item* item, Automation type = AutomationLineItem; } + clicked_control_point = 0; + clicked_axisview = &al->trackview; + clicked_routeview = dynamic_cast(clicked_axisview); + clicked_regionview = 0; + return typed_event (item, event, type); } @@ -657,7 +707,7 @@ bool Editor::canvas_selection_rect_event (GdkEvent *event, ArdourCanvas::Item* item, SelectionRect* rect) { bool ret = false; - + switch (event->type) { case GDK_BUTTON_PRESS: case GDK_2BUTTON_PRESS: @@ -669,7 +719,7 @@ Editor::canvas_selection_rect_event (GdkEvent *event, ArdourCanvas::Item* item, ret = button_release_handler (item, event, SelectionItem); break; case GDK_MOTION_NOTIFY: - ret = motion_handler (item, event, SelectionItem); + ret = motion_handler (item, event); break; /* Don't need these at the moment. */ case GDK_ENTER_NOTIFY: @@ -683,7 +733,7 @@ Editor::canvas_selection_rect_event (GdkEvent *event, ArdourCanvas::Item* item, default: break; } - + return ret; } @@ -703,7 +753,7 @@ Editor::canvas_selection_start_trim_event (GdkEvent *event, ArdourCanvas::Item* ret = button_release_handler (item, event, StartSelectionTrimItem); break; case GDK_MOTION_NOTIFY: - ret = motion_handler (item, event, StartSelectionTrimItem); + ret = motion_handler (item, event); break; case GDK_ENTER_NOTIFY: ret = enter_handler (item, event, StartSelectionTrimItem); @@ -716,7 +766,7 @@ Editor::canvas_selection_start_trim_event (GdkEvent *event, ArdourCanvas::Item* default: break; } - + return ret; } @@ -736,7 +786,7 @@ Editor::canvas_selection_end_trim_event (GdkEvent *event, ArdourCanvas::Item* it ret = button_release_handler (item, event, EndSelectionTrimItem); break; case GDK_MOTION_NOTIFY: - ret = motion_handler (item, event, EndSelectionTrimItem); + ret = motion_handler (item, event); break; case GDK_ENTER_NOTIFY: ret = enter_handler (item, event, EndSelectionTrimItem); @@ -749,7 +799,63 @@ Editor::canvas_selection_end_trim_event (GdkEvent *event, ArdourCanvas::Item* it default: break; } - + + return ret; +} + +bool +Editor::canvas_frame_handle_event (GdkEvent* event, ArdourCanvas::Item* item, RegionView* rv) +{ + bool ret = false; + + /* frame handles are not active when in internal edit mode, because actual notes + might be in the area occupied by the handle - we want them to be editable as normal. + */ + + if (internal_editing() || !rv->sensitive()) { + return false; + } + + /* NOTE: frame handles pretend to be the colored trim bar from an event handling + perspective. XXX change this ?? + */ + + ItemType type; + + if (item->get_data ("isleft")) { + type = LeftFrameHandle; + } else { + type = RightFrameHandle; + } + + switch (event->type) { + case GDK_BUTTON_PRESS: + case GDK_2BUTTON_PRESS: + case GDK_3BUTTON_PRESS: + clicked_regionview = rv; + clicked_control_point = 0; + clicked_axisview = &clicked_regionview->get_time_axis_view(); + clicked_routeview = dynamic_cast(clicked_axisview); + ret = button_press_handler (item, event, type); + break; + case GDK_BUTTON_RELEASE: + ret = button_release_handler (item, event, type); + break; + case GDK_MOTION_NOTIFY: + ret = motion_handler (item, event); + break; + case GDK_ENTER_NOTIFY: + ret = enter_handler (item, event, type); + break; + + case GDK_LEAVE_NOTIFY: + ret = leave_handler (item, event, type); + break; + + default: + break; + } + return ret; } @@ -758,7 +864,7 @@ bool Editor::canvas_region_view_name_highlight_event (GdkEvent* event, ArdourCanvas::Item* item, RegionView* rv) { bool ret = false; - + if (!rv->sensitive()) { return false; } @@ -777,7 +883,8 @@ Editor::canvas_region_view_name_highlight_event (GdkEvent* event, ArdourCanvas:: ret = button_release_handler (item, event, RegionViewNameHighlight); break; case GDK_MOTION_NOTIFY: - ret = motion_handler (item, event, RegionViewNameHighlight); + motion_handler (item, event); + ret = true; // force this to avoid progagating the event into the regionview break; case GDK_ENTER_NOTIFY: ret = enter_handler (item, event, RegionViewNameHighlight); @@ -817,7 +924,7 @@ Editor::canvas_region_view_name_event (GdkEvent *event, ArdourCanvas::Item* item ret = button_release_handler (item, event, RegionViewName); break; case GDK_MOTION_NOTIFY: - ret = motion_handler (item, event, RegionViewName); + ret = motion_handler (item, event); break; case GDK_ENTER_NOTIFY: ret = enter_handler (item, event, RegionViewName); @@ -835,7 +942,46 @@ Editor::canvas_region_view_name_event (GdkEvent *event, ArdourCanvas::Item* item } bool -Editor::canvas_marker_event (GdkEvent *event, ArdourCanvas::Item* item, Marker* marker) +Editor::canvas_feature_line_event (GdkEvent *event, ArdourCanvas::Item* item, RegionView*) +{ + bool ret = false; + + switch (event->type) { + case GDK_BUTTON_PRESS: + case GDK_2BUTTON_PRESS: + case GDK_3BUTTON_PRESS: + clicked_regionview = 0; + clicked_control_point = 0; + clicked_axisview = 0; + clicked_routeview = 0; //dynamic_cast(clicked_axisview); + ret = button_press_handler (item, event, FeatureLineItem); + break; + + case GDK_BUTTON_RELEASE: + ret = button_release_handler (item, event, FeatureLineItem); + break; + + case GDK_MOTION_NOTIFY: + ret = motion_handler (item, event); + break; + + case GDK_ENTER_NOTIFY: + ret = enter_handler (item, event, FeatureLineItem); + break; + + case GDK_LEAVE_NOTIFY: + ret = leave_handler (item, event, FeatureLineItem); + break; + + default: + break; + } + + return ret; +} + +bool +Editor::canvas_marker_event (GdkEvent *event, ArdourCanvas::Item* item, ArdourMarker* /*marker*/) { return typed_event (item, event, MarkerItem); } @@ -865,17 +1011,93 @@ Editor::canvas_cd_marker_bar_event (GdkEvent *event, ArdourCanvas::Item* item) } bool -Editor::canvas_tempo_marker_event (GdkEvent *event, ArdourCanvas::Item* item, TempoMarker* marker) +Editor::canvas_videotl_bar_event (GdkEvent *event, ArdourCanvas::Item* item) +{ + return typed_event (item, event, VideoBarItem); +} + +bool +Editor::canvas_tempo_marker_event (GdkEvent *event, ArdourCanvas::Item* item, TempoMarker* /*marker*/) { return typed_event (item, event, TempoMarkerItem); } bool -Editor::canvas_meter_marker_event (GdkEvent *event, ArdourCanvas::Item* item, MeterMarker* marker) +Editor::canvas_meter_marker_event (GdkEvent *event, ArdourCanvas::Item* item, MeterMarker* /*marker*/) { return typed_event (item, event, MeterMarkerItem); } +bool +Editor::canvas_ruler_event (GdkEvent *event, ArdourCanvas::Item* item, ItemType type) +{ + framepos_t xdelta; + bool handled = false; + + if (event->type == GDK_SCROLL) { + + /* scroll events in the rulers are handled a little differently from + scrolling elsewhere in the canvas. + */ + + switch (event->scroll.direction) { + case GDK_SCROLL_UP: + + if (Profile->get_mixbus()) { + //for mouse-wheel zoom, force zoom-focus to mouse + Editing::ZoomFocus temp_focus = zoom_focus; + zoom_focus = Editing::ZoomFocusMouse; + temporal_zoom_step (false); + zoom_focus = temp_focus; + } else { + temporal_zoom_step (false); + } + handled = true; + break; + + case GDK_SCROLL_DOWN: + if (Profile->get_mixbus()) { + //for mouse-wheel zoom, force zoom-focus to mouse + Editing::ZoomFocus temp_focus = zoom_focus; + zoom_focus = Editing::ZoomFocusMouse; + temporal_zoom_step (true); + zoom_focus = temp_focus; + } else { + temporal_zoom_step (true); + } + handled = true; + break; + + case GDK_SCROLL_LEFT: + xdelta = (current_page_samples() / 2); + if (leftmost_frame > xdelta) { + reset_x_origin (leftmost_frame - xdelta); + } else { + reset_x_origin (0); + } + handled = true; + break; + + case GDK_SCROLL_RIGHT: + xdelta = (current_page_samples() / 2); + if (max_framepos - xdelta > leftmost_frame) { + reset_x_origin (leftmost_frame + xdelta); + } else { + reset_x_origin (max_framepos - current_page_samples()); + } + handled = true; + break; + + default: + /* what? */ + break; + } + return handled; + } + + return typed_event (item, event, type); +} + bool Editor::canvas_tempo_bar_event (GdkEvent *event, ArdourCanvas::Item* item) { @@ -895,8 +1117,252 @@ Editor::canvas_playhead_cursor_event (GdkEvent *event, ArdourCanvas::Item* item) } bool -Editor::canvas_zoom_rect_event (GdkEvent *event, ArdourCanvas::Item* item) +Editor::canvas_note_event (GdkEvent *event, ArdourCanvas::Item* item) { - return typed_event (item, event, NoItem); + if (!internal_editing()) { + return false; + } + + return typed_event (item, event, NoteItem); +} + +bool +Editor::canvas_drop_zone_event (GdkEvent* event) +{ + GdkEventScroll scroll; + ArdourCanvas::Duple winpos; + + switch (event->type) { + case GDK_BUTTON_RELEASE: + if (event->button.button == 1) { + begin_reversible_selection_op (X_("Nowhere Click")); + selection->clear_objects (); + selection->clear_tracks (); + commit_reversible_selection_op (); + } + break; + + case GDK_SCROLL: + /* convert coordinates back into window space so that + we can just call canvas_scroll_event(). + */ + winpos = _track_canvas->canvas_to_window (Duple (event->scroll.x, event->scroll.y)); + scroll = event->scroll; + scroll.x = winpos.x; + scroll.y = winpos.y; + return canvas_scroll_event (&scroll, true); + break; + + case GDK_ENTER_NOTIFY: + return typed_event (_canvas_drop_zone, event, DropZoneItem); + + case GDK_LEAVE_NOTIFY: + return typed_event (_canvas_drop_zone, event, DropZoneItem); + + default: + break; + } + + return true; +} + +bool +Editor::track_canvas_drag_motion (Glib::RefPtr const& context, int x, int y, guint time) +{ + boost::shared_ptr region; + boost::shared_ptr region_copy; + RouteTimeAxisView* rtav; + GdkEvent event; + double px; + double py; + + string target = _track_canvas->drag_dest_find_target (context, _track_canvas->drag_dest_get_target_list()); + + if (target.empty()) { + return false; + } + + event.type = GDK_MOTION_NOTIFY; + event.button.x = x; + event.button.y = y; + /* assume we're dragging with button 1 */ + event.motion.state = Gdk::BUTTON1_MASK; + + (void) window_event_sample (&event, &px, &py); + + std::pair const tv = trackview_by_y_position (py, false); + bool can_drop = false; + + if (tv.first != 0) { + + /* over a time axis view of some kind */ + + rtav = dynamic_cast (tv.first); + + if (rtav != 0 && rtav->is_track ()) { + /* over a track, not a bus */ + can_drop = true; + } + + + } else { + /* not over a time axis view, so drop is possible */ + can_drop = true; + } + + if (can_drop) { + region = _regions->get_dragged_region (); + + if (region) { + + if (tv.first == 0 + && ( + boost::dynamic_pointer_cast (region) != 0 || + boost::dynamic_pointer_cast (region) != 0 + ) + ) + { + /* drop to drop-zone */ + context->drag_status (context->get_suggested_action(), time); + return true; + } + + if ((boost::dynamic_pointer_cast (region) != 0 && + dynamic_cast (tv.first) != 0) || + (boost::dynamic_pointer_cast (region) != 0 && + dynamic_cast (tv.first) != 0)) { + + /* audio to audio + OR + midi to midi + */ + + context->drag_status (context->get_suggested_action(), time); + return true; + } + } else { + /* DND originating from outside ardour + * + * TODO: check if file is audio/midi, allow drops on same track-type only, + * currently: if audio is dropped on a midi-track, it is only added to the region-list + */ + if (Profile->get_sae() || ARDOUR_UI::config()->get_only_copy_imported_files()) { + context->drag_status(Gdk::ACTION_COPY, time); + } else { + if ((context->get_actions() & (Gdk::ACTION_COPY | Gdk::ACTION_LINK | Gdk::ACTION_MOVE)) == Gdk::ACTION_COPY) { + context->drag_status(Gdk::ACTION_COPY, time); + } else { + context->drag_status(Gdk::ACTION_LINK, time); + } + } + return true; + } + } + + /* no drop here */ + context->drag_status (Gdk::DragAction (0), time); + return false; +} + +void +Editor::drop_regions (const Glib::RefPtr& /*context*/, + int x, int y, + const SelectionData& /*data*/, + guint /*info*/, guint /*time*/) +{ + GdkEvent event; + double px; + double py; + + event.type = GDK_MOTION_NOTIFY; + event.button.x = x; + event.button.y = y; + /* assume we're dragging with button 1 */ + event.motion.state = Gdk::BUTTON1_MASK; + framepos_t const pos = window_event_sample (&event, &px, &py); + + boost::shared_ptr region = _regions->get_dragged_region (); + if (!region) { return; } + + RouteTimeAxisView* rtav = 0; + std::pair const tv = trackview_by_y_position (py, false); + + if (tv.first != 0) { + rtav = dynamic_cast (tv.first); + } else { + try { + if (boost::dynamic_pointer_cast (region)) { + uint32_t output_chan = region->n_channels(); + if ((Config->get_output_auto_connect() & AutoConnectMaster) && session()->master_out()) { + output_chan = session()->master_out()->n_inputs().n_audio(); + } + list > audio_tracks; + audio_tracks = session()->new_audio_track (region->n_channels(), output_chan, ARDOUR::Normal, 0, 1, region->name()); + rtav = axis_view_from_route (audio_tracks.front()); + } else if (boost::dynamic_pointer_cast (region)) { + ChanCount one_midi_port (DataType::MIDI, 1); + list > midi_tracks; + midi_tracks = session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr(), ARDOUR::Normal, 0, 1, region->name()); + rtav = axis_view_from_route (midi_tracks.front()); + } else { + return; + } + } catch (...) { + error << _("Could not create new track after region placed in the drop zone") << endmsg; + return; + } + } + + if (rtav != 0 && rtav->is_track ()) { + boost::shared_ptr region_copy = RegionFactory::create (region, true); + + if ((boost::dynamic_pointer_cast (region_copy) != 0 && dynamic_cast (rtav) != 0) || + (boost::dynamic_pointer_cast (region_copy) != 0 && dynamic_cast (rtav) != 0)) { + _drags->set (new RegionInsertDrag (this, region_copy, rtav, pos), &event); + _drags->end_grab (0); + } + } +} + +bool +Editor::key_press_handler (ArdourCanvas::Item*, GdkEvent*, ItemType) +{ + return false; +} + +bool +Editor::key_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType type) +{ + + bool handled = false; + + switch (type) { + case TempoMarkerItem: + switch (event->key.keyval) { + case GDK_Delete: + remove_tempo_marker (item); + handled = true; + break; + default: + break; + } + break; + + case MeterMarkerItem: + switch (event->key.keyval) { + case GDK_Delete: + remove_meter_marker (item); + handled = true; + break; + default: + break; + } + break; + + default: + break; + } + + return handled; }