119bcc34b1e18fe3a8c8a1449597f0855bfaa665
[ardour.git] / gtk2_ardour / editor_drag.h
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 #ifndef __gtk2_ardour_editor_drag_h_
21 #define __gtk2_ardour_editor_drag_h_
22
23 #include <list>
24
25 #include <gdk/gdk.h>
26 #include <stdint.h>
27
28 #include "ardour/types.h"
29
30 #include "canvas.h"
31 #include "editor_items.h"
32
33 namespace ARDOUR {
34         class Location;
35 }
36
37 class Editor;
38 class EditorCursor;
39 class TimeAxisView;
40 class Drag;
41
42 /** Class to manage current drags */
43 class DragManager
44 {
45 public:
46
47         DragManager (Editor* e);
48         ~DragManager ();
49
50         bool motion_handler (GdkEvent *, bool);
51
52         void abort ();
53         void break_drag ();
54         void add (Drag *);
55         void set (Drag *, GdkEvent *, Gdk::Cursor* c = 0);
56         void start_grab (GdkEvent *);
57         bool end_grab (GdkEvent *);
58         bool have_item (ArdourCanvas::Item *) const;
59         std::pair<nframes64_t, nframes64_t> extent () const;
60
61         /** @return true if an end drag or break_drag is in progress */
62         bool ending () const {
63                 return _ending;
64         }
65
66         bool active () const {
67                 return !_drags.empty ();
68         }
69
70         /** @return current pointer x position in trackview coordinates */
71         double current_pointer_x () const {
72                 return _current_pointer_x;
73         }
74
75         /** @return current pointer y position in trackview coordinates */
76         double current_pointer_y () const {
77                 return _current_pointer_y;
78         }
79
80         /** @return current pointer frame */
81         nframes64_t current_pointer_frame () const {
82                 return _current_pointer_frame;
83         }
84
85 private:
86         Editor* _editor;
87         std::list<Drag*> _drags;
88         bool _ending; ///< true if end_grab or break_drag is in progress, otherwise false
89         double _current_pointer_x; ///< trackview x of the current pointer
90         double _current_pointer_y; ///< trackview y of the current pointer
91         nframes64_t _current_pointer_frame; ///< frame that the pointer is now at
92 };
93
94 /** Abstract base class for dragging of things within the editor */
95 class Drag
96 {
97 public:
98         Drag (Editor *, ArdourCanvas::Item *);
99         virtual ~Drag () {}
100
101         void set_manager (DragManager* m) {
102                 _drags = m;
103         }
104
105         /** @return the canvas item being dragged */
106         ArdourCanvas::Item* item () const {
107                 return _item;
108         }
109
110         void swap_grab (ArdourCanvas::Item *, Gdk::Cursor *, uint32_t);
111         bool motion_handler (GdkEvent*, bool);
112         void break_drag ();
113
114         nframes64_t adjusted_frame (nframes64_t, GdkEvent const *, bool snap = true) const;
115         nframes64_t adjusted_current_frame (GdkEvent const *, bool snap = true) const;
116         
117         /** Called to start a grab of an item.
118          *  @param e Event that caused the grab to start.
119          *  @param c Cursor to use, or 0.
120          */
121         virtual void start_grab (GdkEvent* e, Gdk::Cursor* c = 0);
122
123         virtual bool end_grab (GdkEvent *);
124
125         /** Called when a drag motion has occurred.
126          *  @param e Event describing the motion.
127          *  @param f true if this is the first movement, otherwise false.
128          */
129         virtual void motion (GdkEvent* e, bool f) = 0;
130
131         /** Called when a drag has finished.
132          *  @param e Event describing the finish.
133          *  @param m true if some movement occurred, otherwise false.
134          */
135         virtual void finished (GdkEvent* e, bool m) = 0;
136
137         /** Called to abort a drag and return things to how
138          *  they were before it started.
139          */
140         virtual void aborted () = 0;
141
142         /** @param m Mouse mode.
143          *  @return true if this drag should happen in this mouse mode.
144          */
145         virtual bool active (Editing::MouseMode m) {
146                 return (m != Editing::MouseGain);
147         }
148
149         /** @return minimum number of frames (in x) and pixels (in y) that should be considered a movement */
150         virtual std::pair<nframes64_t, int> move_threshold () const {
151                 return std::make_pair (1, 1);
152         }
153
154         virtual bool allow_vertical_autoscroll () const {
155                 return true;
156         }
157
158         /** @return true if x movement matters to this drag */
159         virtual bool x_movement_matters () const {
160                 return true;
161         }
162
163         /** @return true if y movement matters to this drag */
164         virtual bool y_movement_matters () const {
165                 return true;
166         }
167
168         /** @return current x extent of the thing being dragged; ie
169          *  a pair of (leftmost_position, rightmost_position)
170          */
171         virtual std::pair<nframes64_t, nframes64_t> extent () const;
172
173 protected:
174
175         double grab_x () const {
176                 return _grab_x;
177         }
178
179         double grab_y () const {
180                 return _grab_y;
181         }
182
183         double grab_frame () const {
184                 return _grab_frame;
185         }
186
187         double last_pointer_x () const {
188                 return _last_pointer_x;
189         }
190
191         double last_pointer_y () const {
192                 return _last_pointer_y;
193         }
194
195         double last_pointer_frame () const {
196                 return _last_pointer_frame;
197         }
198
199         Editor* _editor; ///< our editor
200         DragManager* _drags;
201         ArdourCanvas::Item* _item; ///< our item
202         /** Offset from the mouse's position for the drag to the start of the thing that is being dragged */
203         nframes64_t _pointer_frame_offset;
204         bool _x_constrained; ///< true if x motion is constrained, otherwise false
205         bool _y_constrained; ///< true if y motion is constrained, otherwise false
206         bool _was_rolling; ///< true if the session was rolling before the drag started, otherwise false
207
208 private:
209
210         bool _move_threshold_passed; ///< true if the move threshold has been passed, otherwise false
211         double _grab_x; ///< trackview x of the grab start position
212         double _grab_y; ///< trackview y of the grab start position
213         double _last_pointer_x; ///< trackview x of the pointer last time a motion occurred
214         double _last_pointer_y; ///< trackview y of the pointer last time a motion occurred
215         nframes64_t _grab_frame; ///< adjusted_frame that the mouse was at when start_grab was called, or 0
216         nframes64_t _last_pointer_frame; ///< adjusted_frame the last time a motion occurred
217 };
218
219
220 /** Abstract base class for drags that involve region(s) */
221 class RegionDrag : public Drag, public sigc::trackable
222 {
223 public:
224         RegionDrag (Editor *, ArdourCanvas::Item *, RegionView *, std::list<RegionView*> const &);
225         virtual ~RegionDrag () {}
226
227         virtual std::pair<nframes64_t, nframes64_t> extent () const;
228
229 protected:
230
231         RegionView* _primary; ///< the view that was clicked on (or whatever) to start the drag
232         std::list<RegionView*> _views; ///< all views that are being dragged
233
234 private:
235         void region_going_away (RegionView *);
236         PBD::ScopedConnection death_connection;
237 };
238
239
240 /** Drags involving region motion from somewhere */
241 class RegionMotionDrag : public RegionDrag
242 {
243 public:
244
245         RegionMotionDrag (Editor *, ArdourCanvas::Item *, RegionView *, std::list<RegionView*> const &, bool);
246         virtual ~RegionMotionDrag () {}
247
248         virtual void start_grab (GdkEvent *, Gdk::Cursor *);
249         virtual void motion (GdkEvent *, bool);
250         virtual void finished (GdkEvent *, bool) = 0;
251         virtual void aborted ();
252
253 protected:
254         struct TimeAxisViewSummary {
255                 TimeAxisViewSummary () : height_list(512) {}
256
257                 std::bitset<512> tracks;
258                 std::vector<int32_t> height_list;
259                 int visible_y_low;
260                 int visible_y_high;
261         };
262
263         void copy_regions (GdkEvent *);
264         bool y_movement_disallowed (int, int, int, TimeAxisViewSummary const &) const;
265         std::map<RegionView*, std::pair<RouteTimeAxisView*, int> > find_time_axis_views_and_layers ();
266         double compute_x_delta (GdkEvent const *, nframes64_t *);
267         bool compute_y_delta (
268                 TimeAxisView const *, TimeAxisView*, int32_t, int32_t, TimeAxisViewSummary const &,
269                 int32_t *, int32_t *, int32_t *
270                 );
271
272         TimeAxisViewSummary get_time_axis_view_summary ();
273         bool x_move_allowed () const;
274
275         TimeAxisView* _dest_trackview;
276         ARDOUR::layer_t _dest_layer;
277         bool check_possible (RouteTimeAxisView **, ARDOUR::layer_t *);
278         bool _brushing;
279         nframes64_t _last_frame_position; ///< last position of the thing being dragged
280         double _total_x_delta;
281 };
282
283
284 /** Drags to move (or copy) regions that are already shown in the GUI to
285  *  somewhere different.
286  */
287 class RegionMoveDrag : public RegionMotionDrag
288 {
289 public:
290         RegionMoveDrag (Editor *, ArdourCanvas::Item *, RegionView *, std::list<RegionView*> const &, bool, bool);
291         virtual ~RegionMoveDrag () {}
292
293         virtual void start_grab (GdkEvent *, Gdk::Cursor *);
294         void motion (GdkEvent *, bool);
295         void finished (GdkEvent *, bool);
296         void aborted ();
297
298         std::pair<nframes64_t, int> move_threshold () const {
299                 return std::make_pair (4, 4);
300         }
301
302 private:
303         bool _copy;
304 };
305
306 /** Drag to insert a region from somewhere */
307 class RegionInsertDrag : public RegionMotionDrag
308 {
309 public:
310         RegionInsertDrag (Editor *, boost::shared_ptr<ARDOUR::Region>, RouteTimeAxisView*, nframes64_t);
311
312         void finished (GdkEvent *, bool);
313         void aborted ();
314 };
315
316 /** Region drag in splice mode */
317 class RegionSpliceDrag : public RegionMoveDrag
318 {
319 public:
320         RegionSpliceDrag (Editor *, ArdourCanvas::Item *, RegionView *, std::list<RegionView*> const &);
321
322         void motion (GdkEvent *, bool);
323         void finished (GdkEvent *, bool);
324         void aborted ();
325 };
326
327 /** Drags to create regions */
328 class RegionCreateDrag : public Drag
329 {
330 public:
331         RegionCreateDrag (Editor *, ArdourCanvas::Item *, TimeAxisView *);
332
333         void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
334         void motion (GdkEvent *, bool);
335         void finished (GdkEvent *, bool);
336         void aborted ();
337
338 private:
339         TimeAxisView* _view;
340         TimeAxisView* _dest_trackview;
341 };
342
343 /** Drags to resize MIDI notes */
344 class NoteResizeDrag : public Drag
345 {
346 public:
347         NoteResizeDrag (Editor *, ArdourCanvas::Item *);
348
349         void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
350         void motion (GdkEvent *, bool);
351         void finished (GdkEvent *, bool);
352         void aborted ();
353
354 private:
355         MidiRegionView*     region;
356         bool                relative;
357         bool                at_front;
358 };
359
360 class NoteDrag : public Drag
361 {
362   public:
363         NoteDrag (Editor*, ArdourCanvas::Item*);
364
365         void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
366         void motion (GdkEvent *, bool);
367         void finished (GdkEvent *, bool);
368         void aborted ();
369
370   private:
371         MidiRegionView* region;
372         double last_x;
373         double last_y;
374         double drag_delta_x;
375         double drag_delta_note;
376         bool   was_selected;
377 };
378
379 /** Drag of region gain */
380 class RegionGainDrag : public Drag
381 {
382 public:
383         RegionGainDrag (Editor *e, ArdourCanvas::Item *i) : Drag (e, i) {}
384
385         void motion (GdkEvent *, bool);
386         void finished (GdkEvent *, bool);
387         bool active (Editing::MouseMode m) {
388                 return (m == Editing::MouseGain);
389         }
390
391         void aborted ();
392 };
393
394 /** Drag to trim region(s) */
395 class TrimDrag : public RegionDrag
396 {
397 public:
398         enum Operation {
399                 StartTrim,
400                 EndTrim,
401                 ContentsTrim,
402         };
403
404         TrimDrag (Editor *, ArdourCanvas::Item *, RegionView*, std::list<RegionView*> const &);
405
406         void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
407         void motion (GdkEvent *, bool);
408         void finished (GdkEvent *, bool);
409         void aborted ();
410
411         bool y_movement_matters () const {
412                 return false;
413         }
414
415         std::pair<nframes64_t, nframes64_t> extent () const;
416
417 private:
418
419         Operation _operation;
420         bool _have_transaction; ///< true if a transaction has been started, false otherwise. Must be set true by derived class.
421 };
422
423 /** Meter marker drag */
424 class MeterMarkerDrag : public Drag
425 {
426 public:
427         MeterMarkerDrag (Editor *, ArdourCanvas::Item *, bool);
428
429         void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
430         void motion (GdkEvent *, bool);
431         void finished (GdkEvent *, bool);
432         void aborted ();
433
434         bool allow_vertical_autoscroll () const {
435                 return false;
436         }
437
438         bool y_movement_matters () const {
439                 return false;
440         }
441         
442 private:
443         MeterMarker* _marker;
444         bool _copy;
445 };
446
447 /** Tempo marker drag */
448 class TempoMarkerDrag : public Drag
449 {
450 public:
451         TempoMarkerDrag (Editor *, ArdourCanvas::Item *, bool);
452
453         void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
454         void motion (GdkEvent *, bool);
455         void finished (GdkEvent *, bool);
456         void aborted ();
457
458         bool allow_vertical_autoscroll () const {
459                 return false;
460         }
461
462         bool y_movement_matters () const {
463                 return false;
464         }
465
466 private:
467         TempoMarker* _marker;
468         bool _copy;
469 };
470
471
472 /** Drag of a cursor */
473 class CursorDrag : public Drag
474 {
475 public:
476         CursorDrag (Editor *, ArdourCanvas::Item *, bool);
477
478         void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
479         void motion (GdkEvent *, bool);
480         void finished (GdkEvent *, bool);
481         void aborted ();
482
483         bool active (Editing::MouseMode) {
484                 return true;
485         }
486
487         bool allow_vertical_autoscroll () const {
488                 return false;
489         }
490
491         bool y_movement_matters () const {
492                 return false;
493         }
494
495 private:
496         EditorCursor* _cursor; ///< cursor being dragged
497         bool _stop; ///< true to stop the transport on starting the drag, otherwise false
498
499 };
500
501 /** Region fade-in drag */
502 class FadeInDrag : public RegionDrag
503 {
504 public:
505         FadeInDrag (Editor *, ArdourCanvas::Item *, RegionView *, std::list<RegionView*> const &);
506
507         void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
508         void motion (GdkEvent *, bool);
509         void finished (GdkEvent *, bool);
510         void aborted ();
511
512         bool y_movement_matters () const {
513                 return false;
514         }
515 };
516
517 /** Region fade-out drag */
518 class FadeOutDrag : public RegionDrag
519 {
520 public:
521         FadeOutDrag (Editor *, ArdourCanvas::Item *, RegionView *, std::list<RegionView*> const &);
522
523         void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
524         void motion (GdkEvent *, bool);
525         void finished (GdkEvent *, bool);
526         void aborted ();
527
528         bool y_movement_matters () const {
529                 return false;
530         }
531 };
532
533 /** Marker drag */
534 class MarkerDrag : public Drag
535 {
536 public:
537         MarkerDrag (Editor *, ArdourCanvas::Item *);
538         ~MarkerDrag ();
539
540         void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
541         void motion (GdkEvent *, bool);
542         void finished (GdkEvent *, bool);
543         void aborted ();
544
545         bool allow_vertical_autoscroll () const {
546                 return false;
547         }
548
549         bool y_movement_matters () const {
550                 return false;
551         }
552         
553 private:
554         void update_item (ARDOUR::Location *);
555
556         Marker* _marker; ///< marker being dragged
557         std::list<ARDOUR::Location*> _copied_locations;
558         ArdourCanvas::Line* _line;
559         ArdourCanvas::Points _points;
560 };
561
562 /** Control point drag */
563 class ControlPointDrag : public Drag
564 {
565 public:
566         ControlPointDrag (Editor *, ArdourCanvas::Item *);
567
568         void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
569         void motion (GdkEvent *, bool);
570         void finished (GdkEvent *, bool);
571         void aborted ();
572
573         bool active (Editing::MouseMode m);
574
575 private:
576
577         ControlPoint* _point;
578         double _time_axis_view_grab_x;
579         double _time_axis_view_grab_y;
580         double _cumulative_x_drag;
581         double _cumulative_y_drag;
582         static double const _zero_gain_fraction;
583 };
584
585 /** Gain or automation line drag */
586 class LineDrag : public Drag
587 {
588 public:
589         LineDrag (Editor *e, ArdourCanvas::Item *i);
590
591         void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
592         void motion (GdkEvent *, bool);
593         void finished (GdkEvent *, bool);
594         void aborted ();
595
596         bool active (Editing::MouseMode) {
597                 return true;
598         }
599
600 private:
601
602         AutomationLine* _line;
603         double _time_axis_view_grab_x;
604         double _time_axis_view_grab_y;
605         uint32_t _before;
606         uint32_t _after;
607         double _cumulative_y_drag;
608 };
609
610 /** Dragging of a rubberband rectangle for selecting things */
611 class RubberbandSelectDrag : public Drag
612 {
613 public:
614         RubberbandSelectDrag (Editor *e, ArdourCanvas::Item *i) : Drag (e, i) {}
615
616         void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
617         void motion (GdkEvent *, bool);
618         void finished (GdkEvent *, bool);
619         void aborted ();
620
621         std::pair<nframes64_t, int> move_threshold () const {
622                 return std::make_pair (8, 1);
623         }
624 };
625
626 /** Region drag in time-FX mode */
627 class TimeFXDrag : public RegionDrag
628 {
629 public:
630         TimeFXDrag (Editor *e, ArdourCanvas::Item *i, RegionView* p, std::list<RegionView*> const & v) : RegionDrag (e, i, p, v) {}
631
632         void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
633         void motion (GdkEvent *, bool);
634         void finished (GdkEvent *, bool);
635         void aborted ();
636 };
637
638 /** Scrub drag in audition mode */
639 class ScrubDrag : public Drag
640 {
641 public:
642         ScrubDrag (Editor *e, ArdourCanvas::Item *i) : Drag (e, i) {}
643
644         void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
645         void motion (GdkEvent *, bool);
646         void finished (GdkEvent *, bool);
647         void aborted ();
648 };
649
650 /** Drag in range select mode */
651 class SelectionDrag : public Drag
652 {
653 public:
654         enum Operation {
655                 CreateSelection,
656                 SelectionStartTrim,
657                 SelectionEndTrim,
658                 SelectionMove
659         };
660
661         SelectionDrag (Editor *, ArdourCanvas::Item *, Operation);
662
663         void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
664         void motion (GdkEvent *, bool);
665         void finished (GdkEvent *, bool);
666         void aborted ();
667
668 private:
669         Operation _operation;
670         bool _copy;
671         int _original_pointer_time_axis;
672         int _last_pointer_time_axis;
673         std::list<TimeAxisView*> _added_time_axes;
674 };
675
676 /** Range marker drag */
677 class RangeMarkerBarDrag : public Drag
678 {
679 public:
680         enum Operation {
681                 CreateRangeMarker,
682                 CreateTransportMarker,
683                 CreateCDMarker
684         };
685
686         RangeMarkerBarDrag (Editor *, ArdourCanvas::Item *, Operation);
687
688         void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
689         void motion (GdkEvent *, bool);
690         void finished (GdkEvent *, bool);
691         void aborted ();
692
693         bool allow_vertical_autoscroll () const {
694                 return false;
695         }
696
697         bool y_movement_matters () const {
698                 return false;
699         }
700
701 private:
702         void update_item (ARDOUR::Location *);
703
704         Operation _operation;
705         ArdourCanvas::SimpleRect* _drag_rect;
706         bool _copy;
707 };
708
709 /** Drag of rectangle to set zoom */
710 class MouseZoomDrag : public Drag
711 {
712 public:
713         MouseZoomDrag (Editor* e, ArdourCanvas::Item *i) : Drag (e, i) {}
714
715         void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
716         void motion (GdkEvent *, bool);
717         void finished (GdkEvent *, bool);
718         void aborted ();
719 };
720
721 /** Drag of a range of automation data, changing value but not position */
722 class AutomationRangeDrag : public Drag
723 {
724 public:
725         AutomationRangeDrag (Editor *, ArdourCanvas::Item *, std::list<ARDOUR::AudioRange> const &);
726
727         void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
728         void motion (GdkEvent *, bool);
729         void finished (GdkEvent *, bool);
730         void aborted ();
731
732         bool x_movement_matters () const {
733                 return false;
734         }
735
736 private:
737         std::list<ARDOUR::AudioRange> _ranges;
738         AutomationTimeAxisView* _atav;
739         boost::shared_ptr<AutomationLine> _line;
740         bool _nothing_to_drag;
741 };
742
743 #endif /* __gtk2_ardour_editor_drag_h_ */
744