From 962efaf05ed33ed12319eaf0e683df398b8bef05 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sat, 30 May 2009 18:25:59 +0000 Subject: Pull dragging code out of the Editor class into its own hierarchy. git-svn-id: svn://localhost/ardour2/branches/3.0@5113 d708f5d6-7413-0410-9779-e7cbd77b26cf --- gtk2_ardour/draginfo.h | 82 - gtk2_ardour/editor.cc | 22 +- gtk2_ardour/editor.h | 173 +- gtk2_ardour/editor_canvas.cc | 75 +- gtk2_ardour/editor_canvas_events.cc | 27 +- gtk2_ardour/editor_cursors.cc | 10 +- gtk2_ardour/editor_drag.cc | 3281 ++++++++++++++++++++++++++ gtk2_ardour/editor_drag.h | 456 ++++ gtk2_ardour/editor_keyboard.cc | 8 +- gtk2_ardour/editor_markers.cc | 3 +- gtk2_ardour/editor_mixer.cc | 2 +- gtk2_ardour/editor_mouse.cc | 4432 ++++------------------------------- gtk2_ardour/editor_ops.cc | 24 +- gtk2_ardour/editor_rulers.cc | 2 +- gtk2_ardour/editor_selection.cc | 2 +- gtk2_ardour/wscript | 1 + 16 files changed, 4356 insertions(+), 4244 deletions(-) delete mode 100644 gtk2_ardour/draginfo.h create mode 100644 gtk2_ardour/editor_drag.cc create mode 100644 gtk2_ardour/editor_drag.h diff --git a/gtk2_ardour/draginfo.h b/gtk2_ardour/draginfo.h deleted file mode 100644 index cdc0b8e9c6..0000000000 --- a/gtk2_ardour/draginfo.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - Copyright (C) 2000-2007 Paul Davis - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#ifndef __gtk2_ardour_drag_info_h_ -#define __gtk2_ardour_drag_info_h_ - -#include - -#include -#include - -#include "canvas.h" -#include "editor_items.h" - -#include "ardour/types.h" - -namespace ARDOUR { - class Location; -} - -class Editor; -class TimeAxisView; - -struct DragInfo { - ArdourCanvas::Item* item; - ItemType item_type; - void* data; - nframes64_t last_frame_position; - nframes64_t pointer_frame_offset; - nframes64_t grab_frame; - nframes64_t last_pointer_frame; - nframes64_t current_pointer_frame; - double original_x, original_y; - double grab_x, grab_y; - double cumulative_x_drag; - double cumulative_y_drag; - double current_pointer_x; - double current_pointer_y; - double last_pointer_x; - double last_pointer_y; - void (Editor::*motion_callback)(ArdourCanvas::Item*, GdkEvent*); - void (Editor::*finished_callback)(ArdourCanvas::Item*, GdkEvent*); - TimeAxisView* source_trackview; - ARDOUR::layer_t source_layer; - TimeAxisView* dest_trackview; - ARDOUR::layer_t dest_layer; - bool x_constrained; - bool y_constrained; - bool copy; - bool was_rolling; - bool first_move; - bool move_threshold_passed; - bool want_move_threshold; - bool brushing; - std::list copied_locations; - - void clear_copied_locations (); -}; - -struct LineDragInfo { - uint32_t before; - uint32_t after; -}; - -#endif /* __gtk2_ardour_drag_info_h_ */ - diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index 569a1d0a2f..1fb22b20ed 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -90,6 +90,7 @@ #include "analysis_window.h" #include "bundle_manager.h" #include "global_port_matrix.h" +#include "editor_drag.h" #include "i18n.h" @@ -196,15 +197,6 @@ show_me_the_size (Requisition* r, const char* what) cerr << "size of " << what << " = " << r->width << " x " << r->height << endl; } -void -DragInfo::clear_copied_locations () -{ - for (list::iterator i = copied_locations.begin(); i != copied_locations.end(); ++i) { - delete *i; - } - copied_locations.clear (); -} - Editor::Editor () : /* time display buttons */ @@ -270,7 +262,7 @@ Editor::Editor () clicked_crossfadeview = 0; clicked_control_point = 0; last_update_frame = 0; - drag_info.item = 0; + _drag = 0; current_mixer_strip = 0; current_bbt_points = 0; tempo_lines = 0; @@ -373,8 +365,6 @@ Editor::Editor () location_loop_color = ARDOUR_UI::config()->canvasvar_LocationLoop.get(); location_punch_color = ARDOUR_UI::config()->canvasvar_LocationPunch.get(); - range_marker_drag_rect = 0; - marker_drag_line = 0; set_midi_edit_mode (MidiEditPencil, true); _edit_point = EditAtMouse; set_mouse_mode (MouseObject, true); @@ -894,7 +884,7 @@ Editor::~Editor() #endif delete track_canvas; - track_canvas = 0; + delete _drag; } void @@ -911,8 +901,10 @@ Editor::catch_vanishing_regionview (RegionView *rv) audioregionview by itself. */ - if (rv->get_canvas_group() == drag_info.item) { - end_grab (drag_info.item, 0); + if (_drag && rv->get_canvas_group() == _drag->item() && !_drag->ending()) { + _drag->end_grab (0); + delete _drag; + _drag = 0; } if (clicked_regionview == rv) { diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index 2e21fe391c..c8fc118efa 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -63,7 +63,6 @@ #include "editor_items.h" #include "region_selection.h" #include "canvas.h" -#include "draginfo.h" namespace Gtkmm2ext { class TearOff; @@ -98,6 +97,7 @@ class AutomationTimeAxisView; class BundleManager; class ControlPoint; class CrossfadeView; +class Drag; class GlobalPortMatrixWindow; class GroupedButtons; class Marker; @@ -124,6 +124,20 @@ class ImageFrameSocketHandler ; class TimeAxisViewItem ; /* */ +struct EditorCursor { + Editor& editor; + ArdourCanvas::Points points; + ArdourCanvas::Line canvas_item; + nframes64_t current_frame; + double length; + + EditorCursor (Editor&, bool (Editor::*)(GdkEvent*,ArdourCanvas::Item*)); + ~EditorCursor (); + + void set_position (nframes64_t); + void set_length (double units); + void set_y_axis (double position); +}; class Editor : public PublicEditor { @@ -795,22 +809,9 @@ class Editor : public PublicEditor Gtk::VBox time_button_vbox; Gtk::HBox time_button_hbox; - struct Cursor { - Editor& editor; - ArdourCanvas::Points points; - ArdourCanvas::Line canvas_item; - nframes64_t current_frame; - double length; - - Cursor (Editor&, bool (Editor::*)(GdkEvent*,ArdourCanvas::Item*)); - ~Cursor (); - - void set_position (nframes64_t); - void set_length (double units); - void set_y_axis (double position); - }; - - Cursor* playhead_cursor; + friend class EditorCursor; + + EditorCursor* playhead_cursor; ArdourCanvas::Group* cursor_group; nframes64_t get_region_boundary (nframes64_t pos, int32_t dir, bool with_selection, bool only_onscreen); @@ -818,11 +819,11 @@ class Editor : public PublicEditor void cursor_to_region_boundary (bool with_selection, int32_t dir); void cursor_to_next_region_boundary (bool with_selection); void cursor_to_previous_region_boundary (bool with_selection); - void cursor_to_next_region_point (Cursor*, ARDOUR::RegionPoint); - void cursor_to_previous_region_point (Cursor*, ARDOUR::RegionPoint); - void cursor_to_region_point (Cursor*, ARDOUR::RegionPoint, int32_t dir); - void cursor_to_selection_start (Cursor *); - void cursor_to_selection_end (Cursor *); + void cursor_to_next_region_point (EditorCursor*, ARDOUR::RegionPoint); + void cursor_to_previous_region_point (EditorCursor*, ARDOUR::RegionPoint); + void cursor_to_region_point (EditorCursor*, ARDOUR::RegionPoint, int32_t dir); + void cursor_to_selection_start (EditorCursor *); + void cursor_to_selection_end (EditorCursor *); void selected_marker_to_region_boundary (bool with_selection, int32_t dir); void selected_marker_to_next_region_boundary (bool with_selection); @@ -833,7 +834,7 @@ class Editor : public PublicEditor void selected_marker_to_selection_start (); void selected_marker_to_selection_end (); - void select_all_selectables_using_cursor (Cursor *, bool); + void select_all_selectables_using_cursor (EditorCursor *, bool); void select_all_selectables_using_edit (bool); void select_all_selectables_between (bool within); void select_range_between (); @@ -1089,7 +1090,7 @@ class Editor : public PublicEditor bool typed_event (ArdourCanvas::Item*, GdkEvent*, ItemType); bool button_press_handler (ArdourCanvas::Item*, GdkEvent*, ItemType); bool button_release_handler (ArdourCanvas::Item*, GdkEvent*, ItemType); - bool motion_handler (ArdourCanvas::Item*, GdkEvent*, ItemType, bool from_autoscroll = false); + bool motion_handler (ArdourCanvas::Item*, GdkEvent*, bool from_autoscroll = false); bool enter_handler (ArdourCanvas::Item*, GdkEvent*, ItemType); bool leave_handler (ArdourCanvas::Item*, GdkEvent*, ItemType); @@ -1390,27 +1391,13 @@ class Editor : public PublicEditor void hide_all_tracks (bool with_select); - DragInfo drag_info; - LineDragInfo current_line_drag_info; + Drag* _drag; - void start_grab (GdkEvent*, Gdk::Cursor* cursor = 0); - bool end_grab (ArdourCanvas::Item*, GdkEvent*); - void swap_grab (ArdourCanvas::Item*, Gdk::Cursor* cursor, uint32_t time); void break_drag (); - void finalize_drag (); Gtk::Menu fade_context_menu; void popup_fade_context_menu (int, int, ArdourCanvas::Item*, ItemType); - void region_gain_motion_callback (ArdourCanvas::Item*, GdkEvent*); - - void start_fade_in_grab (ArdourCanvas::Item*, GdkEvent*); - void start_fade_out_grab (ArdourCanvas::Item*, GdkEvent*); - void fade_in_drag_motion_callback (ArdourCanvas::Item*, GdkEvent*); - void fade_out_drag_motion_callback (ArdourCanvas::Item*, GdkEvent*); - void fade_in_drag_finished_callback (ArdourCanvas::Item*, GdkEvent*); - void fade_out_drag_finished_callback (ArdourCanvas::Item*, GdkEvent*); - void set_fade_in_shape (ARDOUR::AudioRegion::FadeShape); void set_fade_out_shape (ARDOUR::AudioRegion::FadeShape); @@ -1421,51 +1408,20 @@ class Editor : public PublicEditor std::set > motion_frozen_playlists; RegionSelection pre_drag_region_selection; - void region_drag_motion_callback (ArdourCanvas::Item*, GdkEvent*); - void region_drag_finished_callback (ArdourCanvas::Item*, GdkEvent*); - void create_region_drag_motion_callback (ArdourCanvas::Item*, GdkEvent*); - void create_region_drag_finished_callback (ArdourCanvas::Item*, GdkEvent*); - bool check_region_drag_possible (RouteTimeAxisView**, ARDOUR::layer_t*); - void possibly_copy_regions_during_grab (GdkEvent*); - void region_drag_splice_motion_callback (ArdourCanvas::Item*, GdkEvent*); - void region_drag_splice_finished_callback (ArdourCanvas::Item*, GdkEvent*); bool _dragging_playhead; bool _dragging_edit_point; - void cursor_drag_motion_callback (ArdourCanvas::Item*, GdkEvent*); - void cursor_drag_finished_callback (ArdourCanvas::Item*, GdkEvent*); - void cursor_drag_finished_ensure_locate_callback (ArdourCanvas::Item*, GdkEvent*); - void marker_drag_motion_callback (ArdourCanvas::Item*, GdkEvent*); - void marker_drag_finished_callback (ArdourCanvas::Item*, GdkEvent*); - void control_point_drag_motion_callback (ArdourCanvas::Item*, GdkEvent*); - void control_point_drag_finished_callback (ArdourCanvas::Item*, GdkEvent*); - void line_drag_motion_callback (ArdourCanvas::Item*, GdkEvent*); - void line_drag_finished_callback (ArdourCanvas::Item*, GdkEvent*); - - void tempo_marker_drag_motion_callback (ArdourCanvas::Item*, GdkEvent*); - void tempo_marker_drag_finished_callback (ArdourCanvas::Item*, GdkEvent*); - void meter_marker_drag_motion_callback (ArdourCanvas::Item*, GdkEvent*); - void meter_marker_drag_finished_callback (ArdourCanvas::Item*, GdkEvent*); + void marker_drag_motion_callback (GdkEvent*); + void marker_drag_finished_callback (GdkEvent*); gint mouse_rename_region (ArdourCanvas::Item*, GdkEvent*); - void start_region_grab (ArdourCanvas::Item*, GdkEvent*); + void start_region_grab (ArdourCanvas::Item*, GdkEvent*, RegionView*); void start_create_region_grab (ArdourCanvas::Item*, GdkEvent*); - void start_region_copy_grab (ArdourCanvas::Item*, GdkEvent*); - void start_region_brush_grab (ArdourCanvas::Item*, GdkEvent*); + void start_region_copy_grab (ArdourCanvas::Item*, GdkEvent*, RegionView*); + void start_region_brush_grab (ArdourCanvas::Item*, GdkEvent*, RegionView*); void start_selection_grab (ArdourCanvas::Item*, GdkEvent*); - void start_cursor_grab (ArdourCanvas::Item*, GdkEvent*); - void start_cursor_grab_no_stop (ArdourCanvas::Item*, GdkEvent*); - void start_marker_grab (ArdourCanvas::Item*, GdkEvent*); - void start_control_point_grab (ArdourCanvas::Item*, GdkEvent*); - void start_line_grab_from_regionview (ArdourCanvas::Item*, GdkEvent*); - void start_line_grab_from_line (ArdourCanvas::Item*, GdkEvent*); - void start_line_grab (AutomationLine *, GdkEvent*); - void start_tempo_marker_grab (ArdourCanvas::Item*, GdkEvent*); - void start_tempo_marker_copy_grab (ArdourCanvas::Item*, GdkEvent*); - void start_meter_marker_grab (ArdourCanvas::Item*, GdkEvent*); - void start_meter_marker_copy_grab (ArdourCanvas::Item*, GdkEvent*); void region_view_item_click (AudioRegionView&, GdkEventButton*); @@ -1761,16 +1717,6 @@ public: void point_selection_changed (); void marker_selection_changed (); - enum SelectionOp { - CreateSelection, - SelectionStartTrim, - SelectionEndTrim, - SelectionMove - } selection_op; - - void start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp); - void drag_selection (ArdourCanvas::Item* item, GdkEvent* event); - void end_selection_op (ArdourCanvas::Item* item, GdkEvent* event); void cancel_selection (); void region_selection_op (void (ARDOUR::Region::*pmf)(void)); @@ -1780,24 +1726,11 @@ public: bool audio_region_selection_covers (nframes64_t where); /* transport range select process */ - enum RangeMarkerOp { - CreateRangeMarker, - CreateTransportMarker, - CreateCDMarker - } range_marker_op; - - void start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp); - void drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event); - void end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event); ArdourCanvas::SimpleRect* cd_marker_bar_drag_rect; ArdourCanvas::SimpleRect* range_bar_drag_rect; ArdourCanvas::SimpleRect* transport_bar_drag_rect; - ArdourCanvas::Line* marker_drag_line; - ArdourCanvas::Points marker_drag_line_points; - ArdourCanvas::SimpleRect* range_marker_drag_rect; - void update_marker_drag_item (ARDOUR::Location *); #ifdef GTKOSX ArdourCanvas::SimpleRect *bogus_background_rect; #endif @@ -1818,20 +1751,12 @@ public: /* object rubberband select process */ - void start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event); - void drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event); - void end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event); - bool select_all_within (nframes64_t start, nframes64_t end, gdouble topy, gdouble boty, const TrackViewList&, Selection::Operation op); ArdourCanvas::SimpleRect *rubberband_rect; /* mouse zoom process */ - void start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event); - void drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event); - void end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event); - ArdourCanvas::SimpleRect *zoom_rect; void reposition_zoom_rect (nframes64_t start, nframes64_t end); @@ -1957,24 +1882,14 @@ public: void start_canvas_autoscroll (int x, int y); void stop_canvas_autoscroll (); void maybe_autoscroll (GdkEventMotion*); - void maybe_autoscroll_horizontally (GdkEventMotion*); bool allow_vertical_scroll; /* trimming */ - enum TrimOp { - StartTrim, - EndTrim, - ContentsTrim, - } trim_op; - - void start_trim (ArdourCanvas::Item*, GdkEvent*); void point_trim (GdkEvent*); - void trim_motion_callback (ArdourCanvas::Item*, GdkEvent*); void single_contents_trim (RegionView&, nframes64_t, bool, bool, bool); void single_start_trim (RegionView&, nframes64_t, bool, bool, bool); void single_end_trim (RegionView&, nframes64_t, bool, bool, bool); - void trim_finished_callback (ArdourCanvas::Item*, GdkEvent*); void thaw_region_after_trim (RegionView& rv); void trim_region_front(); @@ -2105,10 +2020,6 @@ public: */ bool mouse_frame (nframes64_t&, bool& in_track_canvas) const; - void time_fx_motion (ArdourCanvas::Item*, GdkEvent*); - void start_time_fx (ArdourCanvas::Item*, GdkEvent*); - void end_time_fx (ArdourCanvas::Item*, GdkEvent*); - /* "whats mine is yours" */ TimeFXDialog* current_timefx; @@ -2309,10 +2220,30 @@ public: std::vector pending_resizes; void visible_order_range (int*, int*) const; - bool y_movement_disallowed (int, int, int, int, int, std::bitset<512> const &, std::vector const &) const; void located (); bool _pending_locate_request; + + friend class Drag; + friend class RegionDrag; + friend class RegionMoveDrag; + friend class RegionSpliceDrag; + friend class TrimDrag; + friend class MeterMarkerDrag; + friend class TempoMarkerDrag; + friend class CursorDrag; + friend class FadeInDrag; + friend class FadeOutDrag; + friend class MarkerDrag; + friend class RegionGainDrag; + friend class ControlPointDrag; + friend class LineDrag; + friend class RubberbandSelectDrag; + friend class TimeFXDrag; + friend class SelectionDrag; + friend class RangeMarkerBarDrag; + friend class MouseZoomDrag; + friend class RegionCreateDrag; }; #endif /* __ardour_editor_h__ */ diff --git a/gtk2_ardour/editor_canvas.cc b/gtk2_ardour/editor_canvas.cc index a92f58a133..10878ce712 100644 --- a/gtk2_ardour/editor_canvas.cc +++ b/gtk2_ardour/editor_canvas.cc @@ -41,6 +41,7 @@ #include "utils.h" #include "time_axis_view.h" #include "audio_time_axis.h" +#include "editor_drag.h" #include "i18n.h" @@ -152,9 +153,6 @@ Editor::initialize_canvas () _background_group = new ArdourCanvas::Group (*track_canvas->root()); _master_group = new ArdourCanvas::Group (*track_canvas->root()); - range_marker_drag_rect = new ArdourCanvas::SimpleRect (*time_line_group, 0.0, 0.0, 0.0, physical_screen_height); - range_marker_drag_rect->hide (); - _trackview_group = new ArdourCanvas::Group (*_master_group); _region_motion_group = new ArdourCanvas::Group (*_trackview_group); @@ -228,14 +226,6 @@ Editor::initialize_canvas () marker_group = new ArdourCanvas::Group (*timebar_group, 0.0, timebar_height); cd_marker_group = new ArdourCanvas::Group (*timebar_group, 0.0, 0.0); - marker_drag_line_points.push_back(Gnome::Art::Point(0.0, 0.0)); - marker_drag_line_points.push_back(Gnome::Art::Point(0.0, physical_screen_height)); - - marker_drag_line = new ArdourCanvas::Line (*timebar_group); - marker_drag_line->property_width_pixels() = 1; - marker_drag_line->property_points() = marker_drag_line_points; - marker_drag_line->hide(); - cd_marker_bar_drag_rect = new ArdourCanvas::SimpleRect (*cd_marker_group, 0.0, 0.0, 100, timebar_height); cd_marker_bar_drag_rect->property_outline_pixels() = 0; cd_marker_bar_drag_rect->hide (); @@ -282,7 +272,7 @@ Editor::initialize_canvas () range_marker_bar->signal_event().connect (bind (mem_fun (*this, &Editor::canvas_range_marker_bar_event), range_marker_bar)); transport_marker_bar->signal_event().connect (bind (mem_fun (*this, &Editor::canvas_transport_marker_bar_event), transport_marker_bar)); - playhead_cursor = new Cursor (*this, &Editor::canvas_playhead_cursor_event); + playhead_cursor = new EditorCursor (*this, &Editor::canvas_playhead_cursor_event); if (logo_item) { logo_item->lower_to_bottom (); @@ -561,9 +551,8 @@ Editor::drop_regions (const RefPtr& context, void Editor::maybe_autoscroll (GdkEventMotion* event) { - nframes64_t rightmost_frame = leftmost_frame + current_page_frames(); - nframes64_t frame = drag_info.current_pointer_frame; + nframes64_t frame = _drag->current_pointer_frame(); bool startit = false; autoscroll_y = 0; @@ -607,43 +596,6 @@ Editor::maybe_autoscroll (GdkEventMotion* event) last_autoscroll_y = autoscroll_y; } -void -Editor::maybe_autoscroll_horizontally (GdkEventMotion* event) -{ - nframes64_t rightmost_frame = leftmost_frame + current_page_frames(); - nframes64_t frame = drag_info.current_pointer_frame; - bool startit = false; - - autoscroll_y = 0; - autoscroll_x = 0; - - if (frame > rightmost_frame) { - - if (rightmost_frame < max_frames) { - autoscroll_x = 1; - startit = true; - } - - } else if (frame < leftmost_frame) { - if (leftmost_frame > 0) { - autoscroll_x = -1; - startit = true; - } - - } - - if ((autoscroll_x != last_autoscroll_x) || (autoscroll_y != last_autoscroll_y) || (autoscroll_x == 0 && autoscroll_y == 0)) { - stop_canvas_autoscroll (); - } - - if (startit && autoscroll_timeout_tag < 0) { - start_canvas_autoscroll (autoscroll_x, autoscroll_y); - } - - last_autoscroll_x = autoscroll_x; - last_autoscroll_y = autoscroll_y; -} - gint Editor::_autoscroll_canvas (void *arg) { @@ -661,19 +613,19 @@ Editor::autoscroll_canvas () if (autoscroll_x_distance != 0) { if (autoscroll_x > 0) { - autoscroll_x_distance = (unit_to_frame (drag_info.current_pointer_x) - (leftmost_frame + current_page_frames())) / 3; + autoscroll_x_distance = (unit_to_frame (_drag->current_pointer_x()) - (leftmost_frame + current_page_frames())) / 3; } else if (autoscroll_x < 0) { - autoscroll_x_distance = (leftmost_frame - unit_to_frame (drag_info.current_pointer_x)) / 3; + autoscroll_x_distance = (leftmost_frame - unit_to_frame (_drag->current_pointer_x())) / 3; } } if (autoscroll_y_distance != 0) { if (autoscroll_y > 0) { - autoscroll_y_distance = (drag_info.current_pointer_y - (get_trackview_group_vertical_offset() + canvas_height)) / 3; + autoscroll_y_distance = (_drag->current_pointer_y() - (get_trackview_group_vertical_offset() + canvas_height)) / 3; } else if (autoscroll_y < 0) { - autoscroll_y_distance = (vertical_adjustment.get_value () - drag_info.current_pointer_y) / 3; + autoscroll_y_distance = (vertical_adjustment.get_value () - _drag->current_pointer_y()) / 3; } } @@ -703,7 +655,7 @@ Editor::autoscroll_canvas () new_pixel = vertical_pos - autoscroll_y_distance; } - target_pixel = drag_info.current_pointer_y - autoscroll_y_distance; + target_pixel = _drag->current_pointer_y() - autoscroll_y_distance; target_pixel = max (target_pixel, 0.0); } else if (autoscroll_y > 0) { @@ -718,7 +670,7 @@ Editor::autoscroll_canvas () new_pixel = min (top_of_bottom_of_canvas, new_pixel); - target_pixel = drag_info.current_pointer_y + autoscroll_y_distance; + target_pixel = _drag->current_pointer_y() + autoscroll_y_distance; /* don't move to the full canvas height because the item will be invisible (its top edge will line up with the bottom of the visible canvas. @@ -727,7 +679,7 @@ Editor::autoscroll_canvas () target_pixel = min (target_pixel, full_canvas_height - 10); } else { - target_pixel = drag_info.current_pointer_y; + target_pixel = _drag->current_pointer_y(); new_pixel = vertical_pos; } @@ -753,7 +705,7 @@ Editor::autoscroll_canvas () ev.x = x; ev.y = y; - motion_handler (drag_info.item, (GdkEvent*) &ev, drag_info.item_type, true); + motion_handler (_drag->item(), (GdkEvent*) &ev, true); autoscroll_cnt++; @@ -911,11 +863,6 @@ Editor::color_handler() transport_bar_drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TransportDragRect.get(); transport_bar_drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TransportDragRect.get(); - marker_drag_line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get(); - - range_marker_drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get(); - range_marker_drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get(); - transport_loop_range_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TransportLoopRect.get(); transport_loop_range_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TransportLoopRect.get(); diff --git a/gtk2_ardour/editor_canvas_events.cc b/gtk2_ardour/editor_canvas_events.cc index c0e90abf39..73c02d75d6 100644 --- a/gtk2_ardour/editor_canvas_events.cc +++ b/gtk2_ardour/editor_canvas_events.cc @@ -42,6 +42,7 @@ #include "canvas_impl.h" #include "simplerect.h" #include "interactive-item.h" +#include "editor_drag.h" #include "i18n.h" @@ -188,8 +189,10 @@ Editor::track_canvas_button_press_event (GdkEventButton *event) bool Editor::track_canvas_button_release_event (GdkEventButton *event) { - if (drag_info.item) { - end_grab (drag_info.item, (GdkEvent*) event); + if (_drag) { + _drag->end_grab ((GdkEvent*) event); + delete _drag; + _drag = 0; } return false; } @@ -229,7 +232,7 @@ Editor::typed_event (ArdourCanvas::Item* item, GdkEvent *event, ItemType type) ret = button_release_handler (item, event, type); break; case GDK_MOTION_NOTIFY: - ret = motion_handler (item, event, type); + ret = motion_handler (item, event); break; case GDK_ENTER_NOTIFY: @@ -272,7 +275,7 @@ Editor::canvas_region_view_event (GdkEvent *event, ArdourCanvas::Item* item, Reg break; case GDK_MOTION_NOTIFY: - ret = motion_handler (item, event, RegionItem); + ret = motion_handler (item, event); break; case GDK_ENTER_NOTIFY: @@ -353,7 +356,7 @@ Editor::canvas_automation_track_event (GdkEvent *event, ArdourCanvas::Item* item break; case GDK_MOTION_NOTIFY: - ret = motion_handler (item, event, AutomationTrackItem); + ret = motion_handler (item, event); break; case GDK_ENTER_NOTIFY: @@ -432,7 +435,7 @@ Editor::canvas_fade_in_handle_event (GdkEvent *event, ArdourCanvas::Item* item, break; case GDK_MOTION_NOTIFY: - ret = motion_handler (item, event, FadeInHandleItem); + ret = motion_handler (item, event); break; case GDK_ENTER_NOTIFY: @@ -511,7 +514,7 @@ Editor::canvas_fade_out_handle_event (GdkEvent *event, ArdourCanvas::Item* item, break; case GDK_MOTION_NOTIFY: - ret = motion_handler (item, event, FadeOutHandleItem); + ret = motion_handler (item, event); break; case GDK_ENTER_NOTIFY: @@ -662,7 +665,7 @@ Editor::canvas_selection_rect_event (GdkEvent *event, ArdourCanvas::Item* item, ret = button_release_handler (item, event, SelectionItem); break; case GDK_MOTION_NOTIFY: - ret = motion_handler (item, event, SelectionItem); + ret = motion_handler (item, event); break; /* Don't need these at the moment. */ case GDK_ENTER_NOTIFY: @@ -696,7 +699,7 @@ Editor::canvas_selection_start_trim_event (GdkEvent *event, ArdourCanvas::Item* ret = button_release_handler (item, event, StartSelectionTrimItem); break; case GDK_MOTION_NOTIFY: - ret = motion_handler (item, event, StartSelectionTrimItem); + ret = motion_handler (item, event); break; case GDK_ENTER_NOTIFY: ret = enter_handler (item, event, StartSelectionTrimItem); @@ -729,7 +732,7 @@ Editor::canvas_selection_end_trim_event (GdkEvent *event, ArdourCanvas::Item* it ret = button_release_handler (item, event, EndSelectionTrimItem); break; case GDK_MOTION_NOTIFY: - ret = motion_handler (item, event, EndSelectionTrimItem); + ret = motion_handler (item, event); break; case GDK_ENTER_NOTIFY: ret = enter_handler (item, event, EndSelectionTrimItem); @@ -770,7 +773,7 @@ Editor::canvas_region_view_name_highlight_event (GdkEvent* event, ArdourCanvas:: ret = button_release_handler (item, event, RegionViewNameHighlight); break; case GDK_MOTION_NOTIFY: - ret = motion_handler (item, event, RegionViewNameHighlight); + ret = motion_handler (item, event); break; case GDK_ENTER_NOTIFY: ret = enter_handler (item, event, RegionViewNameHighlight); @@ -810,7 +813,7 @@ Editor::canvas_region_view_name_event (GdkEvent *event, ArdourCanvas::Item* item ret = button_release_handler (item, event, RegionViewName); break; case GDK_MOTION_NOTIFY: - ret = motion_handler (item, event, RegionViewName); + ret = motion_handler (item, event); break; case GDK_ENTER_NOTIFY: ret = enter_handler (item, event, RegionViewName); diff --git a/gtk2_ardour/editor_cursors.cc b/gtk2_ardour/editor_cursors.cc index 93743f580e..0725ad0d12 100644 --- a/gtk2_ardour/editor_cursors.cc +++ b/gtk2_ardour/editor_cursors.cc @@ -30,7 +30,7 @@ using namespace ARDOUR; using namespace PBD; using namespace Gtk; -Editor::Cursor::Cursor (Editor& ed, bool (Editor::*callbck)(GdkEvent*,ArdourCanvas::Item*)) +EditorCursor::EditorCursor (Editor& ed, bool (Editor::*callbck)(GdkEvent*,ArdourCanvas::Item*)) : editor (ed), canvas_item (*editor.cursor_group), length(1.0) @@ -51,13 +51,13 @@ Editor::Cursor::Cursor (Editor& ed, bool (Editor::*callbck)(GdkEvent*,ArdourCanv current_frame = 1; /* force redraw at 0 */ } -Editor::Cursor::~Cursor () +EditorCursor::~EditorCursor () { } void -Editor::Cursor::set_position (nframes64_t frame) +EditorCursor::set_position (nframes64_t frame) { double new_pos = editor.frame_to_unit (frame); @@ -72,7 +72,7 @@ Editor::Cursor::set_position (nframes64_t frame) } void -Editor::Cursor::set_length (double units) +EditorCursor::set_length (double units) { length = units; points.back().set_y (points.front().get_y() + length); @@ -80,7 +80,7 @@ Editor::Cursor::set_length (double units) } void -Editor::Cursor::set_y_axis (double position) +EditorCursor::set_y_axis (double position) { points.front().set_y (position); points.back().set_y (position + length); diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc new file mode 100644 index 0000000000..e58c4f642b --- /dev/null +++ b/gtk2_ardour/editor_drag.cc @@ -0,0 +1,3281 @@ +/* + Copyright (C) 2009 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "pbd/memento_command.h" +#include "pbd/basename.h" +#include "ardour/diskstream.h" +#include "ardour/dB.h" +#include "ardour/region_factory.h" +#include "ardour/midi_diskstream.h" +#include "editor.h" +#include "i18n.h" +#include "keyboard.h" +#include "audio_region_view.h" +#include "midi_region_view.h" +#include "ardour_ui.h" +#include "control_point.h" +#include "utils.h" +#include "region_gain_line.h" +#include "editor_drag.h" + +using namespace std; +using namespace ARDOUR; +using namespace PBD; +using namespace sigc; +using namespace Gtk; +using namespace Editing; + +double const ControlPointDrag::_zero_gain_fraction = gain_to_slider_position (dB_to_coefficient (0.0)); + +Drag::Drag (Editor* e, ArdourCanvas::Item* i) : + _editor (e), + _item (i), + _pointer_frame_offset (0), + _grab_frame (0), + _last_pointer_frame (0), + _current_pointer_frame (0), + _copy (false) +{ + +} + +void +Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time) +{ + _item->ungrab (0); + _item = new_item; + + if (cursor == 0) { + cursor = _editor->which_grabber_cursor (); + } + + _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time); +} + +void +Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor) +{ + if (cursor == 0) { + cursor = _editor->which_grabber_cursor (); + } + + // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained + + if (Keyboard::is_button2_event (&event->button)) { + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) { + _y_constrained = true; + _x_constrained = false; + } else { + _y_constrained = false; + _x_constrained = true; + } + } else { + _x_constrained = false; + _y_constrained = false; + } + + _grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y); + _last_pointer_frame = _grab_frame; + _current_pointer_frame = _grab_frame; + _current_pointer_x = _grab_x; + _current_pointer_y = _grab_y; + _last_pointer_x = _current_pointer_x; + _last_pointer_y = _current_pointer_y; + _first_move = true; + _move_threshold_passed = false; + _want_move_threshold = false; + + _original_x = 0; + _original_y = 0; + _item->i2w (_original_x, _original_y); + + _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, + *cursor, + event->button.time); + + if (_editor->session && _editor->session->transport_rolling()) { + _was_rolling = true; + } else { + _was_rolling = false; + } + + switch (_editor->snap_type) { + case SnapToRegionStart: + case SnapToRegionEnd: + case SnapToRegionSync: + case SnapToRegionBoundary: + _editor->build_region_boundary_cache (); + break; + default: + break; + } +} + +/* @param event GDK event, or 0 */ +bool +Drag::end_grab (GdkEvent* event) +{ + _ending = true; + + bool did_drag = false; + + _editor->stop_canvas_autoscroll (); + + _item->ungrab (event ? event->button.time : 0); + + if (event) { + _last_pointer_x = _current_pointer_x; + _last_pointer_y = _current_pointer_y; + finished (event); + } + + did_drag = !_first_move; + + _editor->hide_verbose_canvas_cursor(); + + _ending = false; + + update_selection (); + + return did_drag; +} + +nframes64_t +Drag::adjusted_current_frame () const +{ + if (_current_pointer_frame > _pointer_frame_offset) { + return _current_pointer_frame - _pointer_frame_offset; + } + + return 0; +} + +bool +Drag::motion_handler (GdkEvent* event, bool from_autoscroll) +{ + _last_pointer_x = _current_pointer_x; + _last_pointer_y = _current_pointer_y; + _current_pointer_frame = _editor->event_frame (event, &_current_pointer_x, &_current_pointer_y); + + if (!from_autoscroll && !_move_threshold_passed) { + bool xp = (::llabs ((nframes64_t) (_current_pointer_x - _grab_x)) > 4LL); + bool yp = (::llabs ((nframes64_t) (_current_pointer_y - _grab_y)) > 4LL); + + _move_threshold_passed = (xp || yp); + + if (_want_move_threshold && _move_threshold_passed) { + _grab_frame = _current_pointer_frame; + _grab_x = _current_pointer_x; + _grab_y = _current_pointer_y; + _last_pointer_frame = _grab_frame; + _pointer_frame_offset = _grab_frame - _last_frame_position; + } + } + + if (active (_editor->mouse_mode)) { + + if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) { + if (!from_autoscroll) { + _editor->maybe_autoscroll (&event->motion); + } + + motion (event); + return true; + } + } + + return false; +} + + +void +Drag::break_drag () +{ + _editor->stop_canvas_autoscroll (); + _editor->hide_verbose_canvas_cursor (); + + if (_item) { + _item->ungrab (0); + + /* put it back where it came from */ + + double cxw, cyw; + cxw = 0; + cyw = 0; + _item->i2w (cxw, cyw); + _item->move (_original_x - cxw, _original_y - cyw); + } +} + + +RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list const & v) + : Drag (e, i), + _primary (p), + _views (v) +{ + RegionView::RegionViewGoingAway.connect (mem_fun (*this, &RegionDrag::region_going_away)); +} + +void +RegionDrag::region_going_away (RegionView* v) +{ + _views.remove (v); +} + +void +RegionDrag::update_selection () +{ + list s; + copy (_views.begin(), _views.end(), back_inserter (s)); + _editor->selection->set (s); +} + +RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list const & v, bool b, bool c) + : RegionDrag (e, i, p, v), + _brushing (b) +{ + _want_move_threshold = true; + _copy = c; + + _source_trackview = &_primary->get_time_axis_view (); + _source_layer = _primary->region()->layer (); + _dest_trackview = _source_trackview; + _dest_layer = _source_layer; + + double speed = 1; + RouteTimeAxisView* tv = dynamic_cast (_source_trackview); + if (tv && tv->is_track()) { + speed = tv->get_diskstream()->speed (); + } + + _last_frame_position = static_cast (_primary->region()->position() / speed); +} + +void +RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor *) +{ + Drag::start_grab (event); + + _pointer_frame_offset = _grab_frame - _last_frame_position; + _editor->show_verbose_time_cursor (_last_frame_position, 10); +} + +void +RegionMoveDrag::motion (GdkEvent* event) +{ + double x_delta; + double y_delta = 0; + nframes64_t pending_region_position = 0; + int32_t pointer_order_span = 0, canvas_pointer_order_span = 0; + int32_t pointer_layer_span = 0; + + bool clamp_y_axis = false; + vector::iterator j; + + if (_copy && _move_threshold_passed && _want_move_threshold) { + copy_regions (event); + _want_move_threshold = false; // don't copy again + } + + /* *pointer* variables reflect things about the pointer; as we may be moving + multiple regions, much detail must be computed per-region */ + + /* current_pointer_view will become the TimeAxisView that we're currently pointing at, and + current_pointer_layer the current layer on that TimeAxisView */ + RouteTimeAxisView* current_pointer_view; + layer_t current_pointer_layer; + if (!check_possible (¤t_pointer_view, ¤t_pointer_layer)) { + return; + } + + /* TimeAxisView that we were pointing at last time we entered this method */ + TimeAxisView const * const last_pointer_view = _dest_trackview; + /* the order of the track that we were pointing at last time we entered this method */ + int32_t const last_pointer_order = last_pointer_view->order (); + /* the layer that we were pointing at last time we entered this method */ + layer_t const last_pointer_layer = _dest_layer; + + /************************************************************ + Y DELTA COMPUTATION + ************************************************************/ + + /* Height of TimeAxisViews, indexed by order */ + /* XXX: hard-coded limit of TimeAxisViews */ + vector height_list (512); + + if (_brushing) { + clamp_y_axis = true; + pointer_order_span = 0; + goto y_axis_done; + } + + /* the change in track order between this callback and the last */ + pointer_order_span = last_pointer_view->order() - current_pointer_view->order(); + /* the change in layer between this callback and the last; + only meaningful if pointer_order_span == 0 (ie we've not moved tracks) */ + pointer_layer_span = last_pointer_layer - current_pointer_layer; + + if (pointer_order_span != 0) { + + int32_t children = 0; + /* XXX: hard-coded limit of tracks */ + bitset <512> tracks (0x00); + + int visible_y_high; + int visible_y_low; + _editor->visible_order_range (&visible_y_low, &visible_y_high); + + /* get a bitmask representing the visible tracks */ + + for (Editor::TrackViewList::iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) { + RouteTimeAxisView* rtv = dynamic_cast (*i); + TimeAxisView::Children children_list; + + /* zeroes are audio/MIDI tracks. ones are other types. */ + + if (!rtv->hidden()) { + + if (!rtv->is_track()) { + /* not an audio nor MIDI track */ + tracks = tracks |= (0x01 << rtv->order()); + } + + height_list[rtv->order()] = (*i)->current_height(); + children = 1; + + if ((children_list = rtv->get_child_list()).size() > 0) { + for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) { + tracks = tracks |= (0x01 << (rtv->order() + children)); + height_list[rtv->order() + children] = (*j)->current_height(); + children++; + } + } + } + } + + /* find the actual pointer span, in terms of the number of visible tracks; + to do this, we reduce |pointer_order_span| by the number of hidden tracks + over the span */ + + canvas_pointer_order_span = pointer_order_span; + if (last_pointer_view->order() >= current_pointer_view->order()) { + for (int32_t y = current_pointer_view->order(); y < last_pointer_view->order(); y++) { + if (height_list[y] == 0) { + canvas_pointer_order_span--; + } + } + } else { + for (int32_t y = last_pointer_view->order(); y <= current_pointer_view->order(); y++) { + if (height_list[y] == 0) { + canvas_pointer_order_span++; + } + } + } + + for (list::const_iterator i = _editor->selection->regions.by_layer().begin(); i != _editor->selection->regions.by_layer().end(); ++i) { + + RegionView* rv = (*i); + + if (rv->region()->locked()) { + continue; + } + + double ix1, ix2, iy1, iy2; + rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2); + rv->get_canvas_frame()->i2w (ix1, iy1); + iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize; + + /* get the new trackview for this particular region */ + pair const tvp = _editor->trackview_by_y_position (iy1); + assert (tvp.first); + RouteTimeAxisView* rtv = dynamic_cast (tvp.first); + + /* I know this method has a slightly excessive argument list, but I think + it's nice to separate the code out all the same, since it has such a + simple result, and it makes it clear that there are no other + side-effects. + */ + + /* XXX: not sure that we should be passing canvas_pointer_order_span in here, + as surely this is a per-region thing... */ + + clamp_y_axis = y_movement_disallowed ( + rtv->order(), last_pointer_order, canvas_pointer_order_span, visible_y_low, visible_y_high, + tracks, height_list + ); + + if (clamp_y_axis) { + break; + } + } + + } else if (_dest_trackview == current_pointer_view) { + + if (current_pointer_layer == last_pointer_layer) { + /* No movement; clamp */ + clamp_y_axis = true; + } + } + + y_axis_done: + if (!clamp_y_axis) { + _dest_trackview = current_pointer_view; + _dest_layer = current_pointer_layer; + } + + /************************************************************ + 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 (_move_threshold_passed) { + + if (_current_pointer_frame >= _pointer_frame_offset) { + + nframes64_t sync_frame; + nframes64_t sync_offset; + int32_t sync_dir; + + pending_region_position = _current_pointer_frame - _pointer_frame_offset; + + sync_offset = _primary->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())) { + _editor->snap_to (sync_frame); + } + + pending_region_position = _primary->region()->adjust_to_sync (sync_frame); + + } else { + pending_region_position = _last_frame_position; + } + + } else { + pending_region_position = 0; + } + + if (pending_region_position > max_frames - _primary->region()->length()) { + pending_region_position = _last_frame_position; + } + + bool x_move_allowed; + + if (Config->get_edit_mode() == Lock) { + if (_copy) { + x_move_allowed = !_x_constrained; + } else { + /* in locked edit mode, reverse the usual meaning of _x_constrained */ + x_move_allowed = _x_constrained; + } + } else { + x_move_allowed = !_x_constrained; + } + + if (( pending_region_position != _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 > _last_frame_position) { + x_delta = ((double) (pending_region_position - _last_frame_position) / _editor->frames_per_unit); + } else { + x_delta = -((double) (_last_frame_position - pending_region_position) / _editor->frames_per_unit); + for (list::const_iterator i = _views.begin(); i != _views.end(); ++i) { + + RegionView* rv = (*i); + + // If any regionview is at zero, we need to know so we can stop further leftward motion. + + double ix1, ix2, iy1, iy2; + rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2); + rv->get_canvas_frame()->i2w (ix1, iy1); + + if (-x_delta > ix1 + _editor->horizontal_adjustment.get_value()) { + x_delta = 0; + pending_region_position = _last_frame_position; + break; + } + } + + } + + _last_frame_position = pending_region_position; + + } else { + x_delta = 0; + } + + } else { + /* threshold not passed */ + + x_delta = 0; + } + + /************************************************************* + PREPARE TO MOVE + ************************************************************/ + + if (x_delta == 0 && pointer_order_span == 0 && pointer_layer_span == 0) { + /* haven't reached next snap point, and we're not switching + trackviews nor layers. nothing to do. + */ + return; + } + + /************************************************************* + MOTION + ************************************************************/ + bool do_move = true; + if (_first_move) { + if (!_move_threshold_passed) { + do_move = false; + } + } + + if (do_move) { + + pair >::iterator,bool> insert_result; + + for (list::const_iterator i = _views.begin(); i != _views.end(); ++i) { + + RegionView* rv = (*i); + + if (rv->region()->locked()) { + continue; + } + + /* here we are calculating the y distance from the + top of the first track view to the top of the region + area of the track view that we're working on */ + + /* this x value is just a dummy value so that we have something + to pass to i2w () */ + + double ix1 = 0; + + /* distance from the top of this track view to the region area + of our track view is always 1 */ + + double iy1 = 1; + + /* convert to world coordinates, ie distance from the top of + the ruler section */ + + rv->get_canvas_frame()->i2w (ix1, iy1); + + /* compensate for the ruler section and the vertical scrollbar position */ + iy1 += _editor->get_trackview_group_vertical_offset (); + + if (_first_move) { + + // hide any dependent views + + rv->get_time_axis_view().hide_dependent_views (*rv); + + /* + 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_canvas_group()->property_y() = iy1 - 1; + rv->get_canvas_group()->reparent(*(_editor->_region_motion_group)); + + rv->fake_set_opaque (true); + } + + /* current view for this particular region */ + pair pos = _editor->trackview_by_y_position (iy1); + RouteTimeAxisView* rtv = dynamic_cast (pos.first); + + if (pointer_order_span != 0 && !clamp_y_axis) { + + /* INTER-TRACK MOVEMENT */ + + /* move through the height list to the track that the region is currently on */ + vector::iterator j = height_list.begin (); + int32_t x = 0; + while (j != height_list.end () && x != rtv->order ()) { + ++x; + ++j; + } + + y_delta = 0; + int32_t temp_pointer_order_span = canvas_pointer_order_span; + + if (j != height_list.end ()) { + + /* Account for layers in the original and + destination tracks. If we're moving around in layers we assume + that only one track is involved, so it's ok to use *pointer* + variables here. */ + + StreamView* lv = last_pointer_view->view (); + assert (lv); + + /* move to the top of the last trackview */ + if (lv->layer_display () == Stacked) { + y_delta -= (lv->layers() - last_pointer_layer - 1) * lv->child_height (); + } + + StreamView* cv = current_pointer_view->view (); + assert (cv); + + /* move to the right layer on the current trackview */ + if (cv->layer_display () == Stacked) { + y_delta += (cv->layers() - current_pointer_layer - 1) * cv->child_height (); + } + + /* And for being on a non-topmost layer on the new + track */ + + while (temp_pointer_order_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 != last_pointer_order) { + if ((*j) == 0) { + ++temp_pointer_order_span; + } + } + + y_delta -= (*j); + temp_pointer_order_span--; + } + + while (temp_pointer_order_span < 0) { + + y_delta += (*j); + + if (x != last_pointer_order) { + if ((*j) == 0) { + --temp_pointer_order_span; + } + } + + if (j != height_list.end()) { + j++; + } + + temp_pointer_order_span++; + } + + + /* find out where we'll be when we move and set height accordingly */ + + pair const pos = _editor->trackview_by_y_position (iy1 + y_delta); + RouteTimeAxisView const * temp_rtv = dynamic_cast (pos.first); + rv->set_height (temp_rtv->view()->child_height()); + + /* if you un-comment the following, the region colours will follow + the track colours whilst dragging; personally + i think this can confuse things, but never mind. + */ + + //const GdkColor& col (temp_rtv->view->get_region_color()); + //rv->set_color (const_cast(col)); + } + } + + if (pointer_order_span == 0 && pointer_layer_span != 0 && !clamp_y_axis) { + + /* INTER-LAYER MOVEMENT in the same track */ + y_delta = rtv->view()->child_height () * pointer_layer_span; + } + + + if (_brushing) { + _editor->mouse_brush_insert_region (rv, pending_region_position); + } else { + rv->move (x_delta, y_delta); + } + + } /* foreach region */ + + } /* if do_move */ + + if (_first_move && _move_threshold_passed) { + _editor->cursor_group->raise_to_top(); + _first_move = false; + } + + if (x_delta != 0 && !_brushing) { + _editor->show_verbose_time_cursor (_last_frame_position, 10); + } +} + +void +RegionMoveDrag::finished (GdkEvent* event) +{ + bool nocommit = true; + vector copies; + RouteTimeAxisView* source_tv; + boost::shared_ptr ds; + boost::shared_ptr from_playlist; + list new_views; + 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; + pair tvp; + map final; + + /* _first_move is set to false if the regionview has been moved in the + motion handler. + */ + + if (_first_move) { + /* just a click */ + goto out; + } + + nocommit = false; + + if (Config->get_edit_mode() == Splice && !_editor->pre_drag_region_selection.empty()) { + _editor->selection->set (_editor->pre_drag_region_selection); + _editor->pre_drag_region_selection.clear (); + } + + if (_brushing) { + /* all changes were made during motion event handlers */ + + if (_copy) { + for (list::iterator i = _views.begin(); i != _views.end(); ++i) { + copies.push_back (*i); + } + } + + goto out; + } + + char* op_string; + + /* reverse this here so that we have the correct logic to finalize + the drag. + */ + + if (Config->get_edit_mode() == Lock && !_copy) { + _x_constrained = !_x_constrained; + } + + if (_copy) { + if (_x_constrained) { + op_string = _("fixed time region copy"); + } else { + op_string = _("region copy"); + } + } else { + if (_x_constrained) { + op_string = _("fixed time region drag"); + } else { + op_string = _("region drag"); + } + } + + _editor->begin_reversible_command (op_string); + changed_position = (_last_frame_position != (nframes64_t) (_primary->region()->position())); + tvp = _editor->trackview_by_y_position (_current_pointer_y); + changed_tracks = (tvp.first != &_primary->get_time_axis_view()); + + drag_delta = _primary->region()->position() - _last_frame_position; + + _editor->track_canvas->update_now (); + + /* make a list of where each region ended up */ + for (list::const_iterator i = _views.begin(); i != _views.end(); ++i) { + + double ix1, ix2, iy1, iy2; + (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2); + (*i)->get_canvas_frame()->i2w (ix1, iy1); + iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize; + + pair tv = _editor->trackview_by_y_position (iy1); + final[*i] = dynamic_cast (tv.first); + } + + for (list::const_iterator i = _views.begin(); i != _views.end(); ) { + + RegionView* rv = (*i); + RouteTimeAxisView* dest_rtv = final[*i]; + + nframes64_t where; + + if (rv->region()->locked()) { + ++i; + continue; + } + + if (changed_position && !_x_constrained) { + where = rv->region()->position() - drag_delta; + } else { + where = rv->region()->position(); + } + + boost::shared_ptr new_region; + + if (_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 || _copy) { + + boost::shared_ptr to_playlist = dest_rtv->playlist(); + + if (!to_playlist) { + ++i; + continue; + } + + _editor->latest_regionviews.clear (); + + sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (mem_fun(*_editor, &Editor::collect_new_region_view)); + + insert_result = modified_playlists.insert (to_playlist); + + if (insert_result.second) { + _editor->session->add_command (new MementoCommand(*to_playlist, &to_playlist->get_state(), 0)); + } + + to_playlist->add_region (new_region, where); + + c.disconnect (); + + if (!_editor->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_views.push_back (_editor->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. + */ + + 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 = modified_playlists.insert (playlist); + + if (insert_result.second) { + _editor->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); + } + + if (changed_tracks && !_copy) { + + /* get the playlist where this drag started. we can't use rv->region()->playlist() + because we may have copied the region and it has not been attached to a playlist. + */ + + assert ((source_tv = dynamic_cast (&rv->get_time_axis_view()))); + assert ((ds = source_tv->get_diskstream())); + assert ((from_playlist = ds->playlist())); + + /* moved to a different audio track, without copying */ + + /* the region that used to be in the old playlist is not + moved to the new one - we use a copy of it. as a result, + any existing editor for the region should no longer be + visible. + */ + + rv->hide_region_editor(); + rv->fake_set_opaque (false); + + /* remove the region from the old playlist */ + + insert_result = modified_playlists.insert (from_playlist); + + if (insert_result.second) { + _editor->session->add_command (new MementoCommand(*from_playlist, &from_playlist->get_state(), 0)); + } + + 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). + + this could have invalidated any and all iterators into the region selection. + + the heuristic we use here is: if the region selection is empty, break out of the loop + here. if the region selection is not empty, then restart the loop because we know that + we must have removed at least the region(view) we've just been working on as well as any + that we processed on previous iterations. + + EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and + we can just iterate. + */ + + if (_views.empty()) { + break; + } else { + i = _views.begin(); + } + + } else { + ++i; + } + + if (_copy) { + copies.push_back (rv); + } + } + + if (new_views.empty()) { + if (_copy) { + /* the region(view)s that are being dragged around are copies and do not + belong to any track. remove them from our list + */ + _views.clear (); + } + } else { + _views = new_views; + _primary = _views.front (); + } + + for (set >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) { + (*p)->thaw(); + } + + out: + if (!nocommit) { + for (set >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) { + _editor->session->add_command (new MementoCommand(*(*p), 0, &(*p)->get_state())); + } + + _editor->commit_reversible_command (); + } + + for (vector::iterator x = copies.begin(); x != copies.end(); ++x) { + delete *x; + } +} + + +void +RegionMoveDrag::copy_regions (GdkEvent* event) +{ + /* duplicate the regionview(s) and region(s) */ + + list new_regionviews; + + for (list::const_iterator i = _views.begin(); i != _views.end(); ++i) { + + RegionView* 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); + + RegionView* nrv; + 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; + } + + /* reflect the fact that we are dragging the copies */ + + _primary = new_regionviews.front(); + _views = new_regionviews; + + 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? + */ + _editor->track_canvas->update_now(); +} + +bool +RegionMoveDrag::check_possible (RouteTimeAxisView** tv, layer_t* layer) +{ + /* Which trackview is this ? */ + + pair const tvp = _editor->trackview_by_y_position (current_pointer_y ()); + (*tv) = dynamic_cast (tvp.first); + (*layer) = tvp.second; + + /* The region motion is only processed if the pointer is over + an audio track. + */ + + if (!(*tv) || !(*tv)->is_track()) { + /* To make sure we hide the verbose canvas cursor when the mouse is + not held over and audiotrack. + */ + _editor->hide_verbose_canvas_cursor (); + return false; + } + + return true; +} + +/** @param new_order New track order. + * @param old_order Old track order. + * @param visible_y_low Lowest visible order. + * @param visible_y_high Highest visible order. + * @param tracks Bitset of tracks indexed by order; 0 means a audio/MIDI track, 1 means something else. + * @param heigh_list Heights of tracks indexed by order. + * @return true if y movement should not happen, otherwise false. + */ +bool +RegionMoveDrag::y_movement_disallowed ( + int new_order, int old_order, int y_span, int visible_y_low, int visible_y_high, + bitset<512> const & tracks, vector const & height_list + ) const +{ + if (new_order != old_order) { + + /* this isn't the pointer track */ + + if (y_span > 0) { + + /* moving up the canvas */ + if ( (new_order - y_span) >= visible_y_low) { + + int32_t n = 0; + + /* work out where we'll end up with this y span, taking hidden TimeAxisViews into account */ + int32_t visible_tracks = 0; + while (visible_tracks < y_span ) { + visible_tracks++; + while (height_list[new_order - (visible_tracks - n)] == 0) { + /* passing through a hidden track */ + n--; + } + } + + if (tracks[new_order - (y_span - n)] != 0x00) { + /* moving to a non-track; disallow */ + return true; + } + + + } else { + /* moving beyond the lowest visible track; disallow */ + return true; + } + + } else if (y_span < 0) { + + /* moving down the canvas */ + if ((new_order - y_span) <= visible_y_high) { + + int32_t visible_tracks = 0; + int32_t n = 0; + while (visible_tracks > y_span ) { + visible_tracks--; + + while (height_list[new_order - (visible_tracks - n)] == 0) { + /* passing through a hidden track */ + n++; + } + } + + if (tracks[new_order - (y_span - n)] != 0x00) { + /* moving to a non-track; disallow */ + return true; + } + + + } else { + + /* moving beyond the highest visible track; disallow */ + return true; + } + } + + } else { + + /* this is the pointer's track */ + + if ((new_order - y_span) > visible_y_high) { + /* we will overflow */ + return true; + } else if ((new_order - y_span) < visible_y_low) { + /* we will overflow */ + return true; + } + } + + return false; +} + +RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list const & v) + : RegionMoveDrag (e, i, p, v, false, false) +{ + +} + +struct RegionSelectionByPosition { + bool operator() (RegionView*a, RegionView* b) { + return a->region()->position () < b->region()->position(); + } +}; + +void +RegionSpliceDrag::motion (GdkEvent* event) +{ + RouteTimeAxisView* tv; + layer_t layer; + + if (!check_possible (&tv, &layer)) { + return; + } + + if (!_move_threshold_passed) { + return; + } + + int dir; + + if (_current_pointer_x - _grab_x > 0) { + dir = 1; + } else { + dir = -1; + } + + RegionSelection copy (_editor->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 (_current_pointer_frame < (*i)->region()->last_frame() + 1) { + continue; + } + } else { + if (_current_pointer_frame > (*i)->region()->first_frame()) { + continue; + } + } + + + playlist->shuffle ((*i)->region(), dir); + + _grab_x = _current_pointer_x; + } +} + +void +RegionSpliceDrag::finished (GdkEvent* event) +{ + +} + + +RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v) + : Drag (e, i), + _view (v) +{ + +} + +void +RegionCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor *) +{ + _source_trackview = _view; + _dest_trackview = _view; + _dest_layer = _source_layer; + + Drag::start_grab (event); +} + + +void +RegionCreateDrag::motion (GdkEvent* event) +{ + if (_move_threshold_passed) { + if (_first_move) { + // TODO: create region-create-drag region view here + _first_move = false; + } + + // TODO: resize region-create-drag region view here + } +} + +void +RegionCreateDrag::finished (GdkEvent* event) +{ + MidiTimeAxisView* mtv = dynamic_cast (_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 (_first_move) { + _editor->begin_reversible_command (_("create region")); + XMLNode &before = mtv->playlist()->get_state(); + + nframes64_t start = _grab_frame; + _editor->snap_to (start, -1); + const Meter& m = _editor->session->tempo_map().meter_at(start); + const Tempo& t = _editor->session->tempo_map().tempo_at(start); + double length = floor (m.frames_per_bar(t, _editor->session->frame_rate())); + + boost::shared_ptr src = _editor->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(); + _editor->session->add_command(new MementoCommand(*mtv->playlist().get(), &before, &after)); + _editor->commit_reversible_command(); + + } else { + motion (event); + // TODO: create region-create-drag region here + } +} + + + + +void +RegionGainDrag::motion (GdkEvent* event) +{ + if (_first_move && _move_threshold_passed) { + _first_move = false; + } +} + +void +RegionGainDrag::finished (GdkEvent *) +{ + +} + +TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list const & v) + : RegionDrag (e, i, p, v) +{ + +} + +void +TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor *) +{ + double speed = 1.0; + TimeAxisView* tvp = &_primary->get_time_axis_view (); + RouteTimeAxisView* tv = dynamic_cast(tvp); + + if (tv && tv->is_track()) { + speed = tv->get_diskstream()->speed(); + } + + nframes64_t region_start = (nframes64_t) (_primary->region()->position() / speed); + nframes64_t region_end = (nframes64_t) (_primary->region()->last_frame() / speed); + nframes64_t region_length = (nframes64_t) (_primary->region()->length() / speed); + + Drag::start_grab (event, _editor->trimmer_cursor); + + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) { + _operation = ContentsTrim; + } else { + /* These will get overridden for a point trim.*/ + if (_current_pointer_frame < (region_start + region_length/2)) { + /* closer to start */ + _operation = StartTrim; + } else if (_current_pointer_frame > (region_end - region_length/2)) { + /* closer to end */ + _operation = EndTrim; + } + } + + switch (_operation) { + case StartTrim: + _editor->show_verbose_time_cursor (region_start, 10); + break; + case EndTrim: + _editor->show_verbose_time_cursor (region_end, 10); + break; + case ContentsTrim: + _editor->show_verbose_time_cursor (_current_pointer_frame, 10); + break; + } +} + +void +TrimDrag::motion (GdkEvent* event) +{ + RegionView* rv = _primary; + nframes64_t frame_delta = 0; + + bool left_direction; + bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()); + + /* snap modifier works differently here.. + its' current state has to be passed to the + various trim functions in order to work properly + */ + + double speed = 1.0; + TimeAxisView* tvp = &_primary->get_time_axis_view (); + RouteTimeAxisView* tv = dynamic_cast(tvp); + pair >::iterator,bool> insert_result; + + if (tv && tv->is_track()) { + speed = tv->get_diskstream()->speed(); + } + + if (_last_pointer_frame > _current_pointer_frame) { + left_direction = true; + } else { + left_direction = false; + } + + if (obey_snap) { + _editor->snap_to (_current_pointer_frame); + } + + if (_current_pointer_frame == _last_pointer_frame) { + return; + } + + if (_first_move) { + + string trim_type; + + switch (_operation) { + case StartTrim: + trim_type = "Region start trim"; + break; + case EndTrim: + trim_type = "Region end trim"; + break; + case ContentsTrim: + trim_type = "Region content trim"; + break; + } + + _editor->begin_reversible_command (trim_type); + + for (list::const_iterator i = _views.begin(); i != _views.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 = _editor->motion_frozen_playlists.insert (pl); + + if (insert_result.second) { + _editor->session->add_command(new MementoCommand(*pl, &pl->get_state(), 0)); + pl->freeze(); + } + } + } + + if (left_direction) { + frame_delta = (_last_pointer_frame - _current_pointer_frame); + } else { + frame_delta = (_current_pointer_frame - _last_pointer_frame); + } + + bool non_overlap_trim = false; + + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) { + non_overlap_trim = true; + } + + switch (_operation) { + case StartTrim: + if ((left_direction == false) && (_current_pointer_frame <= rv->region()->first_frame()/speed)) { + break; + } else { + + for (list::const_iterator i = _views.begin(); i != _views.end(); ++i) { + _editor->single_start_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim); + } + break; + } + + case EndTrim: + if ((left_direction == true) && (_current_pointer_frame > (nframes64_t) (rv->region()->last_frame()/speed))) { + break; + } else { + + for (list::const_iterator i = _views.begin(); i != _views.end(); ++i) { + _editor->single_end_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim); + } + break; + } + + case ContentsTrim: + { + bool swap_direction = false; + + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) { + swap_direction = true; + } + + for (list::const_iterator i = _views.begin(); i != _views.end(); ++i) + { + _editor->single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap); + } + } + break; + } + + switch (_operation) { + case StartTrim: + _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10); + break; + case EndTrim: + _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10); + break; + case ContentsTrim: + _editor->show_verbose_time_cursor(_current_pointer_frame, 10); + break; + } + + _last_pointer_frame = _current_pointer_frame; + _first_move = false; +} + + +void +TrimDrag::finished (GdkEvent* event) +{ + if (!_first_move) { + motion (event); + + if (!_editor->selection->selected (_primary)) { + _editor->thaw_region_after_trim (*_primary); + } else { + + for (list::const_iterator i = _views.begin(); i != _views.end(); ++i) { + _editor->thaw_region_after_trim (**i); + (*i)->fake_set_opaque (true); + } + } + + for (set >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) { + (*p)->thaw (); + _editor->session->add_command (new MementoCommand(*(*p).get(), 0, &(*p)->get_state())); + } + + _editor->motion_frozen_playlists.clear (); + + _editor->commit_reversible_command(); + } else { + /* no mouse movement */ + _editor->point_trim (event); + } +} + +MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c) + : Drag (e, i) +{ + _copy = c; + + _marker = reinterpret_cast (_item->get_data ("marker")); + assert (_marker); +} + +void +MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) +{ + if (_copy) { + // 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", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ()); + MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name, + *new MeterSection (_marker->meter())); + + _item = &new_marker->the_item (); + _marker = new_marker; + + } else { + + MetricSection& section (_marker->meter()); + + if (!section.movable()) { + return; + } + + } + + Drag::start_grab (event, cursor); + + _pointer_frame_offset = _grab_frame - _marker->meter().frame(); + + _editor->show_verbose_time_cursor (_current_pointer_frame, 10); +} + +void +MeterMarkerDrag::motion (GdkEvent* event) +{ + nframes64_t adjusted_frame = adjusted_current_frame (); + + if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { + _editor->snap_to (adjusted_frame); + } + + if (adjusted_frame == _last_pointer_frame) { + return; + } + + _marker->set_position (adjusted_frame); + + _last_pointer_frame = adjusted_frame; + _first_move = false; + + _editor->show_verbose_time_cursor (adjusted_frame, 10); +} + +void +MeterMarkerDrag::finished (GdkEvent* event) +{ + if (_first_move) { + return; + } + + motion (event); + + BBT_Time when; + + TempoMap& map (_editor->session->tempo_map()); + map.bbt_time (_last_pointer_frame, when); + + if (_copy == true) { + _editor->begin_reversible_command (_("copy meter mark")); + XMLNode &before = map.get_state(); + map.add_meter (_marker->meter(), when); + XMLNode &after = map.get_state(); + _editor->session->add_command(new MementoCommand(map, &before, &after)); + _editor->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 { + _editor->begin_reversible_command (_("move meter mark")); + XMLNode &before = map.get_state(); + map.move_meter (_marker->meter(), when); + XMLNode &after = map.get_state(); + _editor->session->add_command(new MementoCommand(map, &before, &after)); + _editor->commit_reversible_command (); + } +} + +TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c) + : Drag (e, i) +{ + _copy = c; + + TempoMarker* _marker = reinterpret_cast (_item->get_data ("marker")); + assert (_marker); +} + +void +TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) +{ + + if (_copy) { + + // 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", _marker->tempo().beats_per_minute()); + TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name, + *new TempoSection (_marker->tempo())); + + _item = &new_marker->the_item (); + _marker = new_marker; + + } else { + + MetricSection& section (_marker->tempo()); + + if (!section.movable()) { + return; + } + } + + Drag::start_grab (event, cursor); + + _pointer_frame_offset = _grab_frame - _marker->tempo().frame(); + _editor->show_verbose_time_cursor (_current_pointer_frame, 10); +} + +void +TempoMarkerDrag::motion (GdkEvent* event) +{ + nframes64_t adjusted_frame = adjusted_current_frame (); + + if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { + _editor->snap_to (adjusted_frame); + } + + if (adjusted_frame == _last_pointer_frame) { + return; + } + + /* OK, we've moved far enough to make it worth actually move the thing. */ + + _marker->set_position (adjusted_frame); + + _editor->show_verbose_time_cursor (adjusted_frame, 10); + + _last_pointer_frame = adjusted_frame; + _first_move = false; +} + +void +TempoMarkerDrag::finished (GdkEvent* event) +{ + if (_first_move) { + return; + } + + motion (event); + + BBT_Time when; + + TempoMap& map (_editor->session->tempo_map()); + map.bbt_time (_last_pointer_frame, when); + + if (_copy == true) { + _editor->begin_reversible_command (_("copy tempo mark")); + XMLNode &before = map.get_state(); + map.add_tempo (_marker->tempo(), when); + XMLNode &after = map.get_state(); + _editor->session->add_command (new MementoCommand(map, &before, &after)); + _editor->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 { + _editor->begin_reversible_command (_("move tempo mark")); + XMLNode &before = map.get_state(); + map.move_tempo (_marker->tempo(), when); + XMLNode &after = map.get_state(); + _editor->session->add_command (new MementoCommand(map, &before, &after)); + _editor->commit_reversible_command (); + } +} + + +CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s) + : Drag (e, i), + _stop (s) +{ + _cursor = reinterpret_cast (_item->get_data ("cursor")); + assert (_cursor); +} + +void +CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c) +{ + Drag::start_grab (event, c); + + if (!_stop) { + + nframes64_t where = _editor->event_frame (event, 0, 0); + + _editor->snap_to (where); + _editor->playhead_cursor->set_position (where); + + } + + if (_cursor == _editor->playhead_cursor) { + _editor->_dragging_playhead = true; + + if (_editor->session && _was_rolling && _stop) { + _editor->session->request_stop (); + } + + if (_editor->session && _editor->session->is_auditioning()) { + _editor->session->cancel_audition (); + } + } + + _pointer_frame_offset = _grab_frame - _cursor->current_frame; + + _editor->show_verbose_time_cursor (_cursor->current_frame, 10); +} + +void +CursorDrag::motion (GdkEvent* event) +{ + nframes64_t adjusted_frame = adjusted_current_frame (); + + if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { + if (_cursor == _editor->playhead_cursor) { + _editor->snap_to (adjusted_frame); + } + } + + if (adjusted_frame == _last_pointer_frame) { + return; + } + + _cursor->set_position (adjusted_frame); + + _editor->show_verbose_time_cursor (_cursor->current_frame, 10); + +#ifdef GTKOSX + _editor->track_canvas->update_now (); +#endif + _editor->UpdateAllTransportClocks (_cursor->current_frame); + + _last_pointer_frame = adjusted_frame; + _first_move = false; +} + +void +CursorDrag::finished (GdkEvent* event) +{ + _editor->_dragging_playhead = false; + + if (_first_move && _stop) { + return; + } + + motion (event); + + if (_item == &_editor->playhead_cursor->canvas_item) { + if (_editor->session) { + _editor->session->request_locate (_editor->playhead_cursor->current_frame, _was_rolling); + _editor->_pending_locate_request = true; + } + } +} + +FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list const & v) + : RegionDrag (e, i, p, v) +{ + +} + +void +FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) +{ + Drag::start_grab (event, cursor); + + AudioRegionView* a = dynamic_cast (_primary); + boost::shared_ptr const r = a->audio_region (); + + _pointer_frame_offset = _grab_frame - ((nframes64_t) r->fade_in()->back()->when + r->position()); +} + +void +FadeInDrag::motion (GdkEvent* event) +{ + nframes64_t fade_length; + + nframes64_t pos = adjusted_current_frame (); + + if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { + _editor->snap_to (pos); + } + + boost::shared_ptr region = _primary->region (); + + if (pos < (region->position() + 64)) { + fade_length = 64; // this should be a minimum defined somewhere + } else if (pos > region->last_frame()) { + fade_length = region->length(); + } else { + fade_length = pos - region->position(); + } + + for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) { + + AudioRegionView* tmp = dynamic_cast (*i); + + if (!tmp) { + continue; + } + + tmp->reset_fade_in_shape_width (fade_length); + } + + _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10); + + _first_move = false; +} + +void +FadeInDrag::finished (GdkEvent* event) +{ + nframes64_t fade_length; + + if (_first_move) { + return; + } + + nframes64_t const pos = adjusted_current_frame (); + + boost::shared_ptr region = _primary->region (); + + if (pos < (region->position() + 64)) { + fade_length = 64; // this should be a minimum defined somewhere + } else if (pos > region->last_frame()) { + fade_length = region->length(); + } else { + fade_length = pos - region->position(); + } + + _editor->begin_reversible_command (_("change fade in length")); + + for (RegionSelection::iterator i = _views.begin(); i != _views.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(); + _editor->session->add_command(new MementoCommand(*alist.get(), &before, &after)); + } + + _editor->commit_reversible_command (); +} + +FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list const & v) + : RegionDrag (e, i, p, v) +{ + +} + +void +FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) +{ + Drag::start_grab (event, cursor); + + AudioRegionView* a = dynamic_cast (_primary); + boost::shared_ptr r = a->audio_region (); + + _pointer_frame_offset = _grab_frame - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position()); +} + +void +FadeOutDrag::motion (GdkEvent* event) +{ + nframes64_t fade_length; + + nframes64_t pos = adjusted_current_frame (); + + if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { + _editor->snap_to (pos); + } + + boost::shared_ptr region = _primary->region (); + + if (pos > (region->last_frame() - 64)) { + fade_length = 64; // this should really be a minimum fade defined somewhere + } + else if (pos < region->position()) { + fade_length = region->length(); + } + else { + fade_length = region->last_frame() - pos; + } + + for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) { + + AudioRegionView* tmp = dynamic_cast (*i); + + if (!tmp) { + continue; + } + + tmp->reset_fade_out_shape_width (fade_length); + } + + _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10); + + _first_move = false; +} + +void +FadeOutDrag::finished (GdkEvent* event) +{ + if (_first_move) { + return; + } + + nframes64_t fade_length; + + nframes64_t pos = adjusted_current_frame (); + + if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { + _editor->snap_to (pos); + } + + boost::shared_ptr region = _primary->region (); + + if (pos > (region->last_frame() - 64)) { + fade_length = 64; // this should really be a minimum fade defined somewhere + } + else if (pos < region->position()) { + fade_length = region->length(); + } + else { + fade_length = region->last_frame() - pos; + } + + _editor->begin_reversible_command (_("change fade out length")); + + for (RegionSelection::iterator i = _views.begin(); i != _views.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(); + _editor->session->add_command(new MementoCommand(*alist.get(), &before, &after)); + } + + _editor->commit_reversible_command (); +} + +MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i) + : Drag (e, i) +{ + _marker = reinterpret_cast (_item->get_data ("marker")); + assert (_marker); + + _points.push_back (Gnome::Art::Point (0, 0)); + _points.push_back (Gnome::Art::Point (0, _editor->physical_screen_height)); + + _line = new ArdourCanvas::Line (*_editor->timebar_group); + _line->property_width_pixels() = 1; + _line->property_points () = _points; + _line->hide (); + + _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get(); +} + +MarkerDrag::~MarkerDrag () +{ + for (list::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) { + delete *i; + } +} + +void +MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) +{ + Drag::start_grab (event, cursor); + + bool is_start; + + Location *location = _editor->find_location_from_marker (_marker, is_start); + _editor->_dragging_edit_point = true; + + _pointer_frame_offset = _grab_frame - (is_start ? location->start() : location->end()); + + update_item (location); + + // _drag_line->show(); + // _line->raise_to_top(); + + if (is_start) { + _editor->show_verbose_time_cursor (location->start(), 10); + } else { + _editor->show_verbose_time_cursor (location->end(), 10); + } + + Selection::Operation op = Keyboard::selection_type (event->button.state); + + switch (op) { + case Selection::Toggle: + _editor->selection->toggle (_marker); + break; + case Selection::Set: + if (!_editor->selection->selected (_marker)) { + _editor->selection->set (_marker); + } + break; + case Selection::Extend: + { + Locations::LocationList ll; + list to_add; + nframes64_t s, e; + _editor->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; + } + _editor->session->locations()->find_all_between (s, e, ll, Location::Flags (0)); + for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) { + Editor::LocationMarkers* lm = _editor->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()) { + _editor->selection->add (to_add); + } + break; + } + case Selection::Add: + _editor->selection->add (_marker); + break; + } + + /* set up copies for us to manipulate during the drag */ + + for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) { + Location *l = _editor->find_location_from_marker (*i, is_start); + _copied_locations.push_back (new Location (*l)); + } +} + +void +MarkerDrag::motion (GdkEvent* event) +{ + nframes64_t f_delta = 0; + bool is_start; + bool move_both = false; + Marker* marker; + Location *real_location; + Location *copy_location = 0; + + nframes64_t newframe = adjusted_current_frame (); + + nframes64_t next = newframe; + + if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { + _editor->snap_to (newframe, 0, true); + } + + if (_current_pointer_frame == _last_pointer_frame) { + return; + } + + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) { + move_both = true; + } + + MarkerSelection::iterator i; + list::iterator x; + + /* find the marker we're dragging, and compute the delta */ + + for (i = _editor->selection->markers.begin(), x = _copied_locations.begin(); + x != _copied_locations.end() && i != _editor->selection->markers.end(); + ++i, ++x) { + + copy_location = *x; + marker = *i; + + if (marker == _marker) { + + if ((real_location = _editor->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 (i == _editor->selection->markers.end()) { + /* hmm, impossible - we didn't find the dragged marker */ + return; + } + + /* now move them all */ + + for (i = _editor->selection->markers.begin(), x = _copied_locations.begin(); + x != _copied_locations.end() && i != _editor->selection->markers.end(); + ++i, ++x) { + + copy_location = *x; + marker = *i; + + /* call this to find out if its the start or end */ + + if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) { + continue; + } + + if (real_location->locked()) { + continue; + } + + if (copy_location->is_mark()) { + + /* 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; + + 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 { + _editor->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) { + _editor->snap_to (next, -1, true); + copy_location->set_start (next); + copy_location->set_end (newframe); + } + } + } + + update_item (copy_location); + + Editor::LocationMarkers* lm = _editor->find_location_markers (real_location); + + if (lm) { + lm->set_position (copy_location->start(), copy_location->end()); + } + } + + _last_pointer_frame = _current_pointer_frame; + _first_move = false; + + assert (!_copied_locations.empty()); + + _editor->edit_point_clock.set (_copied_locations.front()->start()); + _editor->show_verbose_time_cursor (newframe, 10); + +#ifdef GTKOSX + _editor->track_canvas->update_now (); +#endif + _editor->edit_point_clock.set (copy_location->start()); +} + +void +MarkerDrag::finished (GdkEvent* event) +{ + if (_first_move) { + + /* just a click, do nothing but finish + off the selection process + */ + + Selection::Operation op = Keyboard::selection_type (event->button.state); + + switch (op) { + case Selection::Set: + if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) { + _editor->selection->set (_marker); + } + break; + + case Selection::Toggle: + case Selection::Extend: + case Selection::Add: + break; + } + + return; + } + + _editor->_dragging_edit_point = false; + + + _editor->begin_reversible_command ( _("move marker") ); + XMLNode &before = _editor->session->locations()->get_state(); + + MarkerSelection::iterator i; + list::iterator x; + bool is_start; + + for (i = _editor->selection->markers.begin(), x = _copied_locations.begin(); + x != _copied_locations.end() && i != _editor->selection->markers.end(); + ++i, ++x) { + + Location * location = _editor->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 = _editor->session->locations()->get_state(); + _editor->session->add_command(new MementoCommand(*(_editor->session->locations()), &before, &after)); + _editor->commit_reversible_command (); + + _line->hide(); +} + +void +MarkerDrag::update_item (Location* location) +{ + double const x1 = _editor->frame_to_pixel (location->start()); + + _points.front().set_x(x1); + _points.back().set_x(x1); + _line->property_points() = _points; +} + +ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i) + : Drag (e, i), + _cumulative_x_drag (0), + _cumulative_y_drag (0) +{ + ControlPoint* _point = reinterpret_cast (_item->get_data ("control_point")); + assert (_point); +} + + +void +ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) +{ + Drag::start_grab (event, _editor->fader_cursor); + + // start the grab at the center of the control point so + // the point doesn't 'jump' to the mouse after the first drag + _grab_x = _point->get_x(); + _grab_y = _point->get_y(); + + _point->line().parent_group().i2w (_grab_x, _grab_y); + _editor->track_canvas->w2c (_grab_x, _grab_y, _grab_x, _grab_y); + + _grab_frame = _editor->pixel_to_frame (_grab_x); + + _point->line().start_drag (_point, _grab_frame, 0); + + float fraction = 1.0 - (_point->get_y() / _point->line().height()); + _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction), + _current_pointer_x + 10, _current_pointer_y + 10); + + _editor->show_verbose_canvas_cursor (); +} + +void +ControlPointDrag::motion (GdkEvent* event) +{ + double dx = _current_pointer_x - _last_pointer_x; + double dy = _current_pointer_y - _last_pointer_y; + + if (event->button.state & Keyboard::SecondaryModifier) { + dx *= 0.1; + dy *= 0.1; + } + + double cx = _grab_x + _cumulative_x_drag + dx; + double cy = _grab_y + _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) * _point->line().height() - .01; + _point->line().parent_group().i2w(_unused, zero_gain_y); + + // make sure we hit zero when passing through + if ((cy < zero_gain_y and (cy - dy) > zero_gain_y) + or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) { + cy = zero_gain_y; + } + + if (_x_constrained) { + cx = _grab_x; + } + if (_y_constrained) { + cy = _grab_y; + } + + _cumulative_x_drag = cx - _grab_x; + _cumulative_y_drag = cy - _grab_y; + + _point->line().parent_group().w2i (cx, cy); + + cx = max (0.0, cx); + cy = max (0.0, cy); + cy = min ((double) _point->line().height(), cy); + + //translate cx to frames + nframes64_t cx_frames = _editor->unit_to_frame (cx); + + if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !_x_constrained) { + _editor->snap_to (cx_frames); + } + + float const fraction = 1.0 - (cy / _point->line().height()); + + bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier); + + _point->line().point_drag (*_point, cx_frames, fraction, push); + + _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction)); + + _first_move = false; +} + +void +ControlPointDrag::finished (GdkEvent* event) +{ + if (_first_move) { + + /* just a click */ + + if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) { + _editor->reset_point_selection (); + } + + } else { + motion (event); + } + _point->line().end_drag (_point); +} + +LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i) + : Drag (e, i), + _line (0), + _cumulative_y_drag (0) +{ + +} +void +LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) +{ + _line = reinterpret_cast (_item->get_data ("line")); + assert (_line); + + _item = &_line->grab_item (); + + /* need to get x coordinate in terms of parent (TimeAxisItemView) + origin, and ditto for y. + */ + + double cx = event->button.x; + double cy = event->button.y; + + _line->parent_group().w2i (cx, cy); + + nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit); + + if (!_line->control_points_adjacent (frame_within_region, _before, _after)) { + /* no adjacent points */ + return; + } + + Drag::start_grab (event, _editor->fader_cursor); + + /* store grab start in parent frame */ + + _grab_x = cx; + _grab_y = cy; + + double fraction = 1.0 - (cy / _line->height()); + + _line->start_drag (0, _grab_frame, fraction); + + _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction), + _current_pointer_x + 10, _current_pointer_y + 10); + + _editor->show_verbose_canvas_cursor (); +} + +void +LineDrag::motion (GdkEvent* event) +{ + double dy = _current_pointer_y - _last_pointer_y; + + if (event->button.state & Keyboard::SecondaryModifier) { + dy *= 0.1; + } + + double cy = _grab_y + _cumulative_y_drag + dy; + + _cumulative_y_drag = cy - _grab_y; + + cy = max (0.0, cy); + cy = min ((double) _line->height(), cy); + + double const fraction = 1.0 - (cy / _line->height()); + + bool push; + + if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) { + push = false; + } else { + push = true; + } + + _line->line_drag (_before, _after, fraction, push); + + _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction)); +} + +void +LineDrag::finished (GdkEvent* event) +{ + motion (event); + _line->end_drag (0); +} + +void +RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *) +{ + Drag::start_grab (event); + _editor->show_verbose_time_cursor (_current_pointer_frame, 10); +} + +void +RubberbandSelectDrag::motion (GdkEvent* event) +{ + nframes64_t start; + nframes64_t end; + double y1; + double y2; + + /* use a bigger drag threshold than the default */ + + if (abs ((int) (_current_pointer_frame - _grab_frame)) < 8) { + return; + } + + if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) { + if (_first_move) { + _editor->snap_to (_grab_frame); + } + _editor->snap_to (_current_pointer_frame); + } + + /* base start and end on initial click position */ + + if (_current_pointer_frame < _grab_frame) { + start = _current_pointer_frame; + end = _grab_frame; + } else { + end = _current_pointer_frame; + start = _grab_frame; + } + + if (_current_pointer_y < _grab_y) { + y1 = _current_pointer_y; + y2 = _grab_y; + } else { + y2 = _current_pointer_y; + y1 = _grab_y; + } + + + if (start != end || y1 != y2) { + + double x1 = _editor->frame_to_pixel (start); + double x2 = _editor->frame_to_pixel (end); + + _editor->rubberband_rect->property_x1() = x1; + _editor->rubberband_rect->property_y1() = y1; + _editor->rubberband_rect->property_x2() = x2; + _editor->rubberband_rect->property_y2() = y2; + + _editor->rubberband_rect->show(); + _editor->rubberband_rect->raise_to_top(); + + _last_pointer_frame = _current_pointer_frame; + _first_move = false; + + _editor->show_verbose_time_cursor (_current_pointer_frame, 10); + } +} + +void +RubberbandSelectDrag::finished (GdkEvent* event) +{ + if (!_first_move) { + + motion (event); + + double y1,y2; + if (_current_pointer_y < _grab_y) { + y1 = _current_pointer_y; + y2 = _grab_y; + } else { + y2 = _current_pointer_y; + y1 = _grab_y; + } + + + Selection::Operation op = Keyboard::selection_type (event->button.state); + bool commit; + + _editor->begin_reversible_command (_("rubberband selection")); + + if (_grab_frame < _last_pointer_frame) { + commit = _editor->select_all_within (_grab_frame, _last_pointer_frame, y1, y2, _editor->track_views, op); + } else { + commit = _editor->select_all_within (_last_pointer_frame, _grab_frame, y1, y2, _editor->track_views, op); + } + + if (commit) { + _editor->commit_reversible_command (); + } + + } else { + if (!getenv("ARDOUR_SAE")) { + _editor->selection->clear_tracks(); + } + _editor->selection->clear_regions(); + _editor->selection->clear_points (); + _editor->selection->clear_lines (); + } + + _editor->rubberband_rect->hide(); +} + +void +TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor *) +{ + Drag::start_grab (event); + + _editor->show_verbose_time_cursor (_current_pointer_frame, 10); +} + +void +TimeFXDrag::motion (GdkEvent* event) +{ + RegionView* rv = _primary; + + if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { + _editor->snap_to (_current_pointer_frame); + } + + if (_current_pointer_frame == _last_pointer_frame) { + return; + } + + if (_current_pointer_frame > rv->region()->position()) { + rv->get_time_axis_view().show_timestretch (rv->region()->position(), _current_pointer_frame); + } + + _last_pointer_frame = _current_pointer_frame; + _first_move = false; + + _editor->show_verbose_time_cursor (_current_pointer_frame, 10); +} + +void +TimeFXDrag::finished (GdkEvent* event) +{ + _primary->get_time_axis_view().hide_timestretch (); + + if (_first_move) { + return; + } + + if (_last_pointer_frame < _primary->region()->position()) { + /* backwards drag of the left edge - not usable */ + return; + } + + nframes64_t newlen = _last_pointer_frame - _primary->region()->position(); + + float percentage = (double) newlen / (double) _primary->region()->length(); + +#ifndef USE_RUBBERBAND + // Soundtouch uses percentage / 100 instead of normal (/ 1) + if (_primary->region()->data_type() == DataType::AUDIO) { + percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f; + } +#endif + + _editor->begin_reversible_command (_("timestretch")); + + // XXX how do timeFX on multiple regions ? + + RegionSelection rs; + rs.add (_primary); + + if (_editor->time_stretch (rs, percentage) == 0) { + _editor->session->commit_reversible_command (); + } +} + +SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o) + : Drag (e, i), + _operation (o) +{ + +} + +void +SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*) +{ + nframes64_t start = 0; + nframes64_t end = 0; + + if (_editor->session == 0) { + return; + } + + Gdk::Cursor* cursor = 0; + + switch (_operation) { + case CreateSelection: + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) { + _copy = true; + } else { + _copy = false; + } + cursor = _editor->selector_cursor; + Drag::start_grab (event, cursor); + break; + + case SelectionStartTrim: + if (_editor->clicked_axisview) { + _editor->clicked_axisview->order_selection_trims (_item, true); + } + Drag::start_grab (event, cursor); + cursor = _editor->trimmer_cursor; + start = _editor->selection->time[_editor->clicked_selection].start; + _pointer_frame_offset = _grab_frame - start; + break; + + case SelectionEndTrim: + if (_editor->clicked_axisview) { + _editor->clicked_axisview->order_selection_trims (_item, false); + } + Drag::start_grab (event, cursor); + cursor = _editor->trimmer_cursor; + end = _editor->selection->time[_editor->clicked_selection].end; + _pointer_frame_offset = _grab_frame - end; + break; + + case SelectionMove: + start = _editor->selection->time[_editor->clicked_selection].start; + Drag::start_grab (event, cursor); + _pointer_frame_offset = _grab_frame - start; + break; + } + + if (_operation == SelectionMove) { + _editor->show_verbose_time_cursor (start, 10); + } else { + _editor->show_verbose_time_cursor (_current_pointer_frame, 10); + } +} + +void +SelectionDrag::motion (GdkEvent* event) +{ + nframes64_t start = 0; + nframes64_t end = 0; + nframes64_t length; + + nframes64_t pending_position = adjusted_current_frame (); + + if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { + _editor->snap_to (pending_position); + } + + /* only alter selection if the current frame is + different from the last frame position (adjusted) + */ + + if (pending_position == _last_pointer_frame) { + return; + } + + switch (_operation) { + case CreateSelection: + + if (_first_move) { + _editor->snap_to (_grab_frame); + } + + if (pending_position < _grab_frame) { + start = pending_position; + end = _grab_frame; + } else { + end = pending_position; + start = _grab_frame; + } + + /* first drag: Either add to the selection + or create a new selection-> + */ + + if (_first_move) { + + _editor->begin_reversible_command (_("range selection")); + + if (_copy) { + /* adding to the selection */ + _editor->clicked_selection = _editor->selection->add (start, end); + _copy = false; + } else { + /* new selection-> */ + _editor->clicked_selection = _editor->selection->set (_editor->clicked_axisview, start, end); + } + } + break; + + case SelectionStartTrim: + + if (_first_move) { + _editor->begin_reversible_command (_("trim selection start")); + } + + start = _editor->selection->time[_editor->clicked_selection].start; + end = _editor->selection->time[_editor->clicked_selection].end; + + if (pending_position > end) { + start = end; + } else { + start = pending_position; + } + break; + + case SelectionEndTrim: + + if (_first_move) { + _editor->begin_reversible_command (_("trim selection end")); + } + + start = _editor->selection->time[_editor->clicked_selection].start; + end = _editor->selection->time[_editor->clicked_selection].end; + + if (pending_position < start) { + end = start; + } else { + end = pending_position; + } + + break; + + case SelectionMove: + + if (_first_move) { + _editor->begin_reversible_command (_("move selection")); + } + + start = _editor->selection->time[_editor->clicked_selection].start; + end = _editor->selection->time[_editor->clicked_selection].end; + + length = end - start; + + start = pending_position; + _editor->snap_to (start); + + end = start + length; + + break; + } + + if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->canvas_width) { + _editor->start_canvas_autoscroll (1, 0); + } + + if (start != end) { + _editor->selection->replace (_editor->clicked_selection, start, end); + } + + _last_pointer_frame = pending_position; + _first_move = false; + + if (_operation == SelectionMove) { + _editor->show_verbose_time_cursor(start, 10); + } else { + _editor->show_verbose_time_cursor(pending_position, 10); + } +} + +void +SelectionDrag::finished (GdkEvent* event) +{ + if (!_first_move) { + motion (event); + /* XXX this is not object-oriented programming at all. ick */ + if (_editor->selection->time.consolidate()) { + _editor->selection->TimeChanged (); + } + _editor->commit_reversible_command (); + } else { + /* just a click, no pointer movement.*/ + + if (Keyboard::no_modifier_keys_pressed (&event->button)) { + + _editor->selection->clear_time(); + + } + } + + /* XXX what happens if its a music selection? */ + _editor->session->set_audio_range (_editor->selection->time); + _editor->stop_canvas_autoscroll (); +} + +RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o) + : Drag (e, i), + _operation (o) +{ + _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0, _editor->physical_screen_height); + _drag_rect->hide (); + + _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get(); + _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get(); +} + +void +RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *) +{ + if (_editor->session == 0) { + return; + } + + Gdk::Cursor* cursor = 0; + + if (!_editor->temp_location) { + _editor->temp_location = new Location; + } + + switch (_operation) { + case CreateRangeMarker: + case CreateTransportMarker: + case CreateCDMarker: + + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) { + _copy = true; + } else { + _copy = false; + } + cursor = _editor->selector_cursor; + break; + } + + Drag::start_grab (event, cursor); + + _editor->show_verbose_time_cursor (_current_pointer_frame, 10); +} + +void +RangeMarkerBarDrag::motion (GdkEvent* event) +{ + nframes64_t start = 0; + nframes64_t end = 0; + ArdourCanvas::SimpleRect *crect; + + switch (_operation) { + case CreateRangeMarker: + crect = _editor->range_bar_drag_rect; + break; + case CreateTransportMarker: + crect = _editor->transport_bar_drag_rect; + break; + case CreateCDMarker: + crect = _editor->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())) { + _editor->snap_to (_current_pointer_frame); + } + + /* only alter selection if the current frame is + different from the last frame position. + */ + + if (_current_pointer_frame == _last_pointer_frame) { + return; + } + + switch (_operation) { + case CreateRangeMarker: + case CreateTransportMarker: + case CreateCDMarker: + if (_first_move) { + _editor->snap_to (_grab_frame); + } + + if (_current_pointer_frame < _grab_frame) { + start = _current_pointer_frame; + end = _grab_frame; + } else { + end = _current_pointer_frame; + start = _grab_frame; + } + + /* first drag: Either add to the selection + or create a new selection. + */ + + if (_first_move) { + + _editor->temp_location->set (start, end); + + crect->show (); + + update_item (_editor->temp_location); + _drag_rect->show(); + //_drag_rect->raise_to_top(); + + } + break; + } + + if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->canvas_width) { + _editor->start_canvas_autoscroll (1, 0); + } + + if (start != end) { + _editor->temp_location->set (start, end); + + double x1 = _editor->frame_to_pixel (start); + double x2 = _editor->frame_to_pixel (end); + crect->property_x1() = x1; + crect->property_x2() = x2; + + update_item (_editor->temp_location); + } + + _last_pointer_frame = _current_pointer_frame; + _first_move = false; + + _editor->show_verbose_time_cursor (_current_pointer_frame, 10); + +} + +void +RangeMarkerBarDrag::finished (GdkEvent* event) +{ + Location * newloc = 0; + string rangename; + int flags; + + if (!_first_move) { + motion (event); + + switch (_operation) { + case CreateRangeMarker: + case CreateCDMarker: + { + _editor->begin_reversible_command (_("new range marker")); + XMLNode &before = _editor->session->locations()->get_state(); + _editor->session->locations()->next_available_name(rangename,"unnamed"); + if (_operation == CreateCDMarker) { + flags = Location::IsRangeMarker | Location::IsCDMarker; + _editor->cd_marker_bar_drag_rect->hide(); + } + else { + flags = Location::IsRangeMarker; + _editor->range_bar_drag_rect->hide(); + } + newloc = new Location(_editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags); + _editor->session->locations()->add (newloc, true); + XMLNode &after = _editor->session->locations()->get_state(); + _editor->session->add_command(new MementoCommand(*(_editor->session->locations()), &before, &after)); + _editor->commit_reversible_command (); + + _drag_rect->hide(); + break; + } + + case CreateTransportMarker: + // popup menu to pick loop or punch + _editor->new_transport_marker_context_menu (&event->button, _item); + + break; + } + } else { + /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */ + + if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) { + + nframes64_t start; + nframes64_t end; + + start = _editor->session->locations()->first_mark_before (_grab_frame); + end = _editor->session->locations()->first_mark_after (_grab_frame); + + if (end == max_frames) { + end = _editor->session->current_end_frame (); + } + + if (start == 0) { + start = _editor->session->current_start_frame (); + } + + switch (_editor->mouse_mode) { + case MouseObject: + /* find the two markers on either side and then make the selection from it */ + _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set); + break; + + case MouseRange: + /* find the two markers on either side of the click and make the range out of it */ + _editor->selection->set (0, start, end); + break; + + default: + break; + } + } + } + + _editor->stop_canvas_autoscroll (); +} + + + +void +RangeMarkerBarDrag::update_item (Location* location) +{ + double const x1 = _editor->frame_to_pixel (location->start()); + double const x2 = _editor->frame_to_pixel (location->end()); + + _drag_rect->property_x1() = x1; + _drag_rect->property_x2() = x2; +} + +void +MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *) +{ + Drag::start_grab (event, _editor->zoom_cursor); + _editor->show_verbose_time_cursor (_current_pointer_frame, 10); +} + +void +MouseZoomDrag::motion (GdkEvent* event) +{ + nframes64_t start; + nframes64_t end; + + if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { + _editor->snap_to (_current_pointer_frame); + + if (_first_move) { + _editor->snap_to (_grab_frame); + } + } + + if (_current_pointer_frame == _last_pointer_frame) { + return; + } + + /* base start and end on initial click position */ + if (_current_pointer_frame < _grab_frame) { + start = _current_pointer_frame; + end = _grab_frame; + } else { + end = _current_pointer_frame; + start = _grab_frame; + } + + if (start != end) { + + if (_first_move) { + _editor->zoom_rect->show(); + _editor->zoom_rect->raise_to_top(); + } + + _editor->reposition_zoom_rect(start, end); + + _last_pointer_frame = _current_pointer_frame; + _first_move = false; + + _editor->show_verbose_time_cursor (_current_pointer_frame, 10); + } +} + +void +MouseZoomDrag::finished (GdkEvent* event) +{ + if (!_first_move) { + motion (event); + + if (_grab_frame < _last_pointer_frame) { + _editor->temporal_zoom_by_frame (_grab_frame, _last_pointer_frame, "mouse zoom"); + } else { + _editor->temporal_zoom_by_frame (_last_pointer_frame, _grab_frame, "mouse zoom"); + } + } else { + _editor->temporal_zoom_to_frame (false, _grab_frame); + /* + temporal_zoom_step (false); + center_screen (_grab_frame); + */ + } + + _editor->zoom_rect->hide(); +} diff --git a/gtk2_ardour/editor_drag.h b/gtk2_ardour/editor_drag.h new file mode 100644 index 0000000000..6a1561ab70 --- /dev/null +++ b/gtk2_ardour/editor_drag.h @@ -0,0 +1,456 @@ +/* + Copyright (C) 2009 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __gtk2_ardour_editor_drag_h_ +#define __gtk2_ardour_editor_drag_h_ + +#include + +#include +#include + +#include "ardour/types.h" + +#include "canvas.h" +#include "editor_items.h" + +namespace ARDOUR { + class Location; +} + +class Editor; +class EditorCursor; +class TimeAxisView; + +/** Abstract base class for dragging of things within the editor */ +class Drag +{ + +public: + Drag (Editor *, ArdourCanvas::Item *); + virtual ~Drag () {} + + /** @return the canvas item being dragged */ + ArdourCanvas::Item* item () const { + return _item; + } + + void swap_grab (ArdourCanvas::Item *, Gdk::Cursor *, uint32_t); + void break_drag (); + + bool motion_handler (GdkEvent*, bool); + + /** @return true if an end drag is in progress */ + bool ending () const { + return _ending; + } + + /** @return true if the first move (past any move threshold) has occurred */ + bool first_move () const { + return _first_move; + } + + /** @return current pointer x position in item coordinates */ + double current_pointer_x () const { + return _current_pointer_x; + } + + /** @return current pointer y position in item coordinates */ + double current_pointer_y () const { + return _current_pointer_y; + } + + /** @return current pointer frame */ + nframes64_t current_pointer_frame () const { + return _current_pointer_frame; + } + + /** Called to start a grab of an item. + * @param e Event that caused the grab to start. + * @param c Cursor to use, or 0. + */ + virtual void start_grab (GdkEvent* e, Gdk::Cursor* c = 0); + + virtual bool end_grab (GdkEvent *); + + /** Called when a drag motion has occurred. + * @param e Event describing the motion. + */ + virtual void motion (GdkEvent* e) = 0; + + /** Called when a drag has finished. + * @param e Event describing the finish. + */ + virtual void finished (GdkEvent *) = 0; + + /** @param m Mouse mode. + * @return true if this drag should happen in this mouse mode. + */ + virtual bool active (Editing::MouseMode m) { + return (m != Editing::MouseGain); + } + + /** Called when a subclass should update the editor's selection following a drag */ + virtual void update_selection () {} + +protected: + nframes64_t adjusted_current_frame () const; + + Editor* _editor; ///< our editor + ArdourCanvas::Item* _item; ///< our item + nframes64_t _pointer_frame_offset; ///< offset from the mouse's position for the drag + ///< to the start of the thing that is being dragged + nframes64_t _last_frame_position; ///< last position of the thing being dragged + nframes64_t _grab_frame; ///< frame that the mouse was at when start_grab was called, or 0 + nframes64_t _last_pointer_frame; ///< frame that the pointer was at last time a motion occurred + nframes64_t _current_pointer_frame; ///< frame that the pointer is now at + double _original_x; ///< original world x of the thing being dragged + double _original_y; ///< original world y of the thing being dragged + double _grab_x; ///< item x of the grab start position + double _grab_y; ///< item y of the grab start position + double _current_pointer_x; ///< item x of the current pointer + double _current_pointer_y; ///< item y of the current pointer + double _last_pointer_x; ///< item x of the pointer last time a motion occurred + double _last_pointer_y; ///< item y of the pointer last time a motion occurred + bool _x_constrained; ///< true if x motion is constrained, otherwise false + bool _y_constrained; ///< true if y motion is constrained, otherwise false + bool _copy; ///< true if we're copying the things that we're dragging + bool _was_rolling; ///< true if the session was rolling before the drag started, otherwise false + bool _first_move; ///< true if some movement has occurred, otherwise false + bool _move_threshold_passed; ///< true if the move threshold has been passed, otherwise false + bool _want_move_threshold; ///< true if a move threshold should be applied, otherwise false + +private: + + bool _ending; ///< true if end_grab is in progress, otherwise false +}; + + +/** Abstract base class for drags that involve region(s) */ +class RegionDrag : public Drag, public sigc::trackable +{ +public: + RegionDrag (Editor *, ArdourCanvas::Item *, RegionView *, std::list const &); + virtual ~RegionDrag () {} + + void update_selection (); + +protected: + + RegionView* _primary; ///< the view that was clicked on (or whatever) to start the drag + std::list _views; ///< all views that are being dragged + +private: + void region_going_away (RegionView *); +}; + + +/** Drags to move regions */ +class RegionMoveDrag : public RegionDrag +{ +public: + RegionMoveDrag (Editor *, ArdourCanvas::Item *, RegionView *, std::list const &, bool, bool); + virtual ~RegionMoveDrag () {} + + virtual void start_grab (GdkEvent *, Gdk::Cursor *); + virtual void motion (GdkEvent *); + virtual void finished (GdkEvent *); + +protected: + + bool check_possible (RouteTimeAxisView **, ARDOUR::layer_t *); + + TimeAxisView* _source_trackview; + ARDOUR::layer_t _source_layer; + TimeAxisView* _dest_trackview; + ARDOUR::layer_t _dest_layer; + +private: + + void copy_regions (GdkEvent *); + bool y_movement_disallowed (int, int, int, int, int, std::bitset<512> const &, std::vector const &) const; + + bool _brushing; +}; + +/** Region drag in splice mode */ +class RegionSpliceDrag : public RegionMoveDrag +{ +public: + RegionSpliceDrag (Editor *, ArdourCanvas::Item *, RegionView *, std::list const &); + + void motion (GdkEvent *); + void finished (GdkEvent *); +}; + +/** Drags to create regions */ +class RegionCreateDrag : public Drag +{ +public: + RegionCreateDrag (Editor *, ArdourCanvas::Item *, TimeAxisView *); + + void start_grab (GdkEvent *, Gdk::Cursor* c = 0); + void motion (GdkEvent *); + void finished (GdkEvent *); + +private: + TimeAxisView* _view; + + TimeAxisView* _source_trackview; + ARDOUR::layer_t _source_layer; + TimeAxisView* _dest_trackview; + ARDOUR::layer_t _dest_layer; +}; + +/** Drag of region gain */ +class RegionGainDrag : public Drag +{ +public: + RegionGainDrag (Editor *e, ArdourCanvas::Item *i) : Drag (e, i) {} + + void motion (GdkEvent *); + void finished (GdkEvent *); + bool active (Editing::MouseMode m) { + return (m == Editing::MouseGain); + } +}; + +/** Drag to trim region(s) */ +class TrimDrag : public RegionDrag +{ +public: + enum Operation { + StartTrim, + EndTrim, + ContentsTrim, + }; + + TrimDrag (Editor *, ArdourCanvas::Item *, RegionView*, std::list const &); + + void start_grab (GdkEvent *, Gdk::Cursor* c = 0); + void motion (GdkEvent *); + void finished (GdkEvent *); + +private: + + Operation _operation; +}; + +/** Meter marker drag */ +class MeterMarkerDrag : public Drag +{ +public: + MeterMarkerDrag (Editor *, ArdourCanvas::Item *, bool); + + void start_grab (GdkEvent *, Gdk::Cursor* c = 0); + void motion (GdkEvent *); + void finished (GdkEvent *); + +private: + MeterMarker* _marker; +}; + +/** Tempo marker drag */ +class TempoMarkerDrag : public Drag +{ +public: + TempoMarkerDrag (Editor *, ArdourCanvas::Item *, bool); + + void start_grab (GdkEvent *, Gdk::Cursor* c = 0); + void motion (GdkEvent *); + void finished (GdkEvent *); + +private: + TempoMarker* _marker; +}; + + +/** Drag of a cursor */ +class CursorDrag : public Drag +{ +public: + CursorDrag (Editor *, ArdourCanvas::Item *, bool); + + void start_grab (GdkEvent *, Gdk::Cursor* c = 0); + void motion (GdkEvent *); + void finished (GdkEvent *); + +private: + EditorCursor* _cursor; ///< cursor being dragged + bool _stop; ///< true to stop the transport on starting the drag, otherwise false + +}; + +/** Region fade-in drag */ +class FadeInDrag : public RegionDrag +{ +public: + FadeInDrag (Editor *, ArdourCanvas::Item *, RegionView *, std::list const &); + + void start_grab (GdkEvent *, Gdk::Cursor* c = 0); + void motion (GdkEvent *); + void finished (GdkEvent *); +}; + +/** Region fade-out drag */ +class FadeOutDrag : public RegionDrag +{ +public: + FadeOutDrag (Editor *, ArdourCanvas::Item *, RegionView *, std::list const &); + + void start_grab (GdkEvent *, Gdk::Cursor* c = 0); + void motion (GdkEvent *); + void finished (GdkEvent *); +}; + +/** Marker drag */ +class MarkerDrag : public Drag +{ +public: + MarkerDrag (Editor *, ArdourCanvas::Item *); + ~MarkerDrag (); + + void start_grab (GdkEvent *, Gdk::Cursor* c = 0); + void motion (GdkEvent *); + void finished (GdkEvent *); + +private: + void update_item (ARDOUR::Location *); + + Marker* _marker; ///< marker being dragged + std::list _copied_locations; + ArdourCanvas::Line* _line; + ArdourCanvas::Points _points; +}; + +/** Control point drag */ +class ControlPointDrag : public Drag +{ +public: + ControlPointDrag (Editor *, ArdourCanvas::Item *); + + void start_grab (GdkEvent *, Gdk::Cursor* c = 0); + void motion (GdkEvent *); + void finished (GdkEvent *); + +private: + + ControlPoint* _point; + double _cumulative_x_drag; + double _cumulative_y_drag; + static double const _zero_gain_fraction; +}; + +/** Gain or automation line drag */ +class LineDrag : public Drag +{ +public: + LineDrag (Editor *e, ArdourCanvas::Item *i); + + void start_grab (GdkEvent *, Gdk::Cursor* c = 0); + void motion (GdkEvent *); + void finished (GdkEvent *); + +private: + + AutomationLine* _line; + uint32_t _before; + uint32_t _after; + double _cumulative_y_drag; +}; + +/** Dragging of a rubberband rectangle for selecting things */ +class RubberbandSelectDrag : public Drag +{ +public: + RubberbandSelectDrag (Editor *e, ArdourCanvas::Item *i) : Drag (e, i) {} + + void start_grab (GdkEvent *, Gdk::Cursor* c = 0); + void motion (GdkEvent *); + void finished (GdkEvent *); +}; + +/** Region drag in time-FX mode */ +class TimeFXDrag : public RegionDrag +{ +public: + TimeFXDrag (Editor *e, ArdourCanvas::Item *i, RegionView* p, std::list const & v) : RegionDrag (e, i, p, v) {} + + void start_grab (GdkEvent *, Gdk::Cursor* c = 0); + void motion (GdkEvent *); + void finished (GdkEvent *); +}; + +/** Drag in range selection mode */ +class SelectionDrag : public Drag +{ +public: + enum Operation { + CreateSelection, + SelectionStartTrim, + SelectionEndTrim, + SelectionMove + }; + + SelectionDrag (Editor *, ArdourCanvas::Item *, Operation); + + void start_grab (GdkEvent *, Gdk::Cursor* c = 0); + void motion (GdkEvent *); + void finished (GdkEvent *); + +private: + Operation _operation; +}; + +/** Range marker drag */ +class RangeMarkerBarDrag : public Drag +{ +public: + enum Operation { + CreateRangeMarker, + CreateTransportMarker, + CreateCDMarker + }; + + RangeMarkerBarDrag (Editor *, ArdourCanvas::Item *, Operation); + + void start_grab (GdkEvent *, Gdk::Cursor* c = 0); + void motion (GdkEvent *); + void finished (GdkEvent *); + +private: + void update_item (ARDOUR::Location *); + + Operation _operation; + ArdourCanvas::SimpleRect* _drag_rect; +}; + +/* Drag of rectangle to set zoom */ +class MouseZoomDrag : public Drag +{ +public: + MouseZoomDrag (Editor *e, ArdourCanvas::Item *i) : Drag (e, i) {} + + void start_grab (GdkEvent *, Gdk::Cursor* c = 0); + void motion (GdkEvent *); + void finished (GdkEvent *); +}; + +#endif /* __gtk2_ardour_editor_drag_h_ */ + diff --git a/gtk2_ardour/editor_keyboard.cc b/gtk2_ardour/editor_keyboard.cc index 32b797a1a0..69bfb3ba8a 100644 --- a/gtk2_ardour/editor_keyboard.cc +++ b/gtk2_ardour/editor_keyboard.cc @@ -27,6 +27,7 @@ #include "region_view.h" #include "selection.h" #include "keyboard.h" +#include "editor_drag.h" #include "i18n.h" @@ -51,9 +52,10 @@ Editor::kbd_driver (sigc::slot theslot, bool use_track_canvas, b /* any use of "keyboard mouse buttons" invalidates an existing grab */ - if (drag_info.item) { - drag_info.item->ungrab (GDK_CURRENT_TIME); - drag_info.item = 0; + if (_drag) { + _drag->item()->ungrab (GDK_CURRENT_TIME); + delete _drag; + _drag = 0; } if (doit) { diff --git a/gtk2_ardour/editor_markers.cc b/gtk2_ardour/editor_markers.cc index d849f7b971..7f9682e358 100644 --- a/gtk2_ardour/editor_markers.cc +++ b/gtk2_ardour/editor_markers.cc @@ -1090,7 +1090,8 @@ Editor::new_transport_marker_menu_popdown () { // hide rects transport_bar_drag_rect->hide(); - range_marker_drag_rect->hide(); + + break_drag (); } void diff --git a/gtk2_ardour/editor_mixer.cc b/gtk2_ardour/editor_mixer.cc index 25c195dcb1..9082f37917 100644 --- a/gtk2_ardour/editor_mixer.cc +++ b/gtk2_ardour/editor_mixer.cc @@ -357,7 +357,7 @@ Editor::session_going_away () entered_regionview = 0; entered_track = 0; last_update_frame = 0; - drag_info.item = 0; + _drag = 0; playhead_cursor->canvas_item.hide (); diff --git a/gtk2_ardour/editor_mouse.cc b/gtk2_ardour/editor_mouse.cc index 6cc92a2f74..b19021c77c 100644 --- a/gtk2_ardour/editor_mouse.cc +++ b/gtk2_ardour/editor_mouse.cc @@ -49,6 +49,7 @@ #include "editing.h" #include "rgb_macros.h" #include "control_point_dialog.h" +#include "editor_drag.h" #include "ardour/types.h" #include "ardour/profile.h" @@ -76,8 +77,6 @@ using namespace sigc; using namespace Gtk; using namespace Editing; -const static double ZERO_GAIN_FRACTION = gain_to_slider_position(dB_to_coefficient(0.0)); - bool Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const { @@ -273,7 +272,7 @@ Editor::set_canvas_cursor () void Editor::set_mouse_mode (MouseMode m, bool force) { - if (drag_info.item) { + if (_drag) { return; } @@ -467,7 +466,7 @@ Editor::midi_edit_mode_toggled (MidiEditMode m) void Editor::set_midi_edit_mode (MidiEditMode m, bool force) { - if (drag_info.item) { + if (_drag) { return; } @@ -654,7 +653,7 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp button_selection (item, event, item_type); - if (drag_info.item == 0 && + if (_drag == 0 && (Keyboard::is_delete_event (&event->button) || Keyboard::is_context_menu_event (&event->button) || Keyboard::is_edit_event (&event->button))) { @@ -668,8 +667,8 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp if (event->type == GDK_BUTTON_PRESS) { - if (drag_info.item) { - drag_info.item->ungrab (event->button.time); + if (_drag) { + _drag->item()->ungrab (event->button.time); } /* single mouse clicks on any of these item types operate @@ -680,67 +679,85 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp switch (item_type) { case PlayheadCursorItem: - start_cursor_grab (item, event); + assert (_drag == 0); + _drag = new CursorDrag (this, item, true); + _drag->start_grab (event); return true; case MarkerItem: if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) { hide_marker (item, event); } else { - start_marker_grab (item, event); + assert (_drag == 0); + _drag = new MarkerDrag (this, item); + _drag->start_grab (event); } return true; case TempoMarkerItem: - if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) { - start_tempo_marker_copy_grab (item, event); - } else { - start_tempo_marker_grab (item, event); - } + assert (_drag); + _drag = new TempoMarkerDrag ( + this, + item, + Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier) + ); + _drag->start_grab (event); return true; case MeterMarkerItem: - if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) { - start_meter_marker_copy_grab (item, event); - } else { - start_meter_marker_grab (item, event); - } + assert (_drag == 0); + + _drag = new MeterMarkerDrag ( + this, + item, + Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier) + ); + + _drag->start_grab (event); return true; case MarkerBarItem: case TempoBarItem: case MeterBarItem: if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) { - start_cursor_grab_no_stop(&playhead_cursor->canvas_item, event); + assert (_drag == 0); + _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false); + _drag->start_grab (event); } return true; break; case RangeMarkerBarItem: + assert (_drag == 0); if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) { - start_cursor_grab_no_stop(&playhead_cursor->canvas_item, event); + _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false); } else { - start_range_markerbar_op (item, event, CreateRangeMarker); + _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker); } + _drag->start_grab (event); return true; break; case CdMarkerBarItem: + assert (_drag == 0); if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) { - start_cursor_grab_no_stop(&playhead_cursor->canvas_item, event); + _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false); } else { - start_range_markerbar_op (item, event, CreateCDMarker); + _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker); } + _drag->start_grab (event); return true; break; case TransportMarkerBarItem: + assert (_drag == 0); if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) { - start_cursor_grab_no_stop(&playhead_cursor->canvas_item, event); + _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false); } else { - start_range_markerbar_op (item, event, CreateTransportMarker); + _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker); } + _drag->start_grab (event); return true; break; @@ -753,11 +770,15 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp case MouseRange: switch (item_type) { case StartSelectionTrimItem: - start_selection_op (item, event, SelectionStartTrim); + assert (_drag == 0); + _drag = new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim); + _drag->start_grab (event); break; case EndSelectionTrimItem: - start_selection_op (item, event, SelectionEndTrim); + assert (_drag == 0); + _drag = new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim); + _drag->start_grab (event); break; case SelectionItem: @@ -767,16 +788,22 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp start_selection_grab (item, event); } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) { /* grab selection for moving */ - start_selection_op (item, event, SelectionMove); + assert (_drag == 0); + _drag = new SelectionDrag (this, item, SelectionDrag::SelectionMove); + _drag->start_grab (event); } else { /* this was debated, but decided the more common action was to make a new selection */ - start_selection_op (item, event, CreateSelection); + assert (_drag == 0); + _drag = new SelectionDrag (this, item, SelectionDrag::CreateSelection); + _drag->start_grab (event); } break; default: - start_selection_op (item, event, CreateSelection); + assert (_drag == 0); + _drag = new SelectionDrag (this, item, SelectionDrag::CreateSelection); + _drag->start_grab (event); } return true; break; @@ -784,54 +811,70 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp case MouseObject: if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) && event->type == GDK_BUTTON_PRESS) { - - start_rubberband_select (item, event); + + assert (_drag == 0); + _drag = new RubberbandSelectDrag (this, item); + _drag->start_grab (event); } else if (event->type == GDK_BUTTON_PRESS) { switch (item_type) { case FadeInHandleItem: - start_fade_in_grab (item, event); + assert (_drag == 0); + _drag = new FadeInDrag (this, item, reinterpret_cast (item->get_data("regionview")), selection->regions); + _drag->start_grab (event); return true; case FadeOutHandleItem: - start_fade_out_grab (item, event); + assert (_drag == 0); + _drag = new FadeOutDrag (this, item, reinterpret_cast (item->get_data("regionview")), selection->regions); + _drag->start_grab (event); return true; case RegionItem: if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) { - start_region_copy_grab (item, event); + start_region_copy_grab (item, event, clicked_regionview); } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) { - start_region_brush_grab (item, event); + start_region_brush_grab (item, event, clicked_regionview); } else { - start_region_grab (item, event); + start_region_grab (item, event, clicked_regionview); } break; case RegionViewNameHighlight: - start_trim (item, event); + assert (_drag == 0); + _drag = new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()); + _drag->start_grab (event); return true; break; case RegionViewName: /* rename happens on edit clicks */ - start_trim (clicked_regionview->get_name_highlight(), event); + assert (_drag == 0); + _drag = new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()); + _drag->start_grab (event); return true; break; case ControlPointItem: - start_control_point_grab (item, event); + assert (_drag == 0); + _drag = new ControlPointDrag (this, item); + _drag->start_grab (event); return true; break; case AutomationLineItem: - start_line_grab_from_line (item, event); + assert (_drag == 0); + _drag = new LineDrag (this, item); + _drag->start_grab (event); return true; break; case StreamItem: case AutomationTrackItem: - start_rubberband_select (item, event); + assert (_drag == 0); + _drag = new RubberbandSelectDrag (this, item); + _drag->start_grab (event); break; #ifdef WITH_CMT @@ -876,18 +919,21 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp /* start a grab so that if we finish after moving we can tell what happened. */ - drag_info.item = item; - drag_info.motion_callback = &Editor::region_gain_motion_callback; - drag_info.finished_callback = 0; - start_grab (event, current_canvas_cursor); + assert (_drag == 0); + _drag = new RegionGainDrag (this, item); + _drag->start_grab (event, current_canvas_cursor); break; case GainLineItem: - start_line_grab_from_line (item, event); + assert (_drag == 0); + _drag = new LineDrag (this, item); + _drag->start_grab (event); return true; case ControlPointItem: - start_control_point_grab (item, event); + assert (_drag == 0); + _drag = new ControlPointDrag (this, item); + _drag->start_grab (event); return true; break; @@ -899,11 +945,15 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp switch (item_type) { case ControlPointItem: - start_control_point_grab (item, event); + assert (_drag == 0); + _drag = new ControlPointDrag (this, item); + _drag->start_grab (event); break; case AutomationLineItem: - start_line_grab_from_line (item, event); + assert (_drag == 0); + _drag = new LineDrag (this, item); + _drag->start_grab (event); break; case RegionItem: @@ -920,7 +970,9 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp case MouseZoom: if (event->type == GDK_BUTTON_PRESS) { - start_mouse_zoom (item, event); + assert (_drag == 0); + _drag = new MouseZoomDrag (this, item); + _drag->start_grab (event); } return true; @@ -928,7 +980,9 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp case MouseTimeFX: if (item_type == RegionItem) { - start_time_fx (item, event); + assert (_drag == 0); + _drag = new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()); + _drag->start_grab (event); } break; @@ -943,7 +997,9 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp break; case MouseNote: - start_create_region_grab (item, event); + assert (_drag == 0); + _drag = new RegionCreateDrag (this, item, clicked_axisview); + _drag->start_grab (event); break; default: @@ -958,14 +1014,16 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp switch (item_type) { case RegionItem: if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) { - start_region_copy_grab (item, event); + start_region_copy_grab (item, event, clicked_regionview); } else { - start_region_grab (item, event); + start_region_grab (item, event, clicked_regionview); } return true; break; case ControlPointItem: - start_control_point_grab (item, event); + assert (_drag == 0); + _drag = new ControlPointDrag (this, item); + _drag->start_grab (event); return true; break; @@ -977,12 +1035,16 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp switch (item_type) { case RegionViewNameHighlight: - start_trim (item, event); + assert (_drag == 0); + _drag = new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()); + _drag->start_grab (event); return true; break; case RegionViewName: - start_trim (clicked_regionview->get_name_highlight(), event); + assert (_drag == 0); + _drag = new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()); + _drag->start_grab (event); return true; break; @@ -1040,8 +1102,11 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT /* first, see if we're finishing a drag ... */ - if (drag_info.item) { - if (end_grab (item, event)) { + if (_drag) { + bool const r = _drag->end_grab (event); + delete _drag; + _drag = 0; + if (r) { /* grab dragged, so do nothing else */ return true; } @@ -1051,7 +1116,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT /* edit events get handled here */ - if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) { + if (_drag == 0 && Keyboard::is_edit_event (&event->button)) { switch (item_type) { case RegionItem: edit_region (); @@ -1085,7 +1150,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT if (Keyboard::is_context_menu_event (&event->button)) { - if (drag_info.item == 0) { + if (_drag == 0) { /* no matter which button pops up the context menu, tell the menu widget to use button 1 to drive menu selection. @@ -1167,7 +1232,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT /* delete events get handled here */ - if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) { + if (_drag == 0 && Keyboard::is_delete_event (&event->button)) { switch (item_type) { case TempoMarkerItem: @@ -1284,7 +1349,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT its really annoying to create new control points when doing this. */ - if (drag_info.first_move) { + if (_drag->first_move ()) { dynamic_cast(clicked_regionview)->add_gain_point_event (item, event); } return true; @@ -1704,13 +1769,13 @@ Editor::scrub () if (scrubbing_direction == 0) { /* first move */ - session->request_locate (drag_info.current_pointer_frame, false); + session->request_locate (_drag->current_pointer_frame(), false); session->request_transport_speed (0.1); scrubbing_direction = 1; } else { - if (last_scrub_x > drag_info.current_pointer_x) { + if (last_scrub_x > _drag->current_pointer_x()) { /* pointer moved to the left */ @@ -1719,7 +1784,7 @@ Editor::scrub () /* we reversed direction to go backwards */ scrub_reversals++; - scrub_reverse_distance += (int) (last_scrub_x - drag_info.current_pointer_x); + scrub_reverse_distance += (int) (last_scrub_x - _drag->current_pointer_x()); } else { @@ -1728,7 +1793,7 @@ Editor::scrub () scrub_reversals = 0; scrub_reverse_distance = 0; - delta = 0.01 * (last_scrub_x - drag_info.current_pointer_x); + delta = 0.01 * (last_scrub_x - _drag->current_pointer_x()); session->request_transport_speed (session->transport_speed() - delta); } @@ -1739,7 +1804,7 @@ Editor::scrub () /* we reversed direction to go forward */ scrub_reversals++; - scrub_reverse_distance += (int) (drag_info.current_pointer_x - last_scrub_x); + scrub_reverse_distance += (int) (_drag->current_pointer_x() - last_scrub_x); } else { /* still moving to the right */ @@ -1747,7 +1812,7 @@ Editor::scrub () scrub_reversals = 0; scrub_reverse_distance = 0; - delta = 0.01 * (drag_info.current_pointer_x - last_scrub_x); + delta = 0.01 * (_drag->current_pointer_x() - last_scrub_x); session->request_transport_speed (session->transport_speed() + delta); } } @@ -1773,11 +1838,11 @@ Editor::scrub () } } - last_scrub_x = drag_info.current_pointer_x; + last_scrub_x = _drag->current_pointer_x(); } bool -Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll) +Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, bool from_autoscroll) { if (event->motion.is_hint) { gint x, y; @@ -1805,13 +1870,11 @@ Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item 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); + bool handled = false; + if (_drag) { + handled = _drag->motion_handler (event, from_autoscroll); + } - switch (mouse_mode) { case MouseAudition: if (_scrubbing) { @@ -1823,3401 +1886,291 @@ Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item break; } - - if (!from_autoscroll && drag_info.item) { - /* item != 0 is the best test i can think of for dragging. - */ - if (!drag_info.move_threshold_passed) { - - bool x_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL); - bool y_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL); - - drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed); - - // and change the initial grab loc/frame if this drag info wants us to - - if (drag_info.want_move_threshold && drag_info.move_threshold_passed) { - drag_info.grab_frame = drag_info.current_pointer_frame; - drag_info.grab_x = drag_info.current_pointer_x; - drag_info.grab_y = drag_info.current_pointer_y; - drag_info.last_pointer_frame = drag_info.grab_frame; - drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position; - } - } - } - - switch (item_type) { - case PlayheadCursorItem: - case MarkerItem: - case ControlPointItem: - case MarkerBarItem: - case TempoBarItem: - case MeterBarItem: - case RangeMarkerBarItem: - case TransportMarkerBarItem: - case CdMarkerBarItem: - case TempoMarkerItem: - case MeterMarkerItem: - case RegionViewNameHighlight: - case StartSelectionTrimItem: - case EndSelectionTrimItem: - case SelectionItem: - case GainLineItem: - case AutomationLineItem: - case FadeInHandleItem: - case FadeOutHandleItem: - -#ifdef WITH_CMT - case ImageFrameHandleStartItem: - case ImageFrameHandleEndItem: - case MarkerViewHandleStartItem: - case MarkerViewHandleEndItem: -#endif - - if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK || - (event->motion.state & Gdk::BUTTON2_MASK))) { - if (!from_autoscroll) { - maybe_autoscroll_horizontally (&event->motion); - } - if (drag_info.motion_callback) { - (this->*(drag_info.motion_callback)) (item, event); - } - goto handled; - } - goto not_handled; - break; - default: - break; - } - - switch (mouse_mode) { - case MouseGain: - if (item_type == RegionItem) { - if (drag_info.item && drag_info.motion_callback) { - (this->*(drag_info.motion_callback)) (item, event); - } - goto handled; - } - break; - - case MouseObject: - case MouseRange: - case MouseZoom: - case MouseTimeFX: - case MouseNote: - if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK || - (event->motion.state & GDK_BUTTON2_MASK))) { - if (!from_autoscroll) { - maybe_autoscroll (&event->motion); - } - if (drag_info.motion_callback) { - (this->*(drag_info.motion_callback)) (item, event); - } - goto handled; - } - goto not_handled; - break; - - default: - break; + if (!handled) { + return false; } - handled: track_canvas_motion (event); - // drag_info.last_pointer_frame = drag_info.current_pointer_frame; return true; - - not_handled: - return false; } void -Editor::break_drag () +Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event) { - stop_canvas_autoscroll (); - hide_verbose_canvas_cursor (); - - if (drag_info.item) { - drag_info.item->ungrab (0); + ControlPoint* control_point; - /* put it back where it came from */ + 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*/ + } - 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); + // We shouldn't remove the first or last gain point + if (control_point->line().is_last_point(*control_point) || + control_point->line().is_first_point(*control_point)) { + return; } - finalize_drag (); + control_point->line().remove_point (*control_point); } void -Editor::finalize_drag () +Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event) { - 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 (); -} + ControlPoint* control_point; -void -Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor) -{ - if (drag_info.item == 0) { - fatal << _("programming error: start_grab called without drag item") << endmsg; + 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*/ - return; - } - - if (cursor == 0) { - cursor = which_grabber_cursor (); - } - - // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained - - if (Keyboard::is_button2_event (&event->button)) { - 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.last_pointer_frame = drag_info.grab_frame; - drag_info.current_pointer_frame = drag_info.grab_frame; - drag_info.current_pointer_x = drag_info.grab_x; - drag_info.current_pointer_y = drag_info.grab_y; - drag_info.last_pointer_x = drag_info.current_pointer_x; - drag_info.last_pointer_y = drag_info.current_pointer_y; - drag_info.cumulative_x_drag = 0; - drag_info.cumulative_y_drag = 0; - drag_info.first_move = true; - drag_info.move_threshold_passed = false; - drag_info.want_move_threshold = false; - drag_info.pointer_frame_offset = 0; - drag_info.brushing = false; - drag_info.clear_copied_locations (); - - 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()) { - drag_info.was_rolling = true; - } else { - drag_info.was_rolling = false; } - switch (snap_type) { - case SnapToRegionStart: - case SnapToRegionEnd: - case SnapToRegionSync: - case SnapToRegionBoundary: - build_region_boundary_cache (); - break; - default: - break; - } + control_point->line().remove_point (*control_point); } void -Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time) +Editor::edit_control_point (ArdourCanvas::Item* item) { - drag_info.item->ungrab (0); - drag_info.item = new_item; + ControlPoint* p = reinterpret_cast (item->get_data ("control_point")); - if (cursor == 0) { - cursor = which_grabber_cursor (); + if (p == 0) { + fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg; + /*NOTREACHED*/ } - drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time); -} - -/** @param item Canvas item - * @param event GDK event, or 0. - */ -bool -Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event) -{ - bool did_drag = false; - - stop_canvas_autoscroll (); - - if (drag_info.item == 0) { - return false; - } - - drag_info.item->ungrab (event ? event->button.time : 0); + ControlPointDialog d (p); + d.set_position (Gtk::WIN_POS_MOUSE); + ensure_float (d); - if (drag_info.finished_callback && event) { - 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); + if (d.run () != RESPONSE_ACCEPT) { + return; } - did_drag = !drag_info.first_move; - - hide_verbose_canvas_cursor(); - - finalize_drag (); - - return did_drag; + p->line().modify_point_y (*p, d.get_y_fraction ()); } + void -Editor::region_gain_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) +Editor::visible_order_range (int* low, int* high) const { - if (drag_info.first_move && drag_info.move_threshold_passed) { - drag_info.first_move = false; + *low = TimeAxisView::max_order (); + *high = 0; + + for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) { + + RouteTimeAxisView* rtv = dynamic_cast (*i); + + if (!rtv->hidden()) { + + if (*high < rtv->order()) { + *high = rtv->order (); + } + + if (*low > rtv->order()) { + *low = rtv->order (); + } + } } } void -Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event) +Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event) { - drag_info.item = item; - drag_info.motion_callback = &Editor::fade_in_drag_motion_callback; - drag_info.finished_callback = &Editor::fade_in_drag_finished_callback; - - start_grab (event); - - if ((drag_info.data = (item->get_data ("regionview"))) == 0) { - fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg; - /*NOTREACHED*/ - } + /* Either add to or set the set the region selection, unless + this is an alignment click (control used) + */ + + if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) { + TimeAxisView* tv = &rv.get_time_axis_view(); + RouteTimeAxisView* rtv = dynamic_cast(tv); + double speed = 1.0; + if (rtv && rtv->is_track()) { + speed = rtv->get_diskstream()->speed(); + } - AudioRegionView* arv = static_cast(drag_info.data); + nframes64_t where = get_preferred_edit_position(); + if (where >= 0) { - drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes64_t) arv->audio_region()->fade_in()->back()->when + arv->region()->position()); + 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::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) +Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos) { - AudioRegionView* arv = static_cast(drag_info.data); - nframes64_t pos; - nframes64_t fade_length; + char buf[128]; + SMPTE::Time smpte; + BBT_Time bbt; + int hours, mins; + nframes64_t frame_rate; + float secs; - if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { - pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset; - } - else { - pos = 0; + if (session == 0) { + return; } - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { - snap_to (pos); - } + AudioClock::Mode m; - 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(); + if (Profile->get_sae() || Profile->get_small_screen()) { + m = ARDOUR_UI::instance()->primary_clock.mode(); } else { - fade_length = pos - arv->region()->position(); - } - /* mapover the region selection */ - - for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { + m = ARDOUR_UI::instance()->secondary_clock.mode(); + } - AudioRegionView* tmp = dynamic_cast (*i); + switch (m) { + case AudioClock::BBT: + session->bbt_time (frame, bbt); + snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks); + break; - if (!tmp) { - continue; - } - - tmp->reset_fade_in_shape_width (fade_length); - } + case AudioClock::SMPTE: + session->smpte_time (frame, smpte); + snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames); + break; + + case AudioClock::MinSec: + /* 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; - show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10); + default: + snprintf (buf, sizeof(buf), "%" PRIi64, frame); + break; + } - drag_info.first_move = false; + if (xpos >= 0 && ypos >=0) { + set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset); + } + else { + set_verbose_canvas_cursor (buf, _drag->current_pointer_x() + offset - horizontal_adjustment.get_value(), _drag->current_pointer_y() + offset - vertical_adjustment.get_value() + canvas_timebars_vsize); + } + show_verbose_canvas_cursor (); } void -Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event) +Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos) { - AudioRegionView* arv = static_cast(drag_info.data); - nframes64_t pos; - nframes64_t fade_length; - - if (drag_info.first_move) return; + char buf[128]; + 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)); - if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { - pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset; - } else { - pos = 0; + if (session == 0) { + return; } - 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(); + AudioClock::Mode m; + + if (Profile->get_sae() || Profile->get_small_screen()) { + m = ARDOUR_UI::instance()->primary_clock.mode (); } else { - fade_length = pos - arv->region()->position(); + m = ARDOUR_UI::instance()->secondary_clock.mode (); } - - begin_reversible_command (_("change fade in length")); - for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { - - AudioRegionView* tmp = dynamic_cast (*i); - - if (!tmp) { - continue; - } - - boost::shared_ptr alist = tmp->audio_region()->fade_in(); - XMLNode &before = alist->get_state(); - - tmp->audio_region()->set_fade_in_length (fade_length); - 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 (); -} - -void -Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event) -{ - drag_info.item = item; - drag_info.motion_callback = &Editor::fade_out_drag_motion_callback; - drag_info.finished_callback = &Editor::fade_out_drag_finished_callback; - - start_grab (event); - - if ((drag_info.data = (item->get_data ("regionview"))) == 0) { - fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg; - /*NOTREACHED*/ - } - - AudioRegionView* arv = static_cast(drag_info.data); - - drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes64_t) arv->audio_region()->fade_out()->back()->when + arv->region()->position()); -} - -void -Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) -{ - AudioRegionView* arv = static_cast(drag_info.data); - nframes64_t pos; - nframes64_t fade_length; - - if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { - pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset; - } else { - pos = 0; - } - - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { - snap_to (pos); - } - - if (pos > (arv->region()->last_frame() - 64)) { - fade_length = 64; // this should really be a minimum fade defined somewhere - } - else if (pos < arv->region()->position()) { - fade_length = arv->region()->length(); - } - else { - fade_length = arv->region()->last_frame() - pos; - } - - /* mapover the region selection */ - - for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { - - AudioRegionView* tmp = dynamic_cast (*i); - - if (!tmp) { - continue; - } - - tmp->reset_fade_out_shape_width (fade_length); - } - - show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10); - - drag_info.first_move = false; -} - -void -Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event) -{ - if (drag_info.first_move) return; - - AudioRegionView* arv = static_cast(drag_info.data); - nframes64_t pos; - nframes64_t fade_length; - - if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { - pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset; - } - else { - pos = 0; - } - - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { - snap_to (pos); - } - - if (pos > (arv->region()->last_frame() - 64)) { - fade_length = 64; // this should really be a minimum fade defined somewhere - } - else if (pos < arv->region()->position()) { - fade_length = arv->region()->length(); - } - else { - fade_length = arv->region()->last_frame() - pos; - } - - begin_reversible_command (_("change fade out length")); - - for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { - - AudioRegionView* tmp = dynamic_cast (*i); - - if (!tmp) { - continue; - } - - boost::shared_ptr alist = tmp->audio_region()->fade_out(); - XMLNode &before = alist->get_state(); - - tmp->audio_region()->set_fade_out_length (fade_length); - 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 (ArdourCanvas::Item* item, GdkEvent* event) -{ - drag_info.item = item; - drag_info.motion_callback = &Editor::cursor_drag_motion_callback; - drag_info.finished_callback = &Editor::cursor_drag_finished_callback; - - start_grab (event); - - if ((drag_info.data = (item->get_data ("cursor"))) == 0) { - fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg; - /*NOTREACHED*/ - } - - Cursor* cursor = (Cursor *) drag_info.data; - - if (cursor == playhead_cursor) { - _dragging_playhead = true; - - if (session && drag_info.was_rolling) { - session->request_stop (); - } - - if (session && session->is_auditioning()) { - session->cancel_audition (); - } - } - - drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame; - - show_verbose_time_cursor (cursor->current_frame, 10); -} - -void -Editor::start_cursor_grab_no_stop (ArdourCanvas::Item* item, GdkEvent* event) -{ - drag_info.item = item; - drag_info.motion_callback = &Editor::cursor_drag_motion_callback; - drag_info.finished_callback = &Editor::cursor_drag_finished_ensure_locate_callback; - - start_grab (event); - - if ((drag_info.data = (item->get_data ("cursor"))) == 0) { - fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg; - /*NOTREACHED*/ - } - - Cursor* cursor = (Cursor *) drag_info.data; - nframes64_t where = event_frame (event, 0, 0); - - snap_to(where); - playhead_cursor->set_position (where); - - if (cursor == playhead_cursor) { - _dragging_playhead = true; - - if (session && session->is_auditioning()) { - session->cancel_audition (); - } - } - - drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame; - - show_verbose_time_cursor (cursor->current_frame, 10); -} - -void -Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) -{ - Cursor* cursor = (Cursor *) drag_info.data; - nframes64_t adjusted_frame; - - if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { - adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset; - } - else { - adjusted_frame = 0; - } - - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { - if (cursor == playhead_cursor) { - snap_to (adjusted_frame); - } - } - - if (adjusted_frame == drag_info.last_pointer_frame) return; - - cursor->set_position (adjusted_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 (ArdourCanvas::Item* item, GdkEvent* event) -{ - _dragging_playhead = false; - - if (drag_info.first_move) { - return; - } - - cursor_drag_motion_callback (item, event); - - if (item == &playhead_cursor->canvas_item) { - if (session) { - session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling); - _pending_locate_request = true; - } - } -} - -void -Editor::cursor_drag_finished_ensure_locate_callback (ArdourCanvas::Item* item, GdkEvent* event) -{ - _dragging_playhead = false; - - cursor_drag_motion_callback (item, event); - - if (item == &playhead_cursor->canvas_item) { - if (session) { - session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling); - _pending_locate_request = true; - } - } -} - -void -Editor::update_marker_drag_item (Location *location) -{ - double x1 = frame_to_pixel (location->start()); - double x2 = frame_to_pixel (location->end()); - - if (location->is_mark()) { - marker_drag_line_points.front().set_x(x1); - marker_drag_line_points.back().set_x(x1); - marker_drag_line->property_points() = marker_drag_line_points; - } else { - range_marker_drag_rect->property_x1() = x1; - range_marker_drag_rect->property_x2() = x2; - } -} - - -void -Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event) -{ - Marker* marker; - - if ((marker = static_cast (item->get_data ("marker"))) == 0) { - fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg; - /*NOTREACHED*/ - } - - bool is_start; - - Location *location = find_location_from_marker (marker, is_start); - - drag_info.item = item; - drag_info.data = marker; - drag_info.motion_callback = &Editor::marker_drag_motion_callback; - drag_info.finished_callback = &Editor::marker_drag_finished_callback; - - start_grab (event); - - _dragging_edit_point = true; - - drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end()); - - update_marker_drag_item (location); - - if (location->is_mark()) { - // marker_drag_line->show(); - // marker_drag_line->raise_to_top(); - } else { - range_marker_drag_rect->show(); - //range_marker_drag_rect->raise_to_top(); - } - - if (is_start) { - show_verbose_time_cursor (location->start(), 10); - } else { - show_verbose_time_cursor (location->end(), 10); - } - - Selection::Operation op = Keyboard::selection_type (event->button.state); - - switch (op) { - case Selection::Toggle: - selection->toggle (marker); - break; - case Selection::Set: - 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)); - } -} - -void -Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) -{ - 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 = 0; - - if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) { - newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset; - } else { - newframe = 0; - } - - 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 (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) { - move_both = true; - } - - MarkerSelection::iterator i; - list::iterator x; - - /* 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 (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) { - - 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; - } - - if (copy_location->is_mark()) { - - /* 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; - - 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; - - if (drag_info.copied_locations.empty()) { - abort(); - } - - 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 (ArdourCanvas::Item* item, GdkEvent* event) -{ - if (drag_info.first_move) { - - /* 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; - - - begin_reversible_command ( _("move marker") ); - XMLNode &before = session->locations()->get_state(); - - MarkerSelection::iterator i; - list::iterator x; - bool is_start; - - 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 (); - - marker_drag_line->hide(); - range_marker_drag_rect->hide(); -} - -void -Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event) -{ - Marker* marker; - MeterMarker* meter_marker; - - if ((marker = reinterpret_cast (item->get_data ("marker"))) == 0) { - fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg; - /*NOTREACHED*/ - } - - meter_marker = dynamic_cast (marker); - - MetricSection& section (meter_marker->meter()); - - if (!section.movable()) { - return; - } - - drag_info.item = item; - drag_info.copy = false; - drag_info.data = marker; - drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback; - drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback; - - start_grab (event); - - drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame(); - - show_verbose_time_cursor (drag_info.current_pointer_frame, 10); -} - -void -Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event) -{ - Marker* marker; - MeterMarker* meter_marker; - - if ((marker = reinterpret_cast (item->get_data ("marker"))) == 0) { - fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg; - /*NOTREACHED*/ - } - - meter_marker = dynamic_cast (marker); - - // create a dummy marker for visual representation of moving the copy. - // The actual copying is not done before we reach the finish callback. - char name[64]; - snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ()); - MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name, - *new MeterSection(meter_marker->meter())); - - drag_info.item = &new_marker->the_item(); - drag_info.copy = true; - drag_info.data = new_marker; - drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback; - drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback; - - start_grab (event); - - drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame(); - - show_verbose_time_cursor (drag_info.current_pointer_frame, 10); -} - -void -Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) -{ - MeterMarker* marker = (MeterMarker *) drag_info.data; - nframes64_t adjusted_frame; - - if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { - adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset; - } - else { - adjusted_frame = 0; - } - - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { - snap_to (adjusted_frame); - } - - if (adjusted_frame == drag_info.last_pointer_frame) return; - - marker->set_position (adjusted_frame); - - - drag_info.last_pointer_frame = adjusted_frame; - drag_info.first_move = false; - - show_verbose_time_cursor (adjusted_frame, 10); -} - -void -Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event) -{ - if (drag_info.first_move) return; - - meter_marker_drag_motion_callback (drag_info.item, event); - - MeterMarker* marker = (MeterMarker *) drag_info.data; - BBT_Time when; - - TempoMap& map (session->tempo_map()); - map.bbt_time (drag_info.last_pointer_frame, when); - - if (drag_info.copy == true) { - begin_reversible_command (_("copy meter mark")); - XMLNode &before = map.get_state(); - map.add_meter (marker->meter(), when); - XMLNode &after = map.get_state(); - session->add_command(new MementoCommand(map, &before, &after)); - commit_reversible_command (); - - // delete the dummy marker we used for visual representation of copying. - // a new visual marker will show up automatically. - delete marker; - } else { - begin_reversible_command (_("move meter mark")); - XMLNode &before = map.get_state(); - map.move_meter (marker->meter(), when); - XMLNode &after = map.get_state(); - session->add_command(new MementoCommand(map, &before, &after)); - commit_reversible_command (); - } -} - -void -Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event) -{ - Marker* marker; - TempoMarker* tempo_marker; - - if ((marker = reinterpret_cast (item->get_data ("marker"))) == 0) { - fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg; - /*NOTREACHED*/ - } - - if ((tempo_marker = dynamic_cast (marker)) == 0) { - fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg; - /*NOTREACHED*/ - } - - MetricSection& section (tempo_marker->tempo()); - - if (!section.movable()) { - return; - } - - drag_info.item = item; - drag_info.copy = false; - drag_info.data = marker; - drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback; - drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback; - - start_grab (event); - - drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame(); - show_verbose_time_cursor (drag_info.current_pointer_frame, 10); -} - -void -Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event) -{ - Marker* marker; - TempoMarker* tempo_marker; - - if ((marker = reinterpret_cast (item->get_data ("marker"))) == 0) { - fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg; - /*NOTREACHED*/ - } - - if ((tempo_marker = dynamic_cast (marker)) == 0) { - fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg; - /*NOTREACHED*/ - } - - // create a dummy marker for visual representation of moving the copy. - // The actual copying is not done before we reach the finish callback. - char name[64]; - snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute()); - TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name, - *new TempoSection(tempo_marker->tempo())); - - drag_info.item = &new_marker->the_item(); - drag_info.copy = true; - drag_info.data = new_marker; - drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback; - drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback; - - start_grab (event); - - drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame(); - - show_verbose_time_cursor (drag_info.current_pointer_frame, 10); -} - -void -Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) -{ - TempoMarker* marker = (TempoMarker *) drag_info.data; - nframes64_t adjusted_frame; - - if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { - adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset; - } - else { - adjusted_frame = 0; - } - - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { - snap_to (adjusted_frame); - } - - if (adjusted_frame == drag_info.last_pointer_frame) return; - - /* OK, we've moved far enough to make it worth actually move the thing. */ - - marker->set_position (adjusted_frame); - - show_verbose_time_cursor (adjusted_frame, 10); - - drag_info.last_pointer_frame = adjusted_frame; - drag_info.first_move = false; -} - -void -Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event) -{ - if (drag_info.first_move) return; - - tempo_marker_drag_motion_callback (drag_info.item, event); - - TempoMarker* marker = (TempoMarker *) drag_info.data; - BBT_Time when; - - TempoMap& map (session->tempo_map()); - map.bbt_time (drag_info.last_pointer_frame, when); - - if (drag_info.copy == true) { - begin_reversible_command (_("copy tempo mark")); - XMLNode &before = map.get_state(); - map.add_tempo (marker->tempo(), when); - XMLNode &after = map.get_state(); - session->add_command (new MementoCommand(map, &before, &after)); - commit_reversible_command (); - - // delete the dummy marker we used for visual representation of copying. - // a new visual marker will show up automatically. - delete marker; - } else { - begin_reversible_command (_("move tempo mark")); - XMLNode &before = map.get_state(); - map.move_tempo (marker->tempo(), when); - XMLNode &after = map.get_state(); - session->add_command (new MementoCommand(map, &before, &after)); - commit_reversible_command (); - } -} - -void -Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event) -{ - ControlPoint* control_point; - - if ((control_point = reinterpret_cast (item->get_data ("control_point"))) == 0) { - fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg; - /*NOTREACHED*/ - } - - // We shouldn't remove the first or last gain point - if (control_point->line().is_last_point(*control_point) || - control_point->line().is_first_point(*control_point)) { - return; - } - - control_point->line().remove_point (*control_point); -} - -void -Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event) -{ - ControlPoint* control_point; - - if ((control_point = reinterpret_cast (item->get_data ("control_point"))) == 0) { - fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg; - /*NOTREACHED*/ - } - - control_point->line().remove_point (*control_point); -} - -void -Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event) -{ - ControlPoint* control_point; - - if ((control_point = reinterpret_cast (item->get_data ("control_point"))) == 0) { - fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg; - /*NOTREACHED*/ - } - - drag_info.item = item; - drag_info.data = control_point; - drag_info.motion_callback = &Editor::control_point_drag_motion_callback; - drag_info.finished_callback = &Editor::control_point_drag_finished_callback; - - start_grab (event, fader_cursor); - - // start the grab at the center of the control point so - // the point doesn't 'jump' to the mouse after the first drag - drag_info.grab_x = control_point->get_x(); - drag_info.grab_y = control_point->get_y(); - - control_point->line().parent_group().i2w(drag_info.grab_x, drag_info.grab_y); - track_canvas->w2c(drag_info.grab_x, drag_info.grab_y, drag_info.grab_x, drag_info.grab_y); - - drag_info.grab_frame = pixel_to_frame(drag_info.grab_x); - - control_point->line().start_drag (control_point, drag_info.grab_frame, 0); - - 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 (ArdourCanvas::Item* item, GdkEvent* event) -{ - ControlPoint* cp = reinterpret_cast (drag_info.data); - - double dx = drag_info.current_pointer_x - drag_info.last_pointer_x; - double dy = drag_info.current_pointer_y - drag_info.last_pointer_y; - - if (event->button.state & Keyboard::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; - - // calculate zero crossing point. back off by .01 to stay on the - // positive side of zero - double _unused = 0; - double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line().height() - .01; - cp->line().parent_group().i2w(_unused, zero_gain_y); - - // make sure we hit zero when passing through - if ((cy < zero_gain_y and (cy - dy) > zero_gain_y) - or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) { - cy = zero_gain_y; - } - - if (drag_info.x_constrained) { - cx = drag_info.grab_x; - } - if (drag_info.y_constrained) { - cy = drag_info.grab_y; - } - - drag_info.cumulative_x_drag = cx - drag_info.grab_x; - drag_info.cumulative_y_drag = cy - drag_info.grab_y; - - cp->line().parent_group().w2i (cx, cy); - - cx = max (0.0, cx); - cy = max (0.0, cy); - cy = min ((double) cp->line().height(), cy); - - //translate cx to frames - nframes64_t cx_frames = unit_to_frame (cx); - - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) { - snap_to (cx_frames); - } - - float fraction = 1.0 - (cy / cp->line().height()); - - bool push; - - if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) { - push = true; - } else { - push = false; - } - - cp->line().point_drag (*cp, cx_frames , fraction, push); - - set_verbose_canvas_cursor_text (cp->line().get_verbose_cursor_string (fraction)); - - drag_info.first_move = false; -} - -void -Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event) -{ - ControlPoint* cp = reinterpret_cast (drag_info.data); - - if (drag_info.first_move) { - - /* just a click */ - - if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) { - reset_point_selection (); - } - - } else { - control_point_drag_motion_callback (item, event); - } - cp->line().end_drag (cp); -} - -void -Editor::edit_control_point (ArdourCanvas::Item* item) -{ - ControlPoint* p = reinterpret_cast (item->get_data ("control_point")); - - if (p == 0) { - fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg; - /*NOTREACHED*/ - } - - ControlPointDialog d (p); - d.set_position (Gtk::WIN_POS_MOUSE); - ensure_float (d); - - if (d.run () != RESPONSE_ACCEPT) { - return; - } - - p->line().modify_point_y (*p, d.get_y_fraction ()); -} - - -void -Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event) -{ - switch (mouse_mode) { - case MouseGain: - assert(dynamic_cast(clicked_regionview)); - start_line_grab (dynamic_cast(clicked_regionview)->get_gain_line(), event); - break; - default: - break; - } -} - -void -Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event) -{ - AutomationLine* al; - - if ((al = reinterpret_cast (item->get_data ("line"))) == 0) { - fatal << _("programming error: line canvas item has no line pointer!") << endmsg; - /*NOTREACHED*/ - } - - start_line_grab (al, event); -} - -void -Editor::start_line_grab (AutomationLine* line, GdkEvent* event) -{ - double cx; - double cy; - nframes64_t frame_within_region; - - /* need to get x coordinate in terms of parent (TimeAxisItemView) - origin, and ditto for y. - */ - - cx = event->button.x; - cy = event->button.y; - - 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)) { - /* no adjacent points */ - return; - } - - drag_info.item = &line->grab_item(); - drag_info.data = line; - drag_info.motion_callback = &Editor::line_drag_motion_callback; - drag_info.finished_callback = &Editor::line_drag_finished_callback; - - start_grab (event, fader_cursor); - - /* store grab start in parent frame */ - - drag_info.grab_x = cx; - drag_info.grab_y = cy; - - double fraction = 1.0 - (cy / line->height()); - - line->start_drag (0, drag_info.grab_frame, fraction); - - set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction), - drag_info.current_pointer_x + 10, drag_info.current_pointer_y + 10); - show_verbose_canvas_cursor (); -} - -void -Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) -{ - AutomationLine* line = reinterpret_cast (drag_info.data); - - double dy = drag_info.current_pointer_y - drag_info.last_pointer_y; - - if (event->button.state & Keyboard::SecondaryModifier) { - dy *= 0.1; - } - - double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy; - - 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::PrimaryModifier)) { - push = false; - } else { - push = true; - } - - line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push); - - set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction)); -} - -void -Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event) -{ - AutomationLine* line = reinterpret_cast (drag_info.data); - line_drag_motion_callback (item, event); - line->end_drag (0); -} - -void -Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event) -{ - if (selection->regions.empty() || clicked_regionview == 0) { - return; - } - _region_motion_group->raise_to_top (); - drag_info.copy = false; - drag_info.item = item; - drag_info.data = clicked_regionview; - - 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_axisview; - RouteTimeAxisView* tv = dynamic_cast(tvp); - - if (tv && tv->is_track()) { - speed = tv->get_diskstream()->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.source_trackview = &clicked_regionview->get_time_axis_view(); - drag_info.source_layer = clicked_regionview->region()->layer(); - drag_info.dest_trackview = drag_info.source_trackview; - drag_info.dest_layer = drag_info.source_layer; - // 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_create_region_grab (ArdourCanvas::Item* item, GdkEvent* event) -{ - 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.dest_layer = drag_info.source_layer; - drag_info.motion_callback = &Editor::create_region_drag_motion_callback; - drag_info.finished_callback = &Editor::create_region_drag_finished_callback; - - start_grab (event); -} - -void -Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event) -{ - if (selection->regions.empty() || clicked_regionview == 0) { - return; - } - _region_motion_group->raise_to_top (); - drag_info.copy = true; - drag_info.item = item; - drag_info.data = clicked_regionview; - - start_grab(event); - - TimeAxisView* tv = &clicked_regionview->get_time_axis_view(); - RouteTimeAxisView* rtv = dynamic_cast(tv); - double speed = 1.0; - - if (rtv && rtv->is_track()) { - speed = rtv->get_diskstream()->speed(); - } - - drag_info.source_trackview = &clicked_regionview->get_time_axis_view(); - drag_info.dest_trackview = drag_info.source_trackview; - drag_info.dest_layer = drag_info.source_layer; - 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); -} - -void -Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event) -{ - if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) { - return; - } - - drag_info.copy = false; - drag_info.item = item; - drag_info.data = clicked_regionview; - drag_info.motion_callback = &Editor::region_drag_motion_callback; - drag_info.finished_callback = &Editor::region_drag_finished_callback; - - start_grab (event); - - double speed = 1.0; - TimeAxisView* tvp = clicked_axisview; - RouteTimeAxisView* tv = dynamic_cast(tvp); - - if (tv && tv->is_track()) { - speed = tv->get_diskstream()->speed(); - } - - drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed); - drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position; - drag_info.source_trackview = &clicked_regionview->get_time_axis_view(); - drag_info.dest_trackview = drag_info.source_trackview; - drag_info.dest_layer = drag_info.source_layer; - // we want a move threshold - drag_info.want_move_threshold = true; - drag_info.brushing = true; - - begin_reversible_command (_("Drag region brush")); -} - -void -Editor::possibly_copy_regions_during_grab (GdkEvent* event) -{ - 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, layer_t* layer) -{ - /* Which trackview is this ? */ - - std::pair const tvp = trackview_by_y_position (drag_info.current_pointer_y); - (*tv) = dynamic_cast (tvp.first); - (*layer) = tvp.second; - - /* The region motion is only processed if the pointer is over - an audio track. - */ - - if (!(*tv) || !(*tv)->is_track()) { - /* To make sure we hide the verbose canvas cursor when the mouse is - not held over and audiotrack. - */ - hide_verbose_canvas_cursor (); - return false; - } - - return true; -} - -struct RegionSelectionByPosition { - bool operator() (RegionView*a, RegionView* b) { - return a->region()->position () < b->region()->position(); - } -}; - -void -Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) -{ - RouteTimeAxisView* tv; - layer_t layer; - - if (!check_region_drag_possible (&tv, &layer)) { - 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::visible_order_range (int* low, int* high) const -{ - *low = TimeAxisView::max_order (); - *high = 0; - - for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) { - - RouteTimeAxisView* rtv = dynamic_cast (*i); - - if (!rtv->hidden()) { - - if (*high < rtv->order()) { - *high = rtv->order (); - } - - if (*low > rtv->order()) { - *low = rtv->order (); - } - } - } -} - -/** @param new_order New track order. - * @param old_order Old track order. - * @param visible_y_low Lowest visible order. - * @param visible_y_high Highest visible order. - * @param tracks Bitset of tracks indexed by order; 0 means a audio/MIDI track, 1 means something else. - * @param heigh_list Heights of tracks indexed by order. - * @return true if y movement should not happen, otherwise false. - */ -bool -Editor::y_movement_disallowed ( - int new_order, int old_order, int y_span, int visible_y_low, int visible_y_high, - bitset<512> const & tracks, vector const & height_list - ) const -{ - if (new_order != old_order) { - - /* this isn't the pointer track */ - - if (y_span > 0) { - - /* moving up the canvas */ - if ( (new_order - y_span) >= visible_y_low) { - - int32_t n = 0; - - /* work out where we'll end up with this y span, taking hidden TimeAxisViews into account */ - int32_t visible_tracks = 0; - while (visible_tracks < y_span ) { - visible_tracks++; - while (height_list[new_order - (visible_tracks - n)] == 0) { - /* passing through a hidden track */ - n--; - } - } - - if (tracks[new_order - (y_span - n)] != 0x00) { - /* moving to a non-track; disallow */ - return true; - } - - - } else { - /* moving beyond the lowest visible track; disallow */ - return true; - } - - } else if (y_span < 0) { - - /* moving down the canvas */ - if ((new_order - y_span) <= visible_y_high) { - - int32_t visible_tracks = 0; - int32_t n = 0; - while (visible_tracks > y_span ) { - visible_tracks--; - - while (height_list[new_order - (visible_tracks - n)] == 0) { - /* passing through a hidden track */ - n++; - } - } - - if (tracks[new_order - (y_span - n)] != 0x00) { - /* moving to a non-track; disallow */ - return true; - } - - - } else { - - /* moving beyond the highest visible track; disallow */ - return true; - } - } - - } else { - - /* this is the pointer's track */ - - if ((new_order - y_span) > visible_y_high) { - /* we will overflow */ - return true; - } else if ((new_order - y_span) < visible_y_low) { - /* we will overflow */ - return true; - } - } - - return false; -} - -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_order_span = 0, canvas_pointer_order_span = 0; - int32_t pointer_layer_span = 0; - - bool clamp_y_axis = false; - vector::iterator j; - - possibly_copy_regions_during_grab (event); - - /* *pointer* variables reflect things about the pointer; as we may be moving - multiple regions, much detail must be computed per-region */ - - /* current_pointer_view will become the TimeAxisView that we're currently pointing at, and - current_pointer_layer the current layer on that TimeAxisView */ - RouteTimeAxisView* current_pointer_view; - layer_t current_pointer_layer; - if (!check_region_drag_possible (¤t_pointer_view, ¤t_pointer_layer)) { - return; - } - - /* TimeAxisView that we were pointing at last time we entered this method */ - TimeAxisView const * const last_pointer_view = drag_info.dest_trackview; - /* the order of the track that we were pointing at last time we entered this method */ - int32_t const last_pointer_order = last_pointer_view->order (); - /* the layer that we were pointing at last time we entered this method */ - layer_t const last_pointer_layer = drag_info.dest_layer; - - /************************************************************ - Y DELTA COMPUTATION - ************************************************************/ - - /* Height of TimeAxisViews, indexed by order */ - /* XXX: hard-coded limit of TimeAxisViews */ - vector height_list (512); - - if (drag_info.brushing) { - clamp_y_axis = true; - pointer_order_span = 0; - goto y_axis_done; - } - - /* the change in track order between this callback and the last */ - pointer_order_span = last_pointer_view->order() - current_pointer_view->order(); - /* the change in layer between this callback and the last; - only meaningful if pointer_order_span == 0 (ie we've not moved tracks) */ - pointer_layer_span = last_pointer_layer - current_pointer_layer; - - if (pointer_order_span != 0) { - - int32_t children = 0; - /* XXX: hard-coded limit of tracks */ - bitset <512> tracks (0x00); - - int visible_y_high; - int visible_y_low; - visible_order_range (&visible_y_low, &visible_y_high); - - /* get a bitmask representing the visible tracks */ - - for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { - RouteTimeAxisView* rtv = dynamic_cast (*i); - TimeAxisView::Children children_list; - - /* zeroes are audio/MIDI tracks. ones are other types. */ - - if (!rtv->hidden()) { - - if (!rtv->is_track()) { - /* not an audio nor MIDI track */ - tracks = tracks |= (0x01 << rtv->order()); - } - - height_list[rtv->order()] = (*i)->current_height(); - children = 1; - - if ((children_list = rtv->get_child_list()).size() > 0) { - for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) { - tracks = tracks |= (0x01 << (rtv->order() + children)); - height_list[rtv->order() + children] = (*j)->current_height(); - children++; - } - } - } - } - - /* find the actual pointer span, in terms of the number of visible tracks; - to do this, we reduce |pointer_order_span| by the number of hidden tracks - over the span */ - - canvas_pointer_order_span = pointer_order_span; - if (last_pointer_view->order() >= current_pointer_view->order()) { - for (int32_t y = current_pointer_view->order(); y < last_pointer_view->order(); y++) { - if (height_list[y] == 0) { - canvas_pointer_order_span--; - } - } - } else { - for (int32_t y = last_pointer_view->order(); y <= current_pointer_view->order(); y++) { - if (height_list[y] == 0) { - canvas_pointer_order_span++; - } - } - } - - for (list::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) { - - RegionView* rv = (*i); - - if (rv->region()->locked()) { - continue; - } - - 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; - - /* get the new trackview for this particular region */ - std::pair const tvp = trackview_by_y_position (iy1); - assert (tvp.first); - RouteTimeAxisView* rtv = dynamic_cast (tvp.first); - - /* I know this method has a slightly excessive argument list, but I think - it's nice to separate the code out all the same, since it has such a - simple result, and it makes it clear that there are no other - side-effects. - */ - - /* XXX: not sure that we should be passing canvas_pointer_order_span in here, - as surely this is a per-region thing... */ - - clamp_y_axis = y_movement_disallowed ( - rtv->order(), last_pointer_order, canvas_pointer_order_span, visible_y_low, visible_y_high, - tracks, height_list - ); - - if (clamp_y_axis) { - break; - } - } - - } else if (drag_info.dest_trackview == current_pointer_view) { - - if (current_pointer_layer == last_pointer_layer) { - /* No movement; clamp */ - clamp_y_axis = true; - } - } - - y_axis_done: - if (!clamp_y_axis) { - drag_info.dest_trackview = current_pointer_view; - drag_info.dest_layer = current_pointer_layer; - } - - /************************************************************ - 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.current_pointer_frame >= drag_info.pointer_frame_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 = 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); - } - - pending_region_position = clicked_regionview->region()->adjust_to_sync (sync_frame); - - } else { - pending_region_position = drag_info.last_frame_position; - } - - } else { - pending_region_position = 0; - } - - if (pending_region_position > max_frames - clicked_regionview->region()->length()) { - pending_region_position = drag_info.last_frame_position; - } - - 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* rv = (*i); - - // If any regionview is at zero, we need to know so we can stop further leftward motion. - - double ix1, ix2, iy1, iy2; - rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2); - rv->get_canvas_frame()->i2w (ix1, iy1); - - if (-x_delta > ix1 + horizontal_adjustment.get_value()) { - x_delta = 0; - pending_region_position = drag_info.last_frame_position; - break; - } - } - - } - - drag_info.last_frame_position = pending_region_position; - - } else { - x_delta = 0; - } - - } else { - /* threshold not passed */ - - x_delta = 0; - } - - /************************************************************* - PREPARE TO MOVE - ************************************************************/ - - if (x_delta == 0 && pointer_order_span == 0 && pointer_layer_span == 0) { - /* haven't reached next snap point, and we're not switching - trackviews nor layers. nothing to do. - */ - return; - } - - /************************************************************* - MOTION - ************************************************************/ - bool do_move = true; - if (drag_info.first_move) { - if (!drag_info.move_threshold_passed) { - do_move = false; - } - } - - if (do_move) { - - pair >::iterator,bool> insert_result; - const list& layered_regions = selection->regions.by_layer(); - - for (list::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) { - - RegionView* rv = (*i); - - if (rv->region()->locked()) { - continue; - } - - /* here we are calculating the y distance from the - top of the first track view to the top of the region - area of the track view that we're working on */ - - /* this x value is just a dummy value so that we have something - to pass to i2w () */ - - double ix1 = 0; - - /* distance from the top of this track view to the region area - of our track view is always 1 */ - - double iy1 = 1; - - /* convert to world coordinates, ie distance from the top of - the ruler section */ - - rv->get_canvas_frame()->i2w (ix1, iy1); - - /* compensate for the ruler section and the vertical scrollbar position */ - iy1 += get_trackview_group_vertical_offset (); - - if (drag_info.first_move) { - - // hide any dependent views - - rv->get_time_axis_view().hide_dependent_views (*rv); - - /* - 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_canvas_group()->property_y() = iy1 - 1; - rv->get_canvas_group()->reparent(*_region_motion_group); - - rv->fake_set_opaque (true); - } - - /* current view for this particular region */ - std::pair pos = trackview_by_y_position (iy1); - RouteTimeAxisView* rtv = dynamic_cast (pos.first); - - if (pointer_order_span != 0 && !clamp_y_axis) { - - /* INTER-TRACK MOVEMENT */ - - /* move through the height list to the track that the region is currently on */ - vector::iterator j = height_list.begin (); - int32_t x = 0; - while (j != height_list.end () && x != rtv->order ()) { - ++x; - ++j; - } - - y_delta = 0; - int32_t temp_pointer_order_span = canvas_pointer_order_span; - - if (j != height_list.end ()) { - - /* Account for layers in the original and - destination tracks. If we're moving around in layers we assume - that only one track is involved, so it's ok to use *pointer* - variables here. */ - - StreamView* lv = last_pointer_view->view (); - assert (lv); - - /* move to the top of the last trackview */ - if (lv->layer_display () == Stacked) { - y_delta -= (lv->layers() - last_pointer_layer - 1) * lv->child_height (); - } - - StreamView* cv = current_pointer_view->view (); - assert (cv); - - /* move to the right layer on the current trackview */ - if (cv->layer_display () == Stacked) { - y_delta += (cv->layers() - current_pointer_layer - 1) * cv->child_height (); - } - - /* And for being on a non-topmost layer on the new - track */ - - while (temp_pointer_order_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 != last_pointer_order) { - if ((*j) == 0) { - ++temp_pointer_order_span; - } - } - - y_delta -= (*j); - temp_pointer_order_span--; - } - - while (temp_pointer_order_span < 0) { - - y_delta += (*j); - - if (x != last_pointer_order) { - if ((*j) == 0) { - --temp_pointer_order_span; - } - } - - if (j != height_list.end()) { - j++; - } - - temp_pointer_order_span++; - } - - - /* find out where we'll be when we move and set height accordingly */ - - std::pair const pos = trackview_by_y_position (iy1 + y_delta); - RouteTimeAxisView const * temp_rtv = dynamic_cast (pos.first); - rv->set_height (temp_rtv->view()->child_height()); - - /* if you un-comment the following, the region colours will follow - the track colours whilst dragging; personally - i think this can confuse things, but never mind. - */ - - //const GdkColor& col (temp_rtv->view->get_region_color()); - //rv->set_color (const_cast(col)); - } - } - - if (pointer_order_span == 0 && pointer_layer_span != 0 && !clamp_y_axis) { - - /* INTER-LAYER MOVEMENT in the same track */ - y_delta = rtv->view()->child_height () * pointer_layer_span; - } - - - 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 (ArdourCanvas::Item* item, GdkEvent* event) -{ - bool nocommit = true; - 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; - std::pair tvp; - std::map final; - - /* first_move is set to false if the regionview has been moved in the - motion handler. - */ - - if (drag_info.first_move) { - /* just a click */ - goto out; - } - - nocommit = false; - - 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; - } - - char* op_string; - - /* reverse this here so that we have the correct logic to finalize - the drag. - */ - - if (Config->get_edit_mode() == Lock && !drag_info.copy) { - drag_info.x_constrained = !drag_info.x_constrained; - } - - 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"); - } - } - - begin_reversible_command (op_string); - changed_position = (drag_info.last_frame_position != (nframes64_t) (clicked_regionview->region()->position())); - tvp = trackview_by_y_position (drag_info.current_pointer_y); - changed_tracks = (tvp.first != &clicked_regionview->get_time_axis_view()); - - drag_delta = clicked_regionview->region()->position() - drag_info.last_frame_position; - - track_canvas->update_now (); - - /* make a list of where each region ended up */ - for (list::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) { - - double ix1, ix2, iy1, iy2; - (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2); - (*i)->get_canvas_frame()->i2w (ix1, iy1); - iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize; - - std::pair tv = trackview_by_y_position (iy1); - final[*i] = dynamic_cast (tv.first); - } - - for (list::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) { - - RegionView* rv = (*i); - RouteTimeAxisView* dest_rtv = final[*i]; - - 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; - - 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)); - - 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. - */ - - 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 = modified_playlists.insert (playlist); - - if (insert_result.second) { - 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); - } - - if (changed_tracks && !drag_info.copy) { - - /* get the playlist where this drag started. we can't use rv->region()->playlist() - because we may have copied the region and it has not been attached to a playlist. - */ - - assert ((source_tv = dynamic_cast (&rv->get_time_axis_view()))); - assert ((ds = source_tv->get_diskstream())); - assert ((from_playlist = ds->playlist())); - - /* moved to a different audio track, without copying */ - - /* the region that used to be in the old playlist is not - moved to the new one - we use a copy of it. as a result, - any existing editor for the region should no longer be - visible. - */ - - rv->hide_region_editor(); - rv->fake_set_opaque (false); - - /* remove the region from the old playlist */ - - insert_result = modified_playlists.insert (from_playlist); - - if (insert_result.second) { - session->add_command (new MementoCommand(*from_playlist, &from_playlist->get_state(), 0)); - } - - 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). - - this could have invalidated any and all iterators into the region selection. - - the heuristic we use here is: if the region selection is empty, break out of the loop - here. if the region selection is not empty, then restart the loop because we know that - we must have removed at least the region(view) we've just been working on as well as any - that we processed on previous iterations. - - EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and - we can just iterate. - */ - - if (selection->regions.empty()) { - break; - } else { - i = selection->regions.by_layer().begin(); - } - - } 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); - } - - for (set >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) { - (*p)->thaw(); - } - - 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 (); - } - - for (vector::iterator x = copies.begin(); x != copies.end(); ++x) { - delete *x; - } -} - -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; - } - - // 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 - } -} - -void -Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event) -{ - /* Either add to or set the set the region selection, unless - this is an alignment click (control used) - */ - - if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) { - TimeAxisView* tv = &rv.get_time_axis_view(); - RouteTimeAxisView* rtv = dynamic_cast(tv); - double speed = 1.0; - if (rtv && rtv->is_track()) { - speed = rtv->get_diskstream()->speed(); - } - - nframes64_t where = get_preferred_edit_position(); - - if (where >= 0) { - - 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 (nframes64_t frame, double offset, double xpos, double ypos) -{ - char buf[128]; - SMPTE::Time smpte; - BBT_Time bbt; - int hours, mins; - nframes64_t frame_rate; - float secs; - - if (session == 0) { - return; - } - - AudioClock::Mode m; - - if (Profile->get_sae() || Profile->get_small_screen()) { - m = ARDOUR_UI::instance()->primary_clock.mode(); - } else { - m = ARDOUR_UI::instance()->secondary_clock.mode(); - } - - switch (m) { - case AudioClock::BBT: - session->bbt_time (frame, bbt); - snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks); - break; - - case AudioClock::SMPTE: - session->smpte_time (frame, smpte); - snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames); - break; - - case AudioClock::MinSec: - /* 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), "%" PRIi64, frame); - break; - } - - if (xpos >= 0 && ypos >=0) { - set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset); - } - else { - set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset - 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 (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos) -{ - char buf[128]; - 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)); - - if (session == 0) { - return; - } - - 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); - - /* subtract */ - /* XXX this computation won't work well if the - user makes a selection that spans any meter changes. - */ - - ebbt.bars -= sbbt.bars; - if (ebbt.beats >= sbbt.beats) { - ebbt.beats -= sbbt.beats; - } else { - ebbt.bars--; - ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats; - } - if (ebbt.ticks >= sbbt.ticks) { - ebbt.ticks -= sbbt.ticks; - } else { - ebbt.beats--; - ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks; - } - - snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks); - break; - - case AudioClock::SMPTE: - session->smpte_duration (end - start, smpte); - snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames); - break; - - case AudioClock::MinSec: - /* 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), "%" PRIi64, end - start); - break; - } - - if (xpos >= 0 && ypos >=0) { - set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset); - } - else { - set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset); - } - - show_verbose_canvas_cursor (); -} - -void -Editor::collect_new_region_view (RegionView* rv) -{ - latest_regionviews.push_back (rv); -} - -void -Editor::collect_and_select_new_region_view (RegionView* rv) -{ - selection->add(rv); - latest_regionviews.push_back (rv); -} - -void -Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event) -{ - if (clicked_regionview == 0) { - return; - } - - /* lets try to create new Region for the selection */ - - vector > new_regions; - create_region_from_selection (new_regions); - - if (new_regions.empty()) { - return; - } - - /* XXX fix me one day to use all new regions */ - - boost::shared_ptr region (new_regions.front()); - - /* add it to the current stream/playlist. - - tricky: the streamview for the track will add a new regionview. we will - catch the signal it sends when it creates the regionview to - set the regionview we want to then drag. - */ - - latest_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. - */ - - begin_reversible_command (_("selection grab")); - - boost::shared_ptr playlist = clicked_axisview->playlist(); - - XMLNode *before = &(playlist->get_state()); - clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start); - XMLNode *after = &(playlist->get_state()); - session->add_command(new MementoCommand(*playlist, before, after)); - - commit_reversible_command (); - - c.disconnect (); - - if (latest_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_regionviews); - - 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.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); -} - -void -Editor::cancel_selection () -{ - for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { - (*i)->hide_selection (); - } - - selection->clear (); - clicked_selection = 0; -} - -void -Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op) -{ - nframes64_t start = 0; - nframes64_t end = 0; - - if (session == 0) { - return; - } - - drag_info.item = item; - drag_info.motion_callback = &Editor::drag_selection; - drag_info.finished_callback = &Editor::end_selection_op; - - selection_op = op; - - switch (op) { - case CreateSelection: - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) { - drag_info.copy = true; - } else { - drag_info.copy = false; - } - start_grab (event, selector_cursor); - break; - - case SelectionStartTrim: - if (clicked_axisview) { - clicked_axisview->order_selection_trims (item, true); - } - start_grab (event, trimmer_cursor); - start = selection->time[clicked_selection].start; - drag_info.pointer_frame_offset = drag_info.grab_frame - start; - break; - - case SelectionEndTrim: - if (clicked_axisview) { - clicked_axisview->order_selection_trims (item, false); - } - start_grab (event, trimmer_cursor); - end = selection->time[clicked_selection].end; - drag_info.pointer_frame_offset = drag_info.grab_frame - end; - break; - - case SelectionMove: - start = selection->time[clicked_selection].start; - start_grab (event); - drag_info.pointer_frame_offset = drag_info.grab_frame - start; - break; - } - - if (selection_op == SelectionMove) { - show_verbose_time_cursor(start, 10); - } else { - show_verbose_time_cursor(drag_info.current_pointer_frame, 10); - } -} - -void -Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event) -{ - nframes64_t start = 0; - nframes64_t end = 0; - nframes64_t length; - nframes64_t pending_position; - - if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) { - pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset; - } else { - pending_position = 0; - } - - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { - snap_to (pending_position); - } - - /* only alter selection if the current frame is - different from the last frame position (adjusted) - */ - - if (pending_position == drag_info.last_pointer_frame) return; - - switch (selection_op) { - case CreateSelection: - - if (drag_info.first_move) { - snap_to (drag_info.grab_frame); - } - - if (pending_position < drag_info.grab_frame) { - start = pending_position; - end = drag_info.grab_frame; - } else { - end = pending_position; - start = drag_info.grab_frame; - } - - /* first drag: Either add to the selection - or create a new selection-> - */ - - if (drag_info.first_move) { - - begin_reversible_command (_("range selection")); - - if (drag_info.copy) { - /* adding to the selection */ - clicked_selection = selection->add (start, end); - drag_info.copy = false; - } else { - /* new selection-> */ - clicked_selection = selection->set (clicked_axisview, start, end); - } - } - break; - - case SelectionStartTrim: - - if (drag_info.first_move) { - begin_reversible_command (_("trim selection start")); - } - - start = selection->time[clicked_selection].start; - end = selection->time[clicked_selection].end; - - if (pending_position > end) { - start = end; - } else { - start = pending_position; - } - break; - - case SelectionEndTrim: - - if (drag_info.first_move) { - begin_reversible_command (_("trim selection end")); - } - - start = selection->time[clicked_selection].start; - end = selection->time[clicked_selection].end; - - if (pending_position < start) { - end = start; - } else { - end = pending_position; - } - - break; - - case SelectionMove: - - if (drag_info.first_move) { - begin_reversible_command (_("move selection")); - } - - start = selection->time[clicked_selection].start; - end = selection->time[clicked_selection].end; - - length = end - start; - - start = pending_position; - snap_to (start); - - end = start + length; - - break; - } - - if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) { - start_canvas_autoscroll (1, 0); - } - - if (start != end) { - selection->replace (clicked_selection, start, end); - } - - drag_info.last_pointer_frame = pending_position; - drag_info.first_move = false; - - if (selection_op == SelectionMove) { - show_verbose_time_cursor(start, 10); - } else { - show_verbose_time_cursor(pending_position, 10); - } -} - -void -Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event) -{ - if (!drag_info.first_move) { - drag_selection (item, event); - /* XXX this is not object-oriented programming at all. ick */ - if (selection->time.consolidate()) { - selection->TimeChanged (); - } - commit_reversible_command (); - } else { - /* just a click, no pointer movement.*/ - - if (Keyboard::no_modifier_keys_pressed (&event->button)) { - - selection->clear_time(); - - } - } - - /* XXX what happens if its a music selection? */ - session->set_audio_range (selection->time); - stop_canvas_autoscroll (); -} - -void -Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event) -{ - double speed = 1.0; - TimeAxisView* tvp = clicked_axisview; - RouteTimeAxisView* tv = dynamic_cast(tvp); - - if (tv && tv->is_track()) { - speed = tv->get_diskstream()->speed(); - } - - 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; - drag_info.finished_callback = &Editor::trim_finished_callback; - - start_grab (event, trimmer_cursor); - - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) { - trim_op = ContentsTrim; - } else { - /* These will get overridden for a point trim.*/ - if (drag_info.current_pointer_frame < (region_start + region_length/2)) { - /* closer to start */ - trim_op = StartTrim; - } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) { - /* closer to end */ - trim_op = EndTrim; - } - } - - switch (trim_op) { - case StartTrim: - show_verbose_time_cursor(region_start, 10); - break; - case EndTrim: - show_verbose_time_cursor(region_end, 10); - break; - case ContentsTrim: - show_verbose_time_cursor(drag_info.current_pointer_frame, 10); - break; - } -} - -void -Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) -{ - RegionView* rv = clicked_regionview; - nframes64_t frame_delta = 0; - - bool left_direction; - bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()); - - /* snap modifier works differently here.. - its' current state has to be passed to the - various trim functions in order to work properly - */ - - double speed = 1.0; - TimeAxisView* tvp = clicked_axisview; - RouteTimeAxisView* tv = dynamic_cast(tvp); - pair >::iterator,bool> insert_result; - - if (tv && tv->is_track()) { - speed = tv->get_diskstream()->speed(); - } - - if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) { - left_direction = true; - } else { - left_direction = false; - } - - if (obey_snap) { - snap_to (drag_info.current_pointer_frame); - } - - if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) { - return; - } + switch (m) { + case AudioClock::BBT: + session->bbt_time (start, sbbt); + session->bbt_time (end, ebbt); - if (drag_info.first_move) { - - string trim_type; + /* subtract */ + /* XXX this computation won't work well if the + user makes a selection that spans any meter changes. + */ - switch (trim_op) { - case StartTrim: - trim_type = "Region start trim"; - break; - case EndTrim: - trim_type = "Region end trim"; - break; - case ContentsTrim: - trim_type = "Region content trim"; - break; + ebbt.bars -= sbbt.bars; + if (ebbt.beats >= sbbt.beats) { + ebbt.beats -= sbbt.beats; + } else { + ebbt.bars--; + ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats; + } + if (ebbt.ticks >= sbbt.ticks) { + ebbt.ticks -= sbbt.ticks; + } else { + ebbt.beats--; + ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks; } - - begin_reversible_command (trim_type); - - for (list::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) { - (*i)->fake_set_opaque(false); - (*i)->region()->freeze (); - AudioRegionView* const arv = dynamic_cast(*i); - - if (arv){ - arv->temporarily_hide_envelope (); - } + snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks); + break; + + case AudioClock::SMPTE: + session->smpte_duration (end - start, smpte); + snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames); + break; - boost::shared_ptr pl = (*i)->region()->playlist(); - insert_result = motion_frozen_playlists.insert (pl); + case AudioClock::MinSec: + /* 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; - if (insert_result.second) { - session->add_command(new MementoCommand(*pl, &pl->get_state(), 0)); - pl->freeze(); - } - } + default: + snprintf (buf, sizeof(buf), "%" PRIi64, end - start); + break; } - if (left_direction) { - frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame); - } else { - frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame); + if (xpos >= 0 && ypos >=0) { + set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset); } - - bool non_overlap_trim = false; - - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) { - non_overlap_trim = true; + else { + set_verbose_canvas_cursor (buf, _drag->current_pointer_x() + offset, _drag->current_pointer_y() + offset); } - switch (trim_op) { - case StartTrim: - if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) { - break; - } else { + show_verbose_canvas_cursor (); +} - 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, non_overlap_trim); - } - break; - } - - case EndTrim: - if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes64_t) (rv->region()->last_frame()/speed))) { - break; - } else { +void +Editor::collect_new_region_view (RegionView* rv) +{ + latest_regionviews.push_back (rv); +} - 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, non_overlap_trim); - } - break; - } - - case ContentsTrim: - { - bool swap_direction = false; +void +Editor::collect_and_select_new_region_view (RegionView* rv) +{ + selection->add(rv); + latest_regionviews.push_back (rv); +} - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) { - swap_direction = true; - } - - for (list::const_iterator i = selection->regions.by_layer().begin(); - i != selection->regions.by_layer().end(); ++i) - { - single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap); - } - } - break; +void +Editor::cancel_selection () +{ + for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { + (*i)->hide_selection (); } - switch (trim_op) { - case StartTrim: - show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10); - break; - case EndTrim: - 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); - break; - } + selection->clear (); + clicked_selection = 0; +} - drag_info.last_pointer_frame = drag_info.current_pointer_frame; - drag_info.first_move = false; -} void Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap) @@ -5375,44 +2328,12 @@ Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_dire } -void -Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event) -{ - if (!drag_info.first_move) { - trim_motion_callback (item, event); - - if (!selection->selected (clicked_regionview)) { - thaw_region_after_trim (*clicked_regionview); - } else { - - for (list::const_iterator i = selection->regions.by_layer().begin(); - i != selection->regions.by_layer().end(); ++i) - { - thaw_region_after_trim (**i); - (*i)->fake_set_opaque (true); - } - } - - 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); - } -} - void Editor::point_trim (GdkEvent* event) { RegionView* rv = clicked_regionview; - nframes64_t new_bound = drag_info.current_pointer_frame; + nframes64_t new_bound = _drag->current_pointer_frame(); if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { snap_to (new_bound); @@ -5421,7 +2342,6 @@ Editor::point_trim (GdkEvent* event) /* Choose action dependant on which button was pressed */ switch (event->button.button) { case 1: - trim_op = StartTrim; begin_reversible_command (_("Start point trim")); if (selection->selected (rv)) { @@ -5457,7 +2377,6 @@ Editor::point_trim (GdkEvent* event) break; case 2: - trim_op = EndTrim; begin_reversible_command (_("End point trim")); if (selection->selected (rv)) { @@ -5473,340 +2392,59 @@ Editor::point_trim (GdkEvent* event) } } - } else { - - 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)); - } - } - - commit_reversible_command(); - - break; - default: - break; - } -} - -void -Editor::thaw_region_after_trim (RegionView& rv) -{ - boost::shared_ptr region (rv.region()); - - if (region->locked()) { - return; - } - - region->thaw (_("trimmed region")); - XMLNode &after = region->playlist()->get_state(); - session->add_command (new MementoCommand(*(region->playlist()), 0, &after)); - - AudioRegionView* arv = dynamic_cast(&rv); - if (arv) - arv->unhide_envelope (); -} - -void -Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event) -{ - Marker* marker; - bool is_start; - - if ((marker = static_cast (item->get_data ("marker"))) == 0) { - fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg; - /*NOTREACHED*/ - } - - Location* location = find_location_from_marker (marker, is_start); - location->set_hidden (true, this); -} - - -void -Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op) -{ - if (session == 0) { - return; - } - - drag_info.item = item; - drag_info.motion_callback = &Editor::drag_range_markerbar_op; - drag_info.finished_callback = &Editor::end_range_markerbar_op; - - range_marker_op = op; - - if (!temp_location) { - temp_location = new Location; - } - - switch (op) { - case CreateRangeMarker: - case CreateTransportMarker: - case CreateCDMarker: - - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) { - drag_info.copy = true; - } else { - drag_info.copy = false; - } - start_grab (event, selector_cursor); - break; - } - - show_verbose_time_cursor(drag_info.current_pointer_frame, 10); - -} - -void -Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event) -{ - 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); - } - - /* only alter selection if the current frame is - different from the last frame position. - */ - - if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return; - - switch (range_marker_op) { - case CreateRangeMarker: - case CreateTransportMarker: - case CreateCDMarker: - if (drag_info.first_move) { - snap_to (drag_info.grab_frame); - } - - if (drag_info.current_pointer_frame < drag_info.grab_frame) { - start = drag_info.current_pointer_frame; - end = drag_info.grab_frame; - } else { - end = drag_info.current_pointer_frame; - start = drag_info.grab_frame; - } - - /* first drag: Either add to the selection - or create a new selection. - */ - - if (drag_info.first_move) { - - temp_location->set (start, end); - - crect->show (); - - update_marker_drag_item (temp_location); - range_marker_drag_rect->show(); - //range_marker_drag_rect->raise_to_top(); - - } - break; - } - - if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) { - start_canvas_autoscroll (1, 0); - } - - if (start != end) { - temp_location->set (start, end); - - double x1 = frame_to_pixel (start); - double x2 = frame_to_pixel (end); - crect->property_x1() = x1; - crect->property_x2() = x2; - - update_marker_drag_item (temp_location); - } - - drag_info.last_pointer_frame = drag_info.current_pointer_frame; - drag_info.first_move = false; - - show_verbose_time_cursor(drag_info.current_pointer_frame, 10); - -} - -void -Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event) -{ - Location * newloc = 0; - string rangename; - int flags; - - if (!drag_info.first_move) { - drag_range_markerbar_op (item, event); - - switch (range_marker_op) { - case CreateRangeMarker: - case CreateCDMarker: - { - begin_reversible_command (_("new range marker")); - XMLNode &before = session->locations()->get_state(); - session->locations()->next_available_name(rangename,"unnamed"); - if (range_marker_op == CreateCDMarker) { - flags = Location::IsRangeMarker|Location::IsCDMarker; - cd_marker_bar_drag_rect->hide(); - } - else { - flags = Location::IsRangeMarker; - range_bar_drag_rect->hide(); - } - newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags); - session->locations()->add (newloc, true); - XMLNode &after = session->locations()->get_state(); - session->add_command(new MementoCommand(*(session->locations()), &before, &after)); - commit_reversible_command (); - - range_marker_drag_rect->hide(); - break; - } - - case CreateTransportMarker: - // popup menu to pick loop or punch - new_transport_marker_context_menu (&event->button, item); - - break; - } - } else { - /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */ - - if (Keyboard::no_modifier_keys_pressed (&event->button) && 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; - - 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; + } else { - default: - break; + 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)); } - } - } - - stop_canvas_autoscroll (); -} - - - -void -Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event) -{ - drag_info.item = item; - drag_info.motion_callback = &Editor::drag_mouse_zoom; - drag_info.finished_callback = &Editor::end_mouse_zoom; - - start_grab (event, zoom_cursor); + } - show_verbose_time_cursor (drag_info.current_pointer_frame, 10); + commit_reversible_command(); + + break; + default: + break; + } } void -Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event) +Editor::thaw_region_after_trim (RegionView& rv) { - nframes64_t start; - nframes64_t end; - - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { - snap_to (drag_info.current_pointer_frame); - - if (drag_info.first_move) { - snap_to (drag_info.grab_frame); - } - } - - if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return; + boost::shared_ptr region (rv.region()); - /* base start and end on initial click position */ - if (drag_info.current_pointer_frame < drag_info.grab_frame) { - start = drag_info.current_pointer_frame; - end = drag_info.grab_frame; - } else { - end = drag_info.current_pointer_frame; - start = drag_info.grab_frame; + if (region->locked()) { + return; } - - if (start != end) { - - if (drag_info.first_move) { - zoom_rect->show(); - zoom_rect->raise_to_top(); - } - - reposition_zoom_rect(start, end); - drag_info.last_pointer_frame = drag_info.current_pointer_frame; - drag_info.first_move = false; + region->thaw (_("trimmed region")); + XMLNode &after = region->playlist()->get_state(); + session->add_command (new MementoCommand(*(region->playlist()), 0, &after)); - show_verbose_time_cursor (drag_info.current_pointer_frame, 10); - } + AudioRegionView* arv = dynamic_cast(&rv); + if (arv) + arv->unhide_envelope (); } void -Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event) +Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event) { - if (!drag_info.first_move) { - drag_mouse_zoom (item, event); - - if (drag_info.grab_frame < drag_info.last_pointer_frame) { - temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom"); - } else { - temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom"); - } - } else { - temporal_zoom_to_frame (false, drag_info.grab_frame); - /* - temporal_zoom_step (false); - center_screen (drag_info.grab_frame); - */ + Marker* marker; + bool is_start; + + if ((marker = static_cast (item->get_data ("marker"))) == 0) { + fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg; + /*NOTREACHED*/ } - zoom_rect->hide(); + Location* location = find_location_from_marker (marker, is_start); + location->set_hidden (true, this); } + void Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end) { @@ -5820,122 +2458,6 @@ Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end) zoom_rect->property_y2() = y2; } -void -Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event) -{ - drag_info.item = item; - drag_info.motion_callback = &Editor::drag_rubberband_select; - drag_info.finished_callback = &Editor::end_rubberband_select; - - start_grab (event, cross_hair_cursor); - - show_verbose_time_cursor (drag_info.current_pointer_frame, 10); -} - -void -Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event) -{ - nframes64_t start; - nframes64_t end; - double y1; - double y2; - - /* use a bigger drag threshold than the default */ - - if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) { - return; - } - - 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; - } else { - end = drag_info.current_pointer_frame; - start = drag_info.grab_frame; - } - - if (drag_info.current_pointer_y < drag_info.grab_y) { - y1 = drag_info.current_pointer_y; - y2 = drag_info.grab_y; - } else { - y2 = drag_info.current_pointer_y; - y1 = drag_info.grab_y; - } - - - if (start != end || y1 != y2) { - - double x1 = frame_to_pixel (start); - double x2 = frame_to_pixel (end); - - rubberband_rect->property_x1() = x1; - rubberband_rect->property_y1() = y1; - rubberband_rect->property_x2() = x2; - rubberband_rect->property_y2() = y2; - - rubberband_rect->show(); - rubberband_rect->raise_to_top(); - - drag_info.last_pointer_frame = drag_info.current_pointer_frame; - drag_info.first_move = false; - - show_verbose_time_cursor (drag_info.current_pointer_frame, 10); - } -} - -void -Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event) -{ - if (!drag_info.first_move) { - - drag_rubberband_select (item, event); - - double y1,y2; - if (drag_info.current_pointer_y < drag_info.grab_y) { - y1 = drag_info.current_pointer_y; - y2 = drag_info.grab_y; - } else { - y2 = drag_info.current_pointer_y; - y1 = drag_info.grab_y; - } - - - Selection::Operation op = Keyboard::selection_type (event->button.state); - bool commit; - - 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, track_views, op); - } else { - commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op); - } - - if (commit) { - commit_reversible_command (); - } - - } else { - if (!getenv("ARDOUR_SAE")) { - selection->clear_tracks(); - } - selection->clear_regions(); - selection->clear_points (); - selection->clear_lines (); - } - - rubberband_rect->hide(); -} - gint Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event) @@ -5961,77 +2483,6 @@ Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event) return true; } -void -Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event) -{ - drag_info.item = item; - drag_info.motion_callback = &Editor::time_fx_motion; - drag_info.finished_callback = &Editor::end_time_fx; - - start_grab (event); - - show_verbose_time_cursor (drag_info.current_pointer_frame, 10); -} - -void -Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event) -{ - RegionView* rv = clicked_regionview; - - if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) { - snap_to (drag_info.current_pointer_frame); - } - - if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) { - 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); - } - - drag_info.last_pointer_frame = drag_info.current_pointer_frame; - drag_info.first_move = false; - - show_verbose_time_cursor (drag_info.current_pointer_frame, 10); -} - -void -Editor::end_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(); - -#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 (time_stretch (rs, percentage) == 0) { - session->commit_reversible_command (); - } -} void Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos) @@ -6088,3 +2539,126 @@ Editor::track_height_step_timeout () return true; } +void +Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view) +{ + assert (region_view); + + _region_motion_group->raise_to_top (); + + assert (_drag == 0); + + if (Config->get_edit_mode() == Splice) { + _drag = new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()); + } else { + _drag = new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false); + } + + _drag->start_grab (event); + + 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 (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view) +{ + assert (region_view); + assert (_drag == 0); + + _region_motion_group->raise_to_top (); + _drag = new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true); + _drag->start_grab(event); +} + +void +Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view) +{ + assert (region_view); + assert (_drag == 0); + + if (Config->get_edit_mode() == Splice) { + return; + } + + _drag = new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false); + _drag->start_grab (event); + + begin_reversible_command (_("Drag region brush")); +} + +/** Start a grab where a time range is selected, track(s) are selected, and the + * user clicks and drags a region with a modifier in order to create a new region containing + * the section of the clicked region that lies within the time range. + */ +void +Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event) +{ + if (clicked_regionview == 0) { + return; + } + + /* lets try to create new Region for the selection */ + + vector > new_regions; + create_region_from_selection (new_regions); + + if (new_regions.empty()) { + return; + } + + /* XXX fix me one day to use all new regions */ + + boost::shared_ptr region (new_regions.front()); + + /* add it to the current stream/playlist. + + tricky: the streamview for the track will add a new regionview. we will + catch the signal it sends when it creates the regionview to + set the regionview we want to then drag. + */ + + latest_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. + */ + + begin_reversible_command (_("selection grab")); + + boost::shared_ptr playlist = clicked_axisview->playlist(); + + XMLNode *before = &(playlist->get_state()); + clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start); + XMLNode *after = &(playlist->get_state()); + session->add_command(new MementoCommand(*playlist, before, after)); + + commit_reversible_command (); + + c.disconnect (); + + if (latest_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_regionviews); + + assert (_drag == 0); + _drag = new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false); + _drag->start_grab (event); +} + +void +Editor::break_drag () +{ + if (_drag) { + _drag->break_drag (); + } +} diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc index a9cc7ae7d6..16d9e6e0e9 100644 --- a/gtk2_ardour/editor_ops.cc +++ b/gtk2_ardour/editor_ops.cc @@ -74,6 +74,7 @@ #include "gui_thread.h" #include "keyboard.h" #include "utils.h" +#include "editor_drag.h" #include "strip_silence_dialog.h" #include "i18n.h" @@ -859,7 +860,7 @@ Editor::cursor_to_previous_region_boundary (bool with_selection) } void -Editor::cursor_to_region_point (Cursor* cursor, RegionPoint point, int32_t dir) +Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir) { boost::shared_ptr r; nframes64_t pos = cursor->current_frame; @@ -928,19 +929,19 @@ Editor::cursor_to_region_point (Cursor* cursor, RegionPoint point, int32_t dir) } void -Editor::cursor_to_next_region_point (Cursor* cursor, RegionPoint point) +Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point) { cursor_to_region_point (cursor, point, 1); } void -Editor::cursor_to_previous_region_point (Cursor* cursor, RegionPoint point) +Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point) { cursor_to_region_point (cursor, point, -1); } void -Editor::cursor_to_selection_start (Cursor *cursor) +Editor::cursor_to_selection_start (EditorCursor *cursor) { nframes64_t pos = 0; RegionSelection rs; @@ -972,7 +973,7 @@ Editor::cursor_to_selection_start (Cursor *cursor) } void -Editor::cursor_to_selection_end (Cursor *cursor) +Editor::cursor_to_selection_end (EditorCursor *cursor) { nframes64_t pos = 0; RegionSelection rs; @@ -2759,7 +2760,7 @@ Editor::create_region_from_selection (vector >& new_re internal_start = start - current->position(); session->region_name (new_name, current->name(), true); - + new_regions.push_back (RegionFactory::create (current, internal_start, end - start + 1, new_name)); } @@ -3882,9 +3883,10 @@ Editor::cut_copy (CutCopyOp op) */ if (op == Cut || op == Clear) { - if (drag_info.item) { - drag_info.item->ungrab (0); - drag_info.item = 0; + if (_drag) { + _drag->item()->ungrab (0); + delete _drag; + _drag = 0; } } @@ -3902,6 +3904,8 @@ Editor::cut_copy (CutCopyOp op) } break_drag (); + delete _drag; + _drag = 0; return; } @@ -3971,6 +3975,8 @@ Editor::cut_copy (CutCopyOp op) if (op == Cut || op == Clear) { break_drag (); + delete _drag; + _drag = 0; } } diff --git a/gtk2_ardour/editor_rulers.cc b/gtk2_ardour/editor_rulers.cc index 4daf57d3ae..ccb98bcc70 100644 --- a/gtk2_ardour/editor_rulers.cc +++ b/gtk2_ardour/editor_rulers.cc @@ -377,7 +377,7 @@ Editor::ruler_mouse_motion (GdkEventMotion* ev) snap_to (where); - Cursor* cursor = 0; + EditorCursor* cursor = 0; switch (ruler_pressed_button) { case 1: diff --git a/gtk2_ardour/editor_selection.cc b/gtk2_ardour/editor_selection.cc index 730bd0b20d..d8363a5102 100644 --- a/gtk2_ardour/editor_selection.cc +++ b/gtk2_ardour/editor_selection.cc @@ -1185,7 +1185,7 @@ Editor::select_all_selectables_using_loop() } void -Editor::select_all_selectables_using_cursor (Cursor *cursor, bool after) +Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after) { nframes64_t start; nframes64_t end; diff --git a/gtk2_ardour/wscript b/gtk2_ardour/wscript index 19b50af781..1735f08a65 100644 --- a/gtk2_ardour/wscript +++ b/gtk2_ardour/wscript @@ -103,6 +103,7 @@ def build(bld): editor_canvas.cc editor_canvas_events.cc editor_cursors.cc + editor_drag.cc editor_edit_groups.cc editor_export_audio.cc editor_hscroller.cc -- cgit v1.2.3