2014-06-03 16:37:53 -04:00
|
|
|
/*
|
2019-08-02 17:26:43 -04:00
|
|
|
* 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) 2012-2019 Robin Gareus <robin@gareus.org>
|
|
|
|
* Copyright (C) 2013-2017 Nick Mainsbridge <mainsbridge@gmail.com>
|
|
|
|
* Copyright (C) 2013 Michael Fisher <mfisher31@gmail.com>
|
|
|
|
* Copyright (C) 2014-2019 Ben Loftis <ben@harrisonconsoles.com>
|
|
|
|
* Copyright (C) 2014 Colin Fletcher <colin.m.fletcher@googlemail.com>
|
|
|
|
* Copyright (C) 2015-2017 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.
|
|
|
|
*/
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2010-11-13 00:14:48 -05:00
|
|
|
#ifdef WAF_BUILD
|
|
|
|
#include "gtk2ardour-config.h"
|
|
|
|
#endif
|
|
|
|
|
2010-01-15 17:47:56 -05:00
|
|
|
#include <stdint.h>
|
2013-03-27 13:45:08 -04:00
|
|
|
#include <algorithm>
|
2010-09-26 09:33:39 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
#include "pbd/memento_command.h"
|
|
|
|
#include "pbd/basename.h"
|
2010-02-09 09:44:01 -05:00
|
|
|
#include "pbd/stateful_diff_command.h"
|
2010-09-26 09:33:39 -04:00
|
|
|
|
2021-05-24 22:22:32 -04:00
|
|
|
#include <gtkmm/stock.h>
|
|
|
|
|
2010-09-26 09:33:39 -04:00
|
|
|
#include "gtkmm2ext/utils.h"
|
|
|
|
|
2012-07-10 00:05:48 -04:00
|
|
|
#include "ardour/audioengine.h"
|
2011-10-19 17:53:09 -04:00
|
|
|
#include "ardour/audioregion.h"
|
2014-05-17 15:51:08 -04:00
|
|
|
#include "ardour/audio_track.h"
|
2009-05-30 14:25:59 -04:00
|
|
|
#include "ardour/dB.h"
|
2011-10-19 17:53:09 -04:00
|
|
|
#include "ardour/midi_region.h"
|
2014-05-17 15:51:08 -04:00
|
|
|
#include "ardour/midi_track.h"
|
2011-01-19 12:38:56 -05:00
|
|
|
#include "ardour/operations.h"
|
2017-01-20 15:47:09 -05:00
|
|
|
#include "ardour/profile.h"
|
2011-10-19 17:53:09 -04:00
|
|
|
#include "ardour/region_factory.h"
|
|
|
|
#include "ardour/session.h"
|
2017-03-05 09:01:37 -05:00
|
|
|
#include "ardour/session_playlists.h"
|
2011-01-19 12:38:56 -05:00
|
|
|
|
2014-07-08 21:37:49 -04:00
|
|
|
#include "canvas/canvas.h"
|
2014-06-03 15:57:56 -04:00
|
|
|
#include "canvas/scroll_group.h"
|
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
#include "editor.h"
|
2016-07-14 14:44:52 -04:00
|
|
|
#include "pbd/i18n.h"
|
2021-02-13 12:39:32 -05:00
|
|
|
#include "bbt_marker_dialog.h"
|
2009-05-30 14:25:59 -04:00
|
|
|
#include "keyboard.h"
|
|
|
|
#include "audio_region_view.h"
|
2015-01-13 23:30:37 -05:00
|
|
|
#include "automation_region_view.h"
|
2009-05-30 14:25:59 -04:00
|
|
|
#include "midi_region_view.h"
|
|
|
|
#include "ardour_ui.h"
|
2009-12-21 13:23:07 -05:00
|
|
|
#include "gui_thread.h"
|
2009-05-30 14:25:59 -04:00
|
|
|
#include "control_point.h"
|
|
|
|
#include "region_gain_line.h"
|
|
|
|
#include "editor_drag.h"
|
2009-06-08 15:28:51 -04:00
|
|
|
#include "audio_time_axis.h"
|
|
|
|
#include "midi_time_axis.h"
|
2009-09-08 22:09:04 -04:00
|
|
|
#include "selection.h"
|
|
|
|
#include "midi_selection.h"
|
2010-01-01 17:11:15 -05:00
|
|
|
#include "automation_time_axis.h"
|
2010-09-09 17:34:45 -04:00
|
|
|
#include "debug.h"
|
2010-11-16 09:53:16 -05:00
|
|
|
#include "editor_cursors.h"
|
|
|
|
#include "mouse_cursors.h"
|
2013-04-04 00:32:52 -04:00
|
|
|
#include "note_base.h"
|
|
|
|
#include "patch_change.h"
|
2015-01-02 09:44:54 -05:00
|
|
|
#include "ui_config.h"
|
2011-05-02 09:38:16 -04:00
|
|
|
#include "verbose_cursor.h"
|
2019-11-08 23:03:38 -05:00
|
|
|
#include "video_timeline.h"
|
2009-05-30 14:25:59 -04:00
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
using namespace ARDOUR;
|
|
|
|
using namespace PBD;
|
|
|
|
using namespace Gtk;
|
2010-09-26 09:33:39 -04:00
|
|
|
using namespace Gtkmm2ext;
|
2009-05-30 14:25:59 -04:00
|
|
|
using namespace Editing;
|
2009-10-06 18:07:10 -04:00
|
|
|
using namespace ArdourCanvas;
|
2020-10-15 01:09:22 -04:00
|
|
|
using namespace Temporal;
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2009-12-04 17:51:32 -05:00
|
|
|
using Gtkmm2ext::Keyboard;
|
|
|
|
|
2011-06-19 19:02:55 -04:00
|
|
|
double ControlPointDrag::_zero_gain_fraction = -1.0;
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2010-01-12 11:14:49 -05:00
|
|
|
DragManager::DragManager (Editor* e)
|
|
|
|
: _editor (e)
|
|
|
|
, _ending (false)
|
2015-10-23 13:59:57 -04:00
|
|
|
, _current_pointer_x (0.0)
|
|
|
|
, _current_pointer_y (0.0)
|
2020-10-15 01:09:22 -04:00
|
|
|
, _current_pointer_time (0)
|
2015-10-23 13:59:57 -04:00
|
|
|
, _old_follow_playhead (false)
|
2010-01-12 11:14:49 -05:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
DragManager::~DragManager ()
|
|
|
|
{
|
|
|
|
abort ();
|
|
|
|
}
|
|
|
|
|
2010-05-05 18:09:07 -04:00
|
|
|
/** Call abort for each active drag */
|
2010-01-12 11:14:49 -05:00
|
|
|
void
|
|
|
|
DragManager::abort ()
|
|
|
|
{
|
|
|
|
_ending = true;
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-01-12 11:14:49 -05:00
|
|
|
for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
|
2010-05-05 18:09:07 -04:00
|
|
|
(*i)->abort ();
|
2010-01-12 11:14:49 -05:00
|
|
|
delete *i;
|
|
|
|
}
|
|
|
|
|
2011-05-17 11:28:12 -04:00
|
|
|
if (!_drags.empty ()) {
|
|
|
|
_editor->set_follow_playhead (_old_follow_playhead, false);
|
|
|
|
}
|
2010-01-12 11:14:49 -05:00
|
|
|
|
2011-05-17 11:28:12 -04:00
|
|
|
_drags.clear ();
|
2015-02-05 07:32:57 -05:00
|
|
|
_editor->abort_reversible_command();
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-01-12 11:14:49 -05:00
|
|
|
_ending = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
DragManager::add (Drag* d)
|
|
|
|
{
|
|
|
|
d->set_manager (this);
|
|
|
|
_drags.push_back (d);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
|
|
|
|
{
|
|
|
|
d->set_manager (this);
|
|
|
|
_drags.push_back (d);
|
2010-08-18 14:06:31 -04:00
|
|
|
start_grab (e, c);
|
2010-01-12 11:14:49 -05:00
|
|
|
}
|
|
|
|
|
2019-11-08 23:03:38 -05:00
|
|
|
bool
|
|
|
|
DragManager::preview_video () const {
|
|
|
|
for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
|
|
|
|
if ((*i)->preview_video ()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-01-12 11:14:49 -05:00
|
|
|
void
|
2010-08-18 14:06:31 -04:00
|
|
|
DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
|
2010-01-12 11:14:49 -05:00
|
|
|
{
|
2011-01-04 21:28:28 -05:00
|
|
|
/* Prevent follow playhead during the drag to be nice to the user */
|
|
|
|
_old_follow_playhead = _editor->follow_playhead ();
|
|
|
|
_editor->set_follow_playhead (false);
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
_current_pointer_time = timepos_t (_editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y));
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-01-12 11:14:49 -05:00
|
|
|
for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
|
2021-01-08 00:55:31 -05:00
|
|
|
if ((*i)->grab_button() < 0) {
|
|
|
|
(*i)->start_grab (e, c);
|
|
|
|
}
|
2010-01-12 11:14:49 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-05 18:09:07 -04:00
|
|
|
/** Call end_grab for each active drag.
|
|
|
|
* @return true if any drag reported movement having occurred.
|
|
|
|
*/
|
2010-01-12 11:14:49 -05:00
|
|
|
bool
|
|
|
|
DragManager::end_grab (GdkEvent* e)
|
|
|
|
{
|
|
|
|
_ending = true;
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-01-12 11:14:49 -05:00
|
|
|
bool r = false;
|
2021-01-08 00:55:31 -05:00
|
|
|
|
|
|
|
for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ) {
|
|
|
|
list<Drag*>::iterator tmp = i;
|
|
|
|
|
2021-05-06 14:03:27 -04:00
|
|
|
if ((*i)->grab_button() == (int) e->button.button) {
|
2021-01-08 00:55:31 -05:00
|
|
|
bool const t = (*i)->end_grab (e);
|
|
|
|
if (t) {
|
|
|
|
r = true;
|
|
|
|
}
|
|
|
|
delete *i;
|
|
|
|
tmp = _drags.erase (i);
|
|
|
|
} else {
|
|
|
|
++tmp;
|
2010-01-12 11:14:49 -05:00
|
|
|
}
|
|
|
|
|
2021-01-08 00:55:31 -05:00
|
|
|
i = tmp;
|
|
|
|
}
|
2010-01-12 11:14:49 -05:00
|
|
|
|
|
|
|
_ending = false;
|
2011-01-04 21:28:28 -05:00
|
|
|
|
2021-01-08 00:55:31 -05:00
|
|
|
if (_drags.empty()) {
|
|
|
|
_editor->set_follow_playhead (_old_follow_playhead, false);
|
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-01-12 11:14:49 -05:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2013-06-21 15:18:54 -04:00
|
|
|
void
|
|
|
|
DragManager::mark_double_click ()
|
|
|
|
{
|
|
|
|
for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
|
|
|
|
(*i)->set_double_click (true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-01-12 11:14:49 -05:00
|
|
|
bool
|
|
|
|
DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
|
|
|
|
{
|
|
|
|
bool r = false;
|
|
|
|
|
2014-06-17 08:08:07 -04:00
|
|
|
/* calling this implies that we expect the event to have canvas
|
2015-03-24 16:59:57 -04:00
|
|
|
* coordinates
|
2014-06-17 08:08:07 -04:00
|
|
|
*
|
|
|
|
* Can we guarantee that this is true?
|
|
|
|
*/
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
_current_pointer_time = timepos_t (_editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y));
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-01-12 11:14:49 -05:00
|
|
|
for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
|
|
|
|
bool const t = (*i)->motion_handler (e, from_autoscroll);
|
2014-06-06 08:32:35 -04:00
|
|
|
/* run all handlers; return true if at least one of them
|
|
|
|
returns true (indicating that the event has been handled).
|
|
|
|
*/
|
2010-01-12 11:14:49 -05:00
|
|
|
if (t) {
|
|
|
|
r = true;
|
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-01-12 11:14:49 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
DragManager::have_item (ArdourCanvas::Item* i) const
|
|
|
|
{
|
|
|
|
list<Drag*>::const_iterator j = _drags.begin ();
|
|
|
|
while (j != _drags.end() && (*j)->item () != i) {
|
|
|
|
++j;
|
|
|
|
}
|
|
|
|
|
|
|
|
return j != _drags.end ();
|
|
|
|
}
|
|
|
|
|
2014-06-08 11:26:25 -04:00
|
|
|
Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
|
2009-11-08 11:28:21 -05:00
|
|
|
: _editor (e)
|
2015-10-23 13:59:57 -04:00
|
|
|
, _drags (0)
|
2009-11-08 11:28:21 -05:00
|
|
|
, _item (i)
|
2020-10-15 01:09:22 -04:00
|
|
|
, _pointer_offset (0)
|
|
|
|
, _video_offset (0)
|
2019-11-08 23:03:38 -05:00
|
|
|
, _preview_video (false)
|
2015-05-23 08:59:19 -04:00
|
|
|
, _x_constrained (false)
|
|
|
|
, _y_constrained (false)
|
2015-10-23 13:59:57 -04:00
|
|
|
, _was_rolling (false)
|
2021-06-18 14:45:09 -04:00
|
|
|
, _earliest_time_limit (0)
|
2014-06-08 11:26:25 -04:00
|
|
|
, _trackview_only (trackview_only)
|
2009-11-08 11:28:21 -05:00
|
|
|
, _move_threshold_passed (false)
|
2015-02-12 13:37:18 -05:00
|
|
|
, _starting_point_passed (false)
|
|
|
|
, _initially_vertical (false)
|
2013-06-21 15:18:54 -04:00
|
|
|
, _was_double_click (false)
|
2015-10-23 13:59:57 -04:00
|
|
|
, _grab_x (0.0)
|
|
|
|
, _grab_y (0.0)
|
|
|
|
, _last_pointer_x (0.0)
|
|
|
|
, _last_pointer_y (0.0)
|
2017-01-26 12:37:32 -05:00
|
|
|
, _snap_delta (0)
|
2016-08-13 11:50:51 -04:00
|
|
|
, _constraint_pressed (false)
|
2021-01-08 00:55:31 -05:00
|
|
|
, _grab_button (-1)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2013-04-04 18:45:27 -04:00
|
|
|
Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2013-04-04 00:32:52 -04:00
|
|
|
_item->ungrab ();
|
2009-05-30 14:25:59 -04:00
|
|
|
_item = new_item;
|
|
|
|
|
2015-01-23 15:15:29 -05:00
|
|
|
if (!_cursor_ctx) {
|
|
|
|
_cursor_ctx = CursorContext::create (*_editor, cursor);
|
2012-01-19 17:23:28 -05:00
|
|
|
} else {
|
2015-01-23 15:15:29 -05:00
|
|
|
_cursor_ctx->change (cursor);
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
2015-01-23 15:15:29 -05:00
|
|
|
|
|
|
|
_item->grab ();
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
|
|
|
|
{
|
2015-05-17 11:43:44 -04:00
|
|
|
/* we set up x/y dragging constraints on first move */
|
2016-08-13 11:50:51 -04:00
|
|
|
_constraint_pressed = ArdourKeyboard::indicates_constraint (event->button.state);
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2021-02-14 00:23:10 -05:00
|
|
|
const samplepos_t pos = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
|
|
|
|
|
|
|
|
if (_editor->default_time_domain() == Temporal::AudioTime) {
|
|
|
|
_raw_grab_time = timepos_t (pos);
|
|
|
|
} else {
|
|
|
|
_raw_grab_time = timepos_t (timepos_t (pos).beats());
|
|
|
|
}
|
|
|
|
|
2021-01-08 00:55:31 -05:00
|
|
|
_grab_button = event->button.button;
|
2015-05-15 14:15:52 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
setup_pointer_offset ();
|
|
|
|
setup_video_offset ();
|
2019-11-08 23:03:38 -05:00
|
|
|
if (! UIConfiguration::instance ().get_preview_video_frame_on_drag ()) {
|
|
|
|
_preview_video = false;
|
|
|
|
}
|
2021-01-31 20:32:03 -05:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
_grab_time = adjusted_time (_raw_grab_time, event);
|
|
|
|
_last_pointer_time = _grab_time;
|
2010-01-12 11:14:49 -05:00
|
|
|
_last_pointer_x = _grab_x;
|
2014-06-08 11:26:25 -04:00
|
|
|
|
|
|
|
if (_trackview_only) {
|
|
|
|
_grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
|
|
|
|
}
|
|
|
|
|
2010-01-12 11:14:49 -05:00
|
|
|
_last_pointer_y = _grab_y;
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2015-01-23 15:15:29 -05:00
|
|
|
_item->grab ();
|
|
|
|
|
|
|
|
if (!_editor->cursors()->is_invalid (cursor)) {
|
2013-04-04 00:32:52 -04:00
|
|
|
/* CAIROCANVAS need a variant here that passes *cursor */
|
2015-01-23 15:15:29 -05:00
|
|
|
_cursor_ctx = CursorContext::create (*_editor, cursor);
|
2012-01-19 17:23:28 -05:00
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2009-12-17 13:24:23 -05:00
|
|
|
if (_editor->session() && _editor->session()->transport_rolling()) {
|
2009-05-30 14:25:59 -04:00
|
|
|
_was_rolling = true;
|
|
|
|
} else {
|
|
|
|
_was_rolling = false;
|
|
|
|
}
|
|
|
|
|
2018-02-09 10:59:39 -05:00
|
|
|
// if ( UIConfiguration::instance().get_snap_to_region_start() || UIConfiguration::instance().get_snap_to_region_end() || UIConfiguration::instance().get_snap_to_region_sync() ) {
|
|
|
|
// _editor->build_region_boundary_cache ();
|
|
|
|
// }
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2010-05-05 18:09:07 -04:00
|
|
|
/** Call to end a drag `successfully'. Ungrabs item and calls
|
|
|
|
* subclass' finished() method.
|
|
|
|
*
|
|
|
|
* @param event GDK event, or 0.
|
2009-06-01 20:39:57 -04:00
|
|
|
* @return true if some movement occurred, otherwise false.
|
|
|
|
*/
|
2009-05-30 14:25:59 -04:00
|
|
|
bool
|
|
|
|
Drag::end_grab (GdkEvent* event)
|
|
|
|
{
|
|
|
|
_editor->stop_canvas_autoscroll ();
|
|
|
|
|
2013-04-04 00:32:52 -04:00
|
|
|
_item->ungrab ();
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2010-01-04 20:10:53 -05:00
|
|
|
finished (event, _move_threshold_passed);
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2011-05-02 09:38:16 -04:00
|
|
|
_editor->verbose_cursor()->hide ();
|
2014-12-20 01:11:28 -05:00
|
|
|
_cursor_ctx.reset();
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2010-01-04 20:10:53 -05:00
|
|
|
return _move_threshold_passed;
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t
|
|
|
|
Drag::adjusted_time (timepos_t const & f, GdkEvent const * event, bool snap) const
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t pos (f);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
if (f > _pointer_offset) {
|
|
|
|
pos.shift_earlier (_pointer_offset);
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2009-12-21 20:12:41 -05:00
|
|
|
if (snap) {
|
|
|
|
_editor->snap_to_with_modifier (pos, event);
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-06-10 17:31:00 -04:00
|
|
|
return pos;
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t
|
|
|
|
Drag::adjusted_current_time (GdkEvent const * event, bool snap) const
|
2010-01-04 20:10:53 -05:00
|
|
|
{
|
2020-10-15 01:09:22 -04:00
|
|
|
return adjusted_time (_drags->current_pointer_time (), event, snap);
|
2010-01-04 20:10:53 -05:00
|
|
|
}
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
timecnt_t
|
2015-05-21 13:09:29 -04:00
|
|
|
Drag::snap_delta (guint state) const
|
2015-05-17 09:47:01 -04:00
|
|
|
{
|
2015-05-21 13:09:29 -04:00
|
|
|
if (ArdourKeyboard::indicates_snap_delta (state)) {
|
2017-01-26 12:37:32 -05:00
|
|
|
return _snap_delta;
|
2015-05-17 09:47:01 -04:00
|
|
|
}
|
2015-05-21 12:12:58 -04:00
|
|
|
|
2021-02-13 23:58:55 -05:00
|
|
|
return timecnt_t (_editor->default_time_domain());
|
2017-02-04 13:02:01 -05:00
|
|
|
}
|
2015-05-17 09:47:01 -04:00
|
|
|
|
2014-11-24 14:58:56 -05:00
|
|
|
double
|
|
|
|
Drag::current_pointer_x() const
|
|
|
|
{
|
2015-03-24 16:59:57 -04:00
|
|
|
return _drags->current_pointer_x ();
|
2014-11-24 14:58:56 -05:00
|
|
|
}
|
|
|
|
|
2014-06-08 11:26:25 -04:00
|
|
|
double
|
|
|
|
Drag::current_pointer_y () const
|
|
|
|
{
|
|
|
|
if (!_trackview_only) {
|
|
|
|
return _drags->current_pointer_y ();
|
|
|
|
}
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2014-06-08 11:26:25 -04:00
|
|
|
return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
|
|
|
|
}
|
|
|
|
|
2015-05-16 10:23:25 -04:00
|
|
|
void
|
2020-10-15 01:09:22 -04:00
|
|
|
Drag::setup_snap_delta (timepos_t const & pos)
|
2015-05-16 10:23:25 -04:00
|
|
|
{
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t snap (pos);
|
2020-10-01 12:32:06 -04:00
|
|
|
_editor->snap_to (snap, Temporal::RoundNearest, ARDOUR::SnapToAny_Visual, true);
|
2020-10-15 01:09:22 -04:00
|
|
|
_snap_delta = pos.distance (snap);
|
2015-05-16 10:23:25 -04:00
|
|
|
}
|
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
bool
|
|
|
|
Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
|
|
|
|
{
|
2010-01-05 10:51:13 -05:00
|
|
|
/* check to see if we have moved in any way that matters since the last motion event */
|
2010-12-21 20:06:18 -05:00
|
|
|
if (_move_threshold_passed &&
|
2014-11-24 14:58:56 -05:00
|
|
|
(!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
|
2014-06-08 11:26:25 -04:00
|
|
|
(!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
|
2010-01-05 10:51:13 -05:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
pair<timecnt_t, int> const threshold = move_threshold ();
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2010-01-04 20:10:53 -05:00
|
|
|
bool const old_move_threshold_passed = _move_threshold_passed;
|
2010-01-05 10:51:13 -05:00
|
|
|
|
2015-01-23 07:46:23 -05:00
|
|
|
if (!_move_threshold_passed) {
|
2009-06-01 20:39:57 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
bool const xp = (_raw_grab_time.distance (_drags->current_pointer_time ()).abs() >= threshold.first);
|
2014-06-08 11:26:25 -04:00
|
|
|
bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
|
2009-06-01 20:39:57 -04:00
|
|
|
|
2010-01-04 20:10:53 -05:00
|
|
|
_move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
|
2009-06-01 20:39:57 -04:00
|
|
|
}
|
|
|
|
|
2010-01-04 20:10:53 -05:00
|
|
|
if (active (_editor->mouse_mode) && _move_threshold_passed) {
|
2009-05-30 14:25:59 -04:00
|
|
|
|
|
|
|
if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
|
2015-02-12 13:37:18 -05:00
|
|
|
|
|
|
|
if (old_move_threshold_passed != _move_threshold_passed) {
|
|
|
|
|
|
|
|
/* just changed */
|
|
|
|
|
|
|
|
if (fabs (current_pointer_y() - _grab_y) > fabs (current_pointer_x() - _grab_x)) {
|
|
|
|
_initially_vertical = true;
|
|
|
|
} else {
|
|
|
|
_initially_vertical = false;
|
|
|
|
}
|
2015-05-23 09:19:00 -04:00
|
|
|
/** check constraints for this drag.
|
2015-10-04 14:51:05 -04:00
|
|
|
* Note that the current convention is to use "contains" for
|
2015-05-23 09:19:00 -04:00
|
|
|
* key modifiers during motion and "equals" when initiating a drag.
|
|
|
|
* In this case we haven't moved yet, so "equals" applies here.
|
|
|
|
*/
|
2015-05-22 15:36:03 -04:00
|
|
|
if (Config->get_edit_mode() != Lock) {
|
|
|
|
if (event->motion.state & Gdk::BUTTON2_MASK) {
|
|
|
|
// if dragging with button2, the motion is x constrained, with constraint modifier it is y constrained
|
2016-08-13 11:50:51 -04:00
|
|
|
if (_constraint_pressed) {
|
2015-05-22 15:36:03 -04:00
|
|
|
_x_constrained = false;
|
|
|
|
_y_constrained = true;
|
|
|
|
} else {
|
|
|
|
_x_constrained = true;
|
|
|
|
_y_constrained = false;
|
|
|
|
}
|
2016-08-19 11:58:56 -04:00
|
|
|
} else if (_constraint_pressed) {
|
2015-05-22 15:36:03 -04:00
|
|
|
// if dragging normally, the motion is constrained to the first direction of movement.
|
|
|
|
if (_initially_vertical) {
|
|
|
|
_x_constrained = true;
|
|
|
|
_y_constrained = false;
|
|
|
|
} else {
|
|
|
|
_x_constrained = false;
|
|
|
|
_y_constrained = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2015-05-18 13:27:50 -04:00
|
|
|
if (event->button.state & Gdk::BUTTON2_MASK) {
|
|
|
|
_x_constrained = false;
|
|
|
|
} else {
|
|
|
|
_x_constrained = true;
|
|
|
|
}
|
2015-05-17 16:25:36 -04:00
|
|
|
_y_constrained = false;
|
|
|
|
}
|
2015-02-12 13:37:18 -05:00
|
|
|
}
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
if (!from_autoscroll) {
|
2014-03-20 13:29:29 -04:00
|
|
|
_editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2014-03-20 13:29:29 -04:00
|
|
|
if (!_editor->autoscroll_active() || from_autoscroll) {
|
2015-02-12 13:37:18 -05:00
|
|
|
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2015-02-12 13:37:18 -05:00
|
|
|
bool first_move = (_move_threshold_passed != old_move_threshold_passed) || from_autoscroll;
|
|
|
|
|
2015-01-23 07:46:23 -05:00
|
|
|
motion (event, first_move && !_starting_point_passed);
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2015-02-05 03:57:56 -05:00
|
|
|
if (first_move && !_starting_point_passed) {
|
|
|
|
_starting_point_passed = true;
|
|
|
|
}
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2014-03-20 13:29:29 -04:00
|
|
|
_last_pointer_x = _drags->current_pointer_x ();
|
2014-06-08 11:26:25 -04:00
|
|
|
_last_pointer_y = current_pointer_y ();
|
2020-10-15 01:09:22 -04:00
|
|
|
_last_pointer_time = adjusted_current_time (event, false);
|
2014-03-20 13:29:29 -04:00
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2014-06-17 08:08:07 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-05-05 18:09:07 -04:00
|
|
|
/** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
|
2009-05-30 14:25:59 -04:00
|
|
|
void
|
2010-05-05 18:09:07 -04:00
|
|
|
Drag::abort ()
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
|
|
|
if (_item) {
|
2013-04-04 00:32:52 -04:00
|
|
|
_item->ungrab ();
|
2010-01-07 20:28:15 -05:00
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2010-12-28 14:17:37 -05:00
|
|
|
aborted (_move_threshold_passed);
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2010-01-07 20:28:15 -05:00
|
|
|
_editor->stop_canvas_autoscroll ();
|
2011-05-02 09:38:16 -04:00
|
|
|
_editor->verbose_cursor()->hide ();
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2011-05-02 09:38:16 -04:00
|
|
|
void
|
2020-10-15 01:09:22 -04:00
|
|
|
Drag::show_verbose_cursor_time (timepos_t const & pos)
|
2011-05-02 09:38:16 -04:00
|
|
|
{
|
2020-10-15 01:09:22 -04:00
|
|
|
_editor->verbose_cursor()->set_time (pos.samples());
|
2011-05-02 09:38:16 -04:00
|
|
|
_editor->verbose_cursor()->show ();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-10-15 01:09:22 -04:00
|
|
|
Drag::show_verbose_cursor_duration (timepos_t const & start, timepos_t const & end, double /*xoffset*/)
|
2011-05-02 09:38:16 -04:00
|
|
|
{
|
2020-10-15 01:09:22 -04:00
|
|
|
_editor->verbose_cursor()->set_duration (start.samples(), end.samples());
|
2014-06-26 15:07:29 -04:00
|
|
|
_editor->verbose_cursor()->show ();
|
2011-05-02 09:38:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Drag::show_verbose_cursor_text (string const & text)
|
|
|
|
{
|
2014-06-26 15:07:29 -04:00
|
|
|
_editor->verbose_cursor()->set (text);
|
2011-05-04 10:56:24 -04:00
|
|
|
_editor->verbose_cursor()->show ();
|
2011-05-02 09:38:16 -04:00
|
|
|
}
|
|
|
|
|
2019-11-08 23:03:38 -05:00
|
|
|
void
|
2020-10-15 01:09:22 -04:00
|
|
|
Drag::show_view_preview (timepos_t const & pos)
|
2019-11-08 23:03:38 -05:00
|
|
|
{
|
|
|
|
if (_preview_video) {
|
2020-10-15 01:09:22 -04:00
|
|
|
ARDOUR_UI::instance()->video_timeline->manual_seek_video_monitor (pos.samples());
|
2019-11-08 23:03:38 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-25 14:39:20 -05:00
|
|
|
boost::shared_ptr<Region>
|
2017-01-26 08:41:17 -05:00
|
|
|
Drag::add_midi_region (MidiTimeAxisView* view, bool commit)
|
2012-01-25 14:39:20 -05:00
|
|
|
{
|
|
|
|
if (_editor->session()) {
|
2020-10-15 01:09:22 -04:00
|
|
|
const timepos_t pos (grab_time());
|
2012-01-25 14:39:20 -05:00
|
|
|
/* not that the frame rate used here can be affected by pull up/down which
|
|
|
|
might be wrong.
|
|
|
|
*/
|
2021-03-13 11:21:29 -05:00
|
|
|
|
|
|
|
timepos_t beats = timepos_t (grab_time().beats());
|
|
|
|
cerr << "Adding region based on grab @ " << grab_time() << " = " << beats << " aka " << beats.beats() << endl;
|
2020-10-15 01:09:22 -04:00
|
|
|
timecnt_t len = pos.distance (max (timepos_t::zero (Temporal::BeatTime), timepos_t (pos.beats() + Beats (1, 0))));
|
2021-03-13 11:21:29 -05:00
|
|
|
return view->add_region (beats, len, commit);
|
2012-01-25 14:39:20 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return boost::shared_ptr<Region>();
|
|
|
|
}
|
2011-05-02 09:38:16 -04:00
|
|
|
|
2017-06-16 17:45:16 -04:00
|
|
|
struct TimeAxisViewStripableSorter {
|
|
|
|
bool operator() (TimeAxisView* tav_a, TimeAxisView* tav_b) {
|
|
|
|
boost::shared_ptr<ARDOUR::Stripable> const& a = tav_a->stripable ();
|
|
|
|
boost::shared_ptr<ARDOUR::Stripable> const& b = tav_b->stripable ();
|
|
|
|
return ARDOUR::Stripable::Sorter () (a, b);
|
2015-03-24 16:59:57 -04:00
|
|
|
}
|
2010-09-06 08:34:11 -04:00
|
|
|
};
|
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
|
2015-03-26 16:55:04 -04:00
|
|
|
: Drag (e, i)
|
|
|
|
, _primary (p)
|
|
|
|
, _ntracks (0)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2010-09-06 08:34:11 -04:00
|
|
|
_editor->visible_order_range (&_visible_y_low, &_visible_y_high);
|
|
|
|
|
2011-10-22 14:11:40 -04:00
|
|
|
/* Make a list of tracks to refer to during the drag; we include hidden tracks,
|
|
|
|
as some of the regions we are dragging may be on such tracks.
|
|
|
|
*/
|
2010-09-06 08:34:11 -04:00
|
|
|
|
|
|
|
TrackViewList track_views = _editor->track_views;
|
2017-06-16 17:45:16 -04:00
|
|
|
track_views.sort (TimeAxisViewStripableSorter ());
|
2010-09-06 08:34:11 -04:00
|
|
|
|
|
|
|
for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
|
2011-10-22 14:11:40 -04:00
|
|
|
_time_axis_views.push_back (*i);
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2011-10-22 14:11:40 -04:00
|
|
|
TimeAxisView::Children children_list = (*i)->get_child_list ();
|
|
|
|
for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
|
|
|
|
_time_axis_views.push_back (j->get());
|
2010-09-06 08:34:11 -04:00
|
|
|
}
|
|
|
|
}
|
2010-09-18 16:22:03 -04:00
|
|
|
|
2010-11-25 15:37:39 -05:00
|
|
|
/* the list of views can be empty at this point if this is a region list-insert drag
|
|
|
|
*/
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-04-06 20:17:54 -04:00
|
|
|
for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
|
2014-06-19 11:16:27 -04:00
|
|
|
_views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
|
2010-04-06 20:17:54 -04:00
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2012-04-25 08:58:19 -04:00
|
|
|
RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
RegionDrag::region_going_away (RegionView* v)
|
|
|
|
{
|
2010-04-06 20:17:54 -04:00
|
|
|
list<DraggingView>::iterator i = _views.begin ();
|
|
|
|
while (i != _views.end() && i->view != v) {
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i != _views.end()) {
|
|
|
|
_views.erase (i);
|
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2011-10-22 14:11:40 -04:00
|
|
|
/** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
|
|
|
|
* or -1 if it is not found.
|
|
|
|
*/
|
2010-09-06 08:34:11 -04:00
|
|
|
int
|
|
|
|
RegionDrag::find_time_axis_view (TimeAxisView* t) const
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
int const N = _time_axis_views.size ();
|
|
|
|
while (i < N && _time_axis_views[i] != t) {
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
|
2021-05-04 13:54:50 -04:00
|
|
|
if (i == N) {
|
2010-09-06 08:34:11 -04:00
|
|
|
return -1;
|
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-09-06 08:34:11 -04:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2019-11-08 23:03:38 -05:00
|
|
|
void
|
2020-10-15 01:09:22 -04:00
|
|
|
RegionDrag::setup_video_offset ()
|
2019-11-08 23:03:38 -05:00
|
|
|
{
|
|
|
|
if (_views.empty ()) {
|
|
|
|
_preview_video = true;
|
|
|
|
return;
|
|
|
|
}
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t first_sync = _views.begin()->view->region()->sync_position ();
|
2019-11-08 23:03:38 -05:00
|
|
|
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
|
|
|
|
first_sync = std::min (first_sync, i->view->region()->sync_position ());
|
|
|
|
}
|
2020-10-15 01:09:22 -04:00
|
|
|
_video_offset = raw_grab_time().distance (first_sync + _pointer_offset);
|
2019-11-08 23:03:38 -05:00
|
|
|
_preview_video = true;
|
|
|
|
}
|
|
|
|
|
2021-06-15 17:09:24 -04:00
|
|
|
void
|
|
|
|
RegionDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
|
|
|
|
{
|
|
|
|
for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
|
|
|
|
StatefulDiffCommand* c = new StatefulDiffCommand (*i);
|
|
|
|
if (!c->empty()) {
|
|
|
|
_editor->session()->add_command (c);
|
|
|
|
} else {
|
|
|
|
delete c;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-06-15 11:06:39 -04:00
|
|
|
RegionSlipContentsDrag::RegionSlipContentsDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
|
2021-06-15 10:06:28 -04:00
|
|
|
: RegionDrag (e, i, p, v)
|
|
|
|
{
|
2021-06-15 11:06:39 -04:00
|
|
|
DEBUG_TRACE (DEBUG::Drags, "New RegionSlipContentsDrag\n");
|
2021-06-15 10:06:28 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2021-06-15 11:06:39 -04:00
|
|
|
RegionSlipContentsDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
|
2021-06-15 10:06:28 -04:00
|
|
|
{
|
|
|
|
Drag::start_grab (event, _editor->cursors()->trimmer);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2021-06-15 11:06:39 -04:00
|
|
|
RegionSlipContentsDrag::motion (GdkEvent* event, bool first_move)
|
2021-06-15 10:06:28 -04:00
|
|
|
{
|
|
|
|
if (first_move) {
|
2021-06-15 17:09:50 -04:00
|
|
|
|
|
|
|
/*prepare reversible cmd*/
|
|
|
|
_editor->begin_reversible_command (_("Slip Contents"));
|
|
|
|
for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
|
|
|
|
RegionView* rv = i->view;
|
|
|
|
rv->region()->clear_changes ();
|
2021-08-06 09:29:42 -04:00
|
|
|
|
|
|
|
rv->drag_start (); //this allows the region to draw itself 'transparently' while we drag it
|
2021-06-15 17:09:50 -04:00
|
|
|
}
|
|
|
|
|
2021-06-15 10:06:28 -04:00
|
|
|
} else {
|
|
|
|
for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
|
|
|
|
RegionView* rv = i->view;
|
2021-07-04 11:42:04 -04:00
|
|
|
timecnt_t slippage = adjusted_current_time(event, false).distance (last_pointer_time());
|
2021-06-15 10:06:28 -04:00
|
|
|
rv->move_contents (slippage);
|
|
|
|
}
|
|
|
|
show_verbose_cursor_time (_primary->region()->start ());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2021-06-15 17:09:50 -04:00
|
|
|
RegionSlipContentsDrag::finished (GdkEvent *, bool movement_occurred)
|
2021-06-15 10:06:28 -04:00
|
|
|
{
|
2021-06-15 17:09:50 -04:00
|
|
|
if (movement_occurred) {
|
|
|
|
/*finish reversible cmd*/
|
|
|
|
for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
|
|
|
|
RegionView* rv = i->view;
|
|
|
|
_editor->session()->add_command (new StatefulDiffCommand (rv->region()));
|
2021-08-06 09:29:42 -04:00
|
|
|
|
|
|
|
rv->drag_end ();
|
2021-06-15 17:09:50 -04:00
|
|
|
}
|
|
|
|
_editor->commit_reversible_command ();
|
|
|
|
}
|
2021-06-15 10:06:28 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2021-06-15 17:09:50 -04:00
|
|
|
RegionSlipContentsDrag::aborted (bool movement_occurred)
|
2021-06-15 10:06:28 -04:00
|
|
|
{
|
2021-06-15 17:09:50 -04:00
|
|
|
/* ToDo: revert to the original region properties */
|
|
|
|
_editor->abort_reversible_command ();
|
2021-06-15 10:06:28 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-06-15 17:09:24 -04:00
|
|
|
RegionBrushDrag::RegionBrushDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
|
|
|
|
: RegionDrag (e, i, p, v)
|
|
|
|
{
|
|
|
|
DEBUG_TRACE (DEBUG::Drags, "New RegionBrushDrag\n");
|
|
|
|
_y_constrained = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
RegionBrushDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
|
|
|
|
{
|
|
|
|
Drag::start_grab (event, _editor->cursors()->trimmer);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
RegionBrushDrag::motion (GdkEvent* event, bool first_move)
|
|
|
|
{
|
|
|
|
if (first_move) {
|
|
|
|
_editor->begin_reversible_command (_("Region brush drag"));
|
|
|
|
_already_pasted.insert(_primary->region()->position());
|
|
|
|
} else {
|
2021-07-04 11:42:13 -04:00
|
|
|
timepos_t snapped (adjusted_current_time (event, false));
|
2021-06-15 17:09:24 -04:00
|
|
|
_editor->snap_to (snapped, RoundDownAlways, SnapToGrid_Scaled, false);
|
2021-07-04 11:42:13 -04:00
|
|
|
if(_already_pasted.find(snapped) == _already_pasted.end()) {
|
|
|
|
_editor->mouse_brush_insert_region (_primary, snapped);
|
|
|
|
_already_pasted.insert(snapped);
|
2021-06-15 17:09:24 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
RegionBrushDrag::finished (GdkEvent *, bool movement_occurred)
|
|
|
|
{
|
|
|
|
if (!movement_occurred) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
PlaylistSet modified_playlists;
|
|
|
|
modified_playlists.insert(_primary->region()->playlist());
|
|
|
|
add_stateful_diff_commands_for_playlists(modified_playlists);
|
|
|
|
_editor->commit_reversible_command ();
|
|
|
|
_already_pasted.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
RegionBrushDrag::aborted (bool movement_occurred)
|
|
|
|
{
|
|
|
|
_already_pasted.clear();
|
|
|
|
|
|
|
|
/* ToDo: revert to the original playlist properties */
|
|
|
|
_editor->abort_reversible_command ();
|
|
|
|
}
|
|
|
|
|
|
|
|
RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
|
2013-06-26 11:49:16 -04:00
|
|
|
: RegionDrag (e, i, p, v)
|
2015-09-07 20:51:58 -04:00
|
|
|
, _ignore_video_lock (false)
|
2013-06-26 11:49:16 -04:00
|
|
|
, _total_x_delta (0)
|
|
|
|
, _last_pointer_time_axis_view (0)
|
|
|
|
, _last_pointer_layer (0)
|
2015-03-26 16:55:04 -04:00
|
|
|
, _ndropzone (0)
|
|
|
|
, _pdropzone (0)
|
|
|
|
, _ddropzone (0)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2013-04-11 20:19:22 -04:00
|
|
|
DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-09-06 08:34:11 -04:00
|
|
|
RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2010-09-06 08:34:11 -04:00
|
|
|
Drag::start_grab (event, cursor);
|
2017-02-04 13:02:01 -05:00
|
|
|
setup_snap_delta (_last_position);
|
2015-05-15 14:15:52 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
show_verbose_cursor_time (_last_position);
|
|
|
|
show_view_preview (_last_position + _video_offset);
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2021-06-21 11:30:53 -04:00
|
|
|
/* this conditional is required because drag-n-drop'ed regions end up
|
|
|
|
* here, and at this point they are not attached to a playlist.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (_editor->should_ripple() && _primary && _primary->region() && _primary->region()->playlist()) {
|
2021-06-18 14:48:19 -04:00
|
|
|
_earliest_time_limit = _primary->region()->playlist()->find_prev_region_start (_primary->region()->position());
|
|
|
|
}
|
|
|
|
|
2014-06-08 11:26:25 -04:00
|
|
|
pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
|
2013-06-26 11:49:16 -04:00
|
|
|
if (tv.first) {
|
|
|
|
_last_pointer_time_axis_view = find_time_axis_view (tv.first);
|
2015-03-24 10:21:33 -04:00
|
|
|
assert(_last_pointer_time_axis_view >= 0);
|
2013-06-26 11:49:16 -04:00
|
|
|
_last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
|
|
|
|
}
|
2015-06-16 10:45:04 -04:00
|
|
|
|
2015-09-07 20:51:58 -04:00
|
|
|
if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
|
|
|
|
_ignore_video_lock = true;
|
|
|
|
}
|
2021-06-04 11:42:14 -04:00
|
|
|
|
2021-06-18 15:54:44 -04:00
|
|
|
if (_editor->should_ripple()) {
|
2021-06-04 11:42:14 -04:00
|
|
|
/* we do not drag across tracks when rippling or brushing */
|
|
|
|
_y_constrained = true;
|
|
|
|
}
|
2009-06-08 15:28:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
double
|
2020-10-15 01:09:22 -04:00
|
|
|
RegionMotionDrag::compute_x_delta (GdkEvent const * event, Temporal::timepos_t & pending_region_position)
|
2009-06-08 15:28:51 -04:00
|
|
|
{
|
2017-09-18 12:39:17 -04:00
|
|
|
/* compute the amount of pointer motion in samples, and where
|
2009-05-30 14:25:59 -04:00
|
|
|
the region would be if we moved it by that much.
|
|
|
|
*/
|
2017-03-05 09:57:12 -05:00
|
|
|
if (_x_constrained) {
|
2020-10-15 01:09:22 -04:00
|
|
|
pending_region_position = _last_position;
|
2017-03-05 09:57:12 -05:00
|
|
|
return 0.0;
|
|
|
|
}
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
pending_region_position = adjusted_time (_drags->current_pointer_time (), event, false);
|
2010-08-13 12:50:01 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
timecnt_t sync_offset;
|
2009-12-21 20:12:41 -05:00
|
|
|
int32_t sync_dir;
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2009-12-21 20:12:41 -05:00
|
|
|
sync_offset = _primary->region()->sync_offset (sync_dir);
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2009-12-21 20:12:41 -05:00
|
|
|
/* we don't handle a sync point that lies before zero.
|
|
|
|
*/
|
2020-10-15 01:09:22 -04:00
|
|
|
if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) {
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
timecnt_t const sd = snap_delta (event->button.state);
|
|
|
|
timepos_t sync_snap (pending_region_position + (sync_offset * sync_dir) + sd);
|
2017-01-26 08:41:17 -05:00
|
|
|
_editor->snap_to_with_modifier (sync_snap, event);
|
2020-10-15 01:09:22 -04:00
|
|
|
if (sync_offset.zero() && sd.zero()) {
|
|
|
|
pending_region_position = sync_snap;
|
2017-01-26 08:41:17 -05:00
|
|
|
} else {
|
2020-10-15 01:09:22 -04:00
|
|
|
pending_region_position = _primary->region()->adjust_to_sync (sync_snap).earlier (sd);
|
2017-01-26 08:41:17 -05:00
|
|
|
}
|
2009-12-21 20:12:41 -05:00
|
|
|
} else {
|
2020-10-15 01:09:22 -04:00
|
|
|
pending_region_position = _last_position;
|
2009-06-01 20:39:57 -04:00
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2020-11-30 12:59:17 -05:00
|
|
|
if (pending_region_position > timepos_t::max (pending_region_position.time_domain()).earlier (_primary->region()->length())) {
|
2020-10-15 01:09:22 -04:00
|
|
|
pending_region_position = _last_position;
|
2009-06-01 20:39:57 -04:00
|
|
|
}
|
2009-06-08 15:28:51 -04:00
|
|
|
|
2021-06-18 19:38:38 -04:00
|
|
|
if (pending_region_position->sample <= _earliest_time_limit) {
|
|
|
|
pending_region_position->sample = _earliest_time_limit;
|
|
|
|
return 0.0;
|
|
|
|
}
|
|
|
|
|
2010-08-13 12:50:01 -04:00
|
|
|
double dx = 0;
|
2015-05-18 13:27:50 -04:00
|
|
|
|
|
|
|
bool const x_move_allowed = !_x_constrained;
|
2010-09-06 08:34:11 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
if ((pending_region_position != _last_position) && x_move_allowed) {
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2011-04-06 19:12:45 -04:00
|
|
|
/* x movement since last time (in pixels) */
|
2020-10-15 01:09:22 -04:00
|
|
|
dx = _editor->duration_to_pixels_unrounded (_last_position.distance (pending_region_position));
|
2010-08-19 19:56:30 -04:00
|
|
|
|
2010-08-13 12:50:01 -04:00
|
|
|
/* total x movement */
|
2020-10-15 01:09:22 -04:00
|
|
|
timecnt_t total_dx = timecnt_t (samplepos_t (_editor->pixel_to_sample (_total_x_delta + dx)), grab_time());
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2010-08-13 12:50:01 -04:00
|
|
|
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
|
2020-11-30 12:59:17 -05:00
|
|
|
const timepos_t off = i->view->region()->position() + total_dx;
|
2020-10-15 01:09:22 -04:00
|
|
|
if (off.negative()) {
|
|
|
|
dx = dx - _editor->time_to_pixel_unrounded (off);
|
|
|
|
pending_region_position = pending_region_position.earlier (timecnt_t (off, timepos_t (pending_region_position.time_domain())));
|
2010-08-13 12:50:01 -04:00
|
|
|
break;
|
2009-06-01 20:39:57 -04:00
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
}
|
2009-06-01 20:39:57 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
_editor->set_snapped_cursor_position (pending_region_position);
|
2018-02-09 09:21:45 -05:00
|
|
|
|
2010-08-13 12:50:01 -04:00
|
|
|
return dx;
|
2009-06-08 15:28:51 -04:00
|
|
|
}
|
|
|
|
|
2015-03-24 16:59:57 -04:00
|
|
|
int
|
2015-03-25 12:14:48 -04:00
|
|
|
RegionDrag::apply_track_delta (const int start, const int delta, const int skip, const bool distance_only) const
|
2015-03-24 16:59:57 -04:00
|
|
|
{
|
|
|
|
if (delta == 0) {
|
|
|
|
return start;
|
|
|
|
}
|
|
|
|
|
2015-03-26 19:50:27 -04:00
|
|
|
const int tavsize = _time_axis_views.size();
|
2015-03-24 16:59:57 -04:00
|
|
|
const int dt = delta > 0 ? +1 : -1;
|
2015-03-26 19:50:27 -04:00
|
|
|
int current = start;
|
|
|
|
int target = start + delta - skip;
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2015-03-26 19:50:27 -04:00
|
|
|
assert (current < 0 || current >= tavsize || !_time_axis_views[current]->hidden());
|
2015-03-24 16:59:57 -04:00
|
|
|
assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
|
|
|
|
|
|
|
|
while (current >= 0 && current != target) {
|
|
|
|
current += dt;
|
2015-03-25 12:14:48 -04:00
|
|
|
if (current < 0 && dt < 0) {
|
|
|
|
break;
|
|
|
|
}
|
2015-03-26 19:50:27 -04:00
|
|
|
if (current >= tavsize && dt > 0) {
|
2015-03-25 12:14:48 -04:00
|
|
|
break;
|
|
|
|
}
|
2015-03-26 19:50:27 -04:00
|
|
|
if (current < 0 || current >= tavsize) {
|
2015-03-24 16:59:57 -04:00
|
|
|
continue;
|
|
|
|
}
|
2015-03-25 12:14:48 -04:00
|
|
|
|
|
|
|
RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
|
|
|
|
if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
|
2015-03-24 16:59:57 -04:00
|
|
|
target += dt;
|
|
|
|
}
|
2015-03-25 12:14:48 -04:00
|
|
|
|
|
|
|
if (distance_only && current == start + delta) {
|
|
|
|
break;
|
|
|
|
}
|
2015-03-24 16:59:57 -04:00
|
|
|
}
|
|
|
|
return target;
|
|
|
|
}
|
|
|
|
|
2010-09-06 08:34:11 -04:00
|
|
|
bool
|
2015-03-24 16:59:57 -04:00
|
|
|
RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
|
2009-06-08 15:28:51 -04:00
|
|
|
{
|
2015-02-12 14:28:44 -05:00
|
|
|
if (_y_constrained) {
|
|
|
|
return false;
|
|
|
|
}
|
2015-02-13 12:36:08 -05:00
|
|
|
|
2015-03-26 19:50:27 -04:00
|
|
|
const int tavsize = _time_axis_views.size();
|
2010-09-06 08:34:11 -04:00
|
|
|
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
|
2015-03-24 16:59:57 -04:00
|
|
|
int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
|
2015-03-26 19:50:27 -04:00
|
|
|
assert (n < 0 || n >= tavsize || !_time_axis_views[n]->hidden());
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2015-03-26 19:50:27 -04:00
|
|
|
if (i->time_axis_view < 0 || i->time_axis_view >= tavsize) {
|
2015-02-13 12:36:08 -05:00
|
|
|
/* already in the drop zone */
|
2015-03-15 03:45:26 -04:00
|
|
|
if (delta_track >= 0) {
|
2015-03-26 16:55:04 -04:00
|
|
|
/* downward motion - OK if others are still not in the dropzone */
|
2015-02-13 12:36:08 -05:00
|
|
|
continue;
|
|
|
|
}
|
2015-03-15 03:45:26 -04:00
|
|
|
|
2010-09-06 08:34:11 -04:00
|
|
|
}
|
2009-06-08 15:28:51 -04:00
|
|
|
|
2015-03-15 03:45:26 -04:00
|
|
|
if (n < 0) {
|
|
|
|
/* off the top */
|
|
|
|
return false;
|
2015-03-26 19:50:27 -04:00
|
|
|
} else if (n >= tavsize) {
|
2015-02-13 12:36:08 -05:00
|
|
|
/* downward motion into drop zone. That's fine. */
|
|
|
|
continue;
|
|
|
|
}
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2010-09-06 08:34:11 -04:00
|
|
|
RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
|
2015-03-24 10:21:33 -04:00
|
|
|
if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
|
2010-09-06 08:34:11 -04:00
|
|
|
/* not a track, or the wrong type */
|
|
|
|
return false;
|
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2011-12-26 20:42:49 -05:00
|
|
|
double const l = i->layer + delta_layer;
|
|
|
|
|
|
|
|
/* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
|
|
|
|
mode to allow the user to place a region below another on layer 0.
|
|
|
|
*/
|
|
|
|
if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
|
2010-09-06 08:34:11 -04:00
|
|
|
/* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
|
|
|
|
If it has, the layers will be munged later anyway, so it's ok.
|
|
|
|
*/
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2009-06-08 15:28:51 -04:00
|
|
|
|
2010-09-06 08:34:11 -04:00
|
|
|
/* all regions being dragged are ok with this change */
|
2015-03-24 16:59:57 -04:00
|
|
|
return true;
|
2010-09-06 08:34:11 -04:00
|
|
|
}
|
2009-06-08 15:28:51 -04:00
|
|
|
|
2015-03-25 17:05:23 -04:00
|
|
|
struct DraggingViewSorter {
|
|
|
|
bool operator() (const DraggingView& a, const DraggingView& b) {
|
|
|
|
return a.time_axis_view < b.time_axis_view;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-06-04 11:22:44 -04:00
|
|
|
void
|
|
|
|
RegionMotionDrag::collect_ripple_views ()
|
|
|
|
{
|
|
|
|
RegionSelection copy;
|
|
|
|
|
2021-06-21 11:35:13 -04:00
|
|
|
_editor->get_regionviews_at_or_after (_primary->region()->position(), copy);
|
2021-06-04 11:22:44 -04:00
|
|
|
|
2021-06-21 11:35:13 -04:00
|
|
|
for (RegionSelection::reverse_iterator i = copy.rbegin(); i != copy.rend(); ++i) {
|
|
|
|
if (!_editor->selection->regions.contains (*i)) {
|
|
|
|
_views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
|
2021-06-04 11:22:44 -04:00
|
|
|
}
|
|
|
|
}
|
2021-06-21 14:23:52 -04:00
|
|
|
|
|
|
|
_editor->get_markers_to_ripple (_primary->region()->playlist(), _primary->region()->position(), ripple_markers);
|
2021-06-04 11:22:44 -04:00
|
|
|
}
|
|
|
|
|
2010-09-06 08:34:11 -04:00
|
|
|
void
|
|
|
|
RegionMotionDrag::motion (GdkEvent* event, bool first_move)
|
|
|
|
{
|
2014-05-17 12:17:16 -04:00
|
|
|
double delta_layer = 0;
|
|
|
|
int delta_time_axis_view = 0;
|
2015-03-07 21:42:23 -05:00
|
|
|
int current_pointer_time_axis_view = -1;
|
2010-09-20 23:09:02 -04:00
|
|
|
|
2014-05-17 12:17:16 -04:00
|
|
|
assert (!_views.empty ());
|
2009-06-08 15:28:51 -04:00
|
|
|
|
2015-02-13 12:36:08 -05:00
|
|
|
/* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
|
|
|
|
|
|
|
|
/* Find the TimeAxisView that the pointer is now over */
|
2015-03-25 22:04:19 -04:00
|
|
|
const double cur_y = current_pointer_y ();
|
|
|
|
pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
|
2015-02-13 12:36:08 -05:00
|
|
|
TimeAxisView* tv = r.first;
|
|
|
|
|
2015-03-25 22:04:19 -04:00
|
|
|
if (!tv && cur_y < 0) {
|
2015-02-18 12:20:06 -05:00
|
|
|
/* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
|
|
|
|
return;
|
|
|
|
}
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2015-03-26 16:55:04 -04:00
|
|
|
/* find drop-zone y-position */
|
|
|
|
Coord last_track_bottom_edge;
|
|
|
|
last_track_bottom_edge = 0;
|
|
|
|
for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
|
|
|
|
if (!(*t)->hidden()) {
|
|
|
|
last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-03 15:57:56 -04:00
|
|
|
if (tv && tv->view()) {
|
2015-03-26 16:55:04 -04:00
|
|
|
/* the mouse is over a track */
|
2014-05-17 12:17:16 -04:00
|
|
|
double layer = r.second;
|
2014-06-09 10:55:37 -04:00
|
|
|
|
2014-05-17 12:17:16 -04:00
|
|
|
if (first_move && tv->view()->layer_display() == Stacked) {
|
|
|
|
tv->view()->set_layer_display (Expanded);
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2014-05-17 12:17:16 -04:00
|
|
|
/* Here's the current pointer position in terms of time axis view and layer */
|
2015-02-13 12:36:08 -05:00
|
|
|
current_pointer_time_axis_view = find_time_axis_view (tv);
|
2015-03-24 10:21:33 -04:00
|
|
|
assert(current_pointer_time_axis_view >= 0);
|
2015-02-13 12:36:08 -05:00
|
|
|
|
2014-05-17 12:17:16 -04:00
|
|
|
double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2014-05-17 12:17:16 -04:00
|
|
|
/* Work out the change in y */
|
2009-06-08 15:28:51 -04:00
|
|
|
|
2015-03-27 11:22:45 -04:00
|
|
|
RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
|
|
|
|
if (!rtv || !rtv->is_track()) {
|
2016-06-02 08:42:58 -04:00
|
|
|
/* ignore non-tracks early on. we can't move any regions on them */
|
2015-03-27 11:22:45 -04:00
|
|
|
} else if (_last_pointer_time_axis_view < 0) {
|
2015-03-24 10:21:33 -04:00
|
|
|
/* Was in the drop-zone, now over a track.
|
|
|
|
* Hence it must be an upward move (from the bottom)
|
|
|
|
*
|
|
|
|
* track_index is still -1, so delta must be set to
|
|
|
|
* move up the correct number of tracks from the bottom.
|
|
|
|
*
|
|
|
|
* This is necessary because steps may be skipped if
|
2015-03-26 16:55:04 -04:00
|
|
|
* the bottom-most track is not a valid target and/or
|
|
|
|
* if there are hidden tracks at the bottom.
|
|
|
|
* Hence the initial offset (_ddropzone) as well as the
|
|
|
|
* last valid pointer position (_pdropzone) need to be
|
|
|
|
* taken into account.
|
2015-03-24 10:21:33 -04:00
|
|
|
*/
|
2015-03-26 16:55:04 -04:00
|
|
|
delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size () + _ddropzone - _pdropzone;
|
2015-02-13 12:36:08 -05:00
|
|
|
} else {
|
2015-03-27 11:22:45 -04:00
|
|
|
delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
|
2015-02-13 12:36:08 -05:00
|
|
|
}
|
|
|
|
|
2015-03-24 10:21:33 -04:00
|
|
|
/* TODO needs adjustment per DraggingView,
|
|
|
|
*
|
|
|
|
* e.g. select one region on the top-layer of a track
|
|
|
|
* and one region which is at the bottom-layer of another track
|
|
|
|
* drag both.
|
|
|
|
*
|
|
|
|
* Indicated drop-zones and layering is wrong.
|
|
|
|
* and may infer additional layers on the target-track
|
|
|
|
* (depending how many layers the original track had).
|
|
|
|
*
|
|
|
|
* Or select two regions (different layers) on a same track,
|
|
|
|
* move across a non-layer track.. -> layering info is lost.
|
|
|
|
* on drop either of the regions may be on top.
|
|
|
|
*
|
|
|
|
* Proposed solution: screw it :) well,
|
|
|
|
* don't use delta_layer, use an absolute value
|
|
|
|
* 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
|
|
|
|
* 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
|
|
|
|
* 3) iterate over all DraggingView, find the one that is over the track with most layers
|
|
|
|
* 4) proportionally scale layer to layers available on target
|
|
|
|
*/
|
2014-05-17 12:17:16 -04:00
|
|
|
delta_layer = current_pointer_layer - _last_pointer_layer;
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2015-03-25 09:42:44 -04:00
|
|
|
}
|
2015-03-26 16:55:04 -04:00
|
|
|
/* for automation lanes, there is a TimeAxisView but no ->view()
|
2015-10-04 14:51:05 -04:00
|
|
|
* if (!tv) -> dropzone
|
2015-03-26 16:55:04 -04:00
|
|
|
*/
|
2015-03-25 22:04:19 -04:00
|
|
|
else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
|
2015-03-26 16:55:04 -04:00
|
|
|
/* Moving into the drop-zone.. */
|
|
|
|
delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
|
|
|
|
/* delta_time_axis_view may not be sufficient to move into the DZ
|
|
|
|
* the mouse may enter it, but it may not be a valid move due to
|
|
|
|
* constraints.
|
2015-03-25 10:56:51 -04:00
|
|
|
*
|
2015-03-26 16:55:04 -04:00
|
|
|
* -> remember the delta needed to move into the dropzone
|
2015-03-24 16:59:57 -04:00
|
|
|
*/
|
2015-03-26 16:55:04 -04:00
|
|
|
_ddropzone = delta_time_axis_view;
|
|
|
|
/* ..but subtract hidden tracks (or routes) at the bottom.
|
|
|
|
* we silently move mover them
|
|
|
|
*/
|
|
|
|
_ddropzone -= apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
|
|
|
|
- _time_axis_views.size();
|
|
|
|
}
|
|
|
|
else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view < 0) {
|
|
|
|
/* move around inside the zone.
|
|
|
|
* This allows to move further down until all regions are in the zone.
|
|
|
|
*/
|
|
|
|
const double ptr_y = cur_y + _editor->get_trackview_group()->canvas_origin().y;
|
|
|
|
assert(ptr_y >= last_track_bottom_edge);
|
|
|
|
assert(_ddropzone > 0);
|
|
|
|
|
|
|
|
/* calculate mouse position in 'tracks' below last track. */
|
|
|
|
const double dzi_h = TimeAxisView::preset_height (HeightNormal);
|
|
|
|
uint32_t dzpos = _ddropzone + floor((1 + ptr_y - last_track_bottom_edge) / dzi_h);
|
|
|
|
|
|
|
|
if (dzpos > _pdropzone && _ndropzone < _ntracks) {
|
|
|
|
// move further down
|
|
|
|
delta_time_axis_view = dzpos - _pdropzone;
|
|
|
|
} else if (dzpos < _pdropzone && _ndropzone > 0) {
|
|
|
|
// move up inside the DZ
|
|
|
|
delta_time_axis_view = dzpos - _pdropzone;
|
|
|
|
}
|
2015-03-24 16:59:57 -04:00
|
|
|
}
|
2015-02-13 12:36:08 -05:00
|
|
|
|
2010-09-06 08:34:11 -04:00
|
|
|
/* Work out the change in x */
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t pending_region_position;
|
|
|
|
double const x_delta = compute_x_delta (event, pending_region_position);
|
2017-01-30 12:15:21 -05:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
Temporal::Beats const last_pos_qn = _last_position.beats();
|
|
|
|
Temporal::Beats const qn_delta = pending_region_position.beats() - last_pos_qn;
|
2017-01-30 12:15:21 -05:00
|
|
|
|
2017-01-26 08:41:17 -05:00
|
|
|
_last_position = pending_region_position;
|
2009-06-08 15:28:51 -04:00
|
|
|
|
2015-03-26 16:55:04 -04:00
|
|
|
/* calculate hidden tracks in current y-axis delta */
|
2015-03-24 16:59:57 -04:00
|
|
|
int delta_skip = 0;
|
2015-03-26 16:55:04 -04:00
|
|
|
if (_last_pointer_time_axis_view < 0 && _pdropzone > 0) {
|
|
|
|
/* The mouse is more than one track below the dropzone.
|
|
|
|
* distance calculation is not needed (and would not work, either
|
|
|
|
* because the dropzone is "packed").
|
|
|
|
*
|
2015-03-27 11:22:45 -04:00
|
|
|
* Except when [partially] moving regions out of dropzone in a large step.
|
2015-03-26 16:55:04 -04:00
|
|
|
* (the mouse may or may not remain in the DZ)
|
|
|
|
* Hidden tracks at the bottom of the TAV need to be skipped.
|
2015-03-27 11:22:45 -04:00
|
|
|
*
|
|
|
|
* This also handles the case if the mouse entered the DZ
|
|
|
|
* in a large step (exessive delta), either due to fast-movement,
|
|
|
|
* autoscroll, laggy UI. _ddropzone copensates for that (see "move into dz" above)
|
2015-03-26 16:55:04 -04:00
|
|
|
*/
|
2015-03-27 11:22:45 -04:00
|
|
|
if (delta_time_axis_view < 0 && (int)_ddropzone - delta_time_axis_view >= (int)_pdropzone) {
|
|
|
|
const int dt = delta_time_axis_view + (int)_pdropzone - (int)_ddropzone;
|
|
|
|
assert(dt <= 0);
|
2015-03-26 16:55:04 -04:00
|
|
|
delta_skip = apply_track_delta(_time_axis_views.size(), dt, 0, true)
|
|
|
|
-_time_axis_views.size() - dt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (_last_pointer_time_axis_view < 0) {
|
|
|
|
/* Moving out of the zone. Check for hidden tracks at the bottom. */
|
2015-03-25 12:14:48 -04:00
|
|
|
delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
|
2015-03-24 16:59:57 -04:00
|
|
|
-_time_axis_views.size() - delta_time_axis_view;
|
|
|
|
} else {
|
2015-03-26 16:55:04 -04:00
|
|
|
/* calculate hidden tracks that are skipped by the pointer movement */
|
2015-03-25 12:14:48 -04:00
|
|
|
delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
|
2015-03-24 16:59:57 -04:00
|
|
|
- _last_pointer_time_axis_view
|
|
|
|
- delta_time_axis_view;
|
|
|
|
}
|
|
|
|
|
2014-05-17 12:17:16 -04:00
|
|
|
/* Verify change in y */
|
2015-03-24 16:59:57 -04:00
|
|
|
if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
|
2010-09-06 08:34:11 -04:00
|
|
|
/* this y movement is not allowed, so do no y movement this time */
|
|
|
|
delta_time_axis_view = 0;
|
|
|
|
delta_layer = 0;
|
2015-03-24 16:59:57 -04:00
|
|
|
delta_skip = 0;
|
2010-09-06 08:34:11 -04:00
|
|
|
}
|
|
|
|
|
2014-06-19 11:16:27 -04:00
|
|
|
if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
|
2009-05-30 14:25:59 -04:00
|
|
|
/* haven't reached next snap point, and we're not switching
|
|
|
|
trackviews nor layers. nothing to do.
|
|
|
|
*/
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-03-26 16:55:04 -04:00
|
|
|
typedef map<boost::shared_ptr<Playlist>, double> PlaylistDropzoneMap;
|
2015-02-13 12:36:08 -05:00
|
|
|
PlaylistDropzoneMap playlist_dropzone_map;
|
2015-03-26 16:55:04 -04:00
|
|
|
_ndropzone = 0; // number of elements currently in the dropzone
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2015-03-25 17:05:23 -04:00
|
|
|
if (first_move) {
|
|
|
|
/* sort views by time_axis.
|
|
|
|
* This retains track order in the dropzone, regardless
|
|
|
|
* of actual selection order
|
|
|
|
*/
|
|
|
|
_views.sort (DraggingViewSorter());
|
2015-03-26 16:55:04 -04:00
|
|
|
|
|
|
|
/* count number of distinct tracks of all regions
|
|
|
|
* being dragged, used for dropzone.
|
|
|
|
*/
|
|
|
|
int prev_track = -1;
|
|
|
|
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
|
2021-05-06 14:03:27 -04:00
|
|
|
if ((int) i->time_axis_view != prev_track) {
|
2015-03-26 16:55:04 -04:00
|
|
|
prev_track = i->time_axis_view;
|
|
|
|
++_ntracks;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
|
|
int spread =
|
|
|
|
_views.back().time_axis_view -
|
|
|
|
_views.front().time_axis_view;
|
|
|
|
|
|
|
|
spread -= apply_track_delta (_views.front().time_axis_view, spread, 0, true)
|
|
|
|
- _views.back().time_axis_view;
|
|
|
|
|
|
|
|
printf("Dragging region(s) from %d different track(s), max dist: %d\n", _ntracks, spread);
|
|
|
|
#endif
|
2015-03-25 17:05:23 -04:00
|
|
|
}
|
|
|
|
|
2021-06-21 14:23:52 -04:00
|
|
|
if (x_delta) {
|
|
|
|
for (vector<ArdourMarker*>::iterator m = ripple_markers.begin(); m != ripple_markers.end(); ++m) {
|
|
|
|
(*m)->the_item().move (Duple (x_delta, 0.));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-06 08:34:11 -04:00
|
|
|
for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2010-04-06 20:17:54 -04:00
|
|
|
RegionView* rv = i->view;
|
2015-02-05 17:42:34 -05:00
|
|
|
double y_delta;
|
|
|
|
|
|
|
|
y_delta = 0;
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2015-09-07 20:51:58 -04:00
|
|
|
if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
|
2009-06-01 20:39:57 -04:00
|
|
|
continue;
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-06-01 20:39:57 -04:00
|
|
|
if (first_move) {
|
2015-03-24 16:59:57 -04:00
|
|
|
rv->drag_start ();
|
2014-06-05 15:16:55 -04:00
|
|
|
|
|
|
|
/* reparent the regionview into a group above all
|
|
|
|
* others
|
|
|
|
*/
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2014-06-21 11:44:22 -04:00
|
|
|
ArdourCanvas::Item* rvg = rv->get_canvas_group();
|
2015-03-24 16:59:57 -04:00
|
|
|
Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
|
|
|
|
Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
|
|
|
|
rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
|
2014-06-05 15:22:26 -04:00
|
|
|
/* move the item so that it continues to appear at the
|
|
|
|
same location now that its parent has changed.
|
2015-03-24 16:59:57 -04:00
|
|
|
*/
|
|
|
|
rvg->move (rv_canvas_offset - dmg_canvas_offset);
|
2009-06-01 20:39:57 -04:00
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2010-09-06 08:34:11 -04:00
|
|
|
/* If we have moved tracks, we'll fudge the layer delta so that the
|
|
|
|
region gets moved back onto layer 0 on its new track; this avoids
|
|
|
|
confusion when dragging regions from non-zero layers onto different
|
|
|
|
tracks.
|
|
|
|
*/
|
2011-12-26 20:42:49 -05:00
|
|
|
double this_delta_layer = delta_layer;
|
2010-09-06 08:34:11 -04:00
|
|
|
if (delta_time_axis_view != 0) {
|
|
|
|
this_delta_layer = - i->layer;
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2015-03-29 09:23:27 -04:00
|
|
|
int this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
|
2011-12-26 20:42:49 -05:00
|
|
|
|
2015-03-24 16:59:57 -04:00
|
|
|
int track_index = i->time_axis_view + this_delta_time_axis_view;
|
|
|
|
assert(track_index >= 0);
|
2015-02-13 12:36:08 -05:00
|
|
|
|
2015-03-24 16:59:57 -04:00
|
|
|
if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
|
2015-03-26 16:55:04 -04:00
|
|
|
/* Track is in the Dropzone */
|
|
|
|
|
2015-03-24 16:59:57 -04:00
|
|
|
i->time_axis_view = track_index;
|
2015-03-26 19:50:27 -04:00
|
|
|
assert(i->time_axis_view >= (int) _time_axis_views.size());
|
2015-03-25 22:04:19 -04:00
|
|
|
if (cur_y >= 0) {
|
2015-02-13 12:36:08 -05:00
|
|
|
|
2015-03-26 16:55:04 -04:00
|
|
|
double yposition = 0;
|
2015-03-24 16:59:57 -04:00
|
|
|
PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
|
2015-03-26 16:55:04 -04:00
|
|
|
rv->set_height (TimeAxisView::preset_height (HeightNormal));
|
|
|
|
++_ndropzone;
|
2015-03-24 16:59:57 -04:00
|
|
|
|
|
|
|
/* store index of each new playlist as a negative count, starting at -1 */
|
|
|
|
|
|
|
|
if (pdz == playlist_dropzone_map.end()) {
|
|
|
|
/* compute where this new track (which doesn't exist yet) will live
|
|
|
|
on the y-axis.
|
|
|
|
*/
|
2015-03-26 16:55:04 -04:00
|
|
|
yposition = last_track_bottom_edge; /* where to place the top edge of the regionview */
|
2015-03-24 16:59:57 -04:00
|
|
|
|
|
|
|
/* How high is this region view ? */
|
|
|
|
|
|
|
|
boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
|
|
|
|
ArdourCanvas::Rect bbox;
|
|
|
|
|
|
|
|
if (obbox) {
|
|
|
|
bbox = obbox.get ();
|
|
|
|
}
|
|
|
|
|
|
|
|
last_track_bottom_edge += bbox.height();
|
|
|
|
|
2015-03-26 16:55:04 -04:00
|
|
|
playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), yposition));
|
2015-02-13 12:36:08 -05:00
|
|
|
|
|
|
|
} else {
|
2015-03-26 16:55:04 -04:00
|
|
|
yposition = pdz->second;
|
2015-02-13 12:36:08 -05:00
|
|
|
}
|
|
|
|
|
2015-03-24 16:59:57 -04:00
|
|
|
/* values are zero or negative, hence the use of min() */
|
2015-03-26 16:55:04 -04:00
|
|
|
y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
|
2014-06-03 15:57:56 -04:00
|
|
|
}
|
|
|
|
|
2016-11-22 16:04:14 -05:00
|
|
|
MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
|
|
|
|
if (mrv) {
|
|
|
|
mrv->apply_note_range (60, 71, true);
|
|
|
|
}
|
2015-03-24 16:59:57 -04:00
|
|
|
} else {
|
|
|
|
|
2014-06-03 15:57:56 -04:00
|
|
|
/* The TimeAxisView that this region is now over */
|
2014-06-09 10:55:37 -04:00
|
|
|
TimeAxisView* current_tv = _time_axis_views[track_index];
|
2014-05-17 12:17:16 -04:00
|
|
|
|
|
|
|
/* Ensure it is moved from stacked -> expanded if appropriate */
|
|
|
|
if (current_tv->view()->layer_display() == Stacked) {
|
|
|
|
current_tv->view()->set_layer_display (Expanded);
|
|
|
|
}
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2014-05-17 12:17:16 -04:00
|
|
|
/* We're only allowed to go -ve in layer on Expanded views */
|
|
|
|
if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
|
|
|
|
this_delta_layer = - i->layer;
|
|
|
|
}
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2014-05-17 12:17:16 -04:00
|
|
|
/* Set height */
|
|
|
|
rv->set_height (current_tv->view()->child_height ());
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2014-05-17 12:17:16 -04:00
|
|
|
/* Update show/hidden status as the region view may have come from a hidden track,
|
|
|
|
or have moved to one.
|
|
|
|
*/
|
|
|
|
if (current_tv->hidden ()) {
|
|
|
|
rv->get_canvas_group()->hide ();
|
|
|
|
} else {
|
|
|
|
rv->get_canvas_group()->show ();
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2014-05-17 12:17:16 -04:00
|
|
|
/* Update the DraggingView */
|
2014-06-09 10:55:37 -04:00
|
|
|
i->time_axis_view = track_index;
|
2014-05-17 12:17:16 -04:00
|
|
|
i->layer += this_delta_layer;
|
|
|
|
|
2021-06-15 17:09:24 -04:00
|
|
|
Duple track_origin;
|
2015-02-05 17:42:34 -05:00
|
|
|
|
2021-06-15 17:09:24 -04:00
|
|
|
/* Get the y coordinate of the top of the track that this region is now over */
|
|
|
|
track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
|
2015-02-13 12:36:08 -05:00
|
|
|
|
2021-06-15 17:09:24 -04:00
|
|
|
/* And adjust for the layer that it should be on */
|
|
|
|
StreamView* cv = current_tv->view ();
|
|
|
|
switch (cv->layer_display ()) {
|
|
|
|
case Overlaid:
|
|
|
|
break;
|
|
|
|
case Stacked:
|
|
|
|
track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
|
|
|
|
break;
|
|
|
|
case Expanded:
|
|
|
|
track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
|
|
|
|
break;
|
2014-06-03 15:57:56 -04:00
|
|
|
}
|
2016-11-22 16:04:14 -05:00
|
|
|
|
2021-06-15 17:09:24 -04:00
|
|
|
/* need to get the parent of the regionview
|
|
|
|
* canvas group and get its position in
|
|
|
|
* equivalent coordinate space as the trackview
|
|
|
|
* we are now dragging over.
|
|
|
|
*/
|
|
|
|
|
|
|
|
y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
|
|
|
|
|
2016-11-22 16:04:14 -05:00
|
|
|
MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
|
|
|
|
if (mrv) {
|
|
|
|
MidiStreamView* msv;
|
|
|
|
if ((msv = dynamic_cast <MidiStreamView*> (current_tv->view())) != 0) {
|
|
|
|
mrv->apply_note_range (msv->lowest_note(), msv->highest_note(), true);
|
|
|
|
}
|
|
|
|
}
|
2014-06-03 15:57:56 -04:00
|
|
|
}
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2015-02-05 17:42:34 -05:00
|
|
|
/* Now move the region view */
|
2020-10-15 01:09:22 -04:00
|
|
|
if (rv->region()->position_time_domain() == Temporal::BeatTime) {
|
|
|
|
Temporal::Beats const last_qn = rv->get_position().beats();
|
|
|
|
rv->set_position (timepos_t (last_qn + qn_delta), 0);
|
2017-02-06 13:08:54 -05:00
|
|
|
rv->move (0, y_delta);
|
2017-01-30 12:15:21 -05:00
|
|
|
} else {
|
|
|
|
rv->move (x_delta, y_delta);
|
|
|
|
}
|
2009-06-01 20:39:57 -04:00
|
|
|
} /* foreach region */
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2010-01-07 20:28:15 -05:00
|
|
|
_total_x_delta += x_delta;
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2021-06-15 17:09:24 -04:00
|
|
|
if (x_delta != 0) {
|
2020-10-15 01:09:22 -04:00
|
|
|
show_verbose_cursor_time (_last_position);
|
|
|
|
show_view_preview (_last_position + _video_offset);
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
2010-09-06 08:34:11 -04:00
|
|
|
|
2015-03-26 16:55:04 -04:00
|
|
|
/* keep track of pointer movement */
|
2015-02-13 12:36:08 -05:00
|
|
|
if (tv) {
|
|
|
|
/* the pointer is currently over a time axis view */
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2015-02-13 12:36:08 -05:00
|
|
|
if (_last_pointer_time_axis_view < 0) {
|
2015-03-26 16:55:04 -04:00
|
|
|
/* last motion event was not over a time axis view
|
|
|
|
* or last y-movement out of the dropzone was not valid
|
|
|
|
*/
|
|
|
|
int dtz = 0;
|
2015-02-13 12:36:08 -05:00
|
|
|
if (delta_time_axis_view < 0) {
|
2015-03-26 16:55:04 -04:00
|
|
|
/* in the drop zone, moving up */
|
|
|
|
|
|
|
|
/* _pdropzone is the last known pointer y-axis position inside the DZ.
|
|
|
|
* We do not use negative _last_pointer_time_axis_view because
|
|
|
|
* the dropzone is "packed" (the actual track offset is ignored)
|
|
|
|
*
|
2015-10-04 14:51:05 -04:00
|
|
|
* As opposed to the actual number
|
2015-03-26 16:55:04 -04:00
|
|
|
* of elements in the dropzone (_ndropzone)
|
|
|
|
* _pdropzone is not constrained. This is necessary
|
|
|
|
* to allow moving multiple regions with y-distance
|
|
|
|
* into the DZ.
|
|
|
|
*
|
|
|
|
* There can be 0 elements in the dropzone,
|
|
|
|
* even though the drag-pointer is inside the DZ.
|
|
|
|
*
|
|
|
|
* example:
|
|
|
|
* [ Audio-track, Midi-track, Audio-track, DZ ]
|
|
|
|
* move regions from both audio tracks at the same time into the
|
|
|
|
* DZ by grabbing the region in the bottom track.
|
|
|
|
*/
|
2015-03-24 10:21:33 -04:00
|
|
|
assert(current_pointer_time_axis_view >= 0);
|
2015-03-27 11:22:45 -04:00
|
|
|
dtz = std::min((int)_pdropzone, (int)_ddropzone - delta_time_axis_view);
|
2015-03-26 16:55:04 -04:00
|
|
|
_pdropzone -= dtz;
|
2015-02-13 12:36:08 -05:00
|
|
|
}
|
|
|
|
|
2015-03-26 16:55:04 -04:00
|
|
|
/* only move out of the zone if the movement is OK */
|
|
|
|
if (_pdropzone == 0 && delta_time_axis_view != 0) {
|
2015-03-27 11:22:45 -04:00
|
|
|
assert(delta_time_axis_view < 0);
|
2015-03-26 16:55:04 -04:00
|
|
|
_last_pointer_time_axis_view = current_pointer_time_axis_view;
|
2015-03-27 11:22:45 -04:00
|
|
|
/* if all logic and maths are correct, there is no need to assign the 'current' pointer.
|
|
|
|
* the current position can be calculated as follows:
|
|
|
|
*/
|
2015-03-27 15:58:41 -04:00
|
|
|
// a well placed oofus attack can still throw this off.
|
|
|
|
// likley auto-scroll related, printf() debugging may tell, commented out for now.
|
|
|
|
//assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view);
|
2015-03-26 16:55:04 -04:00
|
|
|
}
|
2015-02-13 12:36:08 -05:00
|
|
|
} else {
|
|
|
|
/* last motion event was also over a time axis view */
|
2015-03-24 10:21:33 -04:00
|
|
|
_last_pointer_time_axis_view += delta_time_axis_view;
|
|
|
|
assert(_last_pointer_time_axis_view >= 0);
|
2015-02-13 12:36:08 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
/* the pointer is not over a time axis view */
|
2015-03-26 16:55:04 -04:00
|
|
|
assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
|
|
|
|
_pdropzone += delta_time_axis_view - delta_skip;
|
|
|
|
_last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
|
2015-02-13 12:36:08 -05:00
|
|
|
}
|
|
|
|
|
2010-09-06 08:34:11 -04:00
|
|
|
_last_pointer_layer += delta_layer;
|
2009-06-08 15:28:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
RegionMoveDrag::motion (GdkEvent* event, bool first_move)
|
|
|
|
{
|
2021-06-18 15:54:44 -04:00
|
|
|
if (first_move && _editor->should_ripple() && !_copy) {
|
2021-06-04 13:06:07 -04:00
|
|
|
collect_ripple_views ();
|
|
|
|
}
|
|
|
|
|
2009-06-08 15:28:51 -04:00
|
|
|
if (_copy && first_move) {
|
2021-06-15 17:09:24 -04:00
|
|
|
if (_x_constrained) {
|
2014-12-14 08:13:38 -05:00
|
|
|
_editor->begin_reversible_command (Operations::fixed_time_region_copy);
|
2021-06-15 17:09:24 -04:00
|
|
|
} else {
|
2014-12-14 08:13:38 -05:00
|
|
|
_editor->begin_reversible_command (Operations::region_copy);
|
|
|
|
}
|
2021-06-05 21:33:42 -04:00
|
|
|
|
2010-09-06 08:34:11 -04:00
|
|
|
/* duplicate the regionview(s) and region(s) */
|
|
|
|
|
|
|
|
list<DraggingView> new_regionviews;
|
|
|
|
|
|
|
|
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-09-06 08:34:11 -04:00
|
|
|
RegionView* rv = i->view;
|
|
|
|
AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
|
|
|
|
MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
|
|
|
|
|
|
|
|
const boost::shared_ptr<const Region> original = rv->region();
|
2016-12-12 12:33:58 -05:00
|
|
|
boost::shared_ptr<Region> region_copy;
|
|
|
|
|
2017-01-26 08:41:17 -05:00
|
|
|
region_copy = RegionFactory::create (original, true);
|
2016-12-12 12:33:58 -05:00
|
|
|
|
2015-02-12 14:28:26 -05:00
|
|
|
/* need to set this so that the drop zone code can work. This doesn't
|
|
|
|
actually put the region into the playlist, but just sets a weak pointer
|
|
|
|
to it.
|
|
|
|
*/
|
|
|
|
region_copy->set_playlist (original->playlist());
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2010-09-06 08:34:11 -04:00
|
|
|
RegionView* nrv;
|
|
|
|
if (arv) {
|
|
|
|
boost::shared_ptr<AudioRegion> audioregion_copy
|
|
|
|
= boost::dynamic_pointer_cast<AudioRegion>(region_copy);
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-09-06 08:34:11 -04:00
|
|
|
nrv = new AudioRegionView (*arv, audioregion_copy);
|
|
|
|
} else if (mrv) {
|
|
|
|
boost::shared_ptr<MidiRegion> midiregion_copy
|
|
|
|
= boost::dynamic_pointer_cast<MidiRegion>(region_copy);
|
|
|
|
nrv = new MidiRegionView (*mrv, midiregion_copy);
|
|
|
|
} else {
|
|
|
|
continue;
|
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-09-06 08:34:11 -04:00
|
|
|
nrv->get_canvas_group()->show ();
|
2014-06-19 11:16:27 -04:00
|
|
|
new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-09-06 08:34:11 -04:00
|
|
|
/* swap _primary to the copy */
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-09-06 08:34:11 -04:00
|
|
|
if (rv == _primary) {
|
|
|
|
_primary = nrv;
|
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-09-06 08:34:11 -04:00
|
|
|
/* ..and deselect the one we copied */
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-09-06 08:34:11 -04:00
|
|
|
rv->set_selected (false);
|
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-09-06 08:34:11 -04:00
|
|
|
if (!new_regionviews.empty()) {
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-09-06 08:34:11 -04:00
|
|
|
/* reflect the fact that we are dragging the copies */
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-09-06 08:34:11 -04:00
|
|
|
_views = new_regionviews;
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-09-06 08:34:11 -04:00
|
|
|
swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
|
|
|
|
}
|
2014-12-14 08:13:38 -05:00
|
|
|
|
|
|
|
} else if (!_copy && first_move) {
|
2021-06-15 17:09:24 -04:00
|
|
|
if (_x_constrained) {
|
2014-12-14 08:13:38 -05:00
|
|
|
_editor->begin_reversible_command (_("fixed time region drag"));
|
2021-06-15 17:09:24 -04:00
|
|
|
} else {
|
2014-12-14 08:13:38 -05:00
|
|
|
_editor->begin_reversible_command (Operations::region_drag);
|
|
|
|
}
|
2009-06-08 15:28:51 -04:00
|
|
|
}
|
|
|
|
RegionMotionDrag::motion (event, first_move);
|
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
|
|
|
|
void
|
2011-12-26 20:42:49 -05:00
|
|
|
RegionMotionDrag::finished (GdkEvent *, bool)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2011-12-26 20:42:49 -05:00
|
|
|
for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
|
2011-12-29 09:48:42 -05:00
|
|
|
if (!(*i)->view()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2011-12-26 20:42:49 -05:00
|
|
|
if ((*i)->view()->layer_display() == Expanded) {
|
|
|
|
(*i)->view()->set_layer_display (Stacked);
|
|
|
|
}
|
|
|
|
}
|
2011-12-29 17:14:15 -05:00
|
|
|
}
|
2011-12-26 20:42:49 -05:00
|
|
|
|
|
|
|
void
|
|
|
|
RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
|
|
|
|
{
|
|
|
|
RegionMotionDrag::finished (ev, movement_occurred);
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2009-06-01 20:39:57 -04:00
|
|
|
if (!movement_occurred) {
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
/* just a click */
|
2014-01-27 10:09:58 -05:00
|
|
|
|
|
|
|
if (was_double_click() && !_views.empty()) {
|
|
|
|
DraggingView dv = _views.front();
|
2016-12-12 21:02:57 -05:00
|
|
|
_editor->edit_region (dv.view);
|
2014-01-27 10:09:58 -05:00
|
|
|
}
|
|
|
|
|
2009-08-23 22:10:46 -04:00
|
|
|
return;
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2010-09-18 16:22:03 -04:00
|
|
|
assert (!_views.empty ());
|
|
|
|
|
2011-11-13 14:51:30 -05:00
|
|
|
/* We might have hidden region views so that they weren't visible during the drag
|
|
|
|
(when they have been reparented). Now everything can be shown again, as region
|
|
|
|
views are back in their track parent groups.
|
|
|
|
*/
|
|
|
|
for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
|
|
|
|
i->view->get_canvas_group()->show ();
|
|
|
|
}
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2020-11-30 12:59:17 -05:00
|
|
|
bool const changed_position = (_last_position != _primary->region()->position());
|
2021-05-04 13:55:23 -04:00
|
|
|
bool changed_tracks;
|
|
|
|
|
2021-05-06 14:03:27 -04:00
|
|
|
if (_views.front().time_axis_view >= (int) _time_axis_views.size()) {
|
2021-05-05 20:33:37 -04:00
|
|
|
/* in the drop zone */
|
|
|
|
changed_tracks = true;
|
|
|
|
} else {
|
|
|
|
|
|
|
|
if (_views.front().time_axis_view < 0) {
|
|
|
|
if (&_views.front().view->get_time_axis_view()) {
|
|
|
|
changed_tracks = true;
|
|
|
|
} else {
|
|
|
|
changed_tracks = false;
|
|
|
|
}
|
2021-05-04 13:55:23 -04:00
|
|
|
} else {
|
2021-05-05 20:33:37 -04:00
|
|
|
changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
|
2021-05-04 13:55:23 -04:00
|
|
|
}
|
|
|
|
}
|
2015-02-12 14:28:44 -05:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
if (_copy) {
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-08-23 21:02:40 -04:00
|
|
|
finished_copy (
|
|
|
|
changed_position,
|
|
|
|
changed_tracks,
|
2017-01-26 08:41:17 -05:00
|
|
|
_last_position,
|
2016-06-13 13:21:52 -04:00
|
|
|
ev->button.state
|
2010-08-23 21:02:40 -04:00
|
|
|
);
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-08-23 21:02:40 -04:00
|
|
|
} else {
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-08-23 21:02:40 -04:00
|
|
|
finished_no_copy (
|
|
|
|
changed_position,
|
|
|
|
changed_tracks,
|
2017-01-26 08:41:17 -05:00
|
|
|
_last_position,
|
2016-06-13 13:21:52 -04:00
|
|
|
ev->button.state
|
2010-08-23 21:02:40 -04:00
|
|
|
);
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-08-23 21:02:40 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-17 17:24:42 -04:00
|
|
|
RouteTimeAxisView*
|
2014-06-19 11:16:27 -04:00
|
|
|
RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
|
2015-03-24 16:59:57 -04:00
|
|
|
{
|
2018-11-28 08:56:04 -05:00
|
|
|
if (!ARDOUR_UI_UTILS::engine_is_running ()) {
|
2018-11-27 07:18:39 -05:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-05-17 17:31:11 -04:00
|
|
|
/* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
|
|
|
|
new track.
|
2014-05-17 17:24:42 -04:00
|
|
|
*/
|
2016-12-02 00:17:51 -05:00
|
|
|
TimeAxisView* tav = 0;
|
2014-05-17 17:24:42 -04:00
|
|
|
try {
|
|
|
|
if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
|
|
|
|
list<boost::shared_ptr<AudioTrack> > audio_tracks;
|
2020-10-15 01:09:22 -04:00
|
|
|
uint32_t output_chan = region->sources().size();
|
2015-03-27 14:01:19 -04:00
|
|
|
if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) {
|
|
|
|
output_chan = _editor->session()->master_out()->n_inputs().n_audio();
|
|
|
|
}
|
2020-10-15 01:09:22 -04:00
|
|
|
audio_tracks = _editor->session()->new_audio_track (region->sources().size(), output_chan, 0, 1, region->name(), PresentationInfo::max_order);
|
2017-05-05 07:31:49 -04:00
|
|
|
tav =_editor->time_axis_view_from_stripable (audio_tracks.front());
|
2014-05-17 17:24:42 -04:00
|
|
|
} else {
|
|
|
|
ChanCount one_midi_port (DataType::MIDI, 1);
|
|
|
|
list<boost::shared_ptr<MidiTrack> > midi_tracks;
|
2017-01-20 15:47:09 -05:00
|
|
|
midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port,
|
|
|
|
Config->get_strict_io () || Profile->get_mixbus (),
|
|
|
|
boost::shared_ptr<ARDOUR::PluginInfo>(),
|
2016-05-17 08:21:05 -04:00
|
|
|
(ARDOUR::Plugin::PresetRecord*) 0,
|
|
|
|
(ARDOUR::RouteGroup*) 0, 1, region->name(), PresentationInfo::max_order);
|
2017-05-05 07:31:49 -04:00
|
|
|
tav = _editor->time_axis_view_from_stripable (midi_tracks.front());
|
2016-12-02 00:17:51 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (tav) {
|
|
|
|
tav->set_height (original->current_height());
|
2015-03-24 16:59:57 -04:00
|
|
|
}
|
2014-05-17 17:24:42 -04:00
|
|
|
} catch (...) {
|
|
|
|
error << _("Could not create new track after region placed in the drop zone") << endmsg;
|
|
|
|
}
|
2016-12-02 00:17:51 -05:00
|
|
|
|
|
|
|
return dynamic_cast<RouteTimeAxisView*> (tav);
|
2014-05-17 17:24:42 -04:00
|
|
|
}
|
|
|
|
|
2010-08-23 21:02:40 -04:00
|
|
|
void
|
2021-06-15 13:41:25 -04:00
|
|
|
RegionMoveDrag::clear_draggingview_list ()
|
|
|
|
{
|
|
|
|
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
|
|
|
|
list<DraggingView>::const_iterator next = i;
|
|
|
|
++next;
|
|
|
|
delete i->view;
|
|
|
|
i = next;
|
|
|
|
}
|
|
|
|
_views.clear();
|
|
|
|
}
|
|
|
|
|
2021-07-04 11:42:36 -04:00
|
|
|
void
|
|
|
|
RegionMoveDrag::finished_copy (bool const changed_position, bool const changed_tracks, timepos_t const & last_position, int32_t const ev_state)
|
2010-08-23 21:02:40 -04:00
|
|
|
{
|
|
|
|
RegionSelection new_views;
|
|
|
|
PlaylistSet modified_playlists;
|
2015-03-24 16:59:57 -04:00
|
|
|
RouteTimeAxisView* new_time_axis_view = 0;
|
2020-11-30 12:59:17 -05:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
timecnt_t const drag_delta = _last_position.distance (_primary->region()->nt_position());
|
2021-06-04 13:09:07 -04:00
|
|
|
RegionList ripple_exclude;
|
2010-08-23 21:02:40 -04:00
|
|
|
|
2021-06-15 13:41:25 -04:00
|
|
|
/*x_contrained on the same track: this will just make a duplicate region in the same place: abort the operation */
|
|
|
|
if (_x_constrained && !changed_tracks) {
|
|
|
|
clear_draggingview_list();
|
|
|
|
_editor->abort_reversible_command ();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-02-09 10:50:54 -05:00
|
|
|
typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
|
|
|
|
PlaylistMapping playlist_mapping;
|
|
|
|
|
2021-06-04 13:09:30 -04:00
|
|
|
/* determine boundaries of dragged regions, across all playlists */
|
|
|
|
samplepos_t extent_min = max_samplepos;
|
|
|
|
samplepos_t extent_max = 0;
|
|
|
|
|
2021-06-04 11:42:22 -04:00
|
|
|
/* insert the regions into their (potentially) new (or existing) playlists */
|
|
|
|
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
|
2014-05-17 17:24:42 -04:00
|
|
|
|
|
|
|
RouteTimeAxisView* dest_rtv = 0;
|
2010-08-23 21:02:40 -04:00
|
|
|
|
2015-09-07 20:51:58 -04:00
|
|
|
if (i->view->region()->locked() || (i->view->region()->video_locked() && !_ignore_video_lock)) {
|
2010-08-23 21:02:40 -04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t where;
|
2010-09-06 08:34:11 -04:00
|
|
|
|
2010-08-23 21:02:40 -04:00
|
|
|
if (changed_position && !_x_constrained) {
|
2020-11-30 12:59:17 -05:00
|
|
|
where = timepos_t (i->view->region()->position().earlier (drag_delta));
|
2009-05-30 14:25:59 -04:00
|
|
|
} else {
|
2020-11-30 12:59:17 -05:00
|
|
|
where = timepos_t (i->view->region()->position());
|
2010-08-23 21:02:40 -04:00
|
|
|
}
|
2015-02-09 10:50:54 -05:00
|
|
|
|
2021-06-04 13:09:50 -04:00
|
|
|
/* compute full extent of regions that we're going to insert */
|
|
|
|
|
|
|
|
if (where.sample < extent_min) {
|
|
|
|
extent_min = where.sample;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (where.sample + i->view->region()->length() > extent_max) {
|
|
|
|
extent_max = where.sample + i->view->region()->length();
|
|
|
|
}
|
|
|
|
|
2015-03-26 19:50:27 -04:00
|
|
|
if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
|
2015-02-09 10:50:54 -05:00
|
|
|
/* dragged to drop zone */
|
|
|
|
|
|
|
|
PlaylistMapping::iterator pm;
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2015-02-09 10:50:54 -05:00
|
|
|
if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
|
|
|
|
/* first region from this original playlist: create a new track */
|
2014-06-19 11:16:27 -04:00
|
|
|
new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
|
2016-12-02 00:17:51 -05:00
|
|
|
if(!new_time_axis_view) {
|
|
|
|
Drag::abort();
|
|
|
|
return;
|
|
|
|
}
|
2015-02-09 10:50:54 -05:00
|
|
|
playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
|
|
|
|
dest_rtv = new_time_axis_view;
|
|
|
|
} else {
|
|
|
|
/* we already created a new track for regions from this playlist, use it */
|
|
|
|
dest_rtv = pm->second;
|
2014-05-17 17:24:42 -04:00
|
|
|
}
|
|
|
|
} else {
|
2015-02-09 10:50:54 -05:00
|
|
|
/* destination time axis view is the one we dragged to */
|
2014-05-17 17:24:42 -04:00
|
|
|
dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
|
2015-03-24 16:59:57 -04:00
|
|
|
}
|
|
|
|
|
2014-05-17 17:24:42 -04:00
|
|
|
if (dest_rtv != 0) {
|
2016-12-12 12:33:58 -05:00
|
|
|
RegionView* new_view;
|
2017-01-26 08:41:17 -05:00
|
|
|
if (i->view == _primary && !_x_constrained) {
|
2020-10-15 01:09:22 -04:00
|
|
|
new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, last_position,
|
2017-01-26 08:41:17 -05:00
|
|
|
modified_playlists, true);
|
2016-12-12 12:33:58 -05:00
|
|
|
} else {
|
2020-10-15 01:09:22 -04:00
|
|
|
if (i->view->region()->position_time_domain() == Temporal::AudioTime) {
|
|
|
|
new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where,
|
2017-01-26 08:41:17 -05:00
|
|
|
modified_playlists);
|
2016-12-13 10:08:59 -05:00
|
|
|
} else {
|
2020-10-15 01:09:22 -04:00
|
|
|
new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where,
|
2017-01-26 08:41:17 -05:00
|
|
|
modified_playlists, true);
|
2016-12-13 10:08:59 -05:00
|
|
|
}
|
2016-12-12 12:33:58 -05:00
|
|
|
}
|
2016-06-16 13:20:37 -04:00
|
|
|
|
2014-05-17 17:24:42 -04:00
|
|
|
if (new_view != 0) {
|
|
|
|
new_views.push_back (new_view);
|
2021-06-04 13:10:09 -04:00
|
|
|
ripple_exclude.push_back (new_view->region());
|
2014-05-17 17:24:42 -04:00
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
2010-08-23 21:02:40 -04:00
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2021-06-15 13:41:25 -04:00
|
|
|
/* in the past this was done in the main iterator loop; no need */
|
|
|
|
clear_draggingview_list();
|
|
|
|
|
2021-06-04 13:10:38 -04:00
|
|
|
for (PlaylistSet::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
|
2021-06-18 15:54:44 -04:00
|
|
|
if (_editor->should_ripple()) {
|
2021-06-04 13:10:38 -04:00
|
|
|
(*p)->ripple (extent_min, extent_max - extent_min, &ripple_exclude);
|
|
|
|
}
|
|
|
|
(*p)->rdiff_and_add_command (_editor->session());
|
|
|
|
}
|
|
|
|
|
2021-06-15 17:13:38 -04:00
|
|
|
/* Ripple marks & ranges if appropriate */
|
|
|
|
|
|
|
|
if (Config->get_edit_mode() == RippleAll) {
|
2021-06-16 21:27:30 -04:00
|
|
|
_editor->ripple_marks (_primary->region()->playlist(), extent_min, extent_max - extent_min);
|
2021-06-15 17:13:38 -04:00
|
|
|
}
|
|
|
|
|
2011-06-01 13:00:29 -04:00
|
|
|
/* If we've created new regions either by copying or moving
|
|
|
|
to a new track, we want to replace the old selection with the new ones
|
2010-08-23 21:02:40 -04:00
|
|
|
*/
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2010-08-23 21:02:40 -04:00
|
|
|
if (new_views.size() > 0) {
|
|
|
|
_editor->selection->set (new_views);
|
|
|
|
}
|
|
|
|
|
|
|
|
_editor->commit_reversible_command ();
|
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2010-08-23 21:02:40 -04:00
|
|
|
void
|
|
|
|
RegionMoveDrag::finished_no_copy (
|
|
|
|
bool const changed_position,
|
|
|
|
bool const changed_tracks,
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t const & last_position,
|
2016-06-13 13:21:52 -04:00
|
|
|
int32_t const ev_state
|
2010-08-23 21:02:40 -04:00
|
|
|
)
|
|
|
|
{
|
|
|
|
RegionSelection new_views;
|
|
|
|
PlaylistSet modified_playlists;
|
|
|
|
PlaylistSet frozen_playlists;
|
2011-12-27 16:10:20 -05:00
|
|
|
set<RouteTimeAxisView*> views_to_update;
|
2014-05-17 15:51:08 -04:00
|
|
|
RouteTimeAxisView* new_time_axis_view = 0;
|
2020-11-30 12:59:17 -05:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
timecnt_t const drag_delta = last_position.distance (_primary->region()->nt_position());
|
2021-06-04 13:11:06 -04:00
|
|
|
RegionList ripple_exclude;
|
2010-08-23 21:02:40 -04:00
|
|
|
|
2015-02-09 10:50:54 -05:00
|
|
|
typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
|
|
|
|
PlaylistMapping playlist_mapping;
|
|
|
|
|
2021-06-15 17:13:38 -04:00
|
|
|
/* determine earliest position affected by the drag, across all playlists */
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t extent_min = timepos_t::max (Temporal::AudioTime); /* NUTEMPO fix domain */
|
2021-06-04 13:11:23 -04:00
|
|
|
|
2016-10-26 21:32:59 -04:00
|
|
|
std::set<boost::shared_ptr<const Region> > uniq;
|
2010-04-06 20:17:54 -04:00
|
|
|
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2010-04-06 20:17:54 -04:00
|
|
|
RegionView* rv = i->view;
|
2014-05-17 15:51:08 -04:00
|
|
|
RouteTimeAxisView* dest_rtv = 0;
|
2010-03-01 19:00:00 -05:00
|
|
|
|
2015-09-07 20:51:58 -04:00
|
|
|
if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
|
2009-05-30 14:25:59 -04:00
|
|
|
++i;
|
|
|
|
continue;
|
|
|
|
}
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2016-10-26 21:32:59 -04:00
|
|
|
if (uniq.find (rv->region()) != uniq.end()) {
|
|
|
|
/* prevent duplicate moves when selecting regions from shared playlists */
|
|
|
|
++i;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
uniq.insert(rv->region());
|
|
|
|
|
2015-03-26 19:50:27 -04:00
|
|
|
if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
|
2015-02-09 10:50:54 -05:00
|
|
|
/* dragged to drop zone */
|
|
|
|
|
|
|
|
PlaylistMapping::iterator pm;
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2015-02-09 10:50:54 -05:00
|
|
|
if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
|
|
|
|
/* first region from this original playlist: create a new track */
|
|
|
|
new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
|
2016-12-02 00:17:51 -05:00
|
|
|
if(!new_time_axis_view) { // New track creation failed
|
|
|
|
Drag::abort();
|
|
|
|
return;
|
|
|
|
}
|
2015-02-09 10:50:54 -05:00
|
|
|
playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
|
|
|
|
dest_rtv = new_time_axis_view;
|
|
|
|
} else {
|
|
|
|
/* we already created a new track for regions from this playlist, use it */
|
|
|
|
dest_rtv = pm->second;
|
2014-05-17 15:51:08 -04:00
|
|
|
}
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2014-05-17 15:51:08 -04:00
|
|
|
} else {
|
2015-02-09 10:50:54 -05:00
|
|
|
/* destination time axis view is the one we dragged to */
|
2014-05-17 15:51:08 -04:00
|
|
|
dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
|
2015-03-24 16:59:57 -04:00
|
|
|
}
|
2015-02-09 10:50:54 -05:00
|
|
|
|
2014-05-17 15:51:08 -04:00
|
|
|
assert (dest_rtv);
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2014-05-17 15:51:08 -04:00
|
|
|
double const dest_layer = i->layer;
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2011-12-27 16:10:20 -05:00
|
|
|
views_to_update.insert (dest_rtv);
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t where;
|
2010-08-23 21:02:40 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
if (changed_position && !_x_constrained) {
|
2020-11-30 12:59:17 -05:00
|
|
|
where = rv->region()->position().earlier (drag_delta);
|
2009-05-30 14:25:59 -04:00
|
|
|
} else {
|
2020-11-30 12:59:17 -05:00
|
|
|
where = rv->region()->position();
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2021-06-21 11:35:13 -04:00
|
|
|
/* compute full extent of regions that we're going to insert */
|
2021-06-04 13:11:42 -04:00
|
|
|
|
2021-06-15 17:13:38 -04:00
|
|
|
if (rv->region()->position() < extent_min) {
|
|
|
|
extent_min = rv->region()->position ();
|
2021-06-04 13:11:42 -04:00
|
|
|
}
|
|
|
|
|
2021-06-15 17:13:38 -04:00
|
|
|
cerr << "drag emin " << extent_min << " emax " << drag_delta << endl;
|
2021-06-04 13:11:42 -04:00
|
|
|
|
2010-08-23 21:02:40 -04:00
|
|
|
if (changed_tracks) {
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2010-08-23 21:02:40 -04:00
|
|
|
/* insert into new playlist */
|
2016-12-12 12:33:58 -05:00
|
|
|
RegionView* new_view;
|
2017-02-15 16:57:03 -05:00
|
|
|
if (rv == _primary && !_x_constrained) {
|
2016-12-12 12:33:58 -05:00
|
|
|
new_view = insert_region_into_playlist (
|
2020-10-15 01:09:22 -04:00
|
|
|
RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, last_position,modified_playlists, true);
|
2016-12-12 12:33:58 -05:00
|
|
|
} else {
|
2020-10-15 01:09:22 -04:00
|
|
|
if (rv->region()->position_time_domain() == Temporal::AudioTime) {
|
2016-12-13 10:08:59 -05:00
|
|
|
|
|
|
|
new_view = insert_region_into_playlist (
|
2020-10-15 01:09:22 -04:00
|
|
|
RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists);
|
2016-12-13 10:08:59 -05:00
|
|
|
} else {
|
|
|
|
new_view = insert_region_into_playlist (
|
2020-10-15 01:09:22 -04:00
|
|
|
RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists, true);
|
2016-12-13 10:08:59 -05:00
|
|
|
}
|
2016-12-12 12:33:58 -05:00
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2010-08-23 21:02:40 -04:00
|
|
|
if (new_view == 0) {
|
2009-05-30 14:25:59 -04:00
|
|
|
++i;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2010-08-23 21:02:40 -04:00
|
|
|
new_views.push_back (new_view);
|
2010-03-01 19:00:00 -05:00
|
|
|
|
2010-08-23 21:02:40 -04:00
|
|
|
/* remove from old playlist */
|
2010-03-01 19:00:00 -05:00
|
|
|
|
2010-08-23 21:02:40 -04:00
|
|
|
/* 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();
|
2014-06-13 10:56:46 -04:00
|
|
|
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2010-08-23 21:02:40 -04:00
|
|
|
remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
|
2009-05-30 14:25:59 -04:00
|
|
|
|
|
|
|
} else {
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2014-04-14 08:34:18 -04:00
|
|
|
boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
|
|
|
|
|
|
|
|
/* this movement may result in a crossfade being modified, or a layering change,
|
|
|
|
so we need to get undo data from the playlist as well as the region.
|
|
|
|
*/
|
|
|
|
|
|
|
|
pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
|
|
|
|
if (r.second) {
|
|
|
|
playlist->clear_changes ();
|
|
|
|
}
|
|
|
|
|
2010-08-25 13:32:08 -04:00
|
|
|
rv->region()->clear_changes ();
|
2010-02-08 14:41:43 -05:00
|
|
|
|
2009-10-14 12:10:01 -04:00
|
|
|
/*
|
|
|
|
motion on the same track. plonk the previously reparented region
|
2009-05-30 14:25:59 -04:00
|
|
|
back to its original canvas group (its streamview).
|
|
|
|
No need to do anything for copies as they are fake regions which will be deleted.
|
|
|
|
*/
|
|
|
|
|
2013-04-04 00:32:52 -04:00
|
|
|
rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
|
|
|
|
rv->get_canvas_group()->set_y_position (i->initial_y);
|
2012-05-07 18:02:17 -04:00
|
|
|
rv->drag_end ();
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
/* just change the model */
|
2011-12-29 17:14:15 -05:00
|
|
|
if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
|
2011-12-30 15:05:48 -05:00
|
|
|
playlist->set_layer (rv->region(), dest_layer);
|
2009-08-25 20:06:21 -04:00
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-01-25 15:34:09 -05:00
|
|
|
/* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2014-04-14 08:34:18 -04:00
|
|
|
r = frozen_playlists.insert (playlist);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2010-08-23 21:02:40 -04:00
|
|
|
if (r.second) {
|
|
|
|
playlist->freeze ();
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
2020-10-15 01:09:22 -04:00
|
|
|
|
|
|
|
rv->region()->set_position (where);
|
|
|
|
|
2010-08-23 21:02:40 -04:00
|
|
|
_editor->session()->add_command (new StatefulDiffCommand (rv->region()));
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2021-06-21 11:35:13 -04:00
|
|
|
// ripple_exclude.push_back (i->view->region());
|
2021-06-04 13:12:09 -04:00
|
|
|
|
2010-08-23 21:02:40 -04:00
|
|
|
if (changed_tracks) {
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
/* 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
|
2010-08-23 21:02:40 -04:00
|
|
|
trace of it from _views (i.e. there were N regions selected, we removed 1,
|
2009-05-30 14:25:59 -04:00
|
|
|
but since its the same playlist for N tracks, all N tracks updated themselves, removed the
|
2010-08-23 21:02:40 -04:00
|
|
|
corresponding regionview, and _views is now empty).
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2010-08-23 21:02:40 -04:00
|
|
|
This could have invalidated any and all iterators into _views.
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2010-08-23 21:02:40 -04:00
|
|
|
The heuristic we use here is: if the region selection is empty, break out of the loop
|
2009-05-30 14:25:59 -04:00
|
|
|
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.
|
|
|
|
|
2010-08-23 21:02:40 -04:00
|
|
|
EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
|
2009-05-30 14:25:59 -04:00
|
|
|
we can just iterate.
|
|
|
|
*/
|
|
|
|
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
if (_views.empty()) {
|
2010-11-25 15:37:39 -05:00
|
|
|
break;
|
2009-10-14 12:10:01 -04:00
|
|
|
} else {
|
2009-05-30 14:25:59 -04:00
|
|
|
i = _views.begin();
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
}
|
2010-03-01 19:00:00 -05:00
|
|
|
|
2021-06-16 20:39:45 -04:00
|
|
|
for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
|
|
|
|
(*p)->thaw();
|
|
|
|
}
|
|
|
|
|
2021-06-21 11:35:13 -04:00
|
|
|
if (Config->get_edit_mode() == RippleAll) {
|
|
|
|
_editor->ripple_marks (_primary->region()->playlist(), extent_min, -drag_delta);
|
|
|
|
}
|
2021-06-16 20:39:45 -04:00
|
|
|
|
2011-06-01 13:00:29 -04:00
|
|
|
/* If we've created new regions either by copying or moving
|
|
|
|
to a new track, we want to replace the old selection with the new ones
|
2009-12-10 12:23:56 -05:00
|
|
|
*/
|
2010-03-01 19:00:00 -05:00
|
|
|
|
2009-12-10 12:23:56 -05:00
|
|
|
if (new_views.size() > 0) {
|
|
|
|
_editor->selection->set (new_views);
|
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2009-08-23 22:10:46 -04:00
|
|
|
_editor->commit_reversible_command ();
|
2011-12-27 16:10:20 -05:00
|
|
|
|
|
|
|
/* We have futzed with the layering of canvas items on our streamviews.
|
|
|
|
If any region changed layer, this will have resulted in the stream
|
2011-12-29 17:14:15 -05:00
|
|
|
views being asked to set up their region views, and all will be well.
|
|
|
|
If not, we might now have badly-ordered region views. Ask the StreamViews
|
|
|
|
involved to sort themselves out, just in case.
|
2011-12-27 16:10:20 -05:00
|
|
|
*/
|
2011-12-29 17:14:15 -05:00
|
|
|
|
2011-12-27 16:10:20 -05:00
|
|
|
for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
|
|
|
|
(*i)->view()->playlist_layered ((*i)->track ());
|
|
|
|
}
|
2010-08-23 21:02:40 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
|
|
|
|
* @param region Region to remove.
|
|
|
|
* @param playlist playlist To remove from.
|
|
|
|
* @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
|
2010-08-25 13:32:08 -04:00
|
|
|
* that clear_changes () is only called once per playlist.
|
2010-08-23 21:02:40 -04:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
RegionMoveDrag::remove_region_from_playlist (
|
|
|
|
boost::shared_ptr<Region> region,
|
|
|
|
boost::shared_ptr<Playlist> playlist,
|
|
|
|
PlaylistSet& modified_playlists
|
|
|
|
)
|
|
|
|
{
|
|
|
|
pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2010-08-23 21:02:40 -04:00
|
|
|
if (r.second) {
|
2010-08-25 13:32:08 -04:00
|
|
|
playlist->clear_changes ();
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
2010-08-23 21:02:40 -04:00
|
|
|
|
2021-06-04 11:22:44 -04:00
|
|
|
/* XX NEED TO RIPPLE */
|
|
|
|
|
|
|
|
playlist->remove_region (region);
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-08-23 21:02:40 -04:00
|
|
|
|
|
|
|
/** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
|
|
|
|
* clearing the playlist's diff history first if necessary.
|
|
|
|
* @param region Region to insert.
|
|
|
|
* @param dest_rtv Destination RouteTimeAxisView.
|
|
|
|
* @param dest_layer Destination layer.
|
|
|
|
* @param where Destination position.
|
|
|
|
* @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
|
2011-06-01 13:00:29 -04:00
|
|
|
* that clear_changes () is only called once per playlist.
|
2010-08-23 21:02:40 -04:00
|
|
|
* @return New RegionView, or 0 if no insert was performed.
|
|
|
|
*/
|
|
|
|
RegionView *
|
|
|
|
RegionMoveDrag::insert_region_into_playlist (
|
|
|
|
boost::shared_ptr<Region> region,
|
2017-01-26 08:41:17 -05:00
|
|
|
RouteTimeAxisView* dest_rtv,
|
|
|
|
layer_t dest_layer,
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t const & where,
|
2017-01-26 08:41:17 -05:00
|
|
|
PlaylistSet& modified_playlists,
|
|
|
|
bool for_music
|
2010-08-23 21:02:40 -04:00
|
|
|
)
|
|
|
|
{
|
|
|
|
boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
|
|
|
|
if (!dest_playlist) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* arrange to collect the new region view that will be created as a result of our playlist insertion */
|
|
|
|
_new_region_view = 0;
|
|
|
|
sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
|
|
|
|
|
2011-06-01 13:00:29 -04:00
|
|
|
/* clear history for the playlist we are about to insert to, provided we haven't already done so */
|
2010-08-23 21:02:40 -04:00
|
|
|
pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
|
2021-06-04 13:14:35 -04:00
|
|
|
|
2010-08-23 21:02:40 -04:00
|
|
|
if (r.second) {
|
2010-08-25 13:32:08 -04:00
|
|
|
dest_playlist->clear_changes ();
|
2021-06-04 13:14:35 -04:00
|
|
|
dest_playlist->clear_owned_changes ();
|
|
|
|
/* cannot freeze because we need the new region announcements */
|
2010-08-23 21:02:40 -04:00
|
|
|
}
|
2021-06-04 13:14:35 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
dest_playlist->add_region (region, where, 1.0, for_music);
|
2010-08-23 21:02:40 -04:00
|
|
|
|
2011-12-26 20:42:49 -05:00
|
|
|
if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
|
2011-12-30 15:05:48 -05:00
|
|
|
dest_playlist->set_layer (region, dest_layer);
|
2010-08-23 21:02:40 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
c.disconnect ();
|
|
|
|
|
|
|
|
assert (_new_region_view);
|
|
|
|
|
|
|
|
return _new_region_view;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
RegionMoveDrag::collect_new_region_view (RegionView* rv)
|
|
|
|
{
|
|
|
|
_new_region_view = rv;
|
|
|
|
}
|
|
|
|
|
2010-01-07 20:28:15 -05:00
|
|
|
void
|
2010-12-28 14:17:37 -05:00
|
|
|
RegionMoveDrag::aborted (bool movement_occurred)
|
2010-01-07 20:28:15 -05:00
|
|
|
{
|
|
|
|
if (_copy) {
|
2021-06-15 13:41:25 -04:00
|
|
|
clear_draggingview_list();
|
2010-01-07 20:28:15 -05:00
|
|
|
} else {
|
2010-12-28 14:17:37 -05:00
|
|
|
RegionMotionDrag::aborted (movement_occurred);
|
2010-01-07 20:28:15 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-12-28 14:17:37 -05:00
|
|
|
RegionMotionDrag::aborted (bool)
|
2010-01-07 20:28:15 -05:00
|
|
|
{
|
2011-12-26 20:42:49 -05:00
|
|
|
for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
|
2013-07-09 09:45:54 -04:00
|
|
|
|
|
|
|
StreamView* sview = (*i)->view();
|
|
|
|
|
|
|
|
if (sview) {
|
|
|
|
if (sview->layer_display() == Expanded) {
|
|
|
|
sview->set_layer_display (Stacked);
|
|
|
|
}
|
2011-12-26 20:42:49 -05:00
|
|
|
}
|
|
|
|
}
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2010-04-06 20:17:54 -04:00
|
|
|
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
|
|
|
|
RegionView* rv = i->view;
|
|
|
|
TimeAxisView* tv = &(rv->get_time_axis_view ());
|
2010-01-07 20:28:15 -05:00
|
|
|
RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
|
|
|
|
assert (rtv);
|
2013-04-04 00:32:52 -04:00
|
|
|
rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
|
|
|
|
rv->get_canvas_group()->set_y_position (0);
|
2012-05-07 18:02:17 -04:00
|
|
|
rv->drag_end ();
|
2010-04-06 20:17:54 -04:00
|
|
|
rv->move (-_total_x_delta, 0);
|
|
|
|
rv->set_height (rtv->view()->child_height ());
|
2010-01-07 20:28:15 -05:00
|
|
|
}
|
2021-06-21 14:45:04 -04:00
|
|
|
|
|
|
|
for (vector<ArdourMarker*>::iterator m = ripple_markers.begin(); m != ripple_markers.end(); ++m) {
|
|
|
|
(*m)->the_item().move (Duple (-_total_x_delta, 0.));
|
|
|
|
}
|
2010-01-07 20:28:15 -05:00
|
|
|
}
|
2011-03-02 12:00:35 -05:00
|
|
|
|
|
|
|
/** @param b true to brush, otherwise false.
|
|
|
|
* @param c true to make copies of the regions being moved, otherwise false.
|
|
|
|
*/
|
2021-06-15 17:09:24 -04:00
|
|
|
RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool c)
|
|
|
|
: RegionMotionDrag (e, i, p, v)
|
2015-02-12 13:37:18 -05:00
|
|
|
, _copy (c)
|
2015-10-23 13:59:57 -04:00
|
|
|
, _new_region_view (0)
|
2009-06-08 15:28:51 -04:00
|
|
|
{
|
2010-09-09 17:34:45 -04:00
|
|
|
DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2020-11-30 12:59:17 -05:00
|
|
|
_last_position = _primary->region()->position();
|
2009-06-08 15:28:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-10-15 01:09:22 -04:00
|
|
|
RegionMoveDrag::setup_pointer_offset ()
|
2009-06-08 15:28:51 -04:00
|
|
|
{
|
2020-10-15 01:09:22 -04:00
|
|
|
_pointer_offset = timecnt_t (_last_position.distance (raw_grab_time()), _last_position);
|
2009-06-08 15:28:51 -04:00
|
|
|
}
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, timepos_t const & pos)
|
|
|
|
: RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
|
2009-06-08 15:28:51 -04:00
|
|
|
{
|
2010-09-09 17:34:45 -04:00
|
|
|
DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2009-07-09 13:58:13 -04:00
|
|
|
assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
|
|
|
|
(boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
|
2009-06-08 15:28:51 -04:00
|
|
|
|
|
|
|
_primary = v->view()->create_region_view (r, false, false);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-06-08 15:28:51 -04:00
|
|
|
_primary->get_canvas_group()->show ();
|
|
|
|
_primary->set_position (pos, 0);
|
2014-06-19 11:16:27 -04:00
|
|
|
_views.push_back (DraggingView (_primary, this, v));
|
2009-06-08 15:28:51 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
_last_position = pos;
|
2009-06-08 15:28:51 -04:00
|
|
|
|
|
|
|
_item = _primary->get_canvas_group ();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2016-06-13 13:21:52 -04:00
|
|
|
RegionInsertDrag::finished (GdkEvent * event, bool)
|
2009-06-08 15:28:51 -04:00
|
|
|
{
|
2015-03-25 07:11:49 -04:00
|
|
|
int pos = _views.front().time_axis_view;
|
2015-03-26 19:50:27 -04:00
|
|
|
assert(pos >= 0 && pos < (int)_time_axis_views.size());
|
2015-03-25 07:11:49 -04:00
|
|
|
|
|
|
|
RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
|
2009-06-08 15:28:51 -04:00
|
|
|
|
2013-04-04 00:32:52 -04:00
|
|
|
_primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
|
|
|
|
_primary->get_canvas_group()->set_y_position (0);
|
2009-06-08 15:28:51 -04:00
|
|
|
|
|
|
|
boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
|
|
|
|
|
2011-01-19 12:38:56 -05:00
|
|
|
_editor->begin_reversible_command (Operations::insert_region);
|
2010-11-25 15:37:39 -05:00
|
|
|
playlist->clear_changes ();
|
2021-05-23 11:42:33 -04:00
|
|
|
playlist->clear_owned_changes ();
|
2017-01-30 11:03:45 -05:00
|
|
|
_editor->snap_to_with_modifier (_last_position, event);
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
playlist->add_region (_primary->region (), _last_position, 1.0, false);
|
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
|
|
|
|
2021-06-02 18:34:13 -04:00
|
|
|
if (_editor->should_ripple()) {
|
2020-10-15 01:09:22 -04:00
|
|
|
playlist->ripple (_last_position, _primary->region()->nt_length(), _primary->region());
|
2021-06-02 18:34:13 -04:00
|
|
|
} else {
|
|
|
|
playlist->rdiff_and_add_command (_editor->session());
|
2021-04-29 16:56:09 -04:00
|
|
|
}
|
2021-05-23 11:42:33 -04:00
|
|
|
|
2009-06-08 15:28:51 -04:00
|
|
|
_editor->commit_reversible_command ();
|
|
|
|
|
|
|
|
delete _primary;
|
|
|
|
_primary = 0;
|
|
|
|
_views.clear ();
|
|
|
|
}
|
|
|
|
|
2010-01-07 20:28:15 -05:00
|
|
|
void
|
2010-12-28 14:17:37 -05:00
|
|
|
RegionInsertDrag::aborted (bool)
|
2010-01-07 20:28:15 -05:00
|
|
|
{
|
2010-05-05 18:09:07 -04:00
|
|
|
delete _primary;
|
|
|
|
_primary = 0;
|
|
|
|
_views.clear ();
|
2010-01-07 20:28:15 -05:00
|
|
|
}
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
/***
|
|
|
|
* ripple mode...
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, timepos_t const & where, const RegionSelection &exclude, bool drag_in_progress)
|
|
|
|
{
|
|
|
|
|
|
|
|
boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (TimelineRange (where, timepos_t::max (where.time_domain()), 0));
|
|
|
|
|
|
|
|
RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
|
|
|
|
RegionSelection to_ripple;
|
|
|
|
for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
|
2020-11-30 12:59:17 -05:00
|
|
|
if ((*i)->position() >= where) {
|
2020-10-15 01:09:22 -04:00
|
|
|
to_ripple.push_back (rtv->view()->find_view(*i));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (RegionSelection::reverse_iterator i = to_ripple.rbegin(); i != to_ripple.rend(); ++i) {
|
|
|
|
if (!exclude.contains (*i)) {
|
|
|
|
// the selection has already been added to _views
|
|
|
|
|
|
|
|
if (drag_in_progress) {
|
|
|
|
// do the same things that RegionMotionDrag::motion does when
|
|
|
|
// first_move is true, for the region views that we're adding
|
|
|
|
// to _views this time
|
|
|
|
|
|
|
|
(*i)->drag_start();
|
|
|
|
ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
|
|
|
|
Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
|
|
|
|
Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
|
|
|
|
rvg->reparent (_editor->_drag_motion_group);
|
|
|
|
|
|
|
|
// we only need to move in the y direction
|
|
|
|
Duple fudge = rv_canvas_offset - dmg_canvas_offset;
|
|
|
|
fudge.x = 0;
|
|
|
|
rvg->move (fudge);
|
|
|
|
|
|
|
|
}
|
|
|
|
_views.push_back (DraggingView (*i, this, tav));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
RegionRippleDrag::remove_unselected_from_views(timecnt_t const & amount, bool move_regions)
|
|
|
|
{
|
|
|
|
ThawList thawlist;
|
|
|
|
|
|
|
|
for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
|
|
|
|
// we added all the regions after the selection
|
|
|
|
|
|
|
|
std::list<DraggingView>::iterator to_erase = i++;
|
|
|
|
if (!_editor->selection->regions.contains (to_erase->view)) {
|
|
|
|
// restore the non-selected regions to their original playlist & positions,
|
|
|
|
// and then ripple them back by the length of the regions that were dragged away
|
|
|
|
// do the same things as RegionMotionDrag::aborted
|
|
|
|
|
|
|
|
RegionView *rv = to_erase->view;
|
|
|
|
TimeAxisView* tv = &(rv->get_time_axis_view ());
|
|
|
|
RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
|
|
|
|
assert (rtv);
|
|
|
|
|
|
|
|
// plonk them back onto their own track
|
|
|
|
rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
|
|
|
|
rv->get_canvas_group()->set_y_position (0);
|
|
|
|
rv->drag_end ();
|
|
|
|
|
|
|
|
if (move_regions) {
|
|
|
|
thawlist.add (rv->region ());
|
|
|
|
// move the underlying region to match the view
|
2020-11-30 12:59:17 -05:00
|
|
|
rv->region()->set_position (rv->region()->position() + amount);
|
2020-10-15 01:09:22 -04:00
|
|
|
} else {
|
|
|
|
// restore the view to match the underlying region's original position
|
|
|
|
#warning NUTEMPO ALERT paul test this code in 5.x /* how can this work ... amount used to be in samples but ::move() expect pixels */
|
|
|
|
//rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
|
|
|
|
}
|
|
|
|
|
|
|
|
rv->set_height (rtv->view()->child_height ());
|
|
|
|
_views.erase (to_erase);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
thawlist.release ();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
|
|
|
|
{
|
|
|
|
if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
|
|
|
|
if (delta_track) {
|
|
|
|
return allow_moves_across_tracks;
|
|
|
|
} else {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
|
|
|
|
: RegionMoveDrag (e, i, p, v, false)
|
|
|
|
{
|
|
|
|
DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
|
|
|
|
// compute length of selection
|
|
|
|
RegionSelection selected_regions = _editor->selection->regions;
|
|
|
|
selection_length = selected_regions.start_time().distance (selected_regions.end_time());
|
|
|
|
|
|
|
|
// Rippling accross tracks disabled. Rippling on all tracks is the way to go in the future.
|
|
|
|
allow_moves_across_tracks = false; // (selected_regions.playlists().size() == 1);
|
|
|
|
prev_tav = NULL;
|
2021-01-25 23:44:19 -05:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
exclude = new RegionList;
|
|
|
|
for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
|
|
|
|
exclude->push_back((*i)->region());
|
|
|
|
}
|
|
|
|
|
|
|
|
// also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
|
|
|
|
RegionSelection copy;
|
|
|
|
selected_regions.by_position(copy); // get selected regions sorted by position into copy
|
|
|
|
|
|
|
|
std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
|
|
|
|
std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
|
|
|
|
|
2021-01-25 23:44:19 -05:00
|
|
|
bool need_time_domain = true;
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
|
|
|
|
// find ripple start point on each applicable playlist
|
|
|
|
RegionView *first_selected_on_this_track = NULL;
|
|
|
|
for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
|
|
|
|
if ((*i)->region()->playlist() == (*pi)) {
|
|
|
|
// region is on this playlist - it's the first, because they're sorted
|
|
|
|
first_selected_on_this_track = *i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2021-01-25 23:44:19 -05:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
|
2021-01-25 23:44:19 -05:00
|
|
|
|
|
|
|
TimeAxisView* tav = &first_selected_on_this_track->get_time_axis_view();
|
|
|
|
|
|
|
|
if (need_time_domain) {
|
|
|
|
RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (tav);
|
|
|
|
prev_amount = timecnt_t (rtav->route()->time_domain());
|
|
|
|
need_time_domain = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
add_all_after_to_views (tav, first_selected_on_this_track->region()->position(), selected_regions, false);
|
2020-10-15 01:09:22 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (allow_moves_across_tracks) {
|
|
|
|
orig_tav = &(*selected_regions.begin())->get_time_axis_view();
|
|
|
|
for (std::list<DraggingView>::const_iterator it = _views.begin(); it != _views.end(); ++it) {
|
|
|
|
_orig_tav_ripples.push_back((*it).view->region());
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
orig_tav = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
RegionRippleDrag::motion (GdkEvent* event, bool first_move)
|
|
|
|
{
|
|
|
|
/* Which trackview is this ? */
|
|
|
|
|
|
|
|
pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
|
|
|
|
RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
|
|
|
|
|
|
|
|
/* 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 an audiotrack.
|
|
|
|
*/
|
|
|
|
_editor->verbose_cursor()->hide ();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
timepos_t where = adjusted_current_time (event);
|
|
|
|
assert (!where.negative());
|
|
|
|
timepos_t after;
|
|
|
|
double delta = compute_x_delta (event, after);
|
|
|
|
|
|
|
|
timecnt_t amount = timecnt_t (_editor->pixel_to_sample (delta), grab_time());
|
|
|
|
|
|
|
|
if (allow_moves_across_tracks) {
|
|
|
|
// all the originally selected regions were on the same track
|
|
|
|
|
|
|
|
timecnt_t adjust;
|
|
|
|
|
|
|
|
if (prev_tav && tv != prev_tav) {
|
|
|
|
// dragged onto a different track
|
|
|
|
// remove the unselected regions from _views, restore them to their original positions
|
|
|
|
// and add the regions after the drop point on the new playlist to _views instead.
|
|
|
|
// undo the effect of rippling the previous playlist, and include the effect of removing
|
|
|
|
// the dragged region(s) from this track
|
|
|
|
|
|
|
|
remove_unselected_from_views (prev_amount, false);
|
|
|
|
// ripple previous playlist according to the regions that have been removed onto the new playlist
|
|
|
|
prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
|
2021-01-25 23:44:19 -05:00
|
|
|
prev_amount = timecnt_t (tv->route()->time_domain());
|
2020-10-15 01:09:22 -04:00
|
|
|
|
|
|
|
// move just the selected regions
|
|
|
|
RegionMoveDrag::motion(event, first_move);
|
|
|
|
|
|
|
|
// ensure that the ripple operation on the new playlist inserts selection_length time
|
|
|
|
adjust = selection_length;
|
|
|
|
// ripple the new current playlist
|
|
|
|
tv->playlist()->ripple (where, amount+adjust, exclude);
|
|
|
|
|
|
|
|
// add regions after point where drag entered this track to subsequent ripples
|
|
|
|
add_all_after_to_views (tv, where, _editor->selection->regions, true);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// motion on same track
|
|
|
|
RegionMoveDrag::motion(event, first_move);
|
|
|
|
}
|
|
|
|
prev_tav = tv;
|
|
|
|
|
|
|
|
// remember what we've done to this playlist so we can undo it if the selection is dragged to another track
|
|
|
|
prev_position = where;
|
|
|
|
} else {
|
|
|
|
// selection encompasses multiple tracks - just drag
|
|
|
|
// cross-track drags are forbidden
|
|
|
|
RegionMoveDrag::motion(event, first_move);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!_x_constrained) {
|
|
|
|
prev_amount += amount;
|
|
|
|
}
|
|
|
|
|
|
|
|
_last_position = after;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
|
|
|
|
{
|
|
|
|
if (!movement_occurred) {
|
|
|
|
|
|
|
|
/* just a click */
|
|
|
|
|
|
|
|
if (was_double_click() && !_views.empty()) {
|
|
|
|
DraggingView dv = _views.front();
|
|
|
|
_editor->edit_region (dv.view);
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_editor->begin_reversible_command(_("Ripple drag"));
|
|
|
|
|
|
|
|
// remove the regions being rippled from the dragging view, updating them to
|
|
|
|
// their new positions
|
|
|
|
|
|
|
|
if (allow_moves_across_tracks) {
|
|
|
|
if (orig_tav) {
|
|
|
|
// if regions were dragged across tracks, we've rippled any later
|
|
|
|
// regions on the track the regions were dragged off, so we need
|
|
|
|
// to add the original track to the undo record
|
|
|
|
orig_tav->playlist()->clear_changes();
|
|
|
|
orig_tav->playlist()->clear_owned_changes();
|
|
|
|
orig_tav->playlist()->freeze ();
|
|
|
|
|
|
|
|
remove_unselected_from_views (prev_amount, true);
|
|
|
|
|
|
|
|
std::list<boost::shared_ptr<Region> >::const_iterator it = _orig_tav_ripples.begin();
|
|
|
|
for (; it != _orig_tav_ripples.end(); ++it) {
|
|
|
|
const boost::shared_ptr<Region> r = *it;
|
|
|
|
bool found = false;
|
|
|
|
for (std::list<DraggingView>::const_iterator it = _views.begin(); it != _views.end(); ++it) {
|
|
|
|
if (it->view->region() == r) {
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!found) {
|
2020-11-30 12:59:17 -05:00
|
|
|
const timepos_t pos_after = r->position();
|
2020-10-15 01:09:22 -04:00
|
|
|
const timepos_t pos_before = pos_after + selection_length;
|
|
|
|
r->set_position(pos_before);
|
|
|
|
r->clear_changes();
|
|
|
|
r->set_position(pos_after);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
orig_tav->playlist()->thaw ();
|
|
|
|
|
|
|
|
vector<Command*> cmds;
|
|
|
|
orig_tav->playlist()->rdiff (cmds);
|
|
|
|
_editor->session()->add_commands (cmds);
|
|
|
|
}
|
|
|
|
if (prev_tav && prev_tav != orig_tav) {
|
|
|
|
prev_tav->playlist()->clear_changes();
|
|
|
|
vector<Command*> cmds;
|
|
|
|
prev_tav->playlist()->rdiff (cmds);
|
|
|
|
_editor->session()->add_commands (cmds);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// selection spanned multiple tracks - all will need adding to undo record
|
|
|
|
|
|
|
|
std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
|
|
|
|
std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
|
|
|
|
|
|
|
|
for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
|
|
|
|
(*pi)->clear_changes();
|
|
|
|
(*pi)->clear_owned_changes();
|
|
|
|
(*pi)->freeze();
|
|
|
|
}
|
|
|
|
|
|
|
|
remove_unselected_from_views (prev_amount, true);
|
|
|
|
|
|
|
|
for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
|
|
|
|
(*pi)->thaw();
|
|
|
|
vector<Command*> cmds;
|
|
|
|
(*pi)->rdiff (cmds);
|
|
|
|
_editor->session()->add_commands (cmds);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// other modified playlists are added to undo by RegionMoveDrag::finished()
|
|
|
|
RegionMoveDrag::finished (event, movement_occurred);
|
|
|
|
_editor->commit_reversible_command();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
RegionRippleDrag::aborted (bool movement_occurred)
|
|
|
|
{
|
|
|
|
RegionMoveDrag::aborted (movement_occurred);
|
|
|
|
_views.clear ();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-07-09 13:58:13 -04:00
|
|
|
RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
|
2009-05-30 14:25:59 -04:00
|
|
|
: Drag (e, i),
|
2010-08-20 08:01:13 -04:00
|
|
|
_view (dynamic_cast<MidiTimeAxisView*> (v))
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2010-09-09 17:34:45 -04:00
|
|
|
DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-08-20 08:01:13 -04:00
|
|
|
assert (_view);
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-08-25 08:23:52 -04:00
|
|
|
RegionCreateDrag::motion (GdkEvent* event, bool first_move)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2009-06-01 20:39:57 -04:00
|
|
|
if (first_move) {
|
2015-11-13 11:14:23 -05:00
|
|
|
_editor->begin_reversible_command (_("create region"));
|
2017-01-26 08:41:17 -05:00
|
|
|
_region = add_midi_region (_view, false);
|
2011-03-01 11:23:31 -05:00
|
|
|
_view->playlist()->freeze ();
|
2010-08-20 08:01:13 -04:00
|
|
|
} else {
|
2017-03-06 18:36:48 -05:00
|
|
|
|
2010-11-25 15:37:39 -05:00
|
|
|
if (_region) {
|
2020-12-18 15:12:41 -05:00
|
|
|
timepos_t const pos (adjusted_current_time (event).beats());
|
2021-02-14 00:23:10 -05:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
if (pos <= grab_time()) {
|
|
|
|
_region->set_initial_position (pos);
|
2010-11-25 15:37:39 -05:00
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
if (pos != grab_time()) {
|
2020-12-18 15:12:41 -05:00
|
|
|
/* Force MIDI regions to use Beats ... for now */
|
|
|
|
timecnt_t const len (grab_time().distance (pos).abs().beats());
|
2020-10-15 01:09:22 -04:00
|
|
|
_region->set_length (len);
|
2017-03-06 18:36:48 -05:00
|
|
|
}
|
2010-11-25 15:37:39 -05:00
|
|
|
}
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
|
|
|
|
void
|
2016-06-13 13:21:52 -04:00
|
|
|
RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2010-09-13 15:46:28 -04:00
|
|
|
if (!movement_occurred) {
|
2017-01-26 08:41:17 -05:00
|
|
|
add_midi_region (_view, true);
|
2011-03-01 11:23:31 -05:00
|
|
|
} else {
|
|
|
|
_view->playlist()->thaw ();
|
2015-11-13 11:14:23 -05:00
|
|
|
_editor->commit_reversible_command();
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
2010-09-13 15:46:28 -04:00
|
|
|
}
|
|
|
|
|
2010-01-07 20:28:15 -05:00
|
|
|
void
|
2010-12-28 14:17:37 -05:00
|
|
|
RegionCreateDrag::aborted (bool)
|
2010-01-07 20:28:15 -05:00
|
|
|
{
|
2011-03-01 11:23:31 -05:00
|
|
|
if (_region) {
|
|
|
|
_view->playlist()->thaw ();
|
|
|
|
}
|
|
|
|
|
2010-08-20 08:01:13 -04:00
|
|
|
/* XXX */
|
2010-01-07 20:28:15 -05:00
|
|
|
}
|
|
|
|
|
2009-09-08 17:45:44 -04:00
|
|
|
NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
|
|
|
|
: Drag (e, i)
|
|
|
|
, region (0)
|
2015-06-18 10:02:09 -04:00
|
|
|
, relative (false)
|
|
|
|
, at_front (true)
|
2015-10-23 10:19:00 -04:00
|
|
|
, _was_selected (false)
|
2015-05-15 14:15:52 -04:00
|
|
|
, _snap_delta (0)
|
2009-09-08 17:45:44 -04:00
|
|
|
{
|
2010-09-09 17:34:45 -04:00
|
|
|
DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
|
2009-09-08 17:45:44 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-06-17 18:09:07 -04:00
|
|
|
NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
|
2009-09-08 17:45:44 -04:00
|
|
|
{
|
2010-06-17 11:06:30 -04:00
|
|
|
Gdk::Cursor* cursor;
|
2013-04-04 00:32:52 -04:00
|
|
|
NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
|
|
|
|
assert (cnote);
|
2010-11-25 15:37:39 -05:00
|
|
|
float x_fraction = cnote->mouse_x_fraction ();
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2010-11-25 15:37:39 -05:00
|
|
|
if (x_fraction > 0.0 && x_fraction < 0.25) {
|
|
|
|
cursor = _editor->cursors()->left_side_trim;
|
2014-11-18 02:20:38 -05:00
|
|
|
at_front = true;
|
2010-11-25 15:37:39 -05:00
|
|
|
} else {
|
|
|
|
cursor = _editor->cursors()->right_side_trim;
|
2014-11-18 02:20:38 -05:00
|
|
|
at_front = false;
|
2010-11-25 15:37:39 -05:00
|
|
|
}
|
2010-09-22 11:21:06 -04:00
|
|
|
|
|
|
|
Drag::start_grab (event, cursor);
|
2009-09-08 17:45:44 -04:00
|
|
|
|
|
|
|
region = &cnote->region_view();
|
|
|
|
|
2015-05-17 09:47:01 -04:00
|
|
|
double temp;
|
2015-05-21 14:26:16 -04:00
|
|
|
temp = region->snap_to_pixel (cnote->x0 (), true);
|
2015-05-17 09:47:01 -04:00
|
|
|
_snap_delta = temp - cnote->x0 ();
|
2015-05-15 14:15:52 -04:00
|
|
|
|
2013-04-04 00:32:52 -04:00
|
|
|
_item->grab ();
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2015-05-20 15:32:23 -04:00
|
|
|
if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
|
2009-09-08 17:45:44 -04:00
|
|
|
relative = false;
|
|
|
|
} else {
|
|
|
|
relative = true;
|
|
|
|
}
|
2020-04-16 14:14:42 -04:00
|
|
|
MidiRegionSelection ms = _editor->get_selection().midi_regions();
|
|
|
|
|
2009-09-08 22:09:04 -04:00
|
|
|
if (ms.size() > 1) {
|
|
|
|
/* has to be relative, may make no sense otherwise */
|
|
|
|
relative = true;
|
|
|
|
}
|
2015-10-23 09:07:03 -04:00
|
|
|
|
2015-10-23 10:19:00 -04:00
|
|
|
if (!(_was_selected = cnote->selected())) {
|
|
|
|
|
|
|
|
/* tertiary-click means extend selection - we'll do that on button release,
|
|
|
|
so don't add it here, because otherwise we make it hard to figure
|
|
|
|
out the "extend-to" range.
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
|
|
|
|
|
|
|
|
if (!extend) {
|
|
|
|
bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
|
|
|
|
|
|
|
|
if (add) {
|
|
|
|
region->note_selected (cnote, true);
|
|
|
|
} else {
|
|
|
|
_editor->get_selection().clear_points();
|
|
|
|
region->unique_select (cnote);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-09-08 17:45:44 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2015-06-18 10:02:09 -04:00
|
|
|
NoteResizeDrag::motion (GdkEvent* event, bool first_move)
|
2009-09-08 17:45:44 -04:00
|
|
|
{
|
2020-04-16 14:14:42 -04:00
|
|
|
MidiRegionSelection ms = _editor->get_selection().midi_regions();
|
2015-06-18 10:02:09 -04:00
|
|
|
if (first_move) {
|
|
|
|
_editor->begin_reversible_command (_("resize notes"));
|
|
|
|
|
|
|
|
for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
|
|
|
|
MidiRegionSelection::iterator next;
|
|
|
|
next = r;
|
|
|
|
++next;
|
|
|
|
MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
|
|
|
|
if (mrv) {
|
|
|
|
mrv->begin_resizing (at_front);
|
|
|
|
}
|
|
|
|
r = next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-09-10 16:41:08 -04:00
|
|
|
for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
|
2013-04-04 00:32:52 -04:00
|
|
|
NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
|
|
|
|
assert (nb);
|
2014-11-13 21:32:08 -05:00
|
|
|
MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
|
|
|
|
if (mrv) {
|
2015-05-17 09:47:01 -04:00
|
|
|
double sd = 0.0;
|
2015-05-22 13:09:48 -04:00
|
|
|
bool snap = true;
|
2015-06-10 11:36:34 -04:00
|
|
|
bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
|
2015-05-22 13:09:48 -04:00
|
|
|
|
|
|
|
if (ArdourKeyboard::indicates_snap (event->button.state)) {
|
|
|
|
if (_editor->snap_mode () != SnapOff) {
|
|
|
|
snap = false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (_editor->snap_mode () == SnapOff) {
|
|
|
|
snap = false;
|
|
|
|
/* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
|
2015-06-10 11:36:34 -04:00
|
|
|
if (apply_snap_delta) {
|
2015-05-22 13:09:48 -04:00
|
|
|
snap = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (apply_snap_delta) {
|
2015-05-17 09:47:01 -04:00
|
|
|
sd = _snap_delta;
|
|
|
|
}
|
2015-05-22 13:09:48 -04:00
|
|
|
|
|
|
|
mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
|
2014-11-13 21:32:08 -05:00
|
|
|
}
|
2009-09-08 22:09:04 -04:00
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
}
|
2009-09-08 17:45:44 -04:00
|
|
|
|
|
|
|
void
|
2015-06-18 10:02:09 -04:00
|
|
|
NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
|
2009-09-08 17:45:44 -04:00
|
|
|
{
|
2015-06-18 10:02:09 -04:00
|
|
|
if (!movement_occurred) {
|
2015-10-23 10:19:00 -04:00
|
|
|
/* no motion - select note */
|
|
|
|
NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
|
|
|
|
if (_editor->current_mouse_mode() == Editing::MouseContent ||
|
|
|
|
_editor->current_mouse_mode() == Editing::MouseDraw) {
|
|
|
|
|
|
|
|
bool changed = false;
|
|
|
|
|
|
|
|
if (_was_selected) {
|
|
|
|
bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
|
|
|
|
if (add) {
|
|
|
|
region->note_deselected (cnote);
|
|
|
|
changed = true;
|
|
|
|
} else {
|
|
|
|
_editor->get_selection().clear_points();
|
|
|
|
region->unique_select (cnote);
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
|
|
|
|
bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
|
|
|
|
|
|
|
|
if (!extend && !add && region->selection_size() > 1) {
|
|
|
|
_editor->get_selection().clear_points();
|
|
|
|
region->unique_select (cnote);
|
|
|
|
changed = true;
|
|
|
|
} else if (extend) {
|
|
|
|
region->note_selected (cnote, true, true);
|
|
|
|
changed = true;
|
|
|
|
} else {
|
|
|
|
/* it was added during button press */
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (changed) {
|
|
|
|
_editor->begin_reversible_selection_op(X_("Resize Select Note Release"));
|
|
|
|
_editor->commit_reversible_selection_op();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-18 10:02:09 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-04-16 14:14:42 -04:00
|
|
|
MidiRegionSelection ms = _editor->get_selection().midi_regions();
|
2009-09-10 16:41:08 -04:00
|
|
|
for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
|
2013-04-04 00:32:52 -04:00
|
|
|
NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
|
|
|
|
assert (nb);
|
2014-11-13 21:32:08 -05:00
|
|
|
MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
|
2015-05-17 09:47:01 -04:00
|
|
|
double sd = 0.0;
|
2015-05-22 13:09:48 -04:00
|
|
|
bool snap = true;
|
2015-06-10 11:36:34 -04:00
|
|
|
bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
|
2014-11-13 21:32:08 -05:00
|
|
|
if (mrv) {
|
2015-05-22 13:09:48 -04:00
|
|
|
if (ArdourKeyboard::indicates_snap (event->button.state)) {
|
|
|
|
if (_editor->snap_mode () != SnapOff) {
|
|
|
|
snap = false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (_editor->snap_mode () == SnapOff) {
|
|
|
|
snap = false;
|
|
|
|
/* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
|
2015-06-10 11:36:34 -04:00
|
|
|
if (apply_snap_delta) {
|
2015-05-22 13:09:48 -04:00
|
|
|
snap = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-06-18 10:02:09 -04:00
|
|
|
|
2015-05-22 13:09:48 -04:00
|
|
|
if (apply_snap_delta) {
|
|
|
|
sd = _snap_delta;
|
|
|
|
}
|
2015-06-18 10:02:09 -04:00
|
|
|
|
2015-05-22 13:09:48 -04:00
|
|
|
mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
|
2014-11-13 21:32:08 -05:00
|
|
|
}
|
2009-09-08 22:09:04 -04:00
|
|
|
}
|
2015-01-16 12:55:05 -05:00
|
|
|
|
|
|
|
_editor->commit_reversible_command ();
|
2009-09-08 17:45:44 -04:00
|
|
|
}
|
|
|
|
|
2010-01-07 20:28:15 -05:00
|
|
|
void
|
2010-12-28 14:17:37 -05:00
|
|
|
NoteResizeDrag::aborted (bool)
|
2010-01-07 20:28:15 -05:00
|
|
|
{
|
2020-04-16 14:14:42 -04:00
|
|
|
MidiRegionSelection ms = _editor->get_selection().midi_regions();
|
2011-12-02 22:05:59 -05:00
|
|
|
for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
|
2014-11-13 21:32:08 -05:00
|
|
|
MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
|
|
|
|
if (mrv) {
|
|
|
|
mrv->abort_resizing ();
|
|
|
|
}
|
2011-12-02 22:05:59 -05:00
|
|
|
}
|
2010-01-07 20:28:15 -05:00
|
|
|
}
|
|
|
|
|
2013-03-12 17:00:09 -04:00
|
|
|
AVDraggingView::AVDraggingView (RegionView* v)
|
|
|
|
: view (v)
|
|
|
|
{
|
2020-10-15 01:09:22 -04:00
|
|
|
initial_position = v->region()->position_sample();
|
2013-03-12 17:00:09 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
|
|
|
|
: Drag (e, i)
|
|
|
|
{
|
|
|
|
DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
|
|
|
|
|
|
|
|
RegionSelection rs;
|
|
|
|
TrackViewList empty;
|
|
|
|
empty.clear();
|
2020-10-15 01:09:22 -04:00
|
|
|
_editor->get_regions_after(rs, timepos_t::zero (Temporal::AudioTime), empty);
|
2013-03-12 17:00:09 -04:00
|
|
|
std::list<RegionView*> views = rs.by_layer();
|
2013-04-11 13:49:29 -04:00
|
|
|
|
2015-08-19 20:24:27 -04:00
|
|
|
_stuck = false;
|
2013-03-12 17:00:09 -04:00
|
|
|
for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
|
|
|
|
RegionView* rv = (*i);
|
|
|
|
if (!rv->region()->video_locked()) {
|
|
|
|
continue;
|
|
|
|
}
|
2015-08-19 20:27:24 -04:00
|
|
|
if (rv->region()->locked()) {
|
2015-08-19 20:24:27 -04:00
|
|
|
_stuck = true;
|
|
|
|
}
|
2013-03-12 17:00:09 -04:00
|
|
|
_views.push_back (AVDraggingView (rv));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
|
|
|
|
{
|
|
|
|
Drag::start_grab (event);
|
|
|
|
if (_editor->session() == 0) {
|
|
|
|
return;
|
|
|
|
}
|
2015-09-07 19:35:35 -04:00
|
|
|
|
|
|
|
if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
|
|
|
|
_stuck = false;
|
|
|
|
_views.clear();
|
|
|
|
}
|
|
|
|
|
2015-08-19 20:24:27 -04:00
|
|
|
if (_stuck) {
|
2020-04-16 16:09:01 -04:00
|
|
|
show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot be moved."));
|
2015-08-19 20:24:27 -04:00
|
|
|
return;
|
|
|
|
}
|
2013-03-12 17:00:09 -04:00
|
|
|
|
|
|
|
_startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
|
|
|
|
_max_backwards_drag = (
|
|
|
|
ARDOUR_UI::instance()->video_timeline->get_duration()
|
|
|
|
+ ARDOUR_UI::instance()->video_timeline->get_offset()
|
|
|
|
- ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
|
|
|
|
);
|
|
|
|
|
|
|
|
for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
|
|
|
|
if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
|
2017-09-18 12:39:17 -04:00
|
|
|
_max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_samples_to_apv (i->initial_position);
|
2013-03-12 17:00:09 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
|
|
|
|
|
|
|
|
char buf[128];
|
|
|
|
Timecode::Time timecode;
|
|
|
|
_editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
|
|
|
|
snprintf (buf, sizeof (buf), "Video Start:\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, (_startdrag_video_offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
|
2014-06-26 15:07:29 -04:00
|
|
|
show_verbose_cursor_text (buf);
|
2013-03-12 17:00:09 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
|
|
|
|
{
|
|
|
|
if (_editor->session() == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
|
|
|
|
return;
|
|
|
|
}
|
2015-08-19 20:24:27 -04:00
|
|
|
if (_stuck) {
|
2020-04-16 16:09:01 -04:00
|
|
|
show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot be moved."));
|
2015-08-19 20:24:27 -04:00
|
|
|
return;
|
|
|
|
}
|
2013-03-12 17:00:09 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
samplecnt_t dt = adjusted_current_time (event).samples() - raw_grab_time().samples() + _pointer_offset.samples();
|
2017-09-18 12:39:17 -04:00
|
|
|
dt = ARDOUR_UI::instance()->video_timeline->quantify_samples_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
|
2013-03-12 17:00:09 -04:00
|
|
|
|
|
|
|
if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
|
|
|
|
dt = - _max_backwards_drag;
|
|
|
|
}
|
|
|
|
|
|
|
|
ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
|
|
|
|
ARDOUR_UI::instance()->flush_videotimeline_cache(true);
|
|
|
|
|
|
|
|
for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
|
|
|
|
RegionView* rv = i->view;
|
|
|
|
DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
|
|
|
|
if (first_move) {
|
|
|
|
rv->drag_start ();
|
|
|
|
rv->region()->clear_changes ();
|
|
|
|
rv->region()->suspend_property_changes();
|
|
|
|
}
|
2020-10-15 01:09:22 -04:00
|
|
|
rv->region()->set_position(timepos_t (i->initial_position + dt));
|
2013-03-12 17:00:09 -04:00
|
|
|
rv->region_changed(ARDOUR::Properties::position);
|
|
|
|
}
|
|
|
|
|
2017-09-18 12:39:17 -04:00
|
|
|
const samplepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
|
2013-03-12 17:00:09 -04:00
|
|
|
Timecode::Time timecode;
|
|
|
|
Timecode::Time timediff;
|
|
|
|
char buf[128];
|
|
|
|
_editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
|
|
|
|
_editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
|
|
|
|
snprintf (buf, sizeof (buf),
|
|
|
|
"%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
|
|
|
|
"\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
|
|
|
|
, _("Video Start:"),
|
|
|
|
(offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
|
|
|
|
, _("Diff:"),
|
|
|
|
(dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
|
|
|
|
);
|
2014-06-26 15:07:29 -04:00
|
|
|
show_verbose_cursor_text (buf);
|
2013-03-12 17:00:09 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2013-03-13 20:36:36 -04:00
|
|
|
VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
|
2013-03-12 17:00:09 -04:00
|
|
|
{
|
|
|
|
if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
|
|
|
|
return;
|
|
|
|
}
|
2015-08-19 20:24:27 -04:00
|
|
|
if (_stuck) {
|
|
|
|
return;
|
|
|
|
}
|
2013-03-12 17:00:09 -04:00
|
|
|
|
|
|
|
if (!movement_occurred || ! _editor->session()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ARDOUR_UI::instance()->flush_videotimeline_cache(true);
|
|
|
|
|
|
|
|
_editor->begin_reversible_command (_("Move Video"));
|
|
|
|
|
|
|
|
XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
|
|
|
|
ARDOUR_UI::instance()->video_timeline->save_undo();
|
|
|
|
XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
|
|
|
|
_editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
|
|
|
|
|
|
|
|
for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
|
|
|
|
i->view->drag_end();
|
|
|
|
i->view->region()->resume_property_changes ();
|
|
|
|
|
|
|
|
_editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
|
|
|
|
}
|
|
|
|
|
2013-03-27 13:21:09 -04:00
|
|
|
_editor->session()->maybe_update_session_range(
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t (std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (sampleoffset_t) 0)),
|
|
|
|
timepos_t (std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (sampleoffset_t) 0))
|
|
|
|
);
|
2013-03-27 13:21:09 -04:00
|
|
|
|
|
|
|
|
2013-03-12 17:00:09 -04:00
|
|
|
_editor->commit_reversible_command ();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
VideoTimeLineDrag::aborted (bool)
|
|
|
|
{
|
|
|
|
if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
|
|
|
|
ARDOUR_UI::instance()->flush_videotimeline_cache(true);
|
|
|
|
|
|
|
|
for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
|
|
|
|
i->view->region()->resume_property_changes ();
|
2020-10-15 01:09:22 -04:00
|
|
|
i->view->region()->set_position(timepos_t (i->initial_position));
|
2013-03-12 17:00:09 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-13 11:42:05 -05:00
|
|
|
TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
|
2009-05-30 14:25:59 -04:00
|
|
|
: RegionDrag (e, i, p, v)
|
2015-10-23 13:59:57 -04:00
|
|
|
, _operation (StartTrim)
|
2014-05-07 12:13:34 -04:00
|
|
|
, _preserve_fade_anchor (preserve_fade_anchor)
|
2015-05-20 11:30:57 -04:00
|
|
|
, _jump_position_when_done (false)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2010-09-09 17:34:45 -04:00
|
|
|
DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-11-13 00:14:48 -05:00
|
|
|
TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2020-11-30 12:59:17 -05:00
|
|
|
timepos_t const region_start = _primary->region()->position();
|
|
|
|
timepos_t const region_end = _primary->region()->end();
|
|
|
|
timecnt_t const region_length = _primary->region()->length();
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t const pf = adjusted_current_time (event);
|
|
|
|
setup_snap_delta (region_start);
|
2015-05-15 14:15:52 -04:00
|
|
|
|
2021-06-15 10:22:58 -04:00
|
|
|
/* These will get overridden for a point trim.*/
|
|
|
|
if (pf < (region_start + region_length/2)) {
|
|
|
|
/* closer to front */
|
|
|
|
_operation = StartTrim;
|
|
|
|
if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
|
|
|
|
Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
|
|
|
|
} else {
|
|
|
|
Drag::start_grab (event, _editor->cursors()->left_side_trim);
|
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
} else {
|
2021-06-15 10:22:58 -04:00
|
|
|
/* closer to end */
|
|
|
|
_operation = EndTrim;
|
|
|
|
if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
|
|
|
|
Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
|
2010-06-17 08:45:51 -04:00
|
|
|
} else {
|
2021-06-15 10:22:58 -04:00
|
|
|
Drag::start_grab (event, _editor->cursors()->right_side_trim);
|
2010-11-25 15:37:39 -05:00
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
2021-06-15 10:22:58 -04:00
|
|
|
|
2015-05-20 15:32:23 -04:00
|
|
|
/* jump trim disabled for now
|
2015-05-20 11:30:57 -04:00
|
|
|
if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
|
|
|
|
_jump_position_when_done = true;
|
|
|
|
}
|
2015-05-20 15:32:23 -04:00
|
|
|
*/
|
2015-05-20 11:30:57 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
switch (_operation) {
|
|
|
|
case StartTrim:
|
2011-05-02 09:38:16 -04:00
|
|
|
show_verbose_cursor_time (region_start);
|
2009-05-30 14:25:59 -04:00
|
|
|
break;
|
|
|
|
case EndTrim:
|
2015-05-15 14:15:52 -04:00
|
|
|
show_verbose_cursor_duration (region_start, region_end);
|
2009-05-30 14:25:59 -04:00
|
|
|
break;
|
|
|
|
}
|
2019-11-08 23:03:38 -05:00
|
|
|
show_view_preview (_operation == StartTrim ? region_start : region_end);
|
2010-09-21 19:23:07 -04:00
|
|
|
|
|
|
|
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
|
|
|
|
i->view->region()->suspend_property_changes ();
|
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-06-01 20:39:57 -04:00
|
|
|
TrimDrag::motion (GdkEvent* event, bool first_move)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
|
|
|
RegionView* rv = _primary;
|
|
|
|
|
|
|
|
pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
|
2020-10-15 01:09:22 -04:00
|
|
|
timecnt_t delta;
|
|
|
|
timepos_t adj_time = adjusted_time (_drags->current_pointer_time () + snap_delta (event->button.state), event, true);
|
2020-12-10 20:09:22 -05:00
|
|
|
timecnt_t dt = raw_grab_time().distance (adj_time) + _pointer_offset - snap_delta (event->button.state);
|
2009-12-21 20:12:41 -05:00
|
|
|
|
2009-06-01 20:39:57 -04:00
|
|
|
if (first_move) {
|
2009-06-21 14:34:08 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
string trim_type;
|
|
|
|
|
|
|
|
switch (_operation) {
|
|
|
|
case StartTrim:
|
|
|
|
trim_type = "Region start trim";
|
|
|
|
break;
|
|
|
|
case EndTrim:
|
|
|
|
trim_type = "Region end trim";
|
|
|
|
break;
|
2014-05-30 21:53:59 -04:00
|
|
|
default:
|
|
|
|
assert(0);
|
|
|
|
break;
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
_editor->begin_reversible_command (trim_type);
|
|
|
|
|
2010-04-06 20:17:54 -04:00
|
|
|
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
|
|
|
|
RegionView* rv = i->view;
|
2010-12-28 14:17:37 -05:00
|
|
|
rv->region()->playlist()->clear_owned_changes ();
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2015-10-29 13:01:44 -04:00
|
|
|
if (_operation == StartTrim) {
|
|
|
|
rv->trim_front_starting ();
|
|
|
|
}
|
|
|
|
|
2010-04-06 20:17:54 -04:00
|
|
|
AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2010-11-08 20:58:42 -05:00
|
|
|
if (arv) {
|
2009-05-30 14:25:59 -04:00
|
|
|
arv->temporarily_hide_envelope ();
|
2012-05-07 18:02:17 -04:00
|
|
|
arv->drag_start ();
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2010-04-06 20:17:54 -04:00
|
|
|
boost::shared_ptr<Playlist> pl = rv->region()->playlist();
|
2009-05-30 14:25:59 -04:00
|
|
|
insert_result = _editor->motion_frozen_playlists.insert (pl);
|
|
|
|
|
|
|
|
if (insert_result.second) {
|
|
|
|
pl->freeze();
|
|
|
|
}
|
2017-03-05 09:01:37 -05:00
|
|
|
|
|
|
|
MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (rv);
|
|
|
|
/* a MRV start trim may change the source length. ensure we cover all playlists here */
|
|
|
|
if (mrv && _operation == StartTrim) {
|
|
|
|
vector<boost::shared_ptr<Playlist> > all_playlists;
|
2019-03-19 00:14:40 -04:00
|
|
|
_editor->session()->playlists()->get (all_playlists);
|
2017-03-05 09:01:37 -05:00
|
|
|
for (vector<boost::shared_ptr<Playlist> >::iterator x = all_playlists.begin(); x != all_playlists.end(); ++x) {
|
2017-03-05 23:51:53 -05:00
|
|
|
|
2017-03-05 09:01:37 -05:00
|
|
|
if ((*x)->uses_source (rv->region()->source(0))) {
|
|
|
|
insert_result = _editor->motion_frozen_playlists.insert (*x);
|
|
|
|
if (insert_result.second) {
|
2017-03-05 23:51:53 -05:00
|
|
|
(*x)->clear_owned_changes ();
|
|
|
|
(*x)->freeze();
|
2017-03-05 09:01:37 -05:00
|
|
|
}
|
2017-03-05 23:51:53 -05:00
|
|
|
|
2017-03-05 09:01:37 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool non_overlap_trim = false;
|
|
|
|
|
2015-05-21 12:12:58 -04:00
|
|
|
if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
|
2009-05-30 14:25:59 -04:00
|
|
|
non_overlap_trim = true;
|
|
|
|
}
|
|
|
|
|
2014-05-30 21:53:59 -04:00
|
|
|
/* contstrain trim to fade length */
|
|
|
|
if (_preserve_fade_anchor) {
|
2020-10-15 01:09:22 -04:00
|
|
|
|
|
|
|
/* fades are audio and always use AudioTime domain */
|
|
|
|
|
|
|
|
samplecnt_t dts = dt.samples();
|
|
|
|
|
2014-05-30 21:53:59 -04:00
|
|
|
switch (_operation) {
|
|
|
|
case StartTrim:
|
|
|
|
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
|
|
|
|
AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
|
|
|
|
if (!arv) continue;
|
|
|
|
boost::shared_ptr<AudioRegion> ar (arv->audio_region());
|
|
|
|
if (ar->locked()) continue;
|
2020-10-15 01:09:22 -04:00
|
|
|
samplecnt_t len = ar->fade_in()->back()->when.samples();
|
|
|
|
if (len < dts) dts = min(dts, len);
|
2014-05-30 21:53:59 -04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case EndTrim:
|
|
|
|
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
|
|
|
|
AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
|
|
|
|
if (!arv) continue;
|
|
|
|
boost::shared_ptr<AudioRegion> ar (arv->audio_region());
|
|
|
|
if (ar->locked()) continue;
|
2020-10-15 01:09:22 -04:00
|
|
|
samplecnt_t len = ar->fade_out()->back()->when.samples();
|
|
|
|
if (len < -dts) dts = max(dts, -len);
|
2014-05-30 21:53:59 -04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-14 12:10:01 -04:00
|
|
|
switch (_operation) {
|
2009-05-30 14:25:59 -04:00
|
|
|
case StartTrim:
|
2014-05-30 21:53:59 -04:00
|
|
|
for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
|
2020-10-15 01:09:22 -04:00
|
|
|
bool changed = i->view->trim_front (timepos_t (i->initial_position) + dt, non_overlap_trim);
|
|
|
|
|
2016-06-13 13:21:52 -04:00
|
|
|
|
2012-12-13 11:42:05 -05:00
|
|
|
if (changed && _preserve_fade_anchor) {
|
|
|
|
AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
|
|
|
|
if (arv) {
|
|
|
|
boost::shared_ptr<AudioRegion> ar (arv->audio_region());
|
2020-10-15 01:09:22 -04:00
|
|
|
samplecnt_t len = ar->fade_in()->back()->when.samples();
|
|
|
|
samplecnt_t diff = ar->first_sample() - i->initial_position.samples();
|
2017-09-18 12:39:17 -04:00
|
|
|
samplepos_t new_length = len - diff;
|
2020-10-15 01:09:22 -04:00
|
|
|
i->anchored_fade_length = min (ar->length_samples(), new_length);
|
2014-05-31 18:40:07 -04:00
|
|
|
//i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
|
2014-05-30 21:53:59 -04:00
|
|
|
arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
|
2012-12-13 11:42:05 -05:00
|
|
|
}
|
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
2010-05-04 21:54:16 -04:00
|
|
|
break;
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
case EndTrim:
|
2014-05-30 21:53:59 -04:00
|
|
|
for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
|
2020-10-15 01:09:22 -04:00
|
|
|
bool changed = i->view->trim_end (timepos_t (i->initial_end) + dt, non_overlap_trim);
|
|
|
|
|
2012-12-13 11:42:05 -05:00
|
|
|
if (changed && _preserve_fade_anchor) {
|
|
|
|
AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
|
|
|
|
if (arv) {
|
|
|
|
boost::shared_ptr<AudioRegion> ar (arv->audio_region());
|
2020-10-15 01:09:22 -04:00
|
|
|
samplecnt_t len = ar->fade_out()->back()->when.samples();
|
|
|
|
samplecnt_t diff = 1 + ar->last_sample() - i->initial_end.samples();
|
2017-09-18 12:39:17 -04:00
|
|
|
samplepos_t new_length = len + diff;
|
2020-10-15 01:09:22 -04:00
|
|
|
i->anchored_fade_length = min (ar->length_samples(), new_length);
|
2014-05-31 18:40:07 -04:00
|
|
|
//i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
|
2014-05-30 21:53:59 -04:00
|
|
|
arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
|
2012-12-13 11:42:05 -05:00
|
|
|
}
|
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
2010-05-04 21:54:16 -04:00
|
|
|
break;
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (_operation) {
|
|
|
|
case StartTrim:
|
2020-11-30 12:59:17 -05:00
|
|
|
show_verbose_cursor_time (rv->region()->position());
|
2009-05-30 14:25:59 -04:00
|
|
|
break;
|
|
|
|
case EndTrim:
|
2020-11-30 12:59:17 -05:00
|
|
|
show_verbose_cursor_duration (rv->region()->position(), rv->region()->end());
|
2009-05-30 14:25:59 -04:00
|
|
|
break;
|
|
|
|
}
|
2020-11-30 12:59:17 -05:00
|
|
|
show_view_preview ((_operation == StartTrim ? rv->region()->position() : rv->region()->end()));
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-06-01 20:39:57 -04:00
|
|
|
TrimDrag::finished (GdkEvent* event, bool movement_occurred)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2009-06-01 20:39:57 -04:00
|
|
|
if (movement_occurred) {
|
|
|
|
motion (event, false);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2010-12-09 16:34:46 -05:00
|
|
|
if (_operation == StartTrim) {
|
|
|
|
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
|
2012-12-13 11:42:05 -05:00
|
|
|
{
|
|
|
|
/* This must happen before the region's StatefulDiffCommand is created, as it may
|
|
|
|
`correct' (ahem) the region's _start from being negative to being zero. It
|
|
|
|
needs to be zero in the undo record.
|
|
|
|
*/
|
|
|
|
i->view->trim_front_ending ();
|
|
|
|
}
|
|
|
|
if (_preserve_fade_anchor) {
|
|
|
|
AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
|
|
|
|
if (arv) {
|
|
|
|
boost::shared_ptr<AudioRegion> ar (arv->audio_region());
|
2014-05-30 21:53:59 -04:00
|
|
|
arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
|
|
|
|
ar->set_fade_in_length(i->anchored_fade_length);
|
|
|
|
ar->set_fade_in_active(true);
|
2012-12-13 11:42:05 -05:00
|
|
|
}
|
|
|
|
}
|
2015-05-20 11:30:57 -04:00
|
|
|
if (_jump_position_when_done) {
|
2020-10-15 01:09:22 -04:00
|
|
|
i->view->region()->set_position (timepos_t (i->initial_position));
|
2015-05-20 11:30:57 -04:00
|
|
|
}
|
2012-12-13 11:42:05 -05:00
|
|
|
}
|
|
|
|
} else if (_operation == EndTrim) {
|
|
|
|
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
|
|
|
|
if (_preserve_fade_anchor) {
|
|
|
|
AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
|
|
|
|
if (arv) {
|
|
|
|
boost::shared_ptr<AudioRegion> ar (arv->audio_region());
|
2014-05-30 21:53:59 -04:00
|
|
|
arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
|
|
|
|
ar->set_fade_out_length(i->anchored_fade_length);
|
|
|
|
ar->set_fade_out_active(true);
|
2012-12-13 11:42:05 -05:00
|
|
|
}
|
|
|
|
}
|
2015-05-20 11:30:57 -04:00
|
|
|
if (_jump_position_when_done) {
|
2020-11-30 12:59:17 -05:00
|
|
|
i->view->region()->set_position (timepos_t (i->initial_end).earlier (i->view->region()->length()));
|
2015-05-20 11:30:57 -04:00
|
|
|
}
|
2010-12-09 16:34:46 -05:00
|
|
|
}
|
|
|
|
}
|
2013-01-20 01:57:52 -05:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
if (!_editor->selection->selected (_primary)) {
|
2010-11-08 20:58:12 -05:00
|
|
|
_primary->thaw_after_trim ();
|
2009-05-30 14:25:59 -04:00
|
|
|
} else {
|
2010-04-06 20:17:54 -04:00
|
|
|
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
|
2015-03-24 16:59:57 -04:00
|
|
|
i->view->thaw_after_trim ();
|
2010-06-28 14:43:40 -04:00
|
|
|
i->view->enable_display (true);
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
}
|
2012-12-13 10:54:50 -05:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
|
2017-03-05 09:01:37 -05:00
|
|
|
/* Trimming one region may affect others on the playlist, so we need
|
|
|
|
to get undo Commands from the whole playlist rather than just the
|
|
|
|
region. Use motion_frozen_playlists (a set) to make sure we don't
|
|
|
|
diff a given playlist more than once.
|
|
|
|
*/
|
|
|
|
|
|
|
|
vector<Command*> cmds;
|
|
|
|
(*p)->rdiff (cmds);
|
|
|
|
_editor->session()->add_commands (cmds);
|
2009-05-30 14:25:59 -04:00
|
|
|
(*p)->thaw ();
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
_editor->motion_frozen_playlists.clear ();
|
2010-12-28 14:17:37 -05:00
|
|
|
_editor->commit_reversible_command();
|
2009-11-08 11:28:21 -05:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
} else {
|
|
|
|
/* no mouse movement */
|
2020-10-15 01:09:22 -04:00
|
|
|
if (adjusted_current_time (event) != adjusted_time (_drags->current_pointer_time(), event, false)) {
|
|
|
|
_editor->point_trim (event, adjusted_current_time (event));
|
2015-10-29 13:01:44 -04:00
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
2010-09-21 19:23:07 -04:00
|
|
|
|
|
|
|
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
|
|
|
|
i->view->region()->resume_property_changes ();
|
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2010-01-07 20:28:15 -05:00
|
|
|
void
|
2010-12-28 14:17:37 -05:00
|
|
|
TrimDrag::aborted (bool movement_occurred)
|
2010-01-07 20:28:15 -05:00
|
|
|
{
|
|
|
|
/* Our motion method is changing model state, so use the Undo system
|
|
|
|
to cancel. Perhaps not ideal, as this will leave an Undo point
|
|
|
|
behind which may be slightly odd from the user's point of view.
|
|
|
|
*/
|
|
|
|
|
2016-09-11 08:40:23 -04:00
|
|
|
GdkEvent ev;
|
2019-08-19 22:06:31 -04:00
|
|
|
memset (&ev, 0, sizeof (GdkEvent));
|
2016-09-11 08:40:23 -04:00
|
|
|
finished (&ev, true);
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-12-28 14:17:37 -05:00
|
|
|
if (movement_occurred) {
|
2016-09-11 08:40:23 -04:00
|
|
|
_editor->session()->undo (1);
|
2010-01-07 20:28:15 -05:00
|
|
|
}
|
2010-09-21 19:23:07 -04:00
|
|
|
|
|
|
|
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
|
|
|
|
i->view->region()->resume_property_changes ();
|
|
|
|
}
|
2010-01-07 20:28:15 -05:00
|
|
|
}
|
|
|
|
|
2010-12-21 20:06:18 -05:00
|
|
|
void
|
2020-10-15 01:09:22 -04:00
|
|
|
TrimDrag::setup_pointer_offset ()
|
2010-12-21 20:06:18 -05:00
|
|
|
{
|
|
|
|
list<DraggingView>::iterator i = _views.begin ();
|
|
|
|
while (i != _views.end() && i->view != _primary) {
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == _views.end()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-02-01 13:29:33 -05:00
|
|
|
|
2010-12-21 20:06:18 -05:00
|
|
|
switch (_operation) {
|
|
|
|
case StartTrim:
|
2020-10-15 01:09:22 -04:00
|
|
|
_pointer_offset = i->initial_position.distance (raw_grab_time());
|
2010-12-21 20:06:18 -05:00
|
|
|
break;
|
|
|
|
case EndTrim:
|
2020-10-15 01:09:22 -04:00
|
|
|
_pointer_offset = i->initial_end.distance (raw_grab_time());
|
2010-12-21 20:06:18 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
|
2016-05-27 10:48:18 -04:00
|
|
|
: Drag (e, i)
|
2020-11-24 20:25:34 -05:00
|
|
|
, _marker (reinterpret_cast<MeterMarker*> (_item->get_data ("marker")))
|
2016-05-27 10:48:18 -04:00
|
|
|
, _copy (c)
|
2018-02-09 10:59:39 -05:00
|
|
|
, _old_grid_type (e->grid_type())
|
2016-05-27 12:57:53 -04:00
|
|
|
, _old_snap_mode (e->snap_mode())
|
2015-10-23 13:59:57 -04:00
|
|
|
, before_state (0)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2010-09-09 17:34:45 -04:00
|
|
|
DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
|
2009-05-30 14:25:59 -04:00
|
|
|
assert (_marker);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
|
|
|
|
{
|
|
|
|
Drag::start_grab (event, cursor);
|
2020-10-15 01:09:22 -04:00
|
|
|
show_verbose_cursor_time (adjusted_current_time (event));
|
2020-12-31 14:05:20 -05:00
|
|
|
|
|
|
|
/* setup thread-local tempo map ptr as a writable copy */
|
|
|
|
|
|
|
|
TempoMap::fetch_writable ();
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2010-12-19 22:42:59 -05:00
|
|
|
void
|
2020-10-15 01:09:22 -04:00
|
|
|
MeterMarkerDrag::setup_pointer_offset ()
|
2010-12-19 22:42:59 -05:00
|
|
|
{
|
2020-11-23 20:04:37 -05:00
|
|
|
_pointer_offset = _marker->meter().time().distance (raw_grab_time());
|
2010-12-19 22:42:59 -05:00
|
|
|
}
|
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
void
|
2011-12-13 14:43:41 -05:00
|
|
|
MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2020-11-27 14:41:11 -05:00
|
|
|
TempoMap::SharedPtr map (TempoMap::use());
|
2020-10-15 01:09:22 -04:00
|
|
|
|
2011-12-13 14:43:41 -05:00
|
|
|
if (first_move) {
|
2016-04-02 10:41:27 -04:00
|
|
|
// create a dummy marker to catch events, then hide it.
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2016-03-03 08:08:21 -05:00
|
|
|
char name[64];
|
2020-11-24 20:25:34 -05:00
|
|
|
snprintf (name, sizeof(name), "%d/%d", _marker->meter().divisions_per_bar(), _marker->meter().note_value ());
|
2016-03-03 08:08:21 -05:00
|
|
|
|
2011-12-13 14:43:41 -05:00
|
|
|
_marker = new MeterMarker (
|
|
|
|
*_editor,
|
|
|
|
*_editor->meter_group,
|
2015-01-02 09:44:54 -05:00
|
|
|
UIConfiguration::instance().color ("meter marker"),
|
2011-12-13 14:43:41 -05:00
|
|
|
name,
|
2020-11-24 20:25:34 -05:00
|
|
|
_marker->meter()
|
|
|
|
);
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2011-12-13 14:43:41 -05:00
|
|
|
/* use the new marker for the grab */
|
|
|
|
swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
|
2016-04-02 10:41:27 -04:00
|
|
|
_marker->hide();
|
|
|
|
|
|
|
|
/* get current state */
|
2020-11-27 14:41:11 -05:00
|
|
|
before_state = &map->get_state();
|
2011-12-13 14:43:41 -05:00
|
|
|
if (!_copy) {
|
2016-03-24 04:42:28 -04:00
|
|
|
_editor->begin_reversible_command (_("move meter mark"));
|
2016-04-02 10:41:27 -04:00
|
|
|
} else {
|
|
|
|
|
2020-11-24 20:25:34 -05:00
|
|
|
timepos_t const pointer = adjusted_current_time (event, false);
|
2020-11-27 14:41:11 -05:00
|
|
|
BBT_Time pointer_bbt = map->bbt_at (pointer);
|
|
|
|
Temporal::MeterPoint const & meter = map->metric_at (pointer).meter();
|
2020-11-24 20:25:34 -05:00
|
|
|
BBT_Time bbt = meter.bbt();
|
2016-04-02 13:57:33 -04:00
|
|
|
|
|
|
|
/* we can't add a meter where one currently exists */
|
2020-11-24 20:25:34 -05:00
|
|
|
if (bbt < pointer_bbt) {
|
2016-04-02 10:41:27 -04:00
|
|
|
++bbt.bars;
|
|
|
|
} else {
|
|
|
|
--bbt.bars;
|
|
|
|
}
|
2020-11-24 20:25:34 -05:00
|
|
|
|
|
|
|
_editor->begin_reversible_command (_("copy meter mark"));
|
|
|
|
|
|
|
|
timepos_t pos;
|
|
|
|
|
2020-11-27 14:41:11 -05:00
|
|
|
if (map->time_domain() == AudioTime) {
|
2021-01-26 21:30:56 -05:00
|
|
|
pos = timepos_t (map->sample_at (bbt, AudioEngine::instance()->sample_rate()));
|
2020-11-24 20:25:34 -05:00
|
|
|
} else {
|
2020-12-07 13:40:00 -05:00
|
|
|
pos = timepos_t (map->quarters_at (bbt));
|
2016-07-01 15:35:27 -04:00
|
|
|
}
|
2020-11-24 20:25:34 -05:00
|
|
|
|
2020-11-27 14:41:11 -05:00
|
|
|
_marker->reset_meter (map->set_meter (meter, pos));
|
2020-11-24 20:25:34 -05:00
|
|
|
}
|
|
|
|
|
2016-05-27 13:16:06 -04:00
|
|
|
/* only snap to bars. leave snap mode alone for audio locked meters.*/
|
2020-11-27 14:41:11 -05:00
|
|
|
if (map->time_domain() != AudioTime) {
|
2018-02-09 10:59:39 -05:00
|
|
|
_editor->set_grid_to (GridTypeBar);
|
|
|
|
_editor->set_snap_mode (SnapMagnetic);
|
2016-05-27 13:16:06 -04:00
|
|
|
}
|
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2020-11-24 20:25:34 -05:00
|
|
|
if (_movable && (!first_move || !_copy)) {
|
2016-05-20 13:54:29 -04:00
|
|
|
|
2020-11-24 20:25:34 -05:00
|
|
|
timepos_t pos;
|
2016-05-23 12:50:42 -04:00
|
|
|
|
2020-11-24 20:25:34 -05:00
|
|
|
if (_editor->grid_musical()) {
|
|
|
|
/* not useful to try to snap to a grid we're about to change */
|
|
|
|
pos = adjusted_current_time (event, false);
|
|
|
|
} else {
|
|
|
|
pos = adjusted_current_time (event);
|
|
|
|
}
|
2016-05-23 12:50:42 -04:00
|
|
|
|
2021-01-04 23:58:18 -05:00
|
|
|
if (map->move_meter (_marker->meter(), pos, false)) {
|
|
|
|
/* it was moved */
|
|
|
|
_editor->tempo_map_changed ();
|
|
|
|
}
|
2020-11-24 20:25:34 -05:00
|
|
|
show_verbose_cursor_time (timepos_t (_marker->meter().beats()));
|
|
|
|
_editor->set_snapped_cursor_position (timepos_t (_marker->meter().sample(_editor->session()->sample_rate())));
|
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-06-01 20:39:57 -04:00
|
|
|
MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2009-06-01 20:39:57 -04:00
|
|
|
if (!movement_occurred) {
|
2021-01-04 23:58:18 -05:00
|
|
|
/* reset thread local tempo map to the original state */
|
|
|
|
TempoMap::abort_update ();
|
2014-01-27 10:09:58 -05:00
|
|
|
if (was_double_click()) {
|
|
|
|
_editor->edit_meter_marker (*_marker);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-05-27 10:48:18 -04:00
|
|
|
/* reinstate old snap setting */
|
2018-02-09 10:59:39 -05:00
|
|
|
_editor->set_grid_to (_old_grid_type);
|
2016-05-27 12:57:53 -04:00
|
|
|
_editor->set_snap_mode (_old_snap_mode);
|
2016-05-27 10:48:18 -04:00
|
|
|
|
2020-11-27 14:41:11 -05:00
|
|
|
TempoMap::SharedPtr map (TempoMap::use());
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2020-12-31 14:05:20 -05:00
|
|
|
XMLNode &after = TempoMap::use()->get_state();
|
2020-11-27 17:44:34 -05:00
|
|
|
_editor->session()->add_command (new MementoCommand<Temporal::TempoMap> (new Temporal::TempoMap::MementoBinder(), before_state, &after));
|
2016-04-02 14:32:14 -04:00
|
|
|
_editor->commit_reversible_command ();
|
2011-12-12 21:46:36 -05:00
|
|
|
|
2020-12-31 14:05:20 -05:00
|
|
|
TempoMap::update (map);
|
|
|
|
|
2011-12-12 21:46:36 -05:00
|
|
|
// delete the dummy marker we used for visual representation while moving.
|
|
|
|
// a new visual marker will show up automatically.
|
|
|
|
delete _marker;
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2010-01-07 20:28:15 -05:00
|
|
|
void
|
2011-12-21 09:21:05 -05:00
|
|
|
MeterMarkerDrag::aborted (bool moved)
|
2010-01-07 20:28:15 -05:00
|
|
|
{
|
2020-12-31 14:05:20 -05:00
|
|
|
/* reset thread local tempo map to the original state */
|
2021-01-01 20:29:08 -05:00
|
|
|
TempoMap::abort_update ();
|
2020-12-31 14:05:20 -05:00
|
|
|
|
2020-11-24 20:25:34 -05:00
|
|
|
_marker->set_position (_marker->meter().time());
|
|
|
|
|
2011-12-21 09:21:05 -05:00
|
|
|
if (moved) {
|
2016-05-27 12:47:12 -04:00
|
|
|
/* reinstate old snap setting */
|
2018-02-09 10:59:39 -05:00
|
|
|
_editor->set_grid_to (_old_grid_type);
|
2016-05-27 12:57:53 -04:00
|
|
|
_editor->set_snap_mode (_old_snap_mode);
|
|
|
|
|
2011-12-21 09:21:05 -05:00
|
|
|
// delete the dummy marker we used for visual representation while moving.
|
|
|
|
// a new visual marker will show up automatically.
|
|
|
|
delete _marker;
|
|
|
|
}
|
2020-12-31 14:05:20 -05:00
|
|
|
|
2010-01-07 20:28:15 -05:00
|
|
|
}
|
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
|
2015-10-23 13:59:57 -04:00
|
|
|
: Drag (e, i)
|
|
|
|
, _copy (c)
|
2017-02-24 13:09:16 -05:00
|
|
|
, _grab_bpm (120.0, 4.0)
|
2017-03-04 13:21:56 -05:00
|
|
|
, _before_state (0)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2010-09-09 17:34:45 -04:00
|
|
|
DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2009-06-09 04:41:43 -04:00
|
|
|
_marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
|
2015-12-22 13:58:49 -05:00
|
|
|
_real_section = &_marker->tempo();
|
2020-11-27 20:48:03 -05:00
|
|
|
_movable = !TempoMap::use()->is_initial (_marker->tempo());
|
2017-02-24 13:09:16 -05:00
|
|
|
_grab_bpm = Tempo (_real_section->note_types_per_minute(), _real_section->note_type(), _real_section->end_note_types_per_minute());
|
2020-11-27 20:48:03 -05:00
|
|
|
_grab_qn = _real_section->beats();
|
2009-05-30 14:25:59 -04:00
|
|
|
assert (_marker);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
|
|
|
|
{
|
2011-12-20 21:53:33 -05:00
|
|
|
Drag::start_grab (event, cursor);
|
2016-03-26 19:26:15 -04:00
|
|
|
if (!_real_section->active()) {
|
|
|
|
show_verbose_cursor_text (_("inactive"));
|
|
|
|
} else {
|
2020-10-15 01:09:22 -04:00
|
|
|
show_verbose_cursor_time (adjusted_current_time (event));
|
2020-11-27 21:16:44 -05:00
|
|
|
|
2021-03-22 18:01:57 -04:00
|
|
|
/* setup thread-local tempo map ptr as a writable copy */
|
2020-11-27 21:16:44 -05:00
|
|
|
|
2021-03-22 18:01:57 -04:00
|
|
|
TempoMap::fetch_writable ();
|
|
|
|
}
|
2011-12-20 21:53:33 -05:00
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2011-12-20 21:53:33 -05:00
|
|
|
void
|
2020-10-15 01:09:22 -04:00
|
|
|
TempoMarkerDrag::setup_pointer_offset ()
|
2011-12-20 21:53:33 -05:00
|
|
|
{
|
2020-11-27 20:48:03 -05:00
|
|
|
_pointer_offset = _marker->tempo().time().distance (raw_grab_time());
|
2011-12-20 21:53:33 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
|
|
|
|
{
|
2020-11-27 20:48:03 -05:00
|
|
|
if (!_marker->tempo().active()) {
|
2016-03-26 19:26:15 -04:00
|
|
|
return;
|
|
|
|
}
|
2020-10-15 01:09:22 -04:00
|
|
|
|
2020-11-27 20:48:03 -05:00
|
|
|
TempoMap::SharedPtr map (TempoMap::use());
|
2016-05-20 14:48:42 -04:00
|
|
|
|
2016-03-25 15:28:27 -04:00
|
|
|
if (first_move) {
|
2011-12-20 21:53:33 -05:00
|
|
|
|
2016-04-02 10:41:27 -04:00
|
|
|
// mvc drag - create a dummy marker to catch events, hide it.
|
2011-12-20 21:53:33 -05:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
char name[64];
|
2016-11-05 14:14:20 -04:00
|
|
|
snprintf (name, sizeof (name), "%.2f", _marker->tempo().note_types_per_minute());
|
2010-11-11 08:36:38 -05:00
|
|
|
|
2011-12-20 21:53:33 -05:00
|
|
|
_marker = new TempoMarker (
|
2010-11-11 08:36:38 -05:00
|
|
|
*_editor,
|
|
|
|
*_editor->tempo_group,
|
2015-01-02 09:44:54 -05:00
|
|
|
UIConfiguration::instance().color ("tempo marker"),
|
2010-11-11 08:36:38 -05:00
|
|
|
name,
|
2020-11-27 20:48:03 -05:00
|
|
|
_marker->tempo()
|
2010-11-11 08:36:38 -05:00
|
|
|
);
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2011-12-20 21:53:33 -05:00
|
|
|
/* use the new marker for the grab */
|
|
|
|
swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
|
2016-04-03 13:00:40 -04:00
|
|
|
_marker->hide();
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2016-04-03 13:00:40 -04:00
|
|
|
/* get current state */
|
2020-11-27 20:48:03 -05:00
|
|
|
_before_state = &map->get_state();
|
2016-05-23 15:49:58 -04:00
|
|
|
|
2011-12-20 21:53:33 -05:00
|
|
|
if (!_copy) {
|
2020-11-27 20:48:03 -05:00
|
|
|
|
2015-11-12 08:17:44 -05:00
|
|
|
_editor->begin_reversible_command (_("move tempo mark"));
|
2016-04-03 13:00:40 -04:00
|
|
|
|
2016-05-23 15:49:58 -04:00
|
|
|
} else {
|
2020-11-27 20:48:03 -05:00
|
|
|
|
|
|
|
timepos_t const pointer = adjusted_current_time (event, false);
|
|
|
|
BBT_Time pointer_bbt = map->bbt_at (pointer);
|
|
|
|
Temporal::TempoMetric metric = map->metric_at (pointer);
|
|
|
|
Temporal::MeterPoint const & meter = metric.meter();
|
|
|
|
Temporal::TempoPoint const & tempo = metric.tempo();
|
|
|
|
BBT_Time bbt = tempo.bbt();
|
|
|
|
|
|
|
|
/* we can't add a tempo where one currently exists */
|
|
|
|
if (bbt < pointer_bbt) {
|
|
|
|
bbt = meter.bbt_add (bbt, BBT_Offset (0, 1, 0));
|
|
|
|
} else {
|
|
|
|
bbt = meter.bbt_add (bbt, BBT_Offset (0, -1, 0));
|
|
|
|
}
|
2016-04-03 13:00:40 -04:00
|
|
|
|
2016-05-23 15:49:58 -04:00
|
|
|
_editor->begin_reversible_command (_("copy tempo mark"));
|
2016-04-03 13:00:40 -04:00
|
|
|
|
2020-11-27 20:48:03 -05:00
|
|
|
timepos_t pos;
|
|
|
|
|
|
|
|
if (map->time_domain() == AudioTime) {
|
|
|
|
pos = timepos_t (map->sample_at (bbt, _editor->session()->sample_rate()));
|
2016-04-03 13:00:40 -04:00
|
|
|
} else {
|
2020-12-07 13:40:00 -05:00
|
|
|
pos = timepos_t (map->quarters_at (bbt));
|
2016-04-03 13:00:40 -04:00
|
|
|
}
|
2016-07-01 15:35:27 -04:00
|
|
|
|
2020-11-27 20:48:03 -05:00
|
|
|
_marker->reset_tempo (map->set_tempo (tempo, pos));
|
|
|
|
|
|
|
|
#warning paul, need a return status from set_tempo
|
|
|
|
#if 0
|
|
|
|
if (!) {
|
2016-07-01 15:35:27 -04:00
|
|
|
aborted (true);
|
|
|
|
return;
|
|
|
|
}
|
2020-11-27 20:48:03 -05:00
|
|
|
#endif
|
2011-12-20 21:53:33 -05:00
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
2020-10-15 01:09:22 -04:00
|
|
|
|
2017-02-25 13:07:51 -05:00
|
|
|
if (ArdourKeyboard::indicates_constraint (event->button.state) && ArdourKeyboard::indicates_copy (event->button.state)) {
|
|
|
|
double new_bpm = max (1.5, _grab_bpm.end_note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
|
|
|
|
stringstream strs;
|
2020-11-27 20:48:03 -05:00
|
|
|
map->change_tempo (_marker->tempo(), Tempo (_marker->tempo().note_types_per_minute(), _marker->tempo().note_type(), new_bpm));
|
2017-02-25 13:07:51 -05:00
|
|
|
strs << "end:" << fixed << setprecision(3) << new_bpm;
|
|
|
|
show_verbose_cursor_text (strs.str());
|
|
|
|
|
|
|
|
} else if (ArdourKeyboard::indicates_constraint (event->button.state)) {
|
|
|
|
/* use vertical movement to alter tempo .. should be log */
|
|
|
|
double new_bpm = max (1.5, _grab_bpm.note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
|
|
|
|
stringstream strs;
|
2020-11-27 20:48:03 -05:00
|
|
|
map->change_tempo (_marker->tempo(), Tempo (new_bpm, _marker->tempo().note_type(), _marker->tempo().end_note_types_per_minute()));
|
2017-02-25 13:07:51 -05:00
|
|
|
strs << "start:" << fixed << setprecision(3) << new_bpm;
|
|
|
|
show_verbose_cursor_text (strs.str());
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2020-11-27 20:48:03 -05:00
|
|
|
} else if (_movable) {
|
2016-08-23 13:41:42 -04:00
|
|
|
|
2020-11-27 20:48:03 -05:00
|
|
|
timepos_t pos = adjusted_current_time (event);
|
2016-05-23 10:03:58 -04:00
|
|
|
|
2020-11-27 20:48:03 -05:00
|
|
|
map->move_tempo (_marker->tempo(), pos, false);
|
2016-03-28 14:31:06 -04:00
|
|
|
|
2020-11-27 20:48:03 -05:00
|
|
|
show_verbose_cursor_time (_marker->tempo().time());
|
2016-03-25 13:48:29 -04:00
|
|
|
}
|
2020-10-15 01:09:22 -04:00
|
|
|
|
2020-11-27 20:48:03 -05:00
|
|
|
|
|
|
|
if (_movable && (!first_move || !_copy)) {
|
|
|
|
|
|
|
|
timepos_t pos = adjusted_current_time (event);
|
|
|
|
|
|
|
|
map->move_tempo (_marker->tempo(), pos, false);
|
|
|
|
|
|
|
|
show_verbose_cursor_time (_marker->tempo().time());
|
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-06-01 20:39:57 -04:00
|
|
|
TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2020-11-27 20:48:03 -05:00
|
|
|
if (!_marker->tempo().active()) {
|
2016-03-26 19:26:15 -04:00
|
|
|
return;
|
|
|
|
}
|
2020-12-31 14:05:20 -05:00
|
|
|
|
2009-06-01 20:39:57 -04:00
|
|
|
if (!movement_occurred) {
|
2020-11-27 21:16:44 -05:00
|
|
|
/* reset the per-thread tempo map ptr back to the current
|
|
|
|
* official version
|
|
|
|
*/
|
|
|
|
|
2021-01-01 20:29:08 -05:00
|
|
|
TempoMap::abort_update ();
|
2021-03-22 18:01:57 -04:00
|
|
|
|
|
|
|
if (was_double_click()) {
|
|
|
|
_editor->edit_tempo_marker (*_marker);
|
|
|
|
}
|
|
|
|
|
2014-01-27 10:09:58 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-11-27 21:23:20 -05:00
|
|
|
/* push the current state of our writable map copy */
|
|
|
|
|
2020-11-27 20:48:03 -05:00
|
|
|
TempoMap::SharedPtr map (TempoMap::use());
|
2020-11-27 21:23:20 -05:00
|
|
|
TempoMap::update (map);
|
|
|
|
|
2020-12-31 14:05:20 -05:00
|
|
|
XMLNode &after = TempoMap::use()->get_state();
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2020-11-27 20:48:03 -05:00
|
|
|
_editor->session()->add_command (new MementoCommand<Temporal::TempoMap> (new Temporal::TempoMap::MementoBinder(), _before_state, &after));
|
2016-04-03 13:00:40 -04:00
|
|
|
_editor->commit_reversible_command ();
|
2020-11-27 20:48:03 -05:00
|
|
|
|
2011-12-20 21:53:33 -05:00
|
|
|
// delete the dummy marker we used for visual representation while moving.
|
|
|
|
// a new visual marker will show up automatically.
|
|
|
|
delete _marker;
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2010-01-07 20:28:15 -05:00
|
|
|
void
|
2011-12-21 09:21:05 -05:00
|
|
|
TempoMarkerDrag::aborted (bool moved)
|
2010-01-07 20:28:15 -05:00
|
|
|
{
|
2020-11-27 21:16:44 -05:00
|
|
|
/* reset the per-thread tempo map ptr back to the current
|
|
|
|
* official version
|
|
|
|
*/
|
|
|
|
|
2021-01-01 20:29:08 -05:00
|
|
|
TempoMap::abort_update ();
|
2020-11-27 21:16:44 -05:00
|
|
|
|
2020-12-31 14:05:20 -05:00
|
|
|
// _point->end_float ();
|
|
|
|
_marker->set_position (timepos_t (_marker->tempo().beats()));
|
|
|
|
|
2011-12-21 09:21:05 -05:00
|
|
|
if (moved) {
|
2016-06-13 13:21:52 -04:00
|
|
|
// delete the dummy (hidden) marker we used for events while moving.
|
2011-12-21 09:21:05 -05:00
|
|
|
delete _marker;
|
|
|
|
}
|
2010-01-07 20:28:15 -05:00
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2016-05-07 13:03:12 -04:00
|
|
|
BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i)
|
|
|
|
: Drag (e, i)
|
2016-05-11 15:32:38 -04:00
|
|
|
, _tempo (0)
|
2017-03-04 13:21:56 -05:00
|
|
|
, _before_state (0)
|
2017-06-16 14:59:06 -04:00
|
|
|
, _drag_valid (true)
|
2016-05-07 13:03:12 -04:00
|
|
|
{
|
|
|
|
DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
|
2016-05-17 10:12:28 -04:00
|
|
|
|
2016-05-07 13:03:12 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
|
|
|
|
{
|
2021-01-26 21:30:56 -05:00
|
|
|
TempoMap::fetch_writable ();
|
|
|
|
|
2021-02-13 12:39:32 -05:00
|
|
|
Drag::start_grab (event, cursor);
|
|
|
|
|
2021-01-26 21:30:56 -05:00
|
|
|
TempoMap::SharedPtr map (TempoMap::use());
|
|
|
|
_tempo = const_cast<TempoPoint*> (&map->metric_at (raw_grab_time().beats()).tempo());
|
2017-06-16 14:59:06 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
if (adjusted_current_time (event, false) <= _tempo->time()) {
|
2017-06-16 14:59:06 -04:00
|
|
|
_drag_valid = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-02-25 15:03:02 -05:00
|
|
|
_editor->tempo_curve_selected (_tempo, true);
|
2016-05-11 15:32:38 -04:00
|
|
|
|
2017-02-25 15:03:02 -05:00
|
|
|
ostringstream sstr;
|
2017-03-04 13:21:56 -05:00
|
|
|
if (_tempo->clamped()) {
|
2021-01-26 21:30:56 -05:00
|
|
|
TempoPoint const * prev = map->previous_tempo (*_tempo);
|
2017-03-04 13:21:56 -05:00
|
|
|
if (prev) {
|
|
|
|
sstr << "end: " << fixed << setprecision(3) << prev->end_note_types_per_minute() << "\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-26 20:29:55 -05:00
|
|
|
sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
|
2016-05-17 10:12:28 -04:00
|
|
|
show_verbose_cursor_text (sstr.str());
|
2016-05-07 13:03:12 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-10-15 01:09:22 -04:00
|
|
|
BBTRulerDrag::setup_pointer_offset ()
|
2016-05-07 13:03:12 -04:00
|
|
|
{
|
2021-01-26 21:30:56 -05:00
|
|
|
TempoMap::SharedPtr map (TempoMap::use());
|
2017-03-04 13:21:56 -05:00
|
|
|
/* get current state */
|
2021-01-26 21:30:56 -05:00
|
|
|
_before_state = &map->get_state();
|
2017-03-04 13:21:56 -05:00
|
|
|
|
2021-01-26 21:30:56 -05:00
|
|
|
_grab_qn = max (Beats(), raw_grab_time().beats());
|
2016-05-27 16:50:22 -04:00
|
|
|
|
2021-01-26 21:30:56 -05:00
|
|
|
const uint32_t divisions = _editor->get_grid_beat_divisions ();
|
|
|
|
|
|
|
|
if (divisions == 0) {
|
|
|
|
divisions == 4;
|
2016-05-17 10:12:28 -04:00
|
|
|
}
|
2016-05-27 16:50:22 -04:00
|
|
|
|
2021-01-26 21:30:56 -05:00
|
|
|
_grab_qn = _grab_qn.round_to_subdivision (divisions, Temporal::RoundDownAlways);
|
|
|
|
_pointer_offset = timepos_t (_grab_qn).distance (raw_grab_time());
|
2016-05-07 13:03:12 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
BBTRulerDrag::motion (GdkEvent* event, bool first_move)
|
|
|
|
{
|
2017-06-16 14:59:06 -04:00
|
|
|
if (!_drag_valid) {
|
|
|
|
return;
|
|
|
|
}
|
2016-05-09 15:25:53 -04:00
|
|
|
|
2016-05-07 13:03:12 -04:00
|
|
|
if (first_move) {
|
2016-12-18 12:13:17 -05:00
|
|
|
_editor->begin_reversible_command (_("stretch tempo"));
|
2016-05-07 13:03:12 -04:00
|
|
|
}
|
|
|
|
|
2021-01-26 21:30:56 -05:00
|
|
|
TempoMap::SharedPtr map (TempoMap::use());
|
|
|
|
timepos_t pf;
|
2016-05-27 16:50:22 -04:00
|
|
|
|
2018-02-09 10:59:39 -05:00
|
|
|
if (_editor->grid_musical()) {
|
2021-01-26 21:30:56 -05:00
|
|
|
pf = adjusted_current_time (event, false);
|
2016-05-27 16:50:22 -04:00
|
|
|
} else {
|
2021-01-26 21:30:56 -05:00
|
|
|
pf = adjusted_current_time (event);
|
2016-05-27 16:50:22 -04:00
|
|
|
}
|
2016-05-07 13:03:12 -04:00
|
|
|
|
2016-08-13 11:50:51 -04:00
|
|
|
if (ArdourKeyboard::indicates_constraint (event->button.state)) {
|
2017-09-18 12:39:17 -04:00
|
|
|
/* adjust previous tempo to match pointer sample */
|
2021-01-26 21:30:56 -05:00
|
|
|
#warning NUTEMPO need to implement this
|
|
|
|
// _editor->session()->tempo_map().gui_stretch_tempo (_tempo, map.sample_at (_grab_qn), pf, _grab_qn, map.quarters_at_sample (pf));
|
2016-05-07 13:03:12 -04:00
|
|
|
}
|
2017-03-04 13:21:56 -05:00
|
|
|
|
2016-05-09 15:25:53 -04:00
|
|
|
ostringstream sstr;
|
2017-03-04 13:21:56 -05:00
|
|
|
if (_tempo->clamped()) {
|
2021-01-26 21:30:56 -05:00
|
|
|
TempoPoint const * prev = map->previous_tempo (*_tempo);
|
2017-03-04 13:21:56 -05:00
|
|
|
if (prev) {
|
|
|
|
_editor->tempo_curve_selected (prev, true);
|
|
|
|
sstr << "end: " << fixed << setprecision(3) << prev->end_note_types_per_minute() << "\n";
|
|
|
|
}
|
|
|
|
}
|
2017-02-26 20:29:55 -05:00
|
|
|
sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
|
2016-05-09 15:25:53 -04:00
|
|
|
show_verbose_cursor_text (sstr.str());
|
2016-05-07 13:03:12 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred)
|
|
|
|
{
|
2021-01-31 20:31:48 -05:00
|
|
|
if (!_drag_valid) {
|
2021-01-26 21:30:56 -05:00
|
|
|
TempoMap::abort_update ();
|
2016-05-07 13:03:12 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-01-26 21:30:56 -05:00
|
|
|
TempoMap::SharedPtr map (TempoMap::use());
|
2016-05-07 13:03:12 -04:00
|
|
|
|
2021-01-31 20:31:48 -05:00
|
|
|
if (!movement_occurred) {
|
2021-01-26 21:30:56 -05:00
|
|
|
|
2021-01-31 20:31:48 -05:00
|
|
|
_editor->begin_reversible_command (_("add BBT marker"));
|
2021-02-13 12:39:32 -05:00
|
|
|
/* position markers must always be positioned using audio time */
|
2021-02-13 12:59:09 -05:00
|
|
|
|
|
|
|
BBTMarkerDialog* marker_dialog = new BBTMarkerDialog (timepos_t (grab_sample()));
|
|
|
|
|
|
|
|
/* run this modally since we are finishing a drag and the drag object
|
|
|
|
* will be destroyed when we return from here
|
|
|
|
*/
|
|
|
|
|
|
|
|
int result = marker_dialog->run ();
|
|
|
|
BBT_Time bbt;
|
|
|
|
|
|
|
|
switch (result) {
|
|
|
|
case RESPONSE_ACCEPT:
|
|
|
|
case RESPONSE_OK:
|
|
|
|
bbt = marker_dialog->bbt_value ();
|
|
|
|
map->set_bartime (bbt, marker_dialog->position());
|
|
|
|
delete marker_dialog;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
delete marker_dialog;
|
|
|
|
TempoMap::abort_update ();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* the map change is committed below */
|
2021-01-31 20:31:48 -05:00
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
_editor->tempo_curve_selected (_tempo, false);
|
2021-01-26 21:30:56 -05:00
|
|
|
|
2021-01-31 20:31:48 -05:00
|
|
|
if (_tempo->clamped()) {
|
2021-01-26 21:30:56 -05:00
|
|
|
|
2021-01-31 20:31:48 -05:00
|
|
|
TempoPoint const * prev_tempo = map->previous_tempo (*_tempo);
|
|
|
|
|
|
|
|
if (prev_tempo) {
|
|
|
|
_editor->tempo_curve_selected (prev_tempo, false);
|
|
|
|
}
|
2017-03-04 13:21:56 -05:00
|
|
|
}
|
|
|
|
}
|
2017-06-16 14:59:06 -04:00
|
|
|
|
2021-01-31 20:31:48 -05:00
|
|
|
TempoMap::update (map);
|
2017-06-16 14:59:06 -04:00
|
|
|
|
2021-01-26 21:30:56 -05:00
|
|
|
XMLNode &after = TempoMap::use()->get_state();
|
|
|
|
|
|
|
|
_editor->session()->add_command(new MementoCommand<TempoMap>(new Temporal::TempoMap::MementoBinder(), _before_state, &after));
|
2017-06-16 14:59:06 -04:00
|
|
|
_editor->commit_reversible_command ();
|
2016-05-07 13:03:12 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
BBTRulerDrag::aborted (bool moved)
|
|
|
|
{
|
2021-01-26 21:30:56 -05:00
|
|
|
TempoMap::abort_update ();
|
2016-05-07 13:03:12 -04:00
|
|
|
}
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
#warning NUTEMPO fixme no tempo twist drag for now
|
|
|
|
#if 0
|
|
|
|
|
2017-02-24 13:09:16 -05:00
|
|
|
TempoTwistDrag::TempoTwistDrag (Editor* e, ArdourCanvas::Item* i)
|
|
|
|
: Drag (e, i)
|
|
|
|
, _grab_qn (0.0)
|
|
|
|
, _grab_tempo (0.0)
|
|
|
|
, _tempo (0)
|
2017-02-26 10:26:08 -05:00
|
|
|
, _next_tempo (0)
|
|
|
|
, _drag_valid (true)
|
2017-03-04 13:21:56 -05:00
|
|
|
, _before_state (0)
|
2017-02-24 13:09:16 -05:00
|
|
|
{
|
|
|
|
DEBUG_TRACE (DEBUG::Drags, "New TempoTwistDrag\n");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TempoTwistDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
|
|
|
|
{
|
|
|
|
Drag::start_grab (event, cursor);
|
|
|
|
TempoMap& map (_editor->session()->tempo_map());
|
2017-03-04 13:21:56 -05:00
|
|
|
/* get current state */
|
|
|
|
_before_state = &map.get_state();
|
2017-09-18 12:39:17 -04:00
|
|
|
_tempo = const_cast<TempoSection*> (&map.tempo_section_at_sample (raw_grab_sample()));
|
2017-02-24 13:09:16 -05:00
|
|
|
|
2017-06-16 15:11:38 -04:00
|
|
|
if (_tempo->locked_to_meter()) {
|
|
|
|
_drag_valid = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-02-26 10:26:08 -05:00
|
|
|
_next_tempo = map.next_tempo_section (_tempo);
|
|
|
|
if (_next_tempo) {
|
|
|
|
if (!map.next_tempo_section (_next_tempo)) {
|
|
|
|
_drag_valid = false;
|
|
|
|
finished (event, false);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_editor->tempo_curve_selected (_tempo, true);
|
|
|
|
_editor->tempo_curve_selected (_next_tempo, true);
|
|
|
|
|
|
|
|
ostringstream sstr;
|
|
|
|
sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute() << "\n";
|
|
|
|
sstr << "end: " << fixed << setprecision(3) << _tempo->end_note_types_per_minute() << "\n";
|
|
|
|
sstr << "start: " << fixed << setprecision(3) << _next_tempo->note_types_per_minute();
|
|
|
|
show_verbose_cursor_text (sstr.str());
|
|
|
|
} else {
|
|
|
|
_drag_valid = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
_grab_tempo = Tempo (_tempo->note_types_per_minute(), _tempo->note_type());
|
2017-02-24 13:09:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-10-15 01:09:22 -04:00
|
|
|
TempoTwistDrag::setup_pointer_offset ()
|
2017-02-24 13:09:16 -05:00
|
|
|
{
|
|
|
|
TempoMap& map (_editor->session()->tempo_map());
|
2017-09-18 12:39:17 -04:00
|
|
|
const double beat_at_sample = max (0.0, map.beat_at_sample (raw_grab_sample()));
|
2017-02-24 13:09:16 -05:00
|
|
|
const uint32_t divisions = _editor->get_grid_beat_divisions (0);
|
|
|
|
double beat = 0.0;
|
|
|
|
|
|
|
|
if (divisions > 0) {
|
2017-09-18 12:39:17 -04:00
|
|
|
beat = floor (beat_at_sample) + (floor (((beat_at_sample - floor (beat_at_sample)) * divisions)) / divisions);
|
2017-02-24 13:09:16 -05:00
|
|
|
} else {
|
|
|
|
/* while it makes some sense for the user to determine the division to 'grab',
|
|
|
|
grabbing a bar often leads to confusing results wrt the actual tempo section being altered
|
|
|
|
and the result over steep tempo curves. Use sixteenths.
|
|
|
|
*/
|
2017-09-18 12:39:17 -04:00
|
|
|
beat = floor (beat_at_sample) + (floor (((beat_at_sample - floor (beat_at_sample)) * 4)) / 4);
|
2017-02-24 13:09:16 -05:00
|
|
|
}
|
|
|
|
|
2020-12-07 13:40:00 -05:00
|
|
|
_grab_qn = map.quarters_at_beat (beat);
|
2017-02-24 13:09:16 -05:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
_pointer_offset = raw_grab_sample() - map.sample_at_quarter_note (_grab_qn);
|
2017-02-24 13:09:16 -05:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TempoTwistDrag::motion (GdkEvent* event, bool first_move)
|
|
|
|
{
|
2017-02-26 10:26:08 -05:00
|
|
|
|
|
|
|
if (!_next_tempo || !_drag_valid) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-02-24 13:09:16 -05:00
|
|
|
TempoMap& map (_editor->session()->tempo_map());
|
|
|
|
|
|
|
|
if (first_move) {
|
|
|
|
_editor->begin_reversible_command (_("twist tempo"));
|
|
|
|
}
|
|
|
|
|
2017-09-18 12:39:17 -04:00
|
|
|
samplepos_t pf;
|
2017-02-24 13:09:16 -05:00
|
|
|
|
2018-02-09 10:59:39 -05:00
|
|
|
if (_editor->grid_musical()) {
|
2017-09-18 12:39:17 -04:00
|
|
|
pf = adjusted_current_sample (event, false);
|
2017-02-24 13:09:16 -05:00
|
|
|
} else {
|
2017-09-18 12:39:17 -04:00
|
|
|
pf = adjusted_current_sample (event);
|
2017-02-24 13:09:16 -05:00
|
|
|
}
|
|
|
|
|
2017-09-18 12:39:17 -04:00
|
|
|
/* adjust this and the next tempi to match pointer sample */
|
2017-02-25 13:39:20 -05:00
|
|
|
double new_bpm = max (1.5, _grab_tempo.note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
|
2017-09-18 12:39:17 -04:00
|
|
|
_editor->session()->tempo_map().gui_twist_tempi (_tempo, new_bpm, map.sample_at_quarter_note (_grab_qn), pf);
|
2017-02-24 13:09:16 -05:00
|
|
|
|
|
|
|
ostringstream sstr;
|
2017-02-26 10:26:08 -05:00
|
|
|
sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute() << "\n";
|
|
|
|
sstr << "end: " << fixed << setprecision(3) << _tempo->end_note_types_per_minute() << "\n";
|
|
|
|
sstr << "start: " << fixed << setprecision(3) << _next_tempo->note_types_per_minute();
|
2017-02-24 13:09:16 -05:00
|
|
|
show_verbose_cursor_text (sstr.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TempoTwistDrag::finished (GdkEvent* event, bool movement_occurred)
|
|
|
|
{
|
2017-02-26 10:26:08 -05:00
|
|
|
if (!movement_occurred || !_drag_valid) {
|
2017-02-24 13:09:16 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-02-26 10:26:08 -05:00
|
|
|
_editor->tempo_curve_selected (_tempo, false);
|
|
|
|
_editor->tempo_curve_selected (_next_tempo, false);
|
2017-02-24 13:09:16 -05:00
|
|
|
|
2017-06-16 15:11:38 -04:00
|
|
|
TempoMap& map (_editor->session()->tempo_map());
|
2017-02-24 13:09:16 -05:00
|
|
|
XMLNode &after = map.get_state();
|
2017-03-04 13:21:56 -05:00
|
|
|
_editor->session()->add_command(new MementoCommand<TempoMap>(map, _before_state, &after));
|
2017-02-24 13:09:16 -05:00
|
|
|
_editor->commit_reversible_command ();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TempoTwistDrag::aborted (bool moved)
|
|
|
|
{
|
|
|
|
if (moved) {
|
2017-03-04 13:21:56 -05:00
|
|
|
_editor->session()->tempo_map().set_state (*_before_state, Stateful::current_state_version);
|
2017-02-24 13:09:16 -05:00
|
|
|
}
|
|
|
|
}
|
2020-10-15 01:09:22 -04:00
|
|
|
#endif
|
2017-02-24 13:09:16 -05:00
|
|
|
|
|
|
|
TempoEndDrag::TempoEndDrag (Editor* e, ArdourCanvas::Item* i)
|
|
|
|
: Drag (e, i)
|
|
|
|
, _tempo (0)
|
2017-03-04 13:21:56 -05:00
|
|
|
, _before_state (0)
|
2017-06-16 15:11:38 -04:00
|
|
|
, _drag_valid (true)
|
2017-02-24 13:09:16 -05:00
|
|
|
{
|
|
|
|
DEBUG_TRACE (DEBUG::Drags, "New TempoEndDrag\n");
|
2017-03-04 13:21:56 -05:00
|
|
|
TempoMarker* marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
|
|
|
|
_tempo = &marker->tempo();
|
2020-11-16 13:19:22 -05:00
|
|
|
_grab_qn = _tempo->beats();
|
2017-02-24 13:09:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TempoEndDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
|
|
|
|
{
|
|
|
|
Drag::start_grab (event, cursor);
|
2021-01-26 21:30:56 -05:00
|
|
|
|
|
|
|
TempoMap::fetch_writable();
|
|
|
|
|
|
|
|
TempoMap::SharedPtr tmap (TempoMap::use());
|
2017-03-04 13:21:56 -05:00
|
|
|
|
|
|
|
/* get current state */
|
2021-01-26 21:30:56 -05:00
|
|
|
_before_state = &tmap->get_state();
|
2017-06-16 15:11:38 -04:00
|
|
|
if (_tempo->locked_to_meter()) {
|
|
|
|
_drag_valid = false;
|
|
|
|
return;
|
|
|
|
}
|
2017-02-24 13:09:16 -05:00
|
|
|
|
|
|
|
ostringstream sstr;
|
2017-03-04 13:21:56 -05:00
|
|
|
|
2021-01-26 21:30:56 -05:00
|
|
|
TempoPoint const * prev = 0;
|
|
|
|
if ((prev = tmap->previous_tempo (*_tempo)) != 0) {
|
|
|
|
_editor->tempo_curve_selected (prev, true);
|
|
|
|
const samplecnt_t sr = AudioEngine::instance()->sample_rate();
|
|
|
|
sstr << "end: " << fixed << setprecision(3) << tmap->tempo_at (samples_to_superclock (_tempo->sample (sr) - 1, sr)).end_note_types_per_minute() << "\n";
|
2017-03-04 13:21:56 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (_tempo->clamped()) {
|
|
|
|
_editor->tempo_curve_selected (_tempo, true);
|
|
|
|
sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
|
|
|
|
}
|
|
|
|
|
2017-02-24 13:09:16 -05:00
|
|
|
show_verbose_cursor_text (sstr.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-10-15 01:09:22 -04:00
|
|
|
TempoEndDrag::setup_pointer_offset ()
|
2017-02-24 13:09:16 -05:00
|
|
|
{
|
2021-01-26 21:30:56 -05:00
|
|
|
_pointer_offset = timepos_t (_grab_qn).distance (raw_grab_time());
|
2017-02-24 13:09:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TempoEndDrag::motion (GdkEvent* event, bool first_move)
|
|
|
|
{
|
2017-06-16 15:11:38 -04:00
|
|
|
if (!_drag_valid) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-01-26 21:30:56 -05:00
|
|
|
TempoMap::SharedPtr map (TempoMap::use());
|
2017-02-24 13:09:16 -05:00
|
|
|
|
|
|
|
if (first_move) {
|
|
|
|
_editor->begin_reversible_command (_("stretch end tempo"));
|
|
|
|
}
|
|
|
|
|
2021-01-26 21:30:56 -05:00
|
|
|
timepos_t const pos = adjusted_current_time (event, false);
|
|
|
|
#warning NUTEMPO implement this
|
|
|
|
// map->gui_stretch_tempo_end (&map->tempo_section_at_sample (_tempo->sample() - 1), map.sample_at_quarter_note (_grab_qn), pf);
|
2017-02-24 13:09:16 -05:00
|
|
|
|
|
|
|
ostringstream sstr;
|
2021-01-26 21:30:56 -05:00
|
|
|
const samplecnt_t sr = AudioEngine::instance()->sample_rate();
|
|
|
|
sstr << "end: " << fixed << setprecision(3) << map->tempo_at (samples_to_superclock (_tempo->sample (sr) - 1, sr)).end_note_types_per_minute() << "\n";
|
2017-03-04 13:21:56 -05:00
|
|
|
|
|
|
|
if (_tempo->clamped()) {
|
|
|
|
sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
|
|
|
|
}
|
|
|
|
|
2017-02-24 13:09:16 -05:00
|
|
|
show_verbose_cursor_text (sstr.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TempoEndDrag::finished (GdkEvent* event, bool movement_occurred)
|
|
|
|
{
|
2017-06-16 15:11:38 -04:00
|
|
|
if (!movement_occurred || !_drag_valid) {
|
2021-01-26 21:30:56 -05:00
|
|
|
TempoMap::abort_update ();
|
2017-02-24 13:09:16 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-01-26 21:30:56 -05:00
|
|
|
TempoMap::SharedPtr tmap (TempoMap::use());
|
|
|
|
|
|
|
|
TempoMap::update (tmap);
|
2017-02-24 13:09:16 -05:00
|
|
|
|
2021-01-26 21:30:56 -05:00
|
|
|
tmap = TempoMap::use ();
|
|
|
|
|
|
|
|
XMLNode &after = tmap->get_state();
|
|
|
|
_editor->session()->add_command(new MementoCommand<TempoMap>(new Temporal::TempoMap::MementoBinder(), _before_state, &after));
|
2017-02-24 13:09:16 -05:00
|
|
|
_editor->commit_reversible_command ();
|
2017-03-04 13:21:56 -05:00
|
|
|
|
2021-01-26 21:30:56 -05:00
|
|
|
TempoPoint const * prev = 0;
|
|
|
|
|
|
|
|
if ((prev = tmap->previous_tempo (*_tempo)) != 0) {
|
2017-03-04 13:21:56 -05:00
|
|
|
_editor->tempo_curve_selected (prev, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_tempo->clamped()) {
|
|
|
|
_editor->tempo_curve_selected (_tempo, false);
|
|
|
|
|
|
|
|
}
|
2017-02-24 13:09:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TempoEndDrag::aborted (bool moved)
|
|
|
|
{
|
2021-01-26 21:30:56 -05:00
|
|
|
TempoMap::abort_update ();
|
2017-02-24 13:09:16 -05:00
|
|
|
}
|
2016-05-07 13:03:12 -04:00
|
|
|
|
2013-04-17 15:22:09 -04:00
|
|
|
CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
|
2014-06-08 11:26:25 -04:00
|
|
|
: Drag (e, &c.track_canvas_item(), false)
|
2013-04-17 15:22:09 -04:00
|
|
|
, _cursor (c)
|
|
|
|
, _stop (s)
|
2015-10-23 13:59:57 -04:00
|
|
|
, _grab_zoom (0.0)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2010-09-09 17:34:45 -04:00
|
|
|
DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2010-12-22 18:43:04 -05:00
|
|
|
/** Do all the things we do when dragging the playhead to make it look as though
|
|
|
|
* we have located, without actually doing the locate (because that would cause
|
|
|
|
* the diskstream buffers to be refilled, which is too slow).
|
|
|
|
*/
|
|
|
|
void
|
2017-09-18 12:39:17 -04:00
|
|
|
CursorDrag::fake_locate (samplepos_t t)
|
2010-12-22 18:43:04 -05:00
|
|
|
{
|
2015-08-14 02:04:24 -04:00
|
|
|
if (_editor->session () == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-01-05 15:58:28 -05:00
|
|
|
_editor->playhead_cursor ()->set_position (t);
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-12-22 18:43:04 -05:00
|
|
|
Session* s = _editor->session ();
|
|
|
|
if (s->timecode_transmission_suspended ()) {
|
2021-01-05 15:58:28 -05:00
|
|
|
samplepos_t const f = _editor->playhead_cursor ()->current_sample ();
|
2013-08-08 19:31:10 -04:00
|
|
|
/* This is asynchronous so it will be sent "now"
|
|
|
|
*/
|
2010-12-22 18:43:04 -05:00
|
|
|
s->send_mmc_locate (f);
|
2013-08-08 19:31:10 -04:00
|
|
|
/* These are synchronous and will be sent during the next
|
|
|
|
process cycle
|
|
|
|
*/
|
|
|
|
s->queue_full_time_code ();
|
|
|
|
s->queue_song_position_pointer ();
|
2010-12-22 18:43:04 -05:00
|
|
|
}
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
show_verbose_cursor_time (timepos_t (t));
|
2010-12-22 18:43:04 -05:00
|
|
|
_editor->UpdateAllTransportClocks (t);
|
|
|
|
}
|
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
void
|
|
|
|
CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
|
|
|
|
{
|
|
|
|
Drag::start_grab (event, c);
|
2020-03-25 13:03:18 -04:00
|
|
|
|
2021-01-14 14:55:51 -05:00
|
|
|
setup_snap_delta (timepos_t (_editor->playhead_cursor()->current_sample()));
|
2015-05-15 14:15:52 -04:00
|
|
|
|
2013-04-12 11:31:50 -04:00
|
|
|
_grab_zoom = _editor->samples_per_pixel;
|
2011-05-25 12:38:49 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t where (timepos_t (_editor->canvas_event_sample (event)) + snap_delta (event->button.state));
|
2013-04-18 15:14:48 -04:00
|
|
|
|
2010-12-22 18:43:04 -05:00
|
|
|
_editor->snap_to_with_modifier (where, event);
|
2020-03-25 13:03:18 -04:00
|
|
|
|
2010-12-22 18:16:39 -05:00
|
|
|
_editor->_dragging_playhead = true;
|
2020-10-15 01:09:22 -04:00
|
|
|
_editor->_control_scroll_target = where.samples();
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-12-22 18:16:39 -05:00
|
|
|
Session* s = _editor->session ();
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2013-04-17 15:22:09 -04:00
|
|
|
/* grab the track canvas item as well */
|
|
|
|
|
|
|
|
_cursor.track_canvas_item().grab();
|
|
|
|
|
2010-12-22 18:16:39 -05:00
|
|
|
if (s) {
|
|
|
|
if (_was_rolling && _stop) {
|
|
|
|
s->request_stop ();
|
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-12-22 18:16:39 -05:00
|
|
|
if (s->is_auditioning()) {
|
|
|
|
s->cancel_audition ();
|
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2012-07-10 00:18:06 -04:00
|
|
|
|
2017-04-21 19:06:54 -04:00
|
|
|
if (AudioEngine::instance()->running()) {
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2012-07-10 00:18:06 -04:00
|
|
|
/* do this only if we're the engine is connected
|
|
|
|
* because otherwise this request will never be
|
|
|
|
* serviced and we'll busy wait forever. likewise,
|
|
|
|
* notice if we are disconnected while waiting for the
|
|
|
|
* request to be serviced.
|
|
|
|
*/
|
|
|
|
|
2012-07-10 00:05:48 -04:00
|
|
|
s->request_suspend_timecode_transmission ();
|
2017-04-21 17:54:17 -04:00
|
|
|
while (AudioEngine::instance()->running() && !s->timecode_transmission_suspended ()) {
|
2012-07-10 00:05:48 -04:00
|
|
|
/* twiddle our thumbs */
|
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
fake_locate (where.earlier (snap_delta (event->button.state)).samples());
|
2020-03-24 20:55:16 -04:00
|
|
|
|
2021-05-21 11:57:56 -04:00
|
|
|
_last_mx = event->button.x;
|
|
|
|
_last_my = event->button.y;
|
|
|
|
_last_dx = 0;
|
2017-07-20 10:51:26 -04:00
|
|
|
_last_y_delta = 0;
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-06-01 20:39:57 -04:00
|
|
|
CursorDrag::motion (GdkEvent* event, bool)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t where (timepos_t (_editor->canvas_event_sample (event)) + snap_delta (event->button.state));
|
2017-01-26 08:41:17 -05:00
|
|
|
|
2015-05-15 14:15:52 -04:00
|
|
|
_editor->snap_to_with_modifier (where, event);
|
2017-01-26 08:41:17 -05:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
if (where != last_pointer_time()) {
|
|
|
|
fake_locate (where.earlier (snap_delta (event->button.state)).samples());;
|
2011-05-25 12:38:49 -04:00
|
|
|
}
|
2020-03-24 20:55:16 -04:00
|
|
|
|
2017-07-20 10:51:26 -04:00
|
|
|
//maybe do zooming, too, if the option is enabled
|
|
|
|
if (UIConfiguration::instance ().get_use_time_rulers_to_zoom_with_vertical_drag () ) {
|
2020-03-24 20:55:16 -04:00
|
|
|
|
2017-07-20 10:51:26 -04:00
|
|
|
//To avoid accidental zooming, the mouse must move exactly vertical, not diagonal, to trigger a zoom step
|
|
|
|
//we use screen coordinates for this, not canvas-based grab_x
|
|
|
|
double mx = event->button.x;
|
|
|
|
double dx = fabs(mx - _last_mx);
|
|
|
|
double my = event->button.y;
|
|
|
|
double dy = fabs(my - _last_my);
|
|
|
|
|
|
|
|
{
|
2017-07-20 15:14:35 -04:00
|
|
|
//do zooming in windowed "steps" so it feels more reversible ?
|
|
|
|
const int stepsize = 2; //stepsize ==1 means "trigger on every pixel of movement"
|
2017-07-20 10:51:26 -04:00
|
|
|
int y_delta = grab_y() - current_pointer_y();
|
|
|
|
y_delta = y_delta / stepsize;
|
|
|
|
|
|
|
|
//if all requirements are met, do the actual zoom
|
2017-07-20 15:14:35 -04:00
|
|
|
const double scale = 1.2;
|
2017-07-20 10:51:26 -04:00
|
|
|
if ( (dy>dx) && (_last_dx ==0) && (y_delta != _last_y_delta) ) {
|
|
|
|
if ( _last_y_delta > y_delta ) {
|
|
|
|
_editor->temporal_zoom_step_mouse_focus_scale (true, scale);
|
|
|
|
} else {
|
|
|
|
_editor->temporal_zoom_step_mouse_focus_scale (false, scale);
|
|
|
|
}
|
|
|
|
_last_y_delta = y_delta;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_last_my = my;
|
|
|
|
_last_mx = mx;
|
|
|
|
_last_dx = dx;
|
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-06-01 20:39:57 -04:00
|
|
|
CursorDrag::finished (GdkEvent* event, bool movement_occurred)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
|
|
|
_editor->_dragging_playhead = false;
|
|
|
|
|
2013-04-17 15:22:09 -04:00
|
|
|
_cursor.track_canvas_item().ungrab();
|
|
|
|
|
2009-06-01 20:39:57 -04:00
|
|
|
if (!movement_occurred && _stop) {
|
2009-05-30 14:25:59 -04:00
|
|
|
return;
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-06-01 20:39:57 -04:00
|
|
|
motion (event, false);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2010-12-22 18:16:39 -05:00
|
|
|
Session* s = _editor->session ();
|
|
|
|
if (s) {
|
|
|
|
_editor->_pending_locate_request = true;
|
2021-01-05 15:58:28 -05:00
|
|
|
s->request_locate (_editor->playhead_cursor ()->current_sample (), _was_rolling ? MustRoll : RollIfAppropriate);
|
2010-12-22 18:16:39 -05:00
|
|
|
s->request_resume_timecode_transmission ();
|
2009-10-14 12:10:01 -04:00
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2010-01-07 20:28:15 -05:00
|
|
|
void
|
2010-12-28 14:17:37 -05:00
|
|
|
CursorDrag::aborted (bool)
|
2010-01-07 20:28:15 -05:00
|
|
|
{
|
2013-04-17 15:22:09 -04:00
|
|
|
_cursor.track_canvas_item().ungrab();
|
|
|
|
|
2010-08-01 21:59:34 -04:00
|
|
|
if (_editor->_dragging_playhead) {
|
|
|
|
_editor->session()->request_resume_timecode_transmission ();
|
|
|
|
_editor->_dragging_playhead = false;
|
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2021-01-14 14:55:51 -05:00
|
|
|
_editor->playhead_cursor()->set_position (adjusted_time (grab_time (), 0, false).samples());
|
2010-01-07 20:28:15 -05:00
|
|
|
}
|
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
|
|
|
|
: RegionDrag (e, i, p, v)
|
|
|
|
{
|
2010-09-09 17:34:45 -04:00
|
|
|
DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
|
|
|
|
{
|
|
|
|
Drag::start_grab (event, cursor);
|
|
|
|
|
2010-09-07 07:41:16 -04:00
|
|
|
AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
|
|
|
|
boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
|
2020-11-30 12:59:17 -05:00
|
|
|
setup_snap_delta (r->position());
|
2015-05-15 14:15:52 -04:00
|
|
|
|
2020-11-30 12:59:17 -05:00
|
|
|
show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
|
|
|
|
show_view_preview (r->position() + r->fade_in()->back()->when);
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2010-12-19 22:42:59 -05:00
|
|
|
void
|
2020-10-15 01:09:22 -04:00
|
|
|
FadeInDrag::setup_pointer_offset ()
|
2010-12-19 22:42:59 -05:00
|
|
|
{
|
|
|
|
AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
|
|
|
|
boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
|
2020-11-30 12:59:17 -05:00
|
|
|
_pointer_offset = (r->fade_in()->back()->when + r->position()).distance (raw_grab_time());
|
2010-12-19 22:42:59 -05:00
|
|
|
}
|
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
void
|
2009-06-01 20:39:57 -04:00
|
|
|
FadeInDrag::motion (GdkEvent* event, bool)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2015-05-15 14:15:52 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t tpos (timepos_t (_editor->canvas_event_sample (event)) + snap_delta (event->button.state));
|
|
|
|
_editor->snap_to_with_modifier (tpos, event);
|
|
|
|
tpos.shift_earlier (snap_delta (event->button.state));
|
2017-01-26 08:41:17 -05:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
samplepos_t pos = tpos.samples ();
|
2015-05-15 14:15:52 -04:00
|
|
|
|
2015-02-13 16:13:12 -05:00
|
|
|
boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
samplecnt_t fade_length;
|
|
|
|
|
|
|
|
if (pos < (region->position_sample() + 64)) {
|
2009-05-30 14:25:59 -04:00
|
|
|
fade_length = 64; // this should be a minimum defined somewhere
|
2020-10-15 01:09:22 -04:00
|
|
|
} else if (pos > region->position_sample() + region->length_samples() - region->fade_out()->back()->when.samples()) {
|
|
|
|
fade_length = region->length_samples() - region->fade_out()->back()->when.samples() - 1;
|
2009-05-30 14:25:59 -04:00
|
|
|
} else {
|
2020-10-15 01:09:22 -04:00
|
|
|
fade_length = pos - region->position_sample();
|
2009-10-14 12:10:01 -04:00
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2010-04-06 20:17:54 -04:00
|
|
|
for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2010-04-06 20:17:54 -04:00
|
|
|
AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
if (!tmp) {
|
|
|
|
continue;
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2012-12-14 12:21:44 -05:00
|
|
|
tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2020-11-30 12:59:17 -05:00
|
|
|
show_verbose_cursor_duration (region->position(), region->position() + timepos_t (fade_length), 32);
|
|
|
|
show_view_preview (region->position() + timepos_t (fade_length));
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-06-01 20:39:57 -04:00
|
|
|
FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2009-06-01 20:39:57 -04:00
|
|
|
if (!movement_occurred) {
|
2009-05-30 14:25:59 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t tpos (timepos_t (_editor->canvas_event_sample (event)) + snap_delta (event->button.state));
|
|
|
|
_editor->snap_to_with_modifier (tpos, event);
|
|
|
|
tpos.shift_earlier (snap_delta (event->button.state));
|
2017-01-26 08:41:17 -05:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
samplepos_t pos = tpos.samples ();
|
|
|
|
samplecnt_t fade_length;
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2015-02-13 16:13:12 -05:00
|
|
|
boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
if (pos < (region->position_sample() + 64)) {
|
2009-05-30 14:25:59 -04:00
|
|
|
fade_length = 64; // this should be a minimum defined somewhere
|
2020-10-15 01:09:22 -04:00
|
|
|
} else if (pos >= region->position_sample() + region->length_samples() - region->fade_out()->back()->when.samples()) {
|
|
|
|
fade_length = region->length_samples() - region->fade_out()->back()->when.samples() - 1;
|
2009-05-30 14:25:59 -04:00
|
|
|
} else {
|
2020-10-15 01:09:22 -04:00
|
|
|
fade_length = pos - region->position_sample();
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2015-06-16 19:26:40 -04:00
|
|
|
bool in_command = false;
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2010-04-06 20:17:54 -04:00
|
|
|
for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2010-04-06 20:17:54 -04:00
|
|
|
AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
if (!tmp) {
|
|
|
|
continue;
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
boost::shared_ptr<AutomationList> 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);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2015-06-16 19:26:40 -04:00
|
|
|
if (!in_command) {
|
|
|
|
_editor->begin_reversible_command (_("change fade in length"));
|
|
|
|
in_command = true;
|
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
XMLNode &after = alist->get_state();
|
2009-12-17 13:24:23 -05:00
|
|
|
_editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2015-06-16 19:26:40 -04:00
|
|
|
if (in_command) {
|
|
|
|
_editor->commit_reversible_command ();
|
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2010-01-07 20:28:15 -05:00
|
|
|
void
|
2010-12-28 14:17:37 -05:00
|
|
|
FadeInDrag::aborted (bool)
|
2010-01-07 20:28:15 -05:00
|
|
|
{
|
2010-04-06 20:17:54 -04:00
|
|
|
for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
|
|
|
|
AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
|
2010-01-07 20:28:15 -05:00
|
|
|
|
|
|
|
if (!tmp) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when.samples());
|
2010-01-07 20:28:15 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
|
|
|
|
: RegionDrag (e, i, p, v)
|
|
|
|
{
|
2010-09-09 17:34:45 -04:00
|
|
|
DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
|
|
|
|
{
|
|
|
|
Drag::start_grab (event, cursor);
|
|
|
|
|
2010-09-07 07:41:16 -04:00
|
|
|
AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
|
|
|
|
boost::shared_ptr<AudioRegion> r = arv->audio_region ();
|
2020-10-15 01:09:22 -04:00
|
|
|
setup_snap_delta (r->nt_last());
|
2015-05-15 14:15:52 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
show_verbose_cursor_duration (r->nt_last().earlier (r->fade_out()->back()->when), r->nt_last());
|
2019-11-08 23:03:38 -05:00
|
|
|
show_view_preview (r->fade_out()->back()->when);
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2010-12-19 22:42:59 -05:00
|
|
|
void
|
2020-10-15 01:09:22 -04:00
|
|
|
FadeOutDrag::setup_pointer_offset ()
|
2010-12-19 22:42:59 -05:00
|
|
|
{
|
|
|
|
AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
|
|
|
|
boost::shared_ptr<AudioRegion> r = arv->audio_region ();
|
2020-11-30 12:59:17 -05:00
|
|
|
_pointer_offset = (r->position() + (r->length() - r->fade_out()->back()->when)).distance (raw_grab_time());
|
2011-06-01 13:00:29 -04:00
|
|
|
}
|
2010-12-19 22:42:59 -05:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
void
|
2009-06-01 20:39:57 -04:00
|
|
|
FadeOutDrag::motion (GdkEvent* event, bool)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2017-09-18 12:39:17 -04:00
|
|
|
samplecnt_t fade_length;
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t tpos (timepos_t (_editor->canvas_event_sample (event)) + snap_delta (event->button.state));
|
|
|
|
_editor->snap_to_with_modifier (tpos, event);
|
|
|
|
tpos.shift_earlier (snap_delta (event->button.state));
|
|
|
|
|
|
|
|
samplepos_t pos (tpos.samples());
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2015-02-13 16:13:12 -05:00
|
|
|
boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
if (pos > (region->last_sample() - 64)) {
|
2009-05-30 14:25:59 -04:00
|
|
|
fade_length = 64; // this should really be a minimum fade defined somewhere
|
2020-10-15 01:09:22 -04:00
|
|
|
} else if (pos <= region->position_sample() + region->fade_in()->back()->when.samples()) {
|
|
|
|
fade_length = region->length_samples() - region->fade_in()->back()->when.samples() - 1;
|
2015-02-13 16:13:12 -05:00
|
|
|
} else {
|
2020-10-15 01:09:22 -04:00
|
|
|
fade_length = region->last_sample() - pos;
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2010-04-06 20:17:54 -04:00
|
|
|
for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2010-04-06 20:17:54 -04:00
|
|
|
AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
if (!tmp) {
|
|
|
|
continue;
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2012-12-14 12:21:44 -05:00
|
|
|
tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
show_verbose_cursor_duration (timepos_t (region->last_sample() - fade_length), region->nt_last());
|
|
|
|
show_view_preview (timepos_t (region->last_sample() - fade_length));
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-06-01 20:39:57 -04:00
|
|
|
FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2009-06-01 20:39:57 -04:00
|
|
|
if (!movement_occurred) {
|
2009-05-30 14:25:59 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t tpos (timepos_t (_editor->canvas_event_sample (event)) + snap_delta (event->button.state));
|
|
|
|
_editor->snap_to_with_modifier (tpos, event);
|
|
|
|
tpos.shift_earlier (snap_delta (event->button.state));
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
samplepos_t pos (tpos.samples());
|
|
|
|
|
|
|
|
samplecnt_t fade_length;
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2015-02-13 16:13:12 -05:00
|
|
|
boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
if (pos > (region->last_sample() - 64)) {
|
2009-05-30 14:25:59 -04:00
|
|
|
fade_length = 64; // this should really be a minimum fade defined somewhere
|
2020-10-15 01:09:22 -04:00
|
|
|
} else if (pos <= region->position_sample() + region->fade_in()->back()->when.samples()) {
|
|
|
|
fade_length = region->length_samples() - region->fade_in()->back()->when.samples() - 1;
|
2015-02-13 16:13:12 -05:00
|
|
|
} else {
|
2020-10-15 01:09:22 -04:00
|
|
|
fade_length = region->last_sample() - pos;
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2015-06-16 19:26:40 -04:00
|
|
|
bool in_command = false;
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2010-04-06 20:17:54 -04:00
|
|
|
for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2010-04-06 20:17:54 -04:00
|
|
|
AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
if (!tmp) {
|
|
|
|
continue;
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
|
|
|
|
XMLNode &before = alist->get_state();
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
tmp->audio_region()->set_fade_out_length (fade_length);
|
|
|
|
tmp->audio_region()->set_fade_out_active (true);
|
|
|
|
|
2015-06-16 19:26:40 -04:00
|
|
|
if (!in_command) {
|
|
|
|
_editor->begin_reversible_command (_("change fade out length"));
|
2015-08-18 23:52:23 -04:00
|
|
|
in_command = true;
|
2015-06-16 19:26:40 -04:00
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
XMLNode &after = alist->get_state();
|
2009-12-17 13:24:23 -05:00
|
|
|
_editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2015-06-16 19:26:40 -04:00
|
|
|
if (in_command) {
|
|
|
|
_editor->commit_reversible_command ();
|
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2010-01-07 20:28:15 -05:00
|
|
|
void
|
2010-12-28 14:17:37 -05:00
|
|
|
FadeOutDrag::aborted (bool)
|
2010-01-07 20:28:15 -05:00
|
|
|
{
|
2010-04-06 20:17:54 -04:00
|
|
|
for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
|
|
|
|
AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
|
2010-01-07 20:28:15 -05:00
|
|
|
|
|
|
|
if (!tmp) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when.samples());
|
2010-01-07 20:28:15 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
|
|
|
|
: Drag (e, i)
|
2015-10-24 14:26:44 -04:00
|
|
|
, _selection_changed (false)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2010-09-09 17:34:45 -04:00
|
|
|
DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
|
2015-07-22 17:49:41 -04:00
|
|
|
Gtk::Window* toplevel = _editor->current_toplevel();
|
2015-07-27 18:24:32 -04:00
|
|
|
_marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
assert (_marker);
|
|
|
|
|
2013-04-04 00:32:52 -04:00
|
|
|
_points.push_back (ArdourCanvas::Duple (0, 0));
|
2015-07-22 17:49:41 -04:00
|
|
|
|
|
|
|
_points.push_back (ArdourCanvas::Duple (0, toplevel ? physical_screen_height (toplevel->get_window()) : 900));
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
MarkerDrag::~MarkerDrag ()
|
|
|
|
{
|
2013-01-02 18:54:06 -05:00
|
|
|
for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
|
|
|
|
delete i->location;
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-23 07:23:47 -04:00
|
|
|
MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
|
2013-01-02 18:54:06 -05:00
|
|
|
{
|
|
|
|
location = new Location (*l);
|
|
|
|
markers.push_back (m);
|
|
|
|
move_both = false;
|
|
|
|
}
|
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
void
|
|
|
|
MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
|
|
|
|
{
|
|
|
|
Drag::start_grab (event, cursor);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
bool is_start;
|
|
|
|
|
|
|
|
Location *location = _editor->find_location_from_marker (_marker, is_start);
|
|
|
|
|
|
|
|
update_item (location);
|
|
|
|
|
|
|
|
// _drag_line->show();
|
|
|
|
// _line->raise_to_top();
|
|
|
|
|
|
|
|
if (is_start) {
|
2011-05-02 09:38:16 -04:00
|
|
|
show_verbose_cursor_time (location->start());
|
2009-05-30 14:25:59 -04:00
|
|
|
} else {
|
2011-05-02 09:38:16 -04:00
|
|
|
show_verbose_cursor_time (location->end());
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
2020-10-15 01:09:22 -04:00
|
|
|
show_view_preview ((is_start ? location->start() : location->end()) + _video_offset);
|
|
|
|
setup_snap_delta (timepos_t (is_start ? location->start() : location->end()));
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2009-12-04 17:51:32 -05:00
|
|
|
Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
|
2009-05-30 14:25:59 -04:00
|
|
|
|
|
|
|
switch (op) {
|
|
|
|
case Selection::Toggle:
|
2013-01-02 18:54:06 -05:00
|
|
|
/* we toggle on the button release */
|
2009-05-30 14:25:59 -04:00
|
|
|
break;
|
|
|
|
case Selection::Set:
|
|
|
|
if (!_editor->selection->selected (_marker)) {
|
|
|
|
_editor->selection->set (_marker);
|
2015-10-24 14:26:44 -04:00
|
|
|
_selection_changed = true;
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Selection::Extend:
|
|
|
|
{
|
|
|
|
Locations::LocationList ll;
|
2015-07-23 07:23:47 -04:00
|
|
|
list<ArdourMarker*> to_add;
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t s, e;
|
2009-05-30 14:25:59 -04:00
|
|
|
_editor->selection->markers.range (s, e);
|
|
|
|
s = min (_marker->position(), s);
|
|
|
|
e = max (_marker->position(), e);
|
|
|
|
s = min (s, e);
|
|
|
|
e = max (s, e);
|
2020-10-15 01:09:22 -04:00
|
|
|
if (e < timepos_t::max (e.time_domain())) {
|
|
|
|
e.increment();
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
2009-12-17 13:24:23 -05:00
|
|
|
_editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
|
2009-05-30 14:25:59 -04:00
|
|
|
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);
|
2015-10-24 14:26:44 -04:00
|
|
|
_selection_changed = true;
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Selection::Add:
|
|
|
|
_editor->selection->add (_marker);
|
2015-10-24 14:26:44 -04:00
|
|
|
_selection_changed = true;
|
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-03-24 16:59:57 -04:00
|
|
|
/* Set up copies for us to manipulate during the drag
|
2013-01-02 18:54:06 -05:00
|
|
|
*/
|
2009-05-30 14:25:59 -04:00
|
|
|
|
|
|
|
for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
|
2013-01-02 18:54:06 -05:00
|
|
|
|
2010-04-18 17:29:48 -04:00
|
|
|
Location* l = _editor->find_location_from_marker (*i, is_start);
|
2013-01-02 18:54:06 -05:00
|
|
|
|
|
|
|
if (!l) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (l->is_mark()) {
|
|
|
|
_copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
|
|
|
|
} else {
|
|
|
|
/* range: check that the other end of the range isn't
|
|
|
|
already there.
|
|
|
|
*/
|
|
|
|
CopiedLocationInfo::iterator x;
|
|
|
|
for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
|
|
|
|
if (*(*x).location == *l) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (x == _copied_locations.end()) {
|
|
|
|
_copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
|
|
|
|
} else {
|
|
|
|
(*x).markers.push_back (*i);
|
|
|
|
(*x).move_both = true;
|
|
|
|
}
|
|
|
|
}
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-19 22:42:59 -05:00
|
|
|
void
|
2020-10-15 01:09:22 -04:00
|
|
|
MarkerDrag::setup_pointer_offset ()
|
2010-12-19 22:42:59 -05:00
|
|
|
{
|
|
|
|
bool is_start;
|
|
|
|
Location *location = _editor->find_location_from_marker (_marker, is_start);
|
2020-10-15 01:09:22 -04:00
|
|
|
_pointer_offset = (is_start ? location->start() : location->end()).distance (raw_grab_time());
|
2010-12-19 22:42:59 -05:00
|
|
|
}
|
|
|
|
|
2019-11-08 23:03:38 -05:00
|
|
|
void
|
2020-10-15 01:09:22 -04:00
|
|
|
MarkerDrag::setup_video_offset ()
|
2019-11-08 23:03:38 -05:00
|
|
|
{
|
2020-12-06 14:57:48 -05:00
|
|
|
_video_offset = timecnt_t (Temporal::AudioTime);
|
2019-11-08 23:03:38 -05:00
|
|
|
_preview_video = true;
|
|
|
|
}
|
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
void
|
2009-06-01 20:39:57 -04:00
|
|
|
MarkerDrag::motion (GdkEvent* event, bool)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2020-10-15 01:09:22 -04:00
|
|
|
timecnt_t f_delta;
|
2009-05-30 14:25:59 -04:00
|
|
|
bool is_start;
|
|
|
|
bool move_both = false;
|
2010-04-18 17:29:48 -04:00
|
|
|
Location *real_location;
|
2009-05-30 14:25:59 -04:00
|
|
|
Location *copy_location = 0;
|
2020-10-15 01:09:22 -04:00
|
|
|
timecnt_t const sd = snap_delta (event->button.state);
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t const newpos = adjusted_time (_drags->current_pointer_time () + sd, event, true).earlier (sd);
|
|
|
|
timepos_t next = newpos;
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2015-05-21 12:12:58 -04:00
|
|
|
if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
|
2009-05-30 14:25:59 -04:00
|
|
|
move_both = true;
|
|
|
|
}
|
|
|
|
|
2013-01-02 18:54:06 -05:00
|
|
|
CopiedLocationInfo::iterator x;
|
2009-05-30 14:25:59 -04:00
|
|
|
|
|
|
|
/* find the marker we're dragging, and compute the delta */
|
|
|
|
|
2013-01-02 18:54:06 -05:00
|
|
|
for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
|
|
|
|
|
|
|
|
copy_location = (*x).location;
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2013-01-02 18:54:06 -05:00
|
|
|
if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2013-01-02 18:54:06 -05:00
|
|
|
/* this marker is represented by this
|
2015-03-24 16:59:57 -04:00
|
|
|
* CopiedLocationMarkerInfo
|
2013-01-02 18:54:06 -05:00
|
|
|
*/
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2013-01-02 18:54:06 -05:00
|
|
|
if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
|
2009-05-30 14:25:59 -04:00
|
|
|
/* que pasa ?? */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (real_location->is_mark()) {
|
2020-10-15 01:09:22 -04:00
|
|
|
f_delta = copy_location->start().distance (newpos);
|
2009-05-30 14:25:59 -04:00
|
|
|
} else {
|
|
|
|
|
|
|
|
|
2013-01-02 18:54:06 -05:00
|
|
|
switch (_marker->type()) {
|
2015-07-23 07:23:47 -04:00
|
|
|
case ArdourMarker::SessionStart:
|
|
|
|
case ArdourMarker::RangeStart:
|
|
|
|
case ArdourMarker::LoopStart:
|
|
|
|
case ArdourMarker::PunchIn:
|
2020-10-15 01:09:22 -04:00
|
|
|
f_delta = copy_location->start().distance (newpos);
|
2009-05-30 14:25:59 -04:00
|
|
|
break;
|
|
|
|
|
2015-07-23 07:23:47 -04:00
|
|
|
case ArdourMarker::SessionEnd:
|
|
|
|
case ArdourMarker::RangeEnd:
|
|
|
|
case ArdourMarker::LoopEnd:
|
|
|
|
case ArdourMarker::PunchOut:
|
2020-10-15 01:09:22 -04:00
|
|
|
f_delta = copy_location->end().distance (newpos);
|
2009-05-30 14:25:59 -04:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* what kind of marker is this ? */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2013-01-02 18:54:06 -05:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-02 18:54:06 -05:00
|
|
|
if (x == _copied_locations.end()) {
|
2009-05-30 14:25:59 -04:00
|
|
|
/* hmm, impossible - we didn't find the dragged marker */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* now move them all */
|
|
|
|
|
2013-01-02 18:54:06 -05:00
|
|
|
for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2013-01-02 18:54:06 -05:00
|
|
|
copy_location = x->location;
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2013-01-02 18:54:06 -05:00
|
|
|
if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
|
2009-05-30 14:25:59 -04:00
|
|
|
continue;
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
if (real_location->locked()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (copy_location->is_mark()) {
|
|
|
|
|
2010-04-18 17:29:48 -04:00
|
|
|
/* now move it */
|
2020-10-15 01:09:22 -04:00
|
|
|
copy_location->set_start (copy_location->start() + f_delta, false);
|
2009-05-30 14:25:59 -04:00
|
|
|
|
|
|
|
} else {
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t new_start = copy_location->start() + f_delta;
|
|
|
|
timepos_t new_end = copy_location->end() + f_delta;
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2013-01-02 18:54:06 -05:00
|
|
|
if (is_start) { // start-of-range marker
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2013-01-02 18:54:06 -05:00
|
|
|
if (move_both || (*x).move_both) {
|
2020-10-15 01:09:22 -04:00
|
|
|
copy_location->set_start (new_start, false);
|
|
|
|
copy_location->set_end (new_end, false);
|
2019-04-13 11:48:27 -04:00
|
|
|
} else if (new_start < copy_location->end()) {
|
2020-10-15 01:09:22 -04:00
|
|
|
copy_location->set_start (new_start, false);
|
|
|
|
} else if (newpos.positive()) {
|
2015-10-23 13:59:57 -04:00
|
|
|
//_editor->snap_to (next, RoundUpAlways, true);
|
2020-10-15 01:09:22 -04:00
|
|
|
copy_location->set_end (next, false);
|
|
|
|
copy_location->set_start (newpos, false);
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
} else { // end marker
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2013-01-02 18:54:06 -05:00
|
|
|
if (move_both || (*x).move_both) {
|
2020-10-15 01:09:22 -04:00
|
|
|
copy_location->set_end (new_end);
|
|
|
|
copy_location->set_start (new_start, false);
|
2009-05-30 14:25:59 -04:00
|
|
|
} else if (new_end > copy_location->start()) {
|
2020-10-15 01:09:22 -04:00
|
|
|
copy_location->set_end (new_end, false);
|
|
|
|
} else if (newpos.positive()) {
|
2015-10-23 13:59:57 -04:00
|
|
|
//_editor->snap_to (next, RoundDownAlways, true);
|
2020-10-15 01:09:22 -04:00
|
|
|
copy_location->set_start (next, false);
|
|
|
|
copy_location->set_end (newpos, false);
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
update_item (copy_location);
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2013-01-02 18:54:06 -05:00
|
|
|
/* now lookup the actual GUI items used to display this
|
|
|
|
* location and move them to wherever the copy of the location
|
|
|
|
* is now. This means that the logic in ARDOUR::Location is
|
2015-03-24 16:59:57 -04:00
|
|
|
* still enforced, even though we are not (yet) modifying
|
2013-01-02 18:54:06 -05:00
|
|
|
* the real Location itself.
|
|
|
|
*/
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
|
|
|
|
|
|
|
|
if (lm) {
|
|
|
|
lm->set_position (copy_location->start(), copy_location->end());
|
|
|
|
}
|
2013-01-02 18:54:06 -05:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
assert (!_copied_locations.empty());
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
show_verbose_cursor_time (newpos);
|
|
|
|
show_view_preview (newpos + _video_offset);
|
|
|
|
_editor->set_snapped_cursor_position (newpos);
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-06-01 20:39:57 -04:00
|
|
|
MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2009-06-01 20:39:57 -04:00
|
|
|
if (!movement_occurred) {
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2013-06-21 15:18:54 -04:00
|
|
|
if (was_double_click()) {
|
2014-01-27 10:09:58 -05:00
|
|
|
_editor->rename_marker (_marker);
|
|
|
|
return;
|
2013-06-21 15:18:54 -04:00
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
|
|
|
|
/* just a click, do nothing but finish
|
|
|
|
off the selection process
|
|
|
|
*/
|
|
|
|
|
2009-12-04 17:51:32 -05:00
|
|
|
Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
|
2009-05-30 14:25:59 -04:00
|
|
|
switch (op) {
|
|
|
|
case Selection::Set:
|
|
|
|
if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
|
|
|
|
_editor->selection->set (_marker);
|
2015-10-24 14:26:44 -04:00
|
|
|
_selection_changed = true;
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Selection::Toggle:
|
2013-01-02 18:54:06 -05:00
|
|
|
/* we toggle on the button release, click only */
|
|
|
|
_editor->selection->toggle (_marker);
|
2015-10-24 14:26:44 -04:00
|
|
|
_selection_changed = true;
|
|
|
|
|
2013-01-02 18:54:06 -05:00
|
|
|
break;
|
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
case Selection::Extend:
|
|
|
|
case Selection::Add:
|
|
|
|
break;
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2015-10-24 14:26:44 -04:00
|
|
|
if (_selection_changed) {
|
|
|
|
_editor->begin_reversible_selection_op(X_("Select Marker Release"));
|
|
|
|
_editor->commit_reversible_selection_op();
|
|
|
|
}
|
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-12-17 13:24:23 -05:00
|
|
|
XMLNode &before = _editor->session()->locations()->get_state();
|
2015-06-16 19:26:40 -04:00
|
|
|
bool in_command = false;
|
2009-05-30 14:25:59 -04:00
|
|
|
|
|
|
|
MarkerSelection::iterator i;
|
2013-01-02 18:54:06 -05:00
|
|
|
CopiedLocationInfo::iterator x;
|
2009-05-30 14:25:59 -04:00
|
|
|
bool is_start;
|
|
|
|
|
2009-10-14 12:10:01 -04:00
|
|
|
for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
|
|
|
|
x != _copied_locations.end() && i != _editor->selection->markers.end();
|
2009-05-30 14:25:59 -04:00
|
|
|
++i, ++x) {
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
Location * location = _editor->find_location_from_marker (*i, is_start);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
if (location) {
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
if (location->locked()) {
|
2015-06-16 19:26:40 -04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!in_command) {
|
|
|
|
_editor->begin_reversible_command ( _("move marker") );
|
|
|
|
in_command = true;
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
if (location->is_mark()) {
|
2020-10-15 01:09:22 -04:00
|
|
|
location->set_start (((*x).location)->start(), false);
|
2009-05-30 14:25:59 -04:00
|
|
|
} else {
|
2020-10-15 01:09:22 -04:00
|
|
|
location->set (((*x).location)->start(), ((*x).location)->end());
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
2016-07-12 11:19:34 -04:00
|
|
|
|
|
|
|
if (location->is_session_range()) {
|
2019-02-12 12:15:34 -05:00
|
|
|
_editor->session()->set_session_range_is_free (false);
|
2016-07-12 11:19:34 -04:00
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-16 19:26:40 -04:00
|
|
|
if (in_command) {
|
|
|
|
XMLNode &after = _editor->session()->locations()->get_state();
|
|
|
|
_editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
|
|
|
|
_editor->commit_reversible_command ();
|
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2010-01-07 20:28:15 -05:00
|
|
|
void
|
2016-02-22 15:01:23 -05:00
|
|
|
MarkerDrag::aborted (bool movement_occurred)
|
2010-01-07 20:28:15 -05:00
|
|
|
{
|
2016-02-22 15:01:23 -05:00
|
|
|
if (!movement_occurred) {
|
2015-02-05 18:00:32 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
|
|
|
|
|
|
|
|
/* move all markers to their original location */
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2015-02-05 20:26:02 -05:00
|
|
|
|
2015-07-23 07:23:47 -04:00
|
|
|
for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
|
2015-02-05 20:26:02 -05:00
|
|
|
|
|
|
|
bool is_start;
|
|
|
|
Location * location = _editor->find_location_from_marker (*m, is_start);
|
|
|
|
|
|
|
|
if (location) {
|
|
|
|
(*m)->set_position (is_start ? location->start() : location->end());
|
|
|
|
}
|
2015-02-05 18:00:32 -05:00
|
|
|
}
|
|
|
|
}
|
2010-01-07 20:28:15 -05:00
|
|
|
}
|
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
void
|
2011-09-30 13:55:14 -04:00
|
|
|
MarkerDrag::update_item (Location*)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2017-07-01 12:42:24 -04:00
|
|
|
/* noop */
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
|
2015-10-23 13:59:57 -04:00
|
|
|
: Drag (e, i)
|
|
|
|
, _fixed_grab_x (0.0)
|
|
|
|
, _fixed_grab_y (0.0)
|
|
|
|
, _cumulative_x_drag (0.0)
|
|
|
|
, _cumulative_y_drag (0.0)
|
2015-08-26 12:20:43 -04:00
|
|
|
, _pushing (false)
|
2015-10-23 13:59:57 -04:00
|
|
|
, _final_index (0)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2011-06-19 19:02:55 -04:00
|
|
|
if (_zero_gain_fraction < 0.0) {
|
|
|
|
_zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
|
|
|
|
}
|
|
|
|
|
2010-09-09 17:34:45 -04:00
|
|
|
DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2009-06-09 11:46:33 -04:00
|
|
|
_point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
|
2009-05-30 14:25:59 -04:00
|
|
|
assert (_point);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2009-07-21 11:55:17 -04:00
|
|
|
ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2010-11-16 09:53:16 -05:00
|
|
|
Drag::start_grab (event, _editor->cursors()->fader);
|
2009-05-30 14:25:59 -04:00
|
|
|
|
|
|
|
// start the grab at the center of the control point so
|
|
|
|
// the point doesn't 'jump' to the mouse after the first drag
|
2020-10-15 01:09:22 -04:00
|
|
|
_fixed_grab_x = _point->get_x() + _editor->time_to_pixel_unrounded (timepos_t (_point->line().offset()));
|
2010-08-20 18:43:10 -04:00
|
|
|
_fixed_grab_y = _point->get_y();
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2021-02-14 00:23:10 -05:00
|
|
|
samplepos_t s = _editor->pixel_to_sample (_fixed_grab_x);
|
|
|
|
|
|
|
|
if (_editor->default_time_domain() == Temporal::AudioTime) {
|
|
|
|
setup_snap_delta (timepos_t (s));
|
|
|
|
} else {
|
|
|
|
setup_snap_delta (timepos_t (timepos_t (s).beats()));
|
|
|
|
}
|
2015-05-15 14:15:52 -04:00
|
|
|
|
2010-01-01 17:11:15 -05:00
|
|
|
float const fraction = 1 - (_point->get_y() / _point->line().height());
|
2014-06-26 15:07:29 -04:00
|
|
|
show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
|
2012-05-27 17:02:36 -04:00
|
|
|
|
2015-05-21 12:12:58 -04:00
|
|
|
_pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2015-06-17 16:27:37 -04:00
|
|
|
ControlPointDrag::motion (GdkEvent* event, bool first_motion)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2010-01-12 11:14:49 -05:00
|
|
|
double dx = _drags->current_pointer_x() - last_pointer_x();
|
2014-06-08 11:26:25 -04:00
|
|
|
double dy = current_pointer_y() - last_pointer_y();
|
2015-10-04 13:32:03 -04:00
|
|
|
bool need_snap = true;
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2015-10-03 14:05:56 -04:00
|
|
|
if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
|
2009-05-30 14:25:59 -04:00
|
|
|
dx *= 0.1;
|
|
|
|
dy *= 0.1;
|
2015-10-04 13:32:03 -04:00
|
|
|
need_snap = false;
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2010-08-20 18:43:10 -04:00
|
|
|
/* coordinate in pixels relative to the start of the region (for region-based automation)
|
|
|
|
or track (for track-based automation) */
|
|
|
|
double cx = _fixed_grab_x + _cumulative_x_drag + dx;
|
|
|
|
double cy = _fixed_grab_y + _cumulative_y_drag + dy;
|
2009-05-30 14:25:59 -04:00
|
|
|
|
|
|
|
// calculate zero crossing point. back off by .01 to stay on the
|
|
|
|
// positive side of zero
|
2010-01-01 17:11:15 -05:00
|
|
|
double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2021-06-14 11:22:03 -04:00
|
|
|
if (!_point->can_slide ()) {
|
2010-08-20 18:43:10 -04:00
|
|
|
cx = _fixed_grab_x;
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
if (_y_constrained) {
|
2010-08-20 18:43:10 -04:00
|
|
|
cy = _fixed_grab_y;
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2010-08-20 18:43:10 -04:00
|
|
|
_cumulative_x_drag = cx - _fixed_grab_x;
|
|
|
|
_cumulative_y_drag = cy - _fixed_grab_y;
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2015-10-27 11:46:03 -04:00
|
|
|
cx = max (0.0, cx);
|
|
|
|
cy = max (0.0, cy);
|
|
|
|
cy = min ((double) _point->line().height(), cy);
|
|
|
|
|
2015-06-17 14:42:30 -04:00
|
|
|
// make sure we hit zero when passing through
|
|
|
|
if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
|
|
|
|
cy = zero_gain_y;
|
|
|
|
}
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t cx_pos (timepos_t (_editor->pixel_to_sample (cx)) + snap_delta (event->button.state));
|
2017-01-26 08:41:17 -05:00
|
|
|
|
2021-06-14 11:22:03 -04:00
|
|
|
if (need_snap) {
|
2020-10-15 01:09:22 -04:00
|
|
|
_editor->snap_to_with_modifier (cx_pos, event);
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
cx_pos.shift_earlier (snap_delta (event->button.state));
|
|
|
|
cx_pos = min (cx_pos, _point->line().maximum_time() + _point->line().offset());
|
2010-08-20 18:43:10 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
float const fraction = 1.0 - (cy / _point->line().height());
|
|
|
|
|
2015-06-17 16:27:37 -04:00
|
|
|
if (first_motion) {
|
2015-10-27 11:46:03 -04:00
|
|
|
float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height());
|
2015-06-17 13:48:39 -04:00
|
|
|
_editor->begin_reversible_command (_("automation event move"));
|
2015-10-27 11:46:03 -04:00
|
|
|
_point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
|
2015-06-17 13:48:39 -04:00
|
|
|
}
|
2017-06-21 07:03:00 -04:00
|
|
|
pair<float, float> result;
|
2020-10-15 01:09:22 -04:00
|
|
|
result = _point->line().drag_motion (_editor->time_to_pixel_unrounded (cx_pos), fraction, false, _pushing, _final_index);
|
2017-06-21 07:03:00 -04:00
|
|
|
show_verbose_cursor_text (_point->line().get_verbose_cursor_relative_string (result.first, result.second));
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-06-01 20:39:57 -04:00
|
|
|
ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2009-06-01 20:39:57 -04:00
|
|
|
if (!movement_occurred) {
|
2009-05-30 14:25:59 -04:00
|
|
|
|
|
|
|
/* just a click */
|
2015-05-20 11:30:57 -04:00
|
|
|
if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
|
2009-05-30 14:25:59 -04:00
|
|
|
_editor->reset_point_selection ();
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
2015-06-17 13:48:39 -04:00
|
|
|
_point->line().end_drag (_pushing, _final_index);
|
|
|
|
_editor->commit_reversible_command ();
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-01-07 20:28:15 -05:00
|
|
|
void
|
2010-12-28 14:17:37 -05:00
|
|
|
ControlPointDrag::aborted (bool)
|
2010-01-07 20:28:15 -05:00
|
|
|
{
|
2010-01-07 20:53:50 -05:00
|
|
|
_point->line().reset ();
|
2010-01-07 20:28:15 -05:00
|
|
|
}
|
|
|
|
|
2009-06-26 08:20:11 -04:00
|
|
|
bool
|
|
|
|
ControlPointDrag::active (Editing::MouseMode m)
|
|
|
|
{
|
2014-12-07 19:53:46 -05:00
|
|
|
if (m == Editing::MouseDraw) {
|
|
|
|
/* always active in mouse draw */
|
2009-06-26 08:20:11 -04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
|
|
|
|
return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
|
|
|
|
}
|
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
|
2015-05-15 14:15:52 -04:00
|
|
|
: Drag (e, i)
|
|
|
|
, _line (0)
|
2015-10-23 13:59:57 -04:00
|
|
|
, _fixed_grab_x (0.0)
|
|
|
|
, _fixed_grab_y (0.0)
|
2015-05-15 14:15:52 -04:00
|
|
|
, _cumulative_y_drag (0)
|
2015-06-17 16:27:37 -04:00
|
|
|
, _before (0)
|
|
|
|
, _after (0)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2010-09-09 17:34:45 -04:00
|
|
|
DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
2010-09-09 17:34:45 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
void
|
2009-07-21 11:55:17 -04:00
|
|
|
LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
|
|
|
_line = reinterpret_cast<AutomationLine*> (_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.
|
|
|
|
*/
|
|
|
|
|
2015-10-29 08:37:01 -04:00
|
|
|
double mx = event->button.x;
|
|
|
|
double my = event->button.y;
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2015-10-30 06:14:16 -04:00
|
|
|
_line->grab_item().canvas_to_item (mx, my);
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2017-09-18 12:39:17 -04:00
|
|
|
samplecnt_t const sample_within_region = (samplecnt_t) floor (mx * _editor->samples_per_pixel);
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2017-09-18 12:39:17 -04:00
|
|
|
if (!_line->control_points_adjacent (sample_within_region, _before, _after)) {
|
2009-05-30 14:25:59 -04:00
|
|
|
/* no adjacent points */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-11-16 09:53:16 -05:00
|
|
|
Drag::start_grab (event, _editor->cursors()->fader);
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2017-09-18 12:39:17 -04:00
|
|
|
/* store grab start in item sample */
|
2015-10-29 08:37:01 -04:00
|
|
|
double const bx = _line->nth (_before)->get_x();
|
|
|
|
double const ax = _line->nth (_after)->get_x();
|
2015-10-29 09:01:33 -04:00
|
|
|
double const click_ratio = (ax - mx) / (ax - bx);
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2015-10-29 08:37:01 -04:00
|
|
|
double const cy = ((_line->nth (_before)->get_y() * click_ratio) + (_line->nth (_after)->get_y() * (1 - click_ratio)));
|
|
|
|
|
|
|
|
_fixed_grab_x = mx;
|
2010-08-20 18:43:10 -04:00
|
|
|
_fixed_grab_y = cy;
|
2009-05-30 14:25:59 -04:00
|
|
|
|
|
|
|
double fraction = 1.0 - (cy / _line->height());
|
|
|
|
|
2014-06-26 15:07:29 -04:00
|
|
|
show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2015-06-17 16:27:37 -04:00
|
|
|
LineDrag::motion (GdkEvent* event, bool first_move)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2014-06-08 11:26:25 -04:00
|
|
|
double dy = current_pointer_y() - last_pointer_y();
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2015-10-03 14:05:56 -04:00
|
|
|
if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
|
2009-05-30 14:25:59 -04:00
|
|
|
dy *= 0.1;
|
|
|
|
}
|
|
|
|
|
2010-08-20 18:43:10 -04:00
|
|
|
double cy = _fixed_grab_y + _cumulative_y_drag + dy;
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2010-08-20 18:43:10 -04:00
|
|
|
_cumulative_y_drag = cy - _fixed_grab_y;
|
2009-05-30 14:25:59 -04:00
|
|
|
|
|
|
|
cy = max (0.0, cy);
|
|
|
|
cy = min ((double) _line->height(), cy);
|
|
|
|
|
|
|
|
double const fraction = 1.0 - (cy / _line->height());
|
2012-12-19 10:55:06 -05:00
|
|
|
uint32_t ignored;
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2015-06-17 16:27:37 -04:00
|
|
|
if (first_move) {
|
2015-10-27 14:33:43 -04:00
|
|
|
float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height());
|
|
|
|
|
2015-06-17 16:27:37 -04:00
|
|
|
_editor->begin_reversible_command (_("automation range move"));
|
2015-10-27 14:33:43 -04:00
|
|
|
_line->start_drag_line (_before, _after, initial_fraction);
|
2015-06-17 16:27:37 -04:00
|
|
|
}
|
|
|
|
|
2010-01-11 14:36:29 -05:00
|
|
|
/* we are ignoring x position for this drag, so we can just pass in anything */
|
2017-06-21 07:03:00 -04:00
|
|
|
pair<float, float> result;
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2015-10-27 14:33:43 -04:00
|
|
|
result = _line->drag_motion (0, fraction, true, false, ignored);
|
2017-06-21 07:03:00 -04:00
|
|
|
show_verbose_cursor_text (_line->get_verbose_cursor_relative_string (result.first, result.second));
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2016-02-22 15:01:23 -05:00
|
|
|
LineDrag::finished (GdkEvent* event, bool movement_occurred)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2016-02-22 15:01:23 -05:00
|
|
|
if (movement_occurred) {
|
2014-02-19 13:22:41 -05:00
|
|
|
motion (event, false);
|
|
|
|
_line->end_drag (false, 0);
|
2015-06-17 16:27:37 -04:00
|
|
|
_editor->commit_reversible_command ();
|
2014-02-19 13:22:41 -05:00
|
|
|
} else {
|
|
|
|
/* add a new control point on the line */
|
|
|
|
|
|
|
|
AutomationTimeAxisView* atv;
|
|
|
|
|
|
|
|
if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
|
2020-10-19 14:37:54 -04:00
|
|
|
timepos_t where = grab_time ();
|
2015-08-26 12:20:43 -04:00
|
|
|
|
2015-10-30 06:14:16 -04:00
|
|
|
double cx = 0;
|
|
|
|
double cy = _fixed_grab_y;
|
|
|
|
|
|
|
|
_line->grab_item().item_to_canvas (cx, cy);
|
|
|
|
|
|
|
|
atv->add_automation_event (event, where, cy, false);
|
2015-08-26 12:20:43 -04:00
|
|
|
} else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
|
|
|
|
AudioRegionView* arv;
|
|
|
|
|
|
|
|
if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
|
2015-10-30 06:14:16 -04:00
|
|
|
arv->add_gain_point_event (&arv->get_gain_line()->grab_item(), event, false);
|
2015-08-26 12:20:43 -04:00
|
|
|
}
|
2014-02-19 13:22:41 -05:00
|
|
|
}
|
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2010-01-07 20:28:15 -05:00
|
|
|
void
|
2010-12-28 14:17:37 -05:00
|
|
|
LineDrag::aborted (bool)
|
2010-01-07 20:28:15 -05:00
|
|
|
{
|
2010-01-07 20:53:50 -05:00
|
|
|
_line->reset ();
|
2010-01-07 20:28:15 -05:00
|
|
|
}
|
|
|
|
|
2010-07-06 07:33:27 -04:00
|
|
|
FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
|
|
|
|
: Drag (e, i),
|
|
|
|
_line (0),
|
2015-10-23 13:59:57 -04:00
|
|
|
_arv (0),
|
|
|
|
_region_view_grab_x (0.0),
|
|
|
|
_cumulative_x_drag (0),
|
|
|
|
_before (0.0),
|
|
|
|
_max_x (0)
|
2010-07-06 07:33:27 -04:00
|
|
|
{
|
2010-09-09 17:34:45 -04:00
|
|
|
DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
|
2010-07-06 07:33:27 -04:00
|
|
|
}
|
2010-09-09 17:34:45 -04:00
|
|
|
|
2010-07-06 07:33:27 -04:00
|
|
|
void
|
|
|
|
FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
|
|
|
|
{
|
|
|
|
Drag::start_grab (event);
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2011-02-14 16:49:43 -05:00
|
|
|
_line = reinterpret_cast<Line*> (_item);
|
2010-07-06 07:33:27 -04:00
|
|
|
assert (_line);
|
|
|
|
|
|
|
|
/* need to get x coordinate in terms of parent (AudioRegionView) origin. */
|
|
|
|
|
|
|
|
double cx = event->button.x;
|
|
|
|
double cy = event->button.y;
|
|
|
|
|
2013-04-04 00:32:52 -04:00
|
|
|
_item->parent()->canvas_to_item (cx, cy);
|
2010-07-06 07:33:27 -04:00
|
|
|
|
2017-09-18 12:39:17 -04:00
|
|
|
/* store grab start in parent sample */
|
2010-07-06 07:33:27 -04:00
|
|
|
_region_view_grab_x = cx;
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2011-02-14 16:49:43 -05:00
|
|
|
_before = *(float*) _item->get_data ("position");
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-07-06 07:33:27 -04:00
|
|
|
_arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
|
2011-02-14 16:49:43 -05:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
_max_x = _editor->duration_to_pixels (_arv->get_duration());
|
2010-07-06 07:33:27 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-11-13 00:14:48 -05:00
|
|
|
FeatureLineDrag::motion (GdkEvent*, bool)
|
2010-07-06 07:33:27 -04:00
|
|
|
{
|
|
|
|
double dx = _drags->current_pointer_x() - last_pointer_x();
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-07-06 07:33:27 -04:00
|
|
|
double cx = _region_view_grab_x + _cumulative_x_drag + dx;
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-07-06 07:33:27 -04:00
|
|
|
_cumulative_x_drag += dx;
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-07-06 07:33:27 -04:00
|
|
|
/* Clamp the min and max extent of the drag to keep it within the region view bounds */
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-07-06 07:33:27 -04:00
|
|
|
if (cx > _max_x){
|
|
|
|
cx = _max_x;
|
|
|
|
}
|
|
|
|
else if(cx < 0){
|
|
|
|
cx = 0;
|
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2013-04-12 21:46:44 -04:00
|
|
|
boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
|
2013-04-04 00:32:52 -04:00
|
|
|
assert (bbox);
|
|
|
|
_line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2011-02-14 16:49:43 -05:00
|
|
|
float *pos = new float;
|
|
|
|
*pos = cx;
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2011-02-14 16:49:43 -05:00
|
|
|
_line->set_data ("position", pos);
|
|
|
|
|
|
|
|
_before = cx;
|
2010-07-06 07:33:27 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-11-13 00:14:48 -05:00
|
|
|
FeatureLineDrag::finished (GdkEvent*, bool)
|
2010-07-06 07:33:27 -04:00
|
|
|
{
|
|
|
|
_arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
|
2011-02-14 16:49:43 -05:00
|
|
|
_arv->update_transient(_before, _before);
|
2010-07-06 07:33:27 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-12-28 14:17:37 -05:00
|
|
|
FeatureLineDrag::aborted (bool)
|
2010-07-06 07:33:27 -04:00
|
|
|
{
|
|
|
|
//_line->reset ();
|
|
|
|
}
|
|
|
|
|
2010-09-09 17:34:45 -04:00
|
|
|
RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
|
|
|
|
: Drag (e, i)
|
2012-01-19 21:54:23 -05:00
|
|
|
, _vertical_only (false)
|
2010-09-09 17:34:45 -04:00
|
|
|
{
|
|
|
|
DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
|
|
|
|
}
|
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
void
|
|
|
|
RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
|
|
|
|
{
|
|
|
|
Drag::start_grab (event);
|
2020-10-15 01:09:22 -04:00
|
|
|
show_verbose_cursor_time (adjusted_current_time (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-12-25 16:06:52 -05:00
|
|
|
RubberbandSelectDrag::motion (GdkEvent* event, bool)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t start;
|
|
|
|
timepos_t end;
|
2009-05-30 14:25:59 -04:00
|
|
|
double y1;
|
|
|
|
double y2;
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t const pf = adjusted_current_time (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
|
|
|
|
timepos_t grab (grab_time ());
|
2009-12-21 20:12:41 -05:00
|
|
|
|
2015-01-02 09:44:54 -05:00
|
|
|
if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
|
2018-07-26 15:23:59 -04:00
|
|
|
_editor->snap_to_with_modifier (grab, event, RoundNearest, SnapToGrid_Scaled);
|
2015-01-19 12:49:44 -05:00
|
|
|
} else {
|
2020-10-15 01:09:22 -04:00
|
|
|
grab = raw_grab_time ();
|
2009-12-21 20:12:41 -05:00
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
|
|
|
|
/* base start and end on initial click position */
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
if (pf < grab) {
|
2009-12-21 20:12:41 -05:00
|
|
|
start = pf;
|
2020-10-15 01:09:22 -04:00
|
|
|
end = grab;
|
2009-05-30 14:25:59 -04:00
|
|
|
} else {
|
2009-12-21 20:12:41 -05:00
|
|
|
end = pf;
|
2020-10-15 01:09:22 -04:00
|
|
|
start = grab;
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2014-06-08 11:26:25 -04:00
|
|
|
if (current_pointer_y() < grab_y()) {
|
|
|
|
y1 = current_pointer_y();
|
2009-12-21 20:12:41 -05:00
|
|
|
y2 = grab_y();
|
2009-05-30 14:25:59 -04:00
|
|
|
} else {
|
2014-06-08 11:26:25 -04:00
|
|
|
y2 = current_pointer_y();
|
2009-12-21 20:12:41 -05:00
|
|
|
y1 = grab_y();
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (start != end || y1 != y2) {
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
double x1 = _editor->time_to_pixel (start);
|
|
|
|
double x2 = _editor->time_to_pixel (end);
|
2014-01-02 09:53:53 -05:00
|
|
|
const double min_dimension = 2.0;
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2012-01-19 21:54:23 -05:00
|
|
|
if (_vertical_only) {
|
|
|
|
/* fixed 10 pixel width */
|
2014-06-06 08:32:35 -04:00
|
|
|
x2 = x1 + 10;
|
2012-01-19 21:54:23 -05:00
|
|
|
} else {
|
2014-01-02 09:44:15 -05:00
|
|
|
if (x2 < x1) {
|
2014-01-02 09:53:53 -05:00
|
|
|
x2 = min (x1 - min_dimension, x2);
|
2014-01-02 09:44:15 -05:00
|
|
|
} else {
|
2014-01-02 09:53:53 -05:00
|
|
|
x2 = max (x1 + min_dimension, x2);
|
2014-01-02 09:44:15 -05:00
|
|
|
}
|
2015-03-24 16:59:57 -04:00
|
|
|
}
|
2012-01-19 21:54:23 -05:00
|
|
|
|
2014-01-02 09:44:15 -05:00
|
|
|
if (y2 < y1) {
|
2014-01-02 09:53:53 -05:00
|
|
|
y2 = min (y1 - min_dimension, y2);
|
2014-01-02 09:44:15 -05:00
|
|
|
} else {
|
2014-01-02 09:53:53 -05:00
|
|
|
y2 = max (y1 + min_dimension, y2);
|
2014-01-02 09:44:15 -05:00
|
|
|
}
|
|
|
|
|
2014-06-06 08:32:35 -04:00
|
|
|
/* translate rect into item space and set */
|
|
|
|
|
2015-03-24 16:59:57 -04:00
|
|
|
ArdourCanvas::Rect r (x1, y1, x2, y2);
|
2014-06-06 08:32:35 -04:00
|
|
|
|
2014-06-08 11:26:25 -04:00
|
|
|
/* this drag is a _trackview_only == true drag, so the y1 and
|
|
|
|
* y2 (computed using current_pointer_y() and grab_y()) will be
|
|
|
|
* relative to the top of the trackview group). The
|
|
|
|
* rubberband rect has the same parent/scroll offset as the
|
|
|
|
* the trackview group, so we can use the "r" rect directly
|
|
|
|
* to set the shape of the rubberband.
|
|
|
|
*/
|
|
|
|
|
|
|
|
_editor->rubberband_rect->set (r);
|
2009-05-30 14:25:59 -04:00
|
|
|
_editor->rubberband_rect->show();
|
|
|
|
_editor->rubberband_rect->raise_to_top();
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2011-05-02 09:38:16 -04:00
|
|
|
show_verbose_cursor_time (pf);
|
2011-11-16 15:11:33 -05:00
|
|
|
|
|
|
|
do_select_things (event, true);
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2011-11-16 15:11:33 -05:00
|
|
|
RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t x1;
|
|
|
|
timepos_t x2;
|
|
|
|
timepos_t grab = grab_time ();
|
|
|
|
timepos_t lpf = last_pointer_time ();
|
2015-01-19 12:49:44 -05:00
|
|
|
|
2015-01-02 09:44:54 -05:00
|
|
|
if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
|
2020-10-15 01:09:22 -04:00
|
|
|
grab = raw_grab_time ();
|
2021-03-19 00:42:54 -04:00
|
|
|
|
|
|
|
timepos_t pos (_editor->pixel_to_sample_from_event (last_pointer_x()));
|
|
|
|
|
|
|
|
if (_editor->default_time_domain() == Temporal::AudioTime) {
|
|
|
|
lpf = pos;
|
|
|
|
} else {
|
|
|
|
lpf = timepos_t (pos.beats());
|
|
|
|
}
|
2015-01-19 12:49:44 -05:00
|
|
|
}
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2015-01-19 12:49:44 -05:00
|
|
|
if (grab < lpf) {
|
|
|
|
x1 = grab;
|
|
|
|
x2 = lpf;
|
2011-11-16 15:11:33 -05:00
|
|
|
} else {
|
2015-01-19 12:49:44 -05:00
|
|
|
x2 = grab;
|
|
|
|
x1 = lpf;
|
2011-11-16 15:11:33 -05:00
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2011-11-16 15:11:33 -05:00
|
|
|
double y1;
|
|
|
|
double y2;
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2014-06-08 11:26:25 -04:00
|
|
|
if (current_pointer_y() < grab_y()) {
|
|
|
|
y1 = current_pointer_y();
|
2011-11-16 15:11:33 -05:00
|
|
|
y2 = grab_y();
|
|
|
|
} else {
|
2014-06-08 11:26:25 -04:00
|
|
|
y2 = current_pointer_y();
|
2011-11-16 15:11:33 -05:00
|
|
|
y1 = grab_y();
|
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2011-11-16 15:11:33 -05:00
|
|
|
select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
|
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2011-11-16 15:11:33 -05:00
|
|
|
void
|
|
|
|
RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
|
|
|
|
{
|
|
|
|
if (movement_occurred) {
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2011-11-16 15:11:33 -05:00
|
|
|
motion (event, false);
|
|
|
|
do_select_things (event, false);
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2011-11-16 15:11:33 -05:00
|
|
|
} else {
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2012-01-25 14:39:20 -05:00
|
|
|
/* just a click */
|
|
|
|
|
|
|
|
bool do_deselect = true;
|
|
|
|
MidiTimeAxisView* mtv;
|
2021-02-10 00:44:35 -05:00
|
|
|
AutomationTimeAxisView* atv;
|
2012-01-25 14:39:20 -05:00
|
|
|
|
|
|
|
if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
|
|
|
|
/* MIDI track */
|
2014-11-15 02:03:40 -05:00
|
|
|
if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
|
2012-01-25 14:39:20 -05:00
|
|
|
/* nothing selected */
|
2017-01-26 08:41:17 -05:00
|
|
|
add_midi_region (mtv, true);
|
2012-01-25 14:39:20 -05:00
|
|
|
do_deselect = false;
|
|
|
|
}
|
2021-02-10 00:44:35 -05:00
|
|
|
} else if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
|
2021-02-12 12:49:26 -05:00
|
|
|
timepos_t where = grab_time ();
|
2021-02-10 00:44:35 -05:00
|
|
|
atv->add_automation_event (event, where, event->button.y, false);
|
|
|
|
do_deselect = false;
|
2015-03-24 16:59:57 -04:00
|
|
|
}
|
2012-01-25 14:39:20 -05:00
|
|
|
|
2012-12-05 15:29:54 -05:00
|
|
|
/* do not deselect if Primary or Tertiary (toggle-select or
|
|
|
|
* extend-select are pressed.
|
|
|
|
*/
|
|
|
|
|
2015-03-24 16:59:57 -04:00
|
|
|
if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
|
|
|
|
!Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
|
2012-12-05 15:29:54 -05:00
|
|
|
do_deselect) {
|
2012-01-25 14:39:20 -05:00
|
|
|
deselect_things ();
|
|
|
|
}
|
2011-02-14 16:49:43 -05:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
_editor->rubberband_rect->hide();
|
|
|
|
}
|
|
|
|
|
2010-01-07 20:28:15 -05:00
|
|
|
void
|
2010-12-28 14:17:37 -05:00
|
|
|
RubberbandSelectDrag::aborted (bool)
|
2010-01-07 20:28:15 -05:00
|
|
|
{
|
2010-01-07 20:53:50 -05:00
|
|
|
_editor->rubberband_rect->hide ();
|
2010-01-07 20:28:15 -05:00
|
|
|
}
|
|
|
|
|
2010-09-09 17:34:45 -04:00
|
|
|
TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
|
|
|
|
: RegionDrag (e, i, p, v)
|
|
|
|
{
|
|
|
|
DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
|
2019-11-08 23:03:38 -05:00
|
|
|
_preview_video = false;
|
2010-09-09 17:34:45 -04:00
|
|
|
}
|
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
void
|
2010-09-06 08:34:11 -04:00
|
|
|
TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2010-09-06 08:34:11 -04:00
|
|
|
Drag::start_grab (event, cursor);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2015-05-15 14:15:52 -04:00
|
|
|
_editor->get_selection().add (_primary);
|
2020-11-30 12:59:17 -05:00
|
|
|
timepos_t where (_primary->region()->position());
|
|
|
|
setup_snap_delta (_primary->region()->position());
|
2015-05-15 14:15:52 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
show_verbose_cursor_duration (where, adjusted_current_time (event), 0);
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-06-01 20:39:57 -04:00
|
|
|
TimeFXDrag::motion (GdkEvent* event, bool)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
|
|
|
RegionView* rv = _primary;
|
2012-05-27 16:07:13 -04:00
|
|
|
StreamView* cv = rv->get_time_axis_view().view ();
|
|
|
|
pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
|
|
|
|
int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
|
|
|
|
int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
|
2021-02-14 00:23:10 -05:00
|
|
|
timepos_t pf (_editor->canvas_event_time (event) + snap_delta (event->button.state));
|
2017-01-26 08:41:17 -05:00
|
|
|
|
2015-05-15 14:15:52 -04:00
|
|
|
_editor->snap_to_with_modifier (pf, event);
|
2020-10-15 01:09:22 -04:00
|
|
|
pf.shift_earlier (snap_delta (event->button.state));
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2020-11-30 12:59:17 -05:00
|
|
|
if (pf > rv->region()->position()) {
|
|
|
|
rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2020-11-30 12:59:17 -05:00
|
|
|
show_verbose_cursor_duration (_primary->region()->position(), pf);
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2016-01-20 23:00:54 -05:00
|
|
|
TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2016-02-22 10:29:17 -05:00
|
|
|
/* this may have been a single click, no drag. We still want the dialog
|
|
|
|
to show up in that case, so that the user can manually edit the
|
|
|
|
parameters for the timestretch.
|
|
|
|
*/
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2020-12-27 19:47:08 -05:00
|
|
|
ratio_t ratio (1, 1);
|
2016-01-20 23:00:54 -05:00
|
|
|
|
2016-02-22 10:29:17 -05:00
|
|
|
if (movement_occurred) {
|
2016-01-20 23:00:54 -05:00
|
|
|
|
2016-02-22 10:29:17 -05:00
|
|
|
motion (event, false);
|
2016-01-20 23:00:54 -05:00
|
|
|
|
2016-02-22 10:29:17 -05:00
|
|
|
_primary->get_time_axis_view().hide_timestretch ();
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t adjusted_pos = adjusted_current_time (event);
|
2016-02-22 10:29:17 -05:00
|
|
|
|
2020-11-30 12:59:17 -05:00
|
|
|
if (adjusted_pos < _primary->region()->position()) {
|
2016-02-22 10:29:17 -05:00
|
|
|
/* backwards drag of the left edge - not usable */
|
|
|
|
return;
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2020-11-30 12:59:17 -05:00
|
|
|
timecnt_t newlen = _primary->region()->position().distance (adjusted_pos);
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2020-12-27 19:47:08 -05:00
|
|
|
if (_primary->region()->length().time_domain() == Temporal::BeatTime) {
|
|
|
|
ratio = ratio_t (newlen.ticks(), _primary->region()->length().ticks());
|
|
|
|
} else {
|
|
|
|
ratio = ratio_t (newlen.samples(), _primary->region()->length().samples());
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
#ifndef USE_RUBBERBAND
|
2016-02-22 10:29:17 -05:00
|
|
|
// Soundtouch uses fraction / 100 instead of normal (/ 1)
|
2020-12-27 19:47:08 -05:00
|
|
|
#warning NUTEMPO timefx request now uses a rational type so this needs revisiting
|
2016-02-22 10:29:17 -05:00
|
|
|
if (_primary->region()->data_type() == DataType::AUDIO) {
|
2020-12-27 19:47:08 -05:00
|
|
|
ratio = ((newlen - _primary->region()->length()) / newlen) * 100;
|
2016-02-22 10:29:17 -05:00
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
#endif
|
2016-02-22 10:29:17 -05:00
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2012-01-27 11:29:01 -05:00
|
|
|
if (!_editor->get_selection().regions.empty()) {
|
|
|
|
/* primary will already be included in the selection, and edit
|
|
|
|
group shared editing will propagate selection across
|
|
|
|
equivalent regions, so just use the current region
|
|
|
|
selection.
|
|
|
|
*/
|
2012-01-24 12:04:15 -05:00
|
|
|
|
2020-12-27 19:47:08 -05:00
|
|
|
if (_editor->time_stretch (_editor->get_selection().regions, ratio) == -1) {
|
2012-01-27 11:29:01 -05:00
|
|
|
error << _("An error occurred while executing time stretch operation") << endmsg;
|
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-01-07 20:28:15 -05:00
|
|
|
void
|
2010-12-28 14:17:37 -05:00
|
|
|
TimeFXDrag::aborted (bool)
|
2010-01-07 20:28:15 -05:00
|
|
|
{
|
2010-01-07 20:53:50 -05:00
|
|
|
_primary->get_time_axis_view().hide_timestretch ();
|
2010-01-07 20:28:15 -05:00
|
|
|
}
|
|
|
|
|
2010-09-09 17:34:45 -04:00
|
|
|
ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
|
|
|
|
: Drag (e, i)
|
|
|
|
{
|
|
|
|
DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
|
|
|
|
}
|
2010-01-07 20:28:15 -05:00
|
|
|
|
2009-06-10 09:58:22 -04:00
|
|
|
void
|
|
|
|
ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
|
|
|
|
{
|
|
|
|
Drag::start_grab (event);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-07-21 11:55:17 -04:00
|
|
|
ScrubDrag::motion (GdkEvent* /*event*/, bool)
|
2009-06-10 09:58:22 -04:00
|
|
|
{
|
2020-10-15 01:09:22 -04:00
|
|
|
_editor->scrub (adjusted_current_time (0, false).samples(), _drags->current_pointer_x ());
|
2009-06-10 09:58:22 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-07-21 11:55:17 -04:00
|
|
|
ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
|
2009-06-10 09:58:22 -04:00
|
|
|
{
|
2009-12-17 13:24:23 -05:00
|
|
|
if (movement_occurred && _editor->session()) {
|
2009-06-10 09:58:22 -04:00
|
|
|
/* make sure we stop */
|
2021-04-16 01:20:59 -04:00
|
|
|
_editor->session()->request_stop ();
|
2009-10-14 12:10:01 -04:00
|
|
|
}
|
2009-06-10 09:58:22 -04:00
|
|
|
}
|
|
|
|
|
2010-01-07 20:28:15 -05:00
|
|
|
void
|
2010-12-28 14:17:37 -05:00
|
|
|
ScrubDrag::aborted (bool)
|
2010-01-07 20:28:15 -05:00
|
|
|
{
|
|
|
|
/* XXX: TODO */
|
|
|
|
}
|
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
|
2009-11-08 11:28:21 -05:00
|
|
|
: Drag (e, i)
|
|
|
|
, _operation (o)
|
2012-12-13 14:39:36 -05:00
|
|
|
, _add (false)
|
2012-01-30 17:53:22 -05:00
|
|
|
, _time_selection_at_start (!_editor->get_selection().time.empty())
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2010-09-09 17:34:45 -04:00
|
|
|
DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2012-12-13 14:39:36 -05:00
|
|
|
if (_time_selection_at_start) {
|
2020-10-15 01:09:22 -04:00
|
|
|
start_at_start = _editor->get_selection().time.start_time();
|
|
|
|
end_at_start = _editor->get_selection().time.end_time();
|
2012-12-13 14:39:36 -05:00
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
|
|
|
|
{
|
2009-12-17 13:24:23 -05:00
|
|
|
if (_editor->session() == 0) {
|
2009-05-30 14:25:59 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-01-24 12:26:58 -05:00
|
|
|
Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
|
2009-05-30 14:25:59 -04:00
|
|
|
|
|
|
|
switch (_operation) {
|
|
|
|
case CreateSelection:
|
2012-12-13 14:39:36 -05:00
|
|
|
if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
|
|
|
|
_add = true;
|
2009-05-30 14:25:59 -04:00
|
|
|
} else {
|
2012-12-13 14:39:36 -05:00
|
|
|
_add = false;
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
2010-11-16 09:53:16 -05:00
|
|
|
cursor = _editor->cursors()->selector;
|
2009-05-30 14:25:59 -04:00
|
|
|
Drag::start_grab (event, cursor);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SelectionStartTrim:
|
|
|
|
if (_editor->clicked_axisview) {
|
|
|
|
_editor->clicked_axisview->order_selection_trims (_item, true);
|
2009-10-14 12:10:01 -04:00
|
|
|
}
|
2010-11-16 09:53:16 -05:00
|
|
|
Drag::start_grab (event, _editor->cursors()->left_side_trim);
|
2009-05-30 14:25:59 -04:00
|
|
|
break;
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
case SelectionEndTrim:
|
|
|
|
if (_editor->clicked_axisview) {
|
|
|
|
_editor->clicked_axisview->order_selection_trims (_item, false);
|
|
|
|
}
|
2010-11-16 09:53:16 -05:00
|
|
|
Drag::start_grab (event, _editor->cursors()->right_side_trim);
|
2009-05-30 14:25:59 -04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SelectionMove:
|
|
|
|
Drag::start_grab (event, cursor);
|
|
|
|
break;
|
2012-12-13 14:39:36 -05:00
|
|
|
|
|
|
|
case SelectionExtend:
|
|
|
|
Drag::start_grab (event, cursor);
|
|
|
|
break;
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (_operation == SelectionMove) {
|
2020-10-15 01:09:22 -04:00
|
|
|
show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start());
|
2009-05-30 14:25:59 -04:00
|
|
|
} else {
|
2020-10-15 01:09:22 -04:00
|
|
|
show_verbose_cursor_time (adjusted_current_time (event));
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-19 22:42:59 -05:00
|
|
|
void
|
2020-10-15 01:09:22 -04:00
|
|
|
SelectionDrag::setup_pointer_offset ()
|
2010-12-19 22:42:59 -05:00
|
|
|
{
|
|
|
|
switch (_operation) {
|
|
|
|
case CreateSelection:
|
2021-01-15 17:58:00 -05:00
|
|
|
_pointer_offset = timecnt_t (_editor->default_time_domain());
|
2010-12-19 22:42:59 -05:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SelectionStartTrim:
|
|
|
|
case SelectionMove:
|
2020-10-15 01:09:22 -04:00
|
|
|
_pointer_offset = _editor->selection->time[_editor->clicked_selection].start().distance (raw_grab_time());
|
2010-12-19 22:42:59 -05:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SelectionEndTrim:
|
2020-10-15 01:09:22 -04:00
|
|
|
_pointer_offset = _editor->selection->time[_editor->clicked_selection].end().distance (raw_grab_time());
|
2010-12-19 22:42:59 -05:00
|
|
|
break;
|
2012-12-13 14:39:36 -05:00
|
|
|
|
|
|
|
case SelectionExtend:
|
|
|
|
break;
|
2010-12-19 22:42:59 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
void
|
2009-06-01 20:39:57 -04:00
|
|
|
SelectionDrag::motion (GdkEvent* event, bool first_move)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t start;
|
|
|
|
timepos_t end;
|
|
|
|
timecnt_t length;
|
|
|
|
timecnt_t distance;
|
|
|
|
timepos_t start_mf;
|
|
|
|
timepos_t const pending_position = adjusted_current_time (event);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
if (_operation != CreateSelection && pending_position == last_pointer_time()) {
|
2009-05-30 14:25:59 -04:00
|
|
|
return;
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2017-01-25 15:46:24 -05:00
|
|
|
if (first_move) {
|
2021-06-02 20:23:22 -04:00
|
|
|
if (Config->get_edit_mode() == RippleAll) {
|
|
|
|
_editor->selection->set (_editor->get_track_views());
|
|
|
|
}
|
2017-01-25 15:46:24 -05:00
|
|
|
_track_selection_at_start = _editor->selection->tracks;
|
|
|
|
}
|
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
switch (_operation) {
|
|
|
|
case CreateSelection:
|
2009-12-21 20:12:41 -05:00
|
|
|
{
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t grab (grab_time());
|
2009-06-01 20:39:57 -04:00
|
|
|
if (first_move) {
|
2020-10-15 01:09:22 -04:00
|
|
|
grab = adjusted_current_time (event, false);
|
|
|
|
if (grab < pending_position) {
|
2014-11-16 02:01:02 -05:00
|
|
|
_editor->snap_to (grab, RoundDownMaybe);
|
2012-11-18 10:35:05 -05:00
|
|
|
} else {
|
2014-11-16 02:01:02 -05:00
|
|
|
_editor->snap_to (grab, RoundUpMaybe);
|
2012-11-18 10:35:05 -05:00
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
if (pending_position < grab) {
|
2009-05-30 14:25:59 -04:00
|
|
|
start = pending_position;
|
2020-10-15 01:09:22 -04:00
|
|
|
end = grab;
|
2009-05-30 14:25:59 -04:00
|
|
|
} else {
|
|
|
|
end = pending_position;
|
2020-10-15 01:09:22 -04:00
|
|
|
start = grab;
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
/* first drag: Either add to the selection
|
2009-12-13 16:27:19 -05:00
|
|
|
or create a new selection
|
2009-05-30 14:25:59 -04:00
|
|
|
*/
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-06-01 20:39:57 -04:00
|
|
|
if (first_move) {
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2012-12-13 14:39:36 -05:00
|
|
|
if (_add) {
|
2014-02-25 21:55:25 -05:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
/* adding to the selection */
|
2010-11-25 15:37:39 -05:00
|
|
|
_editor->set_selected_track_as_side_effect (Selection::Add);
|
2009-05-30 14:25:59 -04:00
|
|
|
_editor->clicked_selection = _editor->selection->add (start, end);
|
2012-12-13 14:39:36 -05:00
|
|
|
_add = false;
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
} else {
|
2014-02-25 21:55:25 -05:00
|
|
|
|
2009-12-13 16:27:19 -05:00
|
|
|
/* new selection */
|
|
|
|
|
2010-05-20 18:38:12 -04:00
|
|
|
if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
|
2010-11-25 15:37:39 -05:00
|
|
|
_editor->set_selected_track_as_side_effect (Selection::Set);
|
2009-12-13 16:27:19 -05:00
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2009-12-13 16:27:19 -05:00
|
|
|
_editor->clicked_selection = _editor->selection->set (start, end);
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
}
|
2015-03-24 16:59:57 -04:00
|
|
|
|
|
|
|
//if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
|
|
|
|
// because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
|
2014-08-05 16:42:06 -04:00
|
|
|
AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
|
|
|
|
if (atest) {
|
|
|
|
_editor->selection->add (atest);
|
2015-03-24 16:59:57 -04:00
|
|
|
break;
|
2014-08-05 16:42:06 -04:00
|
|
|
}
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2014-02-25 21:55:25 -05:00
|
|
|
/* select all tracks within the rectangle that we've marked out so far */
|
2014-07-09 15:45:20 -04:00
|
|
|
TrackViewList new_selection;
|
2014-02-25 21:55:25 -05:00
|
|
|
TrackViewList& all_tracks (_editor->track_views);
|
2010-01-01 17:11:15 -05:00
|
|
|
|
2014-06-08 11:26:25 -04:00
|
|
|
ArdourCanvas::Coord const top = grab_y();
|
|
|
|
ArdourCanvas::Coord const bottom = current_pointer_y();
|
2014-06-05 17:06:36 -04:00
|
|
|
|
2021-06-02 20:23:22 -04:00
|
|
|
if ((Config->get_edit_mode() != RippleAll) && top >= 0 && bottom >= 0) {
|
2014-06-05 17:06:36 -04:00
|
|
|
|
2014-07-09 15:45:20 -04:00
|
|
|
//first, find the tracks that are covered in the y range selection
|
2014-06-05 17:06:36 -04:00
|
|
|
for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
|
|
|
|
if ((*i)->covered_by_y_range (top, bottom)) {
|
2014-07-09 15:45:20 -04:00
|
|
|
new_selection.push_back (*i);
|
2014-02-25 21:55:25 -05:00
|
|
|
}
|
2010-01-01 17:11:15 -05:00
|
|
|
}
|
|
|
|
|
2017-01-10 10:18:00 -05:00
|
|
|
//now compare our list with the current selection, and add as necessary
|
2014-07-09 15:45:20 -04:00
|
|
|
//( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
|
2014-07-09 15:34:00 -04:00
|
|
|
TrackViewList tracks_to_add;
|
|
|
|
TrackViewList tracks_to_remove;
|
2017-02-08 11:40:27 -05:00
|
|
|
vector<RouteGroup*> selected_route_groups;
|
2017-01-25 15:46:24 -05:00
|
|
|
|
|
|
|
if (!first_move) {
|
|
|
|
for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i) {
|
|
|
|
if (!new_selection.contains (*i) && !_track_selection_at_start.contains (*i)) {
|
|
|
|
tracks_to_remove.push_back (*i);
|
2017-02-08 11:40:27 -05:00
|
|
|
} else {
|
|
|
|
RouteGroup* rg = (*i)->route_group();
|
|
|
|
if (rg && rg->is_active() && rg->is_select()) {
|
|
|
|
selected_route_groups.push_back (rg);
|
|
|
|
}
|
2017-01-25 15:46:24 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
|
|
|
|
if (!_editor->selection->tracks.contains (*i)) {
|
|
|
|
tracks_to_add.push_back (*i);
|
2017-02-08 11:40:27 -05:00
|
|
|
RouteGroup* rg = (*i)->route_group();
|
|
|
|
|
|
|
|
if (rg && rg->is_active() && rg->is_select()) {
|
|
|
|
selected_route_groups.push_back (rg);
|
|
|
|
}
|
2017-01-25 15:46:24 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_editor->selection->add (tracks_to_add);
|
|
|
|
|
|
|
|
if (!tracks_to_remove.empty()) {
|
2017-02-08 11:40:27 -05:00
|
|
|
|
|
|
|
/* check all these to-be-removed tracks against the
|
|
|
|
* possibility that they are selected by being
|
|
|
|
* in the same group as an approved track.
|
|
|
|
*/
|
|
|
|
|
|
|
|
for (TrackViewList::iterator i = tracks_to_remove.begin(); i != tracks_to_remove.end(); ) {
|
|
|
|
RouteGroup* rg = (*i)->route_group();
|
|
|
|
|
|
|
|
if (rg && find (selected_route_groups.begin(), selected_route_groups.end(), rg) != selected_route_groups.end()) {
|
|
|
|
i = tracks_to_remove.erase (i);
|
|
|
|
} else {
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* remove whatever is left */
|
|
|
|
|
2017-01-25 15:46:24 -05:00
|
|
|
_editor->selection->remove (tracks_to_remove);
|
|
|
|
}
|
2014-02-25 21:55:25 -05:00
|
|
|
}
|
2009-12-21 20:12:41 -05:00
|
|
|
}
|
2010-01-01 17:11:15 -05:00
|
|
|
break;
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
case SelectionStartTrim:
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
end = _editor->selection->time[_editor->clicked_selection].end();
|
2009-05-30 14:25:59 -04:00
|
|
|
|
|
|
|
if (pending_position > end) {
|
|
|
|
start = end;
|
|
|
|
} else {
|
|
|
|
start = pending_position;
|
|
|
|
}
|
|
|
|
break;
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
case SelectionEndTrim:
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
start = _editor->selection->time[_editor->clicked_selection].start();
|
2009-05-30 14:25:59 -04:00
|
|
|
|
|
|
|
if (pending_position < start) {
|
|
|
|
end = start;
|
|
|
|
} else {
|
|
|
|
end = pending_position;
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
break;
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
case SelectionMove:
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
start = _editor->selection->time[_editor->clicked_selection].start();
|
|
|
|
end = _editor->selection->time[_editor->clicked_selection].end();
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
length = start.distance (end);;
|
|
|
|
distance = start.distance (pending_position);
|
2009-05-30 14:25:59 -04:00
|
|
|
start = pending_position;
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
start_mf = start;
|
2017-01-26 08:41:17 -05:00
|
|
|
_editor->snap_to (start_mf);
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
end = start_mf + length;
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
break;
|
2012-12-13 14:39:36 -05:00
|
|
|
|
|
|
|
case SelectionExtend:
|
|
|
|
break;
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
if (start != end) {
|
2012-12-13 14:39:36 -05:00
|
|
|
switch (_operation) {
|
2015-03-24 16:59:57 -04:00
|
|
|
case SelectionMove:
|
2012-12-13 14:39:36 -05:00
|
|
|
if (_time_selection_at_start) {
|
|
|
|
_editor->selection->move_time (distance);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
_editor->selection->replace (_editor->clicked_selection, start, end);
|
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (_operation == SelectionMove) {
|
2011-05-02 09:38:16 -04:00
|
|
|
show_verbose_cursor_time(start);
|
2009-05-30 14:25:59 -04:00
|
|
|
} else {
|
2011-05-02 09:38:16 -04:00
|
|
|
show_verbose_cursor_time(pending_position);
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-06-01 20:39:57 -04:00
|
|
|
SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2009-12-17 13:24:23 -05:00
|
|
|
Session* s = _editor->session();
|
2009-11-08 11:28:21 -05:00
|
|
|
|
2015-03-25 08:31:23 -04:00
|
|
|
_editor->begin_reversible_selection_op (X_("Change Time Selection"));
|
2009-06-01 20:39:57 -04:00
|
|
|
if (movement_occurred) {
|
|
|
|
motion (event, false);
|
2009-05-30 14:25:59 -04:00
|
|
|
/* XXX this is not object-oriented programming at all. ick */
|
|
|
|
if (_editor->selection->time.consolidate()) {
|
|
|
|
_editor->selection->TimeChanged ();
|
|
|
|
}
|
2009-11-08 11:28:21 -05:00
|
|
|
|
|
|
|
/* XXX what if its a music time selection? */
|
2012-01-30 17:53:22 -05:00
|
|
|
if (s) {
|
2018-02-11 10:39:45 -05:00
|
|
|
|
|
|
|
//if Follow Edits is on, maybe try to follow the range selection ... also consider range-audition mode
|
|
|
|
if ( !s->config.get_external_sync() && s->transport_rolling() ) {
|
|
|
|
if ( s->solo_selection_active() ) {
|
|
|
|
_editor->play_solo_selection(true); //play the newly selected range, and move solos to match
|
|
|
|
} else if ( UIConfiguration::instance().get_follow_edits() && s->get_play_range() ) { //already rolling a selected range
|
|
|
|
s->request_play_range (&_editor->selection->time, true); //play the newly selected range
|
2012-01-30 17:53:22 -05:00
|
|
|
}
|
2018-02-11 10:39:45 -05:00
|
|
|
} else if ( !s->transport_rolling() && UIConfiguration::instance().get_follow_edits() ) {
|
2020-10-15 01:09:22 -04:00
|
|
|
s->request_locate (_editor->get_selection().time.start_sample());
|
2012-01-30 17:53:22 -05:00
|
|
|
}
|
2009-11-08 11:28:21 -05:00
|
|
|
|
2015-01-17 12:40:46 -05:00
|
|
|
if (_editor->get_selection().time.length() != 0) {
|
2020-10-15 01:09:22 -04:00
|
|
|
s->set_range_selection (_editor->get_selection().time.start_time(), _editor->get_selection().time.end_time ());
|
2015-01-17 12:40:46 -05:00
|
|
|
} else {
|
|
|
|
s->clear_range_selection ();
|
|
|
|
}
|
|
|
|
}
|
2015-10-05 10:17:49 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
} else {
|
2012-01-30 17:53:22 -05:00
|
|
|
/* just a click, no pointer movement.
|
|
|
|
*/
|
2012-12-13 14:39:36 -05:00
|
|
|
|
2016-12-12 21:02:57 -05:00
|
|
|
if (was_double_click()) {
|
|
|
|
if (UIConfiguration::instance().get_use_double_click_to_zoom_to_selection()) {
|
2016-12-13 02:10:30 -05:00
|
|
|
_editor->temporal_zoom_selection (Both);
|
2016-12-12 21:02:57 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-13 14:39:36 -05:00
|
|
|
if (_operation == SelectionExtend) {
|
|
|
|
if (_time_selection_at_start) {
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t pos = adjusted_current_time (event, false);
|
|
|
|
timepos_t start = min (pos, start_at_start);
|
|
|
|
timepos_t end = max (pos, end_at_start);
|
2012-12-13 14:39:36 -05:00
|
|
|
_editor->selection->set (start, end);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
|
|
|
|
if (_editor->clicked_selection) {
|
|
|
|
_editor->selection->remove (_editor->clicked_selection);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!_editor->clicked_selection) {
|
|
|
|
_editor->selection->clear_time();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2010-05-20 18:38:12 -04:00
|
|
|
if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
|
2009-12-13 16:27:19 -05:00
|
|
|
_editor->selection->set (_editor->clicked_axisview);
|
2009-10-14 12:10:01 -04:00
|
|
|
}
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2009-11-08 11:28:21 -05:00
|
|
|
if (s && s->get_play_range () && s->transport_rolling()) {
|
|
|
|
s->request_stop (false, false);
|
|
|
|
}
|
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
_editor->stop_canvas_autoscroll ();
|
2012-12-14 11:46:01 -05:00
|
|
|
_editor->clicked_selection = 0;
|
2014-12-22 08:30:23 -05:00
|
|
|
_editor->commit_reversible_selection_op ();
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2010-01-07 20:28:15 -05:00
|
|
|
void
|
2010-12-28 14:17:37 -05:00
|
|
|
SelectionDrag::aborted (bool)
|
2010-01-07 20:28:15 -05:00
|
|
|
{
|
|
|
|
/* XXX: TODO */
|
|
|
|
}
|
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
|
2014-06-08 11:26:25 -04:00
|
|
|
: Drag (e, i, false),
|
2009-06-08 15:28:51 -04:00
|
|
|
_operation (o),
|
|
|
|
_copy (false)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2010-09-09 17:34:45 -04:00
|
|
|
DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2015-03-24 16:59:57 -04:00
|
|
|
_drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
|
2013-04-04 18:45:27 -04:00
|
|
|
ArdourCanvas::Rect (0.0, 0.0, 0.0,
|
2015-07-07 22:12:21 -04:00
|
|
|
physical_screen_height (_editor->current_toplevel()->get_window())));
|
2009-05-30 14:25:59 -04:00
|
|
|
_drag_rect->hide ();
|
|
|
|
|
2015-01-02 09:44:54 -05:00
|
|
|
_drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
|
|
|
|
_drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2015-02-05 18:20:53 -05:00
|
|
|
RangeMarkerBarDrag::~RangeMarkerBarDrag()
|
|
|
|
{
|
2015-03-24 16:59:57 -04:00
|
|
|
/* normal canvas items will be cleaned up when their parent group is deleted. But
|
2015-02-05 18:20:53 -05:00
|
|
|
this item is created as the child of a long-lived parent group, and so we
|
|
|
|
need to explicitly delete it.
|
|
|
|
*/
|
|
|
|
delete _drag_rect;
|
|
|
|
}
|
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
void
|
|
|
|
RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
|
|
|
|
{
|
2009-12-17 13:24:23 -05:00
|
|
|
if (_editor->session() == 0) {
|
2009-05-30 14:25:59 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-01-24 12:26:58 -05:00
|
|
|
Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
|
2009-05-30 14:25:59 -04:00
|
|
|
|
|
|
|
if (!_editor->temp_location) {
|
2010-08-09 12:40:31 -04:00
|
|
|
_editor->temp_location = new Location (*_editor->session());
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
switch (_operation) {
|
2014-09-17 09:39:29 -04:00
|
|
|
case CreateSkipMarker:
|
2009-05-30 14:25:59 -04:00
|
|
|
case CreateRangeMarker:
|
|
|
|
case CreateTransportMarker:
|
|
|
|
case CreateCDMarker:
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2015-05-20 11:30:57 -04:00
|
|
|
if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
|
2009-05-30 14:25:59 -04:00
|
|
|
_copy = true;
|
|
|
|
} else {
|
|
|
|
_copy = false;
|
|
|
|
}
|
2010-11-16 09:53:16 -05:00
|
|
|
cursor = _editor->cursors()->selector;
|
2009-05-30 14:25:59 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
Drag::start_grab (event, cursor);
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
show_verbose_cursor_time (adjusted_current_time (event));
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-06-01 20:39:57 -04:00
|
|
|
RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t start;
|
|
|
|
timepos_t end;
|
2013-04-04 00:32:52 -04:00
|
|
|
ArdourCanvas::Rectangle *crect;
|
2009-05-30 14:25:59 -04:00
|
|
|
|
|
|
|
switch (_operation) {
|
2014-09-17 09:39:29 -04:00
|
|
|
case CreateSkipMarker:
|
|
|
|
crect = _editor->range_bar_drag_rect;
|
|
|
|
break;
|
2009-05-30 14:25:59 -04:00
|
|
|
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:
|
2012-11-13 10:11:07 -05:00
|
|
|
error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
|
2009-05-30 14:25:59 -04:00
|
|
|
return;
|
|
|
|
break;
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t const pf = adjusted_current_time (event);
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2014-09-17 09:39:29 -04:00
|
|
|
if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t grab (grab_time());
|
2009-12-21 20:12:41 -05:00
|
|
|
_editor->snap_to (grab);
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
if (pf < grab_time()) {
|
2009-12-21 20:12:41 -05:00
|
|
|
start = pf;
|
2020-10-15 01:09:22 -04:00
|
|
|
end = grab;
|
2009-05-30 14:25:59 -04:00
|
|
|
} else {
|
2009-12-21 20:12:41 -05:00
|
|
|
end = pf;
|
2020-10-15 01:09:22 -04:00
|
|
|
start = grab;
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
/* first drag: Either add to the selection
|
|
|
|
or create a new selection.
|
|
|
|
*/
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-06-01 20:39:57 -04:00
|
|
|
if (first_move) {
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
_editor->temp_location->set (start, end);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
crect->show ();
|
|
|
|
|
|
|
|
update_item (_editor->temp_location);
|
|
|
|
_drag_rect->show();
|
|
|
|
//_drag_rect->raise_to_top();
|
2009-10-14 12:10:01 -04:00
|
|
|
|
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
if (start != end) {
|
|
|
|
_editor->temp_location->set (start, end);
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
double x1 = _editor->time_to_pixel (start);
|
|
|
|
double x2 = _editor->time_to_pixel (end);
|
2013-04-04 00:32:52 -04:00
|
|
|
crect->set_x0 (x1);
|
|
|
|
crect->set_x1 (x2);
|
2009-05-30 14:25:59 -04:00
|
|
|
|
|
|
|
update_item (_editor->temp_location);
|
|
|
|
}
|
|
|
|
|
2011-05-02 09:38:16 -04:00
|
|
|
show_verbose_cursor_time (pf);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-06-01 20:39:57 -04:00
|
|
|
RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
|
2009-05-30 14:25:59 -04:00
|
|
|
{
|
|
|
|
Location * newloc = 0;
|
|
|
|
string rangename;
|
|
|
|
int flags;
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-06-01 20:39:57 -04:00
|
|
|
if (movement_occurred) {
|
|
|
|
motion (event, false);
|
2009-08-04 14:18:57 -04:00
|
|
|
_drag_rect->hide();
|
2009-05-30 14:25:59 -04:00
|
|
|
|
|
|
|
switch (_operation) {
|
2014-09-17 09:39:29 -04:00
|
|
|
case CreateSkipMarker:
|
2009-05-30 14:25:59 -04:00
|
|
|
case CreateRangeMarker:
|
|
|
|
case CreateCDMarker:
|
2017-07-01 12:42:24 -04:00
|
|
|
{
|
2009-12-17 13:24:23 -05:00
|
|
|
XMLNode &before = _editor->session()->locations()->get_state();
|
2014-09-17 09:39:29 -04:00
|
|
|
if (_operation == CreateSkipMarker) {
|
|
|
|
_editor->begin_reversible_command (_("new skip marker"));
|
|
|
|
_editor->session()->locations()->next_available_name(rangename,_("skip"));
|
|
|
|
flags = Location::IsRangeMarker | Location::IsSkip;
|
|
|
|
_editor->range_bar_drag_rect->hide();
|
|
|
|
} else if (_operation == CreateCDMarker) {
|
|
|
|
_editor->session()->locations()->next_available_name(rangename, _("CD"));
|
|
|
|
_editor->begin_reversible_command (_("new CD marker"));
|
2009-05-30 14:25:59 -04:00
|
|
|
flags = Location::IsRangeMarker | Location::IsCDMarker;
|
|
|
|
_editor->cd_marker_bar_drag_rect->hide();
|
2014-09-17 09:39:29 -04:00
|
|
|
} else {
|
|
|
|
_editor->begin_reversible_command (_("new skip marker"));
|
|
|
|
_editor->session()->locations()->next_available_name(rangename, _("unnamed"));
|
2009-05-30 14:25:59 -04:00
|
|
|
flags = Location::IsRangeMarker;
|
|
|
|
_editor->range_bar_drag_rect->hide();
|
|
|
|
}
|
2020-10-15 01:09:22 -04:00
|
|
|
|
|
|
|
newloc = new Location (*_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags);
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2009-12-17 13:24:23 -05:00
|
|
|
_editor->session()->locations()->add (newloc, true);
|
|
|
|
XMLNode &after = _editor->session()->locations()->get_state();
|
|
|
|
_editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
|
2009-05-30 14:25:59 -04:00
|
|
|
_editor->commit_reversible_command ();
|
|
|
|
break;
|
2017-07-01 12:42:24 -04:00
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
|
|
|
|
case CreateTransportMarker:
|
|
|
|
// popup menu to pick loop or punch
|
|
|
|
_editor->new_transport_marker_context_menu (&event->button, _item);
|
|
|
|
break;
|
|
|
|
}
|
2014-01-27 10:09:58 -05:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
} else {
|
2014-01-27 10:09:58 -05:00
|
|
|
|
2009-05-30 14:25:59 -04:00
|
|
|
/* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
|
|
|
|
|
2014-01-27 10:09:58 -05:00
|
|
|
if (_operation == CreateTransportMarker) {
|
|
|
|
|
|
|
|
/* didn't drag, so just locate */
|
|
|
|
|
2021-04-28 21:55:37 -04:00
|
|
|
_editor->session()->request_locate (grab_sample());
|
2014-01-27 10:09:58 -05:00
|
|
|
|
|
|
|
} else if (_operation == CreateCDMarker) {
|
|
|
|
|
|
|
|
/* didn't drag, but mark is already created so do
|
|
|
|
* nothing */
|
|
|
|
|
2014-09-17 09:39:29 -04:00
|
|
|
} else { /* operation == CreateRangeMarker || CreateSkipMarker */
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t start;
|
|
|
|
timepos_t end;
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
_editor->session()->locations()->marks_either_side (grab_time(), start, end);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
if (end == timepos_t::max (end.time_domain())) {
|
|
|
|
end = _editor->session()->current_end ();
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
if (start == timepos_t::max (start.time_domain())) {
|
|
|
|
start = _editor->session()->current_start ();
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (_editor->mouse_mode) {
|
|
|
|
case MouseObject:
|
|
|
|
/* find the two markers on either side and then make the selection from it */
|
2010-08-09 21:52:49 -04:00
|
|
|
_editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
|
2009-05-30 14:25:59 -04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case MouseRange:
|
|
|
|
/* find the two markers on either side of the click and make the range out of it */
|
2009-12-13 16:27:19 -05:00
|
|
|
_editor->selection->set (start, end);
|
2009-05-30 14:25:59 -04:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
_editor->stop_canvas_autoscroll ();
|
|
|
|
}
|
|
|
|
|
2010-01-07 20:28:15 -05:00
|
|
|
void
|
2016-02-22 15:01:23 -05:00
|
|
|
RangeMarkerBarDrag::aborted (bool movement_occurred)
|
2010-01-07 20:28:15 -05:00
|
|
|
{
|
2016-02-22 15:01:23 -05:00
|
|
|
if (movement_occurred) {
|
2015-02-05 18:20:53 -05:00
|
|
|
_drag_rect->hide ();
|
|
|
|
}
|
2010-01-07 20:28:15 -05:00
|
|
|
}
|
2009-05-30 14:25:59 -04:00
|
|
|
|
|
|
|
void
|
|
|
|
RangeMarkerBarDrag::update_item (Location* location)
|
|
|
|
{
|
2020-10-15 01:09:22 -04:00
|
|
|
double const x1 = _editor->time_to_pixel (location->start());
|
|
|
|
double const x2 = _editor->time_to_pixel (location->end());
|
2009-05-30 14:25:59 -04:00
|
|
|
|
2013-04-04 00:32:52 -04:00
|
|
|
_drag_rect->set_x0 (x1);
|
|
|
|
_drag_rect->set_x1 (x2);
|
2009-05-30 14:25:59 -04:00
|
|
|
}
|
|
|
|
|
2009-09-08 17:45:44 -04:00
|
|
|
NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
|
|
|
|
: Drag (e, i)
|
2010-08-19 18:01:10 -04:00
|
|
|
, _cumulative_dy (0)
|
2015-10-23 13:59:57 -04:00
|
|
|
, _was_selected (false)
|
2017-01-23 15:57:38 -05:00
|
|
|
, _copy (false)
|
2009-09-08 17:45:44 -04:00
|
|
|
{
|
2010-09-09 17:34:45 -04:00
|
|
|
DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
|
|
|
|
|
2013-04-04 00:32:52 -04:00
|
|
|
_primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
|
|
|
|
assert (_primary);
|
2010-08-19 18:01:10 -04:00
|
|
|
_region = &_primary->region_view ();
|
|
|
|
_note_height = _region->midi_stream_view()->note_height ();
|
2009-09-08 17:45:44 -04:00
|
|
|
}
|
|
|
|
|
2017-02-04 13:02:01 -05:00
|
|
|
void
|
2020-10-15 01:09:22 -04:00
|
|
|
NoteDrag::setup_pointer_offset ()
|
2017-02-04 13:02:01 -05:00
|
|
|
{
|
2020-10-15 01:09:22 -04:00
|
|
|
_pointer_offset = _region->region()->source_beats_to_absolute_time (_primary->note()->time()).distance (raw_grab_time());
|
2017-02-04 13:02:01 -05:00
|
|
|
}
|
|
|
|
|
2009-09-08 17:45:44 -04:00
|
|
|
void
|
|
|
|
NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
|
|
|
|
{
|
|
|
|
Drag::start_grab (event);
|
2017-01-23 15:57:38 -05:00
|
|
|
|
2017-02-06 09:56:47 -05:00
|
|
|
if (ArdourKeyboard::indicates_copy (event->button.state)) {
|
2017-01-23 15:57:38 -05:00
|
|
|
_copy = true;
|
|
|
|
} else {
|
|
|
|
_copy = false;
|
|
|
|
}
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
setup_snap_delta (_region->region()->source_beats_to_absolute_time (_primary->note()->time ()));
|
2015-05-15 14:15:52 -04:00
|
|
|
|
2010-08-19 18:01:10 -04:00
|
|
|
if (!(_was_selected = _primary->selected())) {
|
2009-09-12 12:46:19 -04:00
|
|
|
|
2009-09-10 16:41:08 -04:00
|
|
|
/* tertiary-click means extend selection - we'll do that on button release,
|
|
|
|
so don't add it here, because otherwise we make it hard to figure
|
|
|
|
out the "extend-to" range.
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
|
|
|
|
|
|
|
|
if (!extend) {
|
2009-09-12 12:46:19 -04:00
|
|
|
bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
|
|
|
|
|
|
|
|
if (add) {
|
2010-08-19 18:01:10 -04:00
|
|
|
_region->note_selected (_primary, true);
|
2009-09-12 12:46:19 -04:00
|
|
|
} else {
|
2015-10-23 09:07:03 -04:00
|
|
|
_editor->get_selection().clear_points();
|
2010-08-19 18:01:10 -04:00
|
|
|
_region->unique_select (_primary);
|
2009-09-12 12:46:19 -04:00
|
|
|
}
|
2009-09-10 16:41:08 -04:00
|
|
|
}
|
|
|
|
}
|
2009-09-08 17:45:44 -04:00
|
|
|
}
|
|
|
|
|
2017-02-04 13:02:01 -05:00
|
|
|
/** @return Current total drag x change in quarter notes */
|
2020-10-15 01:09:22 -04:00
|
|
|
Temporal::timecnt_t
|
2017-02-04 13:02:01 -05:00
|
|
|
NoteDrag::total_dx (GdkEvent * event) const
|
2009-09-08 17:45:44 -04:00
|
|
|
{
|
2016-08-15 11:20:17 -04:00
|
|
|
if (_x_constrained) {
|
2020-10-15 01:09:22 -04:00
|
|
|
return timecnt_t::zero (Temporal::BeatTime);
|
2016-08-15 11:20:17 -04:00
|
|
|
}
|
2017-02-04 13:02:01 -05:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
/* dx in as samples, because we can't do any better */
|
|
|
|
timecnt_t const dx = timecnt_t (_editor->pixel_to_sample (_drags->current_pointer_x() - grab_x()), timepos_t());
|
2011-10-22 19:18:54 -04:00
|
|
|
|
2017-02-04 13:02:01 -05:00
|
|
|
/* primary note time in quarter notes */
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t const n_qn = _region->region()->source_beats_to_absolute_time (_primary->note()->time());
|
2009-09-08 17:45:44 -04:00
|
|
|
|
2021-03-03 12:04:33 -05:00
|
|
|
/* new session relative time of the primary note (will be in beats)
|
|
|
|
|
|
|
|
* start from the note position, add the distance the drag has covered,
|
|
|
|
* and then the required (if any) snap distance
|
|
|
|
*/
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t snap = n_qn + dx + snap_delta (event->button.state);
|
2015-05-22 13:09:48 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
/* possibly snap and return corresponding delta (will be in beats) */
|
|
|
|
_editor->snap_to_with_modifier (snap, event);
|
|
|
|
|
2021-03-03 12:04:33 -05:00
|
|
|
/* we are trying to return the delta on the x-axis (almost certain in
|
|
|
|
* beats), So now, having snapped etc., subtract the original note
|
|
|
|
* position and the snap delta, and we'll know the current dx.
|
|
|
|
*/
|
|
|
|
|
|
|
|
timecnt_t ret (snap.earlier (n_qn).earlier (snap_delta (event->button.state)), n_qn);
|
2015-05-21 16:54:16 -04:00
|
|
|
|
2017-02-04 13:02:01 -05:00
|
|
|
/* prevent the earliest note being dragged earlier than the region's start position */
|
2021-03-03 12:04:33 -05:00
|
|
|
if (_earliest + ret < _region->region()->start()) {
|
2020-11-30 12:59:17 -05:00
|
|
|
ret -= (ret + _earliest) - _region->region()->start();
|
2015-05-21 16:54:16 -04:00
|
|
|
}
|
|
|
|
|
2015-06-10 11:36:34 -04:00
|
|
|
return ret;
|
2010-08-19 18:01:10 -04:00
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2011-07-17 12:02:26 -04:00
|
|
|
/** @return Current total drag y change in note number */
|
2010-08-19 18:01:10 -04:00
|
|
|
int8_t
|
|
|
|
NoteDrag::total_dy () const
|
|
|
|
{
|
2016-08-15 11:20:17 -04:00
|
|
|
if (_y_constrained) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-06-13 14:01:29 -04:00
|
|
|
double const y = _region->midi_view()->y_position ();
|
|
|
|
/* new current note */
|
2016-11-22 16:04:14 -05:00
|
|
|
uint8_t n = _region->y_to_note (current_pointer_y () - y);
|
2012-06-13 14:01:29 -04:00
|
|
|
/* clamp */
|
2016-11-22 16:04:14 -05:00
|
|
|
MidiStreamView* msv = _region->midi_stream_view ();
|
2012-06-13 14:01:29 -04:00
|
|
|
n = max (msv->lowest_note(), n);
|
|
|
|
n = min (msv->highest_note(), n);
|
|
|
|
/* and work out delta */
|
2016-11-22 16:04:14 -05:00
|
|
|
return n - _region->y_to_note (grab_y() - y);
|
2011-06-01 13:00:29 -04:00
|
|
|
}
|
2010-08-19 18:01:10 -04:00
|
|
|
|
|
|
|
void
|
2017-01-23 15:57:38 -05:00
|
|
|
NoteDrag::motion (GdkEvent * event, bool first_move)
|
2010-08-19 18:01:10 -04:00
|
|
|
{
|
2017-02-04 13:02:01 -05:00
|
|
|
if (first_move) {
|
2020-10-15 01:09:22 -04:00
|
|
|
_earliest = timepos_t (_region->earliest_in_selection());
|
2017-02-04 13:02:01 -05:00
|
|
|
if (_copy) {
|
|
|
|
/* make copies of all the selected notes */
|
|
|
|
_primary = _region->copy_selection (_primary);
|
|
|
|
}
|
2017-01-23 15:57:38 -05:00
|
|
|
}
|
|
|
|
|
2010-08-19 18:01:10 -04:00
|
|
|
/* Total change in x and y since the start of the drag */
|
2020-10-15 01:09:22 -04:00
|
|
|
timecnt_t const dx_qn = total_dx (event);
|
2011-07-17 12:02:26 -04:00
|
|
|
int8_t const dy = total_dy ();
|
2010-08-19 18:01:10 -04:00
|
|
|
|
|
|
|
/* Now work out what we have to do to the note canvas items to set this new drag delta */
|
2020-10-15 01:09:22 -04:00
|
|
|
timecnt_t const tdx = _x_constrained ? timecnt_t::zero (_cumulative_dx.time_domain()) : dx_qn - _cumulative_dx;
|
2016-08-15 11:20:17 -04:00
|
|
|
double const tdy = _y_constrained ? 0 : -dy * _note_height - _cumulative_dy;
|
2010-08-19 18:01:10 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
if (!tdx.zero() || tdy) {
|
2017-02-04 13:02:01 -05:00
|
|
|
_cumulative_dx = dx_qn;
|
2010-08-19 18:01:10 -04:00
|
|
|
_cumulative_dy += tdy;
|
2010-05-20 18:38:12 -04:00
|
|
|
|
2010-11-25 15:37:39 -05:00
|
|
|
int8_t note_delta = total_dy();
|
2010-09-17 16:32:18 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
if (!tdx.zero() || tdy) {
|
2017-01-23 15:57:38 -05:00
|
|
|
if (_copy) {
|
2017-02-04 13:02:01 -05:00
|
|
|
_region->move_copies (dx_qn, tdy, note_delta);
|
2017-01-23 15:57:38 -05:00
|
|
|
} else {
|
2017-02-04 13:02:01 -05:00
|
|
|
_region->move_selection (dx_qn, tdy, note_delta);
|
2017-01-23 15:57:38 -05:00
|
|
|
}
|
2010-09-15 12:54:17 -04:00
|
|
|
|
2016-08-15 11:20:17 -04:00
|
|
|
/* the new note value may be the same as the old one, but we
|
|
|
|
* don't know what that means because the selection may have
|
|
|
|
* involved more than one note and we might be doing something
|
|
|
|
* odd with them. so show the note value anyway, always.
|
|
|
|
*/
|
2011-08-12 10:41:11 -04:00
|
|
|
|
2016-08-15 11:20:17 -04:00
|
|
|
uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2016-08-15 11:20:17 -04:00
|
|
|
_region->show_verbose_cursor_for_new_note_value (_primary->note(), new_note);
|
2018-02-09 09:21:45 -05:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
_editor->set_snapped_cursor_position( _region->region()->source_beats_to_absolute_time (_primary->note()->time()) );
|
2016-08-15 11:20:17 -04:00
|
|
|
}
|
2010-11-25 15:37:39 -05:00
|
|
|
}
|
2009-09-08 17:45:44 -04:00
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-09-08 17:45:44 -04:00
|
|
|
void
|
|
|
|
NoteDrag::finished (GdkEvent* ev, bool moved)
|
|
|
|
{
|
|
|
|
if (!moved) {
|
2012-02-08 07:50:32 -05:00
|
|
|
/* no motion - select note */
|
2015-01-10 12:07:31 -05:00
|
|
|
|
2015-08-27 20:25:48 -04:00
|
|
|
if (_editor->current_mouse_mode() == Editing::MouseContent ||
|
2012-02-08 07:50:32 -05:00
|
|
|
_editor->current_mouse_mode() == Editing::MouseDraw) {
|
2015-01-10 12:07:31 -05:00
|
|
|
|
|
|
|
bool changed = false;
|
|
|
|
|
2010-08-19 18:01:10 -04:00
|
|
|
if (_was_selected) {
|
2009-09-10 16:41:08 -04:00
|
|
|
bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
|
|
|
|
if (add) {
|
2010-08-19 18:01:10 -04:00
|
|
|
_region->note_deselected (_primary);
|
2015-01-10 12:07:31 -05:00
|
|
|
changed = true;
|
2015-10-07 09:48:03 -04:00
|
|
|
} else {
|
2015-10-23 09:07:03 -04:00
|
|
|
_editor->get_selection().clear_points();
|
2015-10-07 09:48:03 -04:00
|
|
|
_region->unique_select (_primary);
|
2015-10-23 09:07:03 -04:00
|
|
|
changed = true;
|
2009-09-10 16:41:08 -04:00
|
|
|
}
|
2009-09-08 17:45:44 -04:00
|
|
|
} else {
|
|
|
|
bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
|
|
|
|
bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
|
2009-09-10 16:41:08 -04:00
|
|
|
|
2010-08-19 18:01:10 -04:00
|
|
|
if (!extend && !add && _region->selection_size() > 1) {
|
2015-10-23 09:07:03 -04:00
|
|
|
_editor->get_selection().clear_points();
|
2010-08-19 18:01:10 -04:00
|
|
|
_region->unique_select (_primary);
|
2015-01-10 12:07:31 -05:00
|
|
|
changed = true;
|
2009-09-10 16:41:08 -04:00
|
|
|
} else if (extend) {
|
2010-08-19 18:01:10 -04:00
|
|
|
_region->note_selected (_primary, true, true);
|
2015-01-10 12:07:31 -05:00
|
|
|
changed = true;
|
2009-09-08 17:45:44 -04:00
|
|
|
} else {
|
2009-09-10 16:41:08 -04:00
|
|
|
/* it was added during button press */
|
2015-10-23 09:07:03 -04:00
|
|
|
changed = true;
|
|
|
|
|
2009-09-08 17:45:44 -04:00
|
|
|
}
|
|
|
|
}
|
2015-01-10 12:07:31 -05:00
|
|
|
|
|
|
|
if (changed) {
|
2015-03-25 08:31:23 -04:00
|
|
|
_editor->begin_reversible_selection_op(X_("Select Note Release"));
|
2015-01-10 12:07:31 -05:00
|
|
|
_editor->commit_reversible_selection_op();
|
|
|
|
}
|
2009-09-08 17:45:44 -04:00
|
|
|
}
|
|
|
|
} else {
|
2017-02-04 13:02:01 -05:00
|
|
|
_region->note_dropped (_primary, total_dx (ev), total_dy(), _copy);
|
2009-09-08 17:45:44 -04:00
|
|
|
}
|
|
|
|
}
|
2010-01-01 17:11:15 -05:00
|
|
|
|
2010-01-07 20:28:15 -05:00
|
|
|
void
|
2010-12-28 14:17:37 -05:00
|
|
|
NoteDrag::aborted (bool)
|
2010-01-07 20:28:15 -05:00
|
|
|
{
|
|
|
|
/* XXX: TODO */
|
|
|
|
}
|
|
|
|
|
2012-03-22 12:41:23 -04:00
|
|
|
/** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
|
2020-09-26 11:14:59 -04:00
|
|
|
AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<TimelineRange> const & r)
|
2012-03-22 12:41:23 -04:00
|
|
|
: Drag (editor, atv->base_item ())
|
2010-01-01 17:11:15 -05:00
|
|
|
, _ranges (r)
|
2015-01-13 23:30:37 -05:00
|
|
|
, _y_origin (atv->y_position())
|
2019-03-29 20:19:47 -04:00
|
|
|
, _y_height (atv->effective_height()) // or atv->lines()->front()->height() ?!
|
2010-01-01 17:11:15 -05:00
|
|
|
, _nothing_to_drag (false)
|
|
|
|
{
|
2010-09-09 17:34:45 -04:00
|
|
|
DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
|
2012-03-22 12:41:23 -04:00
|
|
|
setup (atv->lines ());
|
|
|
|
}
|
|
|
|
|
2015-01-13 23:30:37 -05:00
|
|
|
/** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
|
2020-09-26 11:14:59 -04:00
|
|
|
AutomationRangeDrag::AutomationRangeDrag (Editor* editor, list<RegionView*> const & v, list<TimelineRange> const & r, double y_origin, double y_height)
|
2019-03-29 10:57:07 -04:00
|
|
|
: Drag (editor, v.front()->get_canvas_group ())
|
2012-03-22 12:41:23 -04:00
|
|
|
, _ranges (r)
|
2019-03-29 10:57:07 -04:00
|
|
|
, _y_origin (y_origin)
|
2019-03-29 20:19:47 -04:00
|
|
|
, _y_height (y_height)
|
2012-03-22 12:41:23 -04:00
|
|
|
, _nothing_to_drag (false)
|
2015-01-13 23:30:37 -05:00
|
|
|
, _integral (false)
|
2012-03-22 12:41:23 -04:00
|
|
|
{
|
|
|
|
DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
|
2010-01-01 17:11:15 -05:00
|
|
|
|
2012-03-22 12:41:23 -04:00
|
|
|
list<boost::shared_ptr<AutomationLine> > lines;
|
2015-01-13 23:30:37 -05:00
|
|
|
|
2019-03-27 18:23:33 -04:00
|
|
|
for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
|
|
|
|
if (AudioRegionView* audio_view = dynamic_cast<AudioRegionView*>(*i)) {
|
|
|
|
lines.push_back (audio_view->get_gain_line ());
|
|
|
|
} else if (AutomationRegionView* automation_view = dynamic_cast<AutomationRegionView*>(*i)) {
|
|
|
|
lines.push_back (automation_view->line ());
|
|
|
|
_integral = true;
|
|
|
|
} else {
|
|
|
|
error << _("Automation range drag created for invalid region type") << endmsg;
|
|
|
|
}
|
2015-01-13 23:30:37 -05:00
|
|
|
}
|
2012-03-22 12:41:23 -04:00
|
|
|
setup (lines);
|
|
|
|
}
|
2010-09-09 17:35:28 -04:00
|
|
|
|
2012-03-22 12:41:23 -04:00
|
|
|
/** @param lines AutomationLines to drag.
|
|
|
|
* @param offset Offset from the session start to the points in the AutomationLines.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
|
|
|
|
{
|
|
|
|
/* find the lines that overlap the ranges being dragged */
|
|
|
|
list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
|
2010-09-09 17:35:28 -04:00
|
|
|
while (i != lines.end ()) {
|
2012-03-22 12:41:23 -04:00
|
|
|
list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
|
2010-09-09 17:35:28 -04:00
|
|
|
++j;
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
pair<timepos_t, timepos_t> r = (*i)->get_point_x_range ();
|
2010-09-09 17:35:28 -04:00
|
|
|
|
2019-02-12 22:23:01 -05:00
|
|
|
//need a special detection for automation lanes (not region gain line)
|
|
|
|
//TODO: if we implement automation regions, this check can probably be removed
|
|
|
|
AudioRegionGainLine *argl = dynamic_cast<AudioRegionGainLine*> ((*i).get());
|
|
|
|
if (!argl) {
|
2021-02-16 17:58:37 -05:00
|
|
|
//in automation lanes, the EFFECTIVE range should be considered 0->max_position (even if there is no line)
|
2020-12-03 23:34:48 -05:00
|
|
|
r.first = Temporal::timepos_t ((*i)->the_list()->time_domain());
|
|
|
|
r.second = Temporal::timepos_t::max ((*i)->the_list()->time_domain());
|
2019-02-12 22:23:01 -05:00
|
|
|
}
|
2020-03-24 20:55:16 -04:00
|
|
|
|
2020-09-26 11:14:59 -04:00
|
|
|
/* check this range against all the TimelineRanges that we are using */
|
|
|
|
list<TimelineRange>::const_iterator k = _ranges.begin ();
|
2010-09-09 17:35:28 -04:00
|
|
|
while (k != _ranges.end()) {
|
2020-10-15 01:09:22 -04:00
|
|
|
if (k->coverage (r.first, r.second) != Temporal::OverlapNone) {
|
2010-09-09 17:35:28 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
++k;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* add it to our list if it overlaps at all */
|
|
|
|
if (k != _ranges.end()) {
|
|
|
|
Line n;
|
|
|
|
n.line = *i;
|
|
|
|
n.state = 0;
|
|
|
|
n.range = r;
|
|
|
|
_lines.push_back (n);
|
|
|
|
}
|
|
|
|
|
|
|
|
i = j;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now ::lines contains the AutomationLines that somehow overlap our drag */
|
2010-01-01 17:11:15 -05:00
|
|
|
}
|
|
|
|
|
2012-07-10 11:58:09 -04:00
|
|
|
double
|
2019-03-29 20:19:47 -04:00
|
|
|
AutomationRangeDrag::y_fraction (double global_y) const
|
2012-07-10 11:58:09 -04:00
|
|
|
{
|
2019-03-29 20:19:47 -04:00
|
|
|
return 1.0 - ((global_y - _y_origin) / _y_height);
|
2015-01-13 23:30:37 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
double
|
2020-10-15 01:09:22 -04:00
|
|
|
AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, timepos_t const & x) const
|
2015-01-13 23:30:37 -05:00
|
|
|
{
|
|
|
|
const double v = list->eval(x);
|
|
|
|
return _integral ? rint(v) : v;
|
2012-07-10 11:58:09 -04:00
|
|
|
}
|
|
|
|
|
2010-01-01 17:11:15 -05:00
|
|
|
void
|
|
|
|
AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
|
|
|
|
{
|
|
|
|
Drag::start_grab (event, cursor);
|
|
|
|
|
2010-09-09 17:35:28 -04:00
|
|
|
/* Get line states before we start changing things */
|
|
|
|
for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
|
|
|
|
i->state = &i->line->get_state ();
|
|
|
|
}
|
2010-01-01 17:11:15 -05:00
|
|
|
|
|
|
|
if (_ranges.empty()) {
|
2010-09-09 17:35:28 -04:00
|
|
|
|
|
|
|
/* No selected time ranges: drag all points */
|
|
|
|
for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
|
|
|
|
uint32_t const N = i->line->npoints ();
|
|
|
|
for (uint32_t j = 0; j < N; ++j) {
|
|
|
|
i->points.push_back (i->line->nth (j));
|
|
|
|
}
|
2010-01-01 17:11:15 -05:00
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2015-09-13 15:24:28 -04:00
|
|
|
}
|
2010-01-01 17:11:15 -05:00
|
|
|
|
2015-09-13 15:24:28 -04:00
|
|
|
if (_nothing_to_drag) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2010-01-01 20:19:47 -05:00
|
|
|
|
2015-09-13 15:24:28 -04:00
|
|
|
void
|
|
|
|
AutomationRangeDrag::motion (GdkEvent*, bool first_move)
|
|
|
|
{
|
|
|
|
if (_nothing_to_drag && !first_move) {
|
|
|
|
return;
|
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2015-09-13 15:24:28 -04:00
|
|
|
if (first_move) {
|
2019-03-29 19:14:26 -04:00
|
|
|
_editor->begin_reversible_command (_("automation range move"));
|
2015-09-13 15:24:28 -04:00
|
|
|
|
|
|
|
if (!_ranges.empty()) {
|
2010-09-09 17:35:28 -04:00
|
|
|
|
2019-03-29 19:14:26 -04:00
|
|
|
/* add guard points */
|
2020-09-26 11:14:59 -04:00
|
|
|
for (list<TimelineRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
|
2015-09-13 15:24:28 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t const half = (i->start() + i->end()) / 2;
|
2015-09-13 15:24:28 -04:00
|
|
|
|
2019-03-29 19:14:26 -04:00
|
|
|
for (list<Line>::iterator j = _lines.begin(); j != _lines.end(); ++j) {
|
2020-10-15 01:09:22 -04:00
|
|
|
if (j->range.first > i->start() || j->range.second < i->start()) {
|
2019-03-29 19:14:26 -04:00
|
|
|
continue;
|
|
|
|
}
|
2015-09-13 15:24:28 -04:00
|
|
|
|
|
|
|
boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2019-03-29 19:14:26 -04:00
|
|
|
/* j is the line that this audio range starts in; fade into it;
|
|
|
|
* 64 samples length plucked out of thin air.
|
|
|
|
*/
|
2010-09-09 17:35:28 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t a = i->start() + timepos_t (64);
|
2015-09-13 15:24:28 -04:00
|
|
|
if (a > half) {
|
|
|
|
a = half;
|
|
|
|
}
|
2021-02-24 21:00:44 -05:00
|
|
|
|
|
|
|
/* convert from absolute time into time
|
|
|
|
* relative to the line origin
|
|
|
|
*/
|
|
|
|
|
|
|
|
timepos_t p (j->line->get_origin().distance (i->start()).beats());
|
|
|
|
timepos_t q (j->line->get_origin().distance (a).beats());
|
|
|
|
|
|
|
|
/* XXX arguably ControlList::editor_add() should do this */
|
|
|
|
|
|
|
|
p.set_time_domain (the_list->time_domain());
|
|
|
|
q.set_time_domain (the_list->time_domain());
|
2010-09-09 17:35:28 -04:00
|
|
|
|
2015-09-13 15:24:28 -04:00
|
|
|
XMLNode &before = the_list->get_state();
|
|
|
|
bool const add_p = the_list->editor_add (p, value (the_list, p), false);
|
|
|
|
bool const add_q = the_list->editor_add (q, value (the_list, q), false);
|
2010-09-09 17:35:28 -04:00
|
|
|
|
2015-09-13 15:24:28 -04:00
|
|
|
if (add_p || add_q) {
|
|
|
|
_editor->session()->add_command (
|
|
|
|
new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
|
|
|
|
}
|
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2015-09-13 15:24:28 -04:00
|
|
|
/* same thing for the end */
|
2019-03-29 19:14:26 -04:00
|
|
|
for (list<Line>::iterator j = _lines.begin(); j != _lines.end(); ++j) {
|
2010-09-09 17:35:28 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
if (j->range.first > i->end() || j->range.second < i->end()) {
|
2019-03-29 19:14:26 -04:00
|
|
|
continue;
|
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2015-09-13 15:24:28 -04:00
|
|
|
boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2015-09-13 15:24:28 -04:00
|
|
|
/* j is the line that this audio range starts in; fade out of it;
|
2019-03-29 19:14:26 -04:00
|
|
|
* 64 samples length plucked out of thin air.
|
|
|
|
*/
|
2010-09-09 17:35:28 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t b = i->end().earlier (timepos_t (64));
|
2015-09-13 15:24:28 -04:00
|
|
|
if (b < half) {
|
|
|
|
b = half;
|
|
|
|
}
|
|
|
|
|
2021-02-24 21:00:44 -05:00
|
|
|
timepos_t p (j->line->get_origin().distance (b));
|
|
|
|
timepos_t q (j->line->get_origin().distance (i->end()));
|
|
|
|
|
|
|
|
/* XXX arguably ControlList::editor_add() should do this */
|
|
|
|
|
|
|
|
p.set_time_domain (the_list->time_domain());
|
|
|
|
q.set_time_domain (the_list->time_domain());
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2015-09-13 15:24:28 -04:00
|
|
|
XMLNode &before = the_list->get_state();
|
|
|
|
bool const add_p = the_list->editor_add (p, value (the_list, p), false);
|
|
|
|
bool const add_q = the_list->editor_add (q, value (the_list, q), false);
|
|
|
|
|
|
|
|
if (add_p || add_q) {
|
|
|
|
_editor->session()->add_command (
|
|
|
|
new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
|
|
|
|
}
|
|
|
|
}
|
2010-09-09 17:35:28 -04:00
|
|
|
}
|
2010-01-01 17:11:15 -05:00
|
|
|
|
2015-09-13 15:24:28 -04:00
|
|
|
_nothing_to_drag = true;
|
2010-01-01 17:11:15 -05:00
|
|
|
|
2015-09-13 15:24:28 -04:00
|
|
|
/* Find all the points that should be dragged and put them in the relevant
|
2019-03-29 19:14:26 -04:00
|
|
|
* points lists in the Line structs.
|
|
|
|
*/
|
2015-09-13 15:24:28 -04:00
|
|
|
for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
|
2010-01-01 17:11:15 -05:00
|
|
|
|
2015-09-13 15:24:28 -04:00
|
|
|
uint32_t const N = i->line->npoints ();
|
|
|
|
for (uint32_t j = 0; j < N; ++j) {
|
2010-09-09 17:35:28 -04:00
|
|
|
|
2015-09-13 15:24:28 -04:00
|
|
|
/* here's a control point on this line */
|
|
|
|
ControlPoint* p = i->line->nth (j);
|
2021-02-16 17:58:37 -05:00
|
|
|
|
2021-02-24 21:00:44 -05:00
|
|
|
/* convert point time (which is relative to line
|
|
|
|
* origin) into absolute time
|
2021-02-16 17:58:37 -05:00
|
|
|
*/
|
|
|
|
|
2021-02-24 21:00:44 -05:00
|
|
|
timepos_t const w = i->line->get_origin() + (*p->model())->when;
|
2010-09-09 17:35:28 -04:00
|
|
|
|
2015-09-13 15:24:28 -04:00
|
|
|
/* see if it's inside a range */
|
2020-09-26 11:14:59 -04:00
|
|
|
list<TimelineRange>::const_iterator k = _ranges.begin ();
|
2020-10-15 01:09:22 -04:00
|
|
|
while (k != _ranges.end() && (k->start() >= w || k->end() <= w)) {
|
2015-09-13 15:24:28 -04:00
|
|
|
++k;
|
|
|
|
}
|
2010-09-09 17:35:28 -04:00
|
|
|
|
2015-09-13 15:24:28 -04:00
|
|
|
if (k != _ranges.end()) {
|
|
|
|
/* dragging this point */
|
|
|
|
_nothing_to_drag = false;
|
|
|
|
i->points.push_back (p);
|
|
|
|
}
|
2010-09-09 17:35:28 -04:00
|
|
|
}
|
2010-01-01 17:11:15 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-18 10:02:09 -04:00
|
|
|
for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
|
2019-03-29 20:19:47 -04:00
|
|
|
i->line->start_drag_multiple (i->points, y_fraction (current_pointer_y()), i->state);
|
2015-06-18 10:02:09 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-10 11:44:56 -04:00
|
|
|
for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
|
2019-03-29 20:19:47 -04:00
|
|
|
float const f = y_fraction (current_pointer_y());
|
2010-09-09 17:35:28 -04:00
|
|
|
/* we are ignoring x position for this drag, so we can just pass in anything */
|
2017-06-21 07:03:00 -04:00
|
|
|
pair<float, float> result;
|
2012-12-19 10:55:06 -05:00
|
|
|
uint32_t ignored;
|
2015-10-27 11:46:03 -04:00
|
|
|
result = l->line->drag_motion (0, f, true, false, ignored);
|
2017-06-21 07:03:00 -04:00
|
|
|
show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (result.first, result.second));
|
2010-09-09 17:35:28 -04:00
|
|
|
}
|
2010-01-01 17:11:15 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2015-06-18 10:02:09 -04:00
|
|
|
AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
|
2010-01-01 17:11:15 -05:00
|
|
|
{
|
2015-06-18 10:02:09 -04:00
|
|
|
if (_nothing_to_drag || !motion_occurred) {
|
2010-01-01 17:11:15 -05:00
|
|
|
return;
|
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-01-01 17:11:15 -05:00
|
|
|
motion (event, false);
|
2010-09-09 17:35:28 -04:00
|
|
|
for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
|
2012-12-19 10:55:06 -05:00
|
|
|
i->line->end_drag (false, 0);
|
2010-09-09 17:35:28 -04:00
|
|
|
}
|
|
|
|
|
2014-12-14 08:13:38 -05:00
|
|
|
_editor->commit_reversible_command ();
|
2010-01-01 17:11:15 -05:00
|
|
|
}
|
2010-01-07 20:28:15 -05:00
|
|
|
|
|
|
|
void
|
2010-12-28 14:17:37 -05:00
|
|
|
AutomationRangeDrag::aborted (bool)
|
2010-01-07 20:28:15 -05:00
|
|
|
{
|
2010-09-09 17:35:28 -04:00
|
|
|
for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
|
|
|
|
i->line->reset ();
|
|
|
|
}
|
2010-01-07 20:28:15 -05:00
|
|
|
}
|
2010-04-06 20:17:54 -04:00
|
|
|
|
2014-06-19 11:16:27 -04:00
|
|
|
DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
|
2010-04-06 20:17:54 -04:00
|
|
|
: view (v)
|
2014-06-19 11:16:27 -04:00
|
|
|
, initial_time_axis_view (itav)
|
2010-04-06 20:17:54 -04:00
|
|
|
{
|
2021-05-04 13:55:23 -04:00
|
|
|
TimeAxisView* tav = &v->get_time_axis_view();
|
|
|
|
if (tav) {
|
|
|
|
time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
|
|
|
|
} else {
|
|
|
|
time_axis_view = -1;
|
|
|
|
}
|
2010-09-06 08:34:11 -04:00
|
|
|
layer = v->region()->layer ();
|
2013-04-04 00:32:52 -04:00
|
|
|
initial_y = v->get_canvas_group()->position().y;
|
2010-08-23 21:02:40 -04:00
|
|
|
initial_playlist = v->region()->playlist ();
|
2020-11-30 12:59:17 -05:00
|
|
|
initial_position = v->region()->position ();
|
|
|
|
initial_end = v->region()->position () + v->region()->length ();
|
2010-04-06 20:17:54 -04:00
|
|
|
}
|
2010-12-19 22:42:59 -05:00
|
|
|
|
2013-04-04 00:32:52 -04:00
|
|
|
PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
|
|
|
|
: Drag (e, i->canvas_item ())
|
2010-12-19 22:42:59 -05:00
|
|
|
, _region_view (r)
|
2010-12-28 13:19:40 -05:00
|
|
|
, _patch_change (i)
|
2010-12-19 22:42:59 -05:00
|
|
|
, _cumulative_dx (0)
|
|
|
|
{
|
2012-06-12 22:00:51 -04:00
|
|
|
DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
|
2020-10-15 01:09:22 -04:00
|
|
|
_region_view->region()->source_beats_to_absolute_time (_patch_change->patch()->time()),
|
|
|
|
grab_time()));
|
2010-12-19 22:42:59 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-12-28 13:19:40 -05:00
|
|
|
PatchChangeDrag::motion (GdkEvent* ev, bool)
|
2010-12-19 22:42:59 -05:00
|
|
|
{
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t f = adjusted_current_time (ev);
|
2010-12-19 22:42:59 -05:00
|
|
|
boost::shared_ptr<Region> r = _region_view->region ();
|
2020-11-30 12:59:17 -05:00
|
|
|
f = max (f, r->position ());
|
2020-10-15 01:09:22 -04:00
|
|
|
f = min (f, r->nt_last ());
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
timecnt_t const dxf = grab_time().distance (f); // permitted dx
|
|
|
|
double const dxu = _editor->duration_to_pixels (dxf); // permitted fx in units
|
2013-04-04 00:32:52 -04:00
|
|
|
_patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
|
2010-12-19 22:42:59 -05:00
|
|
|
_cumulative_dx = dxu;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-12-28 13:19:40 -05:00
|
|
|
PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
|
2010-12-19 22:42:59 -05:00
|
|
|
{
|
|
|
|
if (!movement_occurred) {
|
2016-05-22 10:19:36 -04:00
|
|
|
if (was_double_click()) {
|
|
|
|
_region_view->edit_patch_change (_patch_change);
|
|
|
|
}
|
2010-12-19 22:42:59 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
boost::shared_ptr<Region> r (_region_view->region ());
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t f = adjusted_current_time (ev);
|
2020-11-30 12:59:17 -05:00
|
|
|
f = max (f, r->position ());
|
2020-10-15 01:09:22 -04:00
|
|
|
f = min (f, r->nt_last ());
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
_region_view->move_patch_change (*_patch_change, _region_view->region()->absolute_time_to_region_beats (f));
|
2010-12-19 22:42:59 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-12-28 14:17:37 -05:00
|
|
|
PatchChangeDrag::aborted (bool)
|
2010-12-19 22:42:59 -05:00
|
|
|
{
|
2013-04-04 00:32:52 -04:00
|
|
|
_patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
|
2010-12-19 22:42:59 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-10-15 01:09:22 -04:00
|
|
|
PatchChangeDrag::setup_pointer_offset ()
|
2010-12-19 22:42:59 -05:00
|
|
|
{
|
|
|
|
boost::shared_ptr<Region> region = _region_view->region ();
|
2020-10-15 01:09:22 -04:00
|
|
|
_pointer_offset = region->source_beats_to_absolute_time (_patch_change->patch()->time()).distance (raw_grab_time());
|
2010-12-19 22:42:59 -05:00
|
|
|
}
|
|
|
|
|
2011-11-16 15:11:33 -05:00
|
|
|
MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
|
2013-04-25 16:05:21 -04:00
|
|
|
: RubberbandSelectDrag (e, rv->get_canvas_group ())
|
2011-11-16 15:11:33 -05:00
|
|
|
, _region_view (rv)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-10-15 01:09:22 -04:00
|
|
|
MidiRubberbandSelectDrag::select_things (int button_state, timepos_t const & x1, timepos_t const & x2, double y1, double y2, bool /*drag_in_progress*/)
|
2011-11-16 15:11:33 -05:00
|
|
|
{
|
|
|
|
_region_view->update_drag_selection (
|
2014-11-16 22:35:37 -05:00
|
|
|
x1, x2, y1, y2,
|
|
|
|
Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
|
2011-11-16 15:11:33 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MidiRubberbandSelectDrag::deselect_things ()
|
|
|
|
{
|
|
|
|
/* XXX */
|
|
|
|
}
|
|
|
|
|
2012-01-19 21:54:23 -05:00
|
|
|
MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
|
2013-04-25 16:05:21 -04:00
|
|
|
: RubberbandSelectDrag (e, rv->get_canvas_group ())
|
2012-01-19 21:54:23 -05:00
|
|
|
, _region_view (rv)
|
|
|
|
{
|
|
|
|
_vertical_only = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-10-15 01:09:22 -04:00
|
|
|
MidiVerticalSelectDrag::select_things (int button_state, timepos_t const & /*x1*/, timepos_t const & /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
|
2012-01-19 21:54:23 -05:00
|
|
|
{
|
|
|
|
double const y = _region_view->midi_view()->y_position ();
|
|
|
|
|
|
|
|
y1 = max (0.0, y1 - y);
|
|
|
|
y2 = max (0.0, y2 - y);
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2012-01-19 21:54:23 -05:00
|
|
|
_region_view->update_vertical_drag_selection (
|
|
|
|
y1,
|
|
|
|
y2,
|
|
|
|
Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MidiVerticalSelectDrag::deselect_things ()
|
|
|
|
{
|
|
|
|
/* XXX */
|
|
|
|
}
|
|
|
|
|
2011-11-16 15:11:33 -05:00
|
|
|
EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
|
|
|
|
: RubberbandSelectDrag (e, i)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-10-15 01:09:22 -04:00
|
|
|
EditorRubberbandSelectDrag::select_things (int button_state, timepos_t const & x1, timepos_t const & x2, double y1, double y2, bool drag_in_progress)
|
2011-11-16 15:11:33 -05:00
|
|
|
{
|
|
|
|
if (drag_in_progress) {
|
|
|
|
/* We just want to select things at the end of the drag, not during it */
|
|
|
|
return;
|
|
|
|
}
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2011-11-16 15:11:33 -05:00
|
|
|
Selection::Operation op = ArdourKeyboard::selection_type (button_state);
|
2014-12-14 08:13:38 -05:00
|
|
|
|
2015-03-25 08:31:23 -04:00
|
|
|
_editor->begin_reversible_selection_op (X_("rubberband selection"));
|
2014-12-14 08:13:38 -05:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
_editor->select_all_within (x1, x2.decrement(), y1, y2, _editor->track_views, op, false);
|
2014-12-14 08:13:38 -05:00
|
|
|
|
2014-12-22 08:30:23 -05:00
|
|
|
_editor->commit_reversible_selection_op ();
|
2011-11-16 15:11:33 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
EditorRubberbandSelectDrag::deselect_things ()
|
|
|
|
{
|
2015-03-25 08:31:23 -04:00
|
|
|
_editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
|
2015-01-10 12:07:31 -05:00
|
|
|
|
|
|
|
_editor->selection->clear_tracks();
|
2011-11-16 15:11:33 -05:00
|
|
|
_editor->selection->clear_regions();
|
|
|
|
_editor->selection->clear_points ();
|
|
|
|
_editor->selection->clear_lines ();
|
2015-01-10 12:07:31 -05:00
|
|
|
_editor->selection->clear_midi_notes ();
|
|
|
|
|
|
|
|
_editor->commit_reversible_selection_op();
|
2011-11-16 15:11:33 -05:00
|
|
|
}
|
2011-12-11 07:54:54 -05:00
|
|
|
|
|
|
|
NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
|
|
|
|
: Drag (e, i)
|
|
|
|
, _region_view (rv)
|
|
|
|
, _drag_rect (0)
|
|
|
|
{
|
2020-12-03 23:34:48 -05:00
|
|
|
_note[0] = _note[1] = timepos_t (Temporal::BeatTime);
|
2011-12-11 07:54:54 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
NoteCreateDrag::~NoteCreateDrag ()
|
|
|
|
{
|
|
|
|
delete _drag_rect;
|
|
|
|
}
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
Temporal::Beats
|
|
|
|
NoteCreateDrag::grid_aligned_beats (timepos_t const & pos, GdkEvent const * event) const
|
2011-12-11 07:54:54 -05:00
|
|
|
{
|
2021-03-15 21:54:02 -04:00
|
|
|
return _editor->snap_to_bbt (pos, RoundNearest, SnapToGrid_Unscaled).beats ();
|
2011-12-11 08:42:18 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
|
|
|
|
{
|
2011-12-11 09:07:24 -05:00
|
|
|
Drag::start_grab (event, cursor);
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2013-04-04 18:45:27 -04:00
|
|
|
_drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
|
2011-12-11 08:42:18 -05:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
const timepos_t pos = _drags->current_pointer_time ();
|
2020-12-18 15:12:41 -05:00
|
|
|
timepos_t aligned_start (grid_aligned_beats (pos, event));
|
|
|
|
const timepos_t grid_beats (_region_view->get_grid_beats (aligned_start));
|
2011-12-11 07:54:54 -05:00
|
|
|
|
2020-12-18 15:12:41 -05:00
|
|
|
_note[0] = aligned_start;
|
2016-11-21 09:59:57 -05:00
|
|
|
/* minimum initial length is grid beats */
|
2021-02-14 18:44:02 -05:00
|
|
|
_note[1] = _note[0] + grid_beats;
|
2020-12-18 15:12:41 -05:00
|
|
|
|
2021-02-14 18:45:49 -05:00
|
|
|
/* the note positions we've just computed are in absolute beats, but
|
|
|
|
* the drag rect is a member of the region view group, so we need
|
|
|
|
* coordinates relative to the region in order to draw it correctly.
|
|
|
|
*/
|
|
|
|
|
2021-02-14 18:44:02 -05:00
|
|
|
const timepos_t rrp1 (_region_view->region()->region_relative_position (_note[0]));
|
|
|
|
const timepos_t rrp2 (_region_view->region()->region_relative_position (_note[1]));
|
2011-12-11 07:54:54 -05:00
|
|
|
|
2021-02-14 18:44:02 -05:00
|
|
|
double const x0 = _editor->time_to_pixel (rrp1);
|
|
|
|
double const x1 = _editor->time_to_pixel (rrp2);
|
2016-11-22 16:04:14 -05:00
|
|
|
double const y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
|
2011-12-11 07:54:54 -05:00
|
|
|
|
2016-11-19 12:49:18 -05:00
|
|
|
_drag_rect->set (ArdourCanvas::Rect (x0, y, x1, y + floor (_region_view->midi_stream_view()->note_height ())));
|
2014-03-11 07:39:25 -04:00
|
|
|
_drag_rect->set_outline_all ();
|
2013-04-04 18:45:27 -04:00
|
|
|
_drag_rect->set_outline_color (0xffffff99);
|
|
|
|
_drag_rect->set_fill_color (0xffffff66);
|
2011-12-11 07:54:54 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
NoteCreateDrag::motion (GdkEvent* event, bool)
|
|
|
|
{
|
2020-10-15 01:09:22 -04:00
|
|
|
const timepos_t pos = _drags->current_pointer_time ();
|
|
|
|
Temporal::Beats aligned_beats = grid_aligned_beats (pos, event);
|
2016-10-25 13:52:09 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
if (_editor->snap_mode() != SnapOff) {
|
2016-11-21 09:59:57 -05:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
const Temporal::Beats grid_beats = _region_view->get_grid_beats (pos);
|
|
|
|
const Temporal::Beats unaligned_beats = pos.beats ();
|
2016-10-25 13:52:09 -04:00
|
|
|
|
|
|
|
/* Hack so that we always snap to the note that we are over, instead of snapping
|
|
|
|
to the next one if we're more than halfway through the one we're over.
|
|
|
|
*/
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
const Temporal::Beats rem = aligned_beats - unaligned_beats;
|
|
|
|
|
|
|
|
if (rem >= std::numeric_limits<Temporal::Beats>::lowest()) {
|
|
|
|
aligned_beats -= grid_beats;
|
2016-10-25 13:52:09 -04:00
|
|
|
}
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
aligned_beats += grid_beats;
|
2016-10-25 13:52:09 -04:00
|
|
|
}
|
|
|
|
|
2020-12-18 15:12:41 -05:00
|
|
|
_note[1] = timepos_t (max (Temporal::Beats(), aligned_beats));
|
|
|
|
|
2021-02-14 18:44:02 -05:00
|
|
|
const timepos_t rrp1 (_region_view->region()->region_relative_position (_note[0]));
|
|
|
|
const timepos_t rrp2 (_region_view->region()->region_relative_position (_note[1]));
|
2020-10-15 01:09:22 -04:00
|
|
|
|
2021-02-14 18:44:02 -05:00
|
|
|
double const x0 = _editor->time_to_pixel (rrp1);
|
|
|
|
double const x1 = _editor->time_to_pixel (rrp2);
|
2014-11-14 00:33:05 -05:00
|
|
|
_drag_rect->set_x0 (std::min(x0, x1));
|
|
|
|
_drag_rect->set_x1 (std::max(x0, x1));
|
2011-12-11 07:54:54 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2016-08-30 13:30:09 -04:00
|
|
|
NoteCreateDrag::finished (GdkEvent* ev, bool had_movement)
|
2011-12-11 07:54:54 -05:00
|
|
|
{
|
2016-11-19 12:49:18 -05:00
|
|
|
/* we create a note even if there was no movement */
|
2020-12-18 15:12:41 -05:00
|
|
|
|
|
|
|
/* Compute start within region, rather than absolute time start */
|
|
|
|
|
|
|
|
Beats const start = _region_view->region()->absolute_time_to_region_beats (min (_note[0], _note[1]));
|
|
|
|
Beats length = max (Beats (0, 1), (_note[0].distance (_note[1]).abs().beats()));
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
int32_t div = _editor->get_grid_music_divisions (ev->button.state);
|
2011-12-11 09:07:24 -05:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
if (div > 0) {
|
|
|
|
length = length.round_to_subdivision (div, RoundUpMaybe);
|
|
|
|
}
|
2011-12-30 08:41:16 -05:00
|
|
|
|
2017-03-04 10:06:49 -05:00
|
|
|
_editor->begin_reversible_command (_("Create Note"));
|
2020-12-18 15:12:41 -05:00
|
|
|
_region_view->create_note_at (timepos_t (start), _drag_rect->y0(), length, ev->button.state, false);
|
2017-03-04 10:06:49 -05:00
|
|
|
_editor->commit_reversible_command ();
|
2011-12-11 07:54:54 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
double
|
|
|
|
NoteCreateDrag::y_to_region (double y) const
|
|
|
|
{
|
|
|
|
double x = 0;
|
2013-04-04 18:45:27 -04:00
|
|
|
_region_view->get_canvas_group()->canvas_to_item (x, y);
|
2011-12-11 07:54:54 -05:00
|
|
|
return y;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
NoteCreateDrag::aborted (bool)
|
|
|
|
{
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2011-12-11 07:54:54 -05:00
|
|
|
}
|
2012-05-09 19:04:18 -04:00
|
|
|
|
2016-11-20 12:20:27 -05:00
|
|
|
HitCreateDrag::HitCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
|
2016-11-20 12:02:21 -05:00
|
|
|
: Drag (e, i)
|
|
|
|
, _region_view (rv)
|
2020-12-18 15:12:41 -05:00
|
|
|
, _last_pos (Temporal::Beats())
|
2017-03-04 09:57:10 -05:00
|
|
|
, _y (0.0)
|
2016-11-20 12:02:21 -05:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2016-11-20 12:20:27 -05:00
|
|
|
HitCreateDrag::~HitCreateDrag ()
|
2016-11-20 12:02:21 -05:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2016-11-20 12:20:27 -05:00
|
|
|
HitCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
|
2016-11-20 12:02:21 -05:00
|
|
|
{
|
|
|
|
Drag::start_grab (event, cursor);
|
|
|
|
|
2017-03-04 09:57:10 -05:00
|
|
|
_y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
const timepos_t pos = _drags->current_pointer_time ();
|
2016-11-20 12:02:21 -05:00
|
|
|
const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
const Beats beats = pos.beats ();
|
2016-11-21 10:05:32 -05:00
|
|
|
|
|
|
|
boost::shared_ptr<MidiRegion> mr = _region_view->midi_region();
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
if (beats >= mr->nt_last().beats()) {
|
2016-11-21 10:05:32 -05:00
|
|
|
return;
|
2016-11-20 12:02:21 -05:00
|
|
|
}
|
|
|
|
|
2020-11-30 12:59:17 -05:00
|
|
|
const Temporal::Beats start = beats - _region_view->region()->position().beats ();
|
2020-10-15 01:09:22 -04:00
|
|
|
Temporal::Beats length = _region_view->get_grid_beats (pos);
|
2016-11-21 10:05:32 -05:00
|
|
|
|
2017-03-04 09:50:04 -05:00
|
|
|
_editor->begin_reversible_command (_("Create Hit"));
|
2020-04-16 14:14:42 -04:00
|
|
|
_region_view->clear_note_selection();
|
2020-12-18 15:12:41 -05:00
|
|
|
_region_view->create_note_at (timepos_t (start), _y, length, event->button.state, false);
|
2016-11-21 10:05:32 -05:00
|
|
|
|
2020-12-18 15:12:41 -05:00
|
|
|
_last_pos = timepos_t (start);
|
2016-11-20 12:02:21 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2016-11-20 12:20:27 -05:00
|
|
|
HitCreateDrag::motion (GdkEvent* event, bool)
|
2016-11-20 12:02:21 -05:00
|
|
|
{
|
2020-10-15 01:09:22 -04:00
|
|
|
const timepos_t pos = _drags->current_pointer_time ();
|
2016-11-20 12:02:21 -05:00
|
|
|
const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
|
2016-11-20 12:11:54 -05:00
|
|
|
|
|
|
|
if (divisions == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
const Beats beats = pos.beats ();
|
2020-11-30 12:59:17 -05:00
|
|
|
const Temporal::Beats start = beats - _region_view->region()->position ().beats();
|
2016-11-20 12:02:21 -05:00
|
|
|
|
2017-03-04 09:57:10 -05:00
|
|
|
if (_last_pos == start) {
|
2016-11-21 10:05:32 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
Temporal::Beats length = _region_view->get_grid_beats (pos);
|
2016-11-21 10:05:32 -05:00
|
|
|
|
|
|
|
boost::shared_ptr<MidiRegion> mr = _region_view->midi_region();
|
2017-03-04 09:57:10 -05:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
if (beats > mr->nt_last().beats()) {
|
2016-11-21 10:05:32 -05:00
|
|
|
return;
|
2016-11-20 12:02:21 -05:00
|
|
|
}
|
|
|
|
|
2020-12-18 15:12:41 -05:00
|
|
|
_region_view->create_note_at (timepos_t (start), _y, length, event->button.state, false);
|
2016-11-20 12:02:21 -05:00
|
|
|
|
2020-12-18 15:12:41 -05:00
|
|
|
_last_pos = timepos_t (start);
|
2016-11-20 12:02:21 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2016-11-20 12:20:27 -05:00
|
|
|
HitCreateDrag::finished (GdkEvent* /* ev */, bool /* had_movement */)
|
2016-11-20 12:02:21 -05:00
|
|
|
{
|
2017-03-04 09:50:04 -05:00
|
|
|
_editor->commit_reversible_command ();
|
2016-11-20 12:02:21 -05:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
double
|
2016-11-20 12:20:27 -05:00
|
|
|
HitCreateDrag::y_to_region (double y) const
|
2016-11-20 12:02:21 -05:00
|
|
|
{
|
|
|
|
double x = 0;
|
|
|
|
_region_view->get_canvas_group()->canvas_to_item (x, y);
|
|
|
|
return y;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2016-11-20 12:20:27 -05:00
|
|
|
HitCreateDrag::aborted (bool)
|
2016-11-20 12:02:21 -05:00
|
|
|
{
|
|
|
|
// umm..
|
|
|
|
}
|
|
|
|
|
2012-05-09 19:04:18 -04:00
|
|
|
CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
|
|
|
|
: Drag (e, i)
|
|
|
|
, arv (rv)
|
|
|
|
, start (start_yn)
|
|
|
|
{
|
2012-12-13 11:42:05 -05:00
|
|
|
std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
|
2012-05-09 19:04:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
|
|
|
|
{
|
|
|
|
Drag::start_grab (event, cursor);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CrossfadeEdgeDrag::motion (GdkEvent*, bool)
|
|
|
|
{
|
2012-05-09 22:50:59 -04:00
|
|
|
double distance;
|
2020-10-15 01:09:22 -04:00
|
|
|
timecnt_t new_length;
|
|
|
|
timecnt_t len;
|
2012-05-09 20:46:22 -04:00
|
|
|
|
|
|
|
boost::shared_ptr<AudioRegion> ar (arv->audio_region());
|
|
|
|
|
|
|
|
if (start) {
|
2012-05-09 22:50:59 -04:00
|
|
|
distance = _drags->current_pointer_x() - grab_x();
|
2020-10-15 01:09:22 -04:00
|
|
|
len = timecnt_t (ar->fade_in()->back()->when);
|
2012-05-09 20:46:22 -04:00
|
|
|
} else {
|
2012-05-09 22:50:59 -04:00
|
|
|
distance = grab_x() - _drags->current_pointer_x();
|
2020-10-15 01:09:22 -04:00
|
|
|
len = timecnt_t (ar->fade_out()->back()->when);
|
2012-05-09 20:46:22 -04:00
|
|
|
}
|
|
|
|
|
2012-05-10 08:14:26 -04:00
|
|
|
/* how long should it be ? */
|
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
new_length = len + timecnt_t (_editor->pixel_to_sample (distance));
|
2012-05-10 08:14:26 -04:00
|
|
|
|
|
|
|
/* now check with the region that this is legal */
|
|
|
|
|
2020-12-06 14:57:48 -05:00
|
|
|
new_length = timecnt_t (ar->verify_xfade_bounds (new_length.samples(), start));
|
2012-05-10 08:14:26 -04:00
|
|
|
|
2012-05-09 20:46:22 -04:00
|
|
|
if (start) {
|
2020-10-15 01:09:22 -04:00
|
|
|
arv->reset_fade_in_shape_width (ar, new_length.samples());
|
2012-05-09 20:46:22 -04:00
|
|
|
} else {
|
2020-10-15 01:09:22 -04:00
|
|
|
arv->reset_fade_out_shape_width (ar, new_length.samples());
|
2012-05-09 20:46:22 -04:00
|
|
|
}
|
2012-05-09 19:04:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CrossfadeEdgeDrag::finished (GdkEvent*, bool)
|
|
|
|
{
|
2012-05-09 22:50:59 -04:00
|
|
|
double distance;
|
2020-10-15 01:09:22 -04:00
|
|
|
timecnt_t new_length;
|
|
|
|
timecnt_t len;
|
2012-05-09 20:46:22 -04:00
|
|
|
|
|
|
|
boost::shared_ptr<AudioRegion> ar (arv->audio_region());
|
|
|
|
|
|
|
|
if (start) {
|
2012-05-09 22:50:59 -04:00
|
|
|
distance = _drags->current_pointer_x() - grab_x();
|
2020-10-15 01:09:22 -04:00
|
|
|
len = timecnt_t (ar->fade_in()->back()->when);
|
2012-05-09 20:46:22 -04:00
|
|
|
} else {
|
2012-05-09 22:50:59 -04:00
|
|
|
distance = grab_x() - _drags->current_pointer_x();
|
2020-10-15 01:09:22 -04:00
|
|
|
len = timecnt_t (ar->fade_out()->back()->when);
|
2012-05-09 20:46:22 -04:00
|
|
|
}
|
|
|
|
|
2020-12-06 14:57:48 -05:00
|
|
|
samplecnt_t samples = _editor->pixel_to_sample (distance);
|
|
|
|
timecnt_t tdist = timecnt_t (samples);
|
|
|
|
timecnt_t newlen = len + tdist;
|
|
|
|
new_length = timecnt_t (ar->verify_xfade_bounds (newlen.samples(), start));
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2012-05-10 14:36:05 -04:00
|
|
|
_editor->begin_reversible_command ("xfade trim");
|
2015-03-24 16:59:57 -04:00
|
|
|
ar->playlist()->clear_owned_changes ();
|
2012-05-10 14:36:05 -04:00
|
|
|
|
2012-05-09 20:46:22 -04:00
|
|
|
if (start) {
|
2020-10-15 01:09:22 -04:00
|
|
|
ar->set_fade_in_length (new_length.samples());
|
2012-05-09 20:46:22 -04:00
|
|
|
} else {
|
2020-10-15 01:09:22 -04:00
|
|
|
ar->set_fade_out_length (new_length.samples());
|
2012-05-09 20:46:22 -04:00
|
|
|
}
|
2012-05-10 14:36:05 -04:00
|
|
|
|
|
|
|
/* Adjusting the xfade may affect other regions in the playlist, so we need
|
|
|
|
to get undo Commands from the whole playlist rather than just the
|
|
|
|
region.
|
|
|
|
*/
|
|
|
|
|
|
|
|
vector<Command*> cmds;
|
|
|
|
ar->playlist()->rdiff (cmds);
|
|
|
|
_editor->session()->add_commands (cmds);
|
2012-06-09 09:25:13 -04:00
|
|
|
_editor->commit_reversible_command ();
|
2012-05-10 14:36:05 -04:00
|
|
|
|
2012-05-09 19:04:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CrossfadeEdgeDrag::aborted (bool)
|
|
|
|
{
|
2012-05-09 20:46:22 -04:00
|
|
|
if (start) {
|
2015-02-05 20:26:02 -05:00
|
|
|
// arv->redraw_start_xfade ();
|
2012-05-09 20:46:22 -04:00
|
|
|
} else {
|
2015-02-05 20:26:02 -05:00
|
|
|
// arv->redraw_end_xfade ();
|
2012-05-09 20:46:22 -04:00
|
|
|
}
|
2012-05-09 19:04:18 -04:00
|
|
|
}
|
|
|
|
|
2017-09-18 12:39:17 -04:00
|
|
|
RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, samplepos_t pos)
|
2014-07-07 07:05:27 -04:00
|
|
|
: Drag (e, item, true)
|
|
|
|
{
|
2014-07-07 08:26:37 -04:00
|
|
|
}
|
2014-07-07 07:05:27 -04:00
|
|
|
|
2014-07-07 08:26:37 -04:00
|
|
|
RegionCutDrag::~RegionCutDrag ()
|
|
|
|
{
|
2014-07-07 07:05:27 -04:00
|
|
|
}
|
|
|
|
|
2016-11-21 05:45:35 -05:00
|
|
|
void
|
|
|
|
RegionCutDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
|
|
|
|
{
|
|
|
|
Drag::start_grab (event, c);
|
|
|
|
motion (event, false);
|
|
|
|
}
|
|
|
|
|
2014-07-07 07:05:27 -04:00
|
|
|
void
|
2016-11-21 07:19:56 -05:00
|
|
|
RegionCutDrag::motion (GdkEvent* event, bool)
|
2014-07-07 07:05:27 -04:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2016-06-15 10:18:27 -04:00
|
|
|
RegionCutDrag::finished (GdkEvent* event, bool)
|
2014-07-07 07:05:27 -04:00
|
|
|
{
|
2014-07-08 21:37:49 -04:00
|
|
|
_editor->get_track_canvas()->canvas()->re_enter();
|
2014-07-07 10:21:47 -04:00
|
|
|
|
2015-03-24 16:59:57 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
timepos_t pos (_drags->current_pointer_time());
|
2017-01-26 08:41:17 -05:00
|
|
|
_editor->snap_to_with_modifier (pos, event);
|
2014-07-07 10:13:19 -04:00
|
|
|
|
2020-10-15 01:09:22 -04:00
|
|
|
RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
|
2014-07-07 10:13:19 -04:00
|
|
|
|
|
|
|
if (rs.empty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-02-27 09:55:17 -05:00
|
|
|
_editor->split_regions_at (pos, rs);
|
2014-07-07 07:05:27 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
RegionCutDrag::aborted (bool)
|
|
|
|
{
|
|
|
|
}
|
2021-05-24 11:12:19 -04:00
|
|
|
|
|
|
|
RegionMarkerDrag::RegionMarkerDrag (Editor* ed, RegionView* r, ArdourCanvas::Item* i)
|
|
|
|
: Drag (ed, i)
|
|
|
|
, rv (r)
|
|
|
|
, view (static_cast<ArdourMarker*> (i->get_data ("marker")))
|
|
|
|
, model (rv->find_model_cue_marker (view))
|
2021-05-24 14:28:19 -04:00
|
|
|
, dragging_model (model)
|
2021-05-24 11:12:19 -04:00
|
|
|
{
|
|
|
|
assert (view);
|
|
|
|
}
|
|
|
|
|
|
|
|
RegionMarkerDrag::~RegionMarkerDrag ()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2021-05-24 14:28:19 -04:00
|
|
|
RegionMarkerDrag::start_grab (GdkEvent* ev, Gdk::Cursor* c)
|
2021-05-24 11:12:19 -04:00
|
|
|
{
|
2021-05-24 14:28:19 -04:00
|
|
|
Drag::start_grab (ev, c);
|
|
|
|
show_verbose_cursor_time (model.position());
|
|
|
|
setup_snap_delta (MusicSample (model.position(), 0));
|
2021-05-24 11:12:19 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2021-05-24 14:28:19 -04:00
|
|
|
RegionMarkerDrag::motion (GdkEvent* ev, bool first_move)
|
2021-05-24 11:12:19 -04:00
|
|
|
{
|
2021-05-24 14:28:19 -04:00
|
|
|
samplepos_t pos = adjusted_current_sample (ev);
|
|
|
|
|
|
|
|
if (pos < rv->region()->position() || pos >= (rv->region()->position() + rv->region()->length())) {
|
|
|
|
/* out of bounds */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-05-25 13:42:16 -04:00
|
|
|
dragging_model.set_position (pos - rv->region()->position());
|
|
|
|
/* view (ArdourMarker) needs a relative position inside the RegionView */
|
2021-05-24 14:28:19 -04:00
|
|
|
view->set_position (pos - rv->region()->position());
|
|
|
|
show_verbose_cursor_time (dragging_model.position() - rv->region()->position()); /* earlier */
|
2021-05-24 11:12:19 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2021-05-24 14:28:19 -04:00
|
|
|
RegionMarkerDrag::finished (GdkEvent *, bool did_move)
|
2021-05-24 11:12:19 -04:00
|
|
|
{
|
2021-05-24 14:28:19 -04:00
|
|
|
if (did_move) {
|
|
|
|
rv->region()->move_cue_marker (model, dragging_model.position());
|
2021-05-24 22:22:32 -04:00
|
|
|
} else if (was_double_click()) {
|
|
|
|
/* edit name */
|
|
|
|
|
|
|
|
ArdourDialog d (_("Edit Cue Marker Name"), true, false);
|
|
|
|
Gtk::Entry e;
|
|
|
|
d.get_vbox()->pack_start (e);
|
|
|
|
e.set_text (model.text());
|
|
|
|
e.select_region (0, -1);
|
|
|
|
e.show ();
|
|
|
|
e.set_activates_default ();
|
|
|
|
|
|
|
|
d.add_button (Stock::CANCEL, RESPONSE_CANCEL);
|
|
|
|
d.add_button (Stock::OK, RESPONSE_OK);
|
|
|
|
d.set_default_response (RESPONSE_OK);
|
|
|
|
d.set_position (WIN_POS_MOUSE);
|
|
|
|
|
|
|
|
int result = d.run();
|
|
|
|
string str = e.get_text();
|
|
|
|
|
|
|
|
if (result == RESPONSE_OK && !str.empty()) {
|
|
|
|
/* explicitly remove the existing (GUI) marker, because
|
|
|
|
we will not find a match when handing the
|
|
|
|
CueMarkersChanged signal.
|
|
|
|
*/
|
|
|
|
rv->drop_cue_marker (view);
|
|
|
|
rv->region()->rename_cue_marker (model, str);
|
|
|
|
}
|
2021-05-24 14:28:19 -04:00
|
|
|
}
|
2021-05-24 11:12:19 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
RegionMarkerDrag::aborted (bool)
|
|
|
|
{
|
2021-05-24 14:28:19 -04:00
|
|
|
view->set_position (model.position());
|
2021-05-24 11:12:19 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
RegionMarkerDrag::setup_pointer_sample_offset ()
|
|
|
|
{
|
2021-05-24 14:28:19 -04:00
|
|
|
const samplepos_t model_abs_pos = rv->region()->position() + (model.position() - rv->region()->start()); /* distance */
|
|
|
|
_pointer_sample_offset = raw_grab_sample() - model_abs_pos; /* distance */
|
2021-05-24 11:12:19 -04:00
|
|
|
}
|