X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Feditor_mouse.cc;h=52105155417dd9fd90336da1f529f40fed3261af;hb=1474b10d39873538f93a466517f518c5013d1ac3;hp=47bb35325e563daa20ca6bfe0e6b5f14fc466f46;hpb=fde04925f1442fba316430e7bffedabfcfe009ca;p=ardour.git diff --git a/gtk2_ardour/editor_mouse.cc b/gtk2_ardour/editor_mouse.cc index 47bb35325e..5210515541 100644 --- a/gtk2_ardour/editor_mouse.cc +++ b/gtk2_ardour/editor_mouse.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2000-2001 Paul Davis + Copyright (C) 2000-2001 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 @@ -25,12 +25,15 @@ #include #include -#include +#include "pbd/error.h" +#include "pbd/enumwriter.h" #include -#include -#include +#include +#include "pbd/memento_command.h" +#include "pbd/basename.h" #include "ardour_ui.h" +#include "actions.h" #include "editor.h" #include "time_axis_view.h" #include "audio_time_axis.h" @@ -47,21 +50,24 @@ #include "keyboard.h" #include "editing.h" #include "rgb_macros.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "control_point_dialog.h" +#include "editor_drag.h" + +#include "ardour/types.h" +#include "ardour/profile.h" +#include "ardour/route.h" +#include "ardour/audio_track.h" +#include "ardour/audio_diskstream.h" +#include "ardour/midi_diskstream.h" +#include "ardour/playlist.h" +#include "ardour/audioplaylist.h" +#include "ardour/audioregion.h" +#include "ardour/midi_region.h" +#include "ardour/dB.h" +#include "ardour/utils.h" +#include "ardour/region_factory.h" +#include "ardour/source_factory.h" +#include "ardour/session.h" #include @@ -74,48 +80,42 @@ using namespace sigc; using namespace Gtk; using namespace Editing; -const static double ZERO_GAIN_FRACTION = gain_to_slider_position(dB_to_coefficient(0.0)); - 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 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()) { + if (!canvas_window) { + return false; + } + + pointer_window = canvas_window->get_pointer (x, y, mask); - track_canvas.window_to_world (x, y, wx, wy); + if (pointer_window == track_canvas->get_bin_window()) { + wx = x; + wy = y; 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 +Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const { double cx, cy; @@ -134,18 +134,24 @@ Editor::event_frame (GdkEvent* event, double* pcx, double* pcy) const case GDK_BUTTON_PRESS: case GDK_2BUTTON_PRESS: case GDK_3BUTTON_PRESS: - track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy); + + *pcx = event->button.x; + *pcy = event->button.y; + _trackview_group->w2i(*pcx, *pcy); break; case GDK_MOTION_NOTIFY: - track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy); + + *pcx = event->motion.x; + *pcy = event->motion.y; + _trackview_group->w2i(*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; @@ -156,76 +162,155 @@ Editor::event_frame (GdkEvent* event, double* pcx, double* pcy) const position is negative (as can be the case with motion events in particular), the frame location is always positive. */ - + return pixel_to_frame (*pcx); } +Gdk::Cursor* +Editor::which_grabber_cursor () +{ + Gdk::Cursor* c = grabber_cursor; + + if (_internal_editing) { + switch (mouse_mode) { + case MouseRange: + c = midi_pencil_cursor; + break; + + case MouseObject: + c = grabber_cursor; + break; + + case MouseTimeFX: + c = midi_resize_cursor; + break; + + default: + break; + } + + } else { + + switch (_edit_point) { + case EditAtMouse: + c = grabber_edit_point_cursor; + break; + default: + break; + } + } + + return c; +} + void -Editor::mouse_mode_toggled (MouseMode m) +Editor::set_canvas_cursor () +{ + if (_internal_editing) { + + switch (mouse_mode) { + case MouseRange: + current_canvas_cursor = midi_pencil_cursor; + break; + + case MouseObject: + current_canvas_cursor = which_grabber_cursor(); + break; + + case MouseTimeFX: + current_canvas_cursor = midi_resize_cursor; + break; + + default: + return; + } + + } else { + + 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) { - if (ignore_mouse_mode_toggle) { + if (_drag) { + return; + } + + if (!force && m == mouse_mode) { return; } + Glib::RefPtr act; + switch (m) { case MouseRange: - if (mouse_select_button.get_active()) { - set_mouse_mode (m); - } + act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range")); break; case MouseObject: - if (mouse_move_button.get_active()) { - set_mouse_mode (m); - } + act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object")); break; case MouseGain: - if (mouse_gain_button.get_active()) { - set_mouse_mode (m); - } + act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain")); break; case MouseZoom: - if (mouse_zoom_button.get_active()) { - set_mouse_mode (m); - } + act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom")); break; case MouseTimeFX: - if (mouse_timefx_button.get_active()) { - set_mouse_mode (m); - } + act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx")); break; case MouseAudition: - if (mouse_audition_button.get_active()) { - set_mouse_mode (m); - } - break; - - case MouseNote: - if (mouse_note_button.get_active()) { - set_mouse_mode (m); - } - break; - - default: + act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition")); break; } -} + + assert (act); + + Glib::RefPtr tact = Glib::RefPtr::cast_dynamic (act); + assert (tact); + + /* go there and back to ensure that the toggled handler is called to set up mouse_mode */ + tact->set_active (false); + tact->set_active (true); +} void -Editor::set_mouse_mode (MouseMode m, bool force) +Editor::mouse_mode_toggled (MouseMode m) { - if (drag_info.item) { - return; - } - - if (!force && m == mouse_mode) { - return; - } - mouse_mode = m; instant_save (); @@ -245,7 +330,7 @@ Editor::set_mouse_mode (MouseMode m, bool force) } else { - /* + /* in range mode,show the range selection. */ @@ -256,60 +341,7 @@ Editor::set_mouse_mode (MouseMode m, bool force) } } - /* 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. - */ - - ignore_mouse_mode_toggle = true; - - 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; - - case MouseNote: - mouse_note_button.set_active (true); - set_midi_edit_cursor (current_midi_edit_mode()); - break; - } - - if (mouse_mode == MouseNote) - midi_toolbar_frame.show(); - else - midi_toolbar_frame.hide(); - - ignore_mouse_mode_toggle = false; - - if (is_drawable()) { - track_canvas.get_window()->set_cursor(*current_canvas_cursor); - } + set_canvas_cursor (); } void @@ -317,8 +349,15 @@ Editor::step_mouse_mode (bool next) { switch (current_mouse_mode()) { case MouseObject: - if (next) set_mouse_mode (MouseRange); - else set_mouse_mode (MouseTimeFX); + if (next) { + if (Profile->get_sae()) { + set_mouse_mode (MouseZoom); + } else { + set_mouse_mode (MouseRange); + } + } else { + set_mouse_mode (MouseTimeFX); + } break; case MouseRange: @@ -327,130 +366,53 @@ Editor::step_mouse_mode (bool next) break; case MouseZoom: - if (next) set_mouse_mode (MouseGain); - else set_mouse_mode (MouseRange); + if (next) { + if (Profile->get_sae()) { + set_mouse_mode (MouseTimeFX); + } else { + set_mouse_mode (MouseGain); + } + } else { + if (Profile->get_sae()) { + set_mouse_mode (MouseObject); + } else { + set_mouse_mode (MouseRange); + } + } break; - + case MouseGain: if (next) set_mouse_mode (MouseTimeFX); else set_mouse_mode (MouseZoom); break; - + case MouseTimeFX: - if (next) set_mouse_mode (MouseAudition); - else set_mouse_mode (MouseGain); + if (next) { + set_mouse_mode (MouseAudition); + } else { + if (Profile->get_sae()) { + set_mouse_mode (MouseZoom); + } else { + set_mouse_mode (MouseGain); + } + } break; case MouseAudition: if (next) set_mouse_mode (MouseObject); else set_mouse_mode (MouseTimeFX); break; - - case MouseNote: - if (next) set_mouse_mode (MouseObject); - else set_mouse_mode (MouseAudition); - break; - } -} - -void -Editor::midi_edit_mode_toggled (MidiEditMode m) -{ - if (ignore_midi_edit_mode_toggle) { - return; - } - - switch (m) { - case MidiEditPencil: - if (midi_tool_pencil_button.get_active()) - set_midi_edit_mode (m); - break; - - case MidiEditSelect: - if (midi_tool_select_button.get_active()) - set_midi_edit_mode (m); - break; - - case MidiEditErase: - if (midi_tool_erase_button.get_active()) - set_midi_edit_mode (m); - break; - - default: - break; - } - - set_midi_edit_cursor(m); -} - - -void -Editor::set_midi_edit_mode (MidiEditMode m, bool force) -{ - if (drag_info.item) { - return; - } - - if (!force && m == midi_edit_mode) { - return; - } - - midi_edit_mode = m; - - instant_save (); - - ignore_midi_edit_mode_toggle = true; - - switch (midi_edit_mode) { - case MidiEditPencil: - midi_tool_pencil_button.set_active (true); - break; - - case MidiEditSelect: - midi_tool_select_button.set_active (true); - break; - - case MidiEditErase: - midi_tool_erase_button.set_active (true); - break; - } - - ignore_midi_edit_mode_toggle = false; - - set_midi_edit_cursor (current_midi_edit_mode()); - - if (is_drawable()) { - track_canvas.get_window()->set_cursor(*current_canvas_cursor); - } -} - -void -Editor::set_midi_edit_cursor (MidiEditMode m) -{ - switch (midi_edit_mode) { - case MidiEditPencil: - current_canvas_cursor = midi_pencil_cursor; - break; - - case MidiEditSelect: - current_canvas_cursor = midi_select_cursor; - break; - - case MidiEditErase: - current_canvas_cursor = midi_erase_cursor; - break; } } void -Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type) +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 - mouse operation is a region alignment. + /* in object/audition/timefx/gain-automation 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 mouse operation is a + region alignment. note: not dbl-click or triple-click */ @@ -458,45 +420,46 @@ Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType it if (((mouse_mode != MouseObject) && (mouse_mode != MouseAudition || item_type != RegionItem) && (mouse_mode != MouseTimeFX || item_type != RegionItem) && + (mouse_mode != MouseGain) && (mouse_mode != MouseRange)) || - (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) { - + ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) { + return; } if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) { if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) { - + /* almost no selection action on modified button-2 or button-3 events */ - + if (item_type != RegionItem && event->button.button != 2) { return; } } } - + Selection::Operation op = Keyboard::selection_type (event->button.state); bool press = (event->type == GDK_BUTTON_PRESS); // begin_reversible_command (_("select on click")); - + 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; @@ -506,216 +469,288 @@ 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 CrossfadeViewItem: - commit = set_selected_track_from_click (press, op, false); - break; - case ControlPointItem: - 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 (); -// } } bool -Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type) +Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type) { - track_canvas.grab_focus(); + if (_drag) { + _drag->item()->ungrab (event->button.time); + delete _drag; + _drag = 0; + } - if (session && session->actively_recording()) { + /* single mouse clicks on any of these item types operate + independent of mouse mode, mostly because they are + not on the main track canvas or because we want + them to be modeless. + */ + + switch (item_type) { + case PlayheadCursorItem: + assert (_drag == 0); + _drag = new CursorDrag (this, item, true); + _drag->start_grab (event); return true; - } - button_selection (item, event, item_type); - - if (drag_info.item == 0 && - (Keyboard::is_delete_event (&event->button) || - Keyboard::is_context_menu_event (&event->button) || - Keyboard::is_edit_event (&event->button))) { - - /* handled by button release */ + case MarkerItem: + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) { + hide_marker (item, event); + } else { + assert (_drag == 0); + _drag = new MarkerDrag (this, item); + _drag->start_grab (event); + } return true; - } - switch (event->button.button) { - case 1: + case TempoMarkerItem: + assert (_drag == 0); + _drag = new TempoMarkerDrag ( + this, + item, + Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier) + ); + _drag->start_grab (event); + return true; - if (event->type == GDK_BUTTON_PRESS) { + case MeterMarkerItem: + assert (_drag == 0); + _drag = new MeterMarkerDrag ( + this, + item, + Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier) + ); + _drag->start_grab (event); + return true; - if (drag_info.item) { - drag_info.item->ungrab (event->button.time); - } + case MarkerBarItem: + case TempoBarItem: + case MeterBarItem: + if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) { + assert (_drag == 0); + _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false); + _drag->start_grab (event); + } + return true; + break; - /* single mouse clicks on any of these item types operate - independent of mouse mode, mostly because they are - not on the main track canvas or because we want - them to be modeless. - */ - - switch (item_type) { - 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))) { - hide_marker (item, event); - } else { - start_marker_grab (item, event); - } - return true; + case RangeMarkerBarItem: + assert (_drag == 0); + if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) { + _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false); + } else { + _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker); + } + _drag->start_grab (event); + return true; + break; - case TempoMarkerItem: - if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) { - start_tempo_marker_copy_grab (item, event); - } else { - start_tempo_marker_grab (item, event); - } - return true; + case CdMarkerBarItem: + assert (_drag == 0); + if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) { + _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false); + } else { + _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker); + } + _drag->start_grab (event); + return true; + break; - case MeterMarkerItem: - if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) { - start_meter_marker_copy_grab (item, event); - } else { - start_meter_marker_grab (item, event); - } - return true; + case TransportMarkerBarItem: + assert (_drag == 0); + if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) { + _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false); + } else { + _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker); + } + _drag->start_grab (event); + return true; + break; - case TempoBarItem: - return true; + default: + break; + } - case MeterBarItem: - return true; - - case RangeMarkerBarItem: - start_range_markerbar_op (item, event, CreateRangeMarker); + if (internal_editing()) { + switch (item_type) { + case StreamItem: + assert (_drag == 0); + _drag = new RegionCreateDrag (this, item, clicked_axisview); + _drag->start_grab (event); + return true; + case NoteItem: + /* Note: we don't get here if not in internal_editing() mode */ + if (mouse_mode == MouseTimeFX) { + assert (_drag == 0); + _drag = new NoteResizeDrag (this, item); + _drag->start_grab (event); return true; - break; - - case TransportMarkerBarItem: - start_range_markerbar_op (item, event, CreateTransportMarker); + } else if (mouse_mode == MouseObject) { + assert (_drag == 0); + _drag = new NoteDrag (this, item); + _drag->start_grab (event); return true; - break; - - default: - break; + } else { + return false; } + break; + default: + return true; } - + } else { switch (mouse_mode) { case MouseRange: switch (item_type) { case StartSelectionTrimItem: - start_selection_op (item, event, SelectionStartTrim); + assert (_drag == 0); + _drag = new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim); + _drag->start_grab (event); break; - + case EndSelectionTrimItem: - start_selection_op (item, event, SelectionEndTrim); + assert (_drag == 0); + _drag = new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim); + _drag->start_grab (event); break; case SelectionItem: - if (Keyboard::modifier_state_contains - (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) { + if (Keyboard::modifier_state_contains + (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 { + assert (_drag == 0); + _drag = new SelectionDrag (this, item, SelectionDrag::SelectionMove); + _drag->start_grab (event); + } else { /* this was debated, but decided the more common action was to make a new selection */ - start_selection_op (item, event, CreateSelection); + assert (_drag == 0); + _drag = new SelectionDrag (this, item, SelectionDrag::CreateSelection); + _drag->start_grab (event); } break; default: - start_selection_op (item, event, CreateSelection); + assert (_drag == 0); + _drag = new SelectionDrag (this, item, SelectionDrag::CreateSelection); + _drag->start_grab (event); } return true; 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); + + assert (_drag == 0); + _drag = new RubberbandSelectDrag (this, item); + _drag->start_grab (event); } else if (event->type == GDK_BUTTON_PRESS) { switch (item_type) { case FadeInHandleItem: - start_fade_in_grab (item, event); + { + assert (_drag == 0); + RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit); + _drag = new FadeInDrag (this, item, reinterpret_cast (item->get_data("regionview")), s); + _drag->start_grab (event); return true; - + } + case FadeOutHandleItem: - start_fade_out_grab (item, event); + { + assert (_drag == 0); + RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit); + _drag = new FadeOutDrag (this, item, reinterpret_cast (item->get_data("regionview")), s); + _drag->start_grab (event); return true; + } case RegionItem: - if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) { - start_region_copy_grab (item, event); + if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) { + start_region_copy_grab (item, event, clicked_regionview); } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) { - start_region_brush_grab (item, event); + start_region_brush_grab (item, event, clicked_regionview); } else { - start_region_grab (item, event); + start_region_grab (item, event, clicked_regionview); } break; - + case RegionViewNameHighlight: - start_trim (item, event); + { + assert (_drag == 0); + RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit); + _drag = new TrimDrag (this, item, clicked_regionview, s.by_layer()); + _drag->start_grab (event); return true; break; - + } + case RegionViewName: + { /* rename happens on edit clicks */ - start_trim (clicked_regionview->get_name_highlight(), event); - return true; + assert (_drag == 0); + RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit); + _drag = new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()); + _drag->start_grab (event); + return true; break; + } case ControlPointItem: - start_control_point_grab (item, event); + assert (_drag == 0); + _drag = new ControlPointDrag (this, item); + _drag->start_grab (event); return true; break; - + case AutomationLineItem: - start_line_grab_from_line (item, event); + assert (_drag == 0); + _drag = new LineDrag (this, item); + _drag->start_grab (event); return true; break; case StreamItem: case AutomationTrackItem: - start_rubberband_select (item, event); + assert (_drag == 0); + _drag = new RubberbandSelectDrag (this, item); + _drag->start_grab (event); break; - + #ifdef WITH_CMT case ImageFrameHandleStartItem: imageframe_start_handle_op(item, event) ; @@ -742,7 +777,7 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp #endif case MarkerBarItem: - + break; default: @@ -751,19 +786,28 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp } return true; break; - + case MouseGain: switch (item_type) { case RegionItem: - // start_line_grab_from_regionview (item, event); + /* start a grab so that if we finish after moving + we can tell what happened. + */ + assert (_drag == 0); + _drag = new RegionGainDrag (this, item); + _drag->start_grab (event, current_canvas_cursor); break; case GainLineItem: - start_line_grab_from_line (item, event); + assert (_drag == 0); + _drag = new LineDrag (this, item); + _drag->start_grab (event); return true; case ControlPointItem: - start_control_point_grab (item, event); + assert (_drag == 0); + _drag = new ControlPointDrag (this, item); + _drag->start_grab (event); return true; break; @@ -775,11 +819,15 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp switch (item_type) { case ControlPointItem: - start_control_point_grab (item, event); + assert (_drag == 0); + _drag = new ControlPointDrag (this, item); + _drag->start_grab (event); break; case AutomationLineItem: - start_line_grab_from_line (item, event); + assert (_drag == 0); + _drag = new LineDrag (this, item); + _drag->start_grab (event); break; case RegionItem: @@ -796,7 +844,9 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp case MouseZoom: if (event->type == GDK_BUTTON_PRESS) { - start_mouse_zoom (item, event); + assert (_drag == 0); + _drag = new MouseZoomDrag (this, item); + _drag->start_grab (event); } return true; @@ -804,81 +854,67 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp case MouseTimeFX: if (item_type == RegionItem) { - start_time_fx (item, event); + assert (_drag == 0); + _drag = new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()); + _drag->start_grab (event); } break; case MouseAudition: - _scrubbing = true; + _drag = new ScrubDrag (this, item); + _drag->start_grab (event); + scrub_reversals = 0; + scrub_reverse_distance = 0; last_scrub_x = event->button.x; scrubbing_direction = 0; - /* rest handled in motion & release */ + track_canvas->get_window()->set_cursor (*transparent_cursor); break; - case MouseNote: - start_create_region_grab (item, event); - break; - default: break; } - break; + } - case 2: - switch (mouse_mode) { - case MouseObject: - if (event->type == GDK_BUTTON_PRESS) { - switch (item_type) { - case RegionItem: - if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) { - start_region_copy_grab (item, event); - } else { - start_region_grab (item, event); - } - - break; - case ControlPointItem: - start_control_point_grab (item, event); - return true; - break; - - default: - break; - } - } - - - switch (item_type) { - case RegionViewNameHighlight: - start_trim (item, event); - return true; - break; - - case RegionViewName: - start_trim (clicked_regionview->get_name_highlight(), event); - return true; - break; - - default: - break; + return false; +} + +bool +Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type) +{ + switch (mouse_mode) { + case MouseObject: + switch (item_type) { + case RegionItem: + if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) { + start_region_copy_grab (item, event, clicked_regionview); + } else { + start_region_grab (item, event, clicked_regionview); } - + return true; + break; + case ControlPointItem: + assert (_drag == 0); + _drag = new ControlPointDrag (this, item); + _drag->start_grab (event); + return true; break; - case MouseRange: - if (event->type == GDK_BUTTON_PRESS) { - /* relax till release */ - } + default: + break; + } + + switch (item_type) { + case RegionViewNameHighlight: + assert (_drag == 0); + _drag = new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()); + _drag->start_grab (event); return true; break; - - - case MouseZoom: - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) { - temporal_zoom_session(); - } else { - temporal_zoom_to_frame (true, event_frame(event)); - } + + case RegionViewName: + assert (_drag == 0); + _drag = new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()); + _drag->start_grab (event); return true; break; @@ -888,6 +924,79 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp break; + case MouseRange: + /* relax till release */ + return true; + break; + + + case MouseZoom: + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) { + temporal_zoom_session(); + } else { + temporal_zoom_to_frame (true, event_frame(event)); + } + return true; + break; + + default: + break; + } + + return false; +} + +bool +Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type) +{ + if (event->type != GDK_BUTTON_PRESS) { + return false; + } + + Glib::RefPtr canvas_window = const_cast(this)->track_canvas->get_window(); + + if (canvas_window) { + Glib::RefPtr pointer_window; + int x, y; + double wx, wy; + Gdk::ModifierType mask; + + 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); + allow_vertical_scroll = true; + } else { + allow_vertical_scroll = false; + } + } + + track_canvas->grab_focus(); + + if (session && session->actively_recording()) { + return true; + } + + button_selection (item, event, item_type); + + if (_drag == 0 && + (Keyboard::is_delete_event (&event->button) || + Keyboard::is_context_menu_event (&event->button) || + Keyboard::is_edit_event (&event->button))) { + + /* handled by button release */ + return true; + } + + switch (event->button.button) { + case 1: + return button_press_handler_1 (item, event, item_type); + break; + + case 2: + return button_press_handler_2 (item, event, item_type); + break; + case 3: break; @@ -902,28 +1011,35 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp bool Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type) { - nframes_t where = event_frame (event, 0, 0); + nframes64_t where = event_frame (event, 0, 0); + AutomationTimeAxisView* atv = 0; /* no action if we're recording */ - + if (session && session->actively_recording()) { return true; } /* first, see if we're finishing a drag ... */ - if (drag_info.item) { - if (end_grab (item, event)) { + bool were_dragging = false; + if (_drag) { + bool const r = _drag->end_grab (event); + delete _drag; + _drag = 0; + if (r) { /* grab dragged, so do nothing else */ return true; } + + were_dragging = true; } - + button_selection (item, event, item_type); /* edit events get handled here */ - - if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) { + + if (_drag == 0 && Keyboard::is_edit_event (&event->button)) { switch (item_type) { case RegionItem: edit_region (); @@ -932,17 +1048,21 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT case TempoMarkerItem: edit_tempo_marker (item); break; - + case MeterMarkerItem: edit_meter_marker (item); break; - + case RegionViewName: if (clicked_regionview->name_active()) { return mouse_rename_region (item, event); } break; + case ControlPointItem: + edit_control_point (item); + break; + default: break; } @@ -953,7 +1073,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT if (Keyboard::is_context_menu_event (&event->button)) { - if (drag_info.item == 0) { + if (_drag == 0) { /* no matter which button pops up the context menu, tell the menu widget to use button 1 to drive menu selection. @@ -968,30 +1088,30 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT break; case StreamItem: - popup_track_context_menu (1, event->button.time, where); + popup_track_context_menu (1, event->button.time, item_type, false, where); break; - + case RegionItem: case RegionViewNameHighlight: case RegionViewName: - popup_track_context_menu (1, event->button.time, where); + popup_track_context_menu (1, event->button.time, item_type, false, where); break; - + case SelectionItem: - popup_track_context_menu (1, event->button.time, where); + popup_track_context_menu (1, event->button.time, item_type, true, where); break; case AutomationTrackItem: - case CrossfadeViewItem: - popup_track_context_menu (1, event->button.time, where); + popup_track_context_menu (1, event->button.time, item_type, false, where); break; - case MarkerBarItem: - case RangeMarkerBarItem: - case TransportMarkerBarItem: + case MarkerBarItem: + case RangeMarkerBarItem: + case TransportMarkerBarItem: + case CdMarkerBarItem: case TempoBarItem: case MeterBarItem: - popup_ruler_menu (pixel_to_frame(event->button.x), item_type); + popup_ruler_menu (where, item_type); break; case MarkerItem: @@ -1001,11 +1121,15 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT case TempoMarkerItem: tm_marker_context_menu (&event->button, item); break; - + case MeterMarkerItem: tm_marker_context_menu (&event->button, item); break; + case CrossfadeViewItem: + popup_track_context_menu (1, event->button.time, item_type, false, where); + break; + #ifdef WITH_CMT case ImageFrameItem: popup_imageframe_edit_menu(1, event->button.time, item, true) ; @@ -1020,7 +1144,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ; break ; #endif - + default: break; } @@ -1031,13 +1155,13 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT /* delete events get handled here */ - if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) { + if (_drag == 0 && Keyboard::is_delete_event (&event->button)) { switch (item_type) { case TempoMarkerItem: remove_tempo_marker (item); break; - + case MeterMarkerItem: remove_meter_marker (item); break; @@ -1051,7 +1175,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT remove_clicked_region (); } break; - + case ControlPointItem: if (mouse_mode == MouseGain) { remove_gain_control_point (item, event); @@ -1080,21 +1204,31 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT return true; case MarkerBarItem: - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { - snap_to (where, 0, true); + if (!_dragging_playhead) { + snap_to_with_modifier (where, event, 0, true); + mouse_add_new_marker (where); + } + return true; + + case CdMarkerBarItem: + if (!_dragging_playhead) { + // if we get here then a dragged range wasn't done + snap_to_with_modifier (where, event, 0, true); + mouse_add_new_marker (where, true); } - mouse_add_new_marker (where); return true; case TempoBarItem: - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { - snap_to (where); + if (!_dragging_playhead) { + snap_to_with_modifier (where, event); + mouse_add_new_tempo_event (where); } - mouse_add_new_tempo_event (where); return true; - + case MeterBarItem: - mouse_add_new_meter_event (pixel_to_frame (event->button.x)); + if (!_dragging_playhead) { + mouse_add_new_meter_event (pixel_to_frame (event->button.x)); + } return true; break; @@ -1106,11 +1240,10 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT case MouseObject: switch (item_type) { case AutomationTrackItem: - dynamic_cast(clicked_axisview)->add_automation_event - (item, - event, - where, - event->button.y); + atv = dynamic_cast(clicked_axisview); + if (atv) { + atv->add_automation_event (item, event, where, event->button.y); + } return true; break; @@ -1128,10 +1261,16 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT switch (item_type) { case RegionItem: - dynamic_cast(clicked_regionview)->add_gain_point_event (item, event); + /* check that we didn't drag before releasing, since + its really annoying to create new control + points when doing this. + */ + if (were_dragging) { + dynamic_cast(clicked_regionview)->add_gain_point_event (item, event); + } return true; break; - + case AutomationTrackItem: dynamic_cast(clicked_axisview)-> add_automation_event (item, event, where, event->button.y); @@ -1141,14 +1280,14 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT break; } break; - + case MouseAudition: - _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: - audition_selected_region (); + play_selected_region (); break; default: break; @@ -1156,9 +1295,9 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT } else { /* make sure we stop */ session->request_transport_speed (0.0); - } + } break; - + default: break; @@ -1170,22 +1309,41 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT case 2: switch (mouse_mode) { - + + case MouseObject: + switch (item_type) { + case RegionItem: + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) { + raise_region (); + } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) { + lower_region (); + } else { + // Button2 click is unused + } + return true; + + break; + + default: + break; + } + break; + case MouseRange: - + // x_style_paste (where, 1.0); return true; break; - + default: break; } break; - + case 3: break; - + default: break; } @@ -1193,12 +1351,17 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT } bool -Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type) +Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* /*event*/, ItemType item_type) { ControlPoint* cp; Marker * marker; double fraction; - + + if (last_item_entered != item) { + last_item_entered = item; + last_item_entered_n = 0; + } + switch (item_type) { case ControlPointItem: if (mouse_mode == MouseGain || mouse_mode == MouseObject) { @@ -1209,31 +1372,34 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_ at_x = cp->get_x(); at_y = cp->get_y (); cp->item()->i2w (at_x, at_y); - at_x += 20.0; - at_y += 20.0; + at_x += 10.0; + at_y += 10.0; 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() && dynamic_cast (_drag) == 0) { + 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; - + case GainLineItem: if (mouse_mode == MouseGain) { ArdourCanvas::Line *line = dynamic_cast (item); if (line) 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; - + case AutomationLineItem: if (mouse_mode == MouseGain || mouse_mode == MouseObject) { { @@ -1242,14 +1408,14 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_ 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; @@ -1264,25 +1430,32 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_ #endif if (is_drawable()) { - track_canvas.get_window()->set_cursor (*trimmer_cursor); + track_canvas->get_window()->set_cursor (*trimmer_cursor); } break; 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; case RegionViewName: - + /* when the name is not an active item, the entire name highlight is for trimming */ 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; @@ -1301,7 +1474,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) { @@ -1314,10 +1487,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); + track_canvas->get_window()->set_cursor (*timebar_cursor); } break; @@ -1331,7 +1505,7 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_ case MeterMarkerItem: case TempoMarkerItem: if (is_drawable()) { - time_canvas.get_window()->set_cursor (*timebar_cursor); + track_canvas->get_window()->set_cursor (*timebar_cursor); } break; case FadeInHandleItem: @@ -1373,7 +1547,7 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_ } bool -Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type) +Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* /*event*/, ItemType item_type) { AutomationLine* al; ControlPoint* cp; @@ -1390,14 +1564,14 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_ cp->set_visible (false); } } - + 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 (); break; - + case RegionViewNameHighlight: case StartSelectionTrimItem: case EndSelectionTrimItem: @@ -1411,7 +1585,7 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_ #endif if (is_drawable()) { - track_canvas.get_window()->set_cursor (*current_canvas_cursor); + track_canvas->get_window()->set_cursor (*current_canvas_cursor); } break; @@ -1424,7 +1598,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; @@ -1432,21 +1606,22 @@ 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); + track_canvas->get_window()->set_cursor (*current_canvas_cursor); } break; - + case MarkerItem: if ((marker = static_cast (item->get_data ("marker"))) == 0) { break; @@ -1458,9 +1633,9 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_ // fall through case MeterMarkerItem: case TempoMarkerItem: - + if (is_drawable()) { - time_canvas.get_window()->set_cursor (*timebar_cursor); + track_canvas->get_window()->set_cursor (*timebar_cursor); } break; @@ -1479,12 +1654,12 @@ 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)); } break; - + default: break; } @@ -1502,2296 +1677,201 @@ 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; - - /* 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. - */ + double delta; - track_canvas.get_pointer (x, y); + if (scrubbing_direction == 0) { + /* first move */ + session->request_locate (_drag->current_pointer_frame(), false); + session->request_transport_speed (0.1); + scrubbing_direction = 1; - if (current_stepping_trackview) { - /* don't keep the persistent stepped trackview if the mouse moves */ - current_stepping_trackview = 0; - step_timeout.disconnect (); - } - - if (session && session->actively_recording()) { - /* Sorry. no dragging stuff around while we record */ - return true; - } - - 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) { - - double delta; - - 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) { - /* move to the left */ - - if (scrubbing_direction > 0) { - /* we reversed direction to go backwards */ - - session->request_transport_speed (-0.1); - - } else { - /* still moving to the left (backwards) */ - - delta = 0.005 * (last_scrub_x - drag_info.current_pointer_x); - session->request_transport_speed (session->transport_speed() - delta); - } - - scrubbing_direction = -1; - - } else { - /* move to the right */ - if (scrubbing_direction < 0) { - /* we reversed direction to go forward */ - - session->request_transport_speed (0.1); - } else { - /* still moving to the right */ - - delta = 0.005 * (drag_info.current_pointer_x - last_scrub_x); - session->request_transport_speed (session->transport_speed() + delta); - } - - scrubbing_direction = 1; - } - } - - last_scrub_x = drag_info.current_pointer_x; - } - - 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 = (::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 - - if (drag_info.want_move_threshold && drag_info.move_threshold_passed) { - drag_info.grab_frame = drag_info.current_pointer_frame; - drag_info.grab_x = drag_info.current_pointer_x; - drag_info.grab_y = drag_info.current_pointer_y; - drag_info.last_pointer_frame = drag_info.grab_frame; - drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position; - } - } - } - - switch (item_type) { - case PlayheadCursorItem: - case MarkerItem: - case ControlPointItem: - case TempoMarkerItem: - case MeterMarkerItem: - case RegionViewNameHighlight: - case StartSelectionTrimItem: - case EndSelectionTrimItem: - case SelectionItem: - case GainLineItem: - case AutomationLineItem: - case FadeInHandleItem: - case FadeOutHandleItem: - -#ifdef WITH_CMT - case ImageFrameHandleStartItem: - case ImageFrameHandleEndItem: - case MarkerViewHandleStartItem: - case MarkerViewHandleEndItem: -#endif - - if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK || - (event->motion.state & Gdk::BUTTON2_MASK))) { - if (!from_autoscroll) { - maybe_autoscroll (event); - } - (this->*(drag_info.motion_callback)) (item, event); - goto handled; - } - goto not_handled; - - default: - break; - } - - switch (mouse_mode) { - case MouseObject: - case MouseRange: - case MouseZoom: - case MouseTimeFX: - case MouseNote: - if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK || - (event->motion.state & GDK_BUTTON2_MASK))) { - if (!from_autoscroll) { - maybe_autoscroll (event); - } - (this->*(drag_info.motion_callback)) (item, event); - goto handled; - } - goto not_handled; - break; - - default: - break; - } - - handled: - track_canvas_motion (event); - // drag_info.last_pointer_frame = drag_info.current_pointer_frame; - return true; - - not_handled: - return false; -} - -void -Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor) -{ - if (drag_info.item == 0) { - fatal << _("programming error: start_grab called without drag item") << endmsg; - /*NOTREACHED*/ - return; - } - - if (cursor == 0) { - cursor = 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)) { - drag_info.y_constrained = true; - drag_info.x_constrained = false; - } else { - drag_info.y_constrained = false; - drag_info.x_constrained = true; - } - } else { - drag_info.x_constrained = false; - drag_info.y_constrained = false; - } - - 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; - drag_info.move_threshold_passed = false; - drag_info.want_move_threshold = false; - drag_info.pointer_frame_offset = 0; - drag_info.brushing = false; - drag_info.copied_location = 0; - - drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, - *cursor, - event->button.time); - - if (session && session->transport_rolling()) { - drag_info.was_rolling = true; - } else { - drag_info.was_rolling = false; - } - - switch (snap_type) { - case SnapToRegionStart: - case SnapToRegionEnd: - case SnapToRegionSync: - case SnapToRegionBoundary: - build_region_boundary_cache (); - break; - default: - break; - } -} - -void -Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time) -{ - drag_info.item->ungrab (0); - drag_info.item = new_item; - - if (cursor == 0) { - cursor = grabber_cursor; - } - - drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time); -} - -bool -Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event) -{ - bool did_drag = false; - - stop_canvas_autoscroll (); - - if (drag_info.item == 0) { - 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); - } - - did_drag = !drag_info.first_move; - - 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; - } - - return did_drag; -} - -void -Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event) -{ - drag_info.item = item; - drag_info.motion_callback = &Editor::fade_in_drag_motion_callback; - drag_info.finished_callback = &Editor::fade_in_drag_finished_callback; - - start_grab (event); - - if ((drag_info.data = (item->get_data ("regionview"))) == 0) { - fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg; - /*NOTREACHED*/ - } - - AudioRegionView* arv = static_cast(drag_info.data); - - drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in()->back()->when + arv->region()->position()); -} - -void -Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) -{ - AudioRegionView* arv = static_cast(drag_info.data); - nframes_t pos; - nframes_t fade_length; - - 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; - } - - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { - snap_to (pos); - } - - if (pos < (arv->region()->position() + 64)) { - fade_length = 64; // this should be a minimum defined somewhere - } else if (pos > arv->region()->last_frame()) { - fade_length = arv->region()->length(); - } else { - fade_length = pos - arv->region()->position(); - } - /* mapover the region selection */ - - for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { - - AudioRegionView* tmp = dynamic_cast (*i); - - if (!tmp) { - continue; - } - - tmp->reset_fade_in_shape_width (fade_length); - } - - show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10); - - drag_info.first_move = false; -} - -void -Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event) -{ - AudioRegionView* arv = static_cast(drag_info.data); - nframes_t pos; - nframes_t fade_length; - - if (drag_info.first_move) return; - - 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; - } - - if (pos < (arv->region()->position() + 64)) { - fade_length = 64; // this should be a minimum defined somewhere - } else if (pos > arv->region()->last_frame()) { - fade_length = arv->region()->length(); - } else { - fade_length = pos - arv->region()->position(); - } - - begin_reversible_command (_("change fade in length")); - - for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { - - AudioRegionView* tmp = dynamic_cast (*i); - - if (!tmp) { - continue; - } - - boost::shared_ptr alist = tmp->audio_region()->fade_in(); - XMLNode &before = alist->get_state(); - - tmp->audio_region()->set_fade_in_length (fade_length); - - XMLNode &after = alist->get_state(); - session->add_command(new MementoCommand(*alist.get(), &before, &after)); - } - - commit_reversible_command (); -} - -void -Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event) -{ - drag_info.item = item; - drag_info.motion_callback = &Editor::fade_out_drag_motion_callback; - drag_info.finished_callback = &Editor::fade_out_drag_finished_callback; - - start_grab (event); - - if ((drag_info.data = (item->get_data ("regionview"))) == 0) { - fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg; - /*NOTREACHED*/ - } - - AudioRegionView* arv = static_cast(drag_info.data); - - drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out()->back()->when + arv->region()->position()); -} - -void -Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) -{ - AudioRegionView* arv = static_cast(drag_info.data); - nframes_t pos; - nframes_t fade_length; - - 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; - } - - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { - snap_to (pos); - } - - if (pos > (arv->region()->last_frame() - 64)) { - fade_length = 64; // this should really be a minimum fade defined somewhere - } - else if (pos < arv->region()->position()) { - fade_length = arv->region()->length(); - } - else { - fade_length = arv->region()->last_frame() - pos; - } - - /* mapover the region selection */ - - for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { - - AudioRegionView* tmp = dynamic_cast (*i); - - if (!tmp) { - continue; - } - - tmp->reset_fade_out_shape_width (fade_length); - } - - show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10); - - drag_info.first_move = false; -} - -void -Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event) -{ - if (drag_info.first_move) return; - - AudioRegionView* arv = static_cast(drag_info.data); - nframes_t pos; - nframes_t fade_length; - - 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; - } - - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { - snap_to (pos); - } - - if (pos > (arv->region()->last_frame() - 64)) { - fade_length = 64; // this should really be a minimum fade defined somewhere - } - else if (pos < arv->region()->position()) { - fade_length = arv->region()->length(); - } - else { - fade_length = arv->region()->last_frame() - pos; - } - - begin_reversible_command (_("change fade out length")); - - for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { - - AudioRegionView* tmp = dynamic_cast (*i); - - if (!tmp) { - continue; - } - - boost::shared_ptr alist = tmp->audio_region()->fade_out(); - XMLNode &before = alist->get_state(); - - tmp->audio_region()->set_fade_out_length (fade_length); - - XMLNode &after = alist->get_state(); - session->add_command(new MementoCommand(*alist.get(), &before, &after)); - } - - commit_reversible_command (); -} - -void -Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event) -{ - drag_info.item = item; - drag_info.motion_callback = &Editor::cursor_drag_motion_callback; - drag_info.finished_callback = &Editor::cursor_drag_finished_callback; - - start_grab (event); - - if ((drag_info.data = (item->get_data ("cursor"))) == 0) { - fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg; - /*NOTREACHED*/ - } - - Cursor* cursor = (Cursor *) drag_info.data; - - if (cursor == playhead_cursor) { - _dragging_playhead = true; - - if (session && drag_info.was_rolling) { - session->request_stop (); - } - - if (session && session->is_auditioning()) { - session->cancel_audition (); - } - } - - drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame; - - show_verbose_time_cursor (cursor->current_frame, 10); -} - -void -Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) -{ - Cursor* cursor = (Cursor *) drag_info.data; - nframes_t adjusted_frame; - - if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { - adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset; - } - else { - adjusted_frame = 0; - } - - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { - if (cursor == playhead_cursor && snap_type != SnapToEditPoint) { - snap_to (adjusted_frame); - } - } - - if (adjusted_frame == drag_info.last_pointer_frame) return; - - cursor->set_position (adjusted_frame); - - UpdateAllTransportClocks (cursor->current_frame); - - show_verbose_time_cursor (cursor->current_frame, 10); - - drag_info.last_pointer_frame = adjusted_frame; - drag_info.first_move = false; -} - -void -Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event) -{ - if (drag_info.first_move) return; - - cursor_drag_motion_callback (item, event); - - _dragging_playhead = false; - - if (item == &playhead_cursor->canvas_item) { - if (session) { - session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling); - } - } -} - -void -Editor::update_marker_drag_item (Location *location) -{ - double x1 = frame_to_pixel (location->start()); - double x2 = frame_to_pixel (location->end()); - - if (location->is_mark()) { - marker_drag_line_points.front().set_x(x1); - marker_drag_line_points.back().set_x(x1); - marker_drag_line->property_points() = marker_drag_line_points; - } - else { - range_marker_drag_rect->property_x1() = x1; - range_marker_drag_rect->property_x2() = x2; - } -} - -void -Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event) -{ - Marker* marker; - - if ((marker = static_cast (item->get_data ("marker"))) == 0) { - fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg; - /*NOTREACHED*/ - } - - bool is_start; - - Location *location = find_location_from_marker (marker, is_start); - - drag_info.item = item; - drag_info.data = marker; - drag_info.motion_callback = &Editor::marker_drag_motion_callback; - drag_info.finished_callback = &Editor::marker_drag_finished_callback; - - 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 { - 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); - } - - 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 -Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) -{ - nframes_t f_delta; - Marker* marker = (Marker *) drag_info.data; - Location *real_location; - Location *copy_location; - bool is_start; - bool move_both = false; - - nframes_t newframe; - if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) { - newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset; - } else { - newframe = 0; - } - - nframes_t next = newframe; - - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { - snap_to (newframe, 0, true); - } - - if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) { - return; - } - - /* call this to find out if its the start or end */ - - 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 */ - - copy_location = drag_info.copied_location; - - f_delta = copy_location->end() - copy_location->start(); - - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) { - move_both = true; - } - - if (copy_location->is_mark()) { - /* just move it */ - - copy_location->set_start (newframe); - - } else { - - if (is_start) { // start-of-range marker - - if (move_both) { - copy_location->set_start (newframe); - copy_location->set_end (newframe + f_delta); - } else if (newframe < copy_location->end()) { - copy_location->set_start (newframe); - } else { - snap_to (next, 1, true); - copy_location->set_end (next); - copy_location->set_start (newframe); - } - - } else { // end marker - - if (move_both) { - copy_location->set_end (newframe); - copy_location->set_start (newframe - f_delta); - } else if (newframe > copy_location->start()) { - copy_location->set_end (newframe); - - } else if (newframe > 0) { - snap_to (next, -1, true); - copy_location->set_start (next); - copy_location->set_end (newframe); - } - } - } - - drag_info.last_pointer_frame = drag_info.current_pointer_frame; - drag_info.first_move = false; - - update_marker_drag_item (copy_location); - - 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); -} - -void -Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event) -{ - if (drag_info.first_move) { - marker_drag_motion_callback (item, event); - - } - - _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 { - location->set (drag_info.copied_location->start(), drag_info.copied_location->end()); - } - } - - XMLNode &after = session->locations()->get_state(); - session->add_command(new MementoCommand(*(session->locations()), &before, &after)); - commit_reversible_command (); - - marker_drag_line->hide(); - range_marker_drag_rect->hide(); -} - -void -Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event) -{ - Marker* marker; - MeterMarker* meter_marker; - - if ((marker = reinterpret_cast (item->get_data ("marker"))) == 0) { - fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg; - /*NOTREACHED*/ - } - - meter_marker = dynamic_cast (marker); - - MetricSection& section (meter_marker->meter()); - - if (!section.movable()) { - return; - } - - 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; - - start_grab (event); - - drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame(); - - show_verbose_time_cursor (drag_info.current_pointer_frame, 10); -} - -void -Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event) -{ - Marker* marker; - MeterMarker* meter_marker; - - if ((marker = reinterpret_cast (item->get_data ("marker"))) == 0) { - fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg; - /*NOTREACHED*/ - } - - meter_marker = dynamic_cast (marker); - - // create a dummy marker for visual representation of moving the copy. - // 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, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name, - *new MeterSection(meter_marker->meter())); - - drag_info.item = &new_marker->the_item(); - drag_info.copy = true; - drag_info.data = new_marker; - drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback; - drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback; - - start_grab (event); - - drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame(); - - show_verbose_time_cursor (drag_info.current_pointer_frame, 10); -} - -void -Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) -{ - MeterMarker* marker = (MeterMarker *) drag_info.data; - nframes_t adjusted_frame; - - if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { - adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset; - } - else { - adjusted_frame = 0; - } - - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { - snap_to (adjusted_frame); - } - - if (adjusted_frame == drag_info.last_pointer_frame) return; - - marker->set_position (adjusted_frame); - - - drag_info.last_pointer_frame = adjusted_frame; - drag_info.first_move = false; - - show_verbose_time_cursor (adjusted_frame, 10); -} - -void -Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event) -{ - if (drag_info.first_move) return; - - meter_marker_drag_motion_callback (drag_info.item, event); - - MeterMarker* marker = (MeterMarker *) drag_info.data; - BBT_Time when; - - TempoMap& map (session->tempo_map()); - map.bbt_time (drag_info.last_pointer_frame, when); - - if (drag_info.copy == true) { - begin_reversible_command (_("copy meter mark")); - XMLNode &before = map.get_state(); - map.add_meter (marker->meter(), when); - XMLNode &after = map.get_state(); - session->add_command(new MementoCommand(map, &before, &after)); - commit_reversible_command (); - - // delete the dummy marker we used for visual representation of copying. - // a new visual marker will show up automatically. - delete marker; - } else { - begin_reversible_command (_("move meter mark")); - XMLNode &before = map.get_state(); - map.move_meter (marker->meter(), when); - XMLNode &after = map.get_state(); - session->add_command(new MementoCommand(map, &before, &after)); - commit_reversible_command (); - } -} - -void -Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event) -{ - Marker* marker; - TempoMarker* tempo_marker; - - if ((marker = reinterpret_cast (item->get_data ("marker"))) == 0) { - fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg; - /*NOTREACHED*/ - } - - if ((tempo_marker = dynamic_cast (marker)) == 0) { - fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg; - /*NOTREACHED*/ - } - - MetricSection& section (tempo_marker->tempo()); - - if (!section.movable()) { - return; - } - - 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; - - start_grab (event); - - drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame(); - show_verbose_time_cursor (drag_info.current_pointer_frame, 10); -} - -void -Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event) -{ - Marker* marker; - TempoMarker* tempo_marker; - - if ((marker = reinterpret_cast (item->get_data ("marker"))) == 0) { - fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg; - /*NOTREACHED*/ - } - - if ((tempo_marker = dynamic_cast (marker)) == 0) { - fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg; - /*NOTREACHED*/ - } - - // create a dummy marker for visual representation of moving the copy. - // 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, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name, - *new TempoSection(tempo_marker->tempo())); - - drag_info.item = &new_marker->the_item(); - drag_info.copy = true; - drag_info.data = new_marker; - drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback; - drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback; - - start_grab (event); - - drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame(); - - show_verbose_time_cursor (drag_info.current_pointer_frame, 10); -} - -void -Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) -{ - TempoMarker* marker = (TempoMarker *) drag_info.data; - nframes_t adjusted_frame; - - if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { - adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset; - } - else { - adjusted_frame = 0; - } - - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { - snap_to (adjusted_frame); - } - - if (adjusted_frame == drag_info.last_pointer_frame) return; - - /* OK, we've moved far enough to make it worth actually move the thing. */ - - marker->set_position (adjusted_frame); - - show_verbose_time_cursor (adjusted_frame, 10); - - drag_info.last_pointer_frame = adjusted_frame; - drag_info.first_move = false; -} - -void -Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event) -{ - if (drag_info.first_move) return; - - tempo_marker_drag_motion_callback (drag_info.item, event); - - TempoMarker* marker = (TempoMarker *) drag_info.data; - BBT_Time when; - - TempoMap& map (session->tempo_map()); - map.bbt_time (drag_info.last_pointer_frame, when); - - if (drag_info.copy == true) { - begin_reversible_command (_("copy tempo mark")); - XMLNode &before = map.get_state(); - map.add_tempo (marker->tempo(), when); - XMLNode &after = map.get_state(); - session->add_command (new MementoCommand(map, &before, &after)); - commit_reversible_command (); - - // delete the dummy marker we used for visual representation of copying. - // a new visual marker will show up automatically. - delete marker; - } else { - begin_reversible_command (_("move tempo mark")); - XMLNode &before = map.get_state(); - map.move_tempo (marker->tempo(), when); - XMLNode &after = map.get_state(); - session->add_command (new MementoCommand(map, &before, &after)); - commit_reversible_command (); - } -} - -void -Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event) -{ - ControlPoint* control_point; - - if ((control_point = reinterpret_cast (item->get_data ("control_point"))) == 0) { - fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg; - /*NOTREACHED*/ - } - - // We shouldn't remove the first or last gain point - if (control_point->line().is_last_point(*control_point) || - control_point->line().is_first_point(*control_point)) { - return; - } - - control_point->line().remove_point (*control_point); -} - -void -Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event) -{ - ControlPoint* control_point; - - if ((control_point = reinterpret_cast (item->get_data ("control_point"))) == 0) { - fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg; - /*NOTREACHED*/ - } - - control_point->line().remove_point (*control_point); -} - -void -Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event) -{ - ControlPoint* control_point; - - if ((control_point = reinterpret_cast (item->get_data ("control_point"))) == 0) { - fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg; - /*NOTREACHED*/ - } - - drag_info.item = item; - drag_info.data = control_point; - drag_info.motion_callback = &Editor::control_point_drag_motion_callback; - drag_info.finished_callback = &Editor::control_point_drag_finished_callback; - - 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); - - double fraction = 1.0 - ((control_point->get_y() - control_point->line().y_position()) / (double)control_point->line().height()); - set_verbose_canvas_cursor (control_point->line().get_verbose_cursor_string (fraction), - drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20); - - show_verbose_canvas_cursor (); -} - -void -Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) -{ - ControlPoint* cp = reinterpret_cast (drag_info.data); - - 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::Alt) { - dx *= 0.1; - dy *= 0.1; - } - - 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; - } - if (drag_info.y_constrained) { - 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); - cy = max (0.0, cy); - cy = min ((double) (cp->line().y_position() + cp->line().height()), cy); - - //translate cx to frames - nframes_t cx_frames = unit_to_frame (cx); - - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) { - snap_to (cx_frames); - } - - const double fraction = 1.0 - ((cy - cp->line().y_position()) / (double)cp->line().height()); - - bool push; - - if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) { - push = true; - } else { - push = false; - } - - cp->line().point_drag (*cp, cx_frames , fraction, push); - - set_verbose_canvas_cursor_text (cp->line().get_verbose_cursor_string (fraction)); - - drag_info.first_move = false; -} - -void -Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event) -{ - ControlPoint* cp = reinterpret_cast (drag_info.data); - - if (drag_info.first_move) { - - /* just a click */ - - if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) { - reset_point_selection (); - } - - } else { - control_point_drag_motion_callback (item, event); - } - cp->line().end_drag (cp); -} - -void -Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event) -{ - switch (mouse_mode) { - case MouseGain: - assert(dynamic_cast(clicked_regionview)); - start_line_grab (dynamic_cast(clicked_regionview)->get_gain_line(), event); - break; - default: - break; - } -} - -void -Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event) -{ - AutomationLine* al; - - if ((al = reinterpret_cast (item->get_data ("line"))) == 0) { - fatal << _("programming error: line canvas item has no line pointer!") << endmsg; - /*NOTREACHED*/ - } - - start_line_grab (al, event); -} - -void -Editor::start_line_grab (AutomationLine* line, GdkEvent* event) -{ - double cx; - double cy; - nframes_t frame_within_region; - - /* need to get x coordinate in terms of parent (TimeAxisItemView) - origin. - */ - - cx = event->button.x; - cy = event->button.y; - line->parent_group().w2i (cx, cy); - frame_within_region = (nframes_t) floor (cx * frames_per_unit); - - if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before, - current_line_drag_info.after)) { - /* no adjacent points */ - return; - } - - drag_info.item = &line->grab_item(); - drag_info.data = line; - drag_info.motion_callback = &Editor::line_drag_motion_callback; - drag_info.finished_callback = &Editor::line_drag_finished_callback; - - start_grab (event, fader_cursor); - - const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height()); - - line->start_drag (0, drag_info.grab_frame, fraction); - - set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction), - drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20); - show_verbose_canvas_cursor (); -} - -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::Alt) { - dy *= 0.1; - } - - double cx = drag_info.current_pointer_x; - 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); - - const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height()); - - bool push; - - if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) { - push = false; - } else { - push = true; - } - - line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push); - - set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction)); -} - -void -Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event) -{ - AutomationLine* line = reinterpret_cast (drag_info.data); - line_drag_motion_callback (item, event); - line->end_drag (0); -} - -void -Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event) -{ - if (selection->regions.empty() || clicked_regionview == 0) { - return; - } - - 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; - - start_grab (event); - - double speed = 1.0; - TimeAxisView* tvp = clicked_axisview; - RouteTimeAxisView* tv = dynamic_cast(tvp); - - if (tv && tv->is_track()) { - speed = tv->get_diskstream()->speed(); - } - - 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(); - // we want a move threshold - drag_info.want_move_threshold = true; - - show_verbose_time_cursor (drag_info.last_frame_position, 10); - - begin_reversible_command (_("move region(s)")); -} - -void -Editor::start_create_region_grab (ArdourCanvas::Item* item, GdkEvent* event) -{ - drag_info.copy = false; - drag_info.item = item; - drag_info.data = clicked_axisview; - drag_info.last_trackview = clicked_axisview; - drag_info.motion_callback = &Editor::create_region_drag_motion_callback; - drag_info.finished_callback = &Editor::create_region_drag_finished_callback; - - start_grab (event); -} - -void -Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event) -{ - if (selection->regions.empty() || clicked_regionview == 0) { - return; - } - - drag_info.copy = true; - drag_info.item = item; - drag_info.data = clicked_regionview; - - start_grab(event); - - TimeAxisView* tv = &clicked_regionview->get_time_axis_view(); - RouteTimeAxisView* rtv = dynamic_cast(tv); - double speed = 1.0; - - if (rtv && rtv->is_track()) { - speed = rtv->get_diskstream()->speed(); - } - - drag_info.last_trackview = &clicked_regionview->get_time_axis_view(); - 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 - drag_info.want_move_threshold = true; - drag_info.motion_callback = &Editor::region_drag_motion_callback; - drag_info.finished_callback = &Editor::region_drag_finished_callback; - show_verbose_time_cursor (drag_info.last_frame_position, 10); -} - -void -Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event) -{ - if (selection->regions.empty() || clicked_regionview == 0) { - return; - } - - 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; - - start_grab (event); - - double speed = 1.0; - TimeAxisView* tvp = clicked_axisview; - RouteTimeAxisView* tv = dynamic_cast(tvp); - - if (tv && tv->is_track()) { - speed = tv->get_diskstream()->speed(); - } - - 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(); - // we want a move threshold - drag_info.want_move_threshold = true; - drag_info.brushing = true; - - begin_reversible_command (_("Drag region brush")); -} - -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; - - if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) { - - drag_info.want_move_threshold = false; // don't copy again + } else { - /* duplicate the region(s) */ + if (last_scrub_x > _drag->current_pointer_x()) { - vector new_regionviews; - - for (list::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) { + /* pointer moved to the left */ - RegionView* rv; - RegionView* nrv; + if (scrubbing_direction > 0) { - rv = (*i); + /* we reversed direction to go backwards */ - AudioRegionView* arv = dynamic_cast(rv); - MidiRegionView* mrv = dynamic_cast(rv); + scrub_reversals++; + scrub_reverse_distance += (int) (last_scrub_x - _drag->current_pointer_x()); - if (arv) { - nrv = new AudioRegionView (*arv); - } else if (mrv) { - nrv = new MidiRegionView (*mrv); } else { - continue; - } - - nrv->get_canvas_group()->show (); - - new_regionviews.push_back (nrv); - } - - if (new_regionviews.empty()) { - return; - } - - /* reset selection to new regionviews */ - - selection->set (new_regionviews); - - /* 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); - } - - /* Which trackview is this ? */ - - TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y); - RouteTimeAxisView* tv = dynamic_cast(tvp); - - /* The region motion is only processed if the pointer is over - an audio track. - */ - - if (!tv || !tv->is_track()) { - /* To make sure we hide the verbose canvas cursor when the mouse is - not held over a track. - */ - hide_verbose_canvas_cursor (); - return; - } - - original_pointer_order = drag_info.last_trackview->order; - - /************************************************************ - Y-Delta Computation - ************************************************************/ - - if (drag_info.brushing) { - clamp_y_axis = true; - pointer_y_span = 0; - goto y_axis_done; - } - - if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) { - int32_t children = 0, numtracks = 0; - // XXX hard coding track limit, oh my, so very very bad - bitset <1024> tracks (0x00); - /* get a bitmask representing the visible tracks */ + /* still moving to the left (backwards) */ - for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { - TimeAxisView *tracklist_timeview; - tracklist_timeview = (*i); - RouteTimeAxisView* rtv2 = dynamic_cast(tracklist_timeview); - TimeAxisView::Children children_list; - - /* zeroes are audio tracks. ones are other types. */ - - if (!rtv2->hidden()) { - - if (visible_y_high < rtv2->order) { - visible_y_high = rtv2->order; - } - if (visible_y_low > rtv2->order) { - visible_y_low = rtv2->order; - } - - if (!rtv2->is_track()) { - tracks = tracks |= (0x01 << rtv2->order); - } - - height_list[rtv2->order] = (*i)->height; - children = 1; - if ((children_list = rtv2->get_child_list()).size() > 0) { - for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) { - tracks = tracks |= (0x01 << (rtv2->order + children)); - height_list[rtv2->order + children] = (*j)->height; - numtracks++; - children++; - } - } - numtracks++; - } - } - /* find the actual span according to the canvas */ - - canvas_pointer_y_span = pointer_y_span; - if (drag_info.last_trackview->order >= tv->order) { - int32_t y; - for (y = tv->order; y < drag_info.last_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++) { - if ( height_list[y] == 0 ) { - canvas_pointer_y_span++; - } - } - } + scrub_reversals = 0; + scrub_reverse_distance = 0; - for (list::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) { - RegionView* rv2 = (*i); - double ix1, ix2, iy1, iy2; - int32_t n = 0; - - rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2); - rv2->get_canvas_group()->i2w (ix1, iy1); - TimeAxisView* tvp2 = trackview_by_y_position (iy1); - RouteTimeAxisView* rtv2 = dynamic_cast(tvp2); - - if (rtv2->order != original_pointer_order) { - /* this isn't the pointer track */ - - if (canvas_pointer_y_span > 0) { - - /* moving up the canvas */ - if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) { - - int32_t visible_tracks = 0; - while (visible_tracks < canvas_pointer_y_span ) { - visible_tracks++; - - while (height_list[rtv2->order - (visible_tracks - n)] == 0) { - /* we're passing through a hidden track */ - n--; - } - } - - if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) { - clamp_y_axis = true; - } - - } else { - clamp_y_axis = true; - } - - } else if (canvas_pointer_y_span < 0) { - - /*moving down the canvas*/ - - if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow - - - int32_t visible_tracks = 0; - - while (visible_tracks > canvas_pointer_y_span ) { - visible_tracks--; - - while (height_list[rtv2->order - (visible_tracks - n)] == 0) { - n++; - } - } - if ( tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) { - clamp_y_axis = true; - - } - } else { - - clamp_y_axis = true; - } - } - - } else { - - /* this is the pointer's track */ - if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow - clamp_y_axis = true; - } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow - clamp_y_axis = true; - } - } - if (clamp_y_axis) { - break; + delta = 0.01 * (last_scrub_x - _drag->current_pointer_x()); + session->request_transport_speed (session->transport_speed() - delta); } - } - - } else if (drag_info.last_trackview == tv) { - clamp_y_axis = true; - } - - y_axis_done: - if (!clamp_y_axis) { - drag_info.last_trackview = tv; - } - - /************************************************************ - X DELTA COMPUTATION - ************************************************************/ - /* compute the amount of pointer motion in frames, and where - the region would be if we moved it by that much. - */ + } else { + /* pointer moved to the right */ - if (drag_info.move_threshold_passed) { + if (scrubbing_direction < 0) { + /* we reversed direction to go forward */ - if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { + scrub_reversals++; + scrub_reverse_distance += (int) (_drag->current_pointer_x() - last_scrub_x); - 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; + } else { + /* still moving to the right */ - sync_offset = rv->region()->sync_offset (sync_dir); - sync_frame = rv->region()->adjust_to_sync (pending_region_position); + scrub_reversals = 0; + scrub_reverse_distance = 0; - /* 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 (sync_frame - sync_offset <= sync_frame) { - pending_region_position = sync_frame - (sync_dir*sync_offset); - } else { - pending_region_position = 0; + delta = 0.01 * (_drag->current_pointer_x() - last_scrub_x); + session->request_transport_speed (session->transport_speed() + delta); } - - } else { - pending_region_position = 0; } - - if (pending_region_position > max_frames - rv->region()->length()) { - pending_region_position = drag_info.last_frame_position; - } - - // 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 - to make it appear at the new location. - */ + /* 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 (pending_region_position > drag_info.last_frame_position) { - x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit); + if (scrubbing_direction > 0) { + /* was forwards, go backwards */ + session->request_transport_speed (-0.1); + scrubbing_direction = -1; } else { - x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit); + /* was backwards, go forwards */ + session->request_transport_speed (0.1); + scrubbing_direction = 1; } - drag_info.last_frame_position = pending_region_position; - - } else { - x_delta = 0; + scrub_reverse_distance = 0; + scrub_reversals = 0; } - - } else { - /* threshold not passed */ - - x_delta = 0; } - /************************************************************* - PREPARE TO MOVE - ************************************************************/ + last_scrub_x = _drag->current_pointer_x(); +} - if (x_delta == 0 && (pointer_y_span == 0)) { - /* haven't reached next snap point, and we're not switching - trackviews. nothing to do. +bool +Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, 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. */ - return; - } - - if (x_delta < 0) { - for (list::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) { - - RegionView* rv2 = (*i); - - // If any regionview is at zero, we need to know so we can stop further leftward motion. - - double ix1, ix2, iy1, iy2; - rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2); - rv2->get_canvas_group()->i2w (ix1, iy1); - - if (ix1 <= 1) { - x_delta = 0; - break; - } - } + track_canvas->get_pointer (x, y); } - /************************************************************* - MOTION - ************************************************************/ - - bool do_move; - - if (drag_info.first_move) { - if (drag_info.move_threshold_passed) { - do_move = true; - } else { - do_move = false; - } - } else { - do_move = true; + if (current_stepping_trackview) { + /* don't keep the persistent stepped trackview if the mouse moves */ + current_stepping_trackview = 0; + step_timeout.disconnect (); } - if (do_move) { - - 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; - - /* 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. - */ - - rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2); - rv->get_canvas_group()->i2w (ix1, iy1); - TimeAxisView* tvp2 = trackview_by_y_position (iy1); - RouteTimeAxisView* canvas_rtv = dynamic_cast(tvp2); - RouteTimeAxisView* temp_rtv; - - if ((pointer_y_span != 0) && !clamp_y_axis) { - y_delta = 0; - int32_t x = 0; - for (j = height_list.begin(); j!= height_list.end(); j++) { - if (x == canvas_rtv->order) { - /* we found the track the region is on */ - if (x != original_pointer_order) { - /*this isn't from the same track we're dragging from */ - temp_pointer_y_span = canvas_pointer_y_span; - } - while (temp_pointer_y_span > 0) { - /* we're moving up canvas-wise, - so we need to find the next track height - */ - if (j != height_list.begin()) { - j--; - } - if (x != original_pointer_order) { - /* we're not from the dragged track, so ignore hidden tracks. */ - if ((*j) == 0) { - temp_pointer_y_span++; - } - } - y_delta -= (*j); - temp_pointer_y_span--; - } - while (temp_pointer_y_span < 0) { - y_delta += (*j); - if (x != original_pointer_order) { - if ((*j) == 0) { - temp_pointer_y_span--; - } - } - - if (j != height_list.end()) { - j++; - } - temp_pointer_y_span++; - } - /* find out where we'll be when we move and set height accordingly */ - - tvp2 = trackview_by_y_position (iy1 + y_delta); - temp_rtv = dynamic_cast(tvp2); - rv->set_y_position_and_height (0, temp_rtv->height); - - /* if you un-comment the following, the region colours will follow the track colours whilst dragging, - personally, i think this can confuse things, but never mind. - */ - - //const GdkColor& col (temp_rtv->view->get_region_color()); - //rv->set_color (const_cast(col)); - break; - } - x++; - } - } - - /* prevent the regionview from being moved to before - the zero position on the canvas. - */ - /* clamp */ - - if (x_delta < 0) { - if (-x_delta > ix1) { - x_delta = -ix1; - } - } 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 */ - - rv->get_time_axis_view().hide_dependent_views (*rv); - - /* this is subtle. raising the regionview itself won't help, - because raise_to_top() just puts the item on the top of - its parent's stack. so, we need to put the trackview canvas_display group - on the top, since its parent is the whole canvas. - */ - - 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 { - rv->move (x_delta, y_delta); - } - - } /* foreach region */ - - } /* if do_move */ - - if (drag_info.first_move && drag_info.move_threshold_passed) { - cursor_group->raise_to_top(); - drag_info.first_move = false; + if (session && session->actively_recording()) { + /* Sorry. no dragging stuff around while we record */ + return true; } - if (x_delta != 0 && !drag_info.brushing) { - show_verbose_time_cursor (drag_info.last_frame_position, 10); + bool handled = false; + if (_drag) { + handled = _drag->motion_handler (event, from_autoscroll); } -} - -void -Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event) -{ - nframes_t where; - RegionView* rvdi = reinterpret_cast (drag_info.data); - pair >::iterator,bool> insert_result; - bool nocommit = true; - double speed; - RouteTimeAxisView* rtv; - bool regionview_y_movement; - bool regionview_x_movement; - vector copies; - list > used_playlists; - list used_connections; - bool preserve_selection = false; - - /* first_move is set to false if the regionview has been moved in the - motion handler. - */ - if (drag_info.first_move) { - /* just a click */ - goto out; + if (!handled) { + return false; } - nocommit = false; - - /* The regionview has been moved at some stage during the grab so we need - to account for any mouse movement between this event and the last one. - */ - - region_drag_motion_callback (item, event); + track_canvas_motion (event); + return true; +} - 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); - } - } +void +Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/) +{ + ControlPoint* control_point; - goto out; + if ((control_point = reinterpret_cast (item->get_data ("control_point"))) == 0) { + fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg; + /*NOTREACHED*/ } - /* adjust for track speed */ - speed = 1.0; - - rtv = dynamic_cast (drag_info.last_trackview); - if (rtv && rtv->get_diskstream()) { - speed = rtv->get_diskstream()->speed(); - } - - regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rvdi->region()->position()/speed)); - regionview_y_movement = (drag_info.last_trackview != &rvdi->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) { - if (drag_info.x_constrained) { - op_string = _("fixed time region copy"); - } else { - op_string = _("region copy"); - } - } else { - if (drag_info.x_constrained) { - op_string = _("fixed time region drag"); - } else { - op_string = _("region drag"); - } + // We shouldn't remove the first or last gain point + if (control_point->line().is_last_point(*control_point) || + control_point->line().is_first_point(*control_point)) { + return; } - begin_reversible_command (op_string); - - if (regionview_y_movement) { - - /* moved to a different audio track. */ - - 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); - - RouteTimeAxisView* rtv2 = dynamic_cast(trackview_by_y_position (iy1)); - - boost::shared_ptr to_playlist = rtv2->playlist(); - - if (! to_playlist->frozen()) { - /* - we haven't seen this playlist before. - we want to freeze it because we don't want to relayer per-region. - its much better to do that just once if the playlist is large. - */ - - /* - connect so the selection is changed when the new regionview finally appears (after thaw). - keep track of it so we can disconnect later. - */ - - sigc::connection c = rtv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_and_select_new_region_view)); - used_connections.push_back (c); - - /* undo */ - session->add_command (new MementoCommand(*to_playlist, &to_playlist->get_state(), 0)); - - /* remember used playlists so we can thaw them later */ - used_playlists.push_back(to_playlist); - to_playlist->freeze(); - } - - where = (nframes_t) (unit_to_frame (ix1) * speed); - boost::shared_ptr new_region (RegionFactory::create (rv->region())); - - 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. - */ - - RouteTimeAxisView* from_playlist_rtv = dynamic_cast(&(*i)->get_trackview()); - boost::shared_ptr from_playlist = from_playlist_rtv->playlist(); - - if (! from_playlist->frozen()) { - from_playlist->freeze(); - used_playlists.push_back(from_playlist); - - sigc::connection c = rtv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_and_select_new_region_view)); - used_connections.push_back (c); - - session->add_command (new MementoCommand(*from_playlist, &from_playlist->get_state(), 0)); - } - - rv->hide_region_editor(); - rv->fake_set_opaque (false); - - from_playlist->remove_region ((rv->region())); - - } else { - - /* the regionview we dragged around is a temporary copy, queue it for deletion */ - - copies.push_back (rv); - } - - latest_regionview = 0; - - to_playlist->add_region (new_region, where); - - /* 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 - 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). - - this could have invalidated any and all iterators into the region selection. - - the heuristic we use here is: if the region selection is empty, break out of the loop - 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 (drag_info.copy) { - ++i; - } else { - if (selection->regions.empty()) { - break; - } else { - /* - XXX see above .. but we just froze the playlists.. we have to keep iterating, right? - */ - - //i = selection->regions.by_layer().begin(); - ++i; - } - } - } - - } else { - - /* motion within a single track */ - - list regions = selection->regions.by_layer(); - - for (list::iterator i = regions.begin(); i != regions.end(); ++i) { - - RegionView* rv = (*i); - boost::shared_ptr to_playlist = (*i)->region()->playlist(); - RouteTimeAxisView* from_rtv = dynamic_cast (&(rv->get_time_axis_view())); - - if (!rv->region()->can_move()) { - continue; - } - - if (regionview_x_movement) { - double ownspeed = 1.0; - - if (from_rtv && from_rtv->get_diskstream()) { - ownspeed = from_rtv->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(); - } - - if (! to_playlist->frozen()) { - sigc::connection c = from_rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_and_select_new_region_view)); - used_connections.push_back (c); - - /* add the undo */ - session->add_command (new MementoCommand(*to_playlist, &to_playlist->get_state(), 0)); - - used_playlists.push_back(to_playlist); - to_playlist->freeze(); - } - - if (drag_info.copy) { - - boost::shared_ptr newregion; - boost::shared_ptr ar; - boost::shared_ptr mr; - - if ((ar = boost::dynamic_pointer_cast(rv->region())) != 0) { - newregion = RegionFactory::create (ar); - } else if ((mr = boost::dynamic_pointer_cast(rv->region())) != 0) { - newregion = RegionFactory::create (mr); - } - - /* add it */ - - to_playlist->add_region (newregion, (nframes_t) (where * from_rtv->get_diskstream()->speed())); - - /* if the original region was locked, we don't care for the new one */ - - newregion->set_locked (false); - copies.push_back (rv); - - } else { - - /* just change the model */ - - rv->region()->set_position (where, (void*) this); - preserve_selection = true; - - } + control_point->line().remove_point (*control_point); +} - } +void +Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/) +{ + ControlPoint* control_point; + if ((control_point = reinterpret_cast (item->get_data ("control_point"))) == 0) { + fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg; + /*NOTREACHED*/ } - if (! preserve_selection) { - //selection->clear_regions(); - } - while (used_playlists.size() > 0) { - list >::iterator i = used_playlists.begin(); - (*i)->thaw(); + control_point->line().remove_point (*control_point); +} - if (used_connections.size()) { - sigc::connection c = used_connections.front(); - c.disconnect(); - used_connections.pop_front(); - } - /* add the redo */ +void +Editor::edit_control_point (ArdourCanvas::Item* item) +{ + ControlPoint* p = reinterpret_cast (item->get_data ("control_point")); - session->add_command (new MementoCommand(*(*i), 0, &(*i)->get_state())); - used_playlists.pop_front(); + if (p == 0) { + fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg; + /*NOTREACHED*/ } - out: - - if (!nocommit) { - commit_reversible_command (); - } + ControlPointDialog d (p); + d.set_position (Gtk::WIN_POS_MOUSE); + ensure_float (d); - for (vector::iterator x = copies.begin(); x != copies.end(); ++x) { - delete *x; + if (d.run () != RESPONSE_ACCEPT) { + return; } + + p->line().modify_point_y (*p, d.get_y_fraction ()); } - + void -Editor::create_region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) +Editor::visible_order_range (int* low, int* high) const { - if (drag_info.move_threshold_passed) { - if (drag_info.first_move) { - // TODO: create region-create-drag region view here - drag_info.first_move = false; - } + *low = TimeAxisView::max_order (); + *high = 0; - // TODO: resize region-create-drag region view here - } -} + for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) { -void -Editor::create_region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event) -{ - MidiTimeAxisView* mtv = dynamic_cast (drag_info.last_trackview); - if (!mtv) - return; + RouteTimeAxisView* rtv = dynamic_cast (*i); - const boost::shared_ptr diskstream = - boost::dynamic_pointer_cast(mtv->view()->trackview().track()->diskstream()); - - if (!diskstream) { - warning << "Cannot create non-MIDI region" << endl; - return; - } + if (!rtv->hidden()) { - if (drag_info.first_move) { - begin_reversible_command (_("create region")); - XMLNode &before = mtv->playlist()->get_state(); - - nframes_t start = drag_info.grab_frame; - snap_to (start, -1); - const Meter& m = session->tempo_map().meter_at(start); - const Tempo& t = session->tempo_map().tempo_at(start); - double length = floor (m.frames_per_bar(t, session->frame_rate())); - - boost::shared_ptr src = session->create_midi_source_for_session(*diskstream.get()); - - mtv->playlist()->add_region (boost::dynamic_pointer_cast - (RegionFactory::create(src, 0, (nframes_t) length, - PBD::basename_nosuffix(src->name()))), start); - XMLNode &after = mtv->playlist()->get_state(); - session->add_command(new MementoCommand(*mtv->playlist().get(), &before, &after)); - commit_reversible_command(); + if (*high < rtv->order()) { + *high = rtv->order (); + } - } else { - create_region_drag_motion_callback (item, event); - // TODO: create region-create-drag region here + if (*low > rtv->order()) { + *low = rtv->order (); + } + } } } @@ -3801,8 +1881,8 @@ Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event) /* Either add to or set the set the region selection, unless 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(); RouteTimeAxisView* rtv = dynamic_cast(tv); double speed = 1.0; @@ -3814,45 +1894,53 @@ Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event) if (where >= 0) { - if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) { - - align_region (rv.region(), SyncPoint, (nframes_t) (where * speed)); - - } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) { - - align_region (rv.region(), End, (nframes_t) (where * speed)); - + if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) { + + align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed)); + + } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) { + + align_region (rv.region(), End, (nframes64_t) (where * speed)); + } else { - - align_region (rv.region(), Start, (nframes_t) (where * speed)); + + align_region (rv.region(), Start, (nframes64_t) (where * speed)); } } } } void -Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos) +Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos) { char buf[128]; - SMPTE::Time smpte; + Timecode::Time timecode; BBT_Time bbt; int hours, mins; - nframes_t frame_rate; + nframes64_t frame_rate; float secs; if (session == 0) { 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); break; - - case AudioClock::SMPTE: - session->smpte_time (frame, smpte); - snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames); + + case AudioClock::Timecode: + session->timecode_time (frame, timecode); + snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames); break; case AudioClock::MinSec: @@ -3867,28 +1955,27 @@ Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, d break; default: - snprintf (buf, sizeof(buf), "%u", frame); + snprintf (buf, sizeof(buf), "%" PRIi64, frame); break; } if (xpos >= 0 && ypos >=0) { set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset); - } - else { - set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset); + } else { + set_verbose_canvas_cursor (buf, _drag->current_pointer_x() + offset - horizontal_adjustment.get_value(), _drag->current_pointer_y() + offset - vertical_adjustment.get_value() + canvas_timebars_vsize); } show_verbose_canvas_cursor (); } void -Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos) +Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos) { char buf[128]; - SMPTE::Time smpte; + Timecode::Time timecode; BBT_Time sbbt; BBT_Time ebbt; int hours, mins; - nframes_t distance, frame_rate; + nframes64_t distance, frame_rate; float secs; Meter meter_at_start(session->tempo_map().meter_at(start)); @@ -3896,7 +1983,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); @@ -3919,13 +2014,13 @@ Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double off ebbt.beats--; ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks; } - + snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks); break; - - case AudioClock::SMPTE: - session->smpte_duration (end - start, smpte); - snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames); + + case AudioClock::Timecode: + session->timecode_duration (end - start, timecode); + snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames); break; case AudioClock::MinSec: @@ -3941,7 +2036,7 @@ Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double off break; default: - snprintf (buf, sizeof(buf), "%u", end - start); + snprintf (buf, sizeof(buf), "%" PRIi64, end - start); break; } @@ -3949,92 +2044,23 @@ Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double off set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset); } else { - set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset); + set_verbose_canvas_cursor (buf, _drag->current_pointer_x() + offset, _drag->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 Editor::collect_and_select_new_region_view (RegionView* rv) { selection->add(rv); - latest_regionview = rv; -} - -void -Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event) -{ - if (clicked_regionview == 0) { - return; - } - - /* lets try to create new Region for the selection */ - - vector > new_regions; - create_region_from_selection (new_regions); - - if (new_regions.empty()) { - return; - } - - /* XXX fix me one day to use all new regions */ - - boost::shared_ptr region (new_regions.front()); - - /* add it to the current stream/playlist. - - tricky: the streamview for the track will add a new regionview. we will - catch the signal it sends when it creates the regionview to - set the regionview we want to then drag. - */ - - latest_regionview = 0; - sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view)); - - /* A selection grab currently creates two undo/redo operations, one for - creating the new region and another for moving it. - */ - - begin_reversible_command (_("selection grab")); - - boost::shared_ptr playlist = clicked_axisview->playlist(); - - XMLNode *before = &(playlist->get_state()); - clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start); - XMLNode *after = &(playlist->get_state()); - session->add_command(new MementoCommand(*playlist, before, after)); - - commit_reversible_command (); - - c.disconnect (); - - if (latest_regionview == 0) { - /* 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); - - drag_info.item = latest_regionview->get_canvas_group(); - drag_info.data = latest_regionview; - 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_axisview; - drag_info.last_frame_position = latest_regionview->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); + latest_regionviews.push_back (rv); } void @@ -4043,404 +2069,14 @@ 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 -Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op) -{ - nframes_t start = 0; - nframes_t end = 0; - - if (session == 0) { - return; - } - - drag_info.item = item; - drag_info.motion_callback = &Editor::drag_selection; - drag_info.finished_callback = &Editor::end_selection_op; - - selection_op = op; - - switch (op) { - case CreateSelection: - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) { - drag_info.copy = true; - } else { - drag_info.copy = false; - } - start_grab (event, selector_cursor); - break; - - case SelectionStartTrim: - if (clicked_axisview) { - clicked_axisview->order_selection_trims (item, true); - } - start_grab (event, trimmer_cursor); - start = selection->time[clicked_selection].start; - drag_info.pointer_frame_offset = drag_info.grab_frame - start; - break; - - case SelectionEndTrim: - if (clicked_axisview) { - clicked_axisview->order_selection_trims (item, false); - } - start_grab (event, trimmer_cursor); - end = selection->time[clicked_selection].end; - drag_info.pointer_frame_offset = drag_info.grab_frame - end; - break; - - case SelectionMove: - start = selection->time[clicked_selection].start; - start_grab (event); - drag_info.pointer_frame_offset = drag_info.grab_frame - start; - break; - } - - if (selection_op == SelectionMove) { - show_verbose_time_cursor(start, 10); - } else { - show_verbose_time_cursor(drag_info.current_pointer_frame, 10); - } -} - -void -Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event) -{ - nframes_t start = 0; - nframes_t end = 0; - nframes_t length; - nframes_t pending_position; - - if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { - pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset; - } else { - pending_position = 0; - } - - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { - snap_to (pending_position); - } - - /* only alter selection if the current frame is - different from the last frame position (adjusted) - */ - - if (pending_position == drag_info.last_pointer_frame) return; - - switch (selection_op) { - case CreateSelection: - - if (drag_info.first_move) { - snap_to (drag_info.grab_frame); - } - - if (pending_position < drag_info.grab_frame) { - start = pending_position; - end = drag_info.grab_frame; - } else { - end = pending_position; - start = drag_info.grab_frame; - } - - /* first drag: Either add to the selection - or create a new selection-> - */ - - if (drag_info.first_move) { - - begin_reversible_command (_("range selection")); - - if (drag_info.copy) { - /* adding to the selection */ - clicked_selection = selection->add (start, end); - drag_info.copy = false; - } else { - /* new selection-> */ - clicked_selection = selection->set (clicked_axisview, start, end); - } - } - break; - - case SelectionStartTrim: - - if (drag_info.first_move) { - begin_reversible_command (_("trim selection start")); - } - - start = selection->time[clicked_selection].start; - end = selection->time[clicked_selection].end; - - if (pending_position > end) { - start = end; - } else { - start = pending_position; - } - break; - - case SelectionEndTrim: - - if (drag_info.first_move) { - begin_reversible_command (_("trim selection end")); - } - - start = selection->time[clicked_selection].start; - end = selection->time[clicked_selection].end; - - if (pending_position < start) { - end = start; - } else { - end = pending_position; - } - - break; - - case SelectionMove: - - if (drag_info.first_move) { - begin_reversible_command (_("move selection")); - } - - start = selection->time[clicked_selection].start; - end = selection->time[clicked_selection].end; - - length = end - start; - - start = pending_position; - snap_to (start); - - end = start + length; - - break; - } - - if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) { - start_canvas_autoscroll (1); - } - - if (start != end) { - selection->replace (clicked_selection, start, end); - } - - drag_info.last_pointer_frame = pending_position; - drag_info.first_move = false; - - if (selection_op == SelectionMove) { - show_verbose_time_cursor(start, 10); - } else { - show_verbose_time_cursor(pending_position, 10); - } -} - -void -Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event) -{ - if (!drag_info.first_move) { - drag_selection (item, event); - /* XXX this is not object-oriented programming at all. ick */ - if (selection->time.consolidate()) { - selection->TimeChanged (); - } - commit_reversible_command (); - } else { - /* just a click, no pointer movement.*/ - - if (Keyboard::no_modifier_keys_pressed (&event->button)) { - - selection->clear_time(); - - } - } - - /* XXX what happens if its a music selection? */ - session->set_audio_range (selection->time); - stop_canvas_autoscroll (); -} - -void -Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event) -{ - double speed = 1.0; - TimeAxisView* tvp = clicked_axisview; - RouteTimeAxisView* tv = dynamic_cast(tvp); - - if (tv && tv->is_track()) { - speed = tv->get_diskstream()->speed(); - } - - nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed); - nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed); - nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed); - - //drag_info.item = clicked_regionview->get_name_highlight(); - drag_info.item = item; - drag_info.motion_callback = &Editor::trim_motion_callback; - drag_info.finished_callback = &Editor::trim_finished_callback; - - start_grab (event, trimmer_cursor); - - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) { - trim_op = ContentsTrim; - } else { - /* These will get overridden for a point trim.*/ - if (drag_info.current_pointer_frame < (region_start + region_length/2)) { - /* closer to start */ - trim_op = StartTrim; - } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) { - /* closer to end */ - trim_op = EndTrim; - } - } - - switch (trim_op) { - case StartTrim: - show_verbose_time_cursor(region_start, 10); - break; - case EndTrim: - show_verbose_time_cursor(region_end, 10); - break; - case ContentsTrim: - show_verbose_time_cursor(drag_info.current_pointer_frame, 10); - break; - } } -void -Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) -{ - RegionView* rv = clicked_regionview; - nframes_t frame_delta = 0; - bool left_direction; - bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()); - - /* snap modifier works differently here.. - its' current state has to be passed to the - various trim functions in order to work properly - */ - - double speed = 1.0; - TimeAxisView* tvp = clicked_axisview; - RouteTimeAxisView* tv = dynamic_cast(tvp); - pair >::iterator,bool> insert_result; - - if (tv && tv->is_track()) { - speed = tv->get_diskstream()->speed(); - } - - if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) { - left_direction = true; - } else { - left_direction = false; - } - - if (obey_snap) { - snap_to (drag_info.current_pointer_frame); - } - - if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) { - return; - } - - if (drag_info.first_move) { - - string trim_type; - - switch (trim_op) { - case StartTrim: - trim_type = "Region start trim"; - break; - case EndTrim: - trim_type = "Region end trim"; - break; - case ContentsTrim: - trim_type = "Region content trim"; - break; - } - - 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)->region()->freeze (); - - AudioRegionView* const arv = dynamic_cast(*i); - if (arv) - arv->temporarily_hide_envelope (); - - boost::shared_ptr pl = (*i)->region()->playlist(); - insert_result = motion_frozen_playlists.insert (pl); - if (insert_result.second) { - session->add_command(new MementoCommand(*pl, &pl->get_state(), 0)); - pl->freeze(); - } - } - } - - if (left_direction) { - frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame); - } else { - frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame); - } - - switch (trim_op) { - case StartTrim: - if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) { - break; - } else { - for (list::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) { - single_start_trim (**i, frame_delta, left_direction, obey_snap); - } - break; - } - - case EndTrim: - if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) { - break; - } else { - for (list::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) { - single_end_trim (**i, frame_delta, left_direction, obey_snap); - } - break; - } - - case ContentsTrim: - { - bool swap_direction = false; - - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) { - swap_direction = true; - } - - for (list::const_iterator i = selection->regions.by_layer().begin(); - i != selection->regions.by_layer().end(); ++i) - { - single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap); - } - } - break; - } - - switch (trim_op) { - case StartTrim: - show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10); - break; - case EndTrim: - show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10); - break; - case ContentsTrim: - show_verbose_time_cursor(drag_info.current_pointer_frame, 10); - break; - } - - drag_info.last_pointer_frame = drag_info.current_pointer_frame; - drag_info.first_move = false; -} void -Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap) +Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap) { boost::shared_ptr region (rv.region()); @@ -4448,7 +2084,7 @@ Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_d return; } - nframes_t new_bound; + nframes64_t new_bound; double speed = 1.0; TimeAxisView* tvp = clicked_axisview; @@ -4457,38 +2093,38 @@ Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_d if (tv && tv->is_track()) { speed = tv->get_diskstream()->speed(); } - + if (left_direction) { if (swap_direction) { - new_bound = (nframes_t) (region->position()/speed) + frame_delta; + new_bound = (nframes64_t) (region->position()/speed) + frame_delta; } else { - new_bound = (nframes_t) (region->position()/speed) - frame_delta; + new_bound = (nframes64_t) (region->position()/speed) - frame_delta; } } else { if (swap_direction) { - new_bound = (nframes_t) (region->position()/speed) - frame_delta; + new_bound = (nframes64_t) (region->position()/speed) - frame_delta; } else { - new_bound = (nframes_t) (region->position()/speed) + frame_delta; + new_bound = (nframes64_t) (region->position()/speed) + frame_delta; } } if (obey_snap) { snap_to (new_bound); } - region->trim_start ((nframes_t) (new_bound * speed), this); + region->trim_start ((nframes64_t) (new_bound * speed), this); rv.region_changed (StartChanged); } void -Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap) +Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap) { - boost::shared_ptr region (rv.region()); + boost::shared_ptr region (rv.region()); if (region->locked()) { return; } - nframes_t new_bound; + nframes64_t new_bound; double speed = 1.0; TimeAxisView* tvp = clicked_axisview; @@ -4497,24 +2133,47 @@ Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_dire if (tv && tv->is_track()) { speed = tv->get_diskstream()->speed(); } - + if (left_direction) { - new_bound = (nframes_t) (region->position()/speed) - frame_delta; + new_bound = (nframes64_t) (region->position()/speed) - frame_delta; } else { - new_bound = (nframes_t) (region->position()/speed) + frame_delta; + new_bound = (nframes64_t) (region->position()/speed) + frame_delta; } if (obey_snap) { - snap_to (new_bound, (left_direction ? 0 : 1)); + snap_to (new_bound, (left_direction ? 0 : 1)); + } + + nframes64_t pre_trim_first_frame = region->first_frame(); + + region->trim_front ((nframes64_t) (new_bound * speed), this); + + if (no_overlap) { + //Get the next region on the left of this region and shrink/expand it. + boost::shared_ptr playlist (region->playlist()); + boost::shared_ptr region_left = playlist->find_next_region (pre_trim_first_frame, End, 0); + + bool regions_touching = false; + + if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){ + regions_touching = true; + } + + //Only trim region on the left if the first frame has gone beyond the left region's last frame. + if (region_left != 0 && + (region_left->last_frame() > region->first_frame() || regions_touching)) + { + region_left->trim_end(region->first_frame(), this); + } } - region->trim_front ((nframes_t) (new_bound * speed), this); + rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged)); } void -Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap) +Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap) { boost::shared_ptr region (rv.region()); @@ -4522,7 +2181,7 @@ Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direct return; } - nframes_t new_bound; + nframes64_t new_bound; double speed = 1.0; TimeAxisView* tvp = clicked_axisview; @@ -4531,102 +2190,98 @@ Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direct if (tv && tv->is_track()) { speed = tv->get_diskstream()->speed(); } - + if (left_direction) { - new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta; + new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta; } else { - new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta; + new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta; } if (obey_snap) { snap_to (new_bound); } - region->trim_end ((nframes_t) (new_bound * speed), this); - rv.region_changed (LengthChanged); -} - -void -Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event) -{ - if (!drag_info.first_move) { - trim_motion_callback (item, event); - - if (!clicked_regionview->get_selected()) { - thaw_region_after_trim (*clicked_regionview); - } else { - - for (list::const_iterator i = selection->regions.by_layer().begin(); - i != selection->regions.by_layer().end(); ++i) - { - thaw_region_after_trim (**i); - (*i)->fake_set_opaque (true); - } + + nframes64_t pre_trim_last_frame = region->last_frame(); + + region->trim_end ((nframes64_t) (new_bound * speed), this); + + if (no_overlap) { + //Get the next region on the right of this region and shrink/expand it. + boost::shared_ptr playlist (region->playlist()); + boost::shared_ptr region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1); + + bool regions_touching = false; + + if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)){ + regions_touching = true; } - - for (set >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) { - (*p)->thaw (); - session->add_command (new MementoCommand(*(*p).get(), 0, &(*p)->get_state())); + + //Only trim region on the right if the last frame has gone beyond the right region's first frame. + if (region_right != 0 && + (region_right->first_frame() < region->last_frame() || regions_touching)) + { + region_right->trim_front(region->last_frame() + 1, this); } - - motion_frozen_playlists.clear (); - commit_reversible_command(); - } else { - /* no mouse movement */ - point_trim (event); + rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged)); + } + else { + rv.region_changed (LengthChanged); } } + void Editor::point_trim (GdkEvent* event) { RegionView* rv = clicked_regionview; - nframes_t new_bound = drag_info.current_pointer_frame; - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { - snap_to (new_bound); - } + nframes64_t new_bound = _drag->current_pointer_frame(); + + snap_to_with_modifier (new_bound, event); /* Choose action dependant on which button was pressed */ switch (event->button.button) { case 1: - 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) { + if ( (*i) == NULL){ + cerr << "region view contains null region" << endl; + } + if (!(*i)->region()->locked()) { boost::shared_ptr pl = (*i)->region()->playlist(); XMLNode &before = pl->get_state(); - (*i)->region()->trim_front (new_bound, this); + + (*i)->region()->trim_front (new_bound, this); + XMLNode &after = pl->get_state(); session->add_command(new MementoCommand(*pl.get(), &before, &after)); } } } else { - if (!rv->region()->locked()) { boost::shared_ptr pl = rv->region()->playlist(); XMLNode &before = pl->get_state(); - rv->region()->trim_front (new_bound, this); + rv->region()->trim_front (new_bound, this); XMLNode &after = pl->get_state(); session->add_command(new MementoCommand(*pl.get(), &before, &after)); } } commit_reversible_command(); - + break; case 2: - 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) { if (!(*i)->region()->locked()) { @@ -4650,7 +2305,7 @@ Editor::point_trim (GdkEvent* event) } commit_reversible_command(); - + break; default: break; @@ -4667,16 +2322,16 @@ Editor::thaw_region_after_trim (RegionView& rv) } region->thaw (_("trimmed region")); - XMLNode &after = region->playlist()->get_state(); - session->add_command (new MementoCommand(*(region->playlist()), 0, &after)); AudioRegionView* arv = dynamic_cast(&rv); - if (arv) + + if (arv) { arv->unhide_envelope (); + } } void -Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event) +Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/) { Marker* marker; bool is_start; @@ -4686,541 +2341,265 @@ Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event) /*NOTREACHED*/ } - Location* location = find_location_from_marker (marker, is_start); + Location* location = find_location_from_marker (marker, is_start); location->set_hidden (true, this); } void -Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op) +Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end) { - if (session == 0) { - return; - } + double x1 = frame_to_pixel (start); + double x2 = frame_to_pixel (end); + double y2 = full_canvas_height - 1.0; + + zoom_rect->property_x1() = x1; + zoom_rect->property_y1() = 1.0; + zoom_rect->property_x2() = x2; + zoom_rect->property_y2() = y2; +} - drag_info.item = item; - drag_info.motion_callback = &Editor::drag_range_markerbar_op; - drag_info.finished_callback = &Editor::end_range_markerbar_op; - range_marker_op = op; +gint +Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/) +{ + using namespace Gtkmm2ext; - if (!temp_location) { - temp_location = new Location; - } - - switch (op) { - case CreateRangeMarker: - case CreateTransportMarker: - - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) { - drag_info.copy = true; - } else { - drag_info.copy = false; + ArdourPrompter prompter (false); + + prompter.set_prompt (_("Name for region:")); + prompter.set_initial_text (clicked_regionview->region()->name()); + prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT); + prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false); + prompter.show_all (); + switch (prompter.run ()) { + case Gtk::RESPONSE_ACCEPT: + string str; + prompter.get_result(str); + if (str.length()) { + clicked_regionview->region()->set_name (str); } - start_grab (event, selector_cursor); break; } - - show_verbose_time_cursor(drag_info.current_pointer_frame, 10); - + return true; } + void -Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event) +Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos) { - nframes_t start = 0; - nframes_t end = 0; - ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect; - - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { - snap_to (drag_info.current_pointer_frame); - } + /* no brushing without a useful snap setting */ - /* only alter selection if the current frame is - different from the last frame position. - */ - - if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return; - - switch (range_marker_op) { - case CreateRangeMarker: - case CreateTransportMarker: - if (drag_info.first_move) { - snap_to (drag_info.grab_frame); - } - - if (drag_info.current_pointer_frame < drag_info.grab_frame) { - start = drag_info.current_pointer_frame; - end = drag_info.grab_frame; - } else { - end = drag_info.current_pointer_frame; - start = drag_info.grab_frame; - } - - /* first drag: Either add to the selection - or create a new selection. - */ - - if (drag_info.first_move) { - - temp_location->set (start, end); - - crect->show (); - - update_marker_drag_item (temp_location); - range_marker_drag_rect->show(); - range_marker_drag_rect->raise_to_top(); - - } - break; - } - - if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) { - start_canvas_autoscroll (1); + switch (_snap_mode) { + case SnapMagnetic: + return; /* can't work because it allows region to be placed anywhere */ + default: + break; /* OK */ } - - if (start != end) { - temp_location->set (start, end); - double x1 = frame_to_pixel (start); - double x2 = frame_to_pixel (end); - crect->property_x1() = x1; - crect->property_x2() = x2; + switch (_snap_type) { + case SnapToMark: + return; - update_marker_drag_item (temp_location); + default: + break; } - drag_info.last_pointer_frame = drag_info.current_pointer_frame; - drag_info.first_move = false; - - show_verbose_time_cursor(drag_info.current_pointer_frame, 10); - -} - -void -Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event) -{ - Location * newloc = 0; - string rangename; - - if (!drag_info.first_move) { - drag_range_markerbar_op (item, event); - - switch (range_marker_op) { - case CreateRangeMarker: - { - 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); - 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; - } - - case CreateTransportMarker: - // popup menu to pick loop or punch - new_transport_marker_context_menu (&event->button, item); - - break; - } - } else { - /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */ - - if (Keyboard::no_modifier_keys_pressed (&event->button)) { + /* don't brush a copy over the original */ - nframes_t start; - nframes_t end; + if (pos == rv->region()->position()) { + return; + } - start = session->locations()->first_mark_before (drag_info.grab_frame); - end = session->locations()->first_mark_after (drag_info.grab_frame); - - if (end == max_frames) { - end = session->current_end_frame (); - } + RouteTimeAxisView* rtv = dynamic_cast(&rv->get_time_axis_view()); - if (start == 0) { - start = session->current_start_frame (); - } + if (rtv == 0 || !rtv->is_track()) { + return; + } - 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, track_views, Selection::Set); - break; + boost::shared_ptr playlist = rtv->playlist(); + double speed = rtv->get_diskstream()->speed(); - case MouseRange: - /* find the two markers on either side of the click and make the range out of it */ - selection->set (0, start, end); - break; + XMLNode &before = playlist->get_state(); + playlist->add_region (RegionFactory::create (rv->region()), (nframes64_t) (pos * speed)); + XMLNode &after = playlist->get_state(); + session->add_command(new MementoCommand(*playlist.get(), &before, &after)); - default: - break; - } - } - } + // playlist is frozen, so we have to update manually - stop_canvas_autoscroll (); + playlist->Modified(); /* EMIT SIGNAL */ } - - -void -Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event) +gint +Editor::track_height_step_timeout () { - drag_info.item = item; - drag_info.motion_callback = &Editor::drag_mouse_zoom; - drag_info.finished_callback = &Editor::end_mouse_zoom; - - start_grab (event, zoom_cursor); - - show_verbose_time_cursor (drag_info.current_pointer_frame, 10); + if (get_microseconds() - last_track_height_step_timestamp < 250000) { + current_stepping_trackview = 0; + return false; + } + return true; } void -Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event) +Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view) { - nframes_t start; - nframes_t end; - - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { - snap_to (drag_info.current_pointer_frame); - - if (drag_info.first_move) { - snap_to (drag_info.grab_frame); - } - } - - if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return; + assert (region_view); - /* base start and end on initial click position */ - if (drag_info.current_pointer_frame < drag_info.grab_frame) { - start = drag_info.current_pointer_frame; - end = drag_info.grab_frame; - } else { - end = drag_info.current_pointer_frame; - start = drag_info.grab_frame; - } - - if (start != end) { - - if (drag_info.first_move) { - zoom_rect->show(); - zoom_rect->raise_to_top(); - } + _region_motion_group->raise_to_top (); - reposition_zoom_rect(start, end); - - drag_info.last_pointer_frame = drag_info.current_pointer_frame; - drag_info.first_move = false; - - show_verbose_time_cursor (drag_info.current_pointer_frame, 10); - } -} + assert (_drag == 0); -void -Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event) -{ - if (!drag_info.first_move) { - drag_mouse_zoom (item, event); - - if (drag_info.grab_frame < drag_info.last_pointer_frame) { - temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom"); - } else { - temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom"); - } + if (Config->get_edit_mode() == Splice) { + _drag = new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()); } else { - temporal_zoom_to_frame (false, drag_info.grab_frame); - /* - temporal_zoom_step (false); - center_screen (drag_info.grab_frame); - */ + RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit); + _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false); } - zoom_rect->hide(); -} + _drag->start_grab (event); -void -Editor::reposition_zoom_rect (nframes_t start, nframes_t end) -{ - double x1 = frame_to_pixel (start); - double x2 = frame_to_pixel (end); - double y2 = full_canvas_height - 1.0; + begin_reversible_command (_("move region(s)")); - zoom_rect->property_x1() = x1; - zoom_rect->property_y1() = 1.0; - zoom_rect->property_x2() = x2; - zoom_rect->property_y2() = y2; + /* sync the canvas to what we think is its current state */ + update_canvas_now(); } void -Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event) +Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view) { - drag_info.item = item; - drag_info.motion_callback = &Editor::drag_rubberband_select; - drag_info.finished_callback = &Editor::end_rubberband_select; + assert (region_view); + assert (_drag == 0); - start_grab (event, cross_hair_cursor); + _region_motion_group->raise_to_top (); - show_verbose_time_cursor (drag_info.current_pointer_frame, 10); + RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit); + _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true); + _drag->start_grab(event); } void -Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event) +Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view) { - nframes_t start; - nframes_t end; - double y1; - double y2; + assert (region_view); + assert (_drag == 0); - /* use a bigger drag threshold than the default */ - - if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) { + if (Config->get_edit_mode() == Splice) { return; } - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { - if (drag_info.first_move) { - snap_to (drag_info.grab_frame); - } - snap_to (drag_info.current_pointer_frame); - } - - /* base start and end on initial click position */ - - if (drag_info.current_pointer_frame < drag_info.grab_frame) { - start = drag_info.current_pointer_frame; - end = drag_info.grab_frame; - } else { - end = drag_info.current_pointer_frame; - start = drag_info.grab_frame; - } - - if (drag_info.current_pointer_y < drag_info.grab_y) { - y1 = drag_info.current_pointer_y; - y2 = drag_info.grab_y; - } else { - y2 = drag_info.current_pointer_y; - y1 = drag_info.grab_y; - } - - - if (start != end || y1 != y2) { - - double x1 = frame_to_pixel (start); - double x2 = frame_to_pixel (end); - - rubberband_rect->property_x1() = x1; - rubberband_rect->property_y1() = y1; - rubberband_rect->property_x2() = x2; - rubberband_rect->property_y2() = y2; + RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit); + _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false); + _drag->start_grab (event); - rubberband_rect->show(); - rubberband_rect->raise_to_top(); - - drag_info.last_pointer_frame = drag_info.current_pointer_frame; - drag_info.first_move = false; - - show_verbose_time_cursor (drag_info.current_pointer_frame, 10); - } + begin_reversible_command (_("Drag region brush")); } +/** Start a grab where a time range is selected, track(s) are selected, and the + * user clicks and drags a region with a modifier in order to create a new region containing + * the section of the clicked region that lies within the time range. + */ void -Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event) +Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event) { - if (!drag_info.first_move) { - - drag_rubberband_select (item, event); - - double y1,y2; - if (drag_info.current_pointer_y < drag_info.grab_y) { - y1 = drag_info.current_pointer_y; - y2 = drag_info.grab_y; - } - else { - y2 = drag_info.current_pointer_y; - y1 = drag_info.grab_y; - } - - - Selection::Operation op = Keyboard::selection_type (event->button.state); - bool commit; + if (clicked_regionview == 0) { + return; + } - begin_reversible_command (_("rubberband selection")); + /* lets try to create new Region for the 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, track_views, op); - } else { - commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op); - } + vector > new_regions; + create_region_from_selection (new_regions); - if (commit) { - commit_reversible_command (); - } - - } else { - selection->clear_tracks(); - selection->clear_regions(); - selection->clear_points (); - selection->clear_lines (); + if (new_regions.empty()) { + return; } - rubberband_rect->hide(); -} + /* XXX fix me one day to use all new regions */ + boost::shared_ptr region (new_regions.front()); -gint -Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event) -{ - using namespace Gtkmm2ext; + /* add it to the current stream/playlist. - ArdourPrompter prompter (false); + tricky: the streamview for the track will add a new regionview. we will + catch the signal it sends when it creates the regionview to + set the regionview we want to then drag. + */ - prompter.set_prompt (_("Name for region:")); - prompter.set_initial_text (clicked_regionview->region()->name()); - prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT); - prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false); - prompter.show_all (); - switch (prompter.run ()) { - case Gtk::RESPONSE_ACCEPT: - string str; - prompter.get_result(str); - if (str.length()) { - clicked_regionview->region()->set_name (str); - } - break; - } - return true; -} + latest_regionviews.clear(); + sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view)); -void -Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event) -{ - drag_info.item = item; - drag_info.motion_callback = &Editor::time_fx_motion; - drag_info.finished_callback = &Editor::end_time_fx; + /* A selection grab currently creates two undo/redo operations, one for + creating the new region and another for moving it. + */ - start_grab (event); + begin_reversible_command (_("selection grab")); - show_verbose_time_cursor (drag_info.current_pointer_frame, 10); -} + boost::shared_ptr playlist = clicked_axisview->playlist(); -void -Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event) -{ - RegionView* rv = clicked_regionview; + XMLNode *before = &(playlist->get_state()); + clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start); + XMLNode *after = &(playlist->get_state()); + session->add_command(new MementoCommand(*playlist, before, after)); - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { - snap_to (drag_info.current_pointer_frame); - } + commit_reversible_command (); - if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) { - return; - } + c.disconnect (); - if (drag_info.current_pointer_frame > rv->region()->position()) { - rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame); + if (latest_regionviews.empty()) { + /* something went wrong */ + return; } - drag_info.last_pointer_frame = drag_info.current_pointer_frame; - drag_info.first_move = false; + /* 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_regionviews); - show_verbose_time_cursor (drag_info.current_pointer_frame, 10); + assert (_drag == 0); + _drag = new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false); + _drag->start_grab (event); } void -Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event) +Editor::break_drag () { - clicked_regionview->get_time_axis_view().hide_timestretch (); - - 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(); - float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f; - - begin_reversible_command (_("timestretch")); - - if (run_timestretch (selection->regions, percentage) == 0) { - session->commit_reversible_command (); + if (_drag) { + _drag->break_drag (); } } void -Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos) +Editor::set_internal_edit (bool yn) { - /* no brushing without a useful snap setting */ + _internal_editing = yn; - // FIXME - AudioRegionView* arv = dynamic_cast(rv); - assert(arv); + if (yn) { + mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil"))))); + mouse_select_button.get_image ()->show (); - switch (snap_mode) { - case SnapMagnetic: - return; /* can't work because it allows region to be placed anywhere */ - default: - break; /* OK */ - } + for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { + MidiTimeAxisView* mtv = dynamic_cast (*i); + if (mtv) { + mtv->start_step_editing (); + } + } + start_step_editing (); - switch (snap_type) { - case SnapToFrame: - case SnapToMark: - case SnapToEditPoint: - return; + } else { - default: - break; - } + mouse_select_button.set_image (*(manage (new Image (::get_icon("tool_range"))))); + mouse_select_button.get_image ()->show (); + stop_step_editing (); - /* don't brush a copy over the original */ - - if (pos == rv->region()->position()) { - return; + for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { + MidiTimeAxisView* mtv = dynamic_cast (*i); + if (mtv) { + mtv->stop_step_editing (); + } + } } - RouteTimeAxisView* rtv = dynamic_cast(&arv->get_time_axis_view()); - - if (rtv == 0 || !rtv->is_track()) { - return; - } + set_canvas_cursor (); - boost::shared_ptr playlist = rtv->playlist(); - double speed = rtv->get_diskstream()->speed(); - - XMLNode &before = playlist->get_state(); - playlist->add_region (boost::dynamic_pointer_cast (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed)); - XMLNode &after = playlist->get_state(); - session->add_command(new MementoCommand(*playlist.get(), &before, &after)); - - // playlist is frozen, so we have to update manually - - playlist->Modified(); /* EMIT SIGNAL */ -} -gint -Editor::track_height_step_timeout () -{ - struct timeval now; - struct timeval delta; - - gettimeofday (&now, 0); - timersub (&now, &last_track_height_step_timestamp, &delta); - - if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */ - current_stepping_trackview = 0; - return false; - } - return true; } -