X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Feditor_mouse.cc;h=b3343c42c33fd8dc79dd312aac96b9b3d04c0309;hb=3a3780249ad4c4e98da317fd18a40890c0cf78b9;hp=1a9e1161c283d3c16367349f5a2c4e4e06ef0644;hpb=8883da6da02d5ebbdb272b41b960408bb1bb8a33;p=ardour.git diff --git a/gtk2_ardour/editor_mouse.cc b/gtk2_ardour/editor_mouse.cc index 1a9e1161c2..b3343c42c3 100644 --- a/gtk2_ardour/editor_mouse.cc +++ b/gtk2_ardour/editor_mouse.cc @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -68,8 +69,46 @@ using namespace sigc; using namespace Gtk; using namespace Editing; -nframes_t -Editor::event_frame (GdkEvent* event, double* pcx, double* pcy) +bool +Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const +{ + int x, y; + double wx, wy; + Gdk::ModifierType mask; + Glib::RefPtr canvas_window = const_cast(this)->track_canvas->get_window(); + Glib::RefPtr pointer_window; + + pointer_window = canvas_window->get_pointer (x, y, mask); + + if (pointer_window == track_canvas->get_bin_window()) { + + track_canvas->window_to_world (x, y, wx, wy); + in_track_canvas = true; + + } else { + in_track_canvas = false; + + if (pointer_window == time_canvas->get_bin_window()) { + time_canvas->window_to_world (x, y, wx, wy); + } else { + return false; + } + } + + wx += horizontal_adjustment.get_value(); + wy += vertical_adjustment.get_value(); + + GdkEvent event; + event.type = GDK_BUTTON_RELEASE; + event.button.x = wx; + event.button.y = wy; + + where = event_frame (&event, 0, 0); + return true; +} + +nframes64_t +Editor::event_frame (GdkEvent* event, double* pcx, double* pcy) const { double cx, cy; @@ -88,18 +127,18 @@ Editor::event_frame (GdkEvent* event, double* pcx, double* pcy) case GDK_BUTTON_PRESS: case GDK_2BUTTON_PRESS: case GDK_3BUTTON_PRESS: - track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy); + track_canvas->w2c(event->button.x, event->button.y, *pcx, *pcy); break; case GDK_MOTION_NOTIFY: - track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy); + track_canvas->w2c(event->motion.x, event->motion.y, *pcx, *pcy); break; case GDK_ENTER_NOTIFY: case GDK_LEAVE_NOTIFY: - track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy); + track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy); break; case GDK_KEY_PRESS: case GDK_KEY_RELEASE: - // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy); + // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy); break; default: warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg; @@ -163,6 +202,53 @@ Editor::mouse_mode_toggled (MouseMode m) } } +Gdk::Cursor* +Editor::which_grabber_cursor () +{ + switch (_edit_point) { + case EditAtMouse: + return grabber_edit_point_cursor; + break; + default: + return grabber_cursor; + break; + } +} + +void +Editor::set_canvas_cursor () +{ + switch (mouse_mode) { + case MouseRange: + current_canvas_cursor = selector_cursor; + break; + + case MouseObject: + current_canvas_cursor = which_grabber_cursor(); + break; + + case MouseGain: + current_canvas_cursor = cross_hair_cursor; + break; + + case MouseZoom: + current_canvas_cursor = zoom_cursor; + break; + + case MouseTimeFX: + current_canvas_cursor = time_fx_cursor; // just use playhead + break; + + case MouseAudition: + current_canvas_cursor = speaker_cursor; + break; + } + + if (is_drawable()) { + track_canvas->get_window()->set_cursor(*current_canvas_cursor); + } +} + void Editor::set_mouse_mode (MouseMode m, bool force) { @@ -204,7 +290,7 @@ Editor::set_mouse_mode (MouseMode m, bool force) } } - /* XXX the hack of unsetting all other buttongs should go + /* XXX the hack of unsetting all other buttons should go away once GTK2 allows us to use regular radio buttons drawn like normal buttons, rather than my silly GroupedButton hack. */ @@ -214,40 +300,32 @@ Editor::set_mouse_mode (MouseMode m, bool force) switch (mouse_mode) { case MouseRange: mouse_select_button.set_active (true); - current_canvas_cursor = selector_cursor; break; case MouseObject: mouse_move_button.set_active (true); - current_canvas_cursor = grabber_cursor; break; case MouseGain: mouse_gain_button.set_active (true); - current_canvas_cursor = cross_hair_cursor; break; case MouseZoom: mouse_zoom_button.set_active (true); - current_canvas_cursor = zoom_cursor; break; case MouseTimeFX: mouse_timefx_button.set_active (true); - current_canvas_cursor = time_fx_cursor; // just use playhead break; case MouseAudition: mouse_audition_button.set_active (true); - current_canvas_cursor = speaker_cursor; break; } ignore_mouse_mode_toggle = false; - - if (is_drawable()) { - track_canvas.get_window()->set_cursor(*current_canvas_cursor); - } + + set_canvas_cursor (); } void @@ -289,8 +367,6 @@ Editor::step_mouse_mode (bool next) void Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type) { - bool commit = false; - /* in object/audition/timefx mode, any button press sets the selection if the object can be selected. this is a bit of hack, because we want to avoid this if the @@ -329,18 +405,18 @@ Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType it switch (item_type) { case RegionItem: if (mouse_mode != MouseRange) { - commit = set_selected_regionview_from_click (press, op, true); + set_selected_regionview_from_click (press, op, true); } else if (event->type == GDK_BUTTON_PRESS) { - commit = set_selected_track_from_click (press, op, false); + set_selected_track_as_side_effect (); } break; case RegionViewNameHighlight: case RegionViewName: if (mouse_mode != MouseRange) { - commit = set_selected_regionview_from_click (press, op, true); + set_selected_regionview_from_click (press, op, true); } else if (event->type == GDK_BUTTON_PRESS) { - commit = set_selected_track_from_click (press, op, false); + set_selected_track_as_side_effect (); } break; @@ -349,47 +425,45 @@ Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType it case FadeOutHandleItem: case FadeOutItem: if (mouse_mode != MouseRange) { - commit = set_selected_regionview_from_click (press, op, true); + set_selected_regionview_from_click (press, op, true); } else if (event->type == GDK_BUTTON_PRESS) { - commit = set_selected_track_from_click (press, op, false); + set_selected_track_as_side_effect (); } break; case GainAutomationControlPointItem: case PanAutomationControlPointItem: case RedirectAutomationControlPointItem: - commit = set_selected_track_from_click (press, op, true); + set_selected_track_as_side_effect (); if (mouse_mode != MouseRange) { - commit |= set_selected_control_point_from_click (op, false); + set_selected_control_point_from_click (op, false); } break; case StreamItem: /* for context click or range selection, select track */ if (event->button.button == 3) { - commit = set_selected_track_from_click (press, op, true); + set_selected_track_as_side_effect (); } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) { - commit = set_selected_track_from_click (press, op, false); + set_selected_track_as_side_effect (); } break; case AutomationTrackItem: - commit = set_selected_track_from_click (press, op, true); + set_selected_track_as_side_effect (true); break; default: break; } - -// if (commit) { -// commit_reversible_command (); -// } } +const static double ZERO_GAIN_FRACTION = gain_to_slider_position(dB_to_coefficient(0.0)); + bool Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type) { - track_canvas.grab_focus(); + track_canvas->grab_focus(); if (session && session->actively_recording()) { return true; @@ -422,14 +496,12 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp */ switch (item_type) { - case EditCursorItem: case PlayheadCursorItem: start_cursor_grab (item, event); return true; case MarkerItem: - if (Keyboard::modifier_state_equals (event->button.state, - Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) { + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) { hide_marker (item, event); } else { start_marker_grab (item, event); @@ -437,7 +509,7 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp return true; case TempoMarkerItem: - if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) { + if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) { start_tempo_marker_copy_grab (item, event); } else { start_tempo_marker_grab (item, event); @@ -445,7 +517,7 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp return true; case MeterMarkerItem: - if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) { + if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) { start_meter_marker_copy_grab (item, event); } else { start_meter_marker_grab (item, event); @@ -463,6 +535,11 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp return true; break; + case CdMarkerBarItem: + start_range_markerbar_op (item, event, CreateCDMarker); + return true; + break; + case TransportMarkerBarItem: start_range_markerbar_op (item, event, CreateTransportMarker); return true; @@ -486,14 +563,13 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp case SelectionItem: if (Keyboard::modifier_state_contains - (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) { + (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) { // contains and not equals because I can't use alt as a modifier alone. start_selection_grab (item, event); - } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) { + } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) { /* grab selection for moving */ start_selection_op (item, event, SelectionMove); - } - else { + } else { /* this was debated, but decided the more common action was to make a new selection */ start_selection_op (item, event, CreateSelection); @@ -507,7 +583,7 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp break; case MouseObject: - if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt)) && + if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) && event->type == GDK_BUTTON_PRESS) { start_rubberband_select (item, event); @@ -524,7 +600,7 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp return true; case RegionItem: - if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) { + if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) { start_region_copy_grab (item, event); } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) { start_region_brush_grab (item, event); @@ -669,7 +745,13 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp break; case MouseAudition: - /* handled in release */ + _scrubbing = true; + scrub_reversals = 0; + scrub_reverse_distance = 0; + last_scrub_x = event->button.x; + scrubbing_direction = 0; + track_canvas->get_window()->set_cursor (*transparent_cursor); + /* rest handled in motion & release */ break; default: @@ -683,13 +765,14 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp if (event->type == GDK_BUTTON_PRESS) { switch (item_type) { case RegionItem: - if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) { + if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) { start_region_copy_grab (item, event); } else { start_region_grab (item, event); } - + return true; break; + case GainAutomationControlPointItem: case PanAutomationControlPointItem: case RedirectAutomationControlPointItem: @@ -729,7 +812,7 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp case MouseZoom: - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) { + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) { temporal_zoom_session(); } else { temporal_zoom_to_frame (true, event_frame(event)); @@ -758,6 +841,7 @@ bool Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type) { nframes_t where = event_frame (event, 0, 0); + AutomationTimeAxisView* atv = 0; /* no action if we're recording */ @@ -842,7 +926,8 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT case MarkerBarItem: case RangeMarkerBarItem: - case TransportMarkerBarItem: + case TransportMarkerBarItem: + case CdMarkerBarItem: case TempoBarItem: case MeterBarItem: popup_ruler_menu (pixel_to_frame(event->button.x), item_type); @@ -934,7 +1019,6 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT switch (item_type) { /* see comments in button_press_handler */ - case EditCursorItem: case PlayheadCursorItem: case MarkerItem: case GainLineItem: @@ -952,6 +1036,14 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT mouse_add_new_marker (where); return true; + case CdMarkerBarItem: + // if we get here then a dragged range wasn't done + if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { + snap_to (where, 0, true); + } + mouse_add_new_marker (where, true); + return true; + case TempoBarItem: if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { snap_to (where); @@ -972,14 +1064,14 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT case MouseObject: switch (item_type) { case AutomationTrackItem: - dynamic_cast(clicked_trackview)->add_automation_event - (item, - event, - where, - event->button.y); + atv = dynamic_cast(clicked_trackview); + if (atv) { + atv->add_automation_event (item, event, where, event->button.y); + } return true; + break; - + default: break; } @@ -1009,12 +1101,20 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT break; case MouseAudition: - switch (item_type) { - case RegionItem: - audition_selected_region (); - break; - default: - break; + _scrubbing = false; + track_canvas->get_window()->set_cursor (*current_canvas_cursor); + if (scrubbing_direction == 0) { + /* no drag, just a click */ + switch (item_type) { + case RegionItem: + play_selected_region (); + break; + default: + break; + } + } else { + /* make sure we stop */ + session->request_transport_speed (0.0); } break; @@ -1033,9 +1133,9 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT case MouseObject: switch (item_type) { case RegionItem: - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) { + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) { raise_region (); - } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) { + } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) { lower_region (); } else { // Button2 click is unused @@ -1077,6 +1177,11 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_ Marker * marker; double fraction; + if (last_item_entered != item) { + last_item_entered = item; + last_item_entered_n = 0; + } + switch (item_type) { case GainControlPointItem: if (mouse_mode == MouseGain) { @@ -1092,11 +1197,14 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_ fraction = 1.0 - (cp->get_y() / cp->line.height()); - set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y); - show_verbose_canvas_cursor (); + if (is_drawable() && !_scrubbing) { + track_canvas->get_window()->set_cursor (*fader_cursor); + } - if (is_drawable()) { - track_canvas.get_window()->set_cursor (*fader_cursor); + last_item_entered_n++; + set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y); + if (last_item_entered_n < 10) { + show_verbose_canvas_cursor (); } } break; @@ -1121,7 +1229,7 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_ show_verbose_canvas_cursor (); if (is_drawable()) { - track_canvas.get_window()->set_cursor (*fader_cursor); + track_canvas->get_window()->set_cursor (*fader_cursor); } } break; @@ -1130,9 +1238,9 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_ if (mouse_mode == MouseGain) { ArdourCanvas::Line *line = dynamic_cast (item); if (line) - line->property_fill_color_rgba() = color_map[cEnteredGainLine]; + line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get(); if (is_drawable()) { - track_canvas.get_window()->set_cursor (*fader_cursor); + track_canvas->get_window()->set_cursor (*fader_cursor); } } break; @@ -1144,17 +1252,17 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_ { ArdourCanvas::Line *line = dynamic_cast (item); if (line) - line->property_fill_color_rgba() = color_map[cEnteredAutomationLine]; + line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get(); } if (is_drawable()) { - track_canvas.get_window()->set_cursor (*fader_cursor); + track_canvas->get_window()->set_cursor (*fader_cursor); } } break; case RegionViewNameHighlight: if (is_drawable() && mouse_mode == MouseObject) { - track_canvas.get_window()->set_cursor (*trimmer_cursor); + track_canvas->get_window()->set_cursor (*trimmer_cursor); } break; @@ -1168,14 +1276,20 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_ /* */ if (is_drawable()) { - track_canvas.get_window()->set_cursor (*trimmer_cursor); + track_canvas->get_window()->set_cursor (*trimmer_cursor); } break; - case EditCursorItem: case PlayheadCursorItem: if (is_drawable()) { - track_canvas.get_window()->set_cursor (*grabber_cursor); + switch (_edit_point) { + case EditAtMouse: + track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor); + break; + default: + track_canvas->get_window()->set_cursor (*grabber_cursor); + break; + } } break; @@ -1185,7 +1299,7 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_ if (!reinterpret_cast (item->get_data ("regionview"))->name_active()) { if (mouse_mode == MouseObject && is_drawable()) { - track_canvas.get_window()->set_cursor (*trimmer_cursor); + track_canvas->get_window()->set_cursor (*trimmer_cursor); } } break; @@ -1206,7 +1320,7 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_ break; } - track_canvas.get_window()->set_cursor (*cursor); + track_canvas->get_window()->set_cursor (*cursor); AutomationTimeAxisView* atv; if ((atv = static_cast(item->get_data ("trackview"))) != 0) { @@ -1219,10 +1333,11 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_ case MarkerBarItem: case RangeMarkerBarItem: case TransportMarkerBarItem: + case CdMarkerBarItem: case MeterBarItem: case TempoBarItem: if (is_drawable()) { - time_canvas.get_window()->set_cursor (*timebar_cursor); + time_canvas->get_window()->set_cursor (*timebar_cursor); } break; @@ -1230,12 +1345,13 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_ if ((marker = static_cast (item->get_data ("marker"))) == 0) { break; } - marker->set_color_rgba (color_map[cEnteredMarker]); + entered_marker = marker; + marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get()); // fall through case MeterMarkerItem: case TempoMarkerItem: if (is_drawable()) { - time_canvas.get_window()->set_cursor (*timebar_cursor); + time_canvas->get_window()->set_cursor (*timebar_cursor); } break; case FadeInHandleItem: @@ -1304,7 +1420,7 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_ } if (is_drawable()) { - track_canvas.get_window()->set_cursor (*current_canvas_cursor); + track_canvas->get_window()->set_cursor (*current_canvas_cursor); } hide_verbose_canvas_cursor (); @@ -1313,7 +1429,6 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_ case RegionViewNameHighlight: case StartSelectionTrimItem: case EndSelectionTrimItem: - case EditCursorItem: case PlayheadCursorItem: /* */ case ImageFrameHandleStartItem: @@ -1322,7 +1437,7 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_ case MarkerViewHandleEndItem: /* */ if (is_drawable()) { - track_canvas.get_window()->set_cursor (*current_canvas_cursor); + track_canvas->get_window()->set_cursor (*current_canvas_cursor); } break; @@ -1337,7 +1452,7 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_ line->property_fill_color_rgba() = al->get_line_color(); } if (is_drawable()) { - track_canvas.get_window()->set_cursor (*current_canvas_cursor); + track_canvas->get_window()->set_cursor (*current_canvas_cursor); } break; @@ -1345,18 +1460,19 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_ /* see enter_handler() for notes */ if (!reinterpret_cast (item->get_data ("regionview"))->name_active()) { if (is_drawable() && mouse_mode == MouseObject) { - track_canvas.get_window()->set_cursor (*current_canvas_cursor); + track_canvas->get_window()->set_cursor (*current_canvas_cursor); } } break; case RangeMarkerBarItem: case TransportMarkerBarItem: + case CdMarkerBarItem: case MeterBarItem: case TempoBarItem: case MarkerBarItem: if (is_drawable()) { - time_canvas.get_window()->set_cursor (*timebar_cursor); + time_canvas->get_window()->set_cursor (*timebar_cursor); } break; @@ -1364,14 +1480,16 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_ if ((marker = static_cast (item->get_data ("marker"))) == 0) { break; } - loc = find_location_from_marker (marker, is_start); - if (loc) location_flags_changed (loc, this); + entered_marker = 0; + if ((loc = find_location_from_marker (marker, is_start)) != 0) { + location_flags_changed (loc, this); + } // fall through case MeterMarkerItem: case TempoMarkerItem: if (is_drawable()) { - time_canvas.get_window()->set_cursor (*timebar_cursor); + time_canvas->get_window()->set_cursor (*timebar_cursor); } break; @@ -1390,7 +1508,7 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_ case AutomationTrackItem: if (is_drawable()) { - track_canvas.get_window()->set_cursor (*current_canvas_cursor); + track_canvas->get_window()->set_cursor (*current_canvas_cursor); clear_entered_track = true; Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track)); } @@ -1413,21 +1531,102 @@ Editor::left_automation_track () return false; } -bool -Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll) +void +Editor::scrub () { - gint x, y; + double delta; - /* We call this so that MOTION_NOTIFY events continue to be - delivered to the canvas. We need to do this because we set - Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces - the density of the events, at the expense of a round-trip - to the server. Given that this will mostly occur on cases - where DISPLAY = :0.0, and given the cost of what the motion - event might do, its a good tradeoff. - */ + if (scrubbing_direction == 0) { + /* first move */ + session->request_locate (drag_info.current_pointer_frame, false); + session->request_transport_speed (0.1); + scrubbing_direction = 1; + + } else { + + if (last_scrub_x > drag_info.current_pointer_x) { + + /* pointer moved to the left */ + + if (scrubbing_direction > 0) { + + /* we reversed direction to go backwards */ + + scrub_reversals++; + scrub_reverse_distance += (int) (last_scrub_x - drag_info.current_pointer_x); + + } else { + + /* still moving to the left (backwards) */ + + scrub_reversals = 0; + scrub_reverse_distance = 0; + + delta = 0.01 * (last_scrub_x - drag_info.current_pointer_x); + session->request_transport_speed (session->transport_speed() - delta); + } + + } else { + /* pointer moved to the right */ + + if (scrubbing_direction < 0) { + /* we reversed direction to go forward */ + + scrub_reversals++; + scrub_reverse_distance += (int) (drag_info.current_pointer_x - last_scrub_x); + + } else { + /* still moving to the right */ + + scrub_reversals = 0; + scrub_reverse_distance = 0; + + delta = 0.01 * (drag_info.current_pointer_x - last_scrub_x); + session->request_transport_speed (session->transport_speed() + delta); + } + } + + /* if there have been more than 2 opposite motion moves detected, or one that moves + back more than 10 pixels, reverse direction + */ + + if (scrub_reversals >= 2 || scrub_reverse_distance > 10) { + + if (scrubbing_direction > 0) { + /* was forwards, go backwards */ + session->request_transport_speed (-0.1); + scrubbing_direction = -1; + } else { + /* was backwards, go forwards */ + session->request_transport_speed (0.1); + scrubbing_direction = 1; + } + + scrub_reverse_distance = 0; + scrub_reversals = 0; + } + } + + last_scrub_x = drag_info.current_pointer_x; +} - track_canvas.get_pointer (x, y); +bool +Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll) +{ + if (event->motion.is_hint) { + gint x, y; + + /* We call this so that MOTION_NOTIFY events continue to be + delivered to the canvas. We need to do this because we set + Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces + the density of the events, at the expense of a round-trip + to the server. Given that this will mostly occur on cases + where DISPLAY = :0.0, and given the cost of what the motion + event might do, its a good tradeoff. + */ + + track_canvas->get_pointer (x, y); + } if (current_stepping_trackview) { /* don't keep the persistent stepped trackview if the mouse moves */ @@ -1441,17 +1640,31 @@ Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item } drag_info.item_type = item_type; + drag_info.last_pointer_x = drag_info.current_pointer_x; + drag_info.last_pointer_y = drag_info.current_pointer_y; drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x, &drag_info.current_pointer_y); + + switch (mouse_mode) { + case MouseAudition: + if (_scrubbing) { + scrub (); + } + break; + + default: + break; + } + if (!from_autoscroll && drag_info.item) { /* item != 0 is the best test i can think of for dragging. */ if (!drag_info.move_threshold_passed) { - bool x_threshold_passed = (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4); - bool y_threshold_passed = (abs ((int) (drag_info.current_pointer_y - drag_info.grab_y)) > 4); - + bool x_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL); + bool y_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL); + drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed); // and change the initial grab loc/frame if this drag info wants us to @@ -1468,7 +1681,6 @@ Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item switch (item_type) { case PlayheadCursorItem: - case EditCursorItem: case MarkerItem: case GainControlPointItem: case RedirectAutomationControlPointItem: @@ -1495,7 +1707,7 @@ Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK || (event->motion.state & Gdk::BUTTON2_MASK))) { if (!from_autoscroll) { - maybe_autoscroll (event); + maybe_autoscroll (&event->motion); } (this->*(drag_info.motion_callback)) (item, event); goto handled; @@ -1514,7 +1726,7 @@ Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK || (event->motion.state & GDK_BUTTON2_MASK))) { if (!from_autoscroll) { - maybe_autoscroll (event); + maybe_autoscroll (&event->motion); } (this->*(drag_info.motion_callback)) (item, event); goto handled; @@ -1535,6 +1747,48 @@ Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item return false; } +void +Editor::break_drag () +{ + stop_canvas_autoscroll (); + hide_verbose_canvas_cursor (); + + if (drag_info.item) { + drag_info.item->ungrab (0); + + /* put it back where it came from */ + + double cxw, cyw; + cxw = 0; + cyw = 0; + drag_info.item->i2w (cxw, cyw); + drag_info.item->move (drag_info.original_x - cxw, drag_info.original_y - cyw); + } + + finalize_drag (); +} + +void +Editor::finalize_drag () +{ + drag_info.item = 0; + drag_info.copy = false; + drag_info.motion_callback = 0; + drag_info.finished_callback = 0; + drag_info.dest_trackview = 0; + drag_info.source_trackview = 0; + drag_info.last_frame_position = 0; + drag_info.grab_frame = 0; + drag_info.last_pointer_frame = 0; + drag_info.current_pointer_frame = 0; + drag_info.brushing = false; + + if (drag_info.copied_location) { + delete drag_info.copied_location; + drag_info.copied_location = 0; + } +} + void Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor) { @@ -1545,13 +1799,13 @@ Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor) } if (cursor == 0) { - cursor = grabber_cursor; + cursor = which_grabber_cursor (); } // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained if (event->button.button == 2) { - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) { + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) { drag_info.y_constrained = true; drag_info.x_constrained = false; } else { @@ -1563,11 +1817,13 @@ Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor) drag_info.y_constrained = false; } - drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y); + drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y); drag_info.last_pointer_frame = drag_info.grab_frame; drag_info.current_pointer_frame = drag_info.grab_frame; drag_info.current_pointer_x = drag_info.grab_x; drag_info.current_pointer_y = drag_info.grab_y; + drag_info.last_pointer_x = drag_info.current_pointer_x; + drag_info.last_pointer_y = drag_info.current_pointer_y; drag_info.cumulative_x_drag = 0; drag_info.cumulative_y_drag = 0; drag_info.first_move = true; @@ -1577,6 +1833,10 @@ Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor) drag_info.brushing = false; drag_info.copied_location = 0; + drag_info.original_x = 0; + drag_info.original_y = 0; + drag_info.item->i2w (drag_info.original_x, drag_info.original_y); + drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, event->button.time); @@ -1606,7 +1866,7 @@ Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t t drag_info.item = new_item; if (cursor == 0) { - cursor = grabber_cursor; + cursor = which_grabber_cursor (); } drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time); @@ -1620,13 +1880,14 @@ Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event) stop_canvas_autoscroll (); if (drag_info.item == 0) { - cerr << "end grab with no item\n"; return false; } drag_info.item->ungrab (event->button.time); if (drag_info.finished_callback) { + drag_info.last_pointer_x = drag_info.current_pointer_x; + drag_info.last_pointer_y = drag_info.current_pointer_y; (this->*(drag_info.finished_callback)) (item, event); } @@ -1634,54 +1895,11 @@ Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event) hide_verbose_canvas_cursor(); - drag_info.item = 0; - drag_info.copy = false; - drag_info.motion_callback = 0; - drag_info.finished_callback = 0; - drag_info.last_trackview = 0; - drag_info.last_frame_position = 0; - drag_info.grab_frame = 0; - drag_info.last_pointer_frame = 0; - drag_info.current_pointer_frame = 0; - drag_info.brushing = false; - - if (drag_info.copied_location) { - delete drag_info.copied_location; - drag_info.copied_location = 0; - } + finalize_drag (); return did_drag; } -void -Editor::set_edit_cursor (GdkEvent* event) -{ - nframes_t pointer_frame = event_frame (event); - - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { - if (snap_type != SnapToEditCursor) { - snap_to (pointer_frame); - } - } - - edit_cursor->set_position (pointer_frame); - edit_cursor_clock.set (pointer_frame); -} - -void -Editor::set_playhead_cursor (GdkEvent* event) -{ - nframes_t pointer_frame = event_frame (event); - - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { - snap_to (pointer_frame); - } - - if (session) { - session->request_locate (pointer_frame, session->transport_rolling()); - } -} - void Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event) { @@ -1708,7 +1926,7 @@ Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) nframes_t pos; nframes_t fade_length; - if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { + if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset; } else { @@ -1753,7 +1971,7 @@ Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* even if (drag_info.first_move) return; - if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { + if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset; } else { pos = 0; @@ -1781,6 +1999,7 @@ Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* even XMLNode &before = alist.get_state(); tmp->audio_region()->set_fade_in_length (fade_length); + tmp->audio_region()->set_fade_in_active (true); XMLNode &after = alist.get_state(); session->add_command(new MementoCommand(alist, &before, &after)); @@ -1815,10 +2034,9 @@ Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event nframes_t pos; nframes_t fade_length; - if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { + if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset; - } - else { + } else { pos = 0; } @@ -1863,7 +2081,7 @@ Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* eve nframes_t pos; nframes_t fade_length; - if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { + if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset; } else { @@ -1898,6 +2116,7 @@ Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* eve XMLNode &before = alist.get_state(); tmp->audio_region()->set_fade_out_length (fade_length); + tmp->audio_region()->set_fade_out_active (true); XMLNode &after = alist.get_state(); session->add_command(new MementoCommand(alist, &before, &after)); @@ -1945,7 +2164,7 @@ Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) Cursor* cursor = (Cursor *) drag_info.data; nframes_t adjusted_frame; - if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { + if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset; } else { @@ -1953,7 +2172,7 @@ Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) } if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { - if (cursor != edit_cursor || snap_type != SnapToEditCursor) { + if (cursor == playhead_cursor) { snap_to (adjusted_frame); } } @@ -1962,11 +2181,7 @@ Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) cursor->set_position (adjusted_frame); - if (cursor == edit_cursor) { - edit_cursor_clock.set (cursor->current_frame); - } else { - UpdateAllTransportClocks (cursor->current_frame); - } + UpdateAllTransportClocks (cursor->current_frame); show_verbose_time_cursor (cursor->current_frame, 10); @@ -1987,9 +2202,6 @@ Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event if (session) { session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling); } - } else if (item == &edit_cursor->canvas_item) { - edit_cursor->set_position (edit_cursor->current_frame); - edit_cursor_clock.set (edit_cursor->current_frame); } } @@ -2031,22 +2243,43 @@ Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event) start_grab (event); + _dragging_edit_point = true; + drag_info.copied_location = new Location (*location); drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end()); update_marker_drag_item (location); if (location->is_mark()) { - marker_drag_line->show(); - marker_drag_line->raise_to_top(); - } - else { + // marker_drag_line->show(); + // marker_drag_line->raise_to_top(); + } else { range_marker_drag_rect->show(); range_marker_drag_rect->raise_to_top(); } - - if (is_start) show_verbose_time_cursor (location->start(), 10); - else show_verbose_time_cursor (location->end(), 10); + + if (is_start) { + show_verbose_time_cursor (location->start(), 10); + } else { + show_verbose_time_cursor (location->end(), 10); + } + + Selection::Operation op = Keyboard::selection_type (event->button.state); + + switch (op) { + case Selection::Toggle: + selection->toggle (marker); + break; + case Selection::Set: + selection->set (marker); + break; + case Selection::Extend: + selection->add (marker); + break; + case Selection::Add: + selection->add (marker); + break; + } } void @@ -2059,12 +2292,10 @@ Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) bool is_start; bool move_both = false; - nframes_t newframe; - if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) { + if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) { newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset; - } - else { + } else { newframe = 0; } @@ -2080,7 +2311,13 @@ Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) /* call this to find out if its the start or end */ - real_location = find_location_from_marker (marker, is_start); + if ((real_location = find_location_from_marker (marker, is_start)) == 0) { + return; + } + + if (real_location->locked()) { + return; + } /* use the copy that we're "dragging" around */ @@ -2088,7 +2325,7 @@ Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) f_delta = copy_location->end() - copy_location->start(); - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) { + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) { move_both = true; } @@ -2135,7 +2372,8 @@ Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) LocationMarkers* lm = find_location_markers (real_location); lm->set_position (copy_location->start(), copy_location->end()); - + edit_point_clock.set (copy_location->start()); + show_verbose_time_cursor (newframe, 10); } @@ -2143,20 +2381,26 @@ void Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event) { if (drag_info.first_move) { - marker_drag_motion_callback (item, event); - + /* just a click, do nothing but whatever selection occured */ + return; } + + _dragging_edit_point = false; Marker* marker = (Marker *) drag_info.data; bool is_start; - begin_reversible_command ( _("move marker") ); XMLNode &before = session->locations()->get_state(); Location * location = find_location_from_marker (marker, is_start); - + if (location) { + + if (location->locked()) { + return; + } + if (location->is_mark()) { location->set_start (drag_info.copied_location->start()); } else { @@ -2192,6 +2436,7 @@ Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event) } drag_info.item = item; + drag_info.copy = false; drag_info.data = marker; drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback; drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback; @@ -2220,7 +2465,7 @@ Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event) // The actual copying is not done before we reach the finish callback. char name[64]; snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ()); - MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name, + MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name, *new MeterSection(meter_marker->meter())); drag_info.item = &new_marker->the_item(); @@ -2242,7 +2487,7 @@ Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* e MeterMarker* marker = (MeterMarker *) drag_info.data; nframes_t adjusted_frame; - if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { + if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset; } else { @@ -2321,6 +2566,7 @@ Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event) } drag_info.item = item; + drag_info.copy = false; drag_info.data = marker; drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback; drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback; @@ -2351,7 +2597,7 @@ Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event) // The actual copying is not done before we reach the finish callback. char name[64]; snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute()); - TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name, + TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name, *new TempoSection(tempo_marker->tempo())); drag_info.item = &new_marker->the_item(); @@ -2373,7 +2619,7 @@ Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* e TempoMarker* marker = (TempoMarker *) drag_info.data; nframes_t adjusted_frame; - if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { + if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset; } else { @@ -2479,6 +2725,16 @@ Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event) start_grab (event, fader_cursor); + // start the grab at the center of the control point so + // the point doesn't 'jump' to the mouse after the first drag + drag_info.grab_x = control_point->get_x(); + drag_info.grab_y = control_point->get_y(); + control_point->line.parent_group().i2w(drag_info.grab_x, drag_info.grab_y); + track_canvas->w2c(drag_info.grab_x, drag_info.grab_y, + drag_info.grab_x, drag_info.grab_y); + + drag_info.grab_frame = pixel_to_frame(drag_info.grab_x); + control_point->line.start_drag (control_point, drag_info.grab_frame, 0); float fraction = 1.0 - (control_point->get_y() / control_point->line.height()); @@ -2493,11 +2749,28 @@ Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* { ControlPoint* cp = reinterpret_cast (drag_info.data); - double cx = drag_info.current_pointer_x; - double cy = drag_info.current_pointer_y; + double dx = drag_info.current_pointer_x - drag_info.last_pointer_x; + double dy = drag_info.current_pointer_y - drag_info.last_pointer_y; + + if (event->button.state & Keyboard::SecondaryModifier) { + dx *= 0.1; + dy *= 0.1; + } - drag_info.cumulative_x_drag = cx - drag_info.grab_x ; - drag_info.cumulative_y_drag = cy - drag_info.grab_y ; + double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx; + double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy; + + // calculate zero crossing point. back off by .01 to stay on the + // positive side of zero + double _unused = 0; + double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line.height() - .01; + cp->line.parent_group().i2w(_unused, zero_gain_y); + + // make sure we hit zero when passing through + if ((cy < zero_gain_y and (cy - dy) > zero_gain_y) + or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) { + cy = zero_gain_y; + } if (drag_info.x_constrained) { cx = drag_info.grab_x; @@ -2506,6 +2779,9 @@ Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* cy = drag_info.grab_y; } + drag_info.cumulative_x_drag = cx - drag_info.grab_x; + drag_info.cumulative_y_drag = cy - drag_info.grab_y; + cp->line.parent_group().w2i (cx, cy); cx = max (0.0, cx); @@ -2520,10 +2796,10 @@ Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* } float fraction = 1.0 - (cy / cp->line.height()); - + bool push; - if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) { + if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) { push = true; } else { push = false; @@ -2545,7 +2821,7 @@ Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent /* just a click */ - if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) { + if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) { reset_point_selection (); } @@ -2623,17 +2899,41 @@ void Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) { AutomationLine* line = reinterpret_cast (drag_info.data); + + double dy = drag_info.current_pointer_y - drag_info.last_pointer_y; + + if (event->button.state & Keyboard::SecondaryModifier) { + dy *= 0.1; + } + double cx = drag_info.current_pointer_x; - double cy = drag_info.current_pointer_y; + double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy; + + // calculate zero crossing point. back off by .01 to stay on the + // positive side of zero + double _unused = 0; + double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01; + line->parent_group().i2w(_unused, zero_gain_y); + + // make sure we hit zero when passing through + if ((cy < zero_gain_y and (cy - dy) > zero_gain_y) + or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) { + cy = zero_gain_y; + } + + drag_info.cumulative_y_drag = cy - drag_info.grab_y; line->parent_group().w2i (cx, cy); - + + cy = max (0.0, cy); + cy = min ((double) line->height(), cy); + double fraction; fraction = 1.0 - (cy / line->height()); bool push; - if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) { + if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) { push = false; } else { push = true; @@ -2662,8 +2962,14 @@ Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event) drag_info.copy = false; drag_info.item = item; drag_info.data = clicked_regionview; - drag_info.motion_callback = &Editor::region_drag_motion_callback; - drag_info.finished_callback = &Editor::region_drag_finished_callback; + + if (Config->get_edit_mode() == Splice) { + drag_info.motion_callback = &Editor::region_drag_splice_motion_callback; + drag_info.finished_callback = &Editor::region_drag_splice_finished_callback; + } else { + drag_info.motion_callback = &Editor::region_drag_motion_callback; + drag_info.finished_callback = &Editor::region_drag_finished_callback; + } start_grab (event); @@ -2677,7 +2983,8 @@ Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event) drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed); drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position; - drag_info.last_trackview = &clicked_regionview->get_time_axis_view(); + drag_info.source_trackview = &clicked_regionview->get_time_axis_view(); + drag_info.dest_trackview = drag_info.source_trackview; // we want a move threshold drag_info.want_move_threshold = true; @@ -2707,7 +3014,8 @@ Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event) speed = atv->get_diskstream()->speed(); } - drag_info.last_trackview = &clicked_regionview->get_time_axis_view(); + drag_info.source_trackview = &clicked_regionview->get_time_axis_view(); + drag_info.dest_trackview = drag_info.source_trackview; drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed); drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position; // we want a move threshold @@ -2720,7 +3028,7 @@ Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event) void Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event) { - if (selection->regions.empty() || clicked_regionview == 0) { + if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) { return; } @@ -2742,7 +3050,8 @@ Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event) drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed); drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position; - drag_info.last_trackview = &clicked_regionview->get_time_axis_view(); + drag_info.source_trackview = &clicked_regionview->get_time_axis_view(); + drag_info.dest_trackview = drag_info.source_trackview; // we want a move threshold drag_info.want_move_threshold = true; drag_info.brushing = true; @@ -2751,31 +3060,21 @@ Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event) } void -Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) +Editor::possibly_copy_regions_during_grab (GdkEvent* event) { - double x_delta; - double y_delta = 0; - RegionView* rv = reinterpret_cast (drag_info.data); - nframes_t pending_region_position = 0; - int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order; - int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen - bool clamp_y_axis = false; - vector height_list(512) ; - vector::iterator j; - if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) { drag_info.want_move_threshold = false; // don't copy again - /* duplicate the region(s) */ - + /* duplicate the regionview(s) and region(s) */ + vector new_regionviews; for (list::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) { RegionView* rv; RegionView* nrv; AudioRegionView* arv; - + rv = (*i); @@ -2783,8 +3082,12 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) /* XXX handle MIDI here */ continue; } + + const boost::shared_ptr original = arv->region(); + boost::shared_ptr region_copy = RegionFactory::create (original); + boost::shared_ptr ar = boost::dynamic_pointer_cast (region_copy); - nrv = new AudioRegionView (*arv); + nrv = new AudioRegionView (*arv, ar); nrv->get_canvas_group()->show (); new_regionviews.push_back (nrv); @@ -2794,35 +3097,140 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) return; } - /* reset selection to new regionviews */ + /* reset selection to new regionviews. This will not set selection visual status for + these regionviews since they don't belong to a track, so do that by hand too. + */ selection->set (new_regionviews); - + + for (vector::iterator i = new_regionviews.begin(); i != new_regionviews.end(); ++i) { + (*i)->set_selected (true); + } + /* reset drag_info data to reflect the fact that we are dragging the copies */ drag_info.data = new_regionviews.front(); + + swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time); + } +} + +bool +Editor::check_region_drag_possible (AudioTimeAxisView** tv) +{ + /* Which trackview is this ? */ + + TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y); + (*tv) = dynamic_cast(tvp); + + /* The region motion is only processed if the pointer is over + an audio track. + */ + + if (!(*tv) || !(*tv)->is_audio_track()) { + /* To make sure we hide the verbose canvas cursor when the mouse is + not held over and audiotrack. + */ + hide_verbose_canvas_cursor (); + return false; + } + + return true; +} + +struct RegionSelectionByPosition { + bool operator() (RegionView*a, RegionView* b) { + return a->region()->position () < b->region()->position(); + } +}; + +void +Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) +{ + AudioTimeAxisView* tv; + + if (!check_region_drag_possible (&tv)) { + return; + } + + if (!drag_info.move_threshold_passed) { + return; + } + + int dir; + + if (drag_info.current_pointer_x - drag_info.grab_x > 0) { + dir = 1; + } else { + dir = -1; + } + + RegionSelection copy (selection->regions); + + RegionSelectionByPosition cmp; + copy.sort (cmp); + + for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) { + + AudioTimeAxisView* atv = dynamic_cast (&(*i)->get_time_axis_view()); + + if (!atv) { + continue; + } + + boost::shared_ptr playlist; + + if ((playlist = atv->playlist()) == 0) { + continue; + } + + if (!playlist->region_is_shuffle_constrained ((*i)->region())) { + continue; + } + + if (dir > 0) { + if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) { + continue; + } + } else { + if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) { + continue; + } + } + - swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time); + playlist->shuffle ((*i)->region(), dir); + + drag_info.grab_x = drag_info.current_pointer_x; } +} - /* Which trackview is this ? */ +void +Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event) +{ +} - TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y); - AudioTimeAxisView* tv = dynamic_cast(tvp); +void +Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) +{ + double x_delta; + double y_delta = 0; + RegionView* rv = reinterpret_cast (drag_info.data); + nframes_t pending_region_position = 0; + int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order; + int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen + bool clamp_y_axis = false; + vector height_list(512) ; + vector::iterator j; + AudioTimeAxisView* tv; - /* The region motion is only processed if the pointer is over - an audio track. - */ - - if (!tv || !tv->is_audio_track()) { - /* To make sure we hide the verbose canvas cursor when the mouse is - not held over and audiotrack. - */ - hide_verbose_canvas_cursor (); + possibly_copy_regions_during_grab (event); + + if (!check_region_drag_possible (&tv)) { return; } - - original_pointer_order = drag_info.last_trackview->order; + + original_pointer_order = drag_info.dest_trackview->order; /************************************************************ Y-Delta Computation @@ -2833,8 +3241,8 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) pointer_y_span = 0; goto y_axis_done; } - - if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) { + + if ((pointer_y_span = (drag_info.dest_trackview->order - tv->order)) != 0) { int32_t children = 0, numtracks = 0; // XXX hard coding track limit, oh my, so very very bad @@ -2878,16 +3286,16 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) /* find the actual span according to the canvas */ canvas_pointer_y_span = pointer_y_span; - if (drag_info.last_trackview->order >= tv->order) { + if (drag_info.dest_trackview->order >= tv->order) { int32_t y; - for (y = tv->order; y < drag_info.last_trackview->order; y++) { + for (y = tv->order; y < drag_info.dest_trackview->order; y++) { if (height_list[y] == 0 ) { canvas_pointer_y_span--; } } } else { int32_t y; - for (y = drag_info.last_trackview->order;y <= tv->order; y++) { + for (y = drag_info.dest_trackview->order;y <= tv->order; y++) { if ( height_list[y] == 0 ) { canvas_pointer_y_span++; } @@ -2899,6 +3307,10 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) double ix1, ix2, iy1, iy2; int32_t n = 0; + if (rv2->region()->locked()) { + continue; + } + rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2); rv2->get_canvas_group()->i2w (ix1, iy1); TimeAxisView* tvp2 = trackview_by_y_position (iy1); @@ -2970,13 +3382,13 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) } } - } else if (drag_info.last_trackview == tv) { + } else if (drag_info.dest_trackview == tv) { clamp_y_axis = true; } y_axis_done: if (!clamp_y_axis) { - drag_info.last_trackview = tv; + drag_info.dest_trackview = tv; } /************************************************************ @@ -2987,30 +3399,34 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) the region would be if we moved it by that much. */ - if (drag_info.move_threshold_passed) { + if ( drag_info.move_threshold_passed ) { - if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { + if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { nframes_t sync_frame; nframes_t sync_offset; int32_t sync_dir; - + pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset; - + sync_offset = rv->region()->sync_offset (sync_dir); - sync_frame = rv->region()->adjust_to_sync (pending_region_position); - /* we snap if the snap modifier is not enabled. + /* we don't handle a sync point that lies before zero. */ + if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) { + sync_frame = pending_region_position + (sync_dir*sync_offset); + + /* we snap if the snap modifier is not enabled. + */ - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { - snap_to (sync_frame); - } + if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { + snap_to (sync_frame); + } - if (sync_frame - sync_offset <= sync_frame) { - pending_region_position = sync_frame - (sync_dir*sync_offset); + pending_region_position = rv->region()->adjust_to_sync (sync_frame); + } else { - pending_region_position = 0; + pending_region_position = drag_info.last_frame_position; } } else { @@ -3022,19 +3438,32 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) } // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position ); - - if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) { - /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order + bool x_move_allowed; + + if (Config->get_edit_mode() == Lock) { + if (drag_info.copy) { + x_move_allowed = !drag_info.x_constrained; + } else { + /* in locked edit mode, reverse the usual meaning of x_constrained */ + x_move_allowed = drag_info.x_constrained; + } + } else { + x_move_allowed = !drag_info.x_constrained; + } + + if ( pending_region_position != drag_info.last_frame_position && x_move_allowed ) { + + /* now compute the canvas unit distance we need to move the regionview to make it appear at the new location. */ - + if (pending_region_position > drag_info.last_frame_position) { x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit); } else { x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit); } - + drag_info.last_frame_position = pending_region_position; } else { @@ -3046,7 +3475,7 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) x_delta = 0; } - + /************************************************************* PREPARE TO MOVE ************************************************************/ @@ -3058,6 +3487,7 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) return; } + if (x_delta < 0) { for (list::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) { @@ -3096,13 +3526,17 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) pair >::iterator,bool> insert_result; const list& layered_regions = selection->regions.by_layer(); - + for (list::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) { RegionView* rv = (*i); double ix1, ix2, iy1, iy2; int32_t temp_pointer_y_span = pointer_y_span; + if (rv->region()->locked()) { + continue; + } + /* get item BBox, which will be relative to parent. so we have to query on a child, then convert to world coordinates using the parent. @@ -3140,6 +3574,7 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) y_delta -= (*j); temp_pointer_y_span--; } + while (temp_pointer_y_span < 0) { y_delta += (*j); if (x != original_pointer_order) { @@ -3170,7 +3605,8 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) x++; } } - + + /* prevent the regionview from being moved to before the zero position on the canvas. */ @@ -3180,10 +3616,11 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) if (-x_delta > ix1) { x_delta = -ix1; } - } else if ((x_delta > 0) &&(rv->region()->last_frame() > max_frames - x_delta)) { + } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) { x_delta = max_frames - rv->region()->last_frame(); } + if (drag_info.first_move) { /* hide any dependent views */ @@ -3199,10 +3636,9 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) rv->get_canvas_group()->raise_to_top(); rv->get_time_axis_view().canvas_display->raise_to_top(); cursor_group->raise_to_top(); - rv->fake_set_opaque (true); } - + if (drag_info.brushing) { mouse_brush_insert_region (rv, pending_region_position); } else { @@ -3226,14 +3662,12 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) void Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event) { - nframes_t where; - RegionView* rv = reinterpret_cast (drag_info.data); - pair >::iterator,bool> insert_result; bool nocommit = true; - double speed; - RouteTimeAxisView* atv; - bool regionview_y_movement; - bool regionview_x_movement; + vector copies; + RouteTimeAxisView* source_tv; + boost::shared_ptr ds; + boost::shared_ptr from_playlist; + vector new_selection; /* first_move is set to false if the regionview has been moved in the motion handler. @@ -3252,25 +3686,23 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event region_drag_motion_callback (item, event); - if (drag_info.brushing) { - /* all changes were made during motion event handlers */ - goto out; + if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) { + selection->set (pre_drag_region_selection); + pre_drag_region_selection.clear (); } - /* adjust for track speed */ - speed = 1.0; + if (drag_info.brushing) { + /* all changes were made during motion event handlers */ + + if (drag_info.copy) { + for (list::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { + copies.push_back (*i); + } + } - atv = dynamic_cast (drag_info.last_trackview); - if (atv && atv->get_diskstream()) { - speed = atv->get_diskstream()->speed(); + goto out; } - - regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed)); - regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view()); - //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed); - //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str()); - char* op_string; if (drag_info.copy) { @@ -3289,70 +3721,110 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event begin_reversible_command (op_string); - if (regionview_y_movement) { - - /* moved to a different audio track. */ - - vector new_selection; + for (list::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) { + + RegionView* rv = (*i); + double ix1, ix2, iy1, iy2; + rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2); + rv->get_canvas_group()->i2w (ix1, iy1); + TimeAxisView* dest_tv = trackview_by_y_position (iy1); + AudioTimeAxisView* dest_atv = dynamic_cast(dest_tv); + double speed; + bool changed_tracks; + bool changed_position; + nframes_t where; + + if (rv->region()->locked()) { + ++i; + continue; + } - for (list::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) { - - RegionView* rv = (*i); + /* adjust for track speed */ - double ix1, ix2, iy1, iy2; - - rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2); - rv->get_canvas_group()->i2w (ix1, iy1); - TimeAxisView* tvp2 = trackview_by_y_position (iy1); - AudioTimeAxisView* atv2 = dynamic_cast(tvp2); + speed = 1.0; + + if (dest_atv && dest_atv->get_diskstream()) { + speed = dest_atv->get_diskstream()->speed(); + } + + changed_position = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed)); + changed_tracks = (dest_tv != &rv->get_time_axis_view()); - boost::shared_ptr from_playlist = rv->region()->playlist(); - boost::shared_ptr to_playlist = atv2->playlist(); - + if (changed_position && !drag_info.x_constrained) { where = (nframes_t) (unit_to_frame (ix1) * speed); - boost::shared_ptr new_region (RegionFactory::create (rv->region())); + } else { + where = rv->region()->position(); + } + + /* undo the previous hide_dependent_views so that xfades don't + disappear on copying regions + */ + + rv->get_time_axis_view().reveal_dependent_views (*rv); + + boost::shared_ptr new_region; - /* undo the previous hide_dependent_views so that xfades don't - disappear on copying regions - */ + if (drag_info.copy) { + /* we already made a copy */ + new_region = rv->region(); + } else { + new_region = RegionFactory::create (rv->region()); + } - rv->get_time_axis_view().reveal_dependent_views (*rv); + if (changed_tracks || drag_info.copy) { - if (!drag_info.copy) { - - /* the region that used to be in the old playlist is not - moved to the new one - we make a copy of it. as a result, - any existing editor for the region should no longer be - visible. - */ - - rv->hide_region_editor(); - rv->fake_set_opaque (false); + boost::shared_ptr to_playlist = dest_atv->playlist(); - session->add_command (new MementoCommand(*from_playlist, &from_playlist->get_state(), 0)); - from_playlist->remove_region ((rv->region())); - session->add_command (new MementoCommand(*from_playlist, 0, &from_playlist->get_state())); - } + latest_regionviews.clear (); - latest_regionview = 0; - - sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view)); + sigc::connection c = dest_atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view)); session->add_command (new MementoCommand(*to_playlist, &to_playlist->get_state(), 0)); to_playlist->add_region (new_region, where); session->add_command (new MementoCommand(*to_playlist, 0, &to_playlist->get_state())); c.disconnect (); - if (latest_regionview) { - new_selection.push_back (latest_regionview); + if (!latest_regionviews.empty()) { + // XXX why just the first one ? we only expect one + dest_atv->reveal_dependent_views (*latest_regionviews.front()); + new_selection.push_back (latest_regionviews.front()); } - if (drag_info.copy) { - // get rid of the copy - delete rv; - } + } else { + + /* just change the model */ + + rv->region()->set_position (where, (void*) this); + } + + if (changed_tracks && !drag_info.copy) { + + /* get the playlist where this drag started. we can't use rv->region()->playlist() + because we may have copied the region and it has not been attached to a playlist. + */ + + assert ((source_tv = dynamic_cast (&rv->get_time_axis_view()))); + assert ((ds = source_tv->get_diskstream())); + assert ((from_playlist = ds->playlist())); + /* moved to a different audio track, without copying */ + + /* the region that used to be in the old playlist is not + moved to the new one - we use a copy of it. as a result, + any existing editor for the region should no longer be + visible. + */ + + rv->hide_region_editor(); + rv->fake_set_opaque (false); + + /* remove the region from the old playlist */ + + session->add_command (new MementoCommand(*from_playlist, &from_playlist->get_state(), 0)); + from_playlist->remove_region ((rv->region())); + session->add_command (new MementoCommand(*from_playlist, 0, &from_playlist->get_state())); + /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region - was selected in all of them, then removing it from the playlist will have removed all + was selected in all of them, then removing it from a playlist will have removed all trace of it from the selection (i.e. there were N regions selected, we removed 1, but since its the same playlist for N tracks, all N tracks updated themselves, removed the corresponding regionview, and the selection is now empty). @@ -3363,6 +3835,9 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event here. if the region selection is not empty, then restart the loop because we know that we must have removed at least the region(view) we've just been working on as well as any that we processed on previous iterations. + + EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and + we can just iterate. */ if (selection->regions.empty()) { @@ -3370,111 +3845,39 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event } else { i = selection->regions.by_layer().begin(); } - } - - selection->set (new_selection); - - } else { - - /* motion within a single track */ - list regions = selection->regions.by_layer(); - - if (drag_info.copy) { - selection->clear_regions(); + } else { + ++i; } - for (list::iterator i = regions.begin(); i != regions.end(); ++i) { - - rv = (*i); - - if (rv->region()->locked()) { - continue; - } - - - if (regionview_x_movement) { - double ownspeed = 1.0; - atv = dynamic_cast (&(rv->get_time_axis_view())); - - if (atv && atv->get_diskstream()) { - ownspeed = atv->get_diskstream()->speed(); - } - - /* base the new region position on the current position of the regionview.*/ - - double ix1, ix2, iy1, iy2; - - rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2); - rv->get_canvas_group()->i2w (ix1, iy1); - where = (nframes_t) (unit_to_frame (ix1) * ownspeed); - - } else { - - where = rv->region()->position(); - } - - boost::shared_ptr to_playlist = rv->region()->playlist(); - - assert (to_playlist); - - /* add the undo */ - - session->add_command (new MementoCommand(*to_playlist, &to_playlist->get_state(), 0)); - - if (drag_info.copy) { - - boost::shared_ptr newregion; - boost::shared_ptr ar; - - if ((ar = boost::dynamic_pointer_cast(rv->region())) != 0) { - newregion = RegionFactory::create (ar); - } else { - /* XXX MIDI HERE drobilla */ - continue; - } - - /* add it */ - - latest_regionview = 0; - sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view)); - to_playlist->add_region (newregion, (nframes_t) (where * atv->get_diskstream()->speed())); - c.disconnect (); - - if (latest_regionview) { - atv->reveal_dependent_views (*latest_regionview); - selection->add (latest_regionview); - } - - /* if the original region was locked, we don't care for the new one */ - - newregion->set_locked (false); - - } else { - - /* just change the model */ - - rv->region()->set_position (where, (void*) this); - - } - - /* add the redo */ - - session->add_command (new MementoCommand(*to_playlist, 0, &to_playlist->get_state())); - - /* get rid of the copy */ - - if (drag_info.copy) { - delete rv; - } + if (drag_info.copy) { + copies.push_back (rv); } } - - out: + if (new_selection.empty()) { + if (drag_info.copy) { + /* the region(view)s that are selected and being dragged around + are copies and do not belong to any track. remove them + from the selection right here. + */ + selection->clear_regions(); + } + } else { + /* this will clear any existing selection that would have been + cleared in the other clause above + */ + selection->set (new_selection); + } + + out: if (!nocommit) { commit_reversible_command (); } + + for (vector::iterator x = copies.begin(); x != copies.end(); ++x) { + delete *x; + } } void @@ -3484,7 +3887,7 @@ Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event) this is an alignment click (control used) */ - if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) { + if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) { TimeAxisView* tv = &rv.get_time_axis_view(); AudioTimeAxisView* atv = dynamic_cast(tv); double speed = 1.0; @@ -3492,17 +3895,22 @@ Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event) speed = atv->get_diskstream()->speed(); } - if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) { - - align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed)); - - } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) { - - align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed)); + nframes64_t where = get_preferred_edit_position(); - } else { + if (where >= 0) { - align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed)); + if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) { + + align_region (rv.region(), SyncPoint, (nframes_t) (where * speed)); + + } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) { + + align_region (rv.region(), End, (nframes_t) (where * speed)); + + } else { + + align_region (rv.region(), Start, (nframes_t) (where * speed)); + } } } } @@ -3521,7 +3929,15 @@ Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, d return; } - switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) { + AudioClock::Mode m; + + if (Profile->get_sae() || Profile->get_small_screen()) { + m = ARDOUR_UI::instance()->primary_clock.mode(); + } else { + m = ARDOUR_UI::instance()->secondary_clock.mode(); + } + + switch (m) { case AudioClock::BBT: session->bbt_time (frame, bbt); snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks); @@ -3573,7 +3989,15 @@ Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double off return; } - switch (ARDOUR_UI::instance()->secondary_clock.mode ()) { + AudioClock::Mode m; + + if (Profile->get_sae() || Profile->get_small_screen()) { + m = ARDOUR_UI::instance()->primary_clock.mode (); + } else { + m = ARDOUR_UI::instance()->secondary_clock.mode (); + } + + switch (m) { case AudioClock::BBT: session->bbt_time (start, sbbt); session->bbt_time (end, ebbt); @@ -3628,13 +4052,14 @@ Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double off else { set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset); } + show_verbose_canvas_cursor (); } void Editor::collect_new_region_view (RegionView* rv) { - latest_regionview = rv; + latest_regionviews.push_back (rv); } void @@ -3664,7 +4089,7 @@ Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event) set the regionview we want to then drag. */ - latest_regionview = 0; + latest_regionviews.clear(); sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view)); /* A selection grab currently creates two undo/redo operations, one for @@ -3684,24 +4109,26 @@ Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event) c.disconnect (); - if (latest_regionview == 0) { + if (latest_regionviews.empty()) { /* something went wrong */ return; } /* we need to deselect all other regionviews, and select this one - i'm ignoring undo stuff, because the region creation will take care of it */ - selection->set (latest_regionview); + i'm ignoring undo stuff, because the region creation will take care of it + */ + selection->set (latest_regionviews); - drag_info.item = latest_regionview->get_canvas_group(); - drag_info.data = latest_regionview; + drag_info.item = latest_regionviews.front()->get_canvas_group(); + drag_info.data = latest_regionviews.front(); drag_info.motion_callback = &Editor::region_drag_motion_callback; drag_info.finished_callback = &Editor::region_drag_finished_callback; start_grab (event); - drag_info.last_trackview = clicked_trackview; - drag_info.last_frame_position = latest_regionview->region()->position(); + drag_info.source_trackview = clicked_trackview; + drag_info.dest_trackview = drag_info.source_trackview; + drag_info.last_frame_position = latest_regionviews.front()->region()->position(); drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position; show_verbose_time_cursor (drag_info.last_frame_position, 10); @@ -3713,10 +4140,8 @@ Editor::cancel_selection () for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { (*i)->hide_selection (); } - begin_reversible_command (_("cancel selection")); selection->clear (); clicked_selection = 0; - commit_reversible_command (); } void @@ -3737,7 +4162,7 @@ Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, Selection switch (op) { case CreateSelection: - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) { + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) { drag_info.copy = true; } else { drag_info.copy = false; @@ -3785,10 +4210,9 @@ Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event) nframes_t length; nframes_t pending_position; - if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { + if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset; - } - else { + } else { pending_position = 0; } @@ -3889,7 +4313,7 @@ Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event) } if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) { - start_canvas_autoscroll (1); + start_canvas_autoscroll (1, 0); } if (start != end) { @@ -3953,7 +4377,7 @@ Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event) start_grab (event, trimmer_cursor); - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) { + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) { trim_op = ContentsTrim; } else { /* These will get overridden for a point trim.*/ @@ -4034,7 +4458,7 @@ Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) begin_reversible_command (trim_type); for (list::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) { - (*i)->fake_set_opaque(false); + (*i)->fake_set_opaque(false); (*i)->region()->freeze (); AudioRegionView* const arv = dynamic_cast(*i); @@ -4080,7 +4504,7 @@ Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) { bool swap_direction = false; - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) { + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) { swap_direction = true; } @@ -4221,7 +4645,7 @@ Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event) if (!drag_info.first_move) { trim_motion_callback (item, event); - if (!clicked_regionview->get_selected()) { + if (!selection->selected (clicked_regionview)) { thaw_region_after_trim (*clicked_regionview); } else { @@ -4263,7 +4687,7 @@ Editor::point_trim (GdkEvent* event) trim_op = StartTrim; begin_reversible_command (_("Start point trim")); - if (rv->get_selected()) { + if (selection->selected (rv)) { for (list::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) @@ -4295,7 +4719,7 @@ Editor::point_trim (GdkEvent* event) trim_op = EndTrim; begin_reversible_command (_("End point trim")); - if (rv->get_selected()) { + if (selection->selected (rv)) { for (list::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) { @@ -4381,8 +4805,9 @@ Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, Ran switch (op) { case CreateRangeMarker: case CreateTransportMarker: - - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) { + case CreateCDMarker: + + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) { drag_info.copy = true; } else { drag_info.copy = false; @@ -4400,7 +4825,23 @@ Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event) { nframes_t start = 0; nframes_t end = 0; - ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect; + ArdourCanvas::SimpleRect *crect; + + switch (range_marker_op) { + case CreateRangeMarker: + crect = range_bar_drag_rect; + break; + case CreateTransportMarker: + crect = transport_bar_drag_rect; + break; + case CreateCDMarker: + crect = cd_marker_bar_drag_rect; + break; + default: + cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl; + return; + break; + } if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { snap_to (drag_info.current_pointer_frame); @@ -4415,6 +4856,7 @@ Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event) switch (range_marker_op) { case CreateRangeMarker: case CreateTransportMarker: + case CreateCDMarker: if (drag_info.first_move) { snap_to (drag_info.grab_frame); } @@ -4446,7 +4888,7 @@ Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event) } if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) { - start_canvas_autoscroll (1); + start_canvas_autoscroll (1, 0); } if (start != end) { @@ -4472,23 +4914,32 @@ Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event) { Location * newloc = 0; string rangename; + int flags; if (!drag_info.first_move) { drag_range_markerbar_op (item, event); switch (range_marker_op) { case CreateRangeMarker: + case CreateCDMarker: { begin_reversible_command (_("new range marker")); XMLNode &before = session->locations()->get_state(); session->locations()->next_available_name(rangename,"unnamed"); - newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker); + if (range_marker_op == CreateCDMarker) { + flags = Location::IsRangeMarker|Location::IsCDMarker; + cd_marker_bar_drag_rect->hide(); + } + else { + flags = Location::IsRangeMarker; + range_bar_drag_rect->hide(); + } + newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags); session->locations()->add (newloc, true); XMLNode &after = session->locations()->get_state(); session->add_command(new MementoCommand(*(session->locations()), &before, &after)); commit_reversible_command (); - range_bar_drag_rect->hide(); range_marker_drag_rect->hide(); break; } @@ -4502,7 +4953,7 @@ Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event) } else { /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */ - if (Keyboard::no_modifier_keys_pressed (&event->button)) { + if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) { nframes_t start; nframes_t end; @@ -4521,7 +4972,7 @@ Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event) switch (mouse_mode) { case MouseObject: /* find the two markers on either side and then make the selection from it */ - select_all_within (start, end, 0.0f, FLT_MAX, Selection::Set); + select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set); break; case MouseRange: @@ -4654,7 +5105,7 @@ Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event) return; } - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { + if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) { if (drag_info.first_move) { snap_to (drag_info.grab_frame); } @@ -4724,9 +5175,9 @@ Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event) begin_reversible_command (_("rubberband selection")); if (drag_info.grab_frame < drag_info.last_pointer_frame) { - commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op); + commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op); } else { - commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op); + commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op); } if (commit) { @@ -4761,7 +5212,7 @@ Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event) string str; prompter.get_result(str); if (str.length()) { - clicked_regionview->region()->set_name (str); + clicked_regionview->region()->set_name (str); } break; } @@ -4811,13 +5262,27 @@ Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event) if (drag_info.first_move) { return; } + + if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) { + /* backwards drag of the left edge - not usable */ + return; + } nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position(); +#ifdef USE_RUBBERBAND + float percentage = (float) ((double) newlen / (double) clicked_regionview->region()->length()); +#else float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f; - +#endif + begin_reversible_command (_("timestretch")); - if (run_timestretch (selection->regions, percentage) == 0) { + // XXX how do timeFX on multiple regions ? + + RegionSelection rs; + rs.add (clicked_regionview); + + if (time_stretch (rs, percentage) == 0) { session->commit_reversible_command (); } } @@ -4839,9 +5304,7 @@ Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos) } switch (snap_type) { - case SnapToFrame: case SnapToMark: - case SnapToEditCursor: return; default: @@ -4888,3 +5351,4 @@ Editor::track_height_step_timeout () } return true; } +