X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Feditor_mouse.cc;h=61a0fdd4205e002e4e8df1cb28cc95f180b99ad7;hb=b1c48972f2914b49d45a7cef2b38a0a125bd731e;hp=904b76c3673ab8746eb24fbc7cdc1c15cc79ed8f;hpb=14d43ca9fe28a8309b4a52fa85e2b0c64a92248b;p=ardour.git diff --git a/gtk2_ardour/editor_mouse.cc b/gtk2_ardour/editor_mouse.cc index 904b76c367..1a03f66f24 100644 --- a/gtk2_ardour/editor_mouse.cc +++ b/gtk2_ardour/editor_mouse.cc @@ -1,3 +1,4 @@ + /* Copyright (C) 2000-2001 Paul Davis @@ -15,9 +16,9 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id$ */ +#include #include #include #include @@ -27,34 +28,42 @@ #include #include +#include +#include +#include #include "ardour_ui.h" #include "editor.h" #include "time_axis_view.h" #include "audio_time_axis.h" -#include "regionview.h" +#include "audio_region_view.h" +#include "midi_region_view.h" #include "marker.h" #include "streamview.h" #include "region_gain_line.h" #include "automation_time_axis.h" +#include "control_point.h" #include "prompter.h" #include "utils.h" #include "selection.h" #include "keyboard.h" #include "editing.h" #include "rgb_macros.h" -#include "extra_bind.h" #include +#include #include #include -#include +#include +#include #include #include #include +#include #include #include #include +#include #include @@ -62,12 +71,49 @@ using namespace std; using namespace ARDOUR; +using namespace PBD; using namespace sigc; using namespace Gtk; using namespace Editing; -jack_nframes_t -Editor::event_frame (GdkEvent* event, double* pcx, double* pcy) +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 pointer_window; + + if (!canvas_window) { + return false; + } + + pointer_window = canvas_window->get_pointer (x, y, mask); + + if (pointer_window == track_canvas->get_bin_window()) { + wx = x; + wy = y; + in_track_canvas = true; + + } else { + in_track_canvas = false; + return false; + } + + GdkEvent event; + event.type = GDK_BUTTON_RELEASE; + event.button.x = wx; + event.button.y = wy; + + where = event_frame (&event, 0, 0); + return true; +} + +nframes64_t +Editor::event_frame (GdkEvent* event, double* pcx, double* pcy) const { double cx, cy; @@ -86,17 +132,27 @@ Editor::event_frame (GdkEvent* event, double* pcx, double* pcy) case GDK_BUTTON_PRESS: case GDK_2BUTTON_PRESS: case GDK_3BUTTON_PRESS: - gnome_canvas_w2c_d (GNOME_CANVAS(track_gnome_canvas), 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: - gnome_canvas_w2c_d (GNOME_CANVAS(track_gnome_canvas), 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: - gnome_canvas_w2c_d (GNOME_CANVAS(track_gnome_canvas), 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); break; default: - warning << compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg; + warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg; break; } @@ -104,7 +160,7 @@ Editor::event_frame (GdkEvent* event, double* pcx, double* pcy) position is negative (as can be the case with motion events in particular), the frame location is always positive. */ - + return pixel_to_frame (*pcx); } @@ -151,12 +207,69 @@ Editor::mouse_mode_toggled (MouseMode m) set_mouse_mode (m); } break; + + case MouseNote: + if (mouse_note_button.get_active()) { + set_mouse_mode (m); + } + break; default: break; } } +Gdk::Cursor* +Editor::which_grabber_cursor () +{ + switch (_edit_point) { + case EditAtMouse: + return grabber_edit_point_cursor; + break; + default: + break; + } + return grabber_cursor; +} + +void +Editor::set_canvas_cursor () +{ + switch (mouse_mode) { + case MouseRange: + current_canvas_cursor = selector_cursor; + break; + + case MouseObject: + current_canvas_cursor = which_grabber_cursor(); + break; + + case MouseGain: + current_canvas_cursor = cross_hair_cursor; + break; + + case MouseZoom: + current_canvas_cursor = zoom_cursor; + break; + + case MouseTimeFX: + current_canvas_cursor = time_fx_cursor; // just use playhead + break; + + case MouseAudition: + current_canvas_cursor = speaker_cursor; + break; + + case MouseNote: + set_midi_edit_cursor (current_midi_edit_mode()); + break; + } + + if (is_drawable()) { + track_canvas->get_window()->set_cursor(*current_canvas_cursor); + } +} + void Editor::set_mouse_mode (MouseMode m, bool force) { @@ -164,7 +277,7 @@ Editor::set_mouse_mode (MouseMode m, bool force) return; } - if (m == mouse_mode && !force) { + if (!force && m == mouse_mode) { return; } @@ -178,7 +291,7 @@ Editor::set_mouse_mode (MouseMode m, bool force) show the object (region) selection. */ - for (AudioRegionSelection::iterator i = selection->audio_regions.begin(); i != selection->audio_regions.end(); ++i) { + for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { (*i)->set_should_show_selection (true); } for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { @@ -187,21 +300,18 @@ Editor::set_mouse_mode (MouseMode m, bool force) } else { - /* in range mode, hide object (region) selection, and show the - range selection. + /* + in range mode,show the range selection. */ - for (AudioRegionSelection::iterator i = selection->audio_regions.begin(); i != selection->audio_regions.end(); ++i) { - (*i)->set_should_show_selection (false); - } for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) { - if ((*i)->selected()) { + if ((*i)->get_selected()) { (*i)->show_selection (selection->time); } } } - /* XXX the hack of unsetting all other buttongs should go + /* XXX the hack of unsetting all other buttons should go away once GTK2 allows us to use regular radio buttons drawn like normal buttons, rather than my silly GroupedButton hack. */ @@ -211,40 +321,45 @@ Editor::set_mouse_mode (MouseMode m, bool force) switch (mouse_mode) { case MouseRange: mouse_select_button.set_active (true); - current_canvas_cursor = selector_cursor; break; case MouseObject: mouse_move_button.set_active (true); - current_canvas_cursor = grabber_cursor; break; case MouseGain: mouse_gain_button.set_active (true); - current_canvas_cursor = cross_hair_cursor; break; case MouseZoom: mouse_zoom_button.set_active (true); - current_canvas_cursor = zoom_cursor; break; case MouseTimeFX: mouse_timefx_button.set_active (true); - current_canvas_cursor = time_fx_cursor; // just use playhead break; case MouseAudition: mouse_audition_button.set_active (true); - current_canvas_cursor = speaker_cursor; + break; + + case MouseNote: + mouse_note_button.set_active (true); + set_midi_edit_cursor (current_midi_edit_mode()); break; } - ignore_mouse_mode_toggle = false; - - if (is_drawable()) { - gdk_window_set_cursor (track_canvas_scroller.get_window(), current_canvas_cursor); + if (midi_tools_tearoff) { + if (mouse_mode == MouseNote) { + midi_tools_tearoff->show(); + } else { + midi_tools_tearoff->hide(); + } } + + ignore_mouse_mode_toggle = false; + + set_canvas_cursor (); } void @@ -280,99 +395,245 @@ Editor::step_mouse_mode (bool next) 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; } } -gint -Editor::button_press_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType item_type) +void +Editor::midi_edit_mode_toggled (MidiEditMode m) { - jack_nframes_t where = event_frame (event, 0, 0); + if (ignore_midi_edit_mode_toggle) { + return; + } - if (session && session->actively_recording()) { - return TRUE; + 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 MidiEditResize: + if (midi_tool_resize_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 MidiEditResize: + midi_tool_resize_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 MidiEditResize: + current_canvas_cursor = midi_resize_cursor; + break; + + case MidiEditErase: + current_canvas_cursor = midi_erase_cursor; + break; } +} +void +Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type) +{ /* 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. + + note: not dbl-click or triple-click */ - if (((mouse_mode == MouseObject) || - (mouse_mode == MouseAudition && item_type == RegionItem) || - (mouse_mode == MouseTimeFX && item_type == RegionItem)) && - event->type == GDK_BUTTON_PRESS && - event->button.button <= 3) { + if (((mouse_mode != MouseObject) && + (mouse_mode != MouseAudition || item_type != RegionItem) && + (mouse_mode != MouseTimeFX || item_type != RegionItem) && + (mouse_mode != MouseRange)) || - AudioRegionView* rv; - ControlPoint* cp; + ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) { + + return; + } - /* not dbl-click or triple-click */ + if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) { - switch (item_type) { - case RegionItem: - set_selected_regionview_from_click (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift), true); - break; + if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) { - case AudioRegionViewNameHighlight: - case AudioRegionViewName: - if ((rv = reinterpret_cast (gtk_object_get_data(GTK_OBJECT(item), "regionview"))) != 0) { - set_selected_regionview_from_click (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift), true); - } - break; - - case GainAutomationControlPointItem: - case PanAutomationControlPointItem: - case RedirectAutomationControlPointItem: - if ((cp = reinterpret_cast (gtk_object_get_data(GTK_OBJECT(item), "control_point"))) != 0) { - set_selected_control_point_from_click (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift), true); + /* almost no selection action on modified button-2 or button-3 events */ + + if (item_type != RegionItem && event->button.button != 2) { + return; } - break; + } + } + + Selection::Operation op = Keyboard::selection_type (event->button.state); + bool press = (event->type == GDK_BUTTON_PRESS); - case StreamItem: - break; + // begin_reversible_command (_("select on click")); + + switch (item_type) { + case RegionItem: + if (mouse_mode != MouseRange) { + set_selected_regionview_from_click (press, op, true); + } else if (event->type == GDK_BUTTON_PRESS) { + set_selected_track_as_side_effect (); + } + break; + + case RegionViewNameHighlight: + case RegionViewName: + if (mouse_mode != MouseRange) { + set_selected_regionview_from_click (press, op, true); + } else if (event->type == GDK_BUTTON_PRESS) { + set_selected_track_as_side_effect (); + } + break; - case AutomationTrackItem: - break; - default: - break; + case FadeInHandleItem: + case FadeInItem: + case FadeOutHandleItem: + case FadeOutItem: + if (mouse_mode != MouseRange) { + set_selected_regionview_from_click (press, op, true); + } else if (event->type == GDK_BUTTON_PRESS) { + set_selected_track_as_side_effect (); + } + break; + + case ControlPointItem: + set_selected_track_as_side_effect (); + if (mouse_mode != MouseRange) { + set_selected_control_point_from_click (op, false); } + break; + + case StreamItem: + /* for context click or range selection, select track */ + if (event->button.button == 3) { + set_selected_track_as_side_effect (); + } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) { + set_selected_track_as_side_effect (); + } + break; + + case AutomationTrackItem: + set_selected_track_as_side_effect (true); + break; + + default: + break; } +} -#define SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE -#ifdef SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE - /* in range mode, button 1/2/3 press potentially selects a track */ +bool +Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type) +{ + 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; - if (mouse_mode == MouseRange && - event->type == GDK_BUTTON_PRESS && - event->button.button <= 3) { + pointer_window = canvas_window->get_pointer (x, y, mask); - AudioRegionView* rv; + 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; + } + } - switch (item_type) { - case StreamItem: - case RegionItem: - case AutomationTrackItem: - set_selected_track_from_click (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift), true, true); - break; + track_canvas->grab_focus(); - case AudioRegionViewNameHighlight: - case AudioRegionViewName: - rv = reinterpret_cast (gtk_object_get_data(GTK_OBJECT(item), "regionview")); - default: - break; - } + if (session && session->actively_recording()) { + return true; } -#endif + 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 */ - return TRUE; + return true; } switch (event->button.button) { @@ -381,7 +642,7 @@ Editor::button_press_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType i if (event->type == GDK_BUTTON_PRESS) { if (drag_info.item) { - gnome_canvas_item_ungrab (drag_info.item, event->button.time); + drag_info.item->ungrab (event->button.time); } /* single mouse clicks on any of these item types operate @@ -391,41 +652,53 @@ Editor::button_press_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType i */ switch (item_type) { - case EditCursorItem: case PlayheadCursorItem: start_cursor_grab (item, event); - return TRUE; + return true; case MarkerItem: - if (Keyboard::modifier_state_equals (event->button.state, - Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) { + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) { hide_marker (item, event); } else { start_marker_grab (item, event); } - return TRUE; + return true; case TempoMarkerItem: - start_tempo_marker_grab (item, event); - return TRUE; + if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) { + start_tempo_marker_copy_grab (item, event); + } else { + start_tempo_marker_grab (item, event); + } + return true; case MeterMarkerItem: - start_meter_marker_grab (item, event); - return TRUE; + if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) { + start_meter_marker_copy_grab (item, event); + } else { + start_meter_marker_grab (item, event); + } + return true; case TempoBarItem: - return TRUE; + return true; case MeterBarItem: - return TRUE; + return true; case RangeMarkerBarItem: start_range_markerbar_op (item, event, CreateRangeMarker); - return TRUE; + return true; + break; + + case CdMarkerBarItem: + start_range_markerbar_op (item, event, CreateCDMarker); + return true; break; + case TransportMarkerBarItem: start_range_markerbar_op (item, event, CreateTransportMarker); - return TRUE; + return true; break; default: @@ -446,14 +719,13 @@ Editor::button_press_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType i case SelectionItem: if (Keyboard::modifier_state_contains - (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) { + (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) { // contains and not equals because I can't use alt as a modifier alone. start_selection_grab (item, event); - } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) { + } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) { /* grab selection for moving */ start_selection_op (item, event, SelectionMove); - } - else { + } else { /* this was debated, but decided the more common action was to make a new selection */ start_selection_op (item, event, CreateSelection); @@ -463,14 +735,13 @@ Editor::button_press_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType i default: start_selection_op (item, event, CreateSelection); } - return TRUE; + return true; break; case MouseObject: - if (Keyboard::modifier_state_contains (event->button.state, - Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt)) - && event->type == GDK_BUTTON_PRESS) { - + if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) && + event->type == GDK_BUTTON_PRESS) { + start_rubberband_select (item, event); } else if (event->type == GDK_BUTTON_PRESS) { @@ -478,14 +749,14 @@ Editor::button_press_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType i switch (item_type) { case FadeInHandleItem: start_fade_in_grab (item, event); - return TRUE; + return true; case FadeOutHandleItem: start_fade_out_grab (item, event); - return TRUE; + return true; case RegionItem: - if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) { + if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) { start_region_copy_grab (item, event); } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) { start_region_brush_grab (item, event); @@ -494,29 +765,25 @@ Editor::button_press_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType i } break; - case AudioRegionViewNameHighlight: + case RegionViewNameHighlight: start_trim (item, event); - return TRUE; + return true; break; - case AudioRegionViewName: + case RegionViewName: /* rename happens on edit clicks */ - start_trim (clicked_regionview->get_name_highlight(), event); - return TRUE; + start_trim (clicked_regionview->get_name_highlight(), event); + return true; break; - case GainAutomationControlPointItem: - case PanAutomationControlPointItem: - case RedirectAutomationControlPointItem: + case ControlPointItem: start_control_point_grab (item, event); - return TRUE; + return true; break; - case GainAutomationLineItem: - case PanAutomationLineItem: - case RedirectAutomationLineItem: + case AutomationLineItem: start_line_grab_from_line (item, event); - return TRUE; + return true; break; case StreamItem: @@ -524,39 +791,40 @@ Editor::button_press_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType i start_rubberband_select (item, event); break; - /* */ +#ifdef WITH_CMT case ImageFrameHandleStartItem: imageframe_start_handle_op(item, event) ; - return(TRUE) ; + return(true) ; break ; case ImageFrameHandleEndItem: imageframe_end_handle_op(item, event) ; - return(TRUE) ; + return(true) ; break ; case MarkerViewHandleStartItem: markerview_item_start_handle_op(item, event) ; - return(TRUE) ; + return(true) ; break ; case MarkerViewHandleEndItem: markerview_item_end_handle_op(item, event) ; - return(TRUE) ; + return(true) ; break ; - /* */ - - /* */ case MarkerViewItem: start_markerview_grab(item, event) ; break ; case ImageFrameItem: start_imageframe_grab(item, event) ; break ; - /* */ +#endif + + case MarkerBarItem: + + break; default: break; } } - return TRUE; + return true; break; case MouseGain: @@ -565,37 +833,27 @@ Editor::button_press_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType i // start_line_grab_from_regionview (item, event); break; - case GainControlPointItem: - start_control_point_grab (item, event); - return TRUE; - case GainLineItem: start_line_grab_from_line (item, event); - return TRUE; + return true; - case GainAutomationControlPointItem: - case PanAutomationControlPointItem: - case RedirectAutomationControlPointItem: + case ControlPointItem: start_control_point_grab (item, event); - return TRUE; + return true; break; default: break; } - return TRUE; + return true; break; switch (item_type) { - case GainAutomationControlPointItem: - case PanAutomationControlPointItem: - case RedirectAutomationControlPointItem: + case ControlPointItem: start_control_point_grab (item, event); break; - case GainAutomationLineItem: - case PanAutomationLineItem: - case RedirectAutomationLineItem: + case AutomationLineItem: start_line_grab_from_line (item, event); break; @@ -608,7 +866,7 @@ Editor::button_press_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType i default: break; } - return TRUE; + return true; break; case MouseZoom: @@ -616,7 +874,7 @@ Editor::button_press_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType i start_mouse_zoom (item, event); } - return TRUE; + return true; break; case MouseTimeFX: @@ -626,9 +884,19 @@ Editor::button_press_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType i break; case MouseAudition: - /* handled in release */ + _scrubbing = true; + scrub_reversals = 0; + scrub_reverse_distance = 0; + last_scrub_x = event->button.x; + scrubbing_direction = 0; + track_canvas->get_window()->set_cursor (*transparent_cursor); + /* rest handled in motion & release */ break; + case MouseNote: + start_create_region_grab (item, event); + break; + default: break; } @@ -640,18 +908,16 @@ Editor::button_press_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType i if (event->type == GDK_BUTTON_PRESS) { switch (item_type) { case RegionItem: - if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) { + if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) { start_region_copy_grab (item, event); } else { start_region_grab (item, event); } - + return true; break; - case GainAutomationControlPointItem: - case PanAutomationControlPointItem: - case RedirectAutomationControlPointItem: + case ControlPointItem: start_control_point_grab (item, event); - return TRUE; + return true; break; default: @@ -661,14 +927,14 @@ Editor::button_press_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType i switch (item_type) { - case AudioRegionViewNameHighlight: + case RegionViewNameHighlight: start_trim (item, event); - return TRUE; + return true; break; - case AudioRegionViewName: + case RegionViewName: start_trim (clicked_regionview->get_name_highlight(), event); - return TRUE; + return true; break; default: @@ -681,17 +947,17 @@ Editor::button_press_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType i if (event->type == GDK_BUTTON_PRESS) { /* relax till release */ } - return TRUE; + return true; break; case MouseZoom: - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) { + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) { temporal_zoom_session(); } else { temporal_zoom_to_frame (true, event_frame(event)); } - return TRUE; + return true; break; default: @@ -703,95 +969,24 @@ Editor::button_press_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType i case 3: break; - case 4: - switch (mouse_mode) { - case MouseZoom: - //temporal_zoom_to_frame (true, where); - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) { - temporal_zoom_to_frame (true, where); - } - else { - temporal_zoom_step (true); - } - break; - default: - - if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) { - scroll_backward (0.6f); - } - else if (Keyboard::no_modifier_keys_pressed (&event->button)) { - scroll_tracks_up_line (); - } else { - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) { - if (clicked_trackview) { - if (!current_stepping_trackview) { - TimeoutSig t; - step_timeout = t.connect (mem_fun(*this, &Editor::track_height_step_timeout), 500); - current_stepping_trackview = clicked_trackview; - } - gettimeofday (&last_track_height_step_timestamp, 0); - current_stepping_trackview->step_height (true); - } - } - else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) { - temporal_zoom_to_frame (true, where); - } - } - } + default: break; - case 5: - switch (mouse_mode) { - case MouseZoom: - // temporal_zoom_to_frame (false, where); - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) { - temporal_zoom_to_frame (false, where); - } - else { - temporal_zoom_step (false); - } - break; - default: + } - if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) { - scroll_forward (0.6f); - } - else if (Keyboard::no_modifier_keys_pressed (&event->button)) { - scroll_tracks_down_line (); - } else { - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) { - if (clicked_trackview) { - if (!current_stepping_trackview) { - TimeoutSig t; - step_timeout = t.connect (mem_fun(*this, &Editor::track_height_step_timeout), 500); - current_stepping_trackview = clicked_trackview; - } - gettimeofday (&last_track_height_step_timestamp, 0); - current_stepping_trackview->step_height (false); - } - } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) { - temporal_zoom_to_frame (false, where); - } - } - } - break; - - default: - break; - - } - return FALSE; + return false; } -gint -Editor::button_release_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType item_type) +bool +Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type) { - jack_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; + return true; } /* first, see if we're finishing a drag ... */ @@ -799,10 +994,12 @@ Editor::button_release_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType if (drag_info.item) { if (end_grab (item, event)) { /* grab dragged, so do nothing else */ - return TRUE; + return true; } } + button_selection (item, event, item_type); + /* edit events get handled here */ if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) { @@ -819,7 +1016,7 @@ Editor::button_release_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType edit_meter_marker (item); break; - case AudioRegionViewName: + case RegionViewName: if (clicked_regionview->name_active()) { return mouse_rename_region (item, event); } @@ -828,7 +1025,7 @@ Editor::button_release_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType default: break; } - return TRUE; + return true; } /* context menu events get handled here */ @@ -848,14 +1045,14 @@ Editor::button_release_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType case FadeOutHandleItem: popup_fade_context_menu (1, event->button.time, item, item_type); break; - + case StreamItem: popup_track_context_menu (1, event->button.time, item_type, false, where); break; case RegionItem: - case AudioRegionViewNameHighlight: - case AudioRegionViewName: + case RegionViewNameHighlight: + case RegionViewName: popup_track_context_menu (1, event->button.time, item_type, false, where); break; @@ -869,7 +1066,8 @@ Editor::button_release_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType case MarkerBarItem: case RangeMarkerBarItem: - case TransportMarkerBarItem: + case TransportMarkerBarItem: + case CdMarkerBarItem: case TempoBarItem: case MeterBarItem: popup_ruler_menu (pixel_to_frame(event->button.x), item_type); @@ -886,12 +1084,12 @@ Editor::button_release_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType 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) ; break ; @@ -904,14 +1102,13 @@ Editor::button_release_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType case MarkerTimeAxisItem: popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ; break ; - /* */ - +#endif default: break; } - return TRUE; + return true; } } @@ -929,7 +1126,7 @@ Editor::button_release_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType break; case MarkerItem: - remove_marker (item, event); + remove_marker (*item, event); break; case RegionItem: @@ -938,22 +1135,18 @@ Editor::button_release_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType } break; - case GainControlPointItem: + case ControlPointItem: if (mouse_mode == MouseGain) { remove_gain_control_point (item, event); + } else { + remove_control_point (item, event); } break; - case GainAutomationControlPointItem: - case PanAutomationControlPointItem: - case RedirectAutomationControlPointItem: - remove_control_point (item, event); - break; - default: break; } - return TRUE; + return true; } switch (event->button.button) { @@ -961,34 +1154,39 @@ Editor::button_release_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType switch (item_type) { /* see comments in button_press_handler */ - case EditCursorItem: case PlayheadCursorItem: case MarkerItem: case GainLineItem: - case GainAutomationLineItem: - case PanAutomationLineItem: - case RedirectAutomationLineItem: + case AutomationLineItem: case StartSelectionTrimItem: case EndSelectionTrimItem: - return TRUE; + return true; case MarkerBarItem: if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { snap_to (where, 0, true); } mouse_add_new_marker (where); - return TRUE; + return true; + + case CdMarkerBarItem: + // if we get here then a dragged range wasn't done + if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { + snap_to (where, 0, true); + } + mouse_add_new_marker (where, true); + return true; case TempoBarItem: if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { snap_to (where); } mouse_add_new_tempo_event (where); - return TRUE; + return true; case MeterBarItem: mouse_add_new_meter_event (pixel_to_frame (event->button.x)); - return TRUE; + return true; break; default: @@ -999,30 +1197,36 @@ Editor::button_release_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType case MouseObject: switch (item_type) { case AutomationTrackItem: - dynamic_cast(clicked_trackview)->add_automation_event - (item, - event, - where, - event->button.y); - return TRUE; + atv = dynamic_cast(clicked_routeview); + if (atv) { + atv->add_automation_event (item, event, where, event->button.y); + } + return true; + break; - + default: break; } break; case MouseGain: + // Gain only makes sense for audio regions + + if (!dynamic_cast(clicked_regionview)) { + break; + } + switch (item_type) { case RegionItem: - clicked_regionview->add_gain_point_event (item, event); - return TRUE; + dynamic_cast(clicked_regionview)->add_gain_point_event (item, event); + return true; break; case AutomationTrackItem: - dynamic_cast(clicked_trackview)-> + dynamic_cast(clicked_axisview)-> add_automation_event (item, event, where, event->button.y); - return TRUE; + return true; break; default: break; @@ -1030,21 +1234,29 @@ Editor::button_release_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType break; case MouseAudition: - switch (item_type) { - case RegionItem: - audition_selected_region (); - break; - default: - break; - } + _scrubbing = false; + track_canvas->get_window()->set_cursor (*current_canvas_cursor); + if (scrubbing_direction == 0) { + /* no drag, just a click */ + switch (item_type) { + case RegionItem: + play_selected_region (); + break; + default: + break; + } + } else { + /* make sure we stop */ + session->request_transport_speed (0.0); + } break; - + default: break; } - return TRUE; + return true; break; @@ -1054,14 +1266,14 @@ Editor::button_release_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType case MouseObject: switch (item_type) { case RegionItem: - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) { + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) { raise_region (); - } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) { + } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) { lower_region (); } else { // Button2 click is unused } - return TRUE; + return true; break; @@ -1073,7 +1285,7 @@ Editor::button_release_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType case MouseRange: // x_style_paste (where, 1.0); - return TRUE; + return true; break; default: @@ -1088,140 +1300,113 @@ Editor::button_release_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType default: break; } - return FALSE; -} - -void -Editor::maybe_autoscroll (GdkEvent* event) -{ - jack_nframes_t one_page = (jack_nframes_t) rint (canvas_width * frames_per_unit); - jack_nframes_t rightmost_frame = leftmost_frame + one_page; - - jack_nframes_t frame = drag_info.current_pointer_frame; - - if (autoscroll_timeout_tag < 0) { - if (frame > rightmost_frame) { - if (rightmost_frame < max_frames) { - start_canvas_autoscroll (1); - } - } else if (frame < leftmost_frame) { - if (leftmost_frame > 0) { - start_canvas_autoscroll (-1); - } - } - } else { - if (frame >= leftmost_frame && frame < rightmost_frame) { - stop_canvas_autoscroll (); - } - } + return false; } -gint -Editor::enter_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType item_type) +bool +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 GainControlPointItem: - if (mouse_mode == MouseGain) { - cp = reinterpret_cast(gtk_object_get_data (GTK_OBJECT(item), "control_point")); + case ControlPointItem: + if (mouse_mode == MouseGain || mouse_mode == MouseObject) { + cp = static_cast(item->get_data ("control_point")); cp->set_visible (true); double at_x, at_y; at_x = cp->get_x(); at_y = cp->get_y (); - gnome_canvas_item_i2w (cp->item, &at_x, &at_y); - at_x += 20.0; - at_y += 20.0; + cp->item()->i2w (at_x, at_y); + at_x += 10.0; + at_y += 10.0; - fraction = 1.0 - (cp->get_y() / cp->line.height()); + fraction = 1.0 - (cp->get_y() / cp->line().height()); - set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y); - show_verbose_canvas_cursor (); + if (is_drawable() && !_scrubbing) { + track_canvas->get_window()->set_cursor (*fader_cursor); + } - if (is_drawable()) { - gdk_window_set_cursor (track_canvas_scroller.get_window(), 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 GainAutomationControlPointItem: - case PanAutomationControlPointItem: - case RedirectAutomationControlPointItem: - cp = reinterpret_cast(gtk_object_get_data (GTK_OBJECT(item), "control_point")); - cp->set_visible (true); - - double at_x, at_y; - at_x = cp->get_x(); - at_y = cp->get_y (); - gnome_canvas_item_i2w (cp->item, &at_x, &at_y); - at_x += 20.0; - at_y += 20.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()) { - gdk_window_set_cursor (track_canvas_scroller.get_window(), fader_cursor); - } - break; - case GainLineItem: if (mouse_mode == MouseGain) { - gnome_canvas_item_set (item, "fill_color_rgba", color_map[cEnteredGainLine], NULL); + ArdourCanvas::Line *line = dynamic_cast (item); + if (line) + line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get(); if (is_drawable()) { - gdk_window_set_cursor (track_canvas_scroller.get_window(), fader_cursor); + track_canvas->get_window()->set_cursor (*fader_cursor); } } break; - case GainAutomationLineItem: - case RedirectAutomationLineItem: - case PanAutomationLineItem: - gnome_canvas_item_set (item, "fill_color_rgba", color_map[cEnteredAutomationLine], NULL); - if (is_drawable()) { - gdk_window_set_cursor (track_canvas_scroller.get_window(), fader_cursor); + case AutomationLineItem: + if (mouse_mode == MouseGain || mouse_mode == MouseObject) { + { + ArdourCanvas::Line *line = dynamic_cast (item); + if (line) + line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get(); + } + if (is_drawable()) { + track_canvas->get_window()->set_cursor (*fader_cursor); + } } break; - case AudioRegionViewNameHighlight: + case RegionViewNameHighlight: if (is_drawable() && mouse_mode == MouseObject) { - gdk_window_set_cursor (track_canvas_scroller.get_window(), trimmer_cursor); + track_canvas->get_window()->set_cursor (*trimmer_cursor); } break; case StartSelectionTrimItem: case EndSelectionTrimItem: - /* */ + +#ifdef WITH_CMT case ImageFrameHandleStartItem: case ImageFrameHandleEndItem: case MarkerViewHandleStartItem: case MarkerViewHandleEndItem: - /* */ +#endif if (is_drawable()) { - gdk_window_set_cursor (track_canvas_scroller.get_window(), trimmer_cursor); + track_canvas->get_window()->set_cursor (*trimmer_cursor); } break; - case EditCursorItem: case PlayheadCursorItem: if (is_drawable()) { - gdk_window_set_cursor (track_canvas_scroller.get_window(), 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 AudioRegionViewName: + case RegionViewName: /* when the name is not an active item, the entire name highlight is for trimming */ - if (!reinterpret_cast (gtk_object_get_data(GTK_OBJECT(item), "regionview"))->name_active()) { + if (!reinterpret_cast (item->get_data ("regionview"))->name_active()) { if (mouse_mode == MouseObject && is_drawable()) { - gdk_window_set_cursor (track_canvas_scroller.get_window(), trimmer_cursor); + track_canvas->get_window()->set_cursor (*trimmer_cursor); } } break; @@ -1229,7 +1414,7 @@ Editor::enter_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType item_typ case AutomationTrackItem: if (is_drawable()) { - GdkCursor *cursor; + Gdk::Cursor *cursor; switch (mouse_mode) { case MouseRange: cursor = selector_cursor; @@ -1242,10 +1427,10 @@ Editor::enter_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType item_typ break; } - gdk_window_set_cursor (track_canvas_scroller.get_window(), cursor); + track_canvas->get_window()->set_cursor (*cursor); AutomationTimeAxisView* atv; - if ((atv = static_cast(gtk_object_get_data(GTK_OBJECT(item), "trackview"))) != 0) { + if ((atv = static_cast(item->get_data ("trackview"))) != 0) { clear_entered_track = false; set_entered_track (atv); } @@ -1255,29 +1440,35 @@ Editor::enter_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType item_typ case MarkerBarItem: case RangeMarkerBarItem: case TransportMarkerBarItem: + case CdMarkerBarItem: case MeterBarItem: case TempoBarItem: if (is_drawable()) { - gdk_window_set_cursor (time_canvas_scroller.get_window(), timebar_cursor); + track_canvas->get_window()->set_cursor (*timebar_cursor); } break; case MarkerItem: - if ((marker = static_cast (gtk_object_get_data (GTK_OBJECT(item), "marker"))) == 0) { + if ((marker = static_cast (item->get_data ("marker"))) == 0) { break; } - marker->set_color_rgba (color_map[cEnteredMarker]); + entered_marker = marker; + marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get()); // fall through case MeterMarkerItem: case TempoMarkerItem: if (is_drawable()) { - gdk_window_set_cursor (time_canvas_scroller.get_window(), timebar_cursor); + track_canvas->get_window()->set_cursor (*timebar_cursor); } break; case FadeInHandleItem: case FadeOutHandleItem: if (mouse_mode == MouseObject) { - gnome_canvas_item_set (item, "fill_color_rgba", 0, "outline_pixels", 1, NULL); + ArdourCanvas::SimpleRect *rect = dynamic_cast (item); + if (rect) { + rect->property_fill_color_rgba() = 0; + rect->property_outline_pixels() = 1; + } } break; @@ -1290,13 +1481,8 @@ Editor::enter_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType item_typ switch (item_type) { case GainLineItem: - case GainAutomationLineItem: - case RedirectAutomationLineItem: - case PanAutomationLineItem: - case GainControlPointItem: - case GainAutomationControlPointItem: - case PanAutomationControlPointItem: - case RedirectAutomationControlPointItem: + case AutomationLineItem: + case ControlPointItem: /* these do not affect the current entered track state */ clear_entered_track = false; break; @@ -1310,112 +1496,120 @@ Editor::enter_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType item_typ break; } - return FALSE; + return false; } -gint -Editor::leave_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType item_type) +bool +Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type) { AutomationLine* al; ControlPoint* cp; Marker *marker; Location *loc; - AudioRegionView* rv; + RegionView* rv; bool is_start; switch (item_type) { - case GainControlPointItem: - case GainAutomationControlPointItem: - case PanAutomationControlPointItem: - case RedirectAutomationControlPointItem: - cp = reinterpret_cast(gtk_object_get_data (GTK_OBJECT(item), "control_point")); - if (cp->line.npoints() > 1) { - if (!cp->selected) { + case ControlPointItem: + cp = reinterpret_cast(item->get_data ("control_point")); + if (cp->line().the_list()->interpolation() != AutomationList::Discrete) { + if (cp->line().npoints() > 1 && !cp->selected()) { cp->set_visible (false); } } if (is_drawable()) { - gdk_window_set_cursor (track_canvas_scroller.get_window(), current_canvas_cursor); + track_canvas->get_window()->set_cursor (*current_canvas_cursor); } hide_verbose_canvas_cursor (); break; - case AudioRegionViewNameHighlight: + case RegionViewNameHighlight: case StartSelectionTrimItem: case EndSelectionTrimItem: - case EditCursorItem: case PlayheadCursorItem: - /* */ + +#ifdef WITH_CMT case ImageFrameHandleStartItem: case ImageFrameHandleEndItem: case MarkerViewHandleStartItem: case MarkerViewHandleEndItem: - /* */ +#endif + if (is_drawable()) { - gdk_window_set_cursor (track_canvas_scroller.get_window(), current_canvas_cursor); + track_canvas->get_window()->set_cursor (*current_canvas_cursor); } break; case GainLineItem: - case GainAutomationLineItem: - case RedirectAutomationLineItem: - case PanAutomationLineItem: - al = reinterpret_cast (gtk_object_get_data (GTK_OBJECT(item),"line")); - gnome_canvas_item_set (item, "fill_color_rgba", al->get_line_color(), NULL); + case AutomationLineItem: + al = reinterpret_cast (item->get_data ("line")); + { + ArdourCanvas::Line *line = dynamic_cast (item); + if (line) + line->property_fill_color_rgba() = al->get_line_color(); + } if (is_drawable()) { - gdk_window_set_cursor (track_canvas_scroller.get_window(), current_canvas_cursor); + track_canvas->get_window()->set_cursor (*current_canvas_cursor); } break; - case AudioRegionViewName: + case RegionViewName: /* see enter_handler() for notes */ - if (!reinterpret_cast (gtk_object_get_data(GTK_OBJECT(item), "regionview"))->name_active()) { + if (!reinterpret_cast (item->get_data ("regionview"))->name_active()) { if (is_drawable() && mouse_mode == MouseObject) { - gdk_window_set_cursor (track_canvas_scroller.get_window(), 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()) { - gdk_window_set_cursor (time_canvas_scroller.get_window(), timebar_cursor); + track_canvas->get_window()->set_cursor (*timebar_cursor); } break; case MarkerItem: - if ((marker = static_cast (gtk_object_get_data (GTK_OBJECT(item), "marker"))) == 0) { + if ((marker = static_cast (item->get_data ("marker"))) == 0) { break; } - loc = find_location_from_marker (marker, is_start); - if (loc) location_flags_changed (loc, this); + entered_marker = 0; + if ((loc = find_location_from_marker (marker, is_start)) != 0) { + location_flags_changed (loc, this); + } // fall through case MeterMarkerItem: case TempoMarkerItem: if (is_drawable()) { - gdk_window_set_cursor (time_canvas_scroller.get_window(), timebar_cursor); + track_canvas->get_window()->set_cursor (*timebar_cursor); } break; case FadeInHandleItem: case FadeOutHandleItem: - rv = static_cast(gtk_object_get_data (GTK_OBJECT(item), "regionview")); - gnome_canvas_item_set (item, "fill_color_rgba", rv->get_fill_color(), "outline_pixels", 0, NULL); + rv = static_cast(item->get_data ("regionview")); + { + ArdourCanvas::SimpleRect *rect = dynamic_cast (item); + if (rect) { + rect->property_fill_color_rgba() = rv->get_fill_color(); + rect->property_outline_pixels() = 0; + } + } break; case AutomationTrackItem: if (is_drawable()) { - gdk_window_set_cursor (track_canvas_scroller.get_window(), current_canvas_cursor); - + track_canvas->get_window()->set_cursor (*current_canvas_cursor); clear_entered_track = true; - Main::idle.connect (mem_fun(*this, &Editor::left_automation_track)); + Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track)); } break; @@ -1423,7 +1617,7 @@ Editor::leave_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType item_typ break; } - return FALSE; + return false; } gint @@ -1433,24 +1627,105 @@ Editor::left_automation_track () set_entered_track (0); clear_entered_track = false; } - return FALSE; + return false; } -gint -Editor::motion_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType item_type) +void +Editor::scrub () { - gint x, y; + double delta; - /* We call this so that MOTION_NOTIFY events continue to be - delivered to the canvas. We need to do this because we set - Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces - the density of the events, at the expense of a round-trip - to the server. Given that this will mostly occur on cases - where DISPLAY = :0.0, and given the cost of what the motion - event might do, its a good tradeoff. - */ + if (scrubbing_direction == 0) { + /* first move */ + session->request_locate (drag_info.current_pointer_frame, false); + session->request_transport_speed (0.1); + scrubbing_direction = 1; + + } else { + + if (last_scrub_x > drag_info.current_pointer_x) { + + /* pointer moved to the left */ + + if (scrubbing_direction > 0) { + + /* we reversed direction to go backwards */ + + scrub_reversals++; + scrub_reverse_distance += (int) (last_scrub_x - drag_info.current_pointer_x); + + } else { + + /* still moving to the left (backwards) */ + + scrub_reversals = 0; + scrub_reverse_distance = 0; + + delta = 0.01 * (last_scrub_x - drag_info.current_pointer_x); + session->request_transport_speed (session->transport_speed() - delta); + } + + } else { + /* pointer moved to the right */ + + if (scrubbing_direction < 0) { + /* we reversed direction to go forward */ + + scrub_reversals++; + scrub_reverse_distance += (int) (drag_info.current_pointer_x - last_scrub_x); + + } else { + /* still moving to the right */ + + scrub_reversals = 0; + scrub_reverse_distance = 0; + + delta = 0.01 * (drag_info.current_pointer_x - last_scrub_x); + session->request_transport_speed (session->transport_speed() + delta); + } + } + + /* if there have been more than 2 opposite motion moves detected, or one that moves + back more than 10 pixels, reverse direction + */ + + if (scrub_reversals >= 2 || scrub_reverse_distance > 10) { + + if (scrubbing_direction > 0) { + /* was forwards, go backwards */ + session->request_transport_speed (-0.1); + scrubbing_direction = -1; + } else { + /* was backwards, go forwards */ + session->request_transport_speed (0.1); + scrubbing_direction = 1; + } + + scrub_reverse_distance = 0; + scrub_reversals = 0; + } + } + + last_scrub_x = drag_info.current_pointer_x; +} + +bool +Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll) +{ + if (event->motion.is_hint) { + gint x, y; + + /* We call this so that MOTION_NOTIFY events continue to be + delivered to the canvas. We need to do this because we set + Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces + the density of the events, at the expense of a round-trip + to the server. Given that this will mostly occur on cases + where DISPLAY = :0.0, and given the cost of what the motion + event might do, its a good tradeoff. + */ - track_canvas->get_pointer (x, y); + track_canvas->get_pointer (x, y); + } if (current_stepping_trackview) { /* don't keep the persistent stepped trackview if the mouse moves */ @@ -1460,21 +1735,41 @@ Editor::motion_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType item_ty if (session && session->actively_recording()) { /* Sorry. no dragging stuff around while we record */ - return TRUE; + 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); - if (drag_info.item) { - /* item != 0 is the best test i can think of for - dragging. + + + switch (mouse_mode) { + case MouseAudition: + if (_scrubbing) { + scrub (); + } + break; + + default: + break; + } + + + if (!from_autoscroll && drag_info.item) { + /* item != 0 is the best test i can think of for dragging. */ - if (!drag_info.move_threshold_passsed) - { - drag_info.move_threshold_passsed = (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4); + 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_passsed) { + + 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; @@ -1486,38 +1781,39 @@ Editor::motion_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType item_ty switch (item_type) { case PlayheadCursorItem: - case EditCursorItem: case MarkerItem: - case GainControlPointItem: - case RedirectAutomationControlPointItem: - case GainAutomationControlPointItem: - case PanAutomationControlPointItem: + case ControlPointItem: + case RangeMarkerBarItem: + case TransportMarkerBarItem: + case CdMarkerBarItem: case TempoMarkerItem: case MeterMarkerItem: - case AudioRegionViewNameHighlight: + case RegionViewNameHighlight: case StartSelectionTrimItem: case EndSelectionTrimItem: case SelectionItem: case GainLineItem: - case RedirectAutomationLineItem: - case GainAutomationLineItem: - case PanAutomationLineItem: + case AutomationLineItem: case FadeInHandleItem: case FadeOutHandleItem: - /* */ + +#ifdef WITH_CMT case ImageFrameHandleStartItem: case ImageFrameHandleEndItem: case MarkerViewHandleStartItem: case MarkerViewHandleEndItem: - /* */ - if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK || - (event->motion.state & GDK_BUTTON2_MASK))) { - maybe_autoscroll (event); - (this->*(drag_info.motion_callback)) (item, event); - goto handled; - } - goto not_handled; +#endif + if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK || + (event->motion.state & Gdk::BUTTON2_MASK))) { + if (!from_autoscroll) { + maybe_autoscroll_horizontally (&event->motion); + } + (this->*(drag_info.motion_callback)) (item, event); + goto handled; + } + goto not_handled; + break; default: break; } @@ -1527,9 +1823,12 @@ Editor::motion_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType item_ty case MouseRange: case MouseZoom: case MouseTimeFX: + case MouseNote: if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK || (event->motion.state & GDK_BUTTON2_MASK))) { - maybe_autoscroll (event); + if (!from_autoscroll) { + maybe_autoscroll (&event->motion); + } (this->*(drag_info.motion_callback)) (item, event); goto handled; } @@ -1541,15 +1840,55 @@ Editor::motion_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType item_ty } handled: - track_canvas_motion (item, event); - return TRUE; + track_canvas_motion (event); + // drag_info.last_pointer_frame = drag_info.current_pointer_frame; + return true; not_handled: - return FALSE; + return false; +} + +void +Editor::break_drag () +{ + stop_canvas_autoscroll (); + hide_verbose_canvas_cursor (); + + if (drag_info.item) { + drag_info.item->ungrab (0); + + /* put it back where it came from */ + + double cxw, cyw; + cxw = 0; + cyw = 0; + drag_info.item->i2w (cxw, cyw); + drag_info.item->move (drag_info.original_x - cxw, drag_info.original_y - cyw); + } + + finalize_drag (); +} + +void +Editor::finalize_drag () +{ + drag_info.item = 0; + drag_info.copy = false; + drag_info.motion_callback = 0; + drag_info.finished_callback = 0; + drag_info.dest_trackview = 0; + drag_info.source_trackview = 0; + drag_info.last_frame_position = 0; + drag_info.grab_frame = 0; + drag_info.last_pointer_frame = 0; + drag_info.current_pointer_frame = 0; + drag_info.brushing = false; + range_marker_drag_rect->hide(); + drag_info.clear_copied_locations (); } void -Editor::start_grab (GdkEvent* event, GdkCursor *cursor) +Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor) { if (drag_info.item == 0) { fatal << _("programming error: start_grab called without drag item") << endmsg; @@ -1558,34 +1897,46 @@ Editor::start_grab (GdkEvent* event, GdkCursor *cursor) } if (cursor == 0) { - cursor = grabber_cursor; + cursor = which_grabber_cursor (); } - // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained + // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained if (event->button.button == 2) { - drag_info.x_constrained = true; + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) { + 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.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_passsed = false; + 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.clear_copied_locations (); - gnome_canvas_item_grab (drag_info.item, - Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, - cursor, + drag_info.original_x = 0; + drag_info.original_y = 0; + drag_info.item->i2w (drag_info.original_x, drag_info.original_y); + + drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, + *cursor, event->button.time); if (session && session->transport_rolling()) { @@ -1606,8 +1957,21 @@ Editor::start_grab (GdkEvent* event, GdkCursor *cursor) } } +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 = which_grabber_cursor (); + } + + drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time); +} + bool -Editor::end_grab (GnomeCanvasItem* item, GdkEvent* event) +Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event) { bool did_drag = false; @@ -1617,9 +1981,11 @@ Editor::end_grab (GnomeCanvasItem* item, GdkEvent* event) return false; } - gnome_canvas_item_ungrab (drag_info.item, event->button.time); + 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); } @@ -1627,56 +1993,13 @@ Editor::end_grab (GnomeCanvasItem* item, GdkEvent* event) hide_verbose_canvas_cursor(); - drag_info.item = 0; - drag_info.copy = false; - drag_info.motion_callback = 0; - drag_info.finished_callback = 0; - drag_info.last_trackview = 0; - drag_info.last_frame_position = 0; - drag_info.grab_frame = 0; - drag_info.last_pointer_frame = 0; - drag_info.current_pointer_frame = 0; - drag_info.brushing = false; - - if (drag_info.copied_location) { - delete drag_info.copied_location; - drag_info.copied_location = 0; - } + finalize_drag (); return did_drag; } void -Editor::set_edit_cursor (GdkEvent* event) -{ - jack_nframes_t pointer_frame = event_frame (event); - - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { - if (snap_type != SnapToEditCursor) { - snap_to (pointer_frame); - } - } - - edit_cursor->set_position (pointer_frame); - edit_cursor_clock.set (pointer_frame); -} - -void -Editor::set_playhead_cursor (GdkEvent* event) -{ - jack_nframes_t pointer_frame = event_frame (event); - - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { - snap_to (pointer_frame); - } - - if (session) { - session->request_locate (pointer_frame, session->transport_rolling()); - } -} - -void -Editor::start_fade_in_grab (GnomeCanvasItem* item, GdkEvent* event) +Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event) { drag_info.item = item; drag_info.motion_callback = &Editor::fade_in_drag_motion_callback; @@ -1684,24 +2007,25 @@ Editor::start_fade_in_grab (GnomeCanvasItem* item, GdkEvent* event) start_grab (event); - if ((drag_info.data = (gtk_object_get_data (GTK_OBJECT(item), "regionview"))) == 0) { + 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 - ((jack_nframes_t) arv->region.fade_in().back()->when + arv->region.position()); + + drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes64_t) arv->audio_region()->fade_in()->back()->when + arv->region()->position()); } void -Editor::fade_in_drag_motion_callback (GnomeCanvasItem* item, GdkEvent* event) +Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) { AudioRegionView* arv = static_cast(drag_info.data); - jack_nframes_t pos; - jack_nframes_t fade_length; + nframes64_t pos; + nframes64_t fade_length; - if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { + if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset; } else { @@ -1711,62 +2035,80 @@ Editor::fade_in_drag_motion_callback (GnomeCanvasItem* item, GdkEvent* event) if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { snap_to (pos); } - - if (pos < (arv->region.position() + 64)) { + + 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 if (pos > arv->region()->last_frame()) { + fade_length = arv->region()->length(); } else { - fade_length = pos - arv->region.position(); - } + 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; + } - arv->reset_fade_in_shape_width (fade_length); + tmp->reset_fade_in_shape_width (fade_length); + } - show_verbose_duration_cursor (arv->region.position(), arv->region.position() + fade_length, 10); + 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 (GnomeCanvasItem* item, GdkEvent* event) +Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event) { - if (drag_info.first_move) return; - AudioRegionView* arv = static_cast(drag_info.data); - jack_nframes_t pos; - jack_nframes_t fade_length; + nframes64_t pos; + nframes64_t fade_length; + + if (drag_info.first_move) return; - if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { + if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset; - } - else { + } else { pos = 0; } - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { - snap_to (pos); - } - - if (pos < (arv->region.position() + 64)) { + 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(); } - 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")); - session->add_undo (arv->region.get_memento()); - arv->region.set_fade_in_length (fade_length); - session->add_redo_no_execute (arv->region.get_memento()); + + 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); + tmp->audio_region()->set_fade_in_active (true); + + XMLNode &after = alist->get_state(); + session->add_command(new MementoCommand(*alist.get(), &before, &after)); + } + commit_reversible_command (); - fade_in_drag_motion_callback (item, event); } void -Editor::start_fade_out_grab (GnomeCanvasItem* item, GdkEvent* event) +Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event) { drag_info.item = item; drag_info.motion_callback = &Editor::fade_out_drag_motion_callback; @@ -1774,61 +2116,71 @@ Editor::start_fade_out_grab (GnomeCanvasItem* item, GdkEvent* event) start_grab (event); - if ((drag_info.data = (gtk_object_get_data (GTK_OBJECT(item), "regionview"))) == 0) { + 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() - (jack_nframes_t) arv->region.fade_out().back()->when + arv->region.position()); + drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes64_t) arv->audio_region()->fade_out()->back()->when + arv->region()->position()); } void -Editor::fade_out_drag_motion_callback (GnomeCanvasItem* item, GdkEvent* event) +Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) { AudioRegionView* arv = static_cast(drag_info.data); - jack_nframes_t pos; - jack_nframes_t fade_length; + nframes64_t pos; + nframes64_t fade_length; - if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { + if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset; - } - else { + } else { pos = 0; } if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { snap_to (pos); } - - if (pos > (arv->region.last_frame() - 64)) { + + 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 if (pos < arv->region()->position()) { + fade_length = arv->region()->length(); } else { - fade_length = arv->region.last_frame() - pos; + 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; + } - arv->reset_fade_out_shape_width (fade_length); + tmp->reset_fade_out_shape_width (fade_length); + } - show_verbose_duration_cursor (arv->region.last_frame() - fade_length, arv->region.last_frame(), 10); + 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 (GnomeCanvasItem* item, GdkEvent* event) +Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event) { if (drag_info.first_move) return; AudioRegionView* arv = static_cast(drag_info.data); - jack_nframes_t pos; - jack_nframes_t fade_length; + nframes64_t pos; + nframes64_t fade_length; - if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { + if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset; } else { @@ -1839,27 +2191,41 @@ Editor::fade_out_drag_finished_callback (GnomeCanvasItem* item, GdkEvent* event) snap_to (pos); } - if (pos > (arv->region.last_frame() - 64)) { + 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 if (pos < arv->region()->position()) { + fade_length = arv->region()->length(); } else { - fade_length = arv->region.last_frame() - pos; + fade_length = arv->region()->last_frame() - pos; } begin_reversible_command (_("change fade out length")); - session->add_undo (arv->region.get_memento()); - arv->region.set_fade_out_length (fade_length); - session->add_redo_no_execute (arv->region.get_memento()); - commit_reversible_command (); - fade_out_drag_motion_callback (item, event); + 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); + tmp->audio_region()->set_fade_out_active (true); + + XMLNode &after = alist->get_state(); + session->add_command(new MementoCommand(*alist.get(), &before, &after)); + } + + commit_reversible_command (); } void -Editor::start_cursor_grab (GnomeCanvasItem* item, GdkEvent* event) +Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event) { drag_info.item = item; drag_info.motion_callback = &Editor::cursor_drag_motion_callback; @@ -1867,17 +2233,23 @@ Editor::start_cursor_grab (GnomeCanvasItem* item, GdkEvent* event) start_grab (event); - if ((drag_info.data = (gtk_object_get_data (GTK_OBJECT(item), "cursor"))) == 0) { + 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 (session && cursor == playhead_cursor) { - if (drag_info.was_rolling) { + 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; @@ -1886,12 +2258,12 @@ Editor::start_cursor_grab (GnomeCanvasItem* item, GdkEvent* event) } void -Editor::cursor_drag_motion_callback (GnomeCanvasItem* item, GdkEvent* event) +Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) { Cursor* cursor = (Cursor *) drag_info.data; - jack_nframes_t adjusted_frame; + nframes64_t adjusted_frame; - if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { + if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset; } else { @@ -1899,7 +2271,7 @@ Editor::cursor_drag_motion_callback (GnomeCanvasItem* item, GdkEvent* event) } if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { - if (cursor != edit_cursor || snap_type != SnapToEditCursor) { + if (cursor == playhead_cursor) { snap_to (adjusted_frame); } } @@ -1907,31 +2279,31 @@ Editor::cursor_drag_motion_callback (GnomeCanvasItem* item, GdkEvent* event) if (adjusted_frame == drag_info.last_pointer_frame) return; cursor->set_position (adjusted_frame); - - if (cursor == edit_cursor) { - edit_cursor_clock.set (cursor->current_frame); - } show_verbose_time_cursor (cursor->current_frame, 10); +#ifdef GTKOSX + track_canvas->update_now (); +#endif + UpdateAllTransportClocks (cursor->current_frame); + drag_info.last_pointer_frame = adjusted_frame; drag_info.first_move = false; } void -Editor::cursor_drag_finished_callback (GnomeCanvasItem* item, GdkEvent* event) +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 (item == &playhead_cursor->canvas_item) { if (session) { session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling); } - } else if (item == edit_cursor->canvas_item) { - edit_cursor->set_position (edit_cursor->current_frame); - edit_cursor_clock.set (edit_cursor->current_frame); } } @@ -1942,21 +2314,22 @@ Editor::update_marker_drag_item (Location *location) double x2 = frame_to_pixel (location->end()); if (location->is_mark()) { - marker_drag_line_points->coords[0] = x1; - marker_drag_line_points->coords[2] = x1; - gnome_canvas_item_set (marker_drag_line, "points", marker_drag_line_points, NULL); - } - else { - gnome_canvas_item_set (range_marker_drag_rect, "x1", x1, "x2", x2, NULL); + 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 (GnomeCanvasItem* item, GdkEvent* event) +Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event) { Marker* marker; - if ((marker = static_cast (gtk_object_get_data (GTK_OBJECT(item), "marker"))) == 0) { + if ((marker = static_cast (item->get_data ("marker"))) == 0) { fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg; /*NOTREACHED*/ } @@ -1972,129 +2345,319 @@ Editor::start_marker_grab (GnomeCanvasItem* item, GdkEvent* event) start_grab (event); - drag_info.copied_location = new Location (*location); + _dragging_edit_point = true; + drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end()); - + update_marker_drag_item (location); if (location->is_mark()) { - gnome_canvas_item_show (marker_drag_line); - gnome_canvas_item_raise_to_top (marker_drag_line); + // marker_drag_line->show(); + // marker_drag_line->raise_to_top(); + } else { + range_marker_drag_rect->show(); + //range_marker_drag_rect->raise_to_top(); } - else { - gnome_canvas_item_show (range_marker_drag_rect); - gnome_canvas_item_raise_to_top (range_marker_drag_rect); + + 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: + if (!selection->selected (marker)) { + selection->set (marker); + } + break; + case Selection::Extend: + { + Locations::LocationList ll; + list to_add; + nframes64_t s, e; + selection->markers.range (s, e); + s = min (marker->position(), s); + e = max (marker->position(), e); + s = min (s, e); + e = max (s, e); + if (e < max_frames) { + ++e; + } + session->locations()->find_all_between (s, e, ll, Location::Flags (0)); + for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) { + LocationMarkers* lm = find_location_markers (*i); + if (lm) { + if (lm->start) { + to_add.push_back (lm->start); + } + if (lm->end) { + to_add.push_back (lm->end); + } + } + } + if (!to_add.empty()) { + selection->add (to_add); + } + break; + } + case Selection::Add: + selection->add (marker); + break; + } + + /* set up copies for us to manipulate during the drag */ + + drag_info.clear_copied_locations (); + + for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) { + Location *l = find_location_from_marker (*i, is_start); + drag_info.copied_locations.push_back (new Location (*l)); } - - if (is_start) show_verbose_time_cursor (location->start(), 10); - else show_verbose_time_cursor (location->end(), 10); } void -Editor::marker_drag_motion_callback (GnomeCanvasItem* item, GdkEvent* event) +Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) { - jack_nframes_t f_delta; - Marker* marker = (Marker *) drag_info.data; - Location *real_location; - Location *copy_location; + nframes64_t f_delta = 0; + nframes64_t newframe; bool is_start; bool move_both = false; + Marker* dragged_marker = (Marker*) drag_info.data; + Marker* marker; + Location *real_location; + Location *copy_location; - jack_nframes_t newframe; - if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) { + if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) { newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset; - } - else { + } else { newframe = 0; } - - jack_nframes_t next = newframe; + + nframes64_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; + if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) { + return; + } - /* call this to find out if its the start or end */ - - real_location = find_location_from_marker (marker, is_start); + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) { + move_both = true; + } - /* use the copy that we're "dragging" around */ - - copy_location = drag_info.copied_location; + MarkerSelection::iterator i; + list::iterator x; - f_delta = copy_location->end() - copy_location->start(); - - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) { - move_both = true; + /* find the marker we're dragging, and compute the delta */ + + for (i = selection->markers.begin(), x = drag_info.copied_locations.begin(); + x != drag_info.copied_locations.end() && i != selection->markers.end(); + ++i, ++x) { + + copy_location = *x; + marker = *i; + + if (marker == dragged_marker) { + + if ((real_location = find_location_from_marker (marker, is_start)) == 0) { + /* que pasa ?? */ + return; + } + + if (real_location->is_mark()) { + f_delta = newframe - copy_location->start(); + } else { + + + switch (marker->type()) { + case Marker::Start: + case Marker::LoopStart: + case Marker::PunchIn: + f_delta = newframe - copy_location->start(); + break; + + case Marker::End: + case Marker::LoopEnd: + case Marker::PunchOut: + f_delta = newframe - copy_location->end(); + break; + default: + /* what kind of marker is this ? */ + return; + } + } + break; + } } - if (is_start) { // start marker + if (i == selection->markers.end()) { + /* hmm, impossible - we didn't find the dragged marker */ + return; + } + + /* now move them all */ + + for (i = selection->markers.begin(), x = drag_info.copied_locations.begin(); + x != drag_info.copied_locations.end() && i != selection->markers.end(); + ++i, ++x) { - 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); + copy_location = *x; + marker = *i; + + /* call this to find out if its the start or end */ + + if ((real_location = find_location_from_marker (marker, is_start)) == 0) { + continue; + } + + if (real_location->locked()) { + continue; } - } else { // end marker + if (copy_location->is_mark()) { - 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); + /* just move it */ + + copy_location->set_start (copy_location->start() + f_delta); + + } else { + + nframes64_t new_start = copy_location->start() + f_delta; + nframes64_t new_end = copy_location->end() + f_delta; - } else if (newframe > 0) { - snap_to (next, -1, true); - copy_location->set_start (next); - copy_location->set_end (newframe); + if (is_start) { // start-of-range marker + + if (move_both) { + copy_location->set_start (new_start); + copy_location->set_end (new_end); + } else if (new_start < copy_location->end()) { + copy_location->set_start (new_start); + } 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 (new_end); + copy_location->set_start (new_start); + } else if (new_end > copy_location->start()) { + copy_location->set_end (new_end); + } else if (newframe > 0) { + snap_to (next, -1, true); + copy_location->set_start (next); + copy_location->set_end (newframe); + } + } + } + update_marker_drag_item (copy_location); + + LocationMarkers* lm = find_location_markers (real_location); + + if (lm) { + lm->set_position (copy_location->start(), copy_location->end()); } } drag_info.last_pointer_frame = drag_info.current_pointer_frame; drag_info.first_move = false; - update_marker_drag_item (copy_location); + if (drag_info.copied_locations.empty()) { + abort(); + } - LocationMarkers* lm = find_location_markers (real_location); - lm->set_position (copy_location->start(), copy_location->end()); - + edit_point_clock.set (drag_info.copied_locations.front()->start()); show_verbose_time_cursor (newframe, 10); + +#ifdef GTKOSX + track_canvas->update_now (); +#endif + edit_point_clock.set (copy_location->start()); } void -Editor::marker_drag_finished_callback (GnomeCanvasItem* item, GdkEvent* event) +Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event) { if (drag_info.first_move) { - marker_drag_motion_callback (item, event); + /* just a click, do nothing but finish + off the selection process + */ + + Selection::Operation op = Keyboard::selection_type (event->button.state); + Marker* marker = (Marker *) drag_info.data; + + switch (op) { + case Selection::Set: + if (selection->selected (marker) && selection->markers.size() > 1) { + selection->set (marker); + } + break; + + case Selection::Toggle: + case Selection::Extend: + case Selection::Add: + break; + } + + return; } + + _dragging_edit_point = false; - Marker* marker = (Marker *) drag_info.data; + + begin_reversible_command ( _("move marker") ); + XMLNode &before = session->locations()->get_state(); + + MarkerSelection::iterator i; + list::iterator x; bool is_start; - Location * location = find_location_from_marker (marker, is_start); - if (location) { - location->set (drag_info.copied_location->start(), drag_info.copied_location->end()); + + for (i = selection->markers.begin(), x = drag_info.copied_locations.begin(); + x != drag_info.copied_locations.end() && i != selection->markers.end(); + ++i, ++x) { + + Location * location = find_location_from_marker ((*i), is_start); + + if (location) { + + if (location->locked()) { + return; + } + + if (location->is_mark()) { + location->set_start ((*x)->start()); + } else { + location->set ((*x)->start(), (*x)->end()); + } + } } + + XMLNode &after = session->locations()->get_state(); + session->add_command(new MementoCommand(*(session->locations()), &before, &after)); + commit_reversible_command (); - gnome_canvas_item_hide (marker_drag_line); - gnome_canvas_item_hide (range_marker_drag_rect); + marker_drag_line->hide(); + range_marker_drag_rect->hide(); } void -Editor::start_meter_marker_grab (GnomeCanvasItem* item, GdkEvent* event) +Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event) { Marker* marker; MeterMarker* meter_marker; - if ((marker = reinterpret_cast (gtk_object_get_data (GTK_OBJECT(item), "marker"))) == 0) { + if ((marker = reinterpret_cast (item->get_data ("marker"))) == 0) { fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg; /*NOTREACHED*/ } @@ -2108,6 +2671,7 @@ Editor::start_meter_marker_grab (GnomeCanvasItem* item, GdkEvent* event) } drag_info.item = item; + drag_info.copy = false; drag_info.data = marker; drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback; drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback; @@ -2120,12 +2684,45 @@ Editor::start_meter_marker_grab (GnomeCanvasItem* item, GdkEvent* event) } void -Editor::meter_marker_drag_motion_callback (GnomeCanvasItem* item, GdkEvent* event) +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; - jack_nframes_t adjusted_frame; + nframes64_t adjusted_frame; - if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { + if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset; } else { @@ -2148,11 +2745,11 @@ Editor::meter_marker_drag_motion_callback (GnomeCanvasItem* item, GdkEvent* even } void -Editor::meter_marker_drag_finished_callback (GnomeCanvasItem* item, GdkEvent* event) +Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event) { if (drag_info.first_move) return; - meter_marker_drag_motion_callback (item, event); + meter_marker_drag_motion_callback (drag_info.item, event); MeterMarker* marker = (MeterMarker *) drag_info.data; BBT_Time when; @@ -2160,20 +2757,34 @@ Editor::meter_marker_drag_finished_callback (GnomeCanvasItem* item, GdkEvent* ev TempoMap& map (session->tempo_map()); map.bbt_time (drag_info.last_pointer_frame, when); - begin_reversible_command (_("move meter mark")); - session->add_undo (map.get_memento()); - map.move_meter (marker->meter(), when); - session->add_redo_no_execute (map.get_memento()); - commit_reversible_command (); + 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 (GnomeCanvasItem* item, GdkEvent* event) +Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event) { Marker* marker; TempoMarker* tempo_marker; - if ((marker = reinterpret_cast (gtk_object_get_data (GTK_OBJECT(item), "tempo_marker"))) == 0) { + if ((marker = reinterpret_cast (item->get_data ("marker"))) == 0) { fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg; /*NOTREACHED*/ } @@ -2190,6 +2801,7 @@ Editor::start_tempo_marker_grab (GnomeCanvasItem* item, GdkEvent* event) } drag_info.item = item; + drag_info.copy = false; drag_info.data = marker; drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback; drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback; @@ -2201,12 +2813,48 @@ Editor::start_tempo_marker_grab (GnomeCanvasItem* item, GdkEvent* event) } void -Editor::tempo_marker_drag_motion_callback (GnomeCanvasItem* item, GdkEvent* event) +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; - jack_nframes_t adjusted_frame; + nframes64_t adjusted_frame; - if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { + if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset; } else { @@ -2230,63 +2878,77 @@ Editor::tempo_marker_drag_motion_callback (GnomeCanvasItem* item, GdkEvent* even } void -Editor::tempo_marker_drag_finished_callback (GnomeCanvasItem* item, GdkEvent* event) +Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event) { if (drag_info.first_move) return; - tempo_marker_drag_motion_callback (item, event); + 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); - - begin_reversible_command (_("move tempo mark")); - session->add_undo (map.get_memento()); - map.move_tempo (marker->tempo(), when); - session->add_redo_no_execute (map.get_memento()); - commit_reversible_command (); + + 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 (GnomeCanvasItem*item, GdkEvent* event) +Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event) { ControlPoint* control_point; - if ((control_point = reinterpret_cast (gtk_object_get_data (GTK_OBJECT(item), "control_point"))) == 0) { + 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)) { + 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); + control_point->line().remove_point (*control_point); } void -Editor::remove_control_point (GnomeCanvasItem*item, GdkEvent* event) +Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event) { ControlPoint* control_point; - if ((control_point = reinterpret_cast (gtk_object_get_data (GTK_OBJECT(item), "control_point"))) == 0) { + 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); + control_point->line().remove_point (*control_point); } void -Editor::start_control_point_grab (GnomeCanvasItem* item, GdkEvent* event) +Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event) { ControlPoint* control_point; - - if ((control_point = reinterpret_cast (gtk_object_get_data (GTK_OBJECT(item), "control_point"))) == 0) { + + 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*/ } @@ -2298,81 +2960,119 @@ Editor::start_control_point_grab (GnomeCanvasItem* item, GdkEvent* event) start_grab (event, fader_cursor); - control_point->line.start_drag (control_point, 0); + // 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); - float fraction = 1.0 - (control_point->get_y() / 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); + drag_info.grab_frame = pixel_to_frame(drag_info.grab_x); + + control_point->line().start_drag (control_point, drag_info.grab_frame, 0); + + float fraction = 1.0 - (control_point->get_y() / control_point->line().height()); + set_verbose_canvas_cursor (control_point->line().get_verbose_cursor_string (fraction), + drag_info.current_pointer_x + 10, drag_info.current_pointer_y + 10); show_verbose_canvas_cursor (); } void -Editor::control_point_drag_motion_callback (GnomeCanvasItem* item, GdkEvent* event) +Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) { ControlPoint* cp = reinterpret_cast (drag_info.data); - double cx = drag_info.current_pointer_x; - double cy = drag_info.current_pointer_y; + double dx = drag_info.current_pointer_x - drag_info.last_pointer_x; + double dy = drag_info.current_pointer_y - drag_info.last_pointer_y; + + if (event->button.state & Keyboard::SecondaryModifier) { + dx *= 0.1; + dy *= 0.1; + } + + double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx; + double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy; - drag_info.cumulative_x_drag = cx - drag_info.grab_x ; - drag_info.cumulative_y_drag = cy - drag_info.grab_y ; + // 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); - bool x_constrained = false; + // 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) { - if (fabs(drag_info.cumulative_x_drag) < fabs(drag_info.cumulative_y_drag)) { - cx = drag_info.grab_x; - x_constrained = true; + cx = drag_info.grab_x; + } + if (drag_info.y_constrained) { + cy = drag_info.grab_y; + } - } else { - 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; - gnome_canvas_item_w2i (cp->line.parent_group(), &cx, &cy); + cp->line().parent_group().w2i (cx, cy); cx = max (0.0, cx); cy = max (0.0, cy); - cy = min ((double) cp->line.height(), cy); + cy = min ((double) cp->line().height(), cy); //translate cx to frames - jack_nframes_t cx_frames = (jack_nframes_t) floor (cx * frames_per_unit); + nframes64_t cx_frames = unit_to_frame (cx); - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !x_constrained) { + if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) { snap_to (cx_frames); } - float fraction = 1.0 - (cy / cp->line.height()); - + float fraction = 1.0 - (cy / cp->line().height()); + bool push; - if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) { + if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) { push = true; } else { push = false; } - cp->line.point_drag (*cp, cx_frames , fraction, push); + cp->line().point_drag (*cp, cx_frames , fraction, push); - set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction)); + set_verbose_canvas_cursor_text (cp->line().get_verbose_cursor_string (fraction)); + + drag_info.first_move = false; } void -Editor::control_point_drag_finished_callback (GnomeCanvasItem* item, GdkEvent* event) +Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event) { ControlPoint* cp = reinterpret_cast (drag_info.data); - control_point_drag_motion_callback (item, event); - cp->line.end_drag (cp); + + 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::TertiaryModifier)) { + reset_point_selection (); + } + + } else { + control_point_drag_motion_callback (item, event); + } + cp->line().end_drag (cp); } void -Editor::start_line_grab_from_regionview (GnomeCanvasItem* item, GdkEvent* event) +Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event) { switch (mouse_mode) { case MouseGain: - start_line_grab (clicked_regionview->get_gain_line(), event); + assert(dynamic_cast(clicked_regionview)); + start_line_grab (dynamic_cast(clicked_regionview)->get_gain_line(), event); break; default: break; @@ -2380,11 +3080,11 @@ Editor::start_line_grab_from_regionview (GnomeCanvasItem* item, GdkEvent* event) } void -Editor::start_line_grab_from_line (GnomeCanvasItem* item, GdkEvent* event) +Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event) { AutomationLine* al; - if ((al = reinterpret_cast (gtk_object_get_data (GTK_OBJECT(item), "line"))) == 0) { + if ((al = reinterpret_cast (item->get_data ("line"))) == 0) { fatal << _("programming error: line canvas item has no line pointer!") << endmsg; /*NOTREACHED*/ } @@ -2397,7 +3097,7 @@ Editor::start_line_grab (AutomationLine* line, GdkEvent* event) { double cx; double cy; - jack_nframes_t frame_within_region; + nframes64_t frame_within_region; /* need to get x coordinate in terms of parent (TimeAxisItemView) origin. @@ -2405,8 +3105,8 @@ Editor::start_line_grab (AutomationLine* line, GdkEvent* event) cx = event->button.x; cy = event->button.y; - gnome_canvas_item_w2i (line->parent_group(), &cx, &cy); - frame_within_region = (jack_nframes_t) floor (cx * frames_per_unit); + line->parent_group().w2i (cx, cy); + frame_within_region = (nframes64_t) floor (cx * frames_per_unit); if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before, current_line_drag_info.after)) { @@ -2414,7 +3114,7 @@ Editor::start_line_grab (AutomationLine* line, GdkEvent* event) return; } - drag_info.item = line->grab_item(); + 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; @@ -2423,28 +3123,48 @@ Editor::start_line_grab (AutomationLine* line, GdkEvent* event) double fraction = 1.0 - (cy / line->height()); - line->start_drag (0, fraction); + 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); + drag_info.current_pointer_x + 10, drag_info.current_pointer_y + 10); show_verbose_canvas_cursor (); } void -Editor::line_drag_motion_callback (GnomeCanvasItem* item, GdkEvent* event) +Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) { AutomationLine* line = reinterpret_cast (drag_info.data); - double cx = drag_info.current_pointer_x; - double cy = drag_info.current_pointer_y; - gnome_canvas_item_w2i (line->parent_group(), &cx, &cy); - - double fraction; - fraction = 1.0 - (cy / line->height()); + double dy = drag_info.current_pointer_y - drag_info.last_pointer_y; + + if (event->button.state & Keyboard::SecondaryModifier) { + dy *= 0.1; + } + + 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; + + cy = max (0.0, cy); + cy = min ((double) line->height(), cy); + + double fraction = 1.0 - (cy / line->height()); bool push; - if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) { + if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) { push = false; } else { push = true; @@ -2456,7 +3176,7 @@ Editor::line_drag_motion_callback (GnomeCanvasItem* item, GdkEvent* event) } void -Editor::line_drag_finished_callback (GnomeCanvasItem* item, GdkEvent* event) +Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event) { AutomationLine* line = reinterpret_cast (drag_info.data); line_drag_motion_callback (item, event); @@ -2464,125 +3184,98 @@ Editor::line_drag_finished_callback (GnomeCanvasItem* item, GdkEvent* event) } void -Editor::start_region_grab (GnomeCanvasItem* item, GdkEvent* event) +Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event) { - if (selection->audio_regions.empty() || clicked_regionview == 0) { + if (selection->regions.empty() || clicked_regionview == 0) { return; } - + _region_motion_group->raise_to_top (); drag_info.copy = false; drag_info.item = item; drag_info.data = clicked_regionview; - drag_info.motion_callback = &Editor::region_drag_motion_callback; - drag_info.finished_callback = &Editor::region_drag_finished_callback; + + if (Config->get_edit_mode() == Splice) { + drag_info.motion_callback = &Editor::region_drag_splice_motion_callback; + drag_info.finished_callback = &Editor::region_drag_splice_finished_callback; + } else { + drag_info.motion_callback = &Editor::region_drag_motion_callback; + drag_info.finished_callback = &Editor::region_drag_finished_callback; + } start_grab (event); double speed = 1.0; - TimeAxisView* tvp = clicked_trackview; - AudioTimeAxisView* tv = dynamic_cast(tvp); + TimeAxisView* tvp = clicked_axisview; + RouteTimeAxisView* tv = dynamic_cast(tvp); - if (tv && tv->is_audio_track()) { + if (tv && tv->is_track()) { speed = tv->get_diskstream()->speed(); } - drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed); + drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed); drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position; - drag_info.last_trackview = &clicked_regionview->get_time_axis_view(); + drag_info.source_trackview = &clicked_regionview->get_time_axis_view(); + drag_info.dest_trackview = drag_info.source_trackview; // we want a move threshold drag_info.want_move_threshold = true; - show_verbose_time_cursor (drag_info.last_frame_position, 10); begin_reversible_command (_("move region(s)")); + + /* sync the canvas to what we think is its current state */ + track_canvas->update_now(); } void -Editor::start_region_copy_grab (GnomeCanvasItem* item, GdkEvent* event) +Editor::start_create_region_grab (ArdourCanvas::Item* item, GdkEvent* event) { - if (selection->audio_regions.empty() || clicked_regionview == 0) { - return; - } - - /* this is committed in the grab finished callback. */ - - begin_reversible_command (_("Drag region copy")); - - /* duplicate the region(s) */ - - vector new_regionviews; - - for (list::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) { - AudioRegionView* rv; - - rv = (*i); - - Playlist* to_playlist = rv->region.playlist(); - AudioTimeAxisView* atv = dynamic_cast(&rv->get_time_axis_view()); - - session->add_undo (to_playlist->get_memento ()); - latest_regionview = 0; - - sigc::connection c = atv->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view)); - - /* create a new region with the same name. - */ - - AudioRegion* newregion = new AudioRegion (rv->region); - - /* if the original region was locked, we don't care */ - - newregion->set_locked (false); + drag_info.copy = false; + drag_info.item = item; + drag_info.data = clicked_axisview; + drag_info.source_trackview = clicked_axisview; + drag_info.dest_trackview = drag_info.source_trackview; + drag_info.motion_callback = &Editor::create_region_drag_motion_callback; + drag_info.finished_callback = &Editor::create_region_drag_finished_callback; - to_playlist->add_region (*newregion, (jack_nframes_t) (rv->region.position() * atv->get_diskstream()->speed())); - - c.disconnect (); - - if (latest_regionview) { - new_regionviews.push_back (latest_regionview); - } - - } + start_grab (event); +} - if (new_regionviews.empty()) { +void +Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event) +{ + if (selection->regions.empty() || clicked_regionview == 0) { return; } - - /* reset selection to new regionviews */ - - selection->set (new_regionviews); - - drag_info.item = new_regionviews.front()->get_canvas_group (); + _region_motion_group->raise_to_top (); drag_info.copy = true; - drag_info.data = new_regionviews.front(); - drag_info.motion_callback = &Editor::region_drag_motion_callback; - drag_info.finished_callback = &Editor::region_drag_finished_callback; + drag_info.item = item; + drag_info.data = clicked_regionview; start_grab(event); TimeAxisView* tv = &clicked_regionview->get_time_axis_view(); - AudioTimeAxisView* atv = dynamic_cast(tv); + RouteTimeAxisView* rtv = dynamic_cast(tv); double speed = 1.0; - if (atv && atv->is_audio_track()) { - speed = atv->get_diskstream()->speed(); + 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 = (jack_nframes_t) (clicked_regionview->region.position() / speed); + drag_info.source_trackview = &clicked_regionview->get_time_axis_view(); + drag_info.dest_trackview = drag_info.source_trackview; + drag_info.last_frame_position = (nframes64_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); - - begin_reversible_command (_("copy region(s)")); } void -Editor::start_region_brush_grab (GnomeCanvasItem* item, GdkEvent* event) +Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event) { - if (selection->audio_regions.empty() || clicked_regionview == 0) { + if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) { return; } @@ -2595,16 +3288,17 @@ Editor::start_region_brush_grab (GnomeCanvasItem* item, GdkEvent* event) start_grab (event); double speed = 1.0; - TimeAxisView* tvp = clicked_trackview; - AudioTimeAxisView* tv = dynamic_cast(tvp); + TimeAxisView* tvp = clicked_axisview; + RouteTimeAxisView* tv = dynamic_cast(tvp); - if (tv && tv->is_audio_track()) { + if (tv && tv->is_track()) { speed = tv->get_diskstream()->speed(); } - drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed); + drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed); drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position; - drag_info.last_trackview = &clicked_regionview->get_time_axis_view(); + drag_info.source_trackview = &clicked_regionview->get_time_axis_view(); + drag_info.dest_trackview = drag_info.source_trackview; // we want a move threshold drag_info.want_move_threshold = true; drag_info.brushing = true; @@ -2613,39 +3307,191 @@ Editor::start_region_brush_grab (GnomeCanvasItem* item, GdkEvent* event) } void -Editor::region_drag_motion_callback (GnomeCanvasItem* item, GdkEvent* event) +Editor::possibly_copy_regions_during_grab (GdkEvent* event) { - double x_delta; - double y_delta = 0; - AudioRegionView *rv = reinterpret_cast (drag_info.data); - jack_nframes_t pending_region_position = 0; - int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order; - int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen - bool clamp_y_axis = false; - vector height_list(512) ; - vector::iterator j; + if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) { + + drag_info.want_move_threshold = false; // don't copy again + /* duplicate the regionview(s) and region(s) */ + + vector new_regionviews; + + for (list::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) { + RegionView* rv; + RegionView* nrv; + + rv = (*i); + AudioRegionView* arv = dynamic_cast(rv); + MidiRegionView* mrv = dynamic_cast(rv); + + const boost::shared_ptr original = rv->region(); + boost::shared_ptr region_copy = RegionFactory::create (original); + + if (arv) { + boost::shared_ptr audioregion_copy + = boost::dynamic_pointer_cast(region_copy); + nrv = new AudioRegionView (*arv, audioregion_copy); + } else if (mrv) { + boost::shared_ptr midiregion_copy + = boost::dynamic_pointer_cast(region_copy); + nrv = new MidiRegionView (*mrv, midiregion_copy); + } else { + continue; + } + + nrv->get_canvas_group()->show (); + new_regionviews.push_back (nrv); + } + + if (new_regionviews.empty()) { + return; + } + + /* reset selection to new regionviews. This will not set selection visual status for + these regionviews since they don't belong to a track, so do that by hand too. + */ + + selection->set (new_regionviews); + + for (vector::iterator i = new_regionviews.begin(); i != new_regionviews.end(); ++i) { + (*i)->set_selected (true); + } + + /* reset drag_info data to reflect the fact that we are dragging the copies */ + + drag_info.data = new_regionviews.front(); + + swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time); + /* + sync the canvas to what we think is its current state + without it, the canvas seems to + "forget" to update properly after the upcoming reparent() + ..only if the mouse is in rapid motion at the time of the grab. + something to do with regionview creation raking so long? + */ + track_canvas->update_now(); + } +} + +bool +Editor::check_region_drag_possible (RouteTimeAxisView** tv) +{ /* Which trackview is this ? */ TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y); - AudioTimeAxisView* tv = dynamic_cast(tvp); + (*tv) = dynamic_cast(tvp); /* The region motion is only processed if the pointer is over an audio track. */ - if (!tv || !tv->is_audio_track()) { + if (!(*tv) || !(*tv)->is_track()) { /* To make sure we hide the verbose canvas cursor when the mouse is not held over and audiotrack. */ hide_verbose_canvas_cursor (); - return; + return false; } + + return true; +} + +struct RegionSelectionByPosition { + bool operator() (RegionView*a, RegionView* b) { + return a->region()->position () < b->region()->position(); + } +}; + +void +Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) +{ + RouteTimeAxisView* tv; - original_pointer_order = drag_info.last_trackview->order; + if (!check_region_drag_possible (&tv)) { + return; + } + + if (!drag_info.move_threshold_passed) { + return; + } + + int dir; + + if (drag_info.current_pointer_x - drag_info.grab_x > 0) { + dir = 1; + } else { + dir = -1; + } + + RegionSelection copy (selection->regions); + + RegionSelectionByPosition cmp; + copy.sort (cmp); + + for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) { + + RouteTimeAxisView* atv = dynamic_cast (&(*i)->get_time_axis_view()); + + if (!atv) { + continue; + } + + boost::shared_ptr playlist; + + if ((playlist = atv->playlist()) == 0) { + continue; + } + + if (!playlist->region_is_shuffle_constrained ((*i)->region())) { + continue; + } + + if (dir > 0) { + if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) { + continue; + } + } else { + if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) { + continue; + } + } + + playlist->shuffle ((*i)->region(), dir); + + drag_info.grab_x = drag_info.current_pointer_x; + } +} + +void +Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event) +{ +} + +void +Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) +{ + double x_delta; + double y_delta = 0; + nframes64_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; + RouteTimeAxisView* tv; + + possibly_copy_regions_during_grab (event); + + if (!check_region_drag_possible (&tv)) { + return; + } + + original_pointer_order = drag_info.dest_trackview->order; + /************************************************************ - Y-Delta Computation + Y-Delta Computation ************************************************************/ if (drag_info.brushing) { @@ -2653,40 +3499,42 @@ Editor::region_drag_motion_callback (GnomeCanvasItem* item, GdkEvent* event) pointer_y_span = 0; goto y_axis_done; } - - if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) { + + if ((pointer_y_span = (drag_info.dest_trackview->order - tv->order)) != 0) { int32_t children = 0, numtracks = 0; - bitset <512> tracks (0x00); + // XXX hard coding track limit, oh my, so very very bad + bitset <1024> tracks (0x00); /* get a bitmask representing the visible tracks */ for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { TimeAxisView *tracklist_timeview; tracklist_timeview = (*i); - AudioTimeAxisView* atv2 = dynamic_cast(tracklist_timeview); - list children_list; + RouteTimeAxisView* rtv2 = dynamic_cast(tracklist_timeview); + TimeAxisView::Children children_list; /* zeroes are audio tracks. ones are other types. */ - if (!atv2->hidden()) { + if (!rtv2->hidden()) { - if (visible_y_high < atv2->order) { - visible_y_high = atv2->order; + if (visible_y_high < rtv2->order) { + visible_y_high = rtv2->order; } - if (visible_y_low > atv2->order) { - visible_y_low = atv2->order; + if (visible_y_low > rtv2->order) { + visible_y_low = rtv2->order; } - if (!atv2->is_audio_track()) { - tracks = tracks |= (0x01 << atv2->order); + if (!rtv2->is_track()) { + tracks = tracks |= (0x01 << rtv2->order); } - height_list[atv2->order] = (*i)->height; + height_list[rtv2->order] = (*i)->current_height(); children = 1; - if ((children_list = atv2->get_child_list()).size() > 0) { - for (list::iterator j = children_list.begin(); j != children_list.end(); ++j) { - tracks = tracks |= (0x01 << (atv2->order + children)); - height_list[atv2->order + children] = (*j)->height; + + 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)->current_height(); numtracks++; children++; } @@ -2697,52 +3545,57 @@ Editor::region_drag_motion_callback (GnomeCanvasItem* item, GdkEvent* event) /* find the actual span according to the canvas */ canvas_pointer_y_span = pointer_y_span; - if (drag_info.last_trackview->order >= tv->order) { + if (drag_info.dest_trackview->order >= tv->order) { int32_t y; - for (y = tv->order; y < drag_info.last_trackview->order; y++) { + for (y = tv->order; y < drag_info.dest_trackview->order; y++) { if (height_list[y] == 0 ) { canvas_pointer_y_span--; } } } else { int32_t y; - for (y = drag_info.last_trackview->order;y <= tv->order; y++) { + for (y = drag_info.dest_trackview->order;y <= tv->order; y++) { if ( height_list[y] == 0 ) { canvas_pointer_y_span++; } } } - for (list::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) { - AudioRegionView* rv2; - rv2 = (*i); + 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; - gnome_canvas_item_get_bounds (rv2->get_canvas_frame(), &ix1, &iy1, &ix2, &iy2); - gnome_canvas_item_i2w (rv2->get_canvas_group(), &ix1, &iy1); + if (rv2->region()->locked()) { + continue; + } + + rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2); + rv2->get_canvas_frame()->i2w (ix1, iy1); + iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize; + TimeAxisView* tvp2 = trackview_by_y_position (iy1); - AudioTimeAxisView* atv2 = dynamic_cast(tvp2); + RouteTimeAxisView* rtv2 = dynamic_cast(tvp2); - if (atv2->order != original_pointer_order) { + if (rtv2->order != original_pointer_order) { /* this isn't the pointer track */ if (canvas_pointer_y_span > 0) { /* moving up the canvas */ - if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) { + 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[atv2->order - (visible_tracks - n)] == 0) { + while (height_list[rtv2->order - (visible_tracks - n)] == 0) { /* we're passing through a hidden track */ n--; } } - if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) { + if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) { clamp_y_axis = true; } @@ -2754,7 +3607,7 @@ Editor::region_drag_motion_callback (GnomeCanvasItem* item, GdkEvent* event) /*moving down the canvas*/ - if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow + if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow int32_t visible_tracks = 0; @@ -2762,11 +3615,11 @@ Editor::region_drag_motion_callback (GnomeCanvasItem* item, GdkEvent* event) while (visible_tracks > canvas_pointer_y_span ) { visible_tracks--; - while (height_list[atv2->order - (visible_tracks - n)] == 0) { + while (height_list[rtv2->order - (visible_tracks - n)] == 0) { n++; } } - if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) { + if ( tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) { clamp_y_axis = true; } @@ -2779,9 +3632,9 @@ Editor::region_drag_motion_callback (GnomeCanvasItem* item, GdkEvent* event) } else { /* this is the pointer's track */ - if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow + if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow clamp_y_axis = true; - } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow + } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow clamp_y_axis = true; } } @@ -2790,73 +3643,107 @@ Editor::region_drag_motion_callback (GnomeCanvasItem* item, GdkEvent* event) } } - } else if (drag_info.last_trackview == tv) { + } else if (drag_info.dest_trackview == tv) { clamp_y_axis = true; } y_axis_done: if (!clamp_y_axis) { - drag_info.last_trackview = tv; + drag_info.dest_trackview = tv; } /************************************************************ - X DELTA COMPUTATION + X DELTA COMPUTATION ************************************************************/ /* compute the amount of pointer motion in frames, and where the region would be if we moved it by that much. */ + if ( drag_info.move_threshold_passed ) { - if (drag_info.move_threshold_passsed) { + if (drag_info.current_pointer_frame >= drag_info.pointer_frame_offset) { - if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { - - jack_nframes_t sync_frame; - jack_nframes_t sync_offset; + nframes64_t sync_frame; + nframes64_t sync_offset; int32_t sync_dir; - + pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset; - - sync_offset = rv->region.sync_offset (sync_dir); - sync_frame = rv->region.adjust_to_sync (pending_region_position); - /* we snap if the snap modifier is not enabled. + sync_offset = clicked_regionview->region()->sync_offset (sync_dir); + + /* we don't handle a sync point that lies before zero. */ + if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) { + sync_frame = pending_region_position + (sync_dir*sync_offset); + + /* we snap if the snap modifier is not enabled. + */ - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { - snap_to (sync_frame); - } + if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { + snap_to (sync_frame); + } - if (sync_frame - sync_offset <= sync_frame) { - pending_region_position = sync_frame - (sync_dir*sync_offset); + pending_region_position = clicked_regionview->region()->adjust_to_sync (sync_frame); + } else { - pending_region_position = 0; + pending_region_position = drag_info.last_frame_position; } } else { pending_region_position = 0; } - if (pending_region_position > max_frames - rv->region.length()) { + if (pending_region_position > max_frames - clicked_regionview->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 + bool x_move_allowed; + + if (Config->get_edit_mode() == Lock) { + if (drag_info.copy) { + x_move_allowed = !drag_info.x_constrained; + } else { + /* in locked edit mode, reverse the usual meaning of x_constrained */ + x_move_allowed = drag_info.x_constrained; + } + } else { + x_move_allowed = !drag_info.x_constrained; + } + + if (( pending_region_position != drag_info.last_frame_position) && x_move_allowed ) { + + /* now compute the canvas unit distance we need to move the regionview to make it appear at the new location. */ - + if (pending_region_position > drag_info.last_frame_position) { x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit); } else { x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit); + 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_frame()->i2w (ix1, iy1); + + if (-x_delta > ix1 + horizontal_adjustment.get_value()) { + // do_move = false; + x_delta = 0; + pending_region_position = drag_info.last_frame_position; + break; + } + } + } - + drag_info.last_frame_position = pending_region_position; - + } else { x_delta = 0; } @@ -2866,9 +3753,9 @@ Editor::region_drag_motion_callback (GnomeCanvasItem* item, GdkEvent* event) x_delta = 0; } - + /************************************************************* - PREPARE TO MOVE + PREPARE TO MOVE ************************************************************/ if (x_delta == 0 && (pointer_y_span == 0)) { @@ -2876,188 +3763,165 @@ Editor::region_drag_motion_callback (GnomeCanvasItem* item, GdkEvent* event) trackviews. nothing to do. */ return; - } + } - if (x_delta < 0) { - for (list::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) { + /************************************************************* + MOTION + ************************************************************/ + bool do_move = true; + if (drag_info.first_move) { + if (!drag_info.move_threshold_passed) { + do_move = false; + } + } - AudioRegionView* rv2; - rv2 = (*i); + if (do_move) { - /* if any regionview is at zero, we need to know so we can - stop further leftward motion. - */ - + 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; - gnome_canvas_item_get_bounds (rv2->get_canvas_frame(), &ix1, &iy1, &ix2, &iy2); - gnome_canvas_item_i2w (rv2->get_canvas_group(), &ix1, &iy1); + int32_t temp_pointer_y_span = pointer_y_span; - if (ix1 <= 1) { - x_delta = 0; - break; + if (rv->region()->locked()) { + continue; } - } - } - /************************************************************* - MOTION - ************************************************************/ + /* 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. + */ - pair::iterator,bool> insert_result; + rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2); + rv->get_canvas_frame()->i2w (ix1, iy1); + + cerr << "adjust y from " << iy1 << " using " + << vertical_adjustment.get_value() << " - " + << canvas_timebars_vsize + << endl; - for (list::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) { - - AudioRegionView* rv; - rv = (*i); - double ix1, ix2, iy1, iy2; - int32_t temp_pointer_y_span = pointer_y_span; + iy1 += get_trackview_group_vertical_offset ();; - /* 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. - */ + if (drag_info.first_move) { - gnome_canvas_item_get_bounds (rv->get_canvas_frame(), &ix1, &iy1, &ix2, &iy2); - gnome_canvas_item_i2w (rv->get_canvas_group(), &ix1, &iy1); - TimeAxisView* tvp2 = trackview_by_y_position (iy1); - AudioTimeAxisView* canvas_atv = dynamic_cast(tvp2); - AudioTimeAxisView* temp_atv; - - 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_atv->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_atv = dynamic_cast(tvp2); - rv->set_height (temp_atv->height); + // hide any dependent views - /* 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_atv->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) { + rv->get_time_axis_view().hide_dependent_views (*rv); - /* hide any dependent views */ + /* + reparent to a non scrolling group so that we can keep the + region selection above all time axis views. + reparenting means we have to move the rv as the two + parent groups have different coordinates. + */ -// 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()->property_y() = iy1 - 1; + rv->get_canvas_group()->reparent(*_region_motion_group); - gnome_canvas_item_raise_to_top (rv->get_canvas_group()); - gnome_canvas_item_raise_to_top (rv->get_time_axis_view().canvas_display); - gnome_canvas_item_raise_to_top (cursor_group); + rv->fake_set_opaque (true); + } - /* freeze the playlists from notifying till - the motion is done. - */ + 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--; + } - AudioTimeAxisView* atv = dynamic_cast (&rv->get_time_axis_view()); - if (atv && atv->is_audio_track()) { - AudioPlaylist* pl = atv->get_diskstream()->playlist(); - if (pl) { - /* only freeze and capture state once */ + 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_height (temp_rtv->current_height()); - insert_result = motion_frozen_playlists.insert (pl); - if (insert_result.second) { - pl->freeze(); - session->add_undo(pl->get_memento()); + /* 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++; } } - } - if (drag_info.brushing) { - mouse_brush_insert_region (rv, pending_region_position); - } else { - rv->move (x_delta, y_delta); - } - } - - if (drag_info.first_move) { - gnome_canvas_item_raise_to_top (cursor_group); - } - - drag_info.first_move = false; - + 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 (x_delta != 0 && !drag_info.brushing) { show_verbose_time_cursor (drag_info.last_frame_position, 10); } - } void -Editor::region_drag_finished_callback (GnomeCanvasItem* item, GdkEvent* event) +Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event) { - jack_nframes_t where; - AudioRegionView* rv = reinterpret_cast (drag_info.data); - pair::iterator,bool> insert_result; bool nocommit = true; - double speed; - AudioTimeAxisView* atv; - bool regionview_y_movement; - bool regionview_x_movement; + vector copies; + RouteTimeAxisView* source_tv; + boost::shared_ptr ds; + boost::shared_ptr from_playlist; + vector new_selection; + typedef set > PlaylistSet; + PlaylistSet modified_playlists; + PlaylistSet frozen_playlists; + list modified_playlist_connections; + pair insert_result, frozen_insert_result; + nframes64_t drag_delta; + bool changed_tracks, changed_position; /* first_move is set to false if the regionview has been moved in the motion handler. @@ -3070,174 +3934,295 @@ Editor::region_drag_finished_callback (GnomeCanvasItem* item, GdkEvent* event) 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); + if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) { + selection->set (pre_drag_region_selection); + pre_drag_region_selection.clear (); + } 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); + } + } + goto out; } - /* adjust for track speed */ - speed = 1.0; + char* op_string; - atv = dynamic_cast (drag_info.last_trackview); - if (atv && atv->get_diskstream()) { - speed = atv->get_diskstream()->speed(); - } + /* reverse this here so that we have the correct logic to finalize + the drag. + */ - regionview_x_movement = (drag_info.last_frame_position != (jack_nframes_t) (rv->region.position()/speed)); - regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view()); + if (Config->get_edit_mode() == Lock && !drag_info.copy) { + drag_info.x_constrained = !drag_info.x_constrained; + } - //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()); - - if (regionview_y_movement) { + 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"); + } + } - /* motion between tracks */ + begin_reversible_command (op_string); - list new_selection; - - /* moved to a different audio track. */ + changed_position = (drag_info.last_frame_position != (nframes64_t) (clicked_regionview->region()->position())); + changed_tracks = (trackview_by_y_position (drag_info.current_pointer_y) != &clicked_regionview->get_time_axis_view()); - for (list::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ) { - - AudioRegionView* rv2 = (*i); - - /* 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. - */ - - if (!drag_info.copy) { - rv2->hide_region_editor(); - } - new_selection.push_back (rv2); - i++; - } + drag_delta = clicked_regionview->region()->position() - drag_info.last_frame_position; - /* first, freeze the target tracks */ + track_canvas->update_now (); - for (list::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) { + 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_frame()->i2w (ix1, iy1); + iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize; - Playlist* from_playlist; - Playlist* to_playlist; - - double ix1, ix2, iy1, iy2; - - gnome_canvas_item_get_bounds ((*i)->get_canvas_frame(), &ix1, &iy1, &ix2, &iy2); - gnome_canvas_item_i2w ((*i)->get_canvas_group(), &ix1, &iy1); - TimeAxisView* tvp2 = trackview_by_y_position (iy1); - AudioTimeAxisView* atv2 = dynamic_cast(tvp2); - - from_playlist = (*i)->region.playlist(); - to_playlist = atv2->playlist(); + TimeAxisView* dest_tv = trackview_by_y_position (iy1); + RouteTimeAxisView* dest_rtv = dynamic_cast(dest_tv); + nframes64_t where; + + if (rv->region()->locked()) { + ++i; + continue; + } + + if (changed_position && !drag_info.x_constrained) { + where = rv->region()->position() - drag_delta; + } else { + where = rv->region()->position(); + } + + boost::shared_ptr new_region; - /* the from_playlist was frozen in the "first_move" case - of the motion handler. the insert can fail, - but that doesn't matter. it just means - we already have the playlist in the list. + if (drag_info.copy) { + /* we already made a copy */ + new_region = rv->region(); + + /* undo the previous hide_dependent_views so that xfades don't + disappear on copying regions */ + + //rv->get_time_axis_view().reveal_dependent_views (*rv); + + } else if (changed_tracks && dest_rtv->playlist()) { + new_region = RegionFactory::create (rv->region()); + } + + if (changed_tracks || drag_info.copy) { + + boost::shared_ptr to_playlist = dest_rtv->playlist(); + if (!to_playlist) { + ++i; + continue; + } + + latest_regionviews.clear (); + + sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view)); - motion_frozen_playlists.insert (from_playlist); + insert_result = modified_playlists.insert (to_playlist); + if (insert_result.second) { + session->add_command (new MementoCommand(*to_playlist, &to_playlist->get_state(), 0)); + } + + to_playlist->add_region (new_region, where); + + c.disconnect (); + + if (!latest_regionviews.empty()) { + // XXX why just the first one ? we only expect one + // commented out in nick_m's canvas reworking. is that intended? + //dest_atv->reveal_dependent_views (*latest_regionviews.front()); + new_selection.push_back (latest_regionviews.front()); + } + + } else { + /* + motion on the same track. plonk the previously reparented region + back to its original canvas group (its streamview). + No need to do anything for copies as they are fake regions which will be deleted. + */ - /* only freeze the to_playlist once */ + rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item()); + rv->get_canvas_group()->property_y() = 0; + + /* just change the model */ + + boost::shared_ptr playlist = dest_rtv->playlist(); - insert_result = motion_frozen_playlists.insert(to_playlist); + insert_result = modified_playlists.insert (playlist); if (insert_result.second) { - to_playlist->freeze(); - session->add_undo(to_playlist->get_memento()); + session->add_command (new MementoCommand(*playlist, &playlist->get_state(), 0)); + } + /* freeze to avoid lots of relayering in the case of a multi-region drag */ + frozen_insert_result = frozen_playlists.insert(playlist); + if (frozen_insert_result.second) { + playlist->freeze(); } + rv->region()->set_position (where, (void*) this); } - /* now do it again with the actual operations */ + if (changed_tracks && !drag_info.copy) { - for (list::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) { + /* get the playlist where this drag started. we can't use rv->region()->playlist() + because we may have copied the region and it has not been attached to a playlist. + */ - Playlist* from_playlist; - Playlist* to_playlist; - - double ix1, ix2, iy1, iy2; - - gnome_canvas_item_get_bounds ((*i)->get_canvas_frame(), &ix1, &iy1, &ix2, &iy2); - gnome_canvas_item_i2w ((*i)->get_canvas_group(), &ix1, &iy1); - TimeAxisView* tvp2 = trackview_by_y_position (iy1); - AudioTimeAxisView* atv2 = dynamic_cast(tvp2); - - from_playlist = (*i)->region.playlist(); - to_playlist = atv2->playlist(); + assert ((source_tv = dynamic_cast (&rv->get_time_axis_view()))); + assert ((ds = source_tv->get_diskstream())); + assert ((from_playlist = ds->playlist())); - latest_regionview = 0; - - where = (jack_nframes_t) (unit_to_frame (ix1) * speed); - Region* new_region = createRegion ((*i)->region); + /* moved to a different audio track, without copying */ - from_playlist->remove_region (&((*i)->region)); - - sigc::connection c = atv2->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view)); - to_playlist->add_region (*new_region, where); - c.disconnect (); + /* the region that used to be in the old playlist is not + moved to the new one - we use a copy of it. as a result, + any existing editor for the region should no longer be + visible. + */ + + rv->hide_region_editor(); + rv->fake_set_opaque (false); - if (latest_regionview) { - selection->add (latest_regionview); + /* remove the region from the old playlist */ + + insert_result = modified_playlists.insert (from_playlist); + if (insert_result.second) { + session->add_command (new MementoCommand(*from_playlist, &from_playlist->get_state(), 0)); } - } - } else { + from_playlist->remove_region ((rv->region())); + + /* 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 a playlist will have removed all + trace of it from the selection (i.e. there were N regions selected, we removed 1, + but since its the same playlist for N tracks, all N tracks updated themselves, removed the + corresponding regionview, and the selection is now empty). - /* motion within a single track */ + this could have invalidated any and all iterators into the region selection. - for (list::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) { + 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. - rv = (*i); + EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and + we can just iterate. + */ - if (rv->region.locked()) { - continue; + if (selection->regions.empty()) { + break; + } else { + i = selection->regions.by_layer().begin(); } - - if (regionview_x_movement) { - double ownspeed = 1.0; - AudioTimeAxisView* atv = dynamic_cast (&(rv->get_time_axis_view())); - if (atv && atv->get_diskstream()) { - ownspeed = atv->get_diskstream()->speed(); - } - - /* base the new region position on the current position of the regionview.*/ - - double ix1, ix2, iy1, iy2; - - gnome_canvas_item_get_bounds (rv->get_canvas_frame(), &ix1, &iy1, &ix2, &iy2); - gnome_canvas_item_i2w (rv->get_canvas_group(), &ix1, &iy1); - where = (jack_nframes_t) (unit_to_frame (ix1) * ownspeed); - - } else { - - where = rv->region.position(); - } + } else { + ++i; + } + + if (drag_info.copy) { + copies.push_back (rv); + } + } + + if (new_selection.empty()) { + if (drag_info.copy) { + /* the region(view)s that are selected and being dragged around + are copies and do not belong to any track. remove them + from the selection right here. + */ + selection->clear_regions(); + } + } else { + /* this will clear any existing selection that would have been + cleared in the other clause above + */ + selection->set (new_selection); + } - rv->get_time_axis_view().reveal_dependent_views (*rv); + for (set >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) { + (*p)->thaw(); + } - session->add_undo (rv->region.playlist()->get_memento()); - rv->region.set_position (where, (void *) this); + out: + if (!nocommit) { + for (set >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) { + session->add_command (new MementoCommand(*(*p), 0, &(*p)->get_state())); } + commit_reversible_command (); } - out: - for (set::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) { - (*p)->thaw (); - session->add_redo_no_execute ((*p)->get_memento()); + for (vector::iterator x = copies.begin(); x != copies.end(); ++x) { + delete *x; } - motion_frozen_playlists.clear (); +} + +void +Editor::create_region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) +{ + if (drag_info.move_threshold_passed) { + if (drag_info.first_move) { + // TODO: create region-create-drag region view here + drag_info.first_move = false; + } - if (!nocommit) { - commit_reversible_command (); + // TODO: resize region-create-drag region view here + } +} + +void +Editor::create_region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event) +{ + MidiTimeAxisView* mtv = dynamic_cast (drag_info.dest_trackview); + if (!mtv) + return; + + 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 (drag_info.first_move) { + begin_reversible_command (_("create region")); + XMLNode &before = mtv->playlist()->get_state(); + + nframes64_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(); + + } else { + create_region_drag_motion_callback (item, event); + // TODO: create region-create-drag region here } } @@ -3248,42 +4233,57 @@ Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event) this is an alignment click (control used) */ - if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) { + if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) { TimeAxisView* tv = &rv.get_time_axis_view(); - AudioTimeAxisView* atv = dynamic_cast(tv); + RouteTimeAxisView* rtv = dynamic_cast(tv); double speed = 1.0; - if (atv && atv->is_audio_track()) { - speed = atv->get_diskstream()->speed(); + if (rtv && rtv->is_track()) { + speed = rtv->get_diskstream()->speed(); } - if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) { - - align_region (rv.region, SyncPoint, (jack_nframes_t) (edit_cursor->current_frame * speed)); - - } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) { + nframes64_t where = get_preferred_edit_position(); - align_region (rv.region, End, (jack_nframes_t) (edit_cursor->current_frame * speed)); + if (where >= 0) { - } else { - - align_region (rv.region, Start, (jack_nframes_t) (edit_cursor->current_frame * speed)); + if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) { + + align_region (rv.region(), SyncPoint, (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, (nframes64_t) (where * speed)); + } } } } void -Editor::show_verbose_time_cursor (jack_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; + SMPTE::Time smpte; BBT_Time bbt; + int hours, mins; + nframes64_t frame_rate; float secs; if (session == 0) { 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 (frame, bbt); snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks); @@ -3295,14 +4295,18 @@ Editor::show_verbose_time_cursor (jack_nframes_t frame, double offset, double xp break; case AudioClock::MinSec: - /* XXX fix this to compute min/sec properly */ - session->smpte_time (frame, smpte); - secs = smpte.seconds + ((float) smpte.frames / session->smpte_frames_per_second); - snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", smpte.hours, smpte.minutes, secs); + /* XXX this is copied from show_verbose_duration_cursor() */ + frame_rate = session->frame_rate(); + hours = frame / (frame_rate * 3600); + frame = frame % (frame_rate * 3600); + mins = frame / (frame_rate * 60); + frame = frame % (frame_rate * 60); + secs = (float) frame / (float) frame_rate; + snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs); break; default: - snprintf (buf, sizeof(buf), "%u", frame); + snprintf (buf, sizeof(buf), "%" PRIi64, frame); break; } @@ -3310,18 +4314,20 @@ Editor::show_verbose_time_cursor (jack_nframes_t frame, double offset, double xp 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_info.current_pointer_x + offset - horizontal_adjustment.get_value(), drag_info.current_pointer_y + offset - vertical_adjustment.get_value() + canvas_timebars_vsize); } show_verbose_canvas_cursor (); } void -Editor::show_verbose_duration_cursor (jack_nframes_t start, jack_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; + SMPTE::Time smpte; BBT_Time sbbt; BBT_Time ebbt; + int hours, mins; + nframes64_t distance, frame_rate; float secs; Meter meter_at_start(session->tempo_map().meter_at(start)); @@ -3329,7 +4335,15 @@ Editor::show_verbose_duration_cursor (jack_nframes_t start, jack_nframes_t end, 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); @@ -3362,14 +4376,19 @@ Editor::show_verbose_duration_cursor (jack_nframes_t start, jack_nframes_t end, break; case AudioClock::MinSec: - /* XXX fix this to compute min/sec properly */ - session->smpte_duration (end - start, smpte); - secs = smpte.seconds + ((float) smpte.frames / session->smpte_frames_per_second); - snprintf (buf, sizeof (buf), "%02ld:%02ld:%.4f", smpte.hours, smpte.minutes, secs); + /* XXX this stuff should be elsewhere.. */ + distance = end - start; + frame_rate = session->frame_rate(); + hours = distance / (frame_rate * 3600); + distance = distance % (frame_rate * 3600); + mins = distance / (frame_rate * 60); + distance = distance % (frame_rate * 60); + secs = (float) distance / (float) frame_rate; + snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs); break; default: - snprintf (buf, sizeof(buf), "%u", end - start); + snprintf (buf, sizeof(buf), "%" PRIi64, end - start); break; } @@ -3379,17 +4398,25 @@ Editor::show_verbose_duration_cursor (jack_nframes_t start, jack_nframes_t end, else { set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset); } + show_verbose_canvas_cursor (); } void -Editor::collect_new_region_view (AudioRegionView* rv) +Editor::collect_new_region_view (RegionView* rv) +{ + latest_regionviews.push_back (rv); +} + +void +Editor::collect_and_select_new_region_view (RegionView* rv) { - latest_regionview = rv; + selection->add(rv); + latest_regionviews.push_back (rv); } void -Editor::start_selection_grab (GnomeCanvasItem* item, GdkEvent* event) +Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event) { if (clicked_regionview == 0) { return; @@ -3397,7 +4424,7 @@ Editor::start_selection_grab (GnomeCanvasItem* item, GdkEvent* event) /* lets try to create new Region for the selection */ - vector new_regions; + vector > new_regions; create_region_from_selection (new_regions); if (new_regions.empty()) { @@ -3406,7 +4433,7 @@ Editor::start_selection_grab (GnomeCanvasItem* item, GdkEvent* event) /* XXX fix me one day to use all new regions */ - Region* region = new_regions.front(); + boost::shared_ptr region (new_regions.front()); /* add it to the current stream/playlist. @@ -3415,8 +4442,8 @@ Editor::start_selection_grab (GnomeCanvasItem* item, GdkEvent* event) set the regionview we want to then drag. */ - latest_regionview = 0; - sigc::connection c = clicked_audio_trackview->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view)); + latest_regionviews.clear(); + 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. @@ -3424,34 +4451,37 @@ Editor::start_selection_grab (GnomeCanvasItem* item, GdkEvent* event) begin_reversible_command (_("selection grab")); - Playlist* playlist = clicked_trackview->playlist(); + boost::shared_ptr playlist = clicked_axisview->playlist(); - session->add_undo (playlist->get_memento ()); - clicked_trackview->playlist()->add_region (*region, selection->time[clicked_selection].start); - session->add_redo_no_execute (playlist->get_memento ()); + 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) { + if (latest_regionviews.empty()) { /* something went wrong */ return; } /* we need to deselect all other regionviews, and select this one - i'm ignoring undo stuff, because the region creation will take care of it */ - selection->set (latest_regionview); + i'm ignoring undo stuff, because the region creation will take care of it + */ + selection->set (latest_regionviews); - drag_info.item = latest_regionview->get_canvas_group(); - drag_info.data = latest_regionview; + drag_info.item = latest_regionviews.front()->get_canvas_group(); + drag_info.data = latest_regionviews.front(); drag_info.motion_callback = &Editor::region_drag_motion_callback; drag_info.finished_callback = &Editor::region_drag_finished_callback; start_grab (event); - drag_info.last_trackview = clicked_trackview; - drag_info.last_frame_position = latest_regionview->region.position(); + drag_info.source_trackview = clicked_routeview; + drag_info.dest_trackview = drag_info.source_trackview; + drag_info.last_frame_position = latest_regionviews.front()->region()->position(); drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position; show_verbose_time_cursor (drag_info.last_frame_position, 10); @@ -3468,10 +4498,10 @@ Editor::cancel_selection () } void -Editor::start_selection_op (GnomeCanvasItem* item, GdkEvent* event, SelectionOp op) +Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op) { - jack_nframes_t start = 0; - jack_nframes_t end = 0; + nframes64_t start = 0; + nframes64_t end = 0; if (session == 0) { return; @@ -3485,8 +4515,7 @@ Editor::start_selection_op (GnomeCanvasItem* item, GdkEvent* event, SelectionOp switch (op) { case CreateSelection: - - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) { + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) { drag_info.copy = true; } else { drag_info.copy = false; @@ -3495,14 +4524,18 @@ Editor::start_selection_op (GnomeCanvasItem* item, GdkEvent* event, SelectionOp break; case SelectionStartTrim: - clicked_trackview->order_selection_trims (item, true); + 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: - clicked_trackview->order_selection_trims (item, false); + 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; @@ -3523,17 +4556,16 @@ Editor::start_selection_op (GnomeCanvasItem* item, GdkEvent* event, SelectionOp } void -Editor::drag_selection (GnomeCanvasItem* item, GdkEvent* event) +Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event) { - jack_nframes_t start = 0; - jack_nframes_t end = 0; - jack_nframes_t length; - jack_nframes_t pending_position; + nframes64_t start = 0; + nframes64_t end = 0; + nframes64_t length; + nframes64_t pending_position; - if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { + if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset; - } - else { + } else { pending_position = 0; } @@ -3576,7 +4608,7 @@ Editor::drag_selection (GnomeCanvasItem* item, GdkEvent* event) drag_info.copy = false; } else { /* new selection-> */ - clicked_selection = selection->set (clicked_trackview, start, end); + clicked_selection = selection->set (clicked_axisview, start, end); } } break; @@ -3633,9 +4665,8 @@ Editor::drag_selection (GnomeCanvasItem* item, GdkEvent* event) break; } - - if (event->button.x >= track_canvas_scroller.get_hadjustment()->get_value() + canvas_width) { - start_canvas_autoscroll (1); + if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) { + start_canvas_autoscroll (1, 0); } if (start != end) { @@ -3653,7 +4684,7 @@ Editor::drag_selection (GnomeCanvasItem* item, GdkEvent* event) } void -Editor::end_selection_op (GnomeCanvasItem* item, GdkEvent* event) +Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event) { if (!drag_info.first_move) { drag_selection (item, event); @@ -3678,20 +4709,20 @@ Editor::end_selection_op (GnomeCanvasItem* item, GdkEvent* event) } void -Editor::start_trim (GnomeCanvasItem* item, GdkEvent* event) +Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event) { double speed = 1.0; - TimeAxisView* tvp = clicked_trackview; - AudioTimeAxisView* tv = dynamic_cast(tvp); + TimeAxisView* tvp = clicked_axisview; + RouteTimeAxisView* tv = dynamic_cast(tvp); - if (tv && tv->is_audio_track()) { + if (tv && tv->is_track()) { speed = tv->get_diskstream()->speed(); } - jack_nframes_t region_start = (jack_nframes_t) (clicked_regionview->region.position() / speed); - jack_nframes_t region_end = (jack_nframes_t) (clicked_regionview->region.last_frame() / speed); - jack_nframes_t region_length = (jack_nframes_t) (clicked_regionview->region.length() / speed); - + nframes64_t region_start = (nframes64_t) (clicked_regionview->region()->position() / speed); + nframes64_t region_end = (nframes64_t) (clicked_regionview->region()->last_frame() / speed); + nframes64_t region_length = (nframes64_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; @@ -3699,7 +4730,7 @@ Editor::start_trim (GnomeCanvasItem* item, GdkEvent* event) start_grab (event, trimmer_cursor); - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) { + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) { trim_op = ContentsTrim; } else { /* These will get overridden for a point trim.*/ @@ -3723,15 +4754,13 @@ Editor::start_trim (GnomeCanvasItem* item, GdkEvent* event) show_verbose_time_cursor(drag_info.current_pointer_frame, 10); break; } - - flush_track_canvas (); } void -Editor::trim_motion_callback (GnomeCanvasItem* item, GdkEvent* event) +Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) { - AudioRegionView* rv = clicked_regionview; - jack_nframes_t frame_delta = 0; + RegionView* rv = clicked_regionview; + nframes64_t frame_delta = 0; bool left_direction; bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()); @@ -3741,10 +4770,11 @@ Editor::trim_motion_callback (GnomeCanvasItem* item, GdkEvent* event) */ double speed = 1.0; - TimeAxisView* tvp = clicked_trackview; - AudioTimeAxisView* tv = dynamic_cast(tvp); + TimeAxisView* tvp = clicked_axisview; + RouteTimeAxisView* tv = dynamic_cast(tvp); + pair >::iterator,bool> insert_result; - if (tv && tv->is_audio_track()) { + if (tv && tv->is_track()) { speed = tv->get_diskstream()->speed(); } @@ -3780,10 +4810,20 @@ Editor::trim_motion_callback (GnomeCanvasItem* item, GdkEvent* event) begin_reversible_command (trim_type); - for (list::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) { - (*i)->region.freeze (); - (*i)->temporarily_hide_envelope (); - session->add_undo ((*i)->region.playlist()->get_memento()); + 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(); + } } } @@ -3795,20 +4835,20 @@ Editor::trim_motion_callback (GnomeCanvasItem* item, GdkEvent* event) switch (trim_op) { case StartTrim: - if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region.first_frame()/speed)) { + if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) { break; - } else { - for (list::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) { + } 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 > (jack_nframes_t) (rv->region.last_frame()/speed))) { + if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes64_t) (rv->region()->last_frame()/speed))) { break; } else { - for (list::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) { + 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; @@ -3818,12 +4858,12 @@ Editor::trim_motion_callback (GnomeCanvasItem* item, GdkEvent* event) { bool swap_direction = false; - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) { + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) { swap_direction = true; } - for (list::const_iterator i = selection->audio_regions.by_layer().begin(); - i != selection->audio_regions.by_layer().end(); ++i) + 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); } @@ -3833,10 +4873,10 @@ Editor::trim_motion_callback (GnomeCanvasItem* item, GdkEvent* event) switch (trim_op) { case StartTrim: - show_verbose_time_cursor((jack_nframes_t) (rv->region.position()/speed), 10); + show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10); break; case EndTrim: - show_verbose_time_cursor((jack_nframes_t) (rv->region.last_frame()/speed), 10); + show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10); break; case ContentsTrim: show_verbose_time_cursor(drag_info.current_pointer_frame, 10); @@ -3848,141 +4888,148 @@ Editor::trim_motion_callback (GnomeCanvasItem* item, GdkEvent* event) } void -Editor::single_contents_trim (AudioRegionView& rv, jack_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) { - Region& region (rv.region); + boost::shared_ptr region (rv.region()); - if (region.locked()) { + if (region->locked()) { return; } - jack_nframes_t new_bound; + nframes64_t new_bound; double speed = 1.0; - TimeAxisView* tvp = clicked_trackview; - AudioTimeAxisView* tv = dynamic_cast(tvp); + TimeAxisView* tvp = clicked_axisview; + RouteTimeAxisView* tv = dynamic_cast(tvp); - if (tv && tv->is_audio_track()) { + if (tv && tv->is_track()) { speed = tv->get_diskstream()->speed(); } if (left_direction) { if (swap_direction) { - new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta; + new_bound = (nframes64_t) (region->position()/speed) + frame_delta; } else { - new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta; + new_bound = (nframes64_t) (region->position()/speed) - frame_delta; } } else { if (swap_direction) { - new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta; + new_bound = (nframes64_t) (region->position()/speed) - frame_delta; } else { - new_bound = (jack_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 ((jack_nframes_t) (new_bound * speed), this); + region->trim_start ((nframes64_t) (new_bound * speed), this); rv.region_changed (StartChanged); } void -Editor::single_start_trim (AudioRegionView& rv, jack_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) { - Region& region (rv.region); + boost::shared_ptr region (rv.region()); - if (region.locked()) { + if (region->locked()) { return; } - jack_nframes_t new_bound; + nframes64_t new_bound; double speed = 1.0; - TimeAxisView* tvp = clicked_trackview; - AudioTimeAxisView* tv = dynamic_cast(tvp); + TimeAxisView* tvp = clicked_axisview; + RouteTimeAxisView* tv = dynamic_cast(tvp); - if (tv && tv->is_audio_track()) { + if (tv && tv->is_track()) { speed = tv->get_diskstream()->speed(); } if (left_direction) { - new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta; + new_bound = (nframes64_t) (region->position()/speed) - frame_delta; } else { - new_bound = (jack_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)); } - region.trim_front ((jack_nframes_t) (new_bound * speed), this); + region->trim_front ((nframes64_t) (new_bound * speed), this); rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged)); } void -Editor::single_end_trim (AudioRegionView& rv, jack_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) { - Region& region (rv.region); + boost::shared_ptr region (rv.region()); - if (region.locked()) { + if (region->locked()) { return; } - jack_nframes_t new_bound; + nframes64_t new_bound; double speed = 1.0; - TimeAxisView* tvp = clicked_trackview; - AudioTimeAxisView* tv = dynamic_cast(tvp); + TimeAxisView* tvp = clicked_axisview; + RouteTimeAxisView* tv = dynamic_cast(tvp); - if (tv && tv->is_audio_track()) { + if (tv && tv->is_track()) { speed = tv->get_diskstream()->speed(); } if (left_direction) { - new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) - frame_delta; + new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta; } else { - new_bound = (jack_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 ((jack_nframes_t) (new_bound * speed), this); + region->trim_end ((nframes64_t) (new_bound * speed), this); rv.region_changed (LengthChanged); } void -Editor::trim_finished_callback (GnomeCanvasItem* item, GdkEvent* event) +Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event) { if (!drag_info.first_move) { trim_motion_callback (item, event); - if (!clicked_regionview->get_selected()) { + if (!selection->selected (clicked_regionview)) { thaw_region_after_trim (*clicked_regionview); } else { - for (list::const_iterator i = selection->audio_regions.by_layer().begin(); - i != selection->audio_regions.by_layer().end(); ++i) + 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); } } + + 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())); + } + + motion_frozen_playlists.clear (); + commit_reversible_command(); } else { /* no mouse movement */ point_trim (event); } - - flush_track_canvas (); } void Editor::point_trim (GdkEvent* event) { - AudioRegionView* rv = clicked_regionview; - jack_nframes_t new_bound = drag_info.current_pointer_frame; + RegionView* rv = clicked_regionview; + nframes64_t new_bound = drag_info.current_pointer_frame; if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { snap_to (new_bound); @@ -3994,24 +5041,28 @@ Editor::point_trim (GdkEvent* event) trim_op = StartTrim; begin_reversible_command (_("Start point trim")); - if (rv->get_selected()) { + if (selection->selected (rv)) { - for (list::const_iterator i = selection->audio_regions.by_layer().begin(); - i != selection->audio_regions.by_layer().end(); ++i) + for (list::const_iterator i = selection->regions.by_layer().begin(); + i != selection->regions.by_layer().end(); ++i) { - if (!(*i)->region.locked()) { - session->add_undo ((*i)->region.playlist()->get_memento()); - (*i)->region.trim_front (new_bound, this); - session->add_redo_no_execute ((*i)->region.playlist()->get_memento()); + if (!(*i)->region()->locked()) { + boost::shared_ptr pl = (*i)->region()->playlist(); + XMLNode &before = pl->get_state(); + (*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()) { - session->add_undo (rv->region.playlist()->get_memento()); - rv->region.trim_front (new_bound, this); - session->add_redo_no_execute (rv->region.playlist()->get_memento()); + if (!rv->region()->locked()) { + boost::shared_ptr pl = rv->region()->playlist(); + XMLNode &before = pl->get_state(); + rv->region()->trim_front (new_bound, this); + XMLNode &after = pl->get_state(); + session->add_command(new MementoCommand(*pl.get(), &before, &after)); } } @@ -4022,23 +5073,27 @@ Editor::point_trim (GdkEvent* event) trim_op = EndTrim; begin_reversible_command (_("End point trim")); - if (rv->get_selected()) { + if (selection->selected (rv)) { - for (list::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) + for (list::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) { - if (!(*i)->region.locked()) { - session->add_undo ((*i)->region.playlist()->get_memento()); - (*i)->region.trim_end (new_bound, this); - session->add_redo_no_execute ((*i)->region.playlist()->get_memento()); + if (!(*i)->region()->locked()) { + boost::shared_ptr pl = (*i)->region()->playlist(); + XMLNode &before = pl->get_state(); + (*i)->region()->trim_end (new_bound, this); + XMLNode &after = pl->get_state(); + session->add_command(new MementoCommand(*pl.get(), &before, &after)); } } } else { - if (!rv->region.locked()) { - session->add_undo (rv->region.playlist()->get_memento()); - rv->region.trim_end (new_bound, this); - session->add_redo_no_execute (rv->region.playlist()->get_memento()); + if (!rv->region()->locked()) { + boost::shared_ptr pl = rv->region()->playlist(); + XMLNode &before = pl->get_state(); + rv->region()->trim_end (new_bound, this); + XMLNode &after = pl->get_state(); + session->add_command (new MementoCommand(*pl.get(), &before, &after)); } } @@ -4051,27 +5106,30 @@ Editor::point_trim (GdkEvent* event) } void -Editor::thaw_region_after_trim (AudioRegionView& rv) +Editor::thaw_region_after_trim (RegionView& rv) { - Region& region (rv.region); + boost::shared_ptr region (rv.region()); - if (region.locked()) { + if (region->locked()) { return; } - region.thaw (_("trimmed region")); - session->add_redo_no_execute (region.playlist()->get_memento()); + region->thaw (_("trimmed region")); + XMLNode &after = region->playlist()->get_state(); + session->add_command (new MementoCommand(*(region->playlist()), 0, &after)); - rv.unhide_envelope (); + AudioRegionView* arv = dynamic_cast(&rv); + if (arv) + arv->unhide_envelope (); } void -Editor::hide_marker (GnomeCanvasItem* item, GdkEvent* event) +Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event) { Marker* marker; bool is_start; - if ((marker = static_cast (gtk_object_get_data (GTK_OBJECT(item), "marker"))) == 0) { + if ((marker = static_cast (item->get_data ("marker"))) == 0) { fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg; /*NOTREACHED*/ } @@ -4082,9 +5140,8 @@ Editor::hide_marker (GnomeCanvasItem* item, GdkEvent* event) void -Editor::start_range_markerbar_op (GnomeCanvasItem* item, GdkEvent* event, RangeMarkerOp op) +Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op) { - if (session == 0) { return; } @@ -4102,8 +5159,9 @@ Editor::start_range_markerbar_op (GnomeCanvasItem* item, GdkEvent* event, RangeM switch (op) { case CreateRangeMarker: case CreateTransportMarker: - - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) { + case CreateCDMarker: + + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) { drag_info.copy = true; } else { drag_info.copy = false; @@ -4117,12 +5175,27 @@ Editor::start_range_markerbar_op (GnomeCanvasItem* item, GdkEvent* event, RangeM } void -Editor::drag_range_markerbar_op (GnomeCanvasItem* item, GdkEvent* event) +Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event) { - jack_nframes_t start = 0; - jack_nframes_t end = 0; - - GnomeCanvasItem * crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect; + nframes64_t start = 0; + nframes64_t end = 0; + ArdourCanvas::SimpleRect *crect; + + switch (range_marker_op) { + case CreateRangeMarker: + crect = range_bar_drag_rect; + break; + case CreateTransportMarker: + crect = transport_bar_drag_rect; + break; + case CreateCDMarker: + crect = cd_marker_bar_drag_rect; + break; + default: + cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl; + return; + break; + } if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { snap_to (drag_info.current_pointer_frame); @@ -4137,6 +5210,7 @@ Editor::drag_range_markerbar_op (GnomeCanvasItem* item, GdkEvent* event) switch (range_marker_op) { case CreateRangeMarker: case CreateTransportMarker: + case CreateCDMarker: if (drag_info.first_move) { snap_to (drag_info.grab_frame); } @@ -4150,26 +5224,25 @@ Editor::drag_range_markerbar_op (GnomeCanvasItem* item, GdkEvent* event) } /* first drag: Either add to the selection - or create a new selection-> + or create a new selection. */ if (drag_info.first_move) { temp_location->set (start, end); - gnome_canvas_item_show (crect); + crect->show (); update_marker_drag_item (temp_location); - gnome_canvas_item_show (range_marker_drag_rect); - gnome_canvas_item_raise_to_top (range_marker_drag_rect); + range_marker_drag_rect->show(); + //range_marker_drag_rect->raise_to_top(); } break; } - - if (event->button.x >= track_canvas_scroller.get_hadjustment()->get_value() + canvas_width) { - start_canvas_autoscroll (1); + if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) { + start_canvas_autoscroll (1, 0); } if (start != end) { @@ -4177,7 +5250,8 @@ Editor::drag_range_markerbar_op (GnomeCanvasItem* item, GdkEvent* event) double x1 = frame_to_pixel (start); double x2 = frame_to_pixel (end); - gnome_canvas_item_set (crect, "x1", x1, "x2", x2, NULL); + crect->property_x1() = x1; + crect->property_x2() = x2; update_marker_drag_item (temp_location); } @@ -4190,26 +5264,39 @@ Editor::drag_range_markerbar_op (GnomeCanvasItem* item, GdkEvent* event) } void -Editor::end_range_markerbar_op (GnomeCanvasItem* item, GdkEvent* event) +Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event) { Location * newloc = 0; + string rangename; + int flags; if (!drag_info.first_move) { drag_range_markerbar_op (item, event); - switch (range_marker_op) - { + switch (range_marker_op) { case CreateRangeMarker: + case CreateCDMarker: + { begin_reversible_command (_("new range marker")); - session->add_undo (session->locations()->get_memento()); - newloc = new Location(temp_location->start(), temp_location->end(), "unnamed"); + XMLNode &before = session->locations()->get_state(); + session->locations()->next_available_name(rangename,"unnamed"); + if (range_marker_op == CreateCDMarker) { + flags = Location::IsRangeMarker|Location::IsCDMarker; + cd_marker_bar_drag_rect->hide(); + } + else { + flags = Location::IsRangeMarker; + range_bar_drag_rect->hide(); + } + newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags); session->locations()->add (newloc, true); - session->add_redo_no_execute (session->locations()->get_memento()); + XMLNode &after = session->locations()->get_state(); + session->add_command(new MementoCommand(*(session->locations()), &before, &after)); commit_reversible_command (); - gnome_canvas_item_hide (range_bar_drag_rect); - gnome_canvas_item_hide (range_marker_drag_rect); + range_marker_drag_rect->hide(); break; + } case CreateTransportMarker: // popup menu to pick loop or punch @@ -4218,12 +5305,38 @@ Editor::end_range_markerbar_op (GnomeCanvasItem* item, GdkEvent* event) break; } } else { - /* just a click, no pointer movement.*/ + /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */ - if (Keyboard::no_modifier_keys_pressed (&event->button)) { + if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) { + + nframes64_t start; + nframes64_t end; + + 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 (); + } + + if (start == 0) { + start = session->current_start_frame (); + } + + 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; - // nothing yet + 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; + default: + break; + } } } @@ -4233,7 +5346,7 @@ Editor::end_range_markerbar_op (GnomeCanvasItem* item, GdkEvent* event) void -Editor::start_mouse_zoom (GnomeCanvasItem* item, GdkEvent* event) +Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event) { drag_info.item = item; drag_info.motion_callback = &Editor::drag_mouse_zoom; @@ -4245,10 +5358,10 @@ Editor::start_mouse_zoom (GnomeCanvasItem* item, GdkEvent* event) } void -Editor::drag_mouse_zoom (GnomeCanvasItem* item, GdkEvent* event) +Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event) { - jack_nframes_t start; - jack_nframes_t end; + nframes64_t start; + nframes64_t end; if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { snap_to (drag_info.current_pointer_frame); @@ -4272,8 +5385,8 @@ Editor::drag_mouse_zoom (GnomeCanvasItem* item, GdkEvent* event) if (start != end) { if (drag_info.first_move) { - gnome_canvas_item_show (zoom_rect); - gnome_canvas_item_raise_to_top (zoom_rect); + zoom_rect->show(); + zoom_rect->raise_to_top(); } reposition_zoom_rect(start, end); @@ -4286,7 +5399,7 @@ Editor::drag_mouse_zoom (GnomeCanvasItem* item, GdkEvent* event) } void -Editor::end_mouse_zoom (GnomeCanvasItem* item, GdkEvent* event) +Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event) { if (!drag_info.first_move) { drag_mouse_zoom (item, event); @@ -4304,26 +5417,24 @@ Editor::end_mouse_zoom (GnomeCanvasItem* item, GdkEvent* event) */ } - gnome_canvas_item_hide (zoom_rect); + zoom_rect->hide(); } void -Editor::reposition_zoom_rect (jack_nframes_t start, jack_nframes_t end) +Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end) { double x1 = frame_to_pixel (start); double x2 = frame_to_pixel (end); - double y2 = canvas_height - 2; - - gtk_object_set (GTK_OBJECT(zoom_rect), - "x1", x1, - "y1", 1.0, - "x2", x2, - "y2", y2, - NULL); + 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; } void -Editor::start_rubberband_select (GnomeCanvasItem* item, GdkEvent* event) +Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event) { drag_info.item = item; drag_info.motion_callback = &Editor::drag_rubberband_select; @@ -4335,10 +5446,10 @@ Editor::start_rubberband_select (GnomeCanvasItem* item, GdkEvent* event) } void -Editor::drag_rubberband_select (GnomeCanvasItem* item, GdkEvent* event) +Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event) { - jack_nframes_t start; - jack_nframes_t end; + nframes64_t start; + nframes64_t end; double y1; double y2; @@ -4348,16 +5459,15 @@ Editor::drag_rubberband_select (GnomeCanvasItem* item, GdkEvent* event) return; } -// 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 (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) { + if (drag_info.first_move) { + snap_to (drag_info.grab_frame); + } + 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; @@ -4369,8 +5479,7 @@ Editor::drag_rubberband_select (GnomeCanvasItem* item, GdkEvent* event) if (drag_info.current_pointer_y < drag_info.grab_y) { y1 = drag_info.current_pointer_y; y2 = drag_info.grab_y; - } - else { + } else { y2 = drag_info.current_pointer_y; y1 = drag_info.grab_y; } @@ -4381,15 +5490,13 @@ Editor::drag_rubberband_select (GnomeCanvasItem* item, GdkEvent* event) double x1 = frame_to_pixel (start); double x2 = frame_to_pixel (end); - gtk_object_set (GTK_OBJECT(rubberband_rect), - "x1", x1, - "y1", y1, - "x2", x2, - "y2", y2, - NULL); - - gnome_canvas_item_show (rubberband_rect); - gnome_canvas_item_raise_to_top (rubberband_rect); + rubberband_rect->property_x1() = x1; + rubberband_rect->property_y1() = y1; + rubberband_rect->property_x2() = x2; + rubberband_rect->property_y2() = y2; + + rubberband_rect->show(); + rubberband_rect->raise_to_top(); drag_info.last_pointer_frame = drag_info.current_pointer_frame; drag_info.first_move = false; @@ -4399,7 +5506,7 @@ Editor::drag_rubberband_select (GnomeCanvasItem* item, GdkEvent* event) } void -Editor::end_rubberband_select (GnomeCanvasItem* item, GdkEvent* event) +Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event) { if (!drag_info.first_move) { @@ -4409,22 +5516,21 @@ Editor::end_rubberband_select (GnomeCanvasItem* item, GdkEvent* event) if (drag_info.current_pointer_y < drag_info.grab_y) { y1 = drag_info.current_pointer_y; y2 = drag_info.grab_y; - } - else { + } else { y2 = drag_info.current_pointer_y; y1 = drag_info.grab_y; } - bool add = Keyboard::modifier_state_contains (event->button.state, Keyboard::Shift); + Selection::Operation op = Keyboard::selection_type (event->button.state); bool commit; - begin_reversible_command (_("select regions")); + begin_reversible_command (_("rubberband selection")); if (drag_info.grab_frame < drag_info.last_pointer_frame) { - commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, add); + 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, add); + commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op); } if (commit) { @@ -4432,43 +5538,42 @@ Editor::end_rubberband_select (GnomeCanvasItem* item, GdkEvent* event) } } else { - selection->clear_audio_regions(); + selection->clear_tracks(); + selection->clear_regions(); selection->clear_points (); selection->clear_lines (); } - gnome_canvas_item_hide (rubberband_rect); + rubberband_rect->hide(); } gint -Editor::mouse_rename_region (GnomeCanvasItem* item, GdkEvent* event) +Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event) { using namespace Gtkmm2ext; ArdourPrompter prompter (false); prompter.set_prompt (_("Name for region:")); - prompter.set_initial_text (clicked_regionview->region.name()); + 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 (); - prompter.done.connect (Main::quit.slot()); - - Main::run (); - - if (prompter.status == Prompter::cancelled) { - return TRUE; + switch (prompter.run ()) { + case Gtk::RESPONSE_ACCEPT: + string str; + prompter.get_result(str); + if (str.length()) { + clicked_regionview->region()->set_name (str); + } + break; } - - string str; - - prompter.get_result(str); - clicked_regionview->region.set_name (str); - - return TRUE; + return true; } void -Editor::start_time_fx (GnomeCanvasItem* item, GdkEvent* event) +Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event) { drag_info.item = item; drag_info.motion_callback = &Editor::time_fx_motion; @@ -4480,9 +5585,9 @@ Editor::start_time_fx (GnomeCanvasItem* item, GdkEvent* event) } void -Editor::time_fx_motion (GnomeCanvasItem *item, GdkEvent* event) +Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event) { - AudioRegionView* rv = clicked_regionview; + RegionView* rv = clicked_regionview; if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { snap_to (drag_info.current_pointer_frame); @@ -4492,8 +5597,8 @@ Editor::time_fx_motion (GnomeCanvasItem *item, GdkEvent* event) return; } - 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 (drag_info.current_pointer_frame > rv->region()->position()) { + rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame); } drag_info.last_pointer_frame = drag_info.current_pointer_frame; @@ -4503,26 +5608,44 @@ Editor::time_fx_motion (GnomeCanvasItem *item, GdkEvent* event) } void -Editor::end_time_fx (GnomeCanvasItem* item, GdkEvent* event) +Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event) { 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; + } + + nframes64_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position(); + + float percentage = (double) newlen / (double) clicked_regionview->region()->length(); - jack_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; +#ifndef USE_RUBBERBAND + // Soundtouch uses percentage / 100 instead of normal (/ 1) + if (clicked_regionview->region()->data_type() == DataType::AUDIO) { + percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f; + } +#endif begin_reversible_command (_("timestretch")); + + // XXX how do timeFX on multiple regions ? + + RegionSelection rs; + rs.add (clicked_regionview); - if (run_timestretch (selection->audio_regions, percentage) == 0) { + if (time_stretch (rs, percentage) == 0) { session->commit_reversible_command (); } } void -Editor::mouse_brush_insert_region (AudioRegionView* rv, jack_nframes_t pos) +Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos) { /* no brushing without a useful snap setting */ @@ -4534,9 +5657,7 @@ Editor::mouse_brush_insert_region (AudioRegionView* rv, jack_nframes_t pos) } switch (snap_type) { - case SnapToFrame: case SnapToMark: - case SnapToEditCursor: return; default: @@ -4545,40 +5666,36 @@ Editor::mouse_brush_insert_region (AudioRegionView* rv, jack_nframes_t pos) /* don't brush a copy over the original */ - if (pos == rv->region.position()) { + if (pos == rv->region()->position()) { return; } - AudioTimeAxisView* atv = dynamic_cast(&rv->get_time_axis_view()); + RouteTimeAxisView* rtv = dynamic_cast(&rv->get_time_axis_view()); - if (atv == 0 || !atv->is_audio_track()) { + if (rtv == 0 || !rtv->is_track()) { return; } - Playlist* playlist = atv->playlist(); - double speed = atv->get_diskstream()->speed(); + boost::shared_ptr playlist = rtv->playlist(); + double speed = rtv->get_diskstream()->speed(); - session->add_undo (playlist->get_memento()); - playlist->add_region (*(new AudioRegion (rv->region)), (jack_nframes_t) (pos * speed)); - session->add_redo_no_execute (playlist->get_memento()); + 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)); // playlist is frozen, so we have to update manually - playlist->StateChanged (Change (~0)); /* EMIT SIGNAL */ + 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 */ + if (get_microseconds() - last_track_height_step_timestamp < 250000) { current_stepping_trackview = 0; - return FALSE; + return false; } - return TRUE; + return true; } +