ardour/gtk2_ardour/editor_drag.h

1636 lines
41 KiB
C
Raw Normal View History

/*
* Copyright (C) 2009-2012 Carl Hetherington <carl@carlh.net>
* Copyright (C) 2009-2015 David Robillard <d@drobilla.net>
* Copyright (C) 2009-2017 Paul Davis <paul@linuxaudiosystems.com>
* Copyright (C) 2013-2014 Colin Fletcher <colin.m.fletcher@googlemail.com>
* Copyright (C) 2013-2019 Robin Gareus <robin@gareus.org>
* Copyright (C) 2015-2017 Nick Mainsbridge <mainsbridge@gmail.com>
* Copyright (C) 2015-2018 Ben Loftis <ben@harrisonconsoles.com>
* Copyright (C) 2016 Tim Mayberry <mojofunk@gmail.com>
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __gtk2_ardour_editor_drag_h_
#define __gtk2_ardour_editor_drag_h_
#include <list>
#include <vector>
#include <gdk/gdk.h>
#include <stdint.h>
#include "ardour/tempo.h"
#include "ardour/types.h"
#include "evoral/ControlList.h"
#include "canvas/types.h"
#include "gtkmm2ext/bindings.h"
#include "cursor_context.h"
#include "editor_items.h"
#include "mouse_cursors.h"
#include "editing.h"
#include "track_selection.h"
#include "region_view.h"
namespace ARDOUR {
class Location;
class Region;
class TempoSection;
}
namespace ArdourCanvas {
class Item;
class Line;
class Rectangle;
2023-06-20 13:28:59 -04:00
class Lollipop;
}
namespace PBD {
class StatefulDiffCommand;
}
class PatchChange;
class Editor;
class EditorCursor;
class TimeAxisView;
class MidiTimeAxisView;
class Drag;
class NoteBase;
class TimeAxisView;
class RouteTimeAxisView;
class RegionSelection;
class MidiRegionView;
class MeterMarker;
class ArdourMarker;
class TempoMarker;
class TempoCurve;
class ControlPoint;
class AudioRegionView;
class AutomationLine;
class AutomationTimeAxisView;
2023-06-20 13:28:59 -04:00
class VelocityGhostRegion;
/** Class to manage current drags */
class DragManager
{
public:
DragManager (Editor* e);
~DragManager ();
bool motion_handler (GdkEvent *, bool);
bool mid_drag_key_event (GdkEventKey*);
void abort ();
void add (Drag *);
void set (Drag *, GdkEvent *, Gdk::Cursor* c = MouseCursors::invalid_cursor());
void start_grab (GdkEvent *, Gdk::Cursor* c = MouseCursors::invalid_cursor());
bool end_grab (GdkEvent *);
bool have_item (ArdourCanvas::Item *) const;
2017-07-01 12:42:24 -04:00
void mark_double_click ();
/** @return true if an end drag or abort is in progress */
bool ending () const {
return _ending;
}
bool active () const {
return !_drags.empty ();
}
/** @return current pointer x position in canvas coordinates */
double current_pointer_x () const {
return _current_pointer_x;
}
/** @return current pointer y position in canvas coordinates */
double current_pointer_y () const {
return _current_pointer_y;
}
/** @return current pointer sample */
ARDOUR::samplepos_t current_pointer_sample () const {
2020-10-15 01:09:22 -04:00
return _current_pointer_time.samples();
}
/** @return current pointer time */
Temporal::timepos_t current_pointer_time () const {
return _current_pointer_time;
}
2019-11-08 23:03:38 -05:00
/** return drag-motion displays video-frame of drag-location */
bool preview_video () const;
private:
Editor* _editor;
std::list<Drag*> _drags;
bool _ending; ///< true if end_grab or abort is in progress, otherwise false
double _current_pointer_x; ///< canvas-coordinate space x of the current pointer
double _current_pointer_y; ///< canvas-coordinate space y of the current pointer
2020-10-15 01:09:22 -04:00
Temporal::timepos_t _current_pointer_time; ///< time that the pointer is now at
bool _old_follow_playhead; ///< state of Editor::follow_playhead() before the drags started
};
/** Abstract base class for dragging of things within the editor */
class Drag
{
public:
Drag (Editor *, ArdourCanvas::Item *, Temporal::TimeDomain td, bool trackview_only = true, bool hide_snapped_cursor = true);
virtual ~Drag ();
void set_manager (DragManager* m) {
_drags = m;
}
/** @return the canvas item being dragged */
ArdourCanvas::Item* item () const {
return _item;
}
Temporal::TimeDomain time_domain() const { return _time_domain; }
void set_time_domain (Temporal::TimeDomain);
void swap_grab (ArdourCanvas::Item *, Gdk::Cursor *, uint32_t);
bool motion_handler (GdkEvent*, bool);
void abort ();
2020-10-15 01:09:22 -04:00
Temporal::timepos_t adjusted_time (Temporal::timepos_t const &, GdkEvent const *, bool snap = true) const;
Temporal::timepos_t adjusted_current_time (GdkEvent const *, bool snap = true) const;
2017-07-01 12:42:24 -04:00
bool was_double_click() const { return _was_double_click; }
void set_double_click (bool yn) { _was_double_click = yn; }
void set_grab_button_anyway (GdkEvent*);
/** 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.
* @param f true if this is the first movement, otherwise false.
*/
virtual void motion (GdkEvent* e, bool f) = 0;
/** Called when a drag has finished.
* @param e Event describing the finish.
* @param m true if some movement occurred, otherwise false.
*/
virtual void finished (GdkEvent* e, bool m) = 0;
/** Called to abort a drag and return things to how
* they were before it started.
* @param m true if some movement occurred, otherwise false.
*/
virtual void aborted (bool m) = 0;
/** @param m Mouse mode.
* @return true if this drag should happen in this mouse mode.
*/
virtual bool active (Editing::MouseMode m) {
return true;
}
2019-11-08 23:03:38 -05:00
bool preview_video () const {
return _preview_video;
}
2020-10-15 01:09:22 -04:00
/** @return minimum number of samples (in x) and pixels (in y) that should be considered a movement */
std::pair<Temporal::timecnt_t,int> move_threshold () const {
return std::make_pair (Temporal::timecnt_t (1, Temporal::AudioTime), 1);
}
virtual bool allow_vertical_autoscroll () const {
return true;
}
virtual bool allow_horizontal_autoscroll () const {
return true;
}
/** @return true if x movement matters to this drag */
virtual bool x_movement_matters () const {
return true;
}
/** @return true if y movement matters to this drag */
virtual bool y_movement_matters () const {
return true;
}
virtual bool mid_drag_key_event (GdkEventKey*) { return false; }
2015-02-12 14:28:44 -05:00
bool initially_vertical() const {
return _initially_vertical;
}
2015-10-05 10:17:49 -04:00
2020-10-15 01:09:22 -04:00
/** Set up the _pointer_offset */
virtual void setup_pointer_offset () {
_pointer_offset = Temporal::timecnt_t();
}
2020-10-15 01:09:22 -04:00
/** Set up the _video_offset - relative to _current_pointer_time */
virtual void setup_video_offset () {
/* video offset is always in audio time */
_video_offset = Temporal::timecnt_t (Temporal::AudioTime);
2019-11-08 23:03:38 -05:00
_preview_video = false;
}
int grab_button() const { return _grab_button; }
protected:
double grab_x () const {
return _grab_x;
}
double grab_y () const {
return _grab_y;
}
Temporal::timepos_t pixel_to_time (double x) const;
2020-10-15 01:09:22 -04:00
Temporal::timepos_t raw_grab_time () const {
return _raw_grab_time;
}
ARDOUR::samplepos_t grab_sample () const {
2020-10-15 01:09:22 -04:00
return _grab_time.samples();
}
Temporal::timepos_t grab_time () const {
return _grab_time;
}
double last_pointer_x () const {
return _last_pointer_x;
}
double last_pointer_y () const {
return _last_pointer_y;
}
ARDOUR::samplepos_t last_pointer_sample () const {
2020-10-15 01:09:22 -04:00
return _last_pointer_time.samples();
}
2020-10-15 01:09:22 -04:00
Temporal::timepos_t last_pointer_time () const {
return _last_pointer_time;
}
Temporal::timecnt_t snap_delta (guint const) const;
2015-05-16 10:23:25 -04:00
double current_pointer_x () const;
double current_pointer_y () const;
2015-05-16 10:23:25 -04:00
/* sets snap delta from unsnapped pos */
2020-10-15 01:09:22 -04:00
void setup_snap_delta (Temporal::timepos_t const & pos);
2015-05-16 10:23:25 -04:00
std::shared_ptr<ARDOUR::Region> add_midi_region (MidiTimeAxisView*, bool commit);
2020-10-15 01:09:22 -04:00
void show_verbose_cursor_time (Temporal::timepos_t const &);
void show_verbose_cursor_duration (Temporal::timepos_t const & , Temporal::timepos_t const & , double xoffset = 0);
void show_verbose_cursor_text (std::string const &);
2020-10-15 01:09:22 -04:00
void show_view_preview (Temporal::timepos_t const &);
Editor* _editor; ///< our editor
DragManager* _drags;
ArdourCanvas::Item* _item; ///< our item
/** Offset from the mouse's position for the drag to the start of the thing that is being dragged */
2020-10-15 01:09:22 -04:00
Temporal::timecnt_t _pointer_offset;
Temporal::timecnt_t _video_offset;
2019-11-08 23:03:38 -05:00
bool _preview_video;
bool _x_constrained; ///< true if x motion is constrained, otherwise false
bool _y_constrained; ///< true if y motion is constrained, otherwise false
bool _was_rolling; ///< true if the session was rolling before the drag started, otherwise false
ARDOUR::timepos_t _earliest_time_limit; ///< time we cannot drag before (defaults to 0, indicating no such limit)
private:
bool _trackview_only; ///< true if pointer y value should always be relative to the top of the trackview group
bool _hide_snapped_cursor; ///< set true of ::motion does not call `set_snapped_cursor_position`
bool _move_threshold_passed; ///< true if the move threshold has been passed, otherwise false
2015-01-23 13:45:21 -05:00
bool _starting_point_passed; ///< true if we called move () with first_move flag, otherwise false
bool _initially_vertical; ///< true if after move threshold is passed we appear to be moving vertically; undefined before that
2017-07-01 12:42:24 -04:00
bool _was_double_click; ///< true if drag initiated by a double click event
double _grab_x; ///< trackview x of the grab start position
double _grab_y; ///< y of the grab start position, possibly adjusted if _trackview_only is true
double _last_pointer_x; ///< trackview x of the pointer last time a motion occurred
double _last_pointer_y; ///< trackview y of the pointer last time a motion occurred
2020-10-15 01:09:22 -04:00
Temporal::timepos_t _raw_grab_time; ///< unsnapped time that the mouse was at when start_grab was called, or 0
Temporal::timepos_t _grab_time; ///< adjusted_time that the mouse was at when start_grab was called, or 0
Temporal::timepos_t _last_pointer_time; ///< adjusted_time the last time a motion occurred
Temporal::TimeDomain _time_domain;
2015-05-16 10:23:25 -04:00
/* difference between some key position's snapped and unsnapped
* samplepos. used for relative snap.
2015-05-16 10:23:25 -04:00
*/
2020-10-15 01:09:22 -04:00
Temporal::timecnt_t _snap_delta;
CursorContext::Handle _cursor_ctx; ///< cursor change context
bool _constraint_pressed; ///< if the keyboard indicated constraint modifier was pressed on start_grab()
int _grab_button;
Gtkmm2ext::Bindings::DragsBlockBindings binding_blocker;
};
class RegionDrag;
/** Container for details about a region being dragged */
class DraggingView
{
public:
DraggingView (RegionView *, RegionDrag *, TimeAxisView* original_tav);
RegionView* view; ///< the view
/** index into RegionDrag::_time_axis_views of the view that this region is currently being displayed on,
* or -1 if it is not visible.
*/
int time_axis_view;
/** layer that this region is currently being displayed on. This is a double
rather than a layer_t as we use fractional layers during drags to allow the user
to indicate a new layer to put a region on.
*/
double layer;
double initial_y; ///< the initial y position of the view before any reparenting
2020-10-15 01:09:22 -04:00
Temporal::timepos_t initial_position; ///< initial position of the region
Temporal::timepos_t initial_end; ///< initial end position of the region
samplepos_t anchored_fade_length; ///< fade_length when anchored during drag
std::shared_ptr<ARDOUR::Playlist> initial_playlist;
TimeAxisView* initial_time_axis_view;
};
/** Abstract base class for drags that involve region(s) */
class RegionDrag : public Drag, public sigc::trackable
{
public:
RegionDrag (Editor *, ArdourCanvas::Item *, RegionView *, std::list<RegionView*> const &, Temporal::TimeDomain, bool hide_snapped_cursor = true);
virtual ~RegionDrag () {}
protected:
RegionView* _primary; ///< the view that was clicked on (or whatever) to start the drag
std::list<DraggingView> _views; ///< information about all views that are being dragged
/** a list of the non-hidden TimeAxisViews sorted by editor order key */
std::vector<TimeAxisView*> _time_axis_views;
int find_time_axis_view (TimeAxisView *) const;
int apply_track_delta (const int start, const int delta, const int skip, const bool distance_only = false) const;
int _visible_y_low;
int _visible_y_high;
uint32_t _ntracks;
2020-10-15 01:09:22 -04:00
void setup_video_offset ();
2019-11-08 23:03:38 -05:00
friend class DraggingView;
protected:
void add_stateful_diff_commands_for_playlists (ARDOUR::PlaylistSet const &);
private:
void region_going_away (RegionView *);
PBD::ScopedConnection death_connection;
};
2021-06-15 10:06:28 -04:00
/** Drag the Contents rather than the bounds of a region (i.e. Slip) */
class RegionSlipContentsDrag : public RegionDrag
2021-06-15 10:06:28 -04:00
{
public:
RegionSlipContentsDrag (Editor *, ArdourCanvas::Item *, RegionView *, std::list<RegionView*> const &, Temporal::TimeDomain td);
virtual ~RegionSlipContentsDrag () {}
2021-06-15 10:06:28 -04:00
virtual void start_grab (GdkEvent *, Gdk::Cursor *);
virtual void motion (GdkEvent *, bool);
virtual void finished (GdkEvent *, bool);
virtual void aborted (bool);
};
/** Brush on a region to repeat it */
class RegionBrushDrag : public RegionDrag
{
public:
RegionBrushDrag (Editor *, ArdourCanvas::Item *, RegionView *, std::list<RegionView*> const &, Temporal::TimeDomain td);
virtual ~RegionBrushDrag () {}
virtual void start_grab (GdkEvent *, Gdk::Cursor *);
virtual void motion (GdkEvent *, bool);
virtual void finished (GdkEvent *, bool);
virtual void aborted (bool);
private:
typedef std::set<Temporal::timepos_t> TimePositionSet;
TimePositionSet _already_pasted;
};
/** Drags involving region motion from somewhere */
class RegionMotionDrag : public RegionDrag
{
public:
RegionMotionDrag (Editor *, ArdourCanvas::Item *, RegionView *, std::list<RegionView*> const &, Temporal::TimeDomain td);
virtual ~RegionMotionDrag () {}
virtual void start_grab (GdkEvent *, Gdk::Cursor *);
virtual void motion (GdkEvent *, bool);
virtual void finished (GdkEvent *, bool);
virtual void aborted (bool);
/** @return true if the regions being `moved' came from somewhere on the canvas;
* false if they came from outside (e.g. from the region list).
*/
virtual bool regions_came_from_canvas () const = 0;
protected:
2020-10-15 01:09:22 -04:00
double compute_x_delta (GdkEvent const *, Temporal::timepos_t &);
virtual bool y_movement_allowed (int, double, int skip_invisible = 0) const;
void collect_ripple_views ();
bool _ignore_video_lock;
2020-10-15 01:09:22 -04:00
Temporal::timepos_t _last_position; ///< last position of the thing being dragged
double _total_x_delta;
int _last_pointer_time_axis_view;
double _last_pointer_layer;
std::vector<ArdourMarker*> ripple_markers;
private:
uint32_t _ndropzone;
uint32_t _pdropzone;
uint32_t _ddropzone;
};
/** Drags to move (or copy) regions that are already shown in the GUI to
* somewhere different.
*/
class RegionMoveDrag : public RegionMotionDrag
{
public:
RegionMoveDrag (Editor *, ArdourCanvas::Item *, RegionView *, std::list<RegionView*> const &, bool, Temporal::TimeDomain);
virtual ~RegionMoveDrag () {}
void motion (GdkEvent *, bool);
void finished (GdkEvent *, bool);
void aborted (bool);
void clear_draggingview_list ();
bool regions_came_from_canvas () const {
return true;
}
2020-10-15 01:09:22 -04:00
std::pair<Temporal::timecnt_t,int> move_threshold () const {
return std::make_pair (Temporal::timecnt_t (4, Temporal::AudioTime), 4);
}
2020-10-15 01:09:22 -04:00
void setup_pointer_offset ();
Ripple mode: basic implementation Add a value for Ripple to EditMode enum. Add Ripple edit mode to edit mode dropdown, by adding it to the Editor::build_edit_mode_menu() helper function, and remove the old code that added items to the (now unused) Editor::edit_mode_strings. Add the regions that should be affected by the drag to RegionDrag::_views so that the drag carries them along automatically. Use a copy of the RegionList in Playlist::core_ripple(), since bad things happen when iterating over regions and they get moved around in the list. Handle rippling in removal of regions from playlist. When dragging in ripple mode, exclude all regions that lie before the original start position of the selected regions being dragged from rippling: this is what Mixbus does. Make editor dragging respect snap-to settings, by using the existing compute_x_delta() function, which did almost the right thing. Move setting of _last_frame_position out of that function so all ripple-dragged regions can move. Ripple when dragging from region list: even though Mixbus doesn't do this, it seems like a good idea. Prevent multi-track selection being dragged across tracks, by making RegionMotionDrag::y_movement_allowed() virtual, and overriding it in RegionRippleDrag to forbid dragging of selections containing regions on more than one track to dofferent tracks in ripple mode. Remember which TimeAxisView a ripple-mode drag that's allowed cross-track drags started from, so that the effect of rippling regions after any region that's dragged off that track can be undone.
2013-10-23 16:50:01 -04:00
private:
void finished_no_copy (
bool const,
bool const,
2020-10-15 01:09:22 -04:00
Temporal::timepos_t const &,
int32_t const ev_state
);
void finished_copy (
bool const,
bool const,
2020-10-15 01:09:22 -04:00
Temporal::timepos_t const &,
int32_t const ev_state
);
RegionView* insert_region_into_playlist (
std::shared_ptr<ARDOUR::Region>,
RouteTimeAxisView*,
ARDOUR::layer_t,
2020-10-15 01:09:22 -04:00
Temporal::timepos_t const &,
ARDOUR::PlaylistSet&
);
void remove_region_from_playlist (
std::shared_ptr<ARDOUR::Region>,
std::shared_ptr<ARDOUR::Playlist>,
ARDOUR::PlaylistSet& modified_playlists
);
void collect_new_region_view (RegionView *);
RouteTimeAxisView* create_destination_time_axis (std::shared_ptr<ARDOUR::Region>, TimeAxisView* original);
bool _copy;
RegionView* _new_region_view;
};
/** Drag to insert a region from somewhere */
class RegionInsertDrag : public RegionMotionDrag
{
public:
RegionInsertDrag (Editor *, std::shared_ptr<ARDOUR::Region>, RouteTimeAxisView*, Temporal::timepos_t const &, Temporal::TimeDomain);
void finished (GdkEvent *, bool);
void aborted (bool);
bool regions_came_from_canvas () const {
return false;
}
};
2020-10-15 01:09:22 -04:00
/** "Drag" to cut a region (action only on button release) */
2014-07-07 07:05:27 -04:00
class RegionCutDrag : public Drag
{
public:
RegionCutDrag (Editor*, ArdourCanvas::Item*, samplepos_t);
~RegionCutDrag ();
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
2014-07-07 07:05:27 -04:00
void motion (GdkEvent*, bool);
void finished (GdkEvent*, bool);
void aborted (bool);
private:
2014-07-07 07:05:27 -04:00
};
/** Drags to create regions */
class RegionCreateDrag : public Drag
{
public:
RegionCreateDrag (Editor *, ArdourCanvas::Item *, TimeAxisView *);
void motion (GdkEvent *, bool);
void finished (GdkEvent *, bool);
void aborted (bool);
private:
MidiTimeAxisView* _view;
std::shared_ptr<ARDOUR::Region> _region;
};
/** Drags to resize MIDI notes */
class NoteResizeDrag : public Drag
{
public:
NoteResizeDrag (Editor *, ArdourCanvas::Item *);
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
void motion (GdkEvent *, bool);
void finished (GdkEvent *, bool);
void aborted (bool);
bool allow_vertical_autoscroll () const {
return false;
}
private:
MidiRegionView* region;
bool relative;
bool at_front;
bool _was_selected;
2015-05-15 14:15:52 -04:00
double _snap_delta;
};
/** Drags to move MIDI notes */
class NoteDrag : public Drag
{
public:
NoteDrag (Editor*, ArdourCanvas::Item*);
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
void motion (GdkEvent *, bool);
void finished (GdkEvent *, bool);
void aborted (bool);
void setup_pointer_offset ();
bool allow_vertical_autoscroll () const {
return false;
}
private:
2020-10-15 01:09:22 -04:00
Temporal::timecnt_t total_dx (GdkEvent * event) const; // total movement in quarter notes
int8_t total_dy () const;
MidiRegionView* _region;
NoteBase* _primary;
2020-10-15 01:09:22 -04:00
Temporal::timecnt_t _cumulative_dx;
double _cumulative_dy;
2020-10-15 01:09:22 -04:00
Temporal::timepos_t _earliest; // earliest note in note selection
bool _was_selected;
double _note_height;
bool _copy;
};
class NoteCreateDrag : public Drag
{
public:
NoteCreateDrag (Editor *, ArdourCanvas::Item *, MidiRegionView *);
~NoteCreateDrag ();
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
void motion (GdkEvent *, bool);
void finished (GdkEvent *, bool);
void aborted (bool);
bool active (Editing::MouseMode mode) {
return mode == Editing::MouseDraw || mode == Editing::MouseContent;
}
bool allow_vertical_autoscroll () const {
return false;
}
bool y_movement_matters () const {
return false;
}
private:
double y_to_region (double) const;
Temporal::Beats round_to_grid (Temporal::timepos_t const & pos, GdkEvent const * event) const;
2015-10-05 10:17:49 -04:00
2020-10-15 01:09:22 -04:00
/** @return minimum number of samples (in x) and pixels (in y) that should be considered a movement */
std::pair<Temporal::timecnt_t,int> move_threshold () const {
return std::make_pair (Temporal::timecnt_t (0, Temporal::AudioTime), 0);
}
MidiRegionView* _region_view;
ArdourCanvas::Rectangle* _drag_rect;
2020-10-15 01:09:22 -04:00
Temporal::timepos_t _note[2];
};
class HitCreateDrag : public Drag
{
public:
HitCreateDrag (Editor *, ArdourCanvas::Item *, MidiRegionView *);
~HitCreateDrag ();
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
void motion (GdkEvent *, bool) {}
void finished (GdkEvent *, bool);
void aborted (bool) {}
bool active (Editing::MouseMode mode) {
return mode == Editing::MouseDraw || mode == Editing::MouseContent;
}
bool y_movement_matters () const {
return false;
}
private:
double y_to_region (double) const;
2020-10-15 01:09:22 -04:00
/** @return minimum number of samples (in x) and pixels (in y) that should be considered a movement */
std::pair<Temporal::timecnt_t,int> move_threshold () const {
return std::make_pair (Temporal::timecnt_t::zero (Temporal::AudioTime), 0);
}
MidiRegionView* _region_view;
Temporal::timepos_t _last_pos;
double _y;
};
/** Drag to move MIDI patch changes */
class PatchChangeDrag : public Drag
{
public:
PatchChangeDrag (Editor *, PatchChange *, MidiRegionView *);
void motion (GdkEvent *, bool);
void finished (GdkEvent *, bool);
void aborted (bool);
bool y_movement_matters () const {
return false;
}
bool allow_vertical_autoscroll () const {
return false;
}
2020-10-15 01:09:22 -04:00
void setup_pointer_offset ();
private:
MidiRegionView* _region_view;
PatchChange* _patch_change;
double _cumulative_dx;
};
/** Container for details about audio regions being dragged along with video */
class AVDraggingView
{
public:
AVDraggingView (RegionView *);
RegionView* view; ///< the view
samplepos_t initial_position; ///< initial position of the region
};
/** Drag of video offset */
class VideoTimeLineDrag : public Drag
{
public:
VideoTimeLineDrag (Editor *e, ArdourCanvas::Item *i);
void motion (GdkEvent *, bool);
void finished (GdkEvent *, bool);
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
bool y_movement_matters () const {
return false;
}
bool allow_vertical_autoscroll () const {
return false;
}
void aborted (bool);
protected:
std::list<AVDraggingView> _views; ///< information about all audio that are being dragged along
private:
ARDOUR::sampleoffset_t _startdrag_video_offset;
ARDOUR::sampleoffset_t _max_backwards_drag;
bool _stuck;
};
/** Drag to trim region(s) */
class TrimDrag : public RegionDrag
{
public:
enum Operation {
StartTrim,
EndTrim
};
TrimDrag (Editor *, ArdourCanvas::Item *, RegionView*, std::list<RegionView*> const &, Temporal::TimeDomain td, bool preserve_fade_anchor = false);
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
void motion (GdkEvent *, bool);
void finished (GdkEvent *, bool);
void aborted (bool);
bool y_movement_matters () const {
return false;
}
bool allow_vertical_autoscroll () const {
return false;
}
2020-10-15 01:09:22 -04:00
void setup_pointer_offset ();
private:
Operation _operation;
2015-10-05 10:17:49 -04:00
bool _preserve_fade_anchor;
bool _jump_position_when_done;
std::vector<RegionView::DisplaySuspender> suspenders;
};
/** Meter marker drag */
class MeterMarkerDrag : public Drag
{
public:
MeterMarkerDrag (Editor *, ArdourCanvas::Item *, bool);
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
void motion (GdkEvent *, bool);
void finished (GdkEvent *, bool);
void aborted (bool);
bool allow_vertical_autoscroll () const {
return false;
}
bool y_movement_matters () const {
return false;
}
2020-10-15 01:09:22 -04:00
void setup_pointer_offset ();
private:
MeterMarker* _marker;
Temporal::TempoMap::WritableSharedPtr map;
2022-05-30 23:14:51 -04:00
Temporal::superclock_t initial_sclock;
2020-11-24 20:25:34 -05:00
bool _movable;
Editing::GridType _old_grid_type;
2016-05-27 12:57:53 -04:00
Editing::SnapMode _old_snap_mode;
XMLNode* before_state;
};
/** Tempo curve drag */
class TempoCurveDrag : public Drag
{
public:
TempoCurveDrag (Editor*, ArdourCanvas::Item*);
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
void motion (GdkEvent *, bool);
void finished (GdkEvent *, bool);
void aborted (bool);
private:
Temporal::TempoPoint* point;
double initial_bpm;
Temporal::TempoMap::WritableSharedPtr map;
XMLNode* _before_state;
};
/** Tempo marker drag */
class TempoMarkerDrag : public Drag
{
public:
TempoMarkerDrag (Editor *, ArdourCanvas::Item *);
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
void motion (GdkEvent *, bool);
void finished (GdkEvent *, bool);
void aborted (bool);
bool allow_vertical_autoscroll () const {
return false;
}
bool y_movement_matters () const {
return true;
}
2020-10-15 01:09:22 -04:00
void setup_pointer_offset ();
private:
TempoMarker* _marker;
Temporal::TempoPoint const * _real_section;
Temporal::TempoMap::WritableSharedPtr map;
bool _movable;
double _grab_bpm;
Temporal::Beats _grab_qn;
XMLNode* _before_state;
};
2022-08-16 18:46:38 -04:00
/** Tempo marker drag */
class BBTMarkerDrag : public Drag
{
public:
BBTMarkerDrag (Editor *, ArdourCanvas::Item *);
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
void motion (GdkEvent *, bool);
void finished (GdkEvent *, bool);
void aborted (bool);
bool allow_vertical_autoscroll () const {
return false;
}
bool y_movement_matters () const {
return false;
}
void setup_pointer_offset ();
private:
BBTMarker* _marker;
Temporal::MusicTimePoint const * _point;
Temporal::TempoMap::WritableSharedPtr map;
XMLNode* _before_state;
};
class MappingEndDrag : public Drag
{
public:
MappingEndDrag (Editor *, ArdourCanvas::Item *, Temporal::TempoMap::WritableSharedPtr&, Temporal::TempoPoint&, Temporal::TempoPoint& after, XMLNode& before_state);
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
void motion (GdkEvent *, bool);
void finished (GdkEvent *, bool);
void aborted (bool);
bool allow_vertical_autoscroll () const {
return false;
}
bool allow_horizontal_autoscroll () const {
return false;
}
bool y_movement_matters () const {
return false;
}
void setup_pointer_offset ();
private:
Temporal::TempoPoint& _tempo;
Temporal::TempoPoint& _after;
double _grab_bpm;
Temporal::TempoMap::WritableSharedPtr map;
2022-08-16 18:46:38 -04:00
XMLNode* _before_state;
bool _drag_valid;
};
class MappingTwistDrag : public Drag
{
public:
MappingTwistDrag (Editor *, ArdourCanvas::Item *, Temporal::TempoMap::WritableSharedPtr&,
Temporal::TempoPoint& prev,
Temporal::TempoPoint& focus,
Temporal::TempoPoint& next,
XMLNode&,
bool ramped);
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
void motion (GdkEvent *, bool);
void finished (GdkEvent *, bool);
void aborted (bool);
bool allow_vertical_autoscroll () const {
return false;
}
bool allow_horizontal_autoscroll () const {
return false;
}
bool y_movement_matters () const {
return false;
}
void setup_pointer_offset ();
private:
Temporal::TempoPoint& prev;
Temporal::TempoPoint& focus;
Temporal::TempoPoint& next;
Temporal::TempoMap::WritableSharedPtr map;
double direction;
double delta;
double initial_focus_npm;
double initial_pre_npm;
XMLNode* _before_state;
bool _drag_valid;
bool _do_ramp;
};
/** tempo curve twist drag */
class TempoTwistDrag : public Drag
{
public:
TempoTwistDrag (Editor *, ArdourCanvas::Item *);
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
void motion (GdkEvent *, bool);
void finished (GdkEvent *, bool);
void aborted (bool);
bool allow_vertical_autoscroll () const {
return false;
}
bool y_movement_matters () const {
return true;
}
2020-10-15 01:09:22 -04:00
void setup_pointer_offset ();
private:
Temporal::Beats _grab_qn;
Temporal::TempoPoint* _tempo;
Temporal::TempoPoint const * _next_tempo;
Temporal::TempoMap::WritableSharedPtr map;
bool _drag_valid;
XMLNode* _before_state;
};
/** tempo curve twist drag */
class TempoEndDrag : public Drag
{
public:
TempoEndDrag (Editor *, ArdourCanvas::Item *);
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
void motion (GdkEvent *, bool);
void finished (GdkEvent *, bool);
void aborted (bool);
bool allow_vertical_autoscroll () const {
return false;
}
bool y_movement_matters () const {
return true;
}
2020-10-15 01:09:22 -04:00
void setup_pointer_offset ();
private:
Temporal::Beats _grab_qn;
Temporal::TempoPoint * _tempo;
Temporal::TempoPoint * previous_tempo;
Temporal::TempoMap::WritableSharedPtr map;
XMLNode* _before_state;
bool _drag_valid;
};
/** Drag of the playhead cursor */
class CursorDrag : public Drag
{
public:
CursorDrag (Editor *, EditorCursor&, bool);
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
void motion (GdkEvent *, bool);
void finished (GdkEvent *, bool);
void aborted (bool);
bool allow_vertical_autoscroll () const {
return false;
}
bool y_movement_matters () const {
return true;
}
private:
void fake_locate (samplepos_t);
2017-07-01 12:42:24 -04:00
EditorCursor& _cursor;
bool _stop; ///< true to stop the transport on starting the drag, otherwise false
double _grab_zoom; ///< editor samples per unit when our grab started
//used for zooming
int _last_mx;
int _last_my;
int _last_dx;
int _last_y_delta;
};
/** Region fade-in drag */
class FadeInDrag : public RegionDrag
{
public:
FadeInDrag (Editor *, ArdourCanvas::Item *, RegionView *, std::list<RegionView*> const &, Temporal::TimeDomain);
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
void motion (GdkEvent *, bool);
void finished (GdkEvent *, bool);
void aborted (bool);
bool y_movement_matters () const {
return false;
}
bool allow_vertical_autoscroll () const {
return false;
}
2020-10-15 01:09:22 -04:00
void setup_pointer_offset ();
};
/** Region fade-out drag */
class FadeOutDrag : public RegionDrag
{
public:
FadeOutDrag (Editor *, ArdourCanvas::Item *, RegionView *, std::list<RegionView*> const &, Temporal::TimeDomain td);
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
void motion (GdkEvent *, bool);
void finished (GdkEvent *, bool);
void aborted (bool);
bool y_movement_matters () const {
return false;
}
bool allow_vertical_autoscroll () const {
return false;
}
2020-10-15 01:09:22 -04:00
void setup_pointer_offset ();
};
/** Marker drag */
class MarkerDrag : public Drag
{
public:
2017-07-01 12:42:24 -04:00
MarkerDrag (Editor *, ArdourCanvas::Item *);
~MarkerDrag ();
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
void motion (GdkEvent *, bool);
void finished (GdkEvent *, bool);
void aborted (bool);
bool allow_vertical_autoscroll () const {
return false;
}
bool y_movement_matters () const {
return false;
}
2020-10-15 01:09:22 -04:00
void setup_pointer_offset ();
void setup_video_offset ();
private:
void update_item (ARDOUR::Location *);
ArdourMarker* _marker; ///< marker being dragged
bool _selection_changed;
2017-07-01 12:42:24 -04:00
struct CopiedLocationMarkerInfo {
ARDOUR::Location* location;
std::vector<ArdourMarker*> markers;
bool move_both;
CopiedLocationMarkerInfo (ARDOUR::Location* l, ArdourMarker* m);
};
std::vector<ARDOUR::Location::ChangeSuspender> lcs;
2017-07-01 12:42:24 -04:00
typedef std::list<CopiedLocationMarkerInfo> CopiedLocationInfo;
CopiedLocationInfo _copied_locations;
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 *, bool);
void finished (GdkEvent *, bool);
void aborted (bool);
bool active (Editing::MouseMode m);
bool allow_vertical_autoscroll () const {
return false;
}
private:
ControlPoint* _point;
double _fixed_grab_x;
double _fixed_grab_y;
double _cumulative_y_drag;
2017-07-01 12:42:24 -04:00
bool _pushing;
uint32_t _final_index;
static double _zero_gain_fraction;
Temporal::timecnt_t total_dt (GdkEvent*) const;
};
/** Gain or automation line drag */
class LineDrag : public Drag
{
public:
LineDrag (Editor *e, ArdourCanvas::Item *i);
~LineDrag ();
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
void motion (GdkEvent *, bool);
void finished (GdkEvent *, bool);
void aborted (bool);
bool allow_vertical_autoscroll () const {
return false;
}
private:
AutomationLine* _line;
double _fixed_grab_x;
double _fixed_grab_y;
double _cumulative_y_drag;
uint32_t _before;
uint32_t _after;
bool have_command;
};
/** Transient feature line drags*/
class FeatureLineDrag : public Drag
{
public:
FeatureLineDrag (Editor *e, ArdourCanvas::Item *i);
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
void motion (GdkEvent *, bool);
void finished (GdkEvent *, bool);
void aborted (bool);
private:
ArdourCanvas::Line* _line;
AudioRegionView* _arv;
double _region_view_grab_x;
double _cumulative_x_drag;
float _before;
uint32_t _max_x;
};
/** Dragging of a rubberband rectangle for selecting things */
class RubberbandSelectDrag : public Drag
{
public:
RubberbandSelectDrag (Editor *, ArdourCanvas::Item *);
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
void motion (GdkEvent *, bool);
void finished (GdkEvent *, bool);
void aborted (bool);
2020-10-15 01:09:22 -04:00
std::pair<Temporal::timecnt_t,int> move_threshold () const {
return std::make_pair (Temporal::timecnt_t (8, Temporal::AudioTime), 1);
}
void do_select_things (GdkEvent *, bool);
/** Select some things within a rectangle.
* @param button_state The button state from the GdkEvent.
2020-10-15 01:09:22 -04:00
* @param x1 The left-hand side of the rectangle as a timepos_t
* @param x2 The right-hand side of the rectangle as a timepos_t.
* @param y1 The top of the rectangle in trackview coordinates.
* @param y2 The bottom of the rectangle in trackview coordinates.
* @param drag_in_progress true if the drag is currently happening.
*/
2020-10-15 01:09:22 -04:00
virtual void select_things (int button_state, Temporal::timepos_t const & x1, Temporal::timepos_t const & x2, double y1, double y2, bool drag_in_progress) = 0;
2015-10-05 10:17:49 -04:00
virtual void deselect_things () = 0;
protected:
bool _vertical_only;
};
/** A general editor RubberbandSelectDrag (for regions, automation points etc.) */
class EditorRubberbandSelectDrag : public RubberbandSelectDrag
{
public:
EditorRubberbandSelectDrag (Editor *, ArdourCanvas::Item *);
2020-10-15 01:09:22 -04:00
void select_things (int, Temporal::timepos_t const &, Temporal::timepos_t const &, double, double, bool);
void deselect_things ();
};
/** A RubberbandSelectDrag for selecting MIDI notes */
class MidiRubberbandSelectDrag : public RubberbandSelectDrag
{
public:
MidiRubberbandSelectDrag (Editor *, MidiRegionView *);
2020-10-15 01:09:22 -04:00
void select_things (int, Temporal::timepos_t const &, Temporal::timepos_t const &, double, double, bool);
void deselect_things ();
private:
MidiRegionView* _region_view;
};
/** A RubberbandSelectDrag for selecting MIDI notes but with no horizontal component */
class MidiVerticalSelectDrag : public RubberbandSelectDrag
{
public:
MidiVerticalSelectDrag (Editor *, MidiRegionView *);
2020-10-15 01:09:22 -04:00
void select_things (int, Temporal::timepos_t const &, Temporal::timepos_t const &, double, double, bool);
void deselect_things ();
private:
MidiRegionView* _region_view;
};
/** Region drag in time-FX mode */
class TimeFXDrag : public RegionDrag
{
public:
TimeFXDrag (Editor *, ArdourCanvas::Item *, RegionView *, std::list<RegionView*> const &, Temporal::TimeDomain td);
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
void motion (GdkEvent *, bool);
void finished (GdkEvent *, bool);
void aborted (bool);
private:
bool _dragging_start;
};
/** Drag in range select mode */
class SelectionDrag : public Drag
{
public:
enum Operation {
CreateSelection,
SelectionStartTrim,
SelectionEndTrim,
SelectionMove,
SelectionExtend
};
SelectionDrag (Editor *, ArdourCanvas::Item *, Operation);
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
void motion (GdkEvent *, bool);
void finished (GdkEvent *, bool);
void aborted (bool);
2020-10-15 01:09:22 -04:00
void setup_pointer_offset ();
private:
Operation _operation;
bool _add;
TrackSelection _track_selection_at_start;
bool _time_selection_at_start;
2020-10-15 01:09:22 -04:00
Temporal::timepos_t start_at_start;
Temporal::timepos_t end_at_start;
};
2023-06-01 15:34:50 -04:00
/** Drag time-selection markers */
class SelectionMarkerDrag : public Drag
{
public:
SelectionMarkerDrag (Editor*, ArdourCanvas::Item*);
void start_grab (GdkEvent*, Gdk::Cursor* c = 0);
void motion (GdkEvent*, bool);
void finished (GdkEvent *, bool);
void aborted (bool);
private:
bool _edit_start;
Temporal::timepos_t _start_at_start;
Temporal::timepos_t _end_at_start;
};
/** Range marker drag */
class RangeMarkerBarDrag : public Drag
{
public:
enum Operation {
CreateSkipMarker,
CreateRangeMarker,
CreateTransportMarker,
CreateCDMarker
};
RangeMarkerBarDrag (Editor *, ArdourCanvas::Item *, Operation);
~RangeMarkerBarDrag ();
2015-10-05 10:17:49 -04:00
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
void motion (GdkEvent *, bool);
void finished (GdkEvent *, bool);
void aborted (bool);
bool allow_vertical_autoscroll () const {
return false;
}
bool y_movement_matters () const {
return false;
}
private:
void update_item (ARDOUR::Location *);
Operation _operation;
ArdourCanvas::Rectangle* _drag_rect;
bool _copy;
};
/** Drag of rectangle to set zoom */
class MouseZoomDrag : public Drag
{
public:
MouseZoomDrag (Editor *, ArdourCanvas::Item *);
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
void motion (GdkEvent *, bool);
void finished (GdkEvent *, bool);
void aborted (bool);
2020-10-15 01:09:22 -04:00
std::pair<Temporal::timecnt_t,int> move_threshold () const {
return std::make_pair (Temporal::timecnt_t (4, Temporal::AudioTime), 4);
}
private:
bool _zoom_out;
};
/** Drag of a range of automation data (either on an automation track or region gain),
* changing value but not position.
*/
class AutomationRangeDrag : public Drag
{
public:
AutomationRangeDrag (Editor *, AutomationTimeAxisView *, float initial_value, std::list<ARDOUR::TimelineRange> const &);
AutomationRangeDrag (Editor *, std::list<RegionView*> const &, std::list<ARDOUR::TimelineRange> const &, double y_origin, double y_height);
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
void motion (GdkEvent *, bool);
void finished (GdkEvent *, bool);
void aborted (bool);
bool x_movement_matters () const {
return false;
}
private:
void setup (std::list<std::shared_ptr<AutomationLine> > const &);
double y_fraction (double global_y_position) const;
double value (std::shared_ptr<ARDOUR::AutomationList> list, Temporal::timepos_t const & x) const;
std::list<ARDOUR::TimelineRange> _ranges;
/** A line that is part of the drag */
struct Line {
std::shared_ptr<AutomationLine> line; ///< the line
std::list<ControlPoint*> points; ///< points to drag on the line
2020-10-15 01:09:22 -04:00
std::pair<Temporal::timepos_t, Temporal::timepos_t> range; ///< the range of all points on the line, in session time
XMLNode* state; ///< the XML state node before the drag
};
std::list<Line> _lines;
double _y_origin;
double _y_height;
bool _nothing_to_drag;
bool _integral;
float_t _initial_value;
};
/** Drag of one edge of an xfade
*/
class CrossfadeEdgeDrag : public Drag
{
public:
CrossfadeEdgeDrag (Editor*, AudioRegionView*, ArdourCanvas::Item*, bool start);
void start_grab (GdkEvent*, Gdk::Cursor* c = 0);
void motion (GdkEvent*, bool);
void finished (GdkEvent*, bool);
void aborted (bool);
2015-10-05 10:17:49 -04:00
bool y_movement_matters () const {
return false;
}
2020-10-15 01:09:22 -04:00
std::pair<Temporal::timecnt_t,int> move_threshold () const {
return std::make_pair (Temporal::timecnt_t (4, Temporal::AudioTime), 4);
}
private:
AudioRegionView* arv;
bool start;
};
2021-05-24 11:12:19 -04:00
class RegionMarkerDrag : public Drag
{
public:
RegionMarkerDrag (Editor*, RegionView*, ArdourCanvas::Item*);
~RegionMarkerDrag ();
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
void motion (GdkEvent *, bool);
void finished (GdkEvent *, bool);
void aborted (bool);
bool allow_vertical_autoscroll () const {
return false;
}
bool y_movement_matters () const {
return false;
}
void setup_pointer_offset ();
2021-05-24 11:12:19 -04:00
private:
RegionView* rv;
ArdourMarker* view;
ARDOUR::CueMarker model;
ARDOUR::CueMarker dragging_model;
2021-05-24 11:12:19 -04:00
};
2023-06-16 14:31:18 -04:00
class LollipopDrag : public Drag
{
public:
2023-06-20 13:28:59 -04:00
LollipopDrag (Editor*, ArdourCanvas::Item*);
2023-06-16 14:31:18 -04:00
~LollipopDrag ();
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
void motion (GdkEvent *, bool);
void finished (GdkEvent *, bool);
void aborted (bool);
bool allow_vertical_autoscroll () const {
return false;
}
bool allow_horizontal_autoscroll () const {
return false;
}
bool x_movement_matters () const {
return false;
}
void setup_pointer_offset ();
2023-06-16 14:31:18 -04:00
private:
2023-06-20 13:28:59 -04:00
VelocityGhostRegion* _region;
ArdourCanvas::Lollipop* _primary;
2023-06-16 14:31:18 -04:00
};
template<typename OrderedPointList, typename OrderedPoint>
class FreehandLineDrag : public Drag
{
public:
FreehandLineDrag (Editor*, ArdourCanvas::Item*, ArdourCanvas::Rectangle&, Temporal::TimeDomain);
~FreehandLineDrag ();
void motion (GdkEvent*, bool);
void finished (GdkEvent*, bool);
bool mid_drag_key_event (GdkEventKey*);
virtual void point_added (ArdourCanvas::Duple const & d, ArdourCanvas::Rectangle const & r, double last_x) {}
virtual void line_extended (ArdourCanvas::Duple const & from, ArdourCanvas::Duple const & to, ArdourCanvas::Rectangle const & r, double last_x) {
point_added (to, r, last_x);
}
protected:
ArdourCanvas::Item* parent; /* we do not own this. If null, use base_rect as the parent */
ArdourCanvas::Rectangle& base_rect; /* we do not own this */
ArdourCanvas::PolyLine* dragging_line;
int direction;
int edge_x;
bool did_snap;
bool line_break_pending;
OrderedPointList drawn_points;
ArdourCanvas::Coord line_start_x;
ArdourCanvas::Coord line_start_y;
void maybe_add_point (GdkEvent*, Temporal::timepos_t const &, bool first_move);
};
class AutomationDrawDrag : public FreehandLineDrag<Evoral::ControlList::OrderedPoints, Evoral::ControlList::OrderedPoint>
{
public:
AutomationDrawDrag (Editor*, ArdourCanvas::Item*, ArdourCanvas::Rectangle&, Temporal::TimeDomain);
~AutomationDrawDrag ();
void finished (GdkEvent*, bool);
void aborted (bool) {}
};
class VelocityLineDrag : public FreehandLineDrag<Evoral::ControlList::OrderedPoints, Evoral::ControlList::OrderedPoint>
{
public:
VelocityLineDrag (Editor*, ArdourCanvas::Rectangle&, Temporal::TimeDomain);
~VelocityLineDrag ();
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
void finished (GdkEvent*, bool);
void aborted (bool);
void point_added (ArdourCanvas::Duple const & d, ArdourCanvas::Rectangle const & r, double last_x);
void line_extended (ArdourCanvas::Duple const & from, ArdourCanvas::Duple const & to, ArdourCanvas::Rectangle const & r, double last_x);
private:
VelocityGhostRegion* grv;
bool drag_did_change;
};
#endif /* __gtk2_ardour_editor_drag_h_ */