ardour
editor_drag.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 Paul Davis
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or
7  (at your option) any later version.
8 
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with this program; if not, write to the Free Software
16  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 
18 */
19 
20 #ifdef WAF_BUILD
21 #include "gtk2ardour-config.h"
22 #endif
23 
24 #include <stdint.h>
25 #include <algorithm>
26 
27 #include "pbd/memento_command.h"
28 #include "pbd/basename.h"
30 
31 #include "gtkmm2ext/utils.h"
32 
33 #include "ardour/audioengine.h"
34 #include "ardour/audioregion.h"
35 #include "ardour/audio_track.h"
36 #include "ardour/dB.h"
37 #include "ardour/midi_region.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/operations.h"
40 #include "ardour/region_factory.h"
41 #include "ardour/session.h"
42 
43 #include "canvas/canvas.h"
44 #include "canvas/scroll_group.h"
45 
46 #include "editor.h"
47 #include "i18n.h"
48 #include "keyboard.h"
49 #include "audio_region_view.h"
50 #include "automation_region_view.h"
51 #include "midi_region_view.h"
52 #include "ardour_ui.h"
53 #include "gui_thread.h"
54 #include "control_point.h"
55 #include "region_gain_line.h"
56 #include "editor_drag.h"
57 #include "audio_time_axis.h"
58 #include "midi_time_axis.h"
59 #include "selection.h"
60 #include "midi_selection.h"
61 #include "automation_time_axis.h"
62 #include "debug.h"
63 #include "editor_cursors.h"
64 #include "mouse_cursors.h"
65 #include "note_base.h"
66 #include "patch_change.h"
67 #include "verbose_cursor.h"
68 
69 using namespace std;
70 using namespace ARDOUR;
71 using namespace PBD;
72 using namespace Gtk;
73 using namespace Gtkmm2ext;
74 using namespace Editing;
75 using namespace ArdourCanvas;
76 
78 
80 
82  : _editor (e)
83  , _ending (false)
84  , _current_pointer_frame (0)
85 {
86 }
87 
89 {
90  abort ();
91 }
92 
94 void
96 {
97  _ending = true;
98 
99  cerr << "Aborting drag\n";
100 
101  for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
102  (*i)->abort ();
103  delete *i;
104  }
105 
106  if (!_drags.empty ()) {
108  }
109 
110  _drags.clear ();
112 
113  _ending = false;
114 }
115 
116 void
118 {
119  d->set_manager (this);
120  _drags.push_back (d);
121 }
122 
123 void
124 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
125 {
126  d->set_manager (this);
127  _drags.push_back (d);
128  start_grab (e, c);
129 }
130 
131 void
132 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
133 {
134  /* Prevent follow playhead during the drag to be nice to the user */
136  _editor->set_follow_playhead (false);
137 
139 
140  for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
141  (*i)->start_grab (e, c);
142  }
143 }
144 
148 bool
150 {
151  _ending = true;
152 
153  bool r = false;
154  for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
155  bool const t = (*i)->end_grab (e);
156  if (t) {
157  r = true;
158  }
159  delete *i;
160  }
161 
162  _drags.clear ();
163 
164  _ending = false;
165 
167 
168  return r;
169 }
170 
171 void
173 {
174  for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
175  (*i)->set_double_click (true);
176  }
177 }
178 
179 bool
180 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
181 {
182  bool r = false;
183 
184  /* calling this implies that we expect the event to have canvas
185  * coordinates
186  *
187  * Can we guarantee that this is true?
188  */
189 
191 
192  for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
193  bool const t = (*i)->motion_handler (e, from_autoscroll);
194  /* run all handlers; return true if at least one of them
195  returns true (indicating that the event has been handled).
196  */
197  if (t) {
198  r = true;
199  }
200 
201  }
202 
203  return r;
204 }
205 
206 bool
207 DragManager::have_item (ArdourCanvas::Item* i) const
208 {
209  list<Drag*>::const_iterator j = _drags.begin ();
210  while (j != _drags.end() && (*j)->item () != i) {
211  ++j;
212  }
213 
214  return j != _drags.end ();
215 }
216 
217 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
218  : _editor (e)
219  , _item (i)
220  , _pointer_frame_offset (0)
221  , _trackview_only (trackview_only)
222  , _move_threshold_passed (false)
223  , _starting_point_passed (false)
224  , _initially_vertical (false)
225  , _was_double_click (false)
226  , _raw_grab_frame (0)
227  , _grab_frame (0)
228  , _last_pointer_frame (0)
229 {
230 
231 }
232 
233 void
234 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
235 {
236  _item->ungrab ();
237  _item = new_item;
238 
239  if (!_cursor_ctx) {
241  } else {
242  _cursor_ctx->change (cursor);
243  }
244 
245  _item->grab ();
246 }
247 
248 void
249 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
250 {
251  // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
252 
253  if (Keyboard::is_button2_event (&event->button)) {
254  if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
255  _y_constrained = true;
256  _x_constrained = false;
257  } else {
258  _y_constrained = false;
259  _x_constrained = true;
260  }
261  } else {
262  _x_constrained = false;
263  _y_constrained = false;
264  }
265 
271 
272  if (_trackview_only) {
273  _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
274  }
275 
277 
278  _item->grab ();
279 
280  if (!_editor->cursors()->is_invalid (cursor)) {
281  /* CAIROCANVAS need a variant here that passes *cursor */
283  }
284 
285  if (_editor->session() && _editor->session()->transport_rolling()) {
286  _was_rolling = true;
287  } else {
288  _was_rolling = false;
289  }
290 
291  switch (_editor->snap_type()) {
292  case SnapToRegionStart:
293  case SnapToRegionEnd:
294  case SnapToRegionSync:
295  case SnapToRegionBoundary:
297  break;
298  default:
299  break;
300  }
301 }
302 
309 bool
310 Drag::end_grab (GdkEvent* event)
311 {
313 
314  _item->ungrab ();
315 
317 
318  _editor->verbose_cursor()->hide ();
319  _cursor_ctx.reset();
320 
321  return _move_threshold_passed;
322 }
323 
325 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
326 {
327  framepos_t pos = 0;
328 
329  if (f > _pointer_frame_offset) {
330  pos = f - _pointer_frame_offset;
331  }
332 
333  if (snap) {
334  _editor->snap_to_with_modifier (pos, event);
335  }
336 
337  return pos;
338 }
339 
341 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
342 {
343  return adjusted_frame (_drags->current_pointer_frame (), event, snap);
344 }
345 
346 double
348 {
349  return _drags->current_pointer_x ();
350 }
351 
352 double
354 {
355  if (!_trackview_only) {
356  return _drags->current_pointer_y ();
357  }
358 
359  return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
360 }
361 
362 bool
363 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
364 {
365  /* check to see if we have moved in any way that matters since the last motion event */
369  return false;
370  }
371 
372  pair<framecnt_t, int> const threshold = move_threshold ();
373 
374  bool const old_move_threshold_passed = _move_threshold_passed;
375 
376  if (!_move_threshold_passed) {
377 
378  bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
379  bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
380 
382  }
383 
385 
386  if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
387 
388  if (old_move_threshold_passed != _move_threshold_passed) {
389 
390  /* just changed */
391 
392  if (fabs (current_pointer_y() - _grab_y) > fabs (current_pointer_x() - _grab_x)) {
393  _initially_vertical = true;
394  } else {
395  _initially_vertical = false;
396  }
397  }
398 
399  if (!from_autoscroll) {
401  }
402 
403  if (!_editor->autoscroll_active() || from_autoscroll) {
404 
405 
406  bool first_move = (_move_threshold_passed != old_move_threshold_passed) || from_autoscroll;
407 
408  motion (event, first_move && !_starting_point_passed);
409 
410  if (first_move && !_starting_point_passed) {
411  _starting_point_passed = true;
412  }
413 
417  }
418 
419  return true;
420  }
421  }
422 
423  return false;
424 }
425 
427 void
429 {
430  if (_item) {
431  _item->ungrab ();
432  }
433 
435 
437  _editor->verbose_cursor()->hide ();
438 }
439 
440 void
442 {
443  _editor->verbose_cursor()->set_time (frame);
444  _editor->verbose_cursor()->show ();
445 }
446 
447 void
449 {
450  _editor->verbose_cursor()->set_duration (start, end);
451  _editor->verbose_cursor()->show ();
452 }
453 
454 void
455 Drag::show_verbose_cursor_text (string const & text)
456 {
457  _editor->verbose_cursor()->set (text);
458  _editor->verbose_cursor()->show ();
459 }
460 
463 {
464  if (_editor->session()) {
465  const TempoMap& map (_editor->session()->tempo_map());
466  framecnt_t pos = grab_frame();
467  const Meter& m = map.meter_at (pos);
468  /* not that the frame rate used here can be affected by pull up/down which
469  might be wrong.
470  */
471  framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
472  return view->add_region (grab_frame(), len, true);
473  }
474 
475  return boost::shared_ptr<Region>();
476 }
477 
480  RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
481  RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
482  assert (ra && rb);
483  return ra->route()->order_key () < rb->route()->order_key ();
484  }
485 };
486 
487 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
488  : Drag (e, i)
489  , _primary (p)
490  , _ntracks (0)
491 {
493 
494  /* Make a list of tracks to refer to during the drag; we include hidden tracks,
495  as some of the regions we are dragging may be on such tracks.
496  */
497 
498  TrackViewList track_views = _editor->track_views;
499  track_views.sort (EditorOrderTimeAxisViewSorter ());
500 
501  for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
502  _time_axis_views.push_back (*i);
503 
504  TimeAxisView::Children children_list = (*i)->get_child_list ();
505  for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
506  _time_axis_views.push_back (j->get());
507  }
508  }
509 
510  /* the list of views can be empty at this point if this is a region list-insert drag
511  */
512 
513  for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
514  _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
515  }
516 
518 }
519 
520 void
522 {
523  list<DraggingView>::iterator i = _views.begin ();
524  while (i != _views.end() && i->view != v) {
525  ++i;
526  }
527 
528  if (i != _views.end()) {
529  _views.erase (i);
530  }
531 }
532 
536 int
538 {
539  int i = 0;
540  int const N = _time_axis_views.size ();
541  while (i < N && _time_axis_views[i] != t) {
542  ++i;
543  }
544 
545  if (_time_axis_views[i] != t) {
546  return -1;
547  }
548 
549  return i;
550 }
551 
552 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
553  : RegionDrag (e, i, p, v)
554  , _brushing (b)
555  , _total_x_delta (0)
556  , _last_pointer_time_axis_view (0)
557  , _last_pointer_layer (0)
558  , _single_axis (false)
559  , _ndropzone (0)
560  , _pdropzone (0)
561  , _ddropzone (0)
562 {
563  DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
564 }
565 
566 void
567 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
568 {
569  Drag::start_grab (event, cursor);
570 
571  if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
572  _single_axis = true;
573  }
574 
576 
577  pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
578  if (tv.first) {
580  assert(_last_pointer_time_axis_view >= 0);
581  _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
582  }
583 }
584 
585 double
586 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
587 {
588  /* compute the amount of pointer motion in frames, and where
589  the region would be if we moved it by that much.
590  */
591  *pending_region_position = adjusted_current_frame (event);
592 
593  framepos_t sync_frame;
594  framecnt_t sync_offset;
595  int32_t sync_dir;
596 
597  sync_offset = _primary->region()->sync_offset (sync_dir);
598 
599  /* we don't handle a sync point that lies before zero.
600  */
601  if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
602 
603  sync_frame = *pending_region_position + (sync_dir*sync_offset);
604 
605  _editor->snap_to_with_modifier (sync_frame, event);
606 
607  *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
608 
609  } else {
610  *pending_region_position = _last_frame_position;
611  }
612 
613  if (*pending_region_position > max_framepos - _primary->region()->length()) {
614  *pending_region_position = _last_frame_position;
615  }
616 
617  double dx = 0;
618 
619  /* in locked edit mode, reverse the usual meaning of _x_constrained */
620  bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
621 
622  if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
623 
624  /* x movement since last time (in pixels) */
625  dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
626 
627  /* total x movement */
628  framecnt_t total_dx = *pending_region_position;
629  if (regions_came_from_canvas()) {
630  total_dx = total_dx - grab_frame ();
631  }
632 
633  /* check that no regions have gone off the start of the session */
634  for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
635  if ((i->view->region()->position() + total_dx) < 0) {
636  dx = 0;
637  *pending_region_position = _last_frame_position;
638  break;
639  }
640  }
641 
642  }
643 
644  return dx;
645 }
646 
647 int
648 RegionDrag::apply_track_delta (const int start, const int delta, const int skip, const bool distance_only) const
649 {
650  if (delta == 0) {
651  return start;
652  }
653 
654  const int tavsize = _time_axis_views.size();
655  const int dt = delta > 0 ? +1 : -1;
656  int current = start;
657  int target = start + delta - skip;
658 
659  assert (current < 0 || current >= tavsize || !_time_axis_views[current]->hidden());
660  assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
661 
662  while (current >= 0 && current != target) {
663  current += dt;
664  if (current < 0 && dt < 0) {
665  break;
666  }
667  if (current >= tavsize && dt > 0) {
668  break;
669  }
670  if (current < 0 || current >= tavsize) {
671  continue;
672  }
673 
674  RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
675  if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
676  target += dt;
677  }
678 
679  if (distance_only && current == start + delta) {
680  break;
681  }
682  }
683  return target;
684 }
685 
686 bool
687 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
688 {
689  if (_y_constrained) {
690  return false;
691  }
692 
693  const int tavsize = _time_axis_views.size();
694  for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
695  int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
696  assert (n < 0 || n >= tavsize || !_time_axis_views[n]->hidden());
697 
698  if (i->time_axis_view < 0 || i->time_axis_view >= tavsize) {
699  /* already in the drop zone */
700  if (delta_track >= 0) {
701  /* downward motion - OK if others are still not in the dropzone */
702  continue;
703  }
704 
705  }
706 
707  if (n < 0) {
708  /* off the top */
709  return false;
710  } else if (n >= tavsize) {
711  /* downward motion into drop zone. That's fine. */
712  continue;
713  }
714 
715  RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
716  if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
717  /* not a track, or the wrong type */
718  return false;
719  }
720 
721  double const l = i->layer + delta_layer;
722 
723  /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
724  mode to allow the user to place a region below another on layer 0.
725  */
726  if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
727  /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
728  If it has, the layers will be munged later anyway, so it's ok.
729  */
730  return false;
731  }
732  }
733 
734  /* all regions being dragged are ok with this change */
735  return true;
736 }
737 
739  bool operator() (const DraggingView& a, const DraggingView& b) {
740  return a.time_axis_view < b.time_axis_view;
741  }
742 };
743 
744 void
745 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
746 {
747  double delta_layer = 0;
748  int delta_time_axis_view = 0;
749  int current_pointer_time_axis_view = -1;
750 
751  assert (!_views.empty ());
752 
753  if (first_move) {
754  if (_single_axis) {
755  if (initially_vertical()) {
756  _y_constrained = false;
757  _x_constrained = true;
758  } else {
759  _y_constrained = true;
760  _x_constrained = false;
761  }
762  }
763  }
764 
765  /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
766 
767  /* Find the TimeAxisView that the pointer is now over */
768  const double cur_y = current_pointer_y ();
769  pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
770  TimeAxisView* tv = r.first;
771 
772  if (!tv && cur_y < 0) {
773  /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
774  return;
775  }
776 
777  /* find drop-zone y-position */
778  Coord last_track_bottom_edge;
779  last_track_bottom_edge = 0;
780  for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
781  if (!(*t)->hidden()) {
782  last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
783  break;
784  }
785  }
786 
787  if (tv && tv->view()) {
788  /* the mouse is over a track */
789  double layer = r.second;
790 
791  if (first_move && tv->view()->layer_display() == Stacked) {
793  }
794 
795  /* Here's the current pointer position in terms of time axis view and layer */
796  current_pointer_time_axis_view = find_time_axis_view (tv);
797  assert(current_pointer_time_axis_view >= 0);
798 
799  double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
800 
801  /* Work out the change in y */
802 
803  RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
804  if (!rtv || !rtv->is_track()) {
805  /* ignore busses early on. we can't move any regions on them */
806  } else if (_last_pointer_time_axis_view < 0) {
807  /* Was in the drop-zone, now over a track.
808  * Hence it must be an upward move (from the bottom)
809  *
810  * track_index is still -1, so delta must be set to
811  * move up the correct number of tracks from the bottom.
812  *
813  * This is necessary because steps may be skipped if
814  * the bottom-most track is not a valid target and/or
815  * if there are hidden tracks at the bottom.
816  * Hence the initial offset (_ddropzone) as well as the
817  * last valid pointer position (_pdropzone) need to be
818  * taken into account.
819  */
820  delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size () + _ddropzone - _pdropzone;
821  } else {
822  delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
823  }
824 
825  /* TODO needs adjustment per DraggingView,
826  *
827  * e.g. select one region on the top-layer of a track
828  * and one region which is at the bottom-layer of another track
829  * drag both.
830  *
831  * Indicated drop-zones and layering is wrong.
832  * and may infer additional layers on the target-track
833  * (depending how many layers the original track had).
834  *
835  * Or select two regions (different layers) on a same track,
836  * move across a non-layer track.. -> layering info is lost.
837  * on drop either of the regions may be on top.
838  *
839  * Proposed solution: screw it :) well,
840  * don't use delta_layer, use an absolute value
841  * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
842  * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
843  * 3) iterate over all DraggingView, find the one that is over the track with most layers
844  * 4) proportionally scale layer to layers available on target
845  */
846  delta_layer = current_pointer_layer - _last_pointer_layer;
847 
848  }
849  /* for automation lanes, there is a TimeAxisView but no ->view()
850  * if (!tv) -> dropzone
851  */
852  else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
853  /* Moving into the drop-zone.. */
854  delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
855  /* delta_time_axis_view may not be sufficient to move into the DZ
856  * the mouse may enter it, but it may not be a valid move due to
857  * constraints.
858  *
859  * -> remember the delta needed to move into the dropzone
860  */
861  _ddropzone = delta_time_axis_view;
862  /* ..but subtract hidden tracks (or routes) at the bottom.
863  * we silently move mover them
864  */
865  _ddropzone -= apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
866  - _time_axis_views.size();
867  }
868  else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view < 0) {
869  /* move around inside the zone.
870  * This allows to move further down until all regions are in the zone.
871  */
872  const double ptr_y = cur_y + _editor->get_trackview_group()->canvas_origin().y;
873  assert(ptr_y >= last_track_bottom_edge);
874  assert(_ddropzone > 0);
875 
876  /* calculate mouse position in 'tracks' below last track. */
877  const double dzi_h = TimeAxisView::preset_height (HeightNormal);
878  uint32_t dzpos = _ddropzone + floor((1 + ptr_y - last_track_bottom_edge) / dzi_h);
879 
880  if (dzpos > _pdropzone && _ndropzone < _ntracks) {
881  // move further down
882  delta_time_axis_view = dzpos - _pdropzone;
883  } else if (dzpos < _pdropzone && _ndropzone > 0) {
884  // move up inside the DZ
885  delta_time_axis_view = dzpos - _pdropzone;
886  }
887  }
888 
889  /* Work out the change in x */
890  framepos_t pending_region_position;
891  double const x_delta = compute_x_delta (event, &pending_region_position);
892  _last_frame_position = pending_region_position;
893 
894  /* calculate hidden tracks in current y-axis delta */
895  int delta_skip = 0;
896  if (_last_pointer_time_axis_view < 0 && _pdropzone > 0) {
897  /* The mouse is more than one track below the dropzone.
898  * distance calculation is not needed (and would not work, either
899  * because the dropzone is "packed").
900  *
901  * Except when [partially] moving regions out of dropzone in a large step.
902  * (the mouse may or may not remain in the DZ)
903  * Hidden tracks at the bottom of the TAV need to be skipped.
904  *
905  * This also handles the case if the mouse entered the DZ
906  * in a large step (exessive delta), either due to fast-movement,
907  * autoscroll, laggy UI. _ddropzone copensates for that (see "move into dz" above)
908  */
909  if (delta_time_axis_view < 0 && (int)_ddropzone - delta_time_axis_view >= (int)_pdropzone) {
910  const int dt = delta_time_axis_view + (int)_pdropzone - (int)_ddropzone;
911  assert(dt <= 0);
912  delta_skip = apply_track_delta(_time_axis_views.size(), dt, 0, true)
913  -_time_axis_views.size() - dt;
914  }
915  }
916  else if (_last_pointer_time_axis_view < 0) {
917  /* Moving out of the zone. Check for hidden tracks at the bottom. */
918  delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
919  -_time_axis_views.size() - delta_time_axis_view;
920  } else {
921  /* calculate hidden tracks that are skipped by the pointer movement */
922  delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
924  - delta_time_axis_view;
925  }
926 
927  /* Verify change in y */
928  if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
929  /* this y movement is not allowed, so do no y movement this time */
930  delta_time_axis_view = 0;
931  delta_layer = 0;
932  delta_skip = 0;
933  }
934 
935  if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
936  /* haven't reached next snap point, and we're not switching
937  trackviews nor layers. nothing to do.
938  */
939  return;
940  }
941 
942  typedef map<boost::shared_ptr<Playlist>, double> PlaylistDropzoneMap;
943  PlaylistDropzoneMap playlist_dropzone_map;
944  _ndropzone = 0; // number of elements currently in the dropzone
945 
946  if (first_move) {
947  /* sort views by time_axis.
948  * This retains track order in the dropzone, regardless
949  * of actual selection order
950  */
951  _views.sort (DraggingViewSorter());
952 
953  /* count number of distinct tracks of all regions
954  * being dragged, used for dropzone.
955  */
956  int prev_track = -1;
957  for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
958  if (i->time_axis_view != prev_track) {
959  prev_track = i->time_axis_view;
960  ++_ntracks;
961  }
962  }
963 #ifndef NDEBUG
964  int spread =
965  _views.back().time_axis_view -
966  _views.front().time_axis_view;
967 
968  spread -= apply_track_delta (_views.front().time_axis_view, spread, 0, true)
969  - _views.back().time_axis_view;
970 
971  printf("Dragging region(s) from %d different track(s), max dist: %d\n", _ntracks, spread);
972 #endif
973  }
974 
975  for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
976 
977  RegionView* rv = i->view;
978  double y_delta;
979 
980  y_delta = 0;
981 
982  if (rv->region()->locked() || rv->region()->video_locked()) {
983  continue;
984  }
985 
986  if (first_move) {
987  rv->drag_start ();
988 
989  /* reparent the regionview into a group above all
990  * others
991  */
992 
993  ArdourCanvas::Item* rvg = rv->get_canvas_group();
994  Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
995  Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
996  rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
997  /* move the item so that it continues to appear at the
998  same location now that its parent has changed.
999  */
1000  rvg->move (rv_canvas_offset - dmg_canvas_offset);
1001  }
1002 
1003  /* If we have moved tracks, we'll fudge the layer delta so that the
1004  region gets moved back onto layer 0 on its new track; this avoids
1005  confusion when dragging regions from non-zero layers onto different
1006  tracks.
1007  */
1008  double this_delta_layer = delta_layer;
1009  if (delta_time_axis_view != 0) {
1010  this_delta_layer = - i->layer;
1011  }
1012 
1013  int this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
1014 
1015  int track_index = i->time_axis_view + this_delta_time_axis_view;
1016  assert(track_index >= 0);
1017 
1018  if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
1019  /* Track is in the Dropzone */
1020 
1021  i->time_axis_view = track_index;
1022  assert(i->time_axis_view >= (int) _time_axis_views.size());
1023  if (cur_y >= 0) {
1024 
1025  double yposition = 0;
1026  PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
1028  ++_ndropzone;
1029 
1030  /* store index of each new playlist as a negative count, starting at -1 */
1031 
1032  if (pdz == playlist_dropzone_map.end()) {
1033  /* compute where this new track (which doesn't exist yet) will live
1034  on the y-axis.
1035  */
1036  yposition = last_track_bottom_edge; /* where to place the top edge of the regionview */
1037 
1038  /* How high is this region view ? */
1039 
1040  boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1041  ArdourCanvas::Rect bbox;
1042 
1043  if (obbox) {
1044  bbox = obbox.get ();
1045  }
1046 
1047  last_track_bottom_edge += bbox.height();
1048 
1049  playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), yposition));
1050 
1051  } else {
1052  yposition = pdz->second;
1053  }
1054 
1055  /* values are zero or negative, hence the use of min() */
1056  y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
1057  }
1058 
1059  } else {
1060 
1061  /* The TimeAxisView that this region is now over */
1062  TimeAxisView* current_tv = _time_axis_views[track_index];
1063 
1064  /* Ensure it is moved from stacked -> expanded if appropriate */
1065  if (current_tv->view()->layer_display() == Stacked) {
1066  current_tv->view()->set_layer_display (Expanded);
1067  }
1068 
1069  /* We're only allowed to go -ve in layer on Expanded views */
1070  if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1071  this_delta_layer = - i->layer;
1072  }
1073 
1074  /* Set height */
1075  rv->set_height (current_tv->view()->child_height ());
1076 
1077  /* Update show/hidden status as the region view may have come from a hidden track,
1078  or have moved to one.
1079  */
1080  if (current_tv->hidden ()) {
1081  rv->get_canvas_group()->hide ();
1082  } else {
1083  rv->get_canvas_group()->show ();
1084  }
1085 
1086  /* Update the DraggingView */
1087  i->time_axis_view = track_index;
1088  i->layer += this_delta_layer;
1089 
1090  if (_brushing) {
1091  _editor->mouse_brush_insert_region (rv, pending_region_position);
1092  } else {
1093  Duple track_origin;
1094 
1095  /* Get the y coordinate of the top of the track that this region is now over */
1096  track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1097 
1098  /* And adjust for the layer that it should be on */
1099  StreamView* cv = current_tv->view ();
1100  switch (cv->layer_display ()) {
1101  case Overlaid:
1102  break;
1103  case Stacked:
1104  track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1105  break;
1106  case Expanded:
1107  track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1108  break;
1109  }
1110 
1111  /* need to get the parent of the regionview
1112  * canvas group and get its position in
1113  * equivalent coordinate space as the trackview
1114  * we are now dragging over.
1115  */
1116 
1117  y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1118 
1119  }
1120  }
1121 
1122  /* Now move the region view */
1123  rv->move (x_delta, y_delta);
1124 
1125  } /* foreach region */
1126 
1127  _total_x_delta += x_delta;
1128 
1129  if (x_delta != 0 && !_brushing) {
1131  }
1132 
1133  /* keep track of pointer movement */
1134  if (tv) {
1135  /* the pointer is currently over a time axis view */
1136 
1137  if (_last_pointer_time_axis_view < 0) {
1138  /* last motion event was not over a time axis view
1139  * or last y-movement out of the dropzone was not valid
1140  */
1141  int dtz = 0;
1142  if (delta_time_axis_view < 0) {
1143  /* in the drop zone, moving up */
1144 
1145  /* _pdropzone is the last known pointer y-axis position inside the DZ.
1146  * We do not use negative _last_pointer_time_axis_view because
1147  * the dropzone is "packed" (the actual track offset is ignored)
1148  *
1149  * As opposed to the actual number
1150  * of elements in the dropzone (_ndropzone)
1151  * _pdropzone is not constrained. This is necessary
1152  * to allow moving multiple regions with y-distance
1153  * into the DZ.
1154  *
1155  * There can be 0 elements in the dropzone,
1156  * even though the drag-pointer is inside the DZ.
1157  *
1158  * example:
1159  * [ Audio-track, Midi-track, Audio-track, DZ ]
1160  * move regions from both audio tracks at the same time into the
1161  * DZ by grabbing the region in the bottom track.
1162  */
1163  assert(current_pointer_time_axis_view >= 0);
1164  dtz = std::min((int)_pdropzone, (int)_ddropzone - delta_time_axis_view);
1165  _pdropzone -= dtz;
1166  }
1167 
1168  /* only move out of the zone if the movement is OK */
1169  if (_pdropzone == 0 && delta_time_axis_view != 0) {
1170  assert(delta_time_axis_view < 0);
1171  _last_pointer_time_axis_view = current_pointer_time_axis_view;
1172  /* if all logic and maths are correct, there is no need to assign the 'current' pointer.
1173  * the current position can be calculated as follows:
1174  */
1175  // a well placed oofus attack can still throw this off.
1176  // likley auto-scroll related, printf() debugging may tell, commented out for now.
1177  //assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view);
1178  }
1179  } else {
1180  /* last motion event was also over a time axis view */
1181  _last_pointer_time_axis_view += delta_time_axis_view;
1182  assert(_last_pointer_time_axis_view >= 0);
1183  }
1184 
1185  } else {
1186 
1187  /* the pointer is not over a time axis view */
1188  assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
1189  _pdropzone += delta_time_axis_view - delta_skip;
1190  _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
1191  }
1192 
1193  _last_pointer_layer += delta_layer;
1194 }
1195 
1196 void
1197 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1198 {
1199  if (_copy && first_move) {
1200 
1201  if (_x_constrained) {
1203  } else {
1205  }
1206 
1207  /* duplicate the regionview(s) and region(s) */
1208 
1209  list<DraggingView> new_regionviews;
1210 
1211  for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1212 
1213  RegionView* rv = i->view;
1214  AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1215  MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1216 
1217  const boost::shared_ptr<const Region> original = rv->region();
1218  boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
1219  region_copy->set_position (original->position());
1220  /* need to set this so that the drop zone code can work. This doesn't
1221  actually put the region into the playlist, but just sets a weak pointer
1222  to it.
1223  */
1224  region_copy->set_playlist (original->playlist());
1225 
1226  RegionView* nrv;
1227  if (arv) {
1228  boost::shared_ptr<AudioRegion> audioregion_copy
1230 
1231  nrv = new AudioRegionView (*arv, audioregion_copy);
1232  } else if (mrv) {
1233  boost::shared_ptr<MidiRegion> midiregion_copy
1235  nrv = new MidiRegionView (*mrv, midiregion_copy);
1236  } else {
1237  continue;
1238  }
1239 
1240  nrv->get_canvas_group()->show ();
1241  new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1242 
1243  /* swap _primary to the copy */
1244 
1245  if (rv == _primary) {
1246  _primary = nrv;
1247  }
1248 
1249  /* ..and deselect the one we copied */
1250 
1251  rv->set_selected (false);
1252  }
1253 
1254  if (!new_regionviews.empty()) {
1255 
1256  /* reflect the fact that we are dragging the copies */
1257 
1258  _views = new_regionviews;
1259 
1260  swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1261  }
1262 
1263  } else if (!_copy && first_move) {
1264 
1265  if (_x_constrained) {
1266  _editor->begin_reversible_command (_("fixed time region drag"));
1267  } else {
1269  }
1270  }
1271 
1272  RegionMotionDrag::motion (event, first_move);
1273 }
1274 
1275 void
1276 RegionMotionDrag::finished (GdkEvent *, bool)
1277 {
1278  for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1279  if (!(*i)->view()) {
1280  continue;
1281  }
1282 
1283  if ((*i)->view()->layer_display() == Expanded) {
1284  (*i)->view()->set_layer_display (Stacked);
1285  }
1286  }
1287 }
1288 
1289 void
1290 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1291 {
1292  RegionMotionDrag::finished (ev, movement_occurred);
1293 
1294  if (!movement_occurred) {
1295 
1296  /* just a click */
1297 
1298  if (was_double_click() && !_views.empty()) {
1299  DraggingView dv = _views.front();
1300  dv.view->show_region_editor ();
1301 
1302  }
1303 
1304  return;
1305  }
1306 
1307  /* reverse this here so that we have the correct logic to finalize
1308  the drag.
1309  */
1310 
1311  if (Config->get_edit_mode() == Lock) {
1313  }
1314 
1315  assert (!_views.empty ());
1316 
1317  /* We might have hidden region views so that they weren't visible during the drag
1318  (when they have been reparented). Now everything can be shown again, as region
1319  views are back in their track parent groups.
1320  */
1321  for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1322  i->view->get_canvas_group()->show ();
1323  }
1324 
1325  bool const changed_position = (_last_frame_position != _primary->region()->position());
1326  bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1327  framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1328 
1329  if (_copy) {
1330 
1331  finished_copy (
1332  changed_position,
1333  changed_tracks,
1334  drag_delta
1335  );
1336 
1337  } else {
1338 
1340  changed_position,
1341  changed_tracks,
1342  drag_delta
1343  );
1344 
1345  }
1346 
1348 }
1349 
1352 {
1353  /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1354  new track.
1355  */
1356 
1357  try {
1358  if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1359  list<boost::shared_ptr<AudioTrack> > audio_tracks;
1360  uint32_t output_chan = region->n_channels();
1361  if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) {
1362  output_chan = _editor->session()->master_out()->n_inputs().n_audio();
1363  }
1364  audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, ARDOUR::Normal, 0, 1, region->name());
1365  RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1366  if (rtav) {
1367  rtav->set_height (original->current_height());
1368  }
1369  return rtav;
1370  } else {
1371  ChanCount one_midi_port (DataType::MIDI, 1);
1372  list<boost::shared_ptr<MidiTrack> > midi_tracks;
1373  midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1374  RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1375  if (rtav) {
1376  rtav->set_height (original->current_height());
1377  }
1378  return rtav;
1379  }
1380  } catch (...) {
1381  error << _("Could not create new track after region placed in the drop zone") << endmsg;
1382  return 0;
1383  }
1384 }
1385 
1386 void
1387 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1388 {
1389  RegionSelection new_views;
1390  PlaylistSet modified_playlists;
1391  RouteTimeAxisView* new_time_axis_view = 0;
1392 
1393  if (_brushing) {
1394  /* all changes were made during motion event handlers */
1395 
1396  for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1397  delete i->view;
1398  }
1399 
1401  return;
1402  }
1403 
1404  typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1405  PlaylistMapping playlist_mapping;
1406 
1407  /* insert the regions into their new playlists */
1408  for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1409 
1410  RouteTimeAxisView* dest_rtv = 0;
1411 
1412  if (i->view->region()->locked() || i->view->region()->video_locked()) {
1413  continue;
1414  }
1415 
1416  framepos_t where;
1417 
1418  if (changed_position && !_x_constrained) {
1419  where = i->view->region()->position() - drag_delta;
1420  } else {
1421  where = i->view->region()->position();
1422  }
1423 
1424  if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1425  /* dragged to drop zone */
1426 
1427  PlaylistMapping::iterator pm;
1428 
1429  if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1430  /* first region from this original playlist: create a new track */
1431  new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1432  playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1433  dest_rtv = new_time_axis_view;
1434  } else {
1435  /* we already created a new track for regions from this playlist, use it */
1436  dest_rtv = pm->second;
1437  }
1438  } else {
1439  /* destination time axis view is the one we dragged to */
1440  dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1441  }
1442 
1443  if (dest_rtv != 0) {
1444  RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1445  if (new_view != 0) {
1446  new_views.push_back (new_view);
1447  }
1448  }
1449 
1450  /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1451  since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1452  */
1453 
1454  list<DraggingView>::const_iterator next = i;
1455  ++next;
1456  delete i->view;
1457  i = next;
1458  }
1459 
1460  /* If we've created new regions either by copying or moving
1461  to a new track, we want to replace the old selection with the new ones
1462  */
1463 
1464  if (new_views.size() > 0) {
1465  _editor->selection->set (new_views);
1466  }
1467 
1468  /* write commands for the accumulated diffs for all our modified playlists */
1469  add_stateful_diff_commands_for_playlists (modified_playlists);
1470 
1472 }
1473 
1474 void
1476  bool const changed_position,
1477  bool const changed_tracks,
1478  framecnt_t const drag_delta
1479  )
1480 {
1481  RegionSelection new_views;
1482  PlaylistSet modified_playlists;
1483  PlaylistSet frozen_playlists;
1484  set<RouteTimeAxisView*> views_to_update;
1485  RouteTimeAxisView* new_time_axis_view = 0;
1486 
1487  if (_brushing) {
1488  /* all changes were made during motion event handlers */
1490  return;
1491  }
1492 
1493  typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1494  PlaylistMapping playlist_mapping;
1495 
1496  for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1497 
1498  RegionView* rv = i->view;
1499  RouteTimeAxisView* dest_rtv = 0;
1500 
1501  if (rv->region()->locked() || rv->region()->video_locked()) {
1502  ++i;
1503  continue;
1504  }
1505 
1506  if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1507  /* dragged to drop zone */
1508 
1509  PlaylistMapping::iterator pm;
1510 
1511  if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1512  /* first region from this original playlist: create a new track */
1513  new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1514  playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1515  dest_rtv = new_time_axis_view;
1516  } else {
1517  /* we already created a new track for regions from this playlist, use it */
1518  dest_rtv = pm->second;
1519  }
1520 
1521  } else {
1522  /* destination time axis view is the one we dragged to */
1523  dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1524  }
1525 
1526  assert (dest_rtv);
1527 
1528  double const dest_layer = i->layer;
1529 
1530  views_to_update.insert (dest_rtv);
1531 
1532  framepos_t where;
1533 
1534  if (changed_position && !_x_constrained) {
1535  where = rv->region()->position() - drag_delta;
1536  } else {
1537  where = rv->region()->position();
1538  }
1539 
1540  if (changed_tracks) {
1541 
1542  /* insert into new playlist */
1543 
1545  RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1546  );
1547 
1548  if (new_view == 0) {
1549  ++i;
1550  continue;
1551  }
1552 
1553  new_views.push_back (new_view);
1554 
1555  /* remove from old playlist */
1556 
1557  /* the region that used to be in the old playlist is not
1558  moved to the new one - we use a copy of it. as a result,
1559  any existing editor for the region should no longer be
1560  visible.
1561  */
1562  rv->hide_region_editor();
1563 
1564 
1565  remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1566 
1567  } else {
1568 
1569  boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1570 
1571  /* this movement may result in a crossfade being modified, or a layering change,
1572  so we need to get undo data from the playlist as well as the region.
1573  */
1574 
1575  pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1576  if (r.second) {
1577  playlist->clear_changes ();
1578  }
1579 
1580  rv->region()->clear_changes ();
1581 
1582  /*
1583  motion on the same track. plonk the previously reparented region
1584  back to its original canvas group (its streamview).
1585  No need to do anything for copies as they are fake regions which will be deleted.
1586  */
1587 
1588  rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1589  rv->get_canvas_group()->set_y_position (i->initial_y);
1590  rv->drag_end ();
1591 
1592  /* just change the model */
1593  if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1594  playlist->set_layer (rv->region(), dest_layer);
1595  }
1596 
1597  /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1598 
1599  r = frozen_playlists.insert (playlist);
1600 
1601  if (r.second) {
1602  playlist->freeze ();
1603  }
1604 
1605  rv->region()->set_position (where);
1606 
1608  }
1609 
1610  if (changed_tracks) {
1611 
1612  /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1613  was selected in all of them, then removing it from a playlist will have removed all
1614  trace of it from _views (i.e. there were N regions selected, we removed 1,
1615  but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1616  corresponding regionview, and _views is now empty).
1617 
1618  This could have invalidated any and all iterators into _views.
1619 
1620  The heuristic we use here is: if the region selection is empty, break out of the loop
1621  here. if the region selection is not empty, then restart the loop because we know that
1622  we must have removed at least the region(view) we've just been working on as well as any
1623  that we processed on previous iterations.
1624 
1625  EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1626  we can just iterate.
1627  */
1628 
1629 
1630  if (_views.empty()) {
1631  break;
1632  } else {
1633  i = _views.begin();
1634  }
1635 
1636  } else {
1637  ++i;
1638  }
1639  }
1640 
1641  /* If we've created new regions either by copying or moving
1642  to a new track, we want to replace the old selection with the new ones
1643  */
1644 
1645  if (new_views.size() > 0) {
1646  _editor->selection->set (new_views);
1647  }
1648 
1649  for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1650  (*p)->thaw();
1651  }
1652 
1653  /* write commands for the accumulated diffs for all our modified playlists */
1654  add_stateful_diff_commands_for_playlists (modified_playlists);
1655 
1657 
1658  /* We have futzed with the layering of canvas items on our streamviews.
1659  If any region changed layer, this will have resulted in the stream
1660  views being asked to set up their region views, and all will be well.
1661  If not, we might now have badly-ordered region views. Ask the StreamViews
1662  involved to sort themselves out, just in case.
1663  */
1664 
1665  for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1666  (*i)->view()->playlist_layered ((*i)->track ());
1667  }
1668 }
1669 
1676 void
1679  boost::shared_ptr<Playlist> playlist,
1680  PlaylistSet& modified_playlists
1681  )
1682 {
1683  pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1684 
1685  if (r.second) {
1686  playlist->clear_changes ();
1687  }
1688 
1689  playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1690 }
1691 
1692 
1703 RegionView *
1706  RouteTimeAxisView* dest_rtv,
1707  layer_t dest_layer,
1708  framecnt_t where,
1709  PlaylistSet& modified_playlists
1710  )
1711 {
1712  boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1713  if (!dest_playlist) {
1714  return 0;
1715  }
1716 
1717  /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1718  _new_region_view = 0;
1719  sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1720 
1721  /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1722  pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1723  if (r.second) {
1724  dest_playlist->clear_changes ();
1725  }
1726 
1727  dest_playlist->add_region (region, where);
1728 
1729  if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1730  dest_playlist->set_layer (region, dest_layer);
1731  }
1732 
1733  c.disconnect ();
1734 
1735  assert (_new_region_view);
1736 
1737  return _new_region_view;
1738 }
1739 
1740 void
1742 {
1743  _new_region_view = rv;
1744 }
1745 
1746 void
1748 {
1749  for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1751  if (!c->empty()) {
1752  _editor->session()->add_command (c);
1753  } else {
1754  delete c;
1755  }
1756  }
1757 }
1758 
1759 
1760 void
1761 RegionMoveDrag::aborted (bool movement_occurred)
1762 {
1763  if (_copy) {
1764 
1765  for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1766  list<DraggingView>::const_iterator next = i;
1767  ++next;
1768  delete i->view;
1769  i = next;
1770  }
1771 
1772  _views.clear ();
1773 
1774  } else {
1775  RegionMotionDrag::aborted (movement_occurred);
1776  }
1777 }
1778 
1779 void
1781 {
1782  for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1783 
1784  StreamView* sview = (*i)->view();
1785 
1786  if (sview) {
1787  if (sview->layer_display() == Expanded) {
1788  sview->set_layer_display (Stacked);
1789  }
1790  }
1791  }
1792 
1793  for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1794  RegionView* rv = i->view;
1795  TimeAxisView* tv = &(rv->get_time_axis_view ());
1796  RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1797  assert (rtv);
1798  rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1799  rv->get_canvas_group()->set_y_position (0);
1800  rv->drag_end ();
1801  rv->move (-_total_x_delta, 0);
1802  rv->set_height (rtv->view()->child_height ());
1803  }
1804 }
1805 
1809 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1810  : RegionMotionDrag (e, i, p, v, b)
1811  , _copy (c)
1812 {
1813  DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1814 
1815  double speed = 1;
1816  RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1817  if (rtv && rtv->is_track()) {
1818  speed = rtv->track()->speed ();
1819  }
1820 
1821  _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1822 }
1823 
1824 void
1826 {
1828 }
1829 
1831  : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1832 {
1833  DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1834 
1835  assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1836  (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1837 
1838  _primary = v->view()->create_region_view (r, false, false);
1839 
1840  _primary->get_canvas_group()->show ();
1841  _primary->set_position (pos, 0);
1842  _views.push_back (DraggingView (_primary, this, v));
1843 
1844  _last_frame_position = pos;
1845 
1847 }
1848 
1849 void
1850 RegionInsertDrag::finished (GdkEvent *, bool)
1851 {
1852  int pos = _views.front().time_axis_view;
1853  assert(pos >= 0 && pos < (int)_time_axis_views.size());
1854 
1855  RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1856 
1857  _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1858  _primary->get_canvas_group()->set_y_position (0);
1859 
1860  boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1861 
1863  playlist->clear_changes ();
1865 
1866  // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1867  if (Config->get_edit_mode() == Ripple) {
1869  }
1870 
1871  _editor->session()->add_command (new StatefulDiffCommand (playlist));
1873 
1874  delete _primary;
1875  _primary = 0;
1876  _views.clear ();
1877 }
1878 
1879 void
1881 {
1882  delete _primary;
1883  _primary = 0;
1884  _views.clear ();
1885 }
1886 
1887 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1888  : RegionMoveDrag (e, i, p, v, false, false)
1889 {
1890  DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1891 }
1892 
1895  return a->region()->position () < b->region()->position();
1896  }
1897 };
1898 
1899 void
1900 RegionSpliceDrag::motion (GdkEvent* event, bool)
1901 {
1902  /* Which trackview is this ? */
1903 
1904  pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1905  RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1906 
1907  /* The region motion is only processed if the pointer is over
1908  an audio track.
1909  */
1910 
1911  if (!tv || !tv->is_track()) {
1912  /* To make sure we hide the verbose canvas cursor when the mouse is
1913  not held over an audio track.
1914  */
1915  _editor->verbose_cursor()->hide ();
1916  return;
1917  } else {
1918  _editor->verbose_cursor()->show ();
1919  }
1920 
1921  int dir;
1922 
1923  if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1924  dir = 1;
1925  } else {
1926  dir = -1;
1927  }
1928 
1929  RegionSelection copy;
1931 
1932  framepos_t const pf = adjusted_current_frame (event);
1933 
1934  for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1935 
1936  RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1937 
1938  if (!atv) {
1939  continue;
1940  }
1941 
1942  boost::shared_ptr<Playlist> playlist;
1943 
1944  if ((playlist = atv->playlist()) == 0) {
1945  continue;
1946  }
1947 
1948  if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1949  continue;
1950  }
1951 
1952  if (dir > 0) {
1953  if (pf < (*i)->region()->last_frame() + 1) {
1954  continue;
1955  }
1956  } else {
1957  if (pf > (*i)->region()->first_frame()) {
1958  continue;
1959  }
1960  }
1961 
1962 
1963  playlist->shuffle ((*i)->region(), dir);
1964  }
1965 }
1966 
1967 void
1968 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1969 {
1970  RegionMoveDrag::finished (event, movement_occurred);
1971 }
1972 
1973 void
1975 {
1976  /* XXX: TODO */
1977 }
1978 
1979 /***
1980  * ripple mode...
1981  */
1982 
1983 void
1984 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
1985 {
1986 
1988 
1989  RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
1990  RegionSelection to_ripple;
1991  for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
1992  if ((*i)->position() >= where) {
1993  to_ripple.push_back (rtv->view()->find_view(*i));
1994  }
1995  }
1996 
1997  for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
1998  if (!exclude.contains (*i)) {
1999  // the selection has already been added to _views
2000 
2001  if (drag_in_progress) {
2002  // do the same things that RegionMotionDrag::motion does when
2003  // first_move is true, for the region views that we're adding
2004  // to _views this time
2005 
2006  (*i)->drag_start();
2007  ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2008  Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2009  Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2010  rvg->reparent (_editor->_drag_motion_group);
2011 
2012  // we only need to move in the y direction
2013  Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2014  fudge.x = 0;
2015  rvg->move (fudge);
2016 
2017  }
2018  _views.push_back (DraggingView (*i, this, tav));
2019  }
2020  }
2021 }
2022 
2023 void
2025 {
2026 
2027  for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2028  // we added all the regions after the selection
2029 
2030  std::list<DraggingView>::iterator to_erase = i++;
2031  if (!_editor->selection->regions.contains (to_erase->view)) {
2032  // restore the non-selected regions to their original playlist & positions,
2033  // and then ripple them back by the length of the regions that were dragged away
2034  // do the same things as RegionMotionDrag::aborted
2035 
2036  RegionView *rv = to_erase->view;
2037  TimeAxisView* tv = &(rv->get_time_axis_view ());
2038  RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2039  assert (rtv);
2040 
2041  // plonk them back onto their own track
2042  rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2043  rv->get_canvas_group()->set_y_position (0);
2044  rv->drag_end ();
2045 
2046  if (move_regions) {
2047  // move the underlying region to match the view
2048  rv->region()->set_position (rv->region()->position() + amount);
2049  } else {
2050  // restore the view to match the underlying region's original position
2051  rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2052  }
2053 
2054  rv->set_height (rtv->view()->child_height ());
2055  _views.erase (to_erase);
2056  }
2057  }
2058 }
2059 
2060 bool
2061 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2062 {
2063  if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2064  if (delta_track) {
2066  } else {
2067  return true;
2068  }
2069  }
2070  return false;
2071 }
2072 
2073 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2074  : RegionMoveDrag (e, i, p, v, false, false)
2075 {
2076  DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2077  // compute length of selection
2078  RegionSelection selected_regions = _editor->selection->regions;
2079  selection_length = selected_regions.end_frame() - selected_regions.start();
2080 
2081  // we'll only allow dragging to another track in ripple mode if all the regions
2082  // being dragged start off on the same track
2083  allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2084  prev_tav = NULL;
2085  prev_amount = 0;
2086  exclude = new RegionList;
2087  for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2088  exclude->push_back((*i)->region());
2089  }
2090 
2091  // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2092  RegionSelection copy;
2093  selected_regions.by_position(copy); // get selected regions sorted by position into copy
2094 
2095  std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2096  std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2097 
2098  for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2099  // find ripple start point on each applicable playlist
2100  RegionView *first_selected_on_this_track = NULL;
2101  for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2102  if ((*i)->region()->playlist() == (*pi)) {
2103  // region is on this playlist - it's the first, because they're sorted
2104  first_selected_on_this_track = *i;
2105  break;
2106  }
2107  }
2108  assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2110  &first_selected_on_this_track->get_time_axis_view(),
2111  first_selected_on_this_track->region()->position(),
2112  selected_regions, false);
2113  }
2114 
2116  orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2117  } else {
2118  orig_tav = NULL;
2119  }
2120 
2121 }
2122 
2123 void
2124 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2125 {
2126  /* Which trackview is this ? */
2127 
2128  pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2129  RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2130 
2131  /* The region motion is only processed if the pointer is over
2132  an audio track.
2133  */
2134 
2135  if (!tv || !tv->is_track()) {
2136  /* To make sure we hide the verbose canvas cursor when the mouse is
2137  not held over an audiotrack.
2138  */
2139  _editor->verbose_cursor()->hide ();
2140  return;
2141  }
2142 
2143  framepos_t where = adjusted_current_frame (event);
2144  assert (where >= 0);
2145  framepos_t after;
2146  double delta = compute_x_delta (event, &after);
2147 
2148  framecnt_t amount = _editor->pixel_to_sample (delta);
2149 
2151  // all the originally selected regions were on the same track
2152 
2153  framecnt_t adjust = 0;
2154  if (prev_tav && tv != prev_tav) {
2155  // dragged onto a different track
2156  // remove the unselected regions from _views, restore them to their original positions
2157  // and add the regions after the drop point on the new playlist to _views instead.
2158  // undo the effect of rippling the previous playlist, and include the effect of removing
2159  // the dragged region(s) from this track
2160 
2162  // ripple previous playlist according to the regions that have been removed onto the new playlist
2164  prev_amount = 0;
2165 
2166  // move just the selected regions
2167  RegionMoveDrag::motion(event, first_move);
2168 
2169  // ensure that the ripple operation on the new playlist inserts selection_length time
2170  adjust = selection_length;
2171  // ripple the new current playlist
2172  tv->playlist()->ripple (where, amount+adjust, exclude);
2173 
2174  // add regions after point where drag entered this track to subsequent ripples
2175  add_all_after_to_views (tv, where, _editor->selection->regions, true);
2176 
2177  } else {
2178  // motion on same track
2179  RegionMoveDrag::motion(event, first_move);
2180  }
2181  prev_tav = tv;
2182 
2183  // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2184  prev_position = where;
2185  } else {
2186  // selection encompasses multiple tracks - just drag
2187  // cross-track drags are forbidden
2188  RegionMoveDrag::motion(event, first_move);
2189  }
2190 
2191  if (!_x_constrained) {
2192  prev_amount += amount;
2193  }
2194 
2195  _last_frame_position = after;
2196 }
2197 
2198 void
2199 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2200 {
2201  if (!movement_occurred) {
2202 
2203  /* just a click */
2204 
2205  if (was_double_click() && !_views.empty()) {
2206  DraggingView dv = _views.front();
2207  dv.view->show_region_editor ();
2208 
2209  }
2210 
2211  return;
2212  }
2213 
2214  _editor->begin_reversible_command(_("Ripple drag"));
2215 
2216  // remove the regions being rippled from the dragging view, updating them to
2217  // their new positions
2219 
2221  if (orig_tav) {
2222  // if regions were dragged across tracks, we've rippled any later
2223  // regions on the track the regions were dragged off, so we need
2224  // to add the original track to the undo record
2226  vector<Command*> cmds;
2227  orig_tav->playlist()->rdiff (cmds);
2228  _editor->session()->add_commands (cmds);
2229  }
2230  if (prev_tav && prev_tav != orig_tav) {
2232  vector<Command*> cmds;
2233  prev_tav->playlist()->rdiff (cmds);
2234  _editor->session()->add_commands (cmds);
2235  }
2236  } else {
2237  // selection spanned multiple tracks - all will need adding to undo record
2238 
2239  std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2240  std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2241 
2242  for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2243  (*pi)->clear_changes();
2244  vector<Command*> cmds;
2245  (*pi)->rdiff (cmds);
2246  _editor->session()->add_commands (cmds);
2247  }
2248  }
2249 
2250  // other modified playlists are added to undo by RegionMoveDrag::finished()
2251  RegionMoveDrag::finished (event, movement_occurred);
2253 }
2254 
2255 void
2256 RegionRippleDrag::aborted (bool movement_occurred)
2257 {
2258  RegionMoveDrag::aborted (movement_occurred);
2259  _views.clear ();
2260 }
2261 
2262 
2264  : Drag (e, i),
2265  _view (dynamic_cast<MidiTimeAxisView*> (v))
2266 {
2267  DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2268 
2269  assert (_view);
2270 }
2271 
2272 void
2273 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2274 {
2275  if (first_move) {
2277  _view->playlist()->freeze ();
2278  } else {
2279  if (_region) {
2280  framepos_t const f = adjusted_current_frame (event);
2281  if (f < grab_frame()) {
2282  _region->set_position (f);
2283  }
2284 
2285  /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2286  so that if this region is duplicated, its duplicate starts on
2287  a snap point rather than 1 frame after a snap point. Otherwise things get
2288  a bit confusing as if a region starts 1 frame after a snap point, one cannot
2289  place snapped notes at the start of the region.
2290  */
2291 
2292  framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2293  _region->set_length (len < 1 ? 1 : len);
2294  }
2295  }
2296 }
2297 
2298 void
2299 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
2300 {
2301  if (!movement_occurred) {
2303  } else {
2304  _view->playlist()->thaw ();
2305  }
2306 }
2307 
2308 void
2310 {
2311  if (_region) {
2312  _view->playlist()->thaw ();
2313  }
2314 
2315  /* XXX */
2316 }
2317 
2318 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2319  : Drag (e, i)
2320  , region (0)
2321 {
2322  DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2323 }
2324 
2325 void
2326 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2327 {
2328  Gdk::Cursor* cursor;
2329  NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2330  assert (cnote);
2331  float x_fraction = cnote->mouse_x_fraction ();
2332 
2333  if (x_fraction > 0.0 && x_fraction < 0.25) {
2334  cursor = _editor->cursors()->left_side_trim;
2335  at_front = true;
2336  } else {
2337  cursor = _editor->cursors()->right_side_trim;
2338  at_front = false;
2339  }
2340 
2341  Drag::start_grab (event, cursor);
2342 
2343  region = &cnote->region_view();
2344 
2345  _item->grab ();
2346 
2347  if (event->motion.state & Keyboard::PrimaryModifier) {
2348  relative = false;
2349  } else {
2350  relative = true;
2351  }
2352 
2354 
2355  if (ms.size() > 1) {
2356  /* has to be relative, may make no sense otherwise */
2357  relative = true;
2358  }
2359 
2360  /* select this note; if it is already selected, preserve the existing selection,
2361  otherwise make this note the only one selected.
2362  */
2363  region->note_selected (cnote, cnote->selected ());
2364 
2365  _editor->begin_reversible_command (_("resize notes"));
2366 
2367  for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2368  MidiRegionSelection::iterator next;
2369  next = r;
2370  ++next;
2371  MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2372  if (mrv) {
2373  mrv->begin_resizing (at_front);
2374  }
2375  r = next;
2376  }
2377 }
2378 
2379 void
2380 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
2381 {
2383  for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2384  NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2385  assert (nb);
2386  MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2387  if (mrv) {
2389  }
2390  }
2391 }
2392 
2393 void
2394 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
2395 {
2397  for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2398  NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2399  assert (nb);
2400  MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2401  if (mrv) {
2403  }
2404  }
2405 
2407 }
2408 
2409 void
2411 {
2413  for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2414  MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2415  if (mrv) {
2416  mrv->abort_resizing ();
2417  }
2418  }
2419 }
2420 
2422  : view (v)
2423 {
2424  initial_position = v->region()->position ();
2425 }
2426 
2428  : Drag (e, i)
2429 {
2430  DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2431 
2432  RegionSelection rs;
2433  TrackViewList empty;
2434  empty.clear();
2435  _editor->get_regions_after(rs, (framepos_t) 0, empty);
2436  std::list<RegionView*> views = rs.by_layer();
2437 
2438  for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2439  RegionView* rv = (*i);
2440  if (!rv->region()->video_locked()) {
2441  continue;
2442  }
2443  _views.push_back (AVDraggingView (rv));
2444  }
2445 }
2446 
2447 void
2448 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2449 {
2450  Drag::start_grab (event);
2451  if (_editor->session() == 0) {
2452  return;
2453  }
2454 
2459  - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2460  );
2461 
2462  for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2463  if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2465  }
2466  }
2467  DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2468 
2469  char buf[128];
2470  Timecode::Time timecode;
2471  _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2472  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);
2474 }
2475 
2476 void
2477 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2478 {
2479  if (_editor->session() == 0) {
2480  return;
2481  }
2483  return;
2484  }
2485 
2488 
2489  if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2490  dt = - _max_backwards_drag;
2491  }
2492 
2495 
2496  for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2497  RegionView* rv = i->view;
2498  DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2499  if (first_move) {
2500  rv->drag_start ();
2501  rv->region()->clear_changes ();
2503  }
2504  rv->region()->set_position(i->initial_position + dt);
2506  }
2507 
2509  Timecode::Time timecode;
2510  Timecode::Time timediff;
2511  char buf[128];
2512  _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2513  _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2514  snprintf (buf, sizeof (buf),
2515  "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2516  "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2517  , _("Video Start:"),
2518  (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2519  , _("Diff:"),
2520  (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2521  );
2523 }
2524 
2525 void
2526 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2527 {
2528  if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2529  return;
2530  }
2531 
2532  if (!movement_occurred || ! _editor->session()) {
2533  return;
2534  }
2535 
2537 
2538  _editor->begin_reversible_command (_("Move Video"));
2539 
2543  _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2544 
2545  for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2546  i->view->drag_end();
2547  i->view->region()->resume_property_changes ();
2548 
2549  _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2550  }
2551 
2553  std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2554  std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2555  );
2556 
2557 
2559 }
2560 
2561 void
2563 {
2564  if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2565  return;
2566  }
2569 
2570  for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2571  i->view->region()->resume_property_changes ();
2572  i->view->region()->set_position(i->initial_position);
2573  }
2574 }
2575 
2576 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2577  : RegionDrag (e, i, p, v)
2578  , _preserve_fade_anchor (preserve_fade_anchor)
2579  , _jump_position_when_done (false)
2580 {
2581  DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2582 }
2583 
2584 void
2585 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2586 {
2587  double speed = 1.0;
2589  RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2590 
2591  if (tv && tv->is_track()) {
2592  speed = tv->track()->speed();
2593  }
2594 
2595  framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2596  framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2597  framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2598 
2599  framepos_t const pf = adjusted_current_frame (event);
2600 
2601  if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2602  /* Move the contents of the region around without changing the region bounds */
2604  Drag::start_grab (event, _editor->cursors()->trimmer);
2605  } else {
2606  /* These will get overridden for a point trim.*/
2607  if (pf < (region_start + region_length/2)) {
2608  /* closer to front */
2610 
2611  if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2613  } else {
2615  }
2616  } else {
2617  /* closer to end */
2618  _operation = EndTrim;
2619  if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2621  } else {
2623  }
2624  }
2625  }
2626 
2627  if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2628  _jump_position_when_done = true;
2629  }
2630 
2631  switch (_operation) {
2632  case StartTrim:
2633  show_verbose_cursor_time (region_start);
2634  for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2635  i->view->trim_front_starting ();
2636  }
2637  break;
2638  case EndTrim:
2639  show_verbose_cursor_time (region_end);
2640  break;
2641  case ContentsTrim:
2643  break;
2644  }
2645 
2646  for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2647  i->view->region()->suspend_property_changes ();
2648  }
2649 }
2650 
2651 void
2652 TrimDrag::motion (GdkEvent* event, bool first_move)
2653 {
2654  RegionView* rv = _primary;
2655 
2656  double speed = 1.0;
2658  RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2659  pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2660  frameoffset_t frame_delta = 0;
2661 
2662  if (tv && tv->is_track()) {
2663  speed = tv->track()->speed();
2664  }
2665 
2667 
2668  if (first_move) {
2669 
2670  string trim_type;
2671 
2672  switch (_operation) {
2673  case StartTrim:
2674  trim_type = "Region start trim";
2675  break;
2676  case EndTrim:
2677  trim_type = "Region end trim";
2678  break;
2679  case ContentsTrim:
2680  trim_type = "Region content trim";
2681  break;
2682  default:
2683  assert(0);
2684  break;
2685  }
2686 
2687  _editor->begin_reversible_command (trim_type);
2688 
2689  for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2690  RegionView* rv = i->view;
2691  rv->enable_display (false);
2692  rv->region()->playlist()->clear_owned_changes ();
2693 
2694  AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2695 
2696  if (arv) {
2697  arv->temporarily_hide_envelope ();
2698  arv->drag_start ();
2699  }
2700 
2702  insert_result = _editor->motion_frozen_playlists.insert (pl);
2703 
2704  if (insert_result.second) {
2705  pl->freeze();
2706  }
2707  }
2708  }
2709 
2710  bool non_overlap_trim = false;
2711 
2712  if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2713  non_overlap_trim = true;
2714  }
2715 
2716  /* contstrain trim to fade length */
2717  if (_preserve_fade_anchor) {
2718  switch (_operation) {
2719  case StartTrim:
2720  for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2721  AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2722  if (!arv) continue;
2724  if (ar->locked()) continue;
2725  framecnt_t len = ar->fade_in()->back()->when;
2726  if (len < dt) dt = min(dt, len);
2727  }
2728  break;
2729  case EndTrim:
2730  for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2731  AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2732  if (!arv) continue;
2734  if (ar->locked()) continue;
2735  framecnt_t len = ar->fade_out()->back()->when;
2736  if (len < -dt) dt = max(dt, -len);
2737  }
2738  break;
2739  case ContentsTrim:
2740  break;
2741  }
2742  }
2743 
2744 
2745  switch (_operation) {
2746  case StartTrim:
2747  for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2748  bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2749  if (changed && _preserve_fade_anchor) {
2750  AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2751  if (arv) {
2753  framecnt_t len = ar->fade_in()->back()->when;
2754  framecnt_t diff = ar->first_frame() - i->initial_position;
2755  framepos_t new_length = len - diff;
2756  i->anchored_fade_length = min (ar->length(), new_length);
2757  //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2758  arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2759  }
2760  }
2761  }
2762  break;
2763 
2764  case EndTrim:
2765  for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2766  bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2767  if (changed && _preserve_fade_anchor) {
2768  AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2769  if (arv) {
2771  framecnt_t len = ar->fade_out()->back()->when;
2772  framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2773  framepos_t new_length = len + diff;
2774  i->anchored_fade_length = min (ar->length(), new_length);
2775  //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2776  arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2777  }
2778  }
2779  }
2780  break;
2781 
2782  case ContentsTrim:
2783  {
2784  frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2785 
2786  for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2787  i->view->move_contents (frame_delta);
2788  }
2789  }
2790  break;
2791  }
2792 
2793  switch (_operation) {
2794  case StartTrim:
2795  show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2796  break;
2797  case EndTrim:
2798  show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2799  break;
2800  case ContentsTrim:
2801  // show_verbose_cursor_time (frame_delta);
2802  break;
2803  }
2804 }
2805 
2806 
2807 void
2808 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2809 {
2810  if (movement_occurred) {
2811  motion (event, false);
2812 
2813  if (_operation == StartTrim) {
2814  for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2815  {
2816  /* This must happen before the region's StatefulDiffCommand is created, as it may
2817  `correct' (ahem) the region's _start from being negative to being zero. It
2818  needs to be zero in the undo record.
2819  */
2820  i->view->trim_front_ending ();
2821  }
2822  if (_preserve_fade_anchor) {
2823  AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2824  if (arv) {
2826  arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2827  ar->set_fade_in_length(i->anchored_fade_length);
2828  ar->set_fade_in_active(true);
2829  }
2830  }
2832  i->view->region()->set_position (i->initial_position);
2833  }
2834  }
2835  } else if (_operation == EndTrim) {
2836  for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2837  if (_preserve_fade_anchor) {
2838  AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2839  if (arv) {
2841  arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2842  ar->set_fade_out_length(i->anchored_fade_length);
2843  ar->set_fade_out_active(true);
2844  }
2845  }
2847  i->view->region()->set_position (i->initial_end - i->view->region()->length());
2848  }
2849  }
2850  }
2851 
2852  if (!_views.empty()) {
2853  if (_operation == StartTrim) {
2855  _views.begin()->view->region()->position());
2856  }
2857  if (_operation == EndTrim) {
2859  _views.begin()->view->region()->position() +
2860  _views.begin()->view->region()->length());
2861  }
2862  }
2863 
2864  if (!_editor->selection->selected (_primary)) {
2866  } else {
2867 
2868  set<boost::shared_ptr<Playlist> > diffed_playlists;
2869 
2870  for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2871  i->view->thaw_after_trim ();
2872  i->view->enable_display (true);
2873 
2874  /* Trimming one region may affect others on the playlist, so we need
2875  to get undo Commands from the whole playlist rather than just the
2876  region. Use diffed_playlists to make sure we don't diff a given
2877  playlist more than once.
2878  */
2879  boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2880  if (diffed_playlists.find (p) == diffed_playlists.end()) {
2881  vector<Command*> cmds;
2882  p->rdiff (cmds);
2883  _editor->session()->add_commands (cmds);
2884  diffed_playlists.insert (p);
2885  }
2886  }
2887  }
2888 
2889  for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2890  (*p)->thaw ();
2891  }
2892 
2893  _editor->motion_frozen_playlists.clear ();
2895 
2896  } else {
2897  /* no mouse movement */
2898  _editor->point_trim (event, adjusted_current_frame (event));
2899  }
2900 
2901  for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2902  if (_operation == StartTrim) {
2903  i->view->trim_front_ending ();
2904  }
2905 
2906  i->view->region()->resume_property_changes ();
2907  }
2908 }
2909 
2910 void
2911 TrimDrag::aborted (bool movement_occurred)
2912 {
2913  /* Our motion method is changing model state, so use the Undo system
2914  to cancel. Perhaps not ideal, as this will leave an Undo point
2915  behind which may be slightly odd from the user's point of view.
2916  */
2917 
2918  finished (0, true);
2919 
2920  if (movement_occurred) {
2921  _editor->undo ();
2922  }
2923 
2924  for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2925  i->view->region()->resume_property_changes ();
2926  }
2927 }
2928 
2929 void
2931 {
2932  list<DraggingView>::iterator i = _views.begin ();
2933  while (i != _views.end() && i->view != _primary) {
2934  ++i;
2935  }
2936 
2937  if (i == _views.end()) {
2938  return;
2939  }
2940 
2941  switch (_operation) {
2942  case StartTrim:
2943  _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2944  break;
2945  case EndTrim:
2946  _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2947  break;
2948  case ContentsTrim:
2949  break;
2950  }
2951 }
2952 
2953 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2954  : Drag (e, i),
2955  _copy (c)
2956 {
2957  DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2958  _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2959  assert (_marker);
2960 }
2961 
2962 void
2963 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2964 {
2965  Drag::start_grab (event, cursor);
2967 }
2968 
2969 void
2971 {
2973 }
2974 
2975 void
2976 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2977 {
2978  if (!_marker->meter().movable()) {
2979  return;
2980  }
2981 
2982  if (first_move) {
2983 
2984  // create a dummy marker for visual representation of moving the
2985  // section, because whether its a copy or not, we're going to
2986  // leave or lose the original marker (leave if its a copy; lose if its
2987  // not, because we'll remove it from the map).
2988 
2989  MeterSection section (_marker->meter());
2990 
2991  if (!section.movable()) {
2992  return;
2993  }
2994 
2995  char name[64];
2996  snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2997 
2998  _marker = new MeterMarker (
2999  *_editor,
3000  *_editor->meter_group,
3001  ARDOUR_UI::config()->color ("meter marker"),
3002  name,
3003  *new MeterSection (_marker->meter())
3004  );
3005 
3006  /* use the new marker for the grab */
3007  swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3008 
3009  if (!_copy) {
3010  TempoMap& map (_editor->session()->tempo_map());
3011  /* get current state */
3012  before_state = &map.get_state();
3013  /* remove the section while we drag it */
3014  map.remove_meter (section, true);
3015  }
3016  }
3017 
3018  framepos_t const pf = adjusted_current_frame (event);
3019 
3020  _marker->set_position (pf);
3022 }
3023 
3024 void
3025 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3026 {
3027  if (!movement_occurred) {
3028  if (was_double_click()) {
3030  }
3031  return;
3032  }
3033 
3034  if (!_marker->meter().movable()) {
3035  return;
3036  }
3037 
3038  motion (event, false);
3039 
3040  Timecode::BBT_Time when;
3041 
3042  TempoMap& map (_editor->session()->tempo_map());
3043  map.bbt_time (last_pointer_frame(), when);
3044 
3045  if (_copy == true) {
3046  _editor->begin_reversible_command (_("copy meter mark"));
3047  XMLNode &before = map.get_state();
3048  map.add_meter (_marker->meter(), when);
3049  XMLNode &after = map.get_state();
3050  _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
3052 
3053  } else {
3054  _editor->begin_reversible_command (_("move meter mark"));
3055 
3056  /* we removed it before, so add it back now */
3057 
3058  map.add_meter (_marker->meter(), when);
3059  XMLNode &after = map.get_state();
3062  }
3063 
3064  // delete the dummy marker we used for visual representation while moving.
3065  // a new visual marker will show up automatically.
3066  delete _marker;
3067 }
3068 
3069 void
3071 {
3073 
3074  if (moved) {
3075  TempoMap& map (_editor->session()->tempo_map());
3076  /* we removed it before, so add it back now */
3077  map.add_meter (_marker->meter(), _marker->meter().frame());
3078  // delete the dummy marker we used for visual representation while moving.
3079  // a new visual marker will show up automatically.
3080  delete _marker;
3081  }
3082 }
3083 
3084 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3085  : Drag (e, i),
3086  _copy (c)
3087 {
3088  DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3089 
3090  _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3091  assert (_marker);
3092 }
3093 
3094 void
3095 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3096 {
3097  Drag::start_grab (event, cursor);
3099 }
3100 
3101 void
3103 {
3105 }
3106 
3107 void
3108 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3109 {
3110  if (!_marker->tempo().movable()) {
3111  return;
3112  }
3113 
3114  if (first_move) {
3115 
3116  // create a dummy marker for visual representation of moving the
3117  // section, because whether its a copy or not, we're going to
3118  // leave or lose the original marker (leave if its a copy; lose if its
3119  // not, because we'll remove it from the map).
3120 
3121  // create a dummy marker for visual representation of moving the copy.
3122  // The actual copying is not done before we reach the finish callback.
3123 
3124  char name[64];
3125  snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3126 
3127  TempoSection section (_marker->tempo());
3128 
3129  _marker = new TempoMarker (
3130  *_editor,
3131  *_editor->tempo_group,
3132  ARDOUR_UI::config()->color ("tempo marker"),
3133  name,
3134  *new TempoSection (_marker->tempo())
3135  );
3136 
3137  /* use the new marker for the grab */
3138  swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3139 
3140  if (!_copy) {
3141  TempoMap& map (_editor->session()->tempo_map());
3142  /* get current state */
3143  before_state = &map.get_state();
3144  /* remove the section while we drag it */
3145  map.remove_tempo (section, true);
3146  }
3147  }
3148 
3149  framepos_t const pf = adjusted_current_frame (event);
3150  _marker->set_position (pf);
3152 }
3153 
3154 void
3155 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3156 {
3157  if (!movement_occurred) {
3158  if (was_double_click()) {
3160  }
3161  return;
3162  }
3163 
3164  if (!_marker->tempo().movable()) {
3165  return;
3166  }
3167 
3168  motion (event, false);
3169 
3170  TempoMap& map (_editor->session()->tempo_map());
3172  Timecode::BBT_Time when;
3173 
3174  map.bbt_time (beat_time, when);
3175 
3176  if (_copy == true) {
3177  _editor->begin_reversible_command (_("copy tempo mark"));
3178  XMLNode &before = map.get_state();
3179  map.add_tempo (_marker->tempo(), when);
3180  XMLNode &after = map.get_state();
3181  _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3183 
3184  } else {
3185  _editor->begin_reversible_command (_("move tempo mark"));
3186  /* we removed it before, so add it back now */
3187  map.add_tempo (_marker->tempo(), when);
3188  XMLNode &after = map.get_state();
3191  }
3192 
3193  // delete the dummy marker we used for visual representation while moving.
3194  // a new visual marker will show up automatically.
3195  delete _marker;
3196 }
3197 
3198 void
3200 {
3202  if (moved) {
3203  TempoMap& map (_editor->session()->tempo_map());
3204  /* we removed it before, so add it back now */
3205  map.add_tempo (_marker->tempo(), _marker->tempo().start());
3206  // delete the dummy marker we used for visual representation while moving.
3207  // a new visual marker will show up automatically.
3208  delete _marker;
3209  }
3210 }
3211 
3213  : Drag (e, &c.track_canvas_item(), false)
3214  , _cursor (c)
3215  , _stop (s)
3216 {
3217  DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3218 }
3219 
3224 void
3226 {
3228 
3229  Session* s = _editor->session ();
3230  if (s->timecode_transmission_suspended ()) {
3232  /* This is asynchronous so it will be sent "now"
3233  */
3234  s->send_mmc_locate (f);
3235  /* These are synchronous and will be sent during the next
3236  process cycle
3237  */
3238  s->queue_full_time_code ();
3240  }
3241 
3244 }
3245 
3246 void
3247 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3248 {
3249  Drag::start_grab (event, c);
3250 
3252 
3253  framepos_t where = _editor->canvas_event_sample (event);
3254 
3255  _editor->snap_to_with_modifier (where, event);
3256 
3257  _editor->_dragging_playhead = true;
3258 
3259  Session* s = _editor->session ();
3260 
3261  /* grab the track canvas item as well */
3262 
3263  _cursor.track_canvas_item().grab();
3264 
3265  if (s) {
3266  if (_was_rolling && _stop) {
3267  s->request_stop ();
3268  }
3269 
3270  if (s->is_auditioning()) {
3271  s->cancel_audition ();
3272  }
3273 
3274 
3275  if (AudioEngine::instance()->connected()) {
3276 
3277  /* do this only if we're the engine is connected
3278  * because otherwise this request will never be
3279  * serviced and we'll busy wait forever. likewise,
3280  * notice if we are disconnected while waiting for the
3281  * request to be serviced.
3282  */
3283 
3285  while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3286  /* twiddle our thumbs */
3287  }
3288  }
3289  }
3290 
3291  fake_locate (where);
3292 }
3293 
3294 void
3295 CursorDrag::motion (GdkEvent* event, bool)
3296 {
3298  if (adjusted_frame != last_pointer_frame()) {
3299  fake_locate (adjusted_frame);
3300  }
3301 }
3302 
3303 void
3304 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3305 {
3306  _editor->_dragging_playhead = false;
3307 
3308  _cursor.track_canvas_item().ungrab();
3309 
3310  if (!movement_occurred && _stop) {
3311  return;
3312  }
3313 
3314  motion (event, false);
3315 
3316  Session* s = _editor->session ();
3317  if (s) {
3321  }
3322 }
3323 
3324 void
3326 {
3327  _cursor.track_canvas_item().ungrab();
3328 
3329  if (_editor->_dragging_playhead) {
3331  _editor->_dragging_playhead = false;
3332  }
3333 
3335 }
3336 
3337 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3338  : RegionDrag (e, i, p, v)
3339 {
3340  DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3341 }
3342 
3343 void
3344 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3345 {
3346  Drag::start_grab (event, cursor);
3347 
3348  AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3349  boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3350 
3351  show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3352 }
3353 
3354 void
3356 {
3357  AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3358  boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3360 }
3361 
3362 void
3363 FadeInDrag::motion (GdkEvent* event, bool)
3364 {
3365  framecnt_t fade_length;
3366  framepos_t const pos = adjusted_current_frame (event);
3368 
3369  if (pos < (region->position() + 64)) {
3370  fade_length = 64; // this should be a minimum defined somewhere
3371  } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3372  fade_length = region->length() - region->fade_out()->back()->when - 1;
3373  } else {
3374  fade_length = pos - region->position();
3375  }
3376 
3377  for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3378 
3379  AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3380 
3381  if (!tmp) {
3382  continue;
3383  }
3384 
3385  tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3386  }
3387 
3388  show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3389 }
3390 
3391 void
3392 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3393 {
3394  if (!movement_occurred) {
3395  return;
3396  }
3397 
3398  framecnt_t fade_length;
3399 
3400  framepos_t const pos = adjusted_current_frame (event);
3401 
3403 
3404  if (pos < (region->position() + 64)) {
3405  fade_length = 64; // this should be a minimum defined somewhere
3406  } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3407  fade_length = region->length() - region->fade_out()->back()->when - 1;
3408  } else {
3409  fade_length = pos - region->position();
3410  }
3411 
3412  _editor->begin_reversible_command (_("change fade in length"));
3413 
3414  for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3415 
3416  AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3417 
3418  if (!tmp) {
3419  continue;
3420  }
3421 
3423  XMLNode &before = alist->get_state();
3424 
3425  tmp->audio_region()->set_fade_in_length (fade_length);
3426  tmp->audio_region()->set_fade_in_active (true);
3427 
3428  XMLNode &after = alist->get_state();
3429  _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3430  }
3431 
3433 }
3434 
3435 void
3437 {
3438  for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3439  AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3440 
3441  if (!tmp) {
3442  continue;
3443  }
3444 
3445  tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3446  }
3447 }
3448 
3449 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3450  : RegionDrag (e, i, p, v)
3451 {
3452  DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3453 }
3454 
3455 void
3456 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3457 {
3458  Drag::start_grab (event, cursor);
3459 
3460  AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3462 
3464 }
3465 
3466 void
3468 {
3469  AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3472 }
3473 
3474 void
3475 FadeOutDrag::motion (GdkEvent* event, bool)
3476 {
3477  framecnt_t fade_length;
3478 
3479  framepos_t const pos = adjusted_current_frame (event);
3480 
3482 
3483  if (pos > (region->last_frame() - 64)) {
3484  fade_length = 64; // this should really be a minimum fade defined somewhere
3485  } else if (pos <= region->position() + region->fade_in()->back()->when) {
3486  fade_length = region->length() - region->fade_in()->back()->when - 1;
3487  } else {
3488  fade_length = region->last_frame() - pos;
3489  }
3490 
3491  for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3492 
3493  AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3494 
3495  if (!tmp) {
3496  continue;
3497  }
3498 
3499  tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3500  }
3501 
3502  show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3503 }
3504 
3505 void
3506 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3507 {
3508  if (!movement_occurred) {
3509  return;
3510  }
3511 
3512  framecnt_t fade_length;
3513 
3514  framepos_t const pos = adjusted_current_frame (event);
3515 
3517 
3518  if (pos > (region->last_frame() - 64)) {
3519  fade_length = 64; // this should really be a minimum fade defined somewhere
3520  } else if (pos <= region->position() + region->fade_in()->back()->when) {
3521  fade_length = region->length() - region->fade_in()->back()->when - 1;
3522  } else {
3523  fade_length = region->last_frame() - pos;
3524  }
3525 
3526  _editor->begin_reversible_command (_("change fade out length"));
3527 
3528  for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3529 
3530  AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3531 
3532  if (!tmp) {
3533  continue;
3534  }
3535 
3537  XMLNode &before = alist->get_state();
3538 
3539  tmp->audio_region()->set_fade_out_length (fade_length);
3540  tmp->audio_region()->set_fade_out_active (true);
3541 
3542  XMLNode &after = alist->get_state();
3543  _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3544  }
3545 
3547 }
3548 
3549 void
3551 {
3552  for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3553  AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3554 
3555  if (!tmp) {
3556  continue;
3557  }
3558 
3560  }
3561 }
3562 
3563 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3564  : Drag (e, i)
3565 {
3566  DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3567 
3568  _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
3569  assert (_marker);
3570 
3571  _points.push_back (ArdourCanvas::Duple (0, 0));
3572  _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3573 }
3574 
3576 {
3577  for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3578  delete i->location;
3579  }
3580 }
3581 
3583 {
3584  location = new Location (*l);
3585  markers.push_back (m);
3586  move_both = false;
3587 }
3588 
3589 void
3590 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3591 {
3592  Drag::start_grab (event, cursor);
3593 
3594  bool is_start;
3595 
3596  Location *location = _editor->find_location_from_marker (_marker, is_start);
3597  _editor->_dragging_edit_point = true;
3598 
3599  update_item (location);
3600 
3601  // _drag_line->show();
3602  // _line->raise_to_top();
3603 
3604  if (is_start) {
3605  show_verbose_cursor_time (location->start());
3606  } else {
3607  show_verbose_cursor_time (location->end());
3608  }
3609 
3610  Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3611 
3612  switch (op) {
3613  case Selection::Toggle:
3614  /* we toggle on the button release */
3615  break;
3616  case Selection::Set:
3617  if (!_editor->selection->selected (_marker)) {
3619  }
3620  break;
3621  case Selection::Extend:
3622  {
3624  list<Marker*> to_add;
3625  framepos_t s, e;
3626  _editor->selection->markers.range (s, e);
3627  s = min (_marker->position(), s);
3628  e = max (_marker->position(), e);
3629  s = min (s, e);
3630  e = max (s, e);
3631  if (e < max_framepos) {
3632  ++e;
3633  }
3634  _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3635  for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3637  if (lm) {
3638  if (lm->start) {
3639  to_add.push_back (lm->start);
3640  }
3641  if (lm->end) {
3642  to_add.push_back (lm->end);
3643  }
3644  }
3645  }
3646  if (!to_add.empty()) {
3647  _editor->selection->add (to_add);
3648  }
3649  break;
3650  }
3651  case Selection::Add:
3653  break;
3654  }
3655 
3656  /* Set up copies for us to manipulate during the drag
3657  */
3658 
3659  for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3660 
3661  Location* l = _editor->find_location_from_marker (*i, is_start);
3662 
3663  if (!l) {
3664  continue;
3665  }
3666 
3667  if (l->is_mark()) {
3668  _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3669  } else {
3670  /* range: check that the other end of the range isn't
3671  already there.
3672  */
3673  CopiedLocationInfo::iterator x;
3674  for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3675  if (*(*x).location == *l) {
3676  break;
3677  }
3678  }
3679  if (x == _copied_locations.end()) {
3680  _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3681  } else {
3682  (*x).markers.push_back (*i);
3683  (*x).move_both = true;
3684  }
3685  }
3686 
3687  }
3688 }
3689 
3690 void
3692 {
3693  bool is_start;
3694  Location *location = _editor->find_location_from_marker (_marker, is_start);
3695  _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3696 }
3697 
3698 void
3699 MarkerDrag::motion (GdkEvent* event, bool)
3700 {
3701  framecnt_t f_delta = 0;
3702  bool is_start;
3703  bool move_both = false;
3704  Location *real_location;
3705  Location *copy_location = 0;
3706 
3707  framepos_t const newframe = adjusted_current_frame (event);
3708  framepos_t next = newframe;
3709 
3710  if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
3711  move_both = true;
3712  }
3713 
3714  CopiedLocationInfo::iterator x;
3715 
3716  /* find the marker we're dragging, and compute the delta */
3717 
3718  for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3719 
3720  copy_location = (*x).location;
3721 
3722  if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3723 
3724  /* this marker is represented by this
3725  * CopiedLocationMarkerInfo
3726  */
3727 
3728  if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3729  /* que pasa ?? */
3730  return;
3731  }
3732 
3733  if (real_location->is_mark()) {
3734  f_delta = newframe - copy_location->start();
3735  } else {
3736 
3737 
3738  switch (_marker->type()) {
3739  case Marker::SessionStart:
3740  case Marker::RangeStart:
3741  case Marker::LoopStart:
3742  case Marker::PunchIn:
3743  f_delta = newframe - copy_location->start();
3744  break;
3745 
3746  case Marker::SessionEnd:
3747  case Marker::RangeEnd:
3748  case Marker::LoopEnd:
3749  case Marker::PunchOut:
3750  f_delta = newframe - copy_location->end();
3751  break;
3752  default:
3753  /* what kind of marker is this ? */
3754  return;
3755  }
3756  }
3757 
3758  break;
3759  }
3760  }
3761 
3762  if (x == _copied_locations.end()) {
3763  /* hmm, impossible - we didn't find the dragged marker */
3764  return;
3765  }
3766 
3767  /* now move them all */
3768 
3769  for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3770 
3771  copy_location = x->location;
3772 
3773  if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3774  continue;
3775  }
3776 
3777  if (real_location->locked()) {
3778  continue;
3779  }
3780 
3781  if (copy_location->is_mark()) {
3782 
3783  /* now move it */
3784 
3785  copy_location->set_start (copy_location->start() + f_delta);
3786 
3787  } else {
3788 
3789  framepos_t new_start = copy_location->start() + f_delta;
3790  framepos_t new_end = copy_location->end() + f_delta;
3791 
3792  if (is_start) { // start-of-range marker
3793 
3794  if (move_both || (*x).move_both) {
3795  copy_location->set_start (new_start);
3796  copy_location->set_end (new_end);
3797  } else if (new_start < copy_location->end()) {
3798  copy_location->set_start (new_start);
3799  } else if (newframe > 0) {
3800  _editor->snap_to (next, RoundUpAlways, true);
3801  copy_location->set_end (next);
3802  copy_location->set_start (newframe);
3803  }
3804 
3805  } else { // end marker
3806 
3807  if (move_both || (*x).move_both) {
3808  copy_location->set_end (new_end);
3809  copy_location->set_start (new_start);
3810  } else if (new_end > copy_location->start()) {
3811  copy_location->set_end (new_end);
3812  } else if (newframe > 0) {
3813  _editor->snap_to (next, RoundDownAlways, true);
3814  copy_location->set_start (next);
3815  copy_location->set_end (newframe);
3816  }
3817  }
3818  }
3819 
3820  update_item (copy_location);
3821 
3822  /* now lookup the actual GUI items used to display this
3823  * location and move them to wherever the copy of the location
3824  * is now. This means that the logic in ARDOUR::Location is
3825  * still enforced, even though we are not (yet) modifying
3826  * the real Location itself.
3827  */
3828 
3829  Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3830 
3831  if (lm) {
3832  lm->set_position (copy_location->start(), copy_location->end());
3833  }
3834 
3835  }
3836 
3837  assert (!_copied_locations.empty());
3838 
3839  show_verbose_cursor_time (newframe);
3840 }
3841 
3842 void
3843 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3844 {
3845  if (!movement_occurred) {
3846 
3847  if (was_double_click()) {
3849  return;
3850  }
3851 
3852  /* just a click, do nothing but finish
3853  off the selection process
3854  */
3855 
3856  Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3857 
3858  switch (op) {
3859  case Selection::Set:
3860  if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3862  }
3863  break;
3864 
3865  case Selection::Toggle:
3866  /* we toggle on the button release, click only */
3868  break;
3869 
3870  case Selection::Extend:
3871  case Selection::Add:
3872  break;
3873  }
3874 
3875  return;
3876  }
3877 
3878  _editor->_dragging_edit_point = false;
3879 
3880  _editor->begin_reversible_command ( _("move marker") );
3881  XMLNode &before = _editor->session()->locations()->get_state();
3882 
3883  MarkerSelection::iterator i;
3884  CopiedLocationInfo::iterator x;
3885  bool is_start;
3886 
3887  for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3888  x != _copied_locations.end() && i != _editor->selection->markers.end();
3889  ++i, ++x) {
3890 
3891  Location * location = _editor->find_location_from_marker (*i, is_start);
3892 
3893  if (location) {
3894 
3895  if (location->locked()) {
3896  return;
3897  }
3898 
3899  if (location->is_mark()) {
3900  location->set_start (((*x).location)->start());
3901  } else {
3902  location->set (((*x).location)->start(), ((*x).location)->end());
3903  }
3904  }
3905  }
3906 
3907  XMLNode &after = _editor->session()->locations()->get_state();
3908  _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3910 }
3911 
3912 void
3913 MarkerDrag::aborted (bool movement_occured)
3914 {
3915  if (!movement_occured) {
3916  return;
3917  }
3918 
3919  for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3920 
3921  /* move all markers to their original location */
3922 
3923 
3924  for (vector<Marker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
3925 
3926  bool is_start;
3927  Location * location = _editor->find_location_from_marker (*m, is_start);
3928 
3929  if (location) {
3930  (*m)->set_position (is_start ? location->start() : location->end());
3931  }
3932  }
3933  }
3934 }
3935 
3936 void
3938 {
3939  /* noop */
3940 }
3941 
3942 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3943  : Drag (e, i),
3944  _cumulative_x_drag (0),
3945  _cumulative_y_drag (0)
3946 {
3947  if (_zero_gain_fraction < 0.0) {
3949  }
3950 
3951  DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3952 
3953  _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3954  assert (_point);
3955 }
3956 
3957 
3958 void
3959 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3960 {
3961  Drag::start_grab (event, _editor->cursors()->fader);
3962 
3963  // start the grab at the center of the control point so
3964  // the point doesn't 'jump' to the mouse after the first drag
3965  _fixed_grab_x = _point->get_x();
3966  _fixed_grab_y = _point->get_y();
3967 
3968  float const fraction = 1 - (_point->get_y() / _point->line().height());
3969 
3970  _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3971 
3973 
3974  _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3975 
3976  if (!_point->can_slide ()) {
3977  _x_constrained = true;
3978  }
3979 }
3980 
3981 void
3982 ControlPointDrag::motion (GdkEvent* event, bool)
3983 {
3984  double dx = _drags->current_pointer_x() - last_pointer_x();
3985  double dy = current_pointer_y() - last_pointer_y();
3986 
3987  if (event->button.state & Keyboard::SecondaryModifier) {
3988  dx *= 0.1;
3989  dy *= 0.1;
3990  }
3991 
3992  /* coordinate in pixels relative to the start of the region (for region-based automation)
3993  or track (for track-based automation) */
3994  double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3995  double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3996 
3997  // calculate zero crossing point. back off by .01 to stay on the
3998  // positive side of zero
3999  double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4000 
4001  // make sure we hit zero when passing through
4002  if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4003  cy = zero_gain_y;
4004  }
4005 
4006  if (_x_constrained) {
4007  cx = _fixed_grab_x;
4008  }
4009  if (_y_constrained) {
4010  cy = _fixed_grab_y;
4011  }
4012 
4015 
4016  cx = max (0.0, cx);
4017  cy = max (0.0, cy);
4018  cy = min ((double) _point->line().height(), cy);
4019 
4020  framepos_t cx_frames = _editor->pixel_to_sample (cx);
4021 
4022  if (!_x_constrained) {
4023  _editor->snap_to_with_modifier (cx_frames, event);
4024  }
4025 
4026  cx_frames = min (cx_frames, _point->line().maximum_time());
4027 
4028  float const fraction = 1.0 - (cy / _point->line().height());
4029 
4030  _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4031 
4033 }
4034 
4035 void
4036 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4037 {
4038  if (!movement_occurred) {
4039 
4040  /* just a click */
4041 
4042  if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4044  }
4045 
4046  } else {
4047  motion (event, false);
4048  }
4049 
4052 }
4053 
4054 void
4056 {
4057  _point->line().reset ();
4058 }
4059 
4060 bool
4062 {
4063  if (m == Editing::MouseDraw) {
4064  /* always active in mouse draw */
4065  return true;
4066  }
4067 
4068  /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4069  return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4070 }
4071 
4072 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4073  : Drag (e, i),
4074  _line (0),
4075  _cumulative_y_drag (0)
4076 {
4077  DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4078 }
4079 
4080 void
4081 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4082 {
4083  _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4084  assert (_line);
4085 
4086  _item = &_line->grab_item ();
4087 
4088  /* need to get x coordinate in terms of parent (TimeAxisItemView)
4089  origin, and ditto for y.
4090  */
4091 
4092  double cx = event->button.x;
4093  double cy = event->button.y;
4094 
4095  _line->parent_group().canvas_to_item (cx, cy);
4096 
4097  framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
4098 
4099  uint32_t before;
4100  uint32_t after;
4101 
4102  if (!_line->control_points_adjacent (frame_within_region, before, after)) {
4103  /* no adjacent points */
4104  return;
4105  }
4106 
4107  Drag::start_grab (event, _editor->cursors()->fader);
4108 
4109  /* store grab start in parent frame */
4110 
4111  _fixed_grab_x = cx;
4112  _fixed_grab_y = cy;
4113 
4114  double fraction = 1.0 - (cy / _line->height());
4115 
4116  _line->start_drag_line (before, after, fraction);
4117 
4119 }
4120 
4121 void
4122 LineDrag::motion (GdkEvent* event, bool)
4123 {
4124  double dy = current_pointer_y() - last_pointer_y();
4125 
4126  if (event->button.state & Keyboard::SecondaryModifier) {
4127  dy *= 0.1;
4128  }
4129 
4130  double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4131 
4133 
4134  cy = max (0.0, cy);
4135  cy = min ((double) _line->height(), cy);
4136 
4137  double const fraction = 1.0 - (cy / _line->height());
4138  uint32_t ignored;
4139 
4140  /* we are ignoring x position for this drag, so we can just pass in anything */
4141  _line->drag_motion (0, fraction, true, false, ignored);
4142 
4144 }
4145 
4146 void
4147 LineDrag::finished (GdkEvent* event, bool movement_occured)
4148 {
4149  if (movement_occured) {
4150  motion (event, false);
4151  _line->end_drag (false, 0);
4152  } else {
4153  /* add a new control point on the line */
4154 
4156 
4157  _line->end_drag (false, 0);
4158 
4159  if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4160  framepos_t where = _editor->window_event_sample (event, 0, 0);
4161  atv->add_automation_event (event, where, event->button.y, false);
4162  }
4163  }
4164 
4166 }
4167 
4168 void
4170 {
4171  _line->reset ();
4172 }
4173 
4174 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4175  : Drag (e, i),
4176  _line (0),
4177  _cumulative_x_drag (0)
4178 {
4179  DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4180 }
4181 
4182 void
4183 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4184 {
4185  Drag::start_grab (event);
4186 
4187  _line = reinterpret_cast<Line*> (_item);
4188  assert (_line);
4189 
4190  /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4191 
4192  double cx = event->button.x;
4193  double cy = event->button.y;
4194 
4195  _item->parent()->canvas_to_item (cx, cy);
4196 
4197  /* store grab start in parent frame */
4198  _region_view_grab_x = cx;
4199 
4200  _before = *(float*) _item->get_data ("position");
4201 
4202  _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4203 
4205 }
4206 
4207 void
4208 FeatureLineDrag::motion (GdkEvent*, bool)
4209 {
4210  double dx = _drags->current_pointer_x() - last_pointer_x();
4211 
4212  double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4213 
4214  _cumulative_x_drag += dx;
4215 
4216  /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4217 
4218  if (cx > _max_x){
4219  cx = _max_x;
4220  }
4221  else if(cx < 0){
4222  cx = 0;
4223  }
4224 
4225  boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4226  assert (bbox);
4227  _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4228 
4229  float *pos = new float;
4230  *pos = cx;
4231 
4232  _line->set_data ("position", pos);
4233 
4234  _before = cx;
4235 }
4236 
4237 void
4238 FeatureLineDrag::finished (GdkEvent*, bool)
4239 {
4240  _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4242 }
4243 
4244 void
4246 {
4247  //_line->reset ();
4248 }
4249 
4251  : Drag (e, i)
4252  , _vertical_only (false)
4253 {
4254  DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4255 }
4256 
4257 void
4258 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4259 {
4260  Drag::start_grab (event);
4261  show_verbose_cursor_time (adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid()));
4262 }
4263 
4264 void
4265 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4266 {
4267  framepos_t start;
4268  framepos_t end;
4269  double y1;
4270  double y2;
4271 
4272  framepos_t const pf = adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid());
4273 
4274  framepos_t grab = grab_frame ();
4275  if (ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4276  _editor->snap_to_with_modifier (grab, event);
4277  } else {
4278  grab = raw_grab_frame ();
4279  }
4280 
4281  /* base start and end on initial click position */
4282 
4283  if (pf < grab) {
4284  start = pf;
4285  end = grab;
4286  } else {
4287  end = pf;
4288  start = grab;
4289  }
4290 
4291  if (current_pointer_y() < grab_y()) {
4292  y1 = current_pointer_y();
4293  y2 = grab_y();
4294  } else {
4295  y2 = current_pointer_y();
4296  y1 = grab_y();
4297  }
4298 
4299  if (start != end || y1 != y2) {
4300 
4301  double x1 = _editor->sample_to_pixel (start);
4302  double x2 = _editor->sample_to_pixel (end);
4303  const double min_dimension = 2.0;
4304 
4305  if (_vertical_only) {
4306  /* fixed 10 pixel width */
4307  x2 = x1 + 10;
4308  } else {
4309  if (x2 < x1) {
4310  x2 = min (x1 - min_dimension, x2);
4311  } else {
4312  x2 = max (x1 + min_dimension, x2);
4313  }
4314  }
4315 
4316  if (y2 < y1) {
4317  y2 = min (y1 - min_dimension, y2);
4318  } else {
4319  y2 = max (y1 + min_dimension, y2);
4320  }
4321 
4322  /* translate rect into item space and set */
4323 
4324  ArdourCanvas::Rect r (x1, y1, x2, y2);
4325 
4326  /* this drag is a _trackview_only == true drag, so the y1 and
4327  * y2 (computed using current_pointer_y() and grab_y()) will be
4328  * relative to the top of the trackview group). The
4329  * rubberband rect has the same parent/scroll offset as the
4330  * the trackview group, so we can use the "r" rect directly
4331  * to set the shape of the rubberband.
4332  */
4333 
4334  _editor->rubberband_rect->set (r);
4335  _editor->rubberband_rect->show();
4336  _editor->rubberband_rect->raise_to_top();
4337 
4339 
4340  do_select_things (event, true);
4341  }
4342 }
4343 
4344 void
4345 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4346 {
4347  framepos_t x1;
4348  framepos_t x2;
4349  framepos_t grab = grab_frame ();
4350  framepos_t lpf = last_pointer_frame ();
4351 
4352  if (!ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4353  grab = raw_grab_frame ();
4355  }
4356 
4357  if (grab < lpf) {
4358  x1 = grab;
4359  x2 = lpf;
4360  } else {
4361  x2 = grab;
4362  x1 = lpf;
4363  }
4364 
4365  double y1;
4366  double y2;
4367 
4368  if (current_pointer_y() < grab_y()) {
4369  y1 = current_pointer_y();
4370  y2 = grab_y();
4371  } else {
4372  y2 = current_pointer_y();
4373  y1 = grab_y();
4374  }
4375 
4376  select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4377 }
4378 
4379 void
4380 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4381 {
4382  if (movement_occurred) {
4383 
4384  motion (event, false);
4385  do_select_things (event, false);
4386 
4387  } else {
4388 
4389  /* just a click */
4390 
4391  bool do_deselect = true;
4392  MidiTimeAxisView* mtv;
4393 
4394  if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4395  /* MIDI track */
4396  if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4397  /* nothing selected */
4398  add_midi_region (mtv);
4399  do_deselect = false;
4400  }
4401  }
4402 
4403  /* do not deselect if Primary or Tertiary (toggle-select or
4404  * extend-select are pressed.
4405  */
4406 
4407  if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4408  !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4409  do_deselect) {
4410  deselect_things ();
4411  }
4412 
4413  }
4414 
4415  _editor->rubberband_rect->hide();
4416 }
4417 
4418 void
4420 {
4421  _editor->rubberband_rect->hide ();
4422 }
4423 
4424 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4425  : RegionDrag (e, i, p, v)
4426 {
4427  DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4428 }
4429 
4430 void
4431 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4432 {
4433  Drag::start_grab (event, cursor);
4434 
4436 }
4437 
4438 void
4439 TimeFXDrag::motion (GdkEvent* event, bool)
4440 {
4441  RegionView* rv = _primary;
4442  StreamView* cv = rv->get_time_axis_view().view ();
4443 
4444  pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4445  int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4446  int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4447 
4448  framepos_t const pf = adjusted_current_frame (event);
4449 
4450  if (pf > rv->region()->position()) {
4451  rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4452  }
4453 
4455 }
4456 
4457 void
4458 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4459 {
4461 
4462  if (!movement_occurred) {
4463  return;
4464  }
4465 
4466  if (last_pointer_frame() < _primary->region()->position()) {
4467  /* backwards drag of the left edge - not usable */
4468  return;
4469  }
4470 
4472 
4473  float percentage = (double) newlen / (double) _primary->region()->length();
4474 
4475 #ifndef USE_RUBBERBAND
4476  // Soundtouch uses percentage / 100 instead of normal (/ 1)
4477  if (_primary->region()->data_type() == DataType::AUDIO) {
4478  percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4479  }
4480 #endif
4481 
4482  if (!_editor->get_selection().regions.empty()) {
4483  /* primary will already be included in the selection, and edit
4484  group shared editing will propagate selection across
4485  equivalent regions, so just use the current region
4486  selection.
4487  */
4488 
4489  if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4490  error << _("An error occurred while executing time stretch operation") << endmsg;
4491  }
4492  }
4493 }
4494 
4495 void
4497 {
4499 }
4500 
4501 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4502  : Drag (e, i)
4503 {
4504  DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4505 }
4506 
4507 void
4508 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4509 {
4510  Drag::start_grab (event);
4511 }
4512 
4513 void
4514 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4515 {
4517 }
4518 
4519 void
4520 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4521 {
4522  if (movement_occurred && _editor->session()) {
4523  /* make sure we stop */
4525  }
4526 }
4527 
4528 void
4530 {
4531  /* XXX: TODO */
4532 }
4533 
4534 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4535  : Drag (e, i)
4536  , _operation (o)
4537  , _add (false)
4538  , _time_selection_at_start (!_editor->get_selection().time.empty())
4539 {
4540  DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4541 
4545  }
4546 }
4547 
4548 void
4549 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4550 {
4551  if (_editor->session() == 0) {
4552  return;
4553  }
4554 
4555  Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4556 
4557  switch (_operation) {
4558  case CreateSelection:
4559  if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4560  _add = true;
4561  } else {
4562  _add = false;
4563  }
4564  cursor = _editor->cursors()->selector;
4565  Drag::start_grab (event, cursor);
4566  break;
4567 
4568  case SelectionStartTrim:
4569  if (_editor->clicked_axisview) {
4571  }
4573  break;
4574 
4575  case SelectionEndTrim:
4576  if (_editor->clicked_axisview) {
4578  }
4580  break;
4581 
4582  case SelectionMove:
4583  Drag::start_grab (event, cursor);
4584  break;
4585 
4586  case SelectionExtend:
4587  Drag::start_grab (event, cursor);
4588  break;
4589  }
4590 
4591  if (_operation == SelectionMove) {
4593  } else {
4595  }
4596 }
4597 
4598 void
4600 {
4601  switch (_operation) {
4602  case CreateSelection:
4604  break;
4605 
4606  case SelectionStartTrim:
4607  case SelectionMove:
4609  break;
4610 
4611  case SelectionEndTrim:
4613  break;
4614 
4615  case SelectionExtend:
4616  break;
4617  }
4618 }
4619 
4620 void
4621 SelectionDrag::motion (GdkEvent* event, bool first_move)
4622 {
4623  framepos_t start = 0;
4624  framepos_t end = 0;
4625  framecnt_t length = 0;
4626  framecnt_t distance = 0;
4627 
4628  framepos_t const pending_position = adjusted_current_frame (event);
4629 
4630  if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4631  return;
4632  }
4633 
4634  switch (_operation) {
4635  case CreateSelection:
4636  {
4637  framepos_t grab = grab_frame ();
4638 
4639  if (first_move) {
4640  grab = adjusted_current_frame (event, false);
4641  if (grab < pending_position) {
4642  _editor->snap_to (grab, RoundDownMaybe);
4643  } else {
4644  _editor->snap_to (grab, RoundUpMaybe);
4645  }
4646  }
4647 
4648  if (pending_position < grab) {
4649  start = pending_position;
4650  end = grab;
4651  } else {
4652  end = pending_position;
4653  start = grab;
4654  }
4655 
4656  /* first drag: Either add to the selection
4657  or create a new selection
4658  */
4659 
4660  if (first_move) {
4661 
4662  if (_add) {
4663 
4664  /* adding to the selection */
4666  _editor->clicked_selection = _editor->selection->add (start, end);
4667  _add = false;
4668 
4669  } else {
4670 
4671  /* new selection */
4672 
4675  }
4676 
4677  _editor->clicked_selection = _editor->selection->set (start, end);
4678  }
4679  }
4680 
4681  //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4682  // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4684  if (atest) {
4685  _editor->selection->add (atest);
4686  break;
4687  }
4688 
4689  /* select all tracks within the rectangle that we've marked out so far */
4690  TrackViewList new_selection;
4691  TrackViewList& all_tracks (_editor->track_views);
4692 
4693  ArdourCanvas::Coord const top = grab_y();
4694  ArdourCanvas::Coord const bottom = current_pointer_y();
4695 
4696  if (top >= 0 && bottom >= 0) {
4697 
4698  //first, find the tracks that are covered in the y range selection
4699  for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4700  if ((*i)->covered_by_y_range (top, bottom)) {
4701  new_selection.push_back (*i);
4702  }
4703  }
4704 
4705  //now find any tracks that are GROUPED with the tracks we selected
4706  TrackViewList grouped_add = new_selection;
4707  for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4708  RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4709  if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4710  for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4711  RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4712  if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4713  grouped_add.push_back (*j);
4714  }
4715  }
4716  }
4717 
4718  //now compare our list with the current selection, and add or remove as necessary
4719  //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4720  TrackViewList tracks_to_add;
4721  TrackViewList tracks_to_remove;
4722  for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4723  if ( !_editor->selection->tracks.contains ( *i ) )
4724  tracks_to_add.push_back ( *i );
4725  for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4726  if ( !grouped_add.contains ( *i ) )
4727  tracks_to_remove.push_back ( *i );
4728  _editor->selection->add(tracks_to_add);
4729  _editor->selection->remove(tracks_to_remove);
4730 
4731  }
4732  }
4733  break;
4734 
4735  case SelectionStartTrim:
4736 
4739 
4740  if (pending_position > end) {
4741  start = end;
4742  } else {
4743  start = pending_position;
4744  }
4745  break;
4746 
4747  case SelectionEndTrim:
4748 
4751 
4752  if (pending_position < start) {
4753  end = start;
4754  } else {
4755  end = pending_position;
4756  }
4757 
4758  break;
4759 
4760  case SelectionMove:
4761 
4764 
4765  length = end - start;
4766  distance = pending_position - start;
4767  start = pending_position;
4768  _editor->snap_to (start);
4769 
4770  end = start + length;
4771 
4772  break;
4773 
4774  case SelectionExtend:
4775  break;
4776  }
4777 
4778  if (start != end) {
4779  switch (_operation) {
4780  case SelectionMove:
4782  _editor->selection->move_time (distance);
4783  }
4784  break;
4785  default:
4787  }
4788  }
4789 
4790  if (_operation == SelectionMove) {
4791  show_verbose_cursor_time(start);
4792  } else {
4793  show_verbose_cursor_time(pending_position);
4794  }
4795 }
4796 
4797 void
4798 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4799 {
4800  Session* s = _editor->session();
4801 
4802  _editor->begin_reversible_selection_op (X_("Change Time Selection"));
4803  if (movement_occurred) {
4804  motion (event, false);
4805  /* XXX this is not object-oriented programming at all. ick */
4806  if (_editor->selection->time.consolidate()) {
4808  }
4809 
4810  /* XXX what if its a music time selection? */
4811  if (s) {
4812  if ( s->get_play_range() && s->transport_rolling() ) {
4813  s->request_play_range (&_editor->selection->time, true);
4814  } else {
4815  if (ARDOUR_UI::config()->get_follow_edits() && !s->transport_rolling()) {
4818  else
4820  }
4821  }
4822  }
4823 
4824  } else {
4825  /* just a click, no pointer movement.
4826  */
4827 
4828  if (_operation == SelectionExtend) {
4830  framepos_t pos = adjusted_current_frame (event, false);
4831  framepos_t start = min (pos, start_at_start);
4832  framepos_t end = max (pos, end_at_start);
4833  _editor->selection->set (start, end);
4834  }
4835  } else {
4836  if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4837  if (_editor->clicked_selection) {
4839  }
4840  } else {
4841  if (!_editor->clicked_selection) {
4843  }
4844  }
4845  }
4846 
4849  }
4850 
4851  if (s && s->get_play_range () && s->transport_rolling()) {
4852  s->request_stop (false, false);
4853  }
4854 
4855  }
4856 
4860 }
4861 
4862 void
4864 {
4865  /* XXX: TODO */
4866 }
4867 
4869  : Drag (e, i, false),
4870  _operation (o),
4871  _copy (false)
4872 {
4873  DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4874 
4875  _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4876  ArdourCanvas::Rect (0.0, 0.0, 0.0,
4877  physical_screen_height (_editor->get_window())));
4878  _drag_rect->hide ();
4879 
4880  _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect"));
4881  _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect"));
4882 }
4883 
4885 {
4886  /* normal canvas items will be cleaned up when their parent group is deleted. But
4887  this item is created as the child of a long-lived parent group, and so we
4888  need to explicitly delete it.
4889  */
4890  delete _drag_rect;
4891 }
4892 
4893 void
4894 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4895 {
4896  if (_editor->session() == 0) {
4897  return;
4898  }
4899 
4900  Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4901 
4902  if (!_editor->temp_location) {
4904  }
4905 
4906  switch (_operation) {
4907  case CreateSkipMarker:
4908  case CreateRangeMarker:
4909  case CreateTransportMarker:
4910  case CreateCDMarker:
4911 
4912  if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4913  _copy = true;
4914  } else {
4915  _copy = false;
4916  }
4917  cursor = _editor->cursors()->selector;
4918  break;
4919  }
4920 
4921  Drag::start_grab (event, cursor);
4922 
4924 }
4925 
4926 void
4927 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4928 {
4929  framepos_t start = 0;
4930  framepos_t end = 0;
4931  ArdourCanvas::Rectangle *crect;
4932 
4933  switch (_operation) {
4934  case CreateSkipMarker:
4935  crect = _editor->range_bar_drag_rect;
4936  break;
4937  case CreateRangeMarker:
4938  crect = _editor->range_bar_drag_rect;
4939  break;
4940  case CreateTransportMarker:
4942  break;
4943  case CreateCDMarker:
4945  break;
4946  default:
4947  error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4948  return;
4949  break;
4950  }
4951 
4952  framepos_t const pf = adjusted_current_frame (event);
4953 
4955  framepos_t grab = grab_frame ();
4956  _editor->snap_to (grab);
4957 
4958  if (pf < grab_frame()) {
4959  start = pf;
4960  end = grab;
4961  } else {
4962  end = pf;
4963  start = grab;
4964  }
4965 
4966  /* first drag: Either add to the selection
4967  or create a new selection.
4968  */
4969 
4970  if (first_move) {
4971 
4972  _editor->temp_location->set (start, end);
4973 
4974  crect->show ();
4975 
4977  _drag_rect->show();
4978  //_drag_rect->raise_to_top();
4979 
4980  }
4981  }
4982 
4983  if (start != end) {
4984  _editor->temp_location->set (start, end);
4985 
4986  double x1 = _editor->sample_to_pixel (start);
4987  double x2 = _editor->sample_to_pixel (end);
4988  crect->set_x0 (x1);
4989  crect->set_x1 (x2);
4990 
4992  }
4993 
4995 
4996 }
4997 
4998 void
4999 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5000 {
5001  Location * newloc = 0;
5002  string rangename;
5003  int flags;
5004 
5005  if (movement_occurred) {
5006  motion (event, false);
5007  _drag_rect->hide();
5008 
5009  switch (_operation) {
5010  case CreateSkipMarker:
5011  case CreateRangeMarker:
5012  case CreateCDMarker:
5013  {
5014  XMLNode &before = _editor->session()->locations()->get_state();
5015  if (_operation == CreateSkipMarker) {
5016  _editor->begin_reversible_command (_("new skip marker"));
5017  _editor->session()->locations()->next_available_name(rangename,_("skip"));
5018  flags = Location::IsRangeMarker | Location::IsSkip;
5019  _editor->range_bar_drag_rect->hide();
5020  } else if (_operation == CreateCDMarker) {
5021  _editor->session()->locations()->next_available_name(rangename, _("CD"));
5022  _editor->begin_reversible_command (_("new CD marker"));
5023  flags = Location::IsRangeMarker | Location::IsCDMarker;
5025  } else {
5026  _editor->begin_reversible_command (_("new skip marker"));
5027  _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5028  flags = Location::IsRangeMarker;
5029  _editor->range_bar_drag_rect->hide();
5030  }
5031  newloc = new Location (
5033  );
5034 
5035  _editor->session()->locations()->add (newloc, true);
5036  XMLNode &after = _editor->session()->locations()->get_state();
5037  _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5039  break;
5040  }
5041 
5042  case CreateTransportMarker:
5043  // popup menu to pick loop or punch
5044  _editor->new_transport_marker_context_menu (&event->button, _item);
5045  break;
5046  }
5047 
5048  } else {
5049 
5050  /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5051 
5053 
5054  /* didn't drag, so just locate */
5055 
5057 
5058  } else if (_operation == CreateCDMarker) {
5059 
5060  /* didn't drag, but mark is already created so do
5061  * nothing */
5062 
5063  } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5064 
5065  framepos_t start;
5066  framepos_t end;
5067 
5068  _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5069 
5070  if (end == max_framepos) {
5071  end = _editor->session()->current_end_frame ();
5072  }
5073 
5074  if (start == max_framepos) {
5075  start = _editor->session()->current_start_frame ();
5076  }
5077 
5078  switch (_editor->mouse_mode) {
5079  case MouseObject:
5080  /* find the two markers on either side and then make the selection from it */
5081  _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5082  break;
5083 
5084  case MouseRange:
5085  /* find the two markers on either side of the click and make the range out of it */
5086  _editor->selection->set (start, end);
5087  break;
5088 
5089  default:
5090  break;
5091  }
5092  }
5093  }
5094 
5096 }
5097 
5098 void
5099 RangeMarkerBarDrag::aborted (bool movement_occured)
5100 {
5101  if (movement_occured) {
5102  _drag_rect->hide ();
5103  }
5104 }
5105 
5106 void
5108 {
5109  double const x1 = _editor->sample_to_pixel (location->start());
5110  double const x2 = _editor->sample_to_pixel (location->end());
5111 
5112  _drag_rect->set_x0 (x1);
5113  _drag_rect->set_x1 (x2);
5114 }
5115 
5116 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5117  : Drag (e, i)
5118  , _cumulative_dx (0)
5119  , _cumulative_dy (0)
5120 {
5121  DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5122 
5123  _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5124  assert (_primary);
5125  _region = &_primary->region_view ();
5127 }
5128 
5129 void
5130 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5131 {
5132  Drag::start_grab (event);
5133 
5134  if (!(_was_selected = _primary->selected())) {
5135 
5136  /* tertiary-click means extend selection - we'll do that on button release,
5137  so don't add it here, because otherwise we make it hard to figure
5138  out the "extend-to" range.
5139  */
5140 
5141  bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5142 
5143  if (!extend) {
5144  bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5145 
5146  if (add) {
5147  _region->note_selected (_primary, true);
5148  } else {
5150  }
5151 
5152  _editor->begin_reversible_selection_op(X_("Select Note Press"));
5154  }
5155  }
5156 }
5157 
5161 {
5162  /* dx in frames */
5164 
5165  /* primary note time */
5167 
5168  /* new time of the primary note in session frames */
5169  frameoffset_t st = n + dx;
5170 
5171  framepos_t const rp = _region->region()->position ();
5172 
5173  /* prevent the note being dragged earlier than the region's position */
5174  st = max (st, rp);
5175 
5176  /* snap and return corresponding delta */
5177  return _region->snap_frame_to_frame (st - rp) + rp - n;
5178 }
5179 
5181 int8_t
5183 {
5185  double const y = _region->midi_view()->y_position ();
5186  /* new current note */
5187  uint8_t n = msv->y_to_note (current_pointer_y () - y);
5188  /* clamp */
5189  n = max (msv->lowest_note(), n);
5190  n = min (msv->highest_note(), n);
5191  /* and work out delta */
5192  return n - msv->y_to_note (grab_y() - y);
5193 }
5194 
5195 void
5196 NoteDrag::motion (GdkEvent *, bool)
5197 {
5198  /* Total change in x and y since the start of the drag */
5199  frameoffset_t const dx = total_dx ();
5200  int8_t const dy = total_dy ();
5201 
5202  /* Now work out what we have to do to the note canvas items to set this new drag delta */
5203  double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5204  double const tdy = -dy * _note_height - _cumulative_dy;
5205 
5206  if (tdx || tdy) {
5207  _cumulative_dx += tdx;
5208  _cumulative_dy += tdy;
5209 
5210  int8_t note_delta = total_dy();
5211 
5212  _region->move_selection (tdx, tdy, note_delta);
5213 
5214  /* the new note value may be the same as the old one, but we
5215  * don't know what that means because the selection may have
5216  * involved more than one note and we might be doing something
5217  * odd with them. so show the note value anyway, always.
5218  */
5219 
5220  char buf[12];
5221  uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5222 
5223  snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
5224  (int) floor ((double)new_note));
5225 
5227  }
5228 }
5229 
5230 void
5231 NoteDrag::finished (GdkEvent* ev, bool moved)
5232 {
5233  if (!moved) {
5234  /* no motion - select note */
5235 
5236  if (_editor->current_mouse_mode() == Editing::MouseObject ||
5237  _editor->current_mouse_mode() == Editing::MouseDraw) {
5238 
5239  bool changed = false;
5240 
5241  if (_was_selected) {
5242  bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5243  if (add) {
5245  changed = true;
5246  }
5247  } else {
5248  bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5249  bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5250 
5251  if (!extend && !add && _region->selection_size() > 1) {
5253  changed = true;
5254  } else if (extend) {
5255  _region->note_selected (_primary, true, true);
5256  changed = true;
5257  } else {
5258  /* it was added during button press */
5259  }
5260  }
5261 
5262  if (changed) {
5263  _editor->begin_reversible_selection_op(X_("Select Note Release"));
5265  }
5266  }
5267  } else {
5269  }
5270 }
5271 
5272 void
5274 {
5275  /* XXX: TODO */
5276 }
5277 
5280  : Drag (editor, atv->base_item ())
5281  , _ranges (r)
5282  , _y_origin (atv->y_position())
5283  , _nothing_to_drag (false)
5284 {
5285  DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5286  setup (atv->lines ());
5287 }
5288 
5290 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5291  : Drag (editor, rv->get_canvas_group ())
5292  , _ranges (r)
5293  , _y_origin (rv->get_time_axis_view().y_position())
5294  , _nothing_to_drag (false)
5295  , _integral (false)
5296 {
5297  DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5298 
5299  list<boost::shared_ptr<AutomationLine> > lines;
5300 
5301  AudioRegionView* audio_view;
5302  AutomationRegionView* automation_view;
5303  if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5304  lines.push_back (audio_view->get_gain_line ());
5305  } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5306  lines.push_back (automation_view->line ());
5307  _integral = true;
5308  } else {
5309  error << _("Automation range drag created for invalid region type") << endmsg;
5310  }
5311 
5312  setup (lines);
5313 }
5314 
5318 void
5320 {
5321  /* find the lines that overlap the ranges being dragged */
5322  list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5323  while (i != lines.end ()) {
5324  list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5325  ++j;
5326 
5327  pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5328 
5329  /* check this range against all the AudioRanges that we are using */
5330  list<AudioRange>::const_iterator k = _ranges.begin ();
5331  while (k != _ranges.end()) {
5332  if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5333  break;
5334  }
5335  ++k;
5336  }
5337 
5338  /* add it to our list if it overlaps at all */
5339  if (k != _ranges.end()) {
5340  Line n;
5341  n.line = *i;
5342  n.state = 0;
5343  n.range = r;
5344  _lines.push_back (n);
5345  }
5346 
5347  i = j;
5348  }
5349 
5350  /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5351 }
5352 
5353 double
5355 {
5356  return 1.0 - ((global_y - _y_origin) / line->height());
5357 }
5358 
5359 double
5361 {
5362  const double v = list->eval(x);
5363  return _integral ? rint(v) : v;
5364 }
5365 
5366 void
5367 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5368 {
5369  Drag::start_grab (event, cursor);
5370 
5371  /* Get line states before we start changing things */
5372  for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5373  i->state = &i->line->get_state ();
5374  i->original_fraction = y_fraction (i->line, current_pointer_y());
5375  }
5376 
5377  if (_ranges.empty()) {
5378 
5379  /* No selected time ranges: drag all points */
5380  for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5381  uint32_t const N = i->line->npoints ();
5382  for (uint32_t j = 0; j < N; ++j) {
5383  i->points.push_back (i->line->nth (j));
5384  }
5385  }
5386 
5387  } else {
5388 
5389  for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5390 
5391  framecnt_t const half = (i->start + i->end) / 2;
5392 
5393  /* find the line that this audio range starts in */
5394  list<Line>::iterator j = _lines.begin();
5395  while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5396  ++j;
5397  }
5398 
5399  if (j != _lines.end()) {
5400  boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5401 
5402  /* j is the line that this audio range starts in; fade into it;
5403  64 samples length plucked out of thin air.
5404  */
5405 
5406  framepos_t a = i->start + 64;
5407  if (a > half) {
5408  a = half;
5409  }
5410 
5411  double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5412  double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5413 
5414  the_list->editor_add (p, value (the_list, p));
5415  the_list->editor_add (q, value (the_list, q));
5416  }
5417 
5418  /* same thing for the end */
5419 
5420  j = _lines.begin();
5421  while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5422  ++j;
5423  }
5424 
5425  if (j != _lines.end()) {
5426  boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5427 
5428  /* j is the line that this audio range starts in; fade out of it;
5429  64 samples length plucked out of thin air.
5430  */
5431 
5432  framepos_t b = i->end - 64;
5433  if (b < half) {
5434  b = half;
5435  }
5436 
5437  double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5438  double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5439 
5440  the_list->editor_add (p, value (the_list, p));
5441  the_list->editor_add (q, value (the_list, q));
5442  }
5443  }
5444 
5445  _nothing_to_drag = true;
5446 
5447  /* Find all the points that should be dragged and put them in the relevant
5448  points lists in the Line structs.
5449  */
5450 
5451  for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5452 
5453  uint32_t const N = i->line->npoints ();
5454  for (uint32_t j = 0; j < N; ++j) {
5455 
5456  /* here's a control point on this line */
5457  ControlPoint* p = i->line->nth (j);
5458  double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5459 
5460  /* see if it's inside a range */
5461  list<AudioRange>::const_iterator k = _ranges.begin ();
5462  while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5463  ++k;
5464  }
5465 
5466  if (k != _ranges.end()) {
5467  /* dragging this point */
5468  _nothing_to_drag = false;
5469  i->points.push_back (p);
5470  }
5471  }
5472  }
5473  }
5474 
5475  if (_nothing_to_drag) {
5476  return;
5477  }
5478 
5479  for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5480  i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5481  }
5482 }
5483 
5484 void
5485 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5486 {
5487  if (_nothing_to_drag) {
5488  return;
5489  }
5490 
5491  for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5492  float const f = y_fraction (l->line, current_pointer_y());
5493  /* we are ignoring x position for this drag, so we can just pass in anything */
5494  uint32_t ignored;
5495  l->line->drag_motion (0, f, true, false, ignored);
5496  show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5497  }
5498 }
5499 
5500 void
5501 AutomationRangeDrag::finished (GdkEvent* event, bool)
5502 {
5503  if (_nothing_to_drag) {
5504  return;
5505  }
5506 
5507  motion (event, false);
5508  for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5509  i->line->end_drag (false, 0);
5510  }
5511 
5513 }
5514 
5515 void
5517 {
5518  for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5519  i->line->reset ();
5520  }
5521 }
5522 
5524  : view (v)
5525  , initial_time_axis_view (itav)
5526 {
5527  /* note that time_axis_view may be null if the regionview was created
5528  * as part of a copy operation.
5529  */
5531  layer = v->region()->layer ();
5532  initial_y = v->get_canvas_group()->position().y;
5533  initial_playlist = v->region()->playlist ();
5534  initial_position = v->region()->position ();
5535  initial_end = v->region()->position () + v->region()->length ();
5536 }
5537 
5539  : Drag (e, i->canvas_item ())
5540  , _region_view (r)
5541  , _patch_change (i)
5542  , _cumulative_dx (0)
5543 {
5544  DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5546  grab_frame()));
5547 }
5548 
5549 void
5550 PatchChangeDrag::motion (GdkEvent* ev, bool)
5551 {
5554  f = max (f, r->position ());
5555  f = min (f, r->last_frame ());
5556 
5557  framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5558  double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5559  _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5560  _cumulative_dx = dxu;
5561 }
5562 
5563 void
5564 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5565 {
5566  if (!movement_occurred) {
5567  return;
5568  }
5569 
5572  f = max (f, r->position ());
5573  f = min (f, r->last_frame ());
5574 
5576  *_patch_change,
5577  _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5578  );
5579 }
5580 
5581 void
5583 {
5584  _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5585 }
5586 
5587 void
5589 {
5592 }
5593 
5595  : RubberbandSelectDrag (e, rv->get_canvas_group ())
5596  , _region_view (rv)
5597 {
5598 
5599 }
5600 
5601 void
5602 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5603 {
5605  x1, x2, y1, y2,
5606  Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5607 }
5608 
5609 void
5611 {
5612  /* XXX */
5613 }
5614 
5616  : RubberbandSelectDrag (e, rv->get_canvas_group ())
5617  , _region_view (rv)
5618 {
5619  _vertical_only = true;
5620 }
5621 
5622 void
5623 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5624 {
5625  double const y = _region_view->midi_view()->y_position ();
5626 
5627  y1 = max (0.0, y1 - y);
5628  y2 = max (0.0, y2 - y);
5629 
5631  y1,
5632  y2,
5633  Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5634  );
5635 }
5636 
5637 void
5639 {
5640  /* XXX */
5641 }
5642 
5644  : RubberbandSelectDrag (e, i)
5645 {
5646 
5647 }
5648 
5649 void
5650 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5651 {
5652  if (drag_in_progress) {
5653  /* We just want to select things at the end of the drag, not during it */
5654  return;
5655  }
5656 
5658 
5659  _editor->begin_reversible_selection_op (X_("rubberband selection"));
5660 
5661  _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5662 
5664 }
5665 
5666 void
5668 {
5669  _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
5670 
5676 
5678 }
5679 
5680 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5681  : Drag (e, i)
5682  , _region_view (rv)
5683  , _drag_rect (0)
5684 {
5685  _note[0] = _note[1] = 0;
5686 }
5687 
5689 {
5690  delete _drag_rect;
5691 }
5692 
5693 framecnt_t
5695 {
5696  bool success;
5697  Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
5698  if (!success) {
5699  grid_beats = Evoral::Beats(1);
5700  }
5701 
5702  return _region_view->region_beats_to_region_frames (grid_beats);
5703 }
5704 
5705 void
5706 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5707 {
5708  Drag::start_grab (event, cursor);
5709 
5710  _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5711 
5713  framecnt_t const g = grid_frames (pf);
5714 
5715  /* Hack so that we always snap to the note that we are over, instead of snapping
5716  to the next one if we're more than halfway through the one we're over.
5717  */
5718  if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5719  pf -= g / 2;
5720  }
5721 
5722  _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5723  _note[1] = _note[0];
5724 
5726  double const x = _editor->sample_to_pixel (_note[0]);
5727  double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5728 
5729  _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5730  _drag_rect->set_outline_all ();
5731  _drag_rect->set_outline_color (0xffffff99);
5732  _drag_rect->set_fill_color (0xffffff66);
5733 }
5734 
5735 void
5736 NoteCreateDrag::motion (GdkEvent* event, bool)
5737 {
5738  _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5739  double const x0 = _editor->sample_to_pixel (_note[0]);
5740  double const x1 = _editor->sample_to_pixel (_note[1]);
5741  _drag_rect->set_x0 (std::min(x0, x1));
5742  _drag_rect->set_x1 (std::max(x0, x1));
5743 }
5744 
5745 void
5746 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5747 {
5748  if (!had_movement) {
5749  return;
5750  }
5751 
5752  framepos_t const start = min (_note[0], _note[1]);
5753  framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5754 
5755  framecnt_t const g = grid_frames (start);
5756  Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
5757 
5758  if (_editor->snap_mode() == SnapNormal && length < g) {
5759  length = g;
5760  }
5761 
5762  Evoral::Beats length_beats = max (
5763  one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5764 
5765  _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5766 }
5767 
5768 double
5770 {
5771  double x = 0;
5772  _region_view->get_canvas_group()->canvas_to_item (x, y);
5773  return y;
5774 }
5775 
5776 void
5778 {
5779 
5780 }
5781 
5782 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5783  : Drag (e, i)
5784  , arv (rv)
5785  , start (start_yn)
5786 {
5787  std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5788 }
5789 
5790 void
5791 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5792 {
5793  Drag::start_grab (event, cursor);
5794 }
5795 
5796 void
5797 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5798 {
5799  double distance;
5800  double new_length;
5801  framecnt_t len;
5802 
5804 
5805  if (start) {
5806  distance = _drags->current_pointer_x() - grab_x();
5807  len = ar->fade_in()->back()->when;
5808  } else {
5809  distance = grab_x() - _drags->current_pointer_x();
5810  len = ar->fade_out()->back()->when;
5811  }
5812 
5813  /* how long should it be ? */
5814 
5815  new_length = len + _editor->pixel_to_sample (distance);
5816 
5817  /* now check with the region that this is legal */
5818 
5819  new_length = ar->verify_xfade_bounds (new_length, start);
5820 
5821  if (start) {
5822  arv->reset_fade_in_shape_width (ar, new_length);
5823  } else {
5824  arv->reset_fade_out_shape_width (ar, new_length);
5825  }
5826 }
5827 
5828 void
5830 {
5831  double distance;
5832  double new_length;
5833  framecnt_t len;
5834 
5836 
5837  if (start) {
5838  distance = _drags->current_pointer_x() - grab_x();
5839  len = ar->fade_in()->back()->when;
5840  } else {
5841  distance = grab_x() - _drags->current_pointer_x();
5842  len = ar->fade_out()->back()->when;
5843  }
5844 
5845  new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5846 
5847  _editor->begin_reversible_command ("xfade trim");
5848  ar->playlist()->clear_owned_changes ();
5849 
5850  if (start) {
5851  ar->set_fade_in_length (new_length);
5852  } else {
5853  ar->set_fade_out_length (new_length);
5854  }
5855 
5856  /* Adjusting the xfade may affect other regions in the playlist, so we need
5857  to get undo Commands from the whole playlist rather than just the
5858  region.
5859  */
5860 
5861  vector<Command*> cmds;
5862  ar->playlist()->rdiff (cmds);
5863  _editor->session()->add_commands (cmds);
5865 
5866 }
5867 
5868 void
5870 {
5871  if (start) {
5872  // arv->redraw_start_xfade ();
5873  } else {
5874  // arv->redraw_end_xfade ();
5875  }
5876 }
5877 
5878 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
5879  : Drag (e, item, true)
5880  , line (new EditorCursor (*e))
5881 {
5882  line->set_position (pos);
5883  line->show ();
5884 }
5885 
5887 {
5888  delete line;
5889 }
5890 
5891 void
5892 RegionCutDrag::motion (GdkEvent*, bool)
5893 {
5895  _editor->snap_to (where);
5896 
5897  line->set_position (where);
5898 }
5899 
5900 void
5901 RegionCutDrag::finished (GdkEvent*, bool)
5902 {
5903  _editor->get_track_canvas()->canvas()->re_enter();
5904 
5906 
5907  line->hide ();
5908 
5910 
5911  if (rs.empty()) {
5912  return;
5913  }
5914 
5915  _editor->split_regions_at (pos, rs);
5916 }
5917 
5918 void
5920 {
5921 }
bool operator()(TimeAxisView *a, TimeAxisView *b)
Definition: editor_drag.cc:479
void change(Gdk::Cursor *cursor)
virtual RegionView * create_region_view(boost::shared_ptr< ARDOUR::Region >, bool, bool)
Definition: streamview.h:114
int next_available_name(std::string &result, std::string base)
Definition: location.cc:797
LIBEVORAL_API std::string midi_note_name(uint8_t noteval)
Definition: midi_util.cpp:25
RegionView * find_view(boost::shared_ptr< const ARDOUR::Region >)
Definition: streamview.cc:481
MidiTimeAxisView * midi_view() const
ArdourCanvas::Item & parent_group() const
bool transport_rolling() const
Definition: session.h:592
bool follow_playhead() const
Definition: editor.h:342
MidiRegionView * _region_view
Definition: editor_drag.h:577
bool control_points_adjacent(double xval, uint32_t &before, uint32_t &after)
void aborted(bool)
void setup_pointer_frame_offset()
std::pair< TimeAxisView *, double > trackview_by_y_position(double, bool trackview_relative_offset=true) const
Definition: editor.cc:2565
virtual void set_playlist(boost::weak_ptr< ARDOUR::Playlist >)
Definition: region.cc:411
boost::shared_ptr< AutomationLine > line
the line
Definition: editor_drag.h:1079
virtual void setup_pointer_frame_offset()
Definition: editor_drag.h:186
framepos_t end_at_start
Definition: editor_drag.h:995
double _cumulative_x_drag
Definition: editor_drag.h:826
Gdk::Cursor * selector
Definition: mouse_cursors.h:47
CopiedLocationMarkerInfo(ARDOUR::Location *l, Marker *m)
ArdourCanvas::Item * get_canvas_group()
void motion(GdkEvent *, bool)
boost::shared_ptr< ARDOUR::Playlist > playlist() const
double initial_y
the initial y position of the view before any reparenting
Definition: editor_drag.h:272
session end
Definition: marker.h:49
void finished(GdkEvent *, bool)
ArdourCanvas::Color color(const std::string &, bool *failed=0) const
Definition: ui_config.cc:567
MidiRegionView * _region
Definition: editor_drag.h:524
void ripple(framepos_t at, framecnt_t distance, RegionList *exclude)
Definition: playlist.cc:2812
ControlPoint * nth(uint32_t)
CopiedLocationInfo _copied_locations
Definition: editor_drag.h:804
void move_time(framecnt_t)
Definition: selection.cc:548
void setup_pointer_frame_offset()
void start_grab(GdkEvent *, Gdk::Cursor *c=0)
void finished(GdkEvent *, bool)
virtual bool active(Editing::MouseMode m)
Definition: editor_drag.h:158
void set_selected_track_as_side_effect(Selection::Operation op)
void clear_regions()
Definition: selection.cc:159
uint32_t _final_index
Definition: editor_drag.h:829
static PBD::Signal1< void, RegionView * > RegionViewGoingAway
Definition: region_view.h:100
int _visible_y_high
Definition: editor_drag.h:298
void commit_reversible_selection_op()
Definition: editor.cc:3376
TimeAxisView & get_time_axis_view() const
RegionMoveDrag(Editor *, ArdourCanvas::Item *, RegionView *, std::list< RegionView * > const &, bool, bool)
std::list< Line > _lines
Definition: editor_drag.h:1086
void set_offset(ARDOUR::frameoffset_t offset)
virtual void drag_end()
std::set< boost::shared_ptr< ARDOUR::Playlist > > motion_frozen_playlists
Definition: editor.h:1510
void motion(GdkEvent *, bool)
void scrub(framepos_t, double)
ArdourCanvas::Container * _drag_motion_group
Definition: editor.h:848
void aborted(bool)
ArdourCanvas::Arrow & track_canvas_item()
ARDOUR::frameoffset_t get_offset()
ArdourCanvas::GtkCanvasViewport * get_track_canvas() const
void setup_pointer_frame_offset()
SelectionDrag(Editor *, ArdourCanvas::Item *, Operation)
void motion(GdkEvent *, bool)
Selection * selection
Definition: editor.h:1801
PatchChange * _patch_change
Definition: editor_drag.h:578
bool initially_vertical() const
Definition: editor_drag.h:181
static Gdk::Cursor * invalid_cursor()
Definition: mouse_cursors.h:84
uint32_t _ddropzone
Definition: editor_drag.h:343
bool operator()(RegionView *a, RegionView *b)
void set_follow_playhead(bool yn, bool catch_up=true)
Definition: editor.cc:4004
MouseMode
Definition: editing.h:91
TimeAxisView * clicked_axisview
Definition: editor.h:695
MidiTimeAxisView * _view
Definition: editor_drag.h:487
void bbt_time(framepos_t when, Timecode::BBT_Time &)
Definition: tempo.cc:1168
framepos_t _note[2]
Definition: editor_drag.h:557
ControlPoint * _point
Definition: editor_drag.h:823
CursorDrag(Editor *, EditorCursor &, bool)
void update_drag_selection(framepos_t start, framepos_t end, double y0, double y1, bool extend)
void set_fade_out_length(framecnt_t)
std::list< Location * > LocationList
Definition: location.h:167
RangeMarkerBarDrag(Editor *, ArdourCanvas::Item *, Operation)
bool _was_rolling
true if the session was rolling before the drag started, otherwise false
Definition: editor_drag.h:236
ARDOUR::framepos_t adjusted_current_frame(GdkEvent const *, bool snap=true) const
Definition: editor_drag.cc:341
void start_grab(GdkEvent *, Gdk::Cursor *c=0)
bool _preserve_fade_anchor
Definition: editor_drag.h:647
framepos_t pixel_to_sample_from_event(double pixel) const
Definition: editor.h:215
void abort_reversible_command()
Definition: editor.cc:3471
bool can_slide() const
Definition: control_point.h:73
void begin_reversible_command(std::string cmd_name)
std::list< ARDOUR::AudioRange > _ranges
Definition: editor_drag.h:1075
void add_tempo(const Tempo &, Timecode::BBT_Time where)
Definition: tempo.cc:513
void start_grab(GdkEvent *, Gdk::Cursor *c=0)
void setup_pointer_frame_offset()
LIBARDOUR_API PBD::PropertyDescriptor< layer_t > layer
Definition: region.cc:67
LIBARDOUR_API PBD::PropertyDescriptor< bool > hidden
Definition: route_group.h:50
void order_selection_trims(ArdourCanvas::Item *item, bool put_start_on_top)
void update_item(ARDOUR::Location *)
void finished(GdkEvent *, bool)
RouteTimeAxisView * create_destination_time_axis(boost::shared_ptr< ARDOUR::Region >, TimeAxisView *original)
sigc::signal< void, RegionView * > RegionViewAdded
Definition: streamview.h:122
EditorCursor & _cursor
Definition: editor_drag.h:728
void start_grab(GdkEvent *, Gdk::Cursor *c=0)
NoteDrag(Editor *, ArdourCanvas::Item *)
void set_layer_display(LayerDisplay)
Definition: streamview.cc:667
virtual DataType data_type() const =0
void add(Drag *)
Definition: editor_drag.cc:117
void setup(std::list< boost::shared_ptr< AutomationLine > > const &)
int time_axis_view
Definition: editor_drag.h:266
virtual void start_drag_single(ControlPoint *, double, float)
uint32_t _ndropzone
Definition: editor_drag.h:341
ArdourCanvas::Line * _line
Definition: editor_drag.h:865
MidiVerticalSelectDrag(Editor *, MidiRegionView *)
void show_verbose_cursor_time(framepos_t)
Definition: editor_drag.cc:441
void remove(TimeAxisView *)
Definition: selection.cc:603
framecnt_t grid_frames(framepos_t) const
void motion(GdkEvent *, bool)
double compute_x_delta(GdkEvent const *, ARDOUR::framepos_t *)
Definition: editor_drag.cc:586
LIBARDOUR_API GQuark region_drag
Definition: operations.cc:30
bool _was_selected
Definition: editor_drag.h:528
ARDOUR::layer_t layers() const
Definition: streamview.h:112
framepos_t position() const
Definition: marker.h:79
TimeAxisView * prev_tav
Definition: editor_drag.h:449
virtual void end_drag(bool with_push, uint32_t final_index)
void temporarily_hide_envelope()
Dangerous!
Definition: ardour_ui.h:130
shared_ptr< T > dynamic_pointer_cast(shared_ptr< U > const &r)
Definition: shared_ptr.hpp:396
virtual void hide_timestretch()
static ARDOUR_UI * instance()
Definition: ardour_ui.h:187
framepos_t end_frame() const
void reset_fade_out_shape_width(boost::shared_ptr< ARDOUR::AudioRegion > ar, framecnt_t, bool drag_active=false)
RegionMotionDrag(Editor *, ArdourCanvas::Item *, RegionView *, std::list< RegionView * > const &, bool)
Definition: editor_drag.cc:552
boost::shared_ptr< AutomationList > fade_in()
Definition: audioregion.h:87
framepos_t canvas_event_sample(GdkEvent const *, double *px=0, double *py=0) const
AutomationLine & line() const
Definition: control_point.h:81
void update_item(ARDOUR::Location *)
void finished(GdkEvent *, bool)
NoteResizeDrag(Editor *, ArdourCanvas::Item *)
void set_manager(DragManager *m)
Definition: editor_drag.h:110
double _last_pointer_layer
Definition: editor_drag.h:337
void set_layer(boost::shared_ptr< Region >, double)
Definition: playlist.cc:2334
void aborted(bool)
std::set< boost::shared_ptr< ARDOUR::Playlist > > PlaylistSet
Definition: editor_drag.h:371
bool allow_moves_across_tracks
Definition: editor_drag.h:454
void undo(uint32_t n=1)
Definition: editor_ops.cc:117
double _region_view_grab_x
Definition: editor_drag.h:868
bool timecode_transmission_suspended() const
double current_pointer_x() const
Definition: editor_drag.cc:347
bool _initially_vertical
true if after move threshold is passed we appear to be moving vertically; undefined before that ...
Definition: editor_drag.h:242
framepos_t window_event_sample(GdkEvent const *, double *px=0, double *py=0) const
std::vector< boost::shared_ptr< TimeAxisView > > Children
void save_undo(void)
framepos_t start_at_start
Definition: editor_drag.h:994
void create_note_at(framepos_t t, double y, Evoral::Beats length, bool snap_t)
void add_command(Command *const cmd)
Definition: session.h:787
void aborted(bool)
LIBGTKMM2EXT_API int physical_screen_height(Glib::RefPtr< Gdk::Window >)
Definition: utils.cc:481
boost::shared_ptr< AudioRegionGainLine > get_gain_line() const
void aborted(bool)
TempoMap & tempo_map()
Definition: session.h:596
void start_grab(GdkEvent *, Gdk::Cursor *c=0)
virtual void start_grab(GdkEvent *e, Gdk::Cursor *c=0)
Definition: editor_drag.cc:249
ArdourCanvas::Container * tempo_group
Definition: editor.h:816
virtual void aborted(bool m)=0
Type type()
Definition: marker.h:87
void remove_region(boost::shared_ptr< Region >)
Definition: playlist.cc:789
Always round up, even if on a division.
Definition: types.h:225
double value(boost::shared_ptr< ARDOUR::AutomationList > list, double x) const
MidiRegionView * _region_view
Definition: editor_drag.h:928
virtual StreamView * view() const
void show_verbose_cursor_duration(framepos_t, framepos_t, double xoffset=0)
Definition: editor_drag.cc:448
boost::shared_ptr< RegionList > regions_with_start_within(Evoral::Range< framepos_t >)
Definition: playlist.cc:1790
boost::shared_ptr< AutomationList > fade_out()
Definition: audioregion.h:89
Definition: marker.h:41
static int N
Definition: signals_test.cc:27
RegionView * view
the view
Definition: editor_drag.h:262
Marker * _marker
marker being dragged
Definition: editor_drag.h:794
ARDOUR::framepos_t end_frame()
MarkerSelection markers
Definition: selection.h:87
virtual B to(A a) const =0
bool _dragging_edit_point
Definition: editor.h:1513
uint32_t n_audio() const
Definition: chan_count.h:63
double sample_to_pixel_unrounded(framepos_t sample) const
Definition: editor.h:238
tuple f
Definition: signals.py:35
bool is_auditioning() const
Definition: session.cc:4184
RegionView * _primary
the view that was clicked on (or whatever) to start the drag
Definition: editor_drag.h:289
framepos_t end() const
Definition: location.h:72
void set(std::list< Selectable * > const &)
Definition: selection.cc:1024
Definition: Beats.hpp:239
virtual void motion(GdkEvent *, bool)
Definition: editor_drag.cc:745
std::list< boost::shared_ptr< AutomationLine > > lines() const
int _visible_y_low
Definition: editor_drag.h:297
LIBPBD_API Transmitter error
double y_fraction(boost::shared_ptr< AutomationLine >, double global_y_position) const
LIBARDOUR_API GQuark fixed_time_region_copy
Definition: operations.cc:36
double grab_x() const
Definition: editor_drag.h:192
double _cumulative_y_drag
Definition: editor_drag.h:827
ARDOUR::TempoSection & tempo() const
Definition: marker.h:142
void clear_owned_changes()
Definition: playlist.cc:2054
CrossfadeEdgeDrag(Editor *, AudioRegionView *, ArdourCanvas::Item *, bool start)
bool get_play_range() const
Definition: session.h:822
bool operator()(const DraggingView &a, const DraggingView &b)
Definition: editor_drag.cc:739
ARDOUR::Session * session() const
Definition: editor.h:141
void finished(GdkEvent *, bool)
XMLNode * before_state
Definition: editor_drag.h:675
void aborted(bool)
bool _starting_point_passed
true if we called move () with first_move flag, otherwise false
Definition: editor_drag.h:241
bool movable() const
Definition: tempo.h:103
void get_regions_after(RegionSelection &, framepos_t where, const TrackViewList &ts) const
Definition: editor.cc:4834
sigc::signal< void > TimeChanged
Definition: selection.h:99
void range(ARDOUR::framepos_t &start, ARDOUR::framepos_t &end)
Definition: selection.cc:1187
std::ostream & endmsg(std::ostream &ostr)
Definition: transmitter.h:71
framepos_t initial_end
initial end position of the region
Definition: editor_drag.h:274
void start_grab(GdkEvent *, Gdk::Cursor *c=0)
PatchChangeDrag(Editor *, PatchChange *, MidiRegionView *)
bool _y_constrained
true if y motion is constrained, otherwise false
Definition: editor_drag.h:235
ArdourCanvas::Container * get_trackview_group() const
Definition: editor.h:507
void collect_new_region_view(RegionView *)
void finished(GdkEvent *, bool)
StreamView * view() const
void unique_select(NoteBase *ev)
double current_pointer_x() const
Definition: editor_drag.h:79
boost::shared_ptr< ARDOUR::AudioRegion > audio_region() const
void edit_meter_marker(MeterMarker &)
Evoral::Beats region_frames_to_region_beats(framepos_t) const
session start
Definition: marker.h:48
Evoral::Beats get_grid_type_as_beats(bool &success, framepos_t position)
Definition: editor.cc:4090
LIBARDOUR_API PBD::PropertyDescriptor< framepos_t > start
Definition: region.cc:63
void marks_either_side(framepos_t const, framepos_t &, framepos_t &) const
Definition: location.cc:1283
void aborted(bool)
ARDOUR::framepos_t _current_pointer_frame
frame that the pointer is now at
Definition: editor_drag.h:99
ARDOUR::frameoffset_t quantify_frames_to_apv(ARDOUR::frameoffset_t offset)
void request_locate(framepos_t frame, bool with_roll=false)
void start_grab(GdkEvent *, Gdk::Cursor *c=0)
XMLNode * state
the XML state node before the drag
Definition: editor_drag.h:1082
uint32_t current_height() const
virtual void drag_start()
EditorCursor * playhead_cursor
Definition: editor.h:1011
framecnt_t frame_rate() const
Definition: session.h:365
void flush_videotimeline_cache(bool localcacheonly=false)
Definition: ardour_ui.cc:4017
double get_x() const
Definition: control_point.h:59
ArdourCanvas::Points _points
Definition: editor_drag.h:805
static bool is_invalid(Gdk::Cursor *c)
Definition: mouse_cursors.h:83
Round to nearest.
Definition: types.h:224
void by_position(std::list< RegionView * > &) const
uint64_t Drags
Definition: debug.cc:28
void start_grab(GdkEvent *, Gdk::Cursor *c=0)
double y_position() const
ScrubDrag(Editor *, ArdourCanvas::Item *)
#define invalidator(x)
Definition: gui_thread.h:40
TimeAxisView * orig_tav
Definition: editor_drag.h:450
void aborted(bool)
void thaw(bool from_undo=false)
Definition: playlist.cc:422
void cancel_audition()
Definition: session.cc:4160
double _grab_y
y of the grab start position, possibly adjusted if _trackview_only is true
Definition: editor_drag.h:245
void new_transport_marker_context_menu(GdkEventButton *, ArdourCanvas::Item *)
boost::shared_ptr< AutomationLine > line()
bool empty(bool internal_selection=false)
Definition: selection.cc:938
Operation _operation
Definition: editor_drag.h:1028
uint8_t lowest_note() const
EditorCursor * line
Definition: editor_drag.h:473
virtual void aborted(bool)
void queue_full_time_code()
Definition: session.h:910
virtual std::pair< ARDOUR::framecnt_t, int > move_threshold() const
Definition: editor_drag.h:163
framepos_t source_beats_to_absolute_frames(Evoral::Beats beats) const
virtual bool regions_came_from_canvas() const =0
void aborted(bool)
MidiStreamView * midi_stream_view() const
static Handle create(Editor &editor, Gdk::Cursor *cursor)
void setup_pointer_frame_offset()
void motion(GdkEvent *, bool)
double _cumulative_dx
Definition: editor_drag.h:526
void update_transient(float old_pos, float new_pos)
uint32_t _max_x
Definition: editor_drag.h:872
void select_things(int, framepos_t, framepos_t, double, double, bool)
Operation _operation
Definition: editor_drag.h:645
const Timecode::BBT_Time & start() const
Definition: tempo.h:99
void finished_copy(bool const, bool const, ARDOUR::framecnt_t const )
void motion(GdkEvent *, bool)
void motion(GdkEvent *, bool)
void clear_time()
Definition: selection.cc:141
Locations * locations()
Definition: session.h:382
MeterMarkerDrag(Editor *, ArdourCanvas::Item *, bool)
ARDOUR::framepos_t _last_pointer_frame
adjusted_frame the last time a motion occurred
Definition: editor_drag.h:250
double y_to_region(double) const
bool set_position(framepos_t pos, void *src, double *delta=0)
Definition: region_view.cc:464
void maybe_locate_with_edit_preroll(framepos_t)
Definition: editor_ops.cc:2526
ARDOUR::framepos_t grab_frame() const
Definition: editor_drag.h:204
void request_suspend_timecode_transmission()
void add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
#define _(Text)
Definition: i18n.h:11
int time_stretch(RegionSelection &, float fraction)
void move(ArdourCanvas::Duple)
uint32_t _ntracks
Definition: editor_drag.h:299
void note_selected(NoteBase *ev, bool add, bool extend=false)
virtual void enable_display(bool yn)
Definition: region_view.h:97
ARDOUR::frameoffset_t _max_backwards_drag
Definition: editor_drag.h:617
void motion(GdkEvent *, bool)
void move(double xdelta, double ydelta)
Definition: region_view.cc:677
void set_height(uint32_t h, TrackHeightMode m=OnlySelf)
void add(std::list< Selectable * > const &)
Definition: selection.cc:1045
void aborted(bool)
double _current_pointer_x
canvas-coordinate space x of the current pointer
Definition: editor_drag.h:97
ARDOUR::framepos_t _grab_frame
adjusted_frame that the mouse was at when start_grab was called, or 0
Definition: editor_drag.h:249
std::list< DraggingView > _views
information about all views that are being dragged
Definition: editor_drag.h:290
boost::shared_ptr< ARDOUR::MidiRegion > add_region(ARDOUR::framepos_t, ARDOUR::framecnt_t, bool)
void finished(GdkEvent *, bool)
bool contains(RegionView *) const
bool _dragging_playhead
Definition: editor.h:1512
double current_pointer_y() const
Definition: editor_drag.cc:353
virtual void show_timestretch(framepos_t start, framepos_t end, int layers, int layer)
ARDOUR::framecnt_t maximum_time() const
void setup_pointer_frame_offset()
MidiRegionView & region_view() const
Definition: note_base.h:104
LIBGTKMM2EXT_API uint64_t Keyboard
Definition: debug.cc:23
void toggle(std::list< Selectable * > const &)
Definition: selection.cc:991
framepos_t current_frame() const
bool region_is_shuffle_constrained(boost::shared_ptr< Region >)
Definition: playlist.cc:2800
void add_region(boost::shared_ptr< Region >, framepos_t position, float times=1, bool auto_partition=false)
Definition: playlist.cc:668
void finished(GdkEvent *, bool)
void request_stop(bool abort=false, bool clear_state=false)
void motion(GdkEvent *, bool)
class LIBPBD_API StatefulDiffCommand
const DataType & data_type() const
Definition: region.h:102
#define X_(Text)
Definition: i18n.h:13
int64_t framecnt_t
Definition: types.h:76
void aborted(bool)
MidiRubberbandSelectDrag(Editor *, MidiRegionView *)
void start_grab(GdkEvent *, Gdk::Cursor *c=0)
uint32_t order_key() const
Definition: route.cc:306
LIBARDOUR_API RCConfiguration * Config
Definition: globals.cc:119
void add_meter(const Meter &, Timecode::BBT_Time where)
Definition: tempo.cc:590
uint32_t clicked_selection
Definition: editor.h:703
Editor * _editor
Definition: editor_drag.h:94
void set_position(framepos_t)
Definition: region.cc:579
int set(framepos_t start, framepos_t end, bool allow_bbt_recompute=true)
Definition: location.cc:321
float mouse_x_fraction() const
Definition: note_base.h:100
void replace(uint32_t time_index, framepos_t start, framepos_t end)
Definition: selection.cc:563
AVDraggingView(RegionView *)
guint32 height() const
double _cumulative_dy
Definition: editor_drag.h:527
bool video_locked() const
Definition: region.h:166
void point_trim(GdkEvent *, framepos_t)
const std::list< RegionView * > & by_layer() const
boost::shared_ptr< ARDOUR::Region > region() const
Definition: region_view.h:66
framecnt_t samples_per_pixel
Definition: editor.h:581
bool _ending
true if end_grab or abort is in progress, otherwise false
Definition: editor_drag.h:96
VideoTimeLine * video_timeline
Definition: ardour_ui.h:236
void finished(GdkEvent *, bool)
void motion(GdkEvent *, bool)
Definition: enums.h:35
virtual bool y_movement_matters() const
Definition: editor_drag.h:177
void set_position(framepos_t)
void finished(GdkEvent *, bool)
ARDOUR::framepos_t raw_grab_frame() const
Definition: editor_drag.h:200
void motion(GdkEvent *, bool)
LineDrag(Editor *e, ArdourCanvas::Item *i)
double _last_pointer_y
trackview y of the pointer last time a motion occurred
Definition: editor_drag.h:247
ARDOUR::Location * temp_location
Definition: editor.h:1853
void begin_reversible_selection_op(std::string cmd_name)
Definition: editor.cc:3366
std::vector< TimeAxisView * > _time_axis_views
Definition: editor_drag.h:293
void note_deselected(NoteBase *ev)
void motion(GdkEvent *, bool)
Editing::MouseMode current_mouse_mode() const
Definition: editor.h:179
void finished(GdkEvent *, bool)
double eval(double where)
void shuffle(boost::shared_ptr< Region >, int dir)
Definition: playlist.cc:2686
void aborted(bool)
void finished(GdkEvent *, bool)
ArdourCanvas::Container * canvas_display()
virtual void start_grab(GdkEvent *, Gdk::Cursor *)
Definition: editor_drag.cc:567
bool _old_follow_playhead
state of Editor::follow_playhead() before the drags started
Definition: editor_drag.h:100
void region_going_away(RegionView *)
Definition: editor_drag.cc:521
virtual std::string get_verbose_cursor_string(double) const
Round down only if necessary.
Definition: types.h:222
framepos_t region_beats_to_region_frames(Evoral::Beats beats) const
double _grab_zoom
editor frames per unit when our grab started
Definition: editor_drag.h:730
ArdourCanvas::Rectangle * _drag_rect
Definition: editor_drag.h:1029
Definition: amp.h:29
ArdourCanvas::Rectangle * transport_bar_drag_rect
Definition: editor.h:1835
boost::shared_ptr< Route > master_out() const
Definition: session.h:718
virtual bool allow_vertical_autoscroll() const
Definition: editor_drag.h:167
double current_pointer_y() const
Definition: editor_drag.h:84
ARDOUR::frameoffset_t snap_frame_to_frame(ARDOUR::frameoffset_t) const
Definition: region_view.cc:945
void request_transport_speed(double speed, bool as_default=false)
void set_fade_in_length(framecnt_t)
double _cumulative_y_drag
Definition: editor_drag.h:849
bool autoscroll_active() const
void visible_order_range(int *, int *) const
static Beats ticks(uint32_t ticks)
Definition: Beats.hpp:49
framecnt_t get_duration() const
bool active(Editing::MouseMode m)
double _note_height
Definition: editor_drag.h:529
RegionCutDrag(Editor *, ArdourCanvas::Item *, framepos_t)
bool locked() const
Definition: location.h:67
MarkerDrag(Editor *, ArdourCanvas::Item *)
CursorContext::Handle _cursor_ctx
cursor change context
Definition: editor_drag.h:251
bool is_active() const
Definition: route_group.h:66
virtual bool y_movement_allowed(int, double, int skip_invisible=0) const
Definition: editor_drag.cc:687
boost::shared_ptr< ARDOUR::Region > add_midi_region(MidiTimeAxisView *)
Definition: editor_drag.cc:462
virtual std::pair< double, float > drag_motion(double, float, bool, bool with_push, uint32_t &final_index)
FadeInDrag(Editor *, ArdourCanvas::Item *, RegionView *, std::list< RegionView * > const &)
void do_select_things(GdkEvent *, bool)
framepos_t initial_position
initial position of the region
Definition: editor_drag.h:273
void setup_pointer_frame_offset()
boost::shared_ptr< ARDOUR::Track > track() const
Definition: route_ui.cc:1738
bool enabled_property(PBD::PropertyID)
Definition: route_group.cc:516
bool _time_selection_at_start
Definition: editor_drag.h:993
double last_pointer_frame() const
Definition: editor_drag.h:216
void swap_grab(ArdourCanvas::Item *, Gdk::Cursor *, uint32_t)
Definition: editor_drag.cc:234
bool y_movement_allowed(int delta_track, double delta_layer, int skip_invisible=0) const
ARDOUR::MeterSection & meter() const
Definition: marker.h:154
#define gui_context()
Definition: gui_thread.h:36
void stop_canvas_autoscroll()
boost::shared_ptr< ARDOUR::Region > _region
Definition: editor_drag.h:488
void clear_points()
Definition: selection.cc:1078
Definition: enums.h:36
MidiRegionSelection midi_regions
Definition: selection.h:88
void update_vertical_drag_selection(double last_y, double y, bool extend)
AutomationRangeDrag(Editor *, AutomationTimeAxisView *, std::list< ARDOUR::AudioRange > const &)
void abort()
Definition: editor_drag.cc:428
void motion(GdkEvent *, bool)
NoteBase * _primary
Definition: editor_drag.h:525
virtual void editor_add(double when, double value)
void start_grab(GdkEvent *, Gdk::Cursor *c=0)
Drag(Editor *, ArdourCanvas::Item *, bool trackview_only=true)
Definition: editor_drag.cc:217
RegionSelection get_regions_from_selection_and_mouse(framepos_t)
Definition: editor.cc:4918
void motion(GdkEvent *, bool)
void set(Drag *, GdkEvent *, Gdk::Cursor *c=MouseCursors::invalid_cursor())
Definition: editor_drag.cc:124
RegionCreateDrag(Editor *, ArdourCanvas::Item *, TimeAxisView *)
void set_duration(framepos_t, framepos_t)
void finished(GdkEvent *, bool)
void show_verbose_cursor_text(std::string const &)
Definition: editor_drag.cc:455
Gdk::Cursor * right_side_trim
Definition: mouse_cursors.h:39
bool _x_constrained
true if x motion is constrained, otherwise false
Definition: editor_drag.h:234
AutomationLine * _line
Definition: editor_drag.h:846
ARDOUR::MidiModel::PatchChangePtr patch() const
Definition: patch_change.h:57
void snap_to_with_modifier(framepos_t &first, GdkEvent const *ev, ARDOUR::RoundMode direction=ARDOUR::RoundNearest, bool for_mark=false)
Definition: editor.cc:2593
void aborted(bool)
uint32_t layer_t
Definition: types.h:59
void motion(GdkEvent *, bool)
ArdourCanvas::Rectangle * cd_marker_bar_drag_rect
Definition: editor.h:1833
virtual void thaw_after_trim()
Definition: region_view.cc:920
double _current_pointer_y
canvas-coordinate space y of the current pointer
Definition: editor_drag.h:98
void select_things(int, framepos_t, framepos_t, double, double, bool)
double layer
Definition: editor_drag.h:271
MouseCursors const * cursors() const
Definition: editor.h:473
framecnt_t prev_amount
Definition: editor_drag.h:451
double _last_pointer_x
trackview x of the pointer last time a motion occurred
Definition: editor_drag.h:246
void motion(GdkEvent *, bool)
frameoffset_t sync_offset(int &dir) const
Definition: region.cc:1041
#define DEBUG_TRACE(bits, str)
Definition: debug.h:55
RegionSelection regions
Definition: selection.h:82
ARDOUR::framepos_t current_pointer_frame() const
Definition: editor_drag.h:89
void set_fade_out_active(bool yn)
int64_t framepos_t
Definition: types.h:66
bool motion_handler(GdkEvent *, bool)
Definition: editor_drag.cc:180
virtual bool end_grab(GdkEvent *)
Definition: editor_drag.cc:310
bool hidden() const
sigc::signal< void, framepos_t > UpdateAllTransportClocks
VerboseCursor * verbose_cursor() const
Definition: editor.h:477
FadeOutDrag(Editor *, ArdourCanvas::Item *, RegionView *, std::list< RegionView * > const &)
void mouse_brush_insert_region(RegionView *, framepos_t pos)
virtual void finished(GdkEvent *e, bool m)=0
uint32_t _pdropzone
Definition: editor_drag.h:342
double _cumulative_dx
Definition: editor_drag.h:579
size_t selection_size()
ARDOUR::framepos_t _raw_grab_frame
unsnapped frame that the mouse was at when start_grab was called, or 0
Definition: editor_drag.h:248
void motion(GdkEvent *, bool)
double note_divisor() const
Definition: tempo.h:71
int64_t frameoffset_t
Definition: types.h:71
XMLNode * before_state
Definition: editor_drag.h:702
virtual void show_region_editor()
Definition: region_view.cc:547
ArdourCanvas::Container * meter_group
Definition: editor.h:817
void find_all_between(framepos_t start, framepos_t, LocationList &, Location::Flags)
Definition: location.cc:1405
void set_position(framepos_t)
Definition: marker.cc:441
AudioRegionView * _arv
Definition: editor_drag.h:866
void maybe_update_session_range(framepos_t, framepos_t)
Definition: session.cc:3461
void update_resizing(NoteBase *, bool, double, bool)
void start_grab(GdkEvent *, Gdk::Cursor *c=0)
layer_t layer() const
Definition: region.h:115
ChanCount n_inputs() const
Definition: route.h:92
T * get() const
Definition: shared_ptr.hpp:268
Gdk::Cursor * fader
Definition: mouse_cursors.h:54
Operation _operation
Definition: editor_drag.h:990
ARDOUR::framepos_t adjusted_frame(ARDOUR::framepos_t, GdkEvent const *, bool snap=true) const
Definition: editor_drag.cc:325
void finished(GdkEvent *, bool)
double _fixed_grab_x
Definition: editor_drag.h:824
framepos_t adjust_to_sync(framepos_t) const
Definition: region.cc:1058
void request_resume_timecode_transmission()
Gdk::Cursor * trimmer
Definition: mouse_cursors.h:38
bool is_mark() const
Definition: location.h:94
void set_fade_in_active(bool yn)
bool is_track() const
Definition: route_ui.cc:1732
double beats_per_minute() const
Definition: tempo.h:53
ARDOUR::framecnt_t _pointer_frame_offset
Definition: editor_drag.h:233
std::list< boost::shared_ptr< Region > > RegionList
Definition: types.h:87
void sample_to_timecode(framepos_t sample, Timecode::Time &timecode, bool use_offset, bool use_subframes) const
void remove_unselected_from_views(framecnt_t amount, bool move_regions)
framepos_t position() const
Definition: region.h:112
void queue_song_position_pointer()
Definition: session.h:911
std::set< boost::shared_ptr< ARDOUR::Playlist > > playlists() const
void start_grab(GdkEvent *, Gdk::Cursor *c=MouseCursors::invalid_cursor())
Definition: editor_drag.cc:132
ArdourCanvas::Item * _item
our item
Definition: editor_drag.h:231
void maybe_autoscroll(bool, bool, bool)
TempoMarker * _marker
Definition: editor_drag.h:700
int set_start(framepos_t s, bool force=false, bool allow_bbt_recompute=true)
Definition: location.cc:181
AudioRegionView * arv
Definition: editor_drag.h:1113
MidiRegionView * _region_view
Definition: editor_drag.h:941
void finished(GdkEvent *, bool)
bool selected(TimeAxisView *)
Definition: selection.cc:920
double sample_to_pixel(framepos_t sample) const
Definition: editor.h:234
const boost::shared_ptr< NoteType > note() const
Definition: note_base.h:103
ArdourCanvas::Container * time_line_group
Definition: editor.h:1620
LocationMarkers * find_location_markers(ARDOUR::Location *) const
double _cumulative_x_drag
Definition: editor_drag.h:869
void motion(GdkEvent *, bool)
void set_position(framepos_t start, framepos_t end=0)
boost::shared_ptr< ARDOUR::Playlist > initial_playlist
Definition: editor_drag.h:276
void aborted(bool)
Editing::SnapMode snap_mode() const
Definition: editor.cc:2040
void add(Location *, bool make_current=false)
Definition: location.cc:953
void finished(GdkEvent *, bool)
void fake_locate(framepos_t)
void finished(GdkEvent *, bool)
Gdk::Cursor * anchored_left_side_trim
Definition: mouse_cursors.h:42
void aborted(bool)
void setup_pointer_frame_offset()
std::list< AVDraggingView > _views
information about all audio that are being dragged along
Definition: editor_drag.h:613
void start_grab(GdkEvent *, Gdk::Cursor *c=0)
double _grab_x
trackview x of the grab start position
Definition: editor_drag.h:244
FeatureLineDrag(Editor *e, ArdourCanvas::Item *i)
void set_time(framepos_t)
double _fixed_grab_y
Definition: editor_drag.h:825
bool motion_handler(GdkEvent *, bool)
Definition: editor_drag.cc:363
LIBARDOUR_API double gain_to_slider_position_with_max(double g, double max_gain=2.0)
Definition: utils.cc:756
void motion(GdkEvent *, bool)
void rename_marker(Marker *marker)
framepos_t start() const
void add_stateful_diff_commands_for_playlists(PlaylistSet const &)
ARDOUR::framepos_t start()
const char * name
DraggingView(RegionView *, RegionDrag *, TimeAxisView *original_tav)
framepos_t pixel_to_sample(double pixel) const
Definition: editor.h:230
bool locked() const
Definition: region.h:164
Editing::SnapType snap_type() const
Definition: editor.cc:2034
XMLNode & get_state(void)
Definition: location.cc:1017
void finished(GdkEvent *, bool)
Definition: editor.h:134
void finished(GdkEvent *, bool)
void commit_resizing(NoteBase *, bool, double, bool)
static Selection::Operation selection_type(guint state)
Definition: keyboard.cc:178
framepos_t initial_position
initial position of the region
Definition: editor_drag.h:589
uint32_t n_channels() const
Definition: region.h:259
bool _jump_position_when_done
Definition: editor_drag.h:648
void motion(GdkEvent *, bool)
XMLNode & get_state()
void finished_no_copy(bool const, bool const, ARDOUR::framecnt_t const )
bool have_item(ArdourCanvas::Item *) const
Definition: editor_drag.cc:207
double child_height() const
Definition: streamview.cc:613
framepos_t current_end_frame() const
Definition: session.cc:5048
double note_to_y(uint8_t note) const
void aborted(bool)
void request_play_range(std::list< AudioRange > *, bool leave_rolling=false)
void abort()
Definition: editor_drag.cc:95
void remove_region_from_playlist(boost::shared_ptr< ARDOUR::Region >, boost::shared_ptr< ARDOUR::Playlist >, PlaylistSet &modified_playlists)
virtual void set_height(double)
Definition: region_view.cc:730
ARDOUR::frameoffset_t total_dx() const
void finished(GdkEvent *, bool)
bool _trackview_only
true if pointer y value should always be relative to the top of the trackview group ...
Definition: editor_drag.h:239
RegionRippleDrag(Editor *, ArdourCanvas::Item *, RegionView *, std::list< RegionView * > const &)
double _fixed_grab_y
Definition: editor_drag.h:848
void move_patch_change(PatchChange &, Evoral::Beats)
const Evoral::TimeConverter< double, ARDOUR::framepos_t > & time_converter() const
double note_height() const
bool _stop
true to stop the transport on starting the drag, otherwise false
Definition: editor_drag.h:729
double _fixed_grab_x
Definition: editor_drag.h:847
void set_length(framecnt_t)
Definition: region.cc:430
framepos_t current_start_frame() const
Definition: session.cc:5042
double last_pointer_y() const
Definition: editor_drag.h:212
Definition: xml++.h:95
RegionInsertDrag(Editor *, boost::shared_ptr< ARDOUR::Region >, RouteTimeAxisView *, ARDOUR::framepos_t)
RegionSpliceDrag(Editor *, ArdourCanvas::Item *, RegionView *, std::list< RegionView * > const &)
LIBARDOUR_API PBD::PropertyDescriptor< Evoral::Beats > length_beats
Definition: midi_region.cc:57
RouteGroup * route_group() const
void finished(GdkEvent *, bool)
std::list< boost::shared_ptr< AudioTrack > > new_audio_track(int input_channels, int output_channels, TrackMode mode=Normal, RouteGroup *route_group=0, uint32_t how_many=1, std::string name_template="")
Definition: session.cc:2361
std::string name() const
void clear_tracks()
Definition: selection.cc:127
LIBARDOUR_API PBD::PropertyDescriptor< framepos_t > position
Definition: region.cc:65
TimeSelection time
Definition: selection.h:83
ARDOUR::Location * find_location_from_marker(Marker *, bool &is_start) const
void finished(GdkEvent *, bool)
void reset_fade_in_shape_width(boost::shared_ptr< ARDOUR::AudioRegion > ar, framecnt_t, bool drag_active=false)
TrackSelection tracks
Definition: selection.h:81
static UIConfiguration * config()
Definition: ardour_ui.h:188
virtual void finished(GdkEvent *, bool)
void move_selection(double dx, double dy, double cumulative_dy)
void rdiff(std::vector< Command * > &) const
Definition: playlist.cc:2047
framepos_t prev_position
Definition: editor_drag.h:452
ARDOUR::AutomationList::iterator model() const
Definition: control_point.h:80
virtual void select_things(int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)=0
uint8_t highest_note() const
int8_t total_dy() const
ArdourCanvas::Rectangle * rubberband_rect
Definition: editor.h:1859
NoteCreateDrag(Editor *, ArdourCanvas::Item *, MidiRegionView *)
Gdk::Cursor * anchored_right_side_trim
Definition: mouse_cursors.h:40
LIBARDOUR_API PBD::PropertyDescriptor< bool > select
Definition: route_group.cc:48
ArdourCanvas::Rectangle * range_bar_drag_rect
Definition: editor.h:1834
Definition: debug.h:30
uint8_t y_to_note(double y) const
virtual void region_changed(const PBD::PropertyChange &)
Definition: region_view.cc:368
void setup_pointer_frame_offset()
double divisions_per_bar() const
Definition: tempo.h:70
RegionView * insert_region_into_playlist(boost::shared_ptr< ARDOUR::Region >, RouteTimeAxisView *, ARDOUR::layer_t, ARDOUR::framecnt_t, PlaylistSet &)
double last_pointer_x() const
Definition: editor_drag.h:208
double _total_x_delta
Definition: editor_drag.h:335
void start_grab(GdkEvent *, Gdk::Cursor *c=0)
void aborted(bool)
void start_grab(GdkEvent *, Gdk::Cursor *c=0)
void start_grab(GdkEvent *, Gdk::Cursor *c=0)
Editor * _editor
our editor
Definition: editor_drag.h:229
void finished(GdkEvent *, bool)
Selection & get_selection() const
Definition: editor.h:244
bool contains(TimeAxisView const *) const
bool was_double_click() const
Definition: editor_drag.h:126
static float dB_to_coefficient(float dB)
Definition: dB.h:30
void aborted(bool)
void finished(GdkEvent *, bool)
double grab_y() const
Definition: editor_drag.h:196
void add_automation_event(GdkEvent *, framepos_t, double, bool with_guard_points)
ARDOUR::RegionList * exclude
Definition: editor_drag.h:455
int find_time_axis_view(TimeAxisView *) const
Definition: editor_drag.cc:537
framepos_t frame() const
Definition: tempo.h:100
ARDOUR::framecnt_t get_duration()
TrackViewList track_views
Definition: editor.h:1135
void reset_point_selection()
Definition: editor_ops.cc:4798
std::list< boost::shared_ptr< MidiTrack > > new_midi_track(const ChanCount &input, const ChanCount &output, boost::shared_ptr< PluginInfo > instrument=boost::shared_ptr< PluginInfo >(), TrackMode mode=Normal, RouteGroup *route_group=0, uint32_t how_many=1, std::string name_template="")
Definition: session.cc:2117
virtual LayerDisplay layer_display() const
LIBEVORAL_API uint64_t Beats
LayerDisplay layer_display() const
Definition: streamview.h:80
void finished(GdkEvent *, bool)
RouteTimeAxisView * axis_view_from_route(boost::shared_ptr< ARDOUR::Route >) const
Definition: editor.cc:5201
void aborted(bool)
framepos_t start() const
Definition: location.h:71
framepos_t round_to_beat(framepos_t frame, RoundMode dir)
Definition: tempo.cc:1301
void note_dropped(NoteBase *ev, ARDOUR::frameoffset_t, int8_t d_note)
framecnt_t length() const
Definition: region.h:114
void clear_lines()
Definition: selection.cc:210
static const framepos_t max_framepos
Definition: types.h:78
void motion(GdkEvent *, bool)
Round up only if necessary.
Definition: types.h:226
double frames_per_bar(const Tempo &, framecnt_t sr) const
Definition: tempo.cc:63
bool selected() const
Definition: note_base.h:74
RubberbandSelectDrag(Editor *, ArdourCanvas::Item *)
void split_regions_at(framepos_t, RegionSelection &)
Definition: editor_ops.cc:151
void clear_midi_notes()
Definition: selection.cc:168
MidiRegionView * region
Definition: editor_drag.h:503
double get_y() const
Definition: control_point.h:60
virtual void deselect_things()=0
void motion(GdkEvent *, bool)
MidiRegionView * _region_view
Definition: editor_drag.h:555
void motion(GdkEvent *, bool)
TrimDrag(Editor *, ArdourCanvas::Item *, RegionView *, std::list< RegionView * > const &, bool preserve_fade_anchor=false)
DragManager(Editor *e)
Definition: editor_drag.cc:81
ControlEvent * back()
void set(std::string const &)
std::pair< ARDOUR::framepos_t, ARDOUR::framepos_t > range
the range of all points on the line, in session frames
Definition: editor_drag.h:1081
ArdourCanvas::Item & grab_item() const
void aborted(bool)
static uint32_t preset_height(Height)
void finished(GdkEvent *, bool)
void aborted(bool)
ControlPointDrag(Editor *, ArdourCanvas::Item *)
bool _move_threshold_passed
true if the move threshold has been passed, otherwise false
Definition: editor_drag.h:240
void commit_reversible_command()
Definition: editor.cc:3483
void add_commands(std::vector< Command * > const &cmds)
virtual void motion(GdkEvent *e, bool f)=0
void aborted(bool)
Gdk::Cursor * left_side_trim
Definition: mouse_cursors.h:41
void send_mmc_locate(framepos_t)
void hide_region_editor()
Definition: region_view.cc:558
int _last_pointer_time_axis_view
Definition: editor_drag.h:336
void motion(GdkEvent *, bool)
virtual boost::shared_ptr< ARDOUR::Playlist > playlist() const
std::list< Drag * > _drags
Definition: editor_drag.h:95
ARDOUR::framepos_t _last_frame_position
last position of the thing being dragged
Definition: editor_drag.h:334
ARDOUR::frameoffset_t _startdrag_video_offset
Definition: editor_drag.h:616
TempoMarkerDrag(Editor *, ArdourCanvas::Item *, bool)
TimeFXDrag(Editor *, ArdourCanvas::Item *, RegionView *, std::list< RegionView * > const &)
void build_region_boundary_cache()
Definition: editor_ops.cc:665
virtual bool x_movement_matters() const
Definition: editor_drag.h:172
bool is_offset_locked()
LIBARDOUR_API GQuark insert_region
Definition: operations.cc:28
PBD::ScopedConnection death_connection
Definition: editor_drag.h:306
void begin_resizing(bool at_front)
void start_grab(GdkEvent *, Gdk::Cursor *c=0)
void start_grab(GdkEvent *, Gdk::Cursor *c=0)
void finished(GdkEvent *, bool)
int64_t framepos_t
std::vector< Marker * > markers
Definition: editor_drag.h:798
Definition: enums.h:37
void clear_changes()
Definition: stateful.cc:184
VideoTimeLineDrag(Editor *e, ArdourCanvas::Item *i)
void aborted(bool)
LIBARDOUR_API GQuark region_copy
Definition: operations.cc:35
framecnt_t selection_length
Definition: editor_drag.h:453
void aborted(bool)
void start_grab(GdkEvent *, Gdk::Cursor *c=0)
void select_things(int, framepos_t, framepos_t, double, double, bool)
virtual void set_selected(bool yn)
void start_grab(GdkEvent *, Gdk::Cursor *c=0)
void start_grab(GdkEvent *, Gdk::Cursor *c=0)
RegionView * _new_region_view
Definition: editor_drag.h:406
boost::shared_ptr< ARDOUR::Route > route() const
Definition: route_ui.h:76
Always round down, even if on a division.
Definition: types.h:223
void start_grab(GdkEvent *, Gdk::Cursor *c=0)
void aborted(bool)
void mark_double_click()
Definition: editor_drag.cc:172
void motion(GdkEvent *, bool)
bool end_grab(GdkEvent *)
Definition: editor_drag.cc:149
void select_all_within(framepos_t, framepos_t, double, double, TrackViewList const &, Selection::Operation, bool)
int set_end(framepos_t e, bool force=false, bool allow_bbt_recompute=true)
Definition: location.cc:258
std::string string_compose(const std::string &fmt, const T1 &o1)
Definition: compose.h:208
static double _zero_gain_fraction
Definition: editor_drag.h:830
virtual void start_drag_line(uint32_t, uint32_t, float)
EditorRubberbandSelectDrag(Editor *, ArdourCanvas::Item *)
int apply_track_delta(const int start, const int delta, const int skip, const bool distance_only=false) const
Definition: editor_drag.cc:648
boost::shared_ptr< ARDOUR::Playlist > playlist() const
Definition: region.h:251
void finished(GdkEvent *, bool)
MeterMarker * _marker
Definition: editor_drag.h:673
Editing::MouseMode mouse_mode
Definition: editor.h:586
ArdourCanvas::Container * canvas_item()
Definition: streamview.h:82
void motion(GdkEvent *, bool)
RegionDrag(Editor *, ArdourCanvas::Item *, RegionView *, std::list< RegionView * > const &)
Definition: editor_drag.cc:487
void motion(GdkEvent *, bool)
void suspend_property_changes()
Definition: region.cc:1292
void edit_tempo_marker(TempoMarker &)
framepos_t last_frame() const
Definition: region.h:142
LIBARDOUR_API PBD::PropertyDescriptor< framecnt_t > length
Definition: region.cc:64
DragManager * _drags
Definition: editor_drag.h:230
double speed() const
Definition: track.cc:759
ArdourCanvas::Rectangle * _drag_rect
Definition: editor_drag.h:556
void snap_to(framepos_t &first, ARDOUR::RoundMode direction=ARDOUR::RoundNearest, bool for_mark=false)
Definition: editor.cc:2611
bool _pending_locate_request
Definition: editor.h:2162
LIBARDOUR_API PBD::PropertyDescriptor< bool > color
Definition: route_group.cc:50