back out "ignore_gui_changes" stuff for track resizing
[ardour.git] / gtk2_ardour / editor_mouse.cc
1
2 /*
3     Copyright (C) 2000-2001 Paul Davis 
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19 */
20
21 #include <cassert>
22 #include <cstdlib>
23 #include <stdint.h>
24 #include <cmath>
25 #include <set>
26 #include <string>
27 #include <algorithm>
28 #include <sys/time.h>
29
30 #include <pbd/error.h>
31 #include <gtkmm2ext/utils.h>
32 #include <pbd/memento_command.h>
33
34 #include "ardour_ui.h"
35 #include "editor.h"
36 #include "time_axis_view.h"
37 #include "audio_time_axis.h"
38 #include "audio_region_view.h"
39 #include "marker.h"
40 #include "streamview.h"
41 #include "region_gain_line.h"
42 #include "automation_time_axis.h"
43 #include "prompter.h"
44 #include "utils.h"
45 #include "selection.h"
46 #include "keyboard.h"
47 #include "editing.h"
48 #include "rgb_macros.h"
49
50 #include <ardour/types.h>
51 #include <ardour/profile.h>
52 #include <ardour/route.h>
53 #include <ardour/audio_track.h>
54 #include <ardour/audio_diskstream.h>
55 #include <ardour/playlist.h>
56 #include <ardour/audioplaylist.h>
57 #include <ardour/audioregion.h>
58 #include <ardour/dB.h>
59 #include <ardour/utils.h>
60 #include <ardour/region_factory.h>
61
62 #include <bitset>
63
64 #include "i18n.h"
65
66 using namespace std;
67 using namespace ARDOUR;
68 using namespace PBD;
69 using namespace sigc;
70 using namespace Gtk;
71 using namespace Editing;
72
73 bool
74 Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
75 {
76         int x, y;
77         double wx, wy;
78         Gdk::ModifierType mask;
79         Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
80         Glib::RefPtr<const Gdk::Window> pointer_window;
81
82         if (!canvas_window) {
83                 return false;
84         }
85         
86         pointer_window = canvas_window->get_pointer (x, y, mask);
87
88         if (pointer_window == track_canvas->get_bin_window()) {
89                 wx = x;
90                 wy = y;
91                 in_track_canvas = true;
92
93         } else {
94                 in_track_canvas = false;
95                         return false;
96         }
97
98         GdkEvent event;
99         event.type = GDK_BUTTON_RELEASE;
100         event.button.x = wx;
101         event.button.y = wy;
102         
103         where = event_frame (&event, 0, 0);
104         return true;
105 }
106
107 nframes64_t
108 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy) const
109 {
110         double cx, cy;
111
112         if (pcx == 0) {
113                 pcx = &cx;
114         }
115         if (pcy == 0) {
116                 pcy = &cy;
117         }
118
119         *pcx = 0;
120         *pcy = 0;
121
122         switch (event->type) {
123         case GDK_BUTTON_RELEASE:
124         case GDK_BUTTON_PRESS:
125         case GDK_2BUTTON_PRESS:
126         case GDK_3BUTTON_PRESS:
127
128                 *pcx = event->button.x;
129                 *pcy = event->button.y;
130                 _trackview_group->w2i(*pcx, *pcy);
131                 break;
132         case GDK_MOTION_NOTIFY:
133         
134                 *pcx = event->motion.x;
135                 *pcy = event->motion.y;
136                 _trackview_group->w2i(*pcx, *pcy);
137                 break;
138         case GDK_ENTER_NOTIFY:
139         case GDK_LEAVE_NOTIFY:
140                 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
141                 break;
142         case GDK_KEY_PRESS:
143         case GDK_KEY_RELEASE:
144                 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
145                 break;
146         default:
147                 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
148                 break;
149         }
150
151         /* note that pixel_to_frame() never returns less than zero, so even if the pixel
152            position is negative (as can be the case with motion events in particular),
153            the frame location is always positive.
154         */
155         
156         return pixel_to_frame (*pcx);
157 }
158
159 void
160 Editor::mouse_mode_toggled (MouseMode m)
161 {
162         if (ignore_mouse_mode_toggle) {
163                 return;
164         }
165
166         switch (m) {
167         case MouseRange:
168                 if (mouse_select_button.get_active()) {
169                         set_mouse_mode (m);
170                 }
171                 break;
172
173         case MouseObject:
174                 if (mouse_move_button.get_active()) {
175                         set_mouse_mode (m);
176                 }
177                 break;
178
179         case MouseGain:
180                 if (mouse_gain_button.get_active()) {
181                         set_mouse_mode (m);
182                 }
183                 break;
184
185         case MouseZoom:
186                 if (mouse_zoom_button.get_active()) {
187                         set_mouse_mode (m);
188                 }
189                 break;
190
191         case MouseTimeFX:
192                 if (mouse_timefx_button.get_active()) {
193                         set_mouse_mode (m);
194                 }
195                 break;
196
197         case MouseAudition:
198                 if (mouse_audition_button.get_active()) {
199                         set_mouse_mode (m);
200                 }
201                 break;
202
203         default:
204                 break;
205         }
206 }       
207
208 Gdk::Cursor*
209 Editor::which_grabber_cursor ()
210 {
211         switch (_edit_point) {
212         case EditAtMouse:
213                 return grabber_edit_point_cursor;
214                 break;
215         default:
216                 break;
217         }
218         return grabber_cursor;
219 }
220
221 void
222 Editor::set_canvas_cursor ()
223 {
224         switch (mouse_mode) {
225         case MouseRange:
226                 current_canvas_cursor = selector_cursor;
227                 break;
228
229         case MouseObject:
230                 current_canvas_cursor = which_grabber_cursor();
231                 break;
232
233         case MouseGain:
234                 current_canvas_cursor = cross_hair_cursor;
235                 break;
236
237         case MouseZoom:
238                 current_canvas_cursor = zoom_cursor;
239                 break;
240
241         case MouseTimeFX:
242                 current_canvas_cursor = time_fx_cursor; // just use playhead
243                 break;
244
245         case MouseAudition:
246                 current_canvas_cursor = speaker_cursor;
247                 break;
248         }
249
250         if (is_drawable()) {
251                 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
252         }
253 }
254
255 void
256 Editor::set_mouse_mode (MouseMode m, bool force)
257 {
258         if (drag_info.item) {
259                 return;
260         }
261
262         if (!force && m == mouse_mode) {
263                 return;
264         }
265         
266         mouse_mode = m;
267
268         instant_save ();
269
270         if (mouse_mode != MouseRange) {
271
272                 /* in all modes except range, hide the range selection,
273                    show the object (region) selection.
274                 */
275
276                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
277                         (*i)->set_should_show_selection (true);
278                 }
279                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
280                         (*i)->hide_selection ();
281                 }
282
283         } else {
284
285                 /* 
286                    in range mode,show the range selection.
287                 */
288
289                 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
290                         if ((*i)->get_selected()) {
291                                 (*i)->show_selection (selection->time);
292                         }
293                 }
294         }
295
296         /* XXX the hack of unsetting all other buttons should go 
297            away once GTK2 allows us to use regular radio buttons drawn like
298            normal buttons, rather than my silly GroupedButton hack.
299         */
300         
301         ignore_mouse_mode_toggle = true;
302
303         switch (mouse_mode) {
304         case MouseRange:
305                 mouse_select_button.set_active (true);
306                 break;
307
308         case MouseObject:
309                 mouse_move_button.set_active (true);
310                 break;
311
312         case MouseGain:
313                 mouse_gain_button.set_active (true);
314                 break;
315
316         case MouseZoom:
317                 mouse_zoom_button.set_active (true);
318                 break;
319
320         case MouseTimeFX:
321                 mouse_timefx_button.set_active (true);
322                 break;
323
324         case MouseAudition:
325                 mouse_audition_button.set_active (true);
326                 break;
327         }
328
329         ignore_mouse_mode_toggle = false;
330         
331         set_canvas_cursor ();
332 }
333
334 void
335 Editor::step_mouse_mode (bool next)
336 {
337         switch (current_mouse_mode()) {
338         case MouseObject:
339                 if (next) set_mouse_mode (MouseRange);
340                 else set_mouse_mode (MouseTimeFX);
341                 break;
342
343         case MouseRange:
344                 if (next) set_mouse_mode (MouseZoom);
345                 else set_mouse_mode (MouseObject);
346                 break;
347
348         case MouseZoom:
349                 if (next) set_mouse_mode (MouseGain);
350                 else set_mouse_mode (MouseRange);
351                 break;
352         
353         case MouseGain:
354                 if (next) set_mouse_mode (MouseTimeFX);
355                 else set_mouse_mode (MouseZoom);
356                 break;
357         
358         case MouseTimeFX:
359                 if (next) set_mouse_mode (MouseAudition);
360                 else set_mouse_mode (MouseGain);
361                 break;
362
363         case MouseAudition:
364                 if (next) set_mouse_mode (MouseObject);
365                 else set_mouse_mode (MouseTimeFX);
366                 break;
367         }
368 }
369
370 void
371 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
372 {
373         /* in object/audition/timefx mode, any button press sets
374            the selection if the object can be selected. this is a
375            bit of hack, because we want to avoid this if the
376            mouse operation is a region alignment.
377
378            note: not dbl-click or triple-click
379         */
380
381         if (((mouse_mode != MouseObject) &&
382              (mouse_mode != MouseAudition || item_type != RegionItem) &&
383              (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
384              (mouse_mode != MouseRange)) ||
385
386             ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
387                 
388                 return;
389         }
390
391         if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
392
393                 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
394                         
395                         /* almost no selection action on modified button-2 or button-3 events */
396                 
397                         if (item_type != RegionItem && event->button.button != 2) {
398                                 return;
399                         }
400                 }
401         }
402             
403         Selection::Operation op = Keyboard::selection_type (event->button.state);
404         bool press = (event->type == GDK_BUTTON_PRESS);
405
406         // begin_reversible_command (_("select on click"));
407         
408         switch (item_type) {
409         case RegionItem:
410                 if (mouse_mode != MouseRange) {
411                         set_selected_regionview_from_click (press, op, true);
412                 } else if (event->type == GDK_BUTTON_PRESS) {
413                         set_selected_track_as_side_effect ();
414                 }
415                 break;
416                 
417         case RegionViewNameHighlight:
418         case RegionViewName:
419                 if (mouse_mode != MouseRange) {
420                         set_selected_regionview_from_click (press, op, true);
421                 } else if (event->type == GDK_BUTTON_PRESS) {
422                         set_selected_track_as_side_effect ();
423                 }
424                 break;
425
426         case FadeInHandleItem:
427         case FadeInItem:
428         case FadeOutHandleItem:
429         case FadeOutItem:
430                 if (mouse_mode != MouseRange) {
431                         set_selected_regionview_from_click (press, op, true);
432                 } else if (event->type == GDK_BUTTON_PRESS) {
433                         set_selected_track_as_side_effect ();
434                 }
435                 break;
436                 
437         case GainAutomationControlPointItem:
438         case PanAutomationControlPointItem:
439         case RedirectAutomationControlPointItem:
440                 set_selected_track_as_side_effect ();
441                 if (mouse_mode != MouseRange) {
442                         set_selected_control_point_from_click (op, false);
443                 }
444                 break;
445                 
446         case StreamItem:
447                 /* for context click or range selection, select track */
448                 if (event->button.button == 3) {
449                         set_selected_track_as_side_effect ();
450                 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
451                         set_selected_track_as_side_effect ();
452                 }
453                 break;
454                     
455         case AutomationTrackItem:
456                 set_selected_track_as_side_effect (true);
457                 break;
458                 
459         default:
460                 break;
461         }
462 }
463
464 const static double ZERO_GAIN_FRACTION = gain_to_slider_position(dB_to_coefficient(0.0));
465
466 bool
467 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
468 {
469         Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
470
471         if (canvas_window) {
472                 Glib::RefPtr<const Gdk::Window> pointer_window;
473                 int x, y;
474                 double wx, wy;
475                 Gdk::ModifierType mask;
476
477                 pointer_window = canvas_window->get_pointer (x, y, mask);
478                 
479                 if (pointer_window == track_canvas->get_bin_window()) {
480                         track_canvas->window_to_world (x, y, wx, wy);
481                         allow_vertical_scroll = true;
482                 } else {
483                         allow_vertical_scroll = false;
484                 }
485         }
486
487         track_canvas->grab_focus();
488
489         if (session && session->actively_recording()) {
490                 return true;
491         }
492
493         button_selection (item, event, item_type);
494
495         if (drag_info.item == 0 &&
496             (Keyboard::is_delete_event (&event->button) ||
497              Keyboard::is_context_menu_event (&event->button) ||
498              Keyboard::is_edit_event (&event->button))) {
499                 
500                 /* handled by button release */
501                 return true;
502         }
503
504         switch (event->button.button) {
505         case 1:
506
507                 if (event->type == GDK_BUTTON_PRESS) {
508
509                         if (drag_info.item) {
510                                 drag_info.item->ungrab (event->button.time);
511                         }
512
513                         /* single mouse clicks on any of these item types operate
514                            independent of mouse mode, mostly because they are
515                            not on the main track canvas or because we want
516                            them to be modeless.
517                         */
518                         
519                         switch (item_type) {
520                         case PlayheadCursorItem:
521                                 start_cursor_grab (item, event);
522                                 return true;
523
524                         case MarkerItem:
525                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
526                                         hide_marker (item, event);
527                                 } else {
528                                         start_marker_grab (item, event);
529                                 }
530                                 return true;
531
532                         case TempoMarkerItem:
533                                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
534                                         start_tempo_marker_copy_grab (item, event);
535                                 } else {
536                                         start_tempo_marker_grab (item, event);
537                                 }
538                                 return true;
539
540                         case MeterMarkerItem:
541                                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
542                                         start_meter_marker_copy_grab (item, event);
543                                 } else {
544                                         start_meter_marker_grab (item, event);
545                                 }
546                                 return true;
547
548                         case MarkerBarItem:
549                         case TempoBarItem:
550                         case MeterBarItem:
551                                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
552                                         start_cursor_grab_no_stop(&playhead_cursor->canvas_item, event);
553                                 }
554                                 return true;
555                                 break;
556
557                                 
558                         case RangeMarkerBarItem:
559                                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {                
560                                         start_cursor_grab_no_stop(&playhead_cursor->canvas_item, event);
561                                 } else {
562                                         start_range_markerbar_op (item, event, CreateRangeMarker); 
563                                 }       
564                                 return true;
565                                 break;
566
567                         case CdMarkerBarItem:
568                                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
569                                         start_cursor_grab_no_stop(&playhead_cursor->canvas_item, event);
570                                 } else {
571                                         start_range_markerbar_op (item, event, CreateCDMarker); 
572                                 }
573                                 return true;
574                                 break;
575
576                         case TransportMarkerBarItem:
577                                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
578                                         start_cursor_grab_no_stop(&playhead_cursor->canvas_item, event);
579                                 } else {
580                                         start_range_markerbar_op (item, event, CreateTransportMarker);
581                                 }
582                                 return true;
583                                 break;
584                                 
585                         default:
586                                 break;
587                         }
588                 }
589
590                 switch (mouse_mode) {
591                 case MouseRange:
592                         switch (item_type) {
593                         case StartSelectionTrimItem:
594                                 start_selection_op (item, event, SelectionStartTrim);
595                                 break;
596                                 
597                         case EndSelectionTrimItem:
598                                 start_selection_op (item, event, SelectionEndTrim);
599                                 break;
600
601                         case SelectionItem:
602                                 if (Keyboard::modifier_state_contains 
603                                     (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) {
604                                         // contains and not equals because I can't use alt as a modifier alone.
605                                         start_selection_grab (item, event);
606                                 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
607                                         /* grab selection for moving */
608                                         start_selection_op (item, event, SelectionMove);
609                                 } else {
610                                         /* this was debated, but decided the more common action was to
611                                            make a new selection */
612                                         start_selection_op (item, event, CreateSelection);
613                                 }
614                                 break;
615
616                         default:
617                                 start_selection_op (item, event, CreateSelection);
618                         }
619                         return true;
620                         break;
621                         
622                 case MouseObject:
623                         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
624                             event->type == GDK_BUTTON_PRESS) {
625                                 
626                                 start_rubberband_select (item, event);
627
628                         } else if (event->type == GDK_BUTTON_PRESS) {
629
630                                 switch (item_type) {
631                                 case FadeInHandleItem:
632                                         start_fade_in_grab (item, event);
633                                         return true;
634                                         
635                                 case FadeOutHandleItem:
636                                         start_fade_out_grab (item, event);
637                                         return true;
638
639                                 case RegionItem:
640                                         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
641                                                 start_region_copy_grab (item, event);
642                                         } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
643                                                 start_region_brush_grab (item, event);
644                                         } else {
645                                                 start_region_grab (item, event);
646                                         }
647                                         break;
648                                         
649                                 case RegionViewNameHighlight:
650                                         start_trim (item, event);
651                                         return true;
652                                         break;
653                                         
654                                 case RegionViewName:
655                                         /* rename happens on edit clicks */
656                                         start_trim (clicked_regionview->get_name_highlight(), event);
657                                         return true;
658                                         break;
659
660                                 case GainAutomationControlPointItem:
661                                 case PanAutomationControlPointItem:
662                                 case RedirectAutomationControlPointItem:
663                                         start_control_point_grab (item, event);
664                                         return true;
665                                         break;
666                                         
667                                 case GainAutomationLineItem:
668                                 case PanAutomationLineItem:
669                                 case RedirectAutomationLineItem:
670                                         start_line_grab_from_line (item, event);
671                                         return true;
672                                         break;
673
674                                 case StreamItem:
675                                 case AutomationTrackItem:
676                                         start_rubberband_select (item, event);
677                                         break;
678                                         
679                                 /* <CMT Additions> */
680                                 case ImageFrameHandleStartItem:
681                                         imageframe_start_handle_op(item, event) ;
682                                         return(true) ;
683                                         break ;
684                                 case ImageFrameHandleEndItem:
685                                         imageframe_end_handle_op(item, event) ;
686                                         return(true) ;
687                                         break ;
688                                 case MarkerViewHandleStartItem:
689                                         markerview_item_start_handle_op(item, event) ;
690                                         return(true) ;
691                                         break ;
692                                 case MarkerViewHandleEndItem:
693                                         markerview_item_end_handle_op(item, event) ;
694                                         return(true) ;
695                                         break ;
696                                 /* </CMT Additions> */
697                                 
698                                 /* <CMT Additions> */
699                                 case MarkerViewItem:
700                                         start_markerview_grab(item, event) ;
701                                         break ;
702                                 case ImageFrameItem:
703                                         start_imageframe_grab(item, event) ;
704                                         break ;
705                                 /* </CMT Additions> */
706
707                                 case MarkerBarItem:
708                                         
709                                         break;
710
711                                 default:
712                                         break;
713                                 }
714                         }
715                         return true;
716                         break;
717                         
718                 case MouseGain:
719                         switch (item_type) {
720                         case RegionItem:
721                                 /* start a grab so that if we finish after moving
722                                    we can tell what happened.
723                                 */
724                                 drag_info.item = item;
725                                 drag_info.motion_callback = &Editor::region_gain_motion_callback;
726                                 drag_info.finished_callback = 0;
727                                 start_grab (event, current_canvas_cursor);
728                                 break;
729
730                         case GainControlPointItem:
731                                 start_control_point_grab (item, event);
732                                 return true;
733                                 
734                         case GainLineItem:
735                                 start_line_grab_from_line (item, event);
736                                 return true;
737
738                         case GainAutomationControlPointItem:
739                         case PanAutomationControlPointItem:
740                         case RedirectAutomationControlPointItem:
741                                 start_control_point_grab (item, event);
742                                 return true;
743                                 break;
744
745                         default:
746                                 break;
747                         }
748                         return true;
749                         break;
750
751                         switch (item_type) {
752                         case GainAutomationControlPointItem:
753                         case PanAutomationControlPointItem:
754                         case RedirectAutomationControlPointItem:
755                                 start_control_point_grab (item, event);
756                                 break;
757
758                         case GainAutomationLineItem:
759                         case PanAutomationLineItem:
760                         case RedirectAutomationLineItem:
761                                 start_line_grab_from_line (item, event);
762                                 break;
763
764                         case RegionItem:
765                                 // XXX need automation mode to identify which
766                                 // line to use
767                                 // start_line_grab_from_regionview (item, event);
768                                 break;
769
770                         default:
771                                 break;
772                         }
773                         return true;
774                         break;
775
776                 case MouseZoom:
777                         if (event->type == GDK_BUTTON_PRESS) {
778                                 start_mouse_zoom (item, event);
779                         }
780
781                         return true;
782                         break;
783
784                 case MouseTimeFX:
785                         if (item_type == RegionItem) {
786                                 start_time_fx (item, event);
787                         }
788                         break;
789
790                 case MouseAudition:
791                         _scrubbing = true;
792                         scrub_reversals = 0;
793                         scrub_reverse_distance = 0;
794                         last_scrub_x = event->button.x;
795                         scrubbing_direction = 0;
796                         track_canvas->get_window()->set_cursor (*transparent_cursor);
797                         /* rest handled in motion & release */
798                         break;
799
800                 default:
801                         break;
802                 }
803                 break;
804
805         case 2:
806                 switch (mouse_mode) {
807                 case MouseObject:
808                         if (event->type == GDK_BUTTON_PRESS) {
809                                 switch (item_type) {
810                                 case RegionItem:
811                                         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
812                                                 start_region_copy_grab (item, event);
813                                         } else {
814                                                 start_region_grab (item, event);
815                                         }
816                                         return true;
817                                         break;
818
819                                 case GainAutomationControlPointItem:
820                                 case PanAutomationControlPointItem:
821                                 case RedirectAutomationControlPointItem:
822                                         start_control_point_grab (item, event);
823                                         return true;
824                                         break;
825                                         
826                                 default:
827                                         break;
828                                 }
829                         }
830                         
831                         
832                         switch (item_type) {
833                         case RegionViewNameHighlight:
834                                 start_trim (item, event);
835                                 return true;
836                                 break;
837                                 
838                         case RegionViewName:
839                                 start_trim (clicked_regionview->get_name_highlight(), event);
840                                 return true;
841                                 break;
842                                 
843                         default:
844                                 break;
845                         }
846                         
847                         break;
848
849                 case MouseRange:
850                         if (event->type == GDK_BUTTON_PRESS) {
851                                 /* relax till release */
852                         }
853                         return true;
854                         break;
855                                         
856                                 
857                 case MouseZoom:
858                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
859                                 temporal_zoom_session();
860                         } else {
861                                 temporal_zoom_to_frame (true, event_frame(event));
862                         }
863                         return true;
864                         break;
865
866                 default:
867                         break;
868                 }
869
870                 break;
871
872         case 3:
873                 break;
874
875         default:
876                 break;
877
878         }
879
880         return false;
881 }
882
883 bool
884 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
885 {
886         nframes64_t where = event_frame (event, 0, 0);
887         AutomationTimeAxisView* atv = 0;
888         
889         /* no action if we're recording */
890                                                 
891         if (session && session->actively_recording()) {
892                 return true;
893         }
894
895         /* first, see if we're finishing a drag ... */
896
897         if (drag_info.item) {
898                 if (end_grab (item, event)) {
899                         /* grab dragged, so do nothing else */
900                         return true;
901                 }
902         }
903         
904         button_selection (item, event, item_type);
905
906         /* edit events get handled here */
907
908         if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
909                 switch (item_type) {
910                 case RegionItem:
911                         edit_region ();
912                         break;
913
914                 case TempoMarkerItem:
915                         edit_tempo_marker (item);
916                         break;
917                         
918                 case MeterMarkerItem:
919                         edit_meter_marker (item);
920                         break;
921                         
922                 case RegionViewName:
923                         if (clicked_regionview->name_active()) {
924                                 return mouse_rename_region (item, event);
925                         }
926                         break;
927
928                 default:
929                         break;
930                 }
931                 return true;
932         }
933
934         /* context menu events get handled here */
935
936         if (Keyboard::is_context_menu_event (&event->button)) {
937
938                 if (drag_info.item == 0) {
939
940                         /* no matter which button pops up the context menu, tell the menu
941                            widget to use button 1 to drive menu selection.
942                         */
943
944                         switch (item_type) {
945                         case FadeInItem:
946                         case FadeInHandleItem:
947                         case FadeOutItem:
948                         case FadeOutHandleItem:
949                                 popup_fade_context_menu (1, event->button.time, item, item_type);
950                                 break;
951
952                         case StreamItem:
953                                 popup_track_context_menu (1, event->button.time, item_type, false, where);
954                                 break;
955                                 
956                         case RegionItem:
957                         case RegionViewNameHighlight:
958                         case RegionViewName:
959                                 popup_track_context_menu (1, event->button.time, item_type, false, where);
960                                 break;
961                                 
962                         case SelectionItem:
963                                 popup_track_context_menu (1, event->button.time, item_type, true, where);
964                                 break;
965
966                         case AutomationTrackItem:
967                                 popup_track_context_menu (1, event->button.time, item_type, false, where);
968                                 break;
969
970                         case MarkerBarItem: 
971                         case RangeMarkerBarItem: 
972                         case TransportMarkerBarItem:
973                         case CdMarkerBarItem:
974                         case TempoBarItem:
975                         case MeterBarItem:
976                                 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
977                                 break;
978
979                         case MarkerItem:
980                                 marker_context_menu (&event->button, item);
981                                 break;
982
983                         case TempoMarkerItem:
984                                 tm_marker_context_menu (&event->button, item);
985                                 break;
986                                 
987                         case MeterMarkerItem:
988                                 tm_marker_context_menu (&event->button, item);
989                                 break;
990
991                         case CrossfadeViewItem:
992                                 popup_track_context_menu (1, event->button.time, item_type, false, where);
993                                 break;
994
995                         /* <CMT Additions> */
996                         case ImageFrameItem:
997                                 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
998                                 break ;
999                         case ImageFrameTimeAxisItem:
1000                                 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1001                                 break ;
1002                         case MarkerViewItem:
1003                                 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1004                                 break ;
1005                         case MarkerTimeAxisItem:
1006                                 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1007                                 break ;
1008                         /* <CMT Additions> */
1009
1010                                 
1011                         default:
1012                                 break;
1013                         }
1014
1015                         return true;
1016                 }
1017         }
1018
1019         /* delete events get handled here */
1020
1021         if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
1022
1023                 switch (item_type) {
1024                 case TempoMarkerItem:
1025                         remove_tempo_marker (item);
1026                         break;
1027                         
1028                 case MeterMarkerItem:
1029                         remove_meter_marker (item);
1030                         break;
1031
1032                 case MarkerItem:
1033                         remove_marker (*item, event);
1034                         break;
1035
1036                 case RegionItem:
1037                         if (mouse_mode == MouseObject) {
1038                                 remove_clicked_region ();
1039                         }
1040                         break;
1041                         
1042                 case GainControlPointItem:
1043                         if (mouse_mode == MouseGain) {
1044                                 remove_gain_control_point (item, event);
1045                         }
1046                         break;
1047
1048                 case GainAutomationControlPointItem:
1049                 case PanAutomationControlPointItem:
1050                 case RedirectAutomationControlPointItem:
1051                         remove_control_point (item, event);
1052                         break;
1053
1054                 default:
1055                         break;
1056                 }
1057                 return true;
1058         }
1059
1060         switch (event->button.button) {
1061         case 1:
1062
1063                 switch (item_type) {
1064                 /* see comments in button_press_handler */
1065                 case PlayheadCursorItem:
1066                 case MarkerItem:
1067                 case GainLineItem:
1068                 case GainAutomationLineItem:
1069                 case PanAutomationLineItem:
1070                 case RedirectAutomationLineItem:
1071                 case StartSelectionTrimItem:
1072                 case EndSelectionTrimItem:
1073                         return true;
1074
1075                 case MarkerBarItem:
1076                         if (!_dragging_playhead) {
1077                                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1078                                         snap_to (where, 0, true);
1079                                 }
1080                                 mouse_add_new_marker (where);
1081                         }
1082                         return true;
1083
1084                 case CdMarkerBarItem:
1085                         if (!_dragging_playhead) {
1086                                 // if we get here then a dragged range wasn't done
1087                                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1088                                         snap_to (where, 0, true);
1089                                 }
1090                                 mouse_add_new_marker (where, true);
1091                         }
1092                         return true;
1093
1094                 case TempoBarItem:
1095                         if (!_dragging_playhead) {
1096                                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1097                                         snap_to (where);
1098                                 }
1099                                 mouse_add_new_tempo_event (where);
1100                         }
1101                         return true;
1102                         
1103                 case MeterBarItem:
1104                         if (!_dragging_playhead) {
1105                                 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1106                         } 
1107                         return true;
1108                         break;
1109
1110                 default:
1111                         break;
1112                 }
1113                 
1114                 switch (mouse_mode) {
1115                 case MouseObject:
1116                         switch (item_type) {
1117                         case AutomationTrackItem:
1118                                 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_trackview);
1119                                 if (atv) {
1120                                         atv->add_automation_event (item, event, where, event->button.y);
1121                                 }
1122                                 return true;
1123                                 
1124                                 break;
1125                                 
1126                         default:
1127                                 break;
1128                         }
1129                         break;
1130
1131                 case MouseGain:
1132                         // Gain only makes sense for audio regions
1133
1134                         if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1135                                 break;
1136                         }
1137
1138                         switch (item_type) {
1139                         case RegionItem:
1140                                 /* check that we didn't drag before releasing, since
1141                                    its really annoying to create new control
1142                                    points when doing this.
1143                                 */
1144                                 if (drag_info.first_move) { 
1145                                         dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1146                                 }
1147                                 return true;
1148                                 break;
1149                                 
1150                         case AutomationTrackItem:
1151                                 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1152                                         add_automation_event (item, event, where, event->button.y);
1153                                 return true;
1154                                 break;
1155                         default:
1156                                 break;
1157                         }
1158                         break;
1159                         
1160                 case MouseAudition:
1161                         _scrubbing = false;
1162                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1163                         if (scrubbing_direction == 0) {
1164                                 /* no drag, just a click */
1165                                 switch (item_type) {
1166                                 case RegionItem:
1167                                         play_selected_region ();
1168                                         break;
1169                                 default:
1170                                         break;
1171                                 }
1172                         } else {
1173                                 /* make sure we stop */
1174                                 session->request_transport_speed (0.0);
1175                         }
1176                         break;
1177
1178                 default:
1179                         break;
1180
1181                 }
1182
1183                 return true;
1184                 break;
1185
1186
1187         case 2:
1188                 switch (mouse_mode) {
1189                         
1190                 case MouseObject:
1191                         switch (item_type) {
1192                         case RegionItem:
1193                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1194                                         raise_region ();
1195                                 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1196                                         lower_region ();
1197                                 } else {
1198                                         // Button2 click is unused
1199                                 }
1200                                 return true;
1201                                 
1202                                 break;
1203                                 
1204                         default:
1205                                 break;
1206                         }
1207                         break;
1208                         
1209                 case MouseRange:
1210                         
1211                         // x_style_paste (where, 1.0);
1212                         return true;
1213                         break;
1214                         
1215                 default:
1216                         break;
1217                 }
1218
1219                 break;
1220         
1221         case 3:
1222                 break;
1223                 
1224         default:
1225                 break;
1226         }
1227         return false;
1228 }
1229
1230 bool
1231 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1232 {
1233         ControlPoint* cp;
1234         Marker * marker;
1235         double fraction;
1236         
1237         if (last_item_entered != item) {
1238                 last_item_entered = item;
1239                 last_item_entered_n = 0;
1240         }
1241
1242         switch (item_type) {
1243         case GainControlPointItem:
1244                 if (mouse_mode == MouseGain) {
1245                         cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1246                         cp->set_visible (true);
1247
1248                         double at_x, at_y;
1249                         at_x = cp->get_x();
1250                         at_y = cp->get_y ();
1251                         cp->item->i2w (at_x, at_y);
1252                         at_x += 10.0;
1253                         at_y += 10.0;
1254
1255                         fraction = 1.0 - (cp->get_y() / cp->line.height());
1256
1257                         if (is_drawable() && !_scrubbing) {
1258                                 track_canvas->get_window()->set_cursor (*fader_cursor);
1259                         }
1260
1261                         last_item_entered_n++;
1262                         set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1263                         if (last_item_entered_n < 10) {
1264                                 show_verbose_canvas_cursor ();
1265                         }
1266                 }
1267                 break;
1268
1269         case GainAutomationControlPointItem:
1270         case PanAutomationControlPointItem:
1271         case RedirectAutomationControlPointItem:
1272                 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1273                         cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1274                         cp->set_visible (true);
1275
1276                         double at_x, at_y;
1277                         at_x = cp->get_x();
1278                         at_y = cp->get_y ();
1279                         cp->item->i2w (at_x, at_y);
1280                         at_x += 10.0;
1281                         at_y += 10.0;
1282
1283                         fraction = 1.0 - (cp->get_y() / cp->line.height());
1284
1285                         set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1286                         show_verbose_canvas_cursor ();
1287
1288                         if (is_drawable()) {
1289                                 track_canvas->get_window()->set_cursor (*fader_cursor);
1290                         }
1291                 }
1292                 break;
1293                 
1294         case GainLineItem:
1295                 if (mouse_mode == MouseGain) {
1296                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1297                         if (line)
1298                                 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1299                         if (is_drawable()) {
1300                                 track_canvas->get_window()->set_cursor (*fader_cursor);
1301                         }
1302                 }
1303                 break;
1304                         
1305         case GainAutomationLineItem:
1306         case RedirectAutomationLineItem:
1307         case PanAutomationLineItem:
1308                 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1309                         {
1310                                 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1311                                 if (line)
1312                                         line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1313                         }
1314                         if (is_drawable()) {
1315                                 track_canvas->get_window()->set_cursor (*fader_cursor);
1316                         }
1317                 }
1318                 break;
1319                 
1320         case RegionViewNameHighlight:
1321                 if (is_drawable() && mouse_mode == MouseObject) {
1322                         track_canvas->get_window()->set_cursor (*trimmer_cursor);
1323                 }
1324                 break;
1325
1326         case StartSelectionTrimItem:
1327         case EndSelectionTrimItem:
1328         /* <CMT Additions> */
1329         case ImageFrameHandleStartItem:
1330         case ImageFrameHandleEndItem:
1331         case MarkerViewHandleStartItem:
1332         case MarkerViewHandleEndItem:
1333         /* </CMT Additions> */
1334
1335                 if (is_drawable()) {
1336                         track_canvas->get_window()->set_cursor (*trimmer_cursor);
1337                 }
1338                 break;
1339
1340         case PlayheadCursorItem:
1341                 if (is_drawable()) {
1342                         switch (_edit_point) {
1343                         case EditAtMouse:
1344                                 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1345                                 break;
1346                         default:
1347                                 track_canvas->get_window()->set_cursor (*grabber_cursor);
1348                                 break;
1349                         }
1350                 }
1351                 break;
1352
1353         case RegionViewName:
1354                 
1355                 /* when the name is not an active item, the entire name highlight is for trimming */
1356
1357                 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1358                         if (mouse_mode == MouseObject && is_drawable()) {
1359                                 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1360                         }
1361                 } 
1362                 break;
1363
1364
1365         case AutomationTrackItem:
1366                 if (is_drawable()) {
1367                         Gdk::Cursor *cursor;
1368                         switch (mouse_mode) {
1369                         case MouseRange:
1370                                 cursor = selector_cursor;
1371                                 break;
1372                         case MouseZoom:
1373                                 cursor = zoom_cursor;
1374                                 break;
1375                         default:
1376                                 cursor = cross_hair_cursor;
1377                                 break;
1378                         }
1379
1380                         track_canvas->get_window()->set_cursor (*cursor);
1381
1382                         AutomationTimeAxisView* atv;
1383                         if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1384                                 clear_entered_track = false;
1385                                 set_entered_track (atv);
1386                         }
1387                 }
1388                 break;
1389
1390         case MarkerBarItem:
1391         case RangeMarkerBarItem:
1392         case TransportMarkerBarItem:
1393         case CdMarkerBarItem:
1394         case MeterBarItem:
1395         case TempoBarItem:
1396                 if (is_drawable()) {
1397                         track_canvas->get_window()->set_cursor (*timebar_cursor);
1398                 }
1399                 break;
1400
1401         case MarkerItem:
1402                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1403                         break;
1404                 }
1405                 entered_marker = marker;
1406                 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1407                 // fall through
1408         case MeterMarkerItem:
1409         case TempoMarkerItem:
1410                 if (is_drawable()) {
1411                         track_canvas->get_window()->set_cursor (*timebar_cursor);
1412                 }
1413                 break;
1414         case FadeInHandleItem:
1415         case FadeOutHandleItem:
1416                 if (mouse_mode == MouseObject) {
1417                         ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1418                         if (rect) {
1419                                 rect->property_fill_color_rgba() = 0;
1420                                 rect->property_outline_pixels() = 1;
1421                         }
1422                 }
1423                 break;
1424
1425         default:
1426                 break;
1427         }
1428
1429         /* second pass to handle entered track status in a comprehensible way.
1430          */
1431
1432         switch (item_type) {
1433         case GainLineItem:
1434         case GainAutomationLineItem:
1435         case RedirectAutomationLineItem:
1436         case PanAutomationLineItem:
1437         case GainControlPointItem:
1438         case GainAutomationControlPointItem:
1439         case PanAutomationControlPointItem:
1440         case RedirectAutomationControlPointItem:
1441                 /* these do not affect the current entered track state */
1442                 clear_entered_track = false;
1443                 break;
1444
1445         case AutomationTrackItem:
1446                 /* handled above already */
1447                 break;
1448
1449         default:
1450                 set_entered_track (0);
1451                 break;
1452         }
1453
1454         return false;
1455 }
1456
1457 bool
1458 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1459 {
1460         AutomationLine* al;
1461         ControlPoint* cp;
1462         Marker *marker;
1463         Location *loc;
1464         RegionView* rv;
1465         bool is_start;
1466
1467         switch (item_type) {
1468         case GainControlPointItem:
1469         case GainAutomationControlPointItem:
1470         case PanAutomationControlPointItem:
1471         case RedirectAutomationControlPointItem:
1472                 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1473                 if (cp->line.npoints() > 1) {
1474                         if (!cp->selected) {
1475                                 cp->set_visible (false);
1476                         }
1477                 }
1478                 
1479                 if (is_drawable()) {
1480                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1481                 }
1482
1483                 hide_verbose_canvas_cursor ();
1484                 break;
1485                 
1486         case RegionViewNameHighlight:
1487         case StartSelectionTrimItem:
1488         case EndSelectionTrimItem:
1489         case PlayheadCursorItem:
1490         /* <CMT Additions> */
1491         case ImageFrameHandleStartItem:
1492         case ImageFrameHandleEndItem:
1493         case MarkerViewHandleStartItem:
1494         case MarkerViewHandleEndItem:
1495         /* </CMT Additions> */
1496                 if (is_drawable()) {
1497                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1498                 }
1499                 break;
1500
1501         case GainLineItem:
1502         case GainAutomationLineItem:
1503         case RedirectAutomationLineItem:
1504         case PanAutomationLineItem:
1505                 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1506                 {
1507                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1508                         if (line)
1509                                 line->property_fill_color_rgba() = al->get_line_color();
1510                 }
1511                 if (is_drawable()) {
1512                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1513                 }
1514                 break;
1515
1516         case RegionViewName:
1517                 /* see enter_handler() for notes */
1518                 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1519                         if (is_drawable() && mouse_mode == MouseObject) {
1520                                 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1521                         }
1522                 }
1523                 break;
1524
1525         case RangeMarkerBarItem:
1526         case TransportMarkerBarItem:
1527         case CdMarkerBarItem:
1528         case MeterBarItem:
1529         case TempoBarItem:
1530         case MarkerBarItem:
1531                 if (is_drawable()) {
1532                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1533                 }
1534                 break;
1535                 
1536         case MarkerItem:
1537                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1538                         break;
1539                 }
1540                 entered_marker = 0;
1541                 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1542                         location_flags_changed (loc, this);
1543                 }
1544                 // fall through
1545         case MeterMarkerItem:
1546         case TempoMarkerItem:
1547                 
1548                 if (is_drawable()) {
1549                         track_canvas->get_window()->set_cursor (*timebar_cursor);
1550                 }
1551
1552                 break;
1553
1554         case FadeInHandleItem:
1555         case FadeOutHandleItem:
1556                 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1557                 {
1558                         ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1559                         if (rect) {
1560                                 rect->property_fill_color_rgba() = rv->get_fill_color();
1561                                 rect->property_outline_pixels() = 0;
1562                         }
1563                 }
1564                 break;
1565
1566         case AutomationTrackItem:
1567                 if (is_drawable()) {
1568                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1569                         clear_entered_track = true;
1570                         Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1571                 }
1572                 break;
1573                 
1574         default:
1575                 break;
1576         }
1577
1578         return false;
1579 }
1580
1581 gint
1582 Editor::left_automation_track ()
1583 {
1584         if (clear_entered_track) {
1585                 set_entered_track (0);
1586                 clear_entered_track = false;
1587         }
1588         return false;
1589 }
1590
1591 void
1592 Editor::scrub ()
1593 {
1594         double delta;
1595         
1596         if (scrubbing_direction == 0) {
1597                 /* first move */
1598                 session->request_locate (drag_info.current_pointer_frame, false);
1599                 session->request_transport_speed (0.1);
1600                 scrubbing_direction = 1;
1601                 
1602         } else {
1603                 
1604                 if (last_scrub_x > drag_info.current_pointer_x) {
1605                         
1606                         /* pointer moved to the left */
1607                         
1608                         if (scrubbing_direction > 0) {
1609                                 
1610                                 /* we reversed direction to go backwards */
1611                                 
1612                                 scrub_reversals++;
1613                                 scrub_reverse_distance += (int) (last_scrub_x - drag_info.current_pointer_x);
1614                                 
1615                         } else {
1616                                 
1617                                 /* still moving to the left (backwards) */
1618                                 
1619                                 scrub_reversals = 0;
1620                                 scrub_reverse_distance = 0;
1621                                 
1622                                 delta = 0.01 * (last_scrub_x - drag_info.current_pointer_x);
1623                                 session->request_transport_speed (session->transport_speed() - delta);
1624                         }
1625                         
1626                 } else {
1627                         /* pointer moved to the right */
1628                         
1629                         if (scrubbing_direction < 0) {
1630                                 /* we reversed direction to go forward */
1631                                 
1632                                 scrub_reversals++;
1633                                 scrub_reverse_distance += (int) (drag_info.current_pointer_x - last_scrub_x);
1634                                 
1635                         } else {
1636                                 /* still moving to the right */
1637                                 
1638                                 scrub_reversals = 0;
1639                                 scrub_reverse_distance = 0;
1640                                 
1641                                 delta = 0.01 * (drag_info.current_pointer_x - last_scrub_x);
1642                                 session->request_transport_speed (session->transport_speed() + delta);
1643                         }
1644                 }
1645                 
1646                 /* if there have been more than 2 opposite motion moves detected, or one that moves
1647                    back more than 10 pixels, reverse direction
1648                 */
1649                 
1650                 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1651                         
1652                         if (scrubbing_direction > 0) {
1653                                 /* was forwards, go backwards */
1654                                 session->request_transport_speed (-0.1);
1655                                 scrubbing_direction = -1;
1656                         } else {
1657                                 /* was backwards, go forwards */
1658                                 session->request_transport_speed (0.1);
1659                                 scrubbing_direction = 1;
1660                         }
1661                         
1662                         scrub_reverse_distance = 0;
1663                         scrub_reversals = 0;
1664                 }
1665         }
1666         
1667         last_scrub_x = drag_info.current_pointer_x;
1668 }
1669
1670 bool
1671 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1672 {
1673         if (event->motion.is_hint) {
1674                 gint x, y;
1675                 
1676                 /* We call this so that MOTION_NOTIFY events continue to be
1677                    delivered to the canvas. We need to do this because we set
1678                    Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1679                    the density of the events, at the expense of a round-trip
1680                    to the server. Given that this will mostly occur on cases
1681                    where DISPLAY = :0.0, and given the cost of what the motion
1682                    event might do, its a good tradeoff.  
1683                 */
1684
1685                 track_canvas->get_pointer (x, y);
1686         } 
1687
1688         if (current_stepping_trackview) {
1689                 /* don't keep the persistent stepped trackview if the mouse moves */
1690                 current_stepping_trackview = 0;
1691                 step_timeout.disconnect ();
1692         }
1693
1694         if (session && session->actively_recording()) {
1695                 /* Sorry. no dragging stuff around while we record */
1696                 return true;
1697         }
1698
1699         drag_info.item_type = item_type;
1700         drag_info.last_pointer_x = drag_info.current_pointer_x;
1701         drag_info.last_pointer_y = drag_info.current_pointer_y;
1702         drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1703                                                        &drag_info.current_pointer_y);
1704
1705         
1706         switch (mouse_mode) {
1707         case MouseAudition:
1708                 if (_scrubbing) {
1709                         scrub ();
1710                 }
1711                 break;
1712
1713         default:
1714                 break;
1715         }
1716
1717         if (!from_autoscroll && drag_info.item) {
1718                 /* item != 0 is the best test i can think of for dragging.
1719                 */
1720                 if (!drag_info.move_threshold_passed) {
1721
1722                         bool x_threshold_passed =  (::llabs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1723                         bool y_threshold_passed =  (::llabs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1724                         
1725                         drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1726                         
1727                         // and change the initial grab loc/frame if this drag info wants us to
1728
1729                         if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1730                                 drag_info.grab_frame = drag_info.current_pointer_frame;
1731                                 drag_info.grab_x = drag_info.current_pointer_x;
1732                                 drag_info.grab_y = drag_info.current_pointer_y;
1733                                 drag_info.last_pointer_frame = drag_info.grab_frame;
1734                                 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1735                         }
1736                 }
1737         }
1738
1739         switch (item_type) {
1740         case PlayheadCursorItem:
1741         case MarkerItem:
1742         case MarkerBarItem:
1743         case TempoBarItem:
1744         case MeterBarItem:
1745         case RangeMarkerBarItem:
1746         case TransportMarkerBarItem:
1747         case CdMarkerBarItem:
1748         case GainControlPointItem:
1749         case RedirectAutomationControlPointItem:
1750         case GainAutomationControlPointItem:
1751         case PanAutomationControlPointItem:
1752         case TempoMarkerItem:
1753         case MeterMarkerItem:
1754         case RegionViewNameHighlight:
1755         case StartSelectionTrimItem:
1756         case EndSelectionTrimItem:
1757         case SelectionItem:
1758         case GainLineItem:
1759         case RedirectAutomationLineItem:
1760         case GainAutomationLineItem:
1761         case PanAutomationLineItem:
1762         case FadeInHandleItem:
1763         case FadeOutHandleItem:
1764         /* <CMT Additions> */
1765         case ImageFrameHandleStartItem:
1766         case ImageFrameHandleEndItem:
1767         case MarkerViewHandleStartItem:
1768         case MarkerViewHandleEndItem:
1769         /* </CMT Additions> */
1770           if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1771                                  (event->motion.state & Gdk::BUTTON2_MASK))) {
1772                   if (!from_autoscroll) {
1773                           maybe_autoscroll_horizontally (&event->motion);
1774                   }
1775                   if (drag_info.motion_callback) {
1776                           (this->*(drag_info.motion_callback)) (item, event);
1777                   }
1778                   goto handled;
1779           }
1780           goto not_handled;
1781           break;
1782         default:
1783                 break;
1784         }
1785
1786         switch (mouse_mode) {
1787         case MouseGain:
1788                 if (item_type == RegionItem) {
1789                         if (drag_info.item && drag_info.motion_callback) {
1790                                 (this->*(drag_info.motion_callback)) (item, event);
1791                         }
1792                         goto handled;
1793                 }
1794                 break;
1795
1796         case MouseObject:
1797         case MouseRange:
1798         case MouseZoom:
1799         case MouseTimeFX:
1800                 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1801                                        (event->motion.state & GDK_BUTTON2_MASK))) {
1802                         if (!from_autoscroll) {
1803                                 maybe_autoscroll (&event->motion);
1804                         }
1805                         if (drag_info.motion_callback) {
1806                                 (this->*(drag_info.motion_callback)) (item, event);
1807                         }
1808                         goto handled;
1809                 }
1810                 goto not_handled;
1811                 break;
1812
1813         default:
1814                 break;
1815         }
1816
1817   handled:
1818         track_canvas_motion (event);
1819         // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1820         return true;
1821         
1822   not_handled:
1823         return false;
1824 }
1825
1826 void
1827 Editor::break_drag ()
1828 {
1829         stop_canvas_autoscroll ();
1830         hide_verbose_canvas_cursor ();
1831
1832         if (drag_info.item) {
1833                 drag_info.item->ungrab (0);
1834
1835                 /* put it back where it came from */
1836
1837                 double cxw, cyw;
1838                 cxw = 0;
1839                 cyw = 0;
1840                 drag_info.item->i2w (cxw, cyw);
1841                 drag_info.item->move (drag_info.original_x - cxw, drag_info.original_y - cyw);
1842         }
1843
1844         finalize_drag ();
1845 }
1846
1847 void
1848 Editor::finalize_drag ()
1849 {
1850         drag_info.item = 0;
1851         drag_info.copy = false;
1852         drag_info.motion_callback = 0;
1853         drag_info.finished_callback = 0;
1854         drag_info.dest_trackview = 0;
1855         drag_info.source_trackview = 0;
1856         drag_info.last_frame_position = 0;
1857         drag_info.grab_frame = 0;
1858         drag_info.last_pointer_frame = 0;
1859         drag_info.current_pointer_frame = 0;
1860         drag_info.brushing = false;
1861         range_marker_drag_rect->hide();
1862         drag_info.clear_copied_locations ();
1863 }
1864
1865 void
1866 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1867 {
1868         if (drag_info.item == 0) {
1869                 fatal << _("programming error: start_grab called without drag item") << endmsg;
1870                 /*NOTREACHED*/
1871                 return;
1872         }
1873
1874         if (cursor == 0) {
1875                 cursor = which_grabber_cursor ();
1876         }
1877
1878         // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1879
1880         if (Keyboard::is_button2_event (&event->button)) {
1881                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
1882                         drag_info.y_constrained = true;
1883                         drag_info.x_constrained = false;
1884                 } else {
1885                         drag_info.y_constrained = false;
1886                         drag_info.x_constrained = true;
1887                 }
1888         } else {
1889                 drag_info.x_constrained = false;
1890                 drag_info.y_constrained = false;
1891         }
1892
1893         drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1894         drag_info.last_pointer_frame = drag_info.grab_frame;
1895         drag_info.current_pointer_frame = drag_info.grab_frame;
1896         drag_info.current_pointer_x = drag_info.grab_x;
1897         drag_info.current_pointer_y = drag_info.grab_y;
1898         drag_info.last_pointer_x = drag_info.current_pointer_x;
1899         drag_info.last_pointer_y = drag_info.current_pointer_y;
1900         drag_info.cumulative_x_drag = 0;
1901         drag_info.cumulative_y_drag = 0;
1902         drag_info.first_move = true;
1903         drag_info.move_threshold_passed = false;
1904         drag_info.want_move_threshold = false;
1905         drag_info.pointer_frame_offset = 0;
1906         drag_info.brushing = false;
1907         drag_info.clear_copied_locations ();
1908
1909         drag_info.original_x = 0;
1910         drag_info.original_y = 0;
1911         drag_info.item->i2w (drag_info.original_x, drag_info.original_y);
1912
1913         drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1914                               *cursor,
1915                               event->button.time);
1916
1917         if (session && session->transport_rolling()) {
1918                 drag_info.was_rolling = true;
1919         } else {
1920                 drag_info.was_rolling = false;
1921         }
1922
1923         switch (snap_type) {
1924         case SnapToRegionStart:
1925         case SnapToRegionEnd:
1926         case SnapToRegionSync:
1927         case SnapToRegionBoundary:
1928                 build_region_boundary_cache ();
1929                 break;
1930         default:
1931                 break;
1932         }
1933 }
1934
1935 void
1936 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1937 {
1938         drag_info.item->ungrab (0);
1939         drag_info.item = new_item;
1940
1941         if (cursor == 0) {
1942                 cursor = which_grabber_cursor ();
1943         }
1944
1945         drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1946 }
1947
1948 bool
1949 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1950 {
1951         bool did_drag = false;
1952
1953         stop_canvas_autoscroll ();
1954
1955         if (drag_info.item == 0) {
1956                 return false;
1957         }
1958         
1959         drag_info.item->ungrab (event->button.time);
1960
1961         if (drag_info.finished_callback) {
1962                 drag_info.last_pointer_x = drag_info.current_pointer_x;
1963                 drag_info.last_pointer_y = drag_info.current_pointer_y;
1964                 (this->*(drag_info.finished_callback)) (item, event);
1965         }
1966
1967         did_drag = !drag_info.first_move;
1968
1969         hide_verbose_canvas_cursor();
1970
1971         finalize_drag ();
1972
1973         return did_drag;
1974 }
1975
1976 void
1977 Editor::region_gain_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1978 {
1979         if (drag_info.first_move && drag_info.move_threshold_passed) {
1980                 drag_info.first_move = false;
1981         }
1982 }
1983
1984 void
1985 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1986 {
1987         drag_info.item = item;
1988         drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1989         drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1990
1991         start_grab (event);
1992
1993         if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1994                 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1995                 /*NOTREACHED*/
1996         }
1997
1998         AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1999
2000         drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes64_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());        
2001 }
2002
2003 void
2004 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2005 {
2006         AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2007         nframes64_t pos;
2008         nframes64_t fade_length;
2009
2010         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2011                 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2012         }
2013         else {
2014                 pos = 0;
2015         }
2016
2017         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2018                 snap_to (pos);
2019         }
2020
2021         if (pos < (arv->region()->position() + 64)) {
2022                 fade_length = 64; // this should be a minimum defined somewhere
2023         } else if (pos > arv->region()->last_frame()) {
2024                 fade_length = arv->region()->length();
2025         } else {
2026                 fade_length = pos - arv->region()->position();
2027         }               
2028         /* mapover the region selection */
2029
2030         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2031
2032                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2033                 
2034                 if (!tmp) {
2035                         continue;
2036                 }
2037         
2038                 tmp->reset_fade_in_shape_width (fade_length);
2039         }
2040
2041         show_verbose_duration_cursor (arv->region()->position(),  arv->region()->position() + fade_length, 10);
2042
2043         drag_info.first_move = false;
2044 }
2045
2046 void
2047 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2048 {
2049         AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2050         nframes64_t pos;
2051         nframes64_t fade_length;
2052
2053         if (drag_info.first_move) return;
2054
2055         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2056                 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2057         } else {
2058                 pos = 0;
2059         }
2060
2061         if (pos < (arv->region()->position() + 64)) {
2062                 fade_length = 64; // this should be a minimum defined somewhere
2063         } else if (pos > arv->region()->last_frame()) {
2064                 fade_length = arv->region()->length();
2065         } else {
2066                 fade_length = pos - arv->region()->position();
2067         }
2068                 
2069         begin_reversible_command (_("change fade in length"));
2070
2071         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2072
2073                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2074                 
2075                 if (!tmp) {
2076                         continue;
2077                 }
2078         
2079                 AutomationList& alist = tmp->audio_region()->fade_in();
2080                 XMLNode &before = alist.get_state();
2081
2082                 tmp->audio_region()->set_fade_in_length (fade_length);
2083                 tmp->audio_region()->set_fade_in_active (true);
2084                 
2085                 XMLNode &after = alist.get_state();
2086                 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
2087         }
2088
2089         commit_reversible_command ();
2090 }
2091
2092 void
2093 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
2094 {
2095         drag_info.item = item;
2096         drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
2097         drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
2098
2099         start_grab (event);
2100
2101         if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
2102                 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
2103                 /*NOTREACHED*/
2104         }
2105
2106         AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2107
2108         drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes64_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());     
2109 }
2110
2111 void
2112 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2113 {
2114         AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2115         nframes64_t pos;
2116         nframes64_t fade_length;
2117
2118         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2119                 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2120         } else {
2121                 pos = 0;
2122         }
2123
2124         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2125                 snap_to (pos);
2126         }
2127         
2128         if (pos > (arv->region()->last_frame() - 64)) {
2129                 fade_length = 64; // this should really be a minimum fade defined somewhere
2130         }
2131         else if (pos < arv->region()->position()) {
2132                 fade_length = arv->region()->length();
2133         }
2134         else {
2135                 fade_length = arv->region()->last_frame() - pos;
2136         }
2137                 
2138         /* mapover the region selection */
2139
2140         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2141
2142                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2143                 
2144                 if (!tmp) {
2145                         continue;
2146                 }
2147         
2148                 tmp->reset_fade_out_shape_width (fade_length);
2149         }
2150
2151         show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
2152
2153         drag_info.first_move = false;
2154 }
2155
2156 void
2157 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2158 {
2159         if (drag_info.first_move) return;
2160
2161         AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2162         nframes64_t pos;
2163         nframes64_t fade_length;
2164
2165         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2166                 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2167         }
2168         else {
2169                 pos = 0;
2170         }
2171
2172         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2173                 snap_to (pos);
2174         }
2175
2176         if (pos > (arv->region()->last_frame() - 64)) {
2177                 fade_length = 64; // this should really be a minimum fade defined somewhere
2178         }
2179         else if (pos < arv->region()->position()) {
2180                 fade_length = arv->region()->length();
2181         }
2182         else {
2183                 fade_length = arv->region()->last_frame() - pos;
2184         }
2185
2186         begin_reversible_command (_("change fade out length"));
2187
2188         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2189
2190                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2191                 
2192                 if (!tmp) {
2193                         continue;
2194                 }
2195         
2196                 AutomationList& alist = tmp->audio_region()->fade_out();
2197                 XMLNode &before = alist.get_state();
2198                 
2199                 tmp->audio_region()->set_fade_out_length (fade_length);
2200                 tmp->audio_region()->set_fade_out_active (true);
2201
2202                 XMLNode &after = alist.get_state();
2203                 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
2204         }
2205
2206         commit_reversible_command ();
2207 }
2208
2209 void
2210 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2211 {
2212         drag_info.item = item;
2213         drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2214         drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2215
2216         start_grab (event);
2217
2218         if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2219                 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2220                 /*NOTREACHED*/
2221         }
2222
2223         Cursor* cursor = (Cursor *) drag_info.data;
2224
2225         if (cursor == playhead_cursor) {
2226                 _dragging_playhead = true;
2227                 
2228                 if (session && drag_info.was_rolling) {
2229                         session->request_stop ();
2230                 }
2231
2232                 if (session && session->is_auditioning()) {
2233                         session->cancel_audition ();
2234                 }
2235         }
2236
2237         drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;  
2238         
2239         show_verbose_time_cursor (cursor->current_frame, 10);
2240 }
2241
2242 void
2243 Editor::start_cursor_grab_no_stop (ArdourCanvas::Item* item, GdkEvent* event)
2244 {
2245         drag_info.item = item;
2246         drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2247         drag_info.finished_callback = &Editor::cursor_drag_finished_ensure_locate_callback;
2248
2249         start_grab (event);
2250
2251         if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2252                 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2253                 /*NOTREACHED*/
2254         }
2255
2256         Cursor* cursor = (Cursor *) drag_info.data;
2257         nframes64_t where = event_frame (event, 0, 0);
2258
2259         snap_to(where);
2260         playhead_cursor->set_position (where);
2261
2262         if (cursor == playhead_cursor) {
2263                 _dragging_playhead = true;
2264
2265                 if (session && session->is_auditioning()) {
2266                         session->cancel_audition ();
2267                 }
2268         }
2269
2270         drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;  
2271         
2272         show_verbose_time_cursor (cursor->current_frame, 10);
2273 }
2274
2275 void
2276 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2277 {
2278         Cursor* cursor = (Cursor *) drag_info.data;
2279         nframes64_t adjusted_frame;
2280         
2281         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2282                 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2283         }
2284         else {
2285                 adjusted_frame = 0;
2286         }
2287         
2288         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2289                 if (cursor == playhead_cursor) {
2290                         snap_to (adjusted_frame);
2291                 }
2292         }
2293         
2294         if (adjusted_frame == drag_info.last_pointer_frame) return;
2295
2296         cursor->set_position (adjusted_frame);
2297
2298         show_verbose_time_cursor (cursor->current_frame, 10);
2299
2300 #ifdef GTKOSX
2301         track_canvas->update_now ();
2302 #endif
2303         UpdateAllTransportClocks (cursor->current_frame);
2304
2305         drag_info.last_pointer_frame = adjusted_frame;
2306         drag_info.first_move = false;
2307 }
2308
2309 void
2310 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2311 {
2312         _dragging_playhead = false;
2313
2314         if (drag_info.first_move) {
2315                 return;
2316         }
2317         
2318         cursor_drag_motion_callback (item, event);
2319         
2320         if (item == &playhead_cursor->canvas_item) {
2321                 if (session) {
2322                         session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2323                 }
2324         } 
2325 }
2326
2327 void
2328 Editor::cursor_drag_finished_ensure_locate_callback (ArdourCanvas::Item* item, GdkEvent* event)
2329 {
2330         _dragging_playhead = false;
2331         
2332         cursor_drag_motion_callback (item, event);
2333         
2334         if (item == &playhead_cursor->canvas_item) {
2335                 if (session) {
2336                         session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2337                 }
2338         } 
2339 }
2340
2341 void
2342 Editor::update_marker_drag_item (Location *location)
2343 {
2344         double x1 = frame_to_pixel (location->start());
2345         double x2 = frame_to_pixel (location->end());
2346
2347         if (location->is_mark()) {
2348                 marker_drag_line_points.front().set_x(x1);
2349                 marker_drag_line_points.back().set_x(x1);
2350                 marker_drag_line->property_points() = marker_drag_line_points;
2351         } else {
2352                 range_marker_drag_rect->property_x1() = x1;
2353                 range_marker_drag_rect->property_x2() = x2;
2354         }
2355 }
2356
2357 void
2358 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2359 {
2360         Marker* marker;
2361
2362         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2363                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2364                 /*NOTREACHED*/
2365         }
2366
2367         bool is_start;
2368
2369         Location  *location = find_location_from_marker (marker, is_start);
2370
2371         drag_info.item = item;
2372         drag_info.data = marker;
2373         drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2374         drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2375
2376         start_grab (event);
2377
2378         _dragging_edit_point = true;
2379
2380         drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());       
2381
2382         update_marker_drag_item (location);
2383
2384         if (location->is_mark()) {
2385                 // marker_drag_line->show();
2386                 // marker_drag_line->raise_to_top();
2387         } else {
2388                 range_marker_drag_rect->show();
2389                 //range_marker_drag_rect->raise_to_top();
2390         }
2391
2392         if (is_start) {
2393                 show_verbose_time_cursor (location->start(), 10);
2394         } else {
2395                 show_verbose_time_cursor (location->end(), 10);
2396         }
2397
2398         Selection::Operation op = Keyboard::selection_type (event->button.state);
2399
2400         switch (op) {
2401         case Selection::Toggle:
2402                 selection->toggle (marker);
2403                 break;
2404         case Selection::Set:
2405                 if (!selection->selected (marker)) {
2406                         selection->set (marker);
2407                 }
2408                 break;
2409         case Selection::Extend:
2410         {
2411                 Locations::LocationList ll;
2412                 list<Marker*> to_add;
2413                 nframes64_t s, e;
2414                 selection->markers.range (s, e);
2415                 s = min (marker->position(), s);
2416                 e = max (marker->position(), e);
2417                 s = min (s, e);
2418                 e = max (s, e);
2419                 if (e < max_frames) {
2420                         ++e;
2421                 }
2422                 session->locations()->find_all_between (s, e, ll, Location::Flags (0));
2423                 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2424                         LocationMarkers* lm = find_location_markers (*i);
2425                         if (lm) {
2426                                 if (lm->start) {
2427                                         to_add.push_back (lm->start);
2428                                 }
2429                                 if (lm->end) {
2430                                         to_add.push_back (lm->end);
2431                                 }
2432                         }
2433                 }
2434                 if (!to_add.empty()) {
2435                         selection->add (to_add);
2436                 }
2437                 break;
2438         }
2439         case Selection::Add:
2440                 selection->add (marker);
2441                 break;
2442         }
2443
2444         /* set up copies for us to manipulate during the drag */
2445
2446         drag_info.clear_copied_locations ();
2447
2448         for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
2449                 Location  *l = find_location_from_marker (*i, is_start);
2450                 drag_info.copied_locations.push_back (new Location (*l));
2451         }
2452 }
2453
2454 void
2455 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2456 {
2457         nframes64_t f_delta = 0;
2458         nframes64_t newframe;
2459         bool is_start;
2460         bool move_both = false;
2461         Marker* dragged_marker = (Marker*) drag_info.data;
2462         Marker* marker;
2463         Location  *real_location;
2464         Location  *copy_location;
2465
2466         if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2467                 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2468         } else {
2469                 newframe = 0;
2470         }
2471
2472         nframes64_t next = newframe;
2473
2474         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2475                 snap_to (newframe, 0, true);
2476         }
2477         
2478         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) { 
2479                 return;
2480         }
2481
2482         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2483                 move_both = true;
2484         }
2485
2486         MarkerSelection::iterator i;
2487         list<Location*>::iterator x;
2488
2489         /* find the marker we're dragging, and compute the delta */
2490
2491         for (i = selection->markers.begin(), x = drag_info.copied_locations.begin(); 
2492              x != drag_info.copied_locations.end() && i != selection->markers.end(); 
2493              ++i, ++x) {
2494
2495                 copy_location = *x;
2496                 marker = *i;
2497
2498                 if (marker == dragged_marker) {
2499
2500                         if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2501                                 /* que pasa ?? */
2502                                 return;
2503                         }
2504
2505                         if (real_location->is_mark()) {
2506                                 f_delta = newframe - copy_location->start();
2507                         } else {
2508
2509
2510                                 switch (marker->type()) {
2511                                 case Marker::Start:
2512                                 case Marker::LoopStart:
2513                                 case Marker::PunchIn:
2514                                         f_delta = newframe - copy_location->start();
2515                                         break;
2516
2517                                 case Marker::End:
2518                                 case Marker::LoopEnd:
2519                                 case Marker::PunchOut:
2520                                         f_delta = newframe - copy_location->end();
2521                                         break;
2522                                 default:
2523                                         /* what kind of marker is this ? */
2524                                         return;
2525                                 }
2526                         }
2527                         break;
2528                 }
2529         }
2530
2531         if (i == selection->markers.end()) {
2532                 /* hmm, impossible - we didn't find the dragged marker */
2533                 return;
2534         }
2535
2536         /* now move them all */
2537
2538         for (i = selection->markers.begin(), x = drag_info.copied_locations.begin(); 
2539              x != drag_info.copied_locations.end() && i != selection->markers.end(); 
2540              ++i, ++x) {
2541
2542                 copy_location = *x;
2543                 marker = *i;
2544
2545                 /* call this to find out if its the start or end */
2546                 
2547                 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2548                         continue;
2549                 }
2550                 
2551                 if (real_location->locked()) {
2552                         continue;
2553                 }
2554
2555                 if (copy_location->is_mark()) {
2556
2557                         /* just move it */
2558                         
2559                         copy_location->set_start (copy_location->start() + f_delta);
2560
2561                 } else {
2562                         
2563                         nframes64_t new_start = copy_location->start() + f_delta;
2564                         nframes64_t new_end = copy_location->end() + f_delta;
2565                         
2566                         if (is_start) { // start-of-range marker
2567                                 
2568                                 if (move_both) {
2569                                         copy_location->set_start (new_start);
2570                                         copy_location->set_end (new_end);
2571                                 } else  if (new_start < copy_location->end()) {
2572                                         copy_location->set_start (new_start);
2573                                 } else { 
2574                                         snap_to (next, 1, true);
2575                                         copy_location->set_end (next);
2576                                         copy_location->set_start (newframe);
2577                                 }
2578                                 
2579                         } else { // end marker
2580                                 
2581                                 if (move_both) {
2582                                         copy_location->set_end (new_end);
2583                                         copy_location->set_start (new_start);
2584                                 } else if (new_end > copy_location->start()) {
2585                                         copy_location->set_end (new_end);
2586                                 } else if (newframe > 0) {
2587                                         snap_to (next, -1, true);
2588                                         copy_location->set_start (next);
2589                                         copy_location->set_end (newframe);
2590                                 }
2591                         }
2592                 }
2593                 update_marker_drag_item (copy_location);
2594
2595                 LocationMarkers* lm = find_location_markers (real_location);
2596
2597                 if (lm) {
2598                         lm->set_position (copy_location->start(), copy_location->end());
2599                 }
2600         }
2601
2602         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2603         drag_info.first_move = false;
2604
2605         if (drag_info.copied_locations.empty()) {
2606                 abort();
2607         }
2608
2609         edit_point_clock.set (drag_info.copied_locations.front()->start());
2610         show_verbose_time_cursor (newframe, 10);
2611
2612 #ifdef GTKOSX
2613         track_canvas->update_now ();
2614 #endif
2615 }
2616
2617 void
2618 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2619 {
2620         if (drag_info.first_move) {
2621
2622                 /* just a click, do nothing but finish
2623                    off the selection process
2624                 */
2625
2626                 Selection::Operation op = Keyboard::selection_type (event->button.state);
2627                 Marker* marker = (Marker *) drag_info.data;
2628
2629                 switch (op) {
2630                 case Selection::Set:
2631                         if (selection->selected (marker) && selection->markers.size() > 1) {
2632                                 selection->set (marker);
2633                         }
2634                         break;
2635
2636                 case Selection::Toggle:
2637                 case Selection::Extend:
2638                 case Selection::Add:
2639                         break;
2640                 }
2641                 
2642                 return;
2643         }
2644
2645         _dragging_edit_point = false;
2646         
2647
2648         begin_reversible_command ( _("move marker") );
2649         XMLNode &before = session->locations()->get_state();
2650
2651         MarkerSelection::iterator i;
2652         list<Location*>::iterator x;
2653         bool is_start;
2654
2655         for (i = selection->markers.begin(), x = drag_info.copied_locations.begin(); 
2656              x != drag_info.copied_locations.end() && i != selection->markers.end(); 
2657              ++i, ++x) {
2658         
2659                 Location * location = find_location_from_marker ((*i), is_start);
2660                 
2661                 if (location) {
2662                         
2663                         if (location->locked()) {
2664                                 return;
2665                         }
2666                         
2667                         if (location->is_mark()) {
2668                                 location->set_start ((*x)->start());
2669                         } else {
2670                                 location->set ((*x)->start(), (*x)->end());
2671                         }
2672                 }
2673         }
2674
2675         XMLNode &after = session->locations()->get_state();
2676         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2677         commit_reversible_command ();
2678         
2679         marker_drag_line->hide();
2680         range_marker_drag_rect->hide();
2681 }
2682
2683 void
2684 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2685 {
2686         Marker* marker;
2687         MeterMarker* meter_marker;
2688
2689         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2690                 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2691                 /*NOTREACHED*/
2692         }
2693
2694         meter_marker = dynamic_cast<MeterMarker*> (marker);
2695
2696         MetricSection& section (meter_marker->meter());
2697
2698         if (!section.movable()) {
2699                 return;
2700         }
2701
2702         drag_info.item = item;
2703         drag_info.copy = false;
2704         drag_info.data = marker;
2705         drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2706         drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2707
2708         start_grab (event);
2709
2710         drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();  
2711
2712         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2713 }
2714
2715 void
2716 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2717 {
2718         Marker* marker;
2719         MeterMarker* meter_marker;
2720
2721         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2722                 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2723                 /*NOTREACHED*/
2724         }
2725
2726         meter_marker = dynamic_cast<MeterMarker*> (marker);
2727         
2728         // create a dummy marker for visual representation of moving the copy.
2729         // The actual copying is not done before we reach the finish callback.
2730         char name[64];
2731         snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2732         MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name, 
2733                                                   *new MeterSection(meter_marker->meter()));
2734
2735         drag_info.item = &new_marker->the_item();
2736         drag_info.copy = true;
2737         drag_info.data = new_marker;
2738         drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2739         drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2740
2741         start_grab (event);
2742
2743         drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();  
2744
2745         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2746 }
2747
2748 void
2749 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2750 {
2751         MeterMarker* marker = (MeterMarker *) drag_info.data;
2752         nframes64_t adjusted_frame;
2753
2754         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2755                 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2756         }
2757         else {
2758                 adjusted_frame = 0;
2759         }
2760         
2761         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2762                 snap_to (adjusted_frame);
2763         }
2764         
2765         if (adjusted_frame == drag_info.last_pointer_frame) return;
2766
2767         marker->set_position (adjusted_frame);
2768         
2769         
2770         drag_info.last_pointer_frame = adjusted_frame;
2771         drag_info.first_move = false;
2772
2773         show_verbose_time_cursor (adjusted_frame, 10);
2774 }
2775
2776 void
2777 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2778 {
2779         if (drag_info.first_move) return;
2780
2781         meter_marker_drag_motion_callback (drag_info.item, event);
2782         
2783         MeterMarker* marker = (MeterMarker *) drag_info.data;
2784         BBT_Time when;
2785         
2786         TempoMap& map (session->tempo_map());
2787         map.bbt_time (drag_info.last_pointer_frame, when);
2788         
2789         if (drag_info.copy == true) {
2790                 begin_reversible_command (_("copy meter mark"));
2791                 XMLNode &before = map.get_state();
2792                 map.add_meter (marker->meter(), when);
2793                 XMLNode &after = map.get_state();
2794                 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2795                 commit_reversible_command ();
2796                 
2797                 // delete the dummy marker we used for visual representation of copying.
2798                 // a new visual marker will show up automatically.
2799                 delete marker;
2800         } else {
2801                 begin_reversible_command (_("move meter mark"));
2802                 XMLNode &before = map.get_state();
2803                 map.move_meter (marker->meter(), when);
2804                 XMLNode &after = map.get_state();
2805                 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2806                 commit_reversible_command ();
2807         }
2808 }
2809
2810 void
2811 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2812 {
2813         Marker* marker;
2814         TempoMarker* tempo_marker;
2815
2816         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2817                 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2818                 /*NOTREACHED*/
2819         }
2820
2821         if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2822                 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2823                 /*NOTREACHED*/
2824         }
2825
2826         MetricSection& section (tempo_marker->tempo());
2827
2828         if (!section.movable()) {
2829                 return;
2830         }
2831
2832         drag_info.item = item;
2833         drag_info.copy = false;
2834         drag_info.data = marker;
2835         drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2836         drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2837
2838         start_grab (event);
2839
2840         drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();  
2841         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2842 }
2843
2844 void
2845 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2846 {
2847         Marker* marker;
2848         TempoMarker* tempo_marker;
2849
2850         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2851                 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2852                 /*NOTREACHED*/
2853         }
2854
2855         if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2856                 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2857                 /*NOTREACHED*/
2858         }
2859
2860         // create a dummy marker for visual representation of moving the copy.
2861         // The actual copying is not done before we reach the finish callback.
2862         char name[64];
2863         snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2864         TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name, 
2865                                                   *new TempoSection(tempo_marker->tempo()));
2866
2867         drag_info.item = &new_marker->the_item();
2868         drag_info.copy = true;
2869         drag_info.data = new_marker;
2870         drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2871         drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2872
2873         start_grab (event);
2874
2875         drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2876
2877         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2878 }
2879
2880 void
2881 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2882 {
2883         TempoMarker* marker = (TempoMarker *) drag_info.data;
2884         nframes64_t adjusted_frame;
2885         
2886         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2887                 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2888         }
2889         else {
2890                 adjusted_frame = 0;
2891         }
2892
2893         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2894                 snap_to (adjusted_frame);
2895         }
2896         
2897         if (adjusted_frame == drag_info.last_pointer_frame) return;
2898
2899         /* OK, we've moved far enough to make it worth actually move the thing. */
2900                 
2901         marker->set_position (adjusted_frame);
2902         
2903         show_verbose_time_cursor (adjusted_frame, 10);
2904
2905         drag_info.last_pointer_frame = adjusted_frame;
2906         drag_info.first_move = false;
2907 }
2908
2909 void
2910 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2911 {
2912         if (drag_info.first_move) return;
2913         
2914         tempo_marker_drag_motion_callback (drag_info.item, event);
2915         
2916         TempoMarker* marker = (TempoMarker *) drag_info.data;
2917         BBT_Time when;
2918         
2919         TempoMap& map (session->tempo_map());
2920         map.bbt_time (drag_info.last_pointer_frame, when);
2921
2922         if (drag_info.copy == true) {
2923                 begin_reversible_command (_("copy tempo mark"));
2924                 XMLNode &before = map.get_state();
2925                 map.add_tempo (marker->tempo(), when);
2926                 XMLNode &after = map.get_state();
2927                 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2928                 commit_reversible_command ();
2929                 
2930                 // delete the dummy marker we used for visual representation of copying.
2931                 // a new visual marker will show up automatically.
2932                 delete marker;
2933         } else {
2934                 begin_reversible_command (_("move tempo mark"));
2935                 XMLNode &before = map.get_state();
2936                 map.move_tempo (marker->tempo(), when);
2937                 XMLNode &after = map.get_state();
2938                 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2939                 commit_reversible_command ();
2940         }
2941 }
2942
2943 void
2944 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2945 {
2946         ControlPoint* control_point;
2947
2948         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2949                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2950                 /*NOTREACHED*/
2951         }
2952
2953         // We shouldn't remove the first or last gain point
2954         if (control_point->line.is_last_point(*control_point) ||
2955                 control_point->line.is_first_point(*control_point)) {   
2956                 return;
2957         }
2958
2959         control_point->line.remove_point (*control_point);
2960 }
2961
2962 void
2963 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2964 {
2965         ControlPoint* control_point;
2966
2967         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2968                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2969                 /*NOTREACHED*/
2970         }
2971
2972         control_point->line.remove_point (*control_point);
2973 }
2974
2975 void
2976 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2977 {
2978         ControlPoint* control_point;
2979         
2980         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2981                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2982                 /*NOTREACHED*/
2983         }
2984
2985         drag_info.item = item;
2986         drag_info.data = control_point;
2987         drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2988         drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2989
2990         start_grab (event, fader_cursor);
2991
2992         // start the grab at the center of the control point so
2993         // the point doesn't 'jump' to the mouse after the first drag
2994         drag_info.grab_x = control_point->get_x();
2995         drag_info.grab_y = control_point->get_y();
2996         control_point->line.parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
2997         track_canvas->w2c(drag_info.grab_x, drag_info.grab_y, drag_info.grab_x, drag_info.grab_y);
2998
2999         drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
3000
3001         control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
3002
3003         float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
3004         set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction), 
3005                                    drag_info.current_pointer_x + 10, drag_info.current_pointer_y + 10);
3006
3007         show_verbose_canvas_cursor ();
3008 }
3009
3010 void
3011 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3012 {
3013         ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
3014
3015         double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
3016         double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
3017
3018         if (event->button.state & Keyboard::SecondaryModifier) {
3019                 dx *= 0.1;
3020                 dy *= 0.1;
3021         }
3022
3023         double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
3024         double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
3025
3026         // calculate zero crossing point. back off by .01 to stay on the
3027         // positive side of zero
3028         double _unused = 0;
3029         double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line.height() - .01;
3030         cp->line.parent_group().i2w(_unused, zero_gain_y);
3031
3032         // make sure we hit zero when passing through
3033         if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
3034                         or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
3035                 cy = zero_gain_y;
3036         }
3037
3038         if (drag_info.x_constrained) {
3039                 cx = drag_info.grab_x;
3040         }
3041         if (drag_info.y_constrained) {
3042                 cy = drag_info.grab_y;
3043         }
3044
3045         drag_info.cumulative_x_drag = cx - drag_info.grab_x;
3046         drag_info.cumulative_y_drag = cy - drag_info.grab_y;
3047
3048         cp->line.parent_group().w2i (cx, cy);
3049
3050         cx = max (0.0, cx);
3051         cy = max (0.0, cy);
3052         cy = min ((double) cp->line.height(), cy);
3053
3054         //translate cx to frames
3055         nframes64_t cx_frames = unit_to_frame (cx);
3056
3057         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
3058                 snap_to (cx_frames);
3059         }
3060
3061         float fraction = 1.0 - (cy / cp->line.height());
3062
3063         bool push;
3064
3065         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3066                 push = true;
3067         } else {
3068                 push = false;
3069         }
3070
3071         cp->line.point_drag (*cp, cx_frames , fraction, push);
3072         
3073         set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
3074
3075         drag_info.first_move = false;
3076 }
3077
3078 void
3079 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3080 {
3081         ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
3082
3083         if (drag_info.first_move) {
3084
3085                 /* just a click */
3086                 
3087                 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3088                         reset_point_selection ();
3089                 }
3090
3091         } else {
3092                 control_point_drag_motion_callback (item, event);
3093         }
3094         cp->line.end_drag (cp);
3095 }
3096
3097 void
3098 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
3099 {
3100         switch (mouse_mode) {
3101         case MouseGain:
3102                 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
3103                 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
3104                 break;
3105         default:
3106                 break;
3107         }
3108 }
3109
3110 void
3111 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
3112 {
3113         AutomationLine* al;
3114         
3115         if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
3116                 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
3117                 /*NOTREACHED*/
3118         }
3119
3120         start_line_grab (al, event);
3121 }
3122
3123 void
3124 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
3125 {
3126         double cx;
3127         double cy;
3128         nframes64_t frame_within_region;
3129
3130         /* need to get x coordinate in terms of parent (TimeAxisItemView)
3131            origin, and ditto for y.
3132         */
3133
3134         cx = event->button.x;
3135         cy = event->button.y;
3136
3137         line->parent_group().w2i (cx, cy);
3138
3139         frame_within_region = (nframes64_t) floor (cx * frames_per_unit);
3140
3141         if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before, 
3142                                             current_line_drag_info.after)) {
3143                 /* no adjacent points */
3144                 return;
3145         }
3146
3147         drag_info.item = &line->grab_item();
3148         drag_info.data = line;
3149         drag_info.motion_callback = &Editor::line_drag_motion_callback;
3150         drag_info.finished_callback = &Editor::line_drag_finished_callback;
3151
3152         start_grab (event, fader_cursor);
3153
3154         /* store grab start in parent frame */
3155
3156         drag_info.grab_x = cx;
3157         drag_info.grab_y = cy;
3158
3159         double fraction = 1.0 - (cy / line->height());
3160
3161         line->start_drag (0, drag_info.grab_frame, fraction);
3162         
3163         set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
3164                                    drag_info.current_pointer_x + 10, drag_info.current_pointer_y + 10);
3165         show_verbose_canvas_cursor ();
3166 }
3167
3168 void
3169 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3170 {
3171         AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3172
3173         double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
3174         
3175         if (event->button.state & Keyboard::SecondaryModifier) {
3176                 dy *= 0.1;
3177         }
3178
3179         double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
3180
3181         // calculate zero crossing point. back off by .01 to stay on the
3182         // positive side of zero
3183         double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
3184
3185         // line->parent_group().i2w(_unused, zero_gain_y);
3186
3187         // make sure we hit zero when passing through
3188         if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
3189                         or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
3190                 cy = zero_gain_y;
3191         }
3192
3193         drag_info.cumulative_y_drag = cy - drag_info.grab_y;
3194
3195         cy = max (0.0, cy);
3196         cy = min ((double) line->height(), cy);
3197
3198
3199         double fraction = 1.0 - (cy / line->height());
3200
3201         bool push;
3202
3203         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3204                 push = false;
3205         } else {
3206                 push = true;
3207         }
3208
3209         line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
3210         
3211         set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
3212 }
3213
3214 void
3215 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3216 {
3217         AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3218         line_drag_motion_callback (item, event);
3219         line->end_drag (0);
3220 }
3221
3222 void
3223 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3224 {
3225         if (selection->regions.empty() || clicked_regionview == 0) {
3226                 return;
3227         }
3228
3229         drag_info.copy = false;
3230         drag_info.item = item;
3231         drag_info.data = clicked_regionview;
3232
3233         if (Config->get_edit_mode() == Splice) {
3234                 drag_info.motion_callback = &Editor::region_drag_splice_motion_callback;
3235                 drag_info.finished_callback = &Editor::region_drag_splice_finished_callback;
3236         } else {
3237                 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3238                 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3239         }
3240
3241         start_grab (event);
3242
3243         double speed = 1.0;
3244         TimeAxisView* tvp = clicked_trackview;
3245         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3246
3247         if (tv && tv->is_audio_track()) {
3248                 speed = tv->get_diskstream()->speed();
3249         }
3250         
3251         drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3252         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3253         drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3254         drag_info.dest_trackview = drag_info.source_trackview;
3255         // we want a move threshold
3256         drag_info.want_move_threshold = true;
3257         
3258         show_verbose_time_cursor (drag_info.last_frame_position, 10);
3259
3260         begin_reversible_command (_("move region(s)"));
3261
3262         _region_motion_group->raise_to_top ();
3263
3264         /* sync the canvas to what we think is its current state */
3265         track_canvas->update_now();
3266 }
3267
3268 void
3269 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
3270 {
3271         if (selection->regions.empty() || clicked_regionview == 0) {
3272                 return;
3273         }
3274
3275         drag_info.copy = true;
3276         drag_info.item = item;
3277         drag_info.data = clicked_regionview;    
3278
3279         start_grab(event);
3280
3281         TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
3282         RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
3283         double speed = 1.0;
3284
3285         if (atv && atv->is_audio_track()) {
3286                 speed = atv->get_diskstream()->speed();
3287         }
3288         
3289         drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3290         drag_info.dest_trackview = drag_info.source_trackview;
3291         drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3292         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3293         // we want a move threshold
3294         drag_info.want_move_threshold = true;
3295         drag_info.motion_callback = &Editor::region_drag_motion_callback;
3296         drag_info.finished_callback = &Editor::region_drag_finished_callback;
3297         show_verbose_time_cursor (drag_info.last_frame_position, 10);
3298         _region_motion_group->raise_to_top ();
3299 }
3300
3301 void
3302 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
3303 {
3304         if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) {
3305                 return;
3306         }
3307
3308         drag_info.copy = false;
3309         drag_info.item = item;
3310         drag_info.data = clicked_regionview;
3311         drag_info.motion_callback = &Editor::region_drag_motion_callback;
3312         drag_info.finished_callback = &Editor::region_drag_finished_callback;
3313
3314         start_grab (event);
3315
3316         double speed = 1.0;
3317         TimeAxisView* tvp = clicked_trackview;
3318         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3319
3320         if (tv && tv->is_audio_track()) {
3321                 speed = tv->get_diskstream()->speed();
3322         }
3323         
3324         drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3325         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3326         drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3327         drag_info.dest_trackview = drag_info.source_trackview;
3328         // we want a move threshold
3329         drag_info.want_move_threshold = true;
3330         drag_info.brushing = true;
3331         
3332         begin_reversible_command (_("Drag region brush"));
3333 }
3334
3335 void
3336 Editor::possibly_copy_regions_during_grab (GdkEvent* event)
3337 {
3338         if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
3339
3340                 drag_info.want_move_threshold = false; // don't copy again
3341
3342                 /* duplicate the regionview(s) and region(s) */
3343
3344                 vector<RegionView*> new_regionviews;
3345                 
3346                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3347                         RegionView* nrv;
3348                         AudioRegionView* arv;
3349                         
3350                         if ((arv = dynamic_cast<AudioRegionView*>(*i)) == 0) {
3351                                 /* XXX handle MIDI here */
3352                                 continue;
3353                         }
3354
3355                         const boost::shared_ptr<const Region> original = arv->region();
3356                         boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
3357                         boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (region_copy);
3358
3359                         nrv = new AudioRegionView (*arv, ar);
3360                         nrv->get_canvas_group()->show ();
3361
3362                         new_regionviews.push_back (nrv);
3363                 }
3364
3365                 if (new_regionviews.empty()) {
3366                         return;
3367                 }
3368
3369                 /* reset selection to new regionviews. This will not set selection visual status for 
3370                    these regionviews since they don't belong to a track, so do that by hand too.
3371                  */
3372                 
3373                 selection->set (new_regionviews);
3374
3375                 for (vector<RegionView*>::iterator i = new_regionviews.begin(); i != new_regionviews.end(); ++i) {
3376                         (*i)->set_selected (true);
3377                 }
3378
3379                 /* reset drag_info data to reflect the fact that we are dragging the copies */
3380                 
3381                 drag_info.data = new_regionviews.front();
3382
3383                 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3384                 /* 
3385                    sync the canvas to what we think is its current state 
3386                    without it, the canvas seems to 
3387                    "forget" to update properly after the upcoming reparent() 
3388                    ..only if the mouse is in rapid motion at the time of the grab. 
3389                    something to do with regionview creation raking so long?
3390                  */
3391                 track_canvas->update_now();
3392         }
3393 }
3394
3395 bool
3396 Editor::check_region_drag_possible (AudioTimeAxisView** tv)
3397 {
3398         /* Which trackview is this ? */
3399
3400         TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
3401         (*tv) = dynamic_cast<AudioTimeAxisView*>(tvp);
3402
3403         /* The region motion is only processed if the pointer is over
3404            an audio track.
3405         */
3406         
3407         if (!(*tv) || !(*tv)->is_audio_track()) {
3408                 /* To make sure we hide the verbose canvas cursor when the mouse is 
3409                    not held over and audiotrack. 
3410                 */
3411                 hide_verbose_canvas_cursor ();
3412                 return false;
3413         }
3414         
3415         return true;
3416 }
3417
3418 struct RegionSelectionByPosition {
3419     bool operator() (RegionView*a, RegionView* b) {
3420             return a->region()->position () < b->region()->position();
3421     }
3422 };
3423
3424 void
3425 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3426 {
3427         AudioTimeAxisView* tv;
3428         
3429         if (!check_region_drag_possible (&tv)) {
3430                 return;
3431         }
3432
3433         if (!drag_info.move_threshold_passed) {
3434                 return;
3435         }
3436
3437         int dir;
3438
3439         if (drag_info.current_pointer_x - drag_info.grab_x > 0) {
3440                 dir = 1;
3441         } else {
3442                 dir = -1;
3443         }
3444
3445         RegionSelection copy (selection->regions);
3446
3447         RegionSelectionByPosition cmp;
3448         copy.sort (cmp);
3449
3450         for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
3451
3452                 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(*i)->get_time_axis_view());
3453
3454                 if (!atv) {
3455                         continue;
3456                 }
3457
3458                 boost::shared_ptr<Playlist> playlist;
3459
3460                 if ((playlist = atv->playlist()) == 0) {
3461                         continue;
3462                 }
3463
3464                 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
3465                         continue;
3466                 } 
3467
3468                 if (dir > 0) {
3469                         if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) {
3470                                 continue;
3471                         }
3472                 } else {
3473                         if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) {
3474                                 continue;
3475                         }
3476                 }
3477
3478                 
3479                 playlist->shuffle ((*i)->region(), dir);
3480
3481                 drag_info.grab_x = drag_info.current_pointer_x;
3482         }
3483 }
3484
3485 void
3486 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3487 {
3488 }
3489
3490 void
3491 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3492 {
3493         double x_delta;
3494         double y_delta = 0;
3495         nframes64_t pending_region_position = 0;
3496         int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
3497         int32_t visible_y_high = 0, visible_y_low = 512;  //high meaning higher numbered.. not the height on the screen
3498         bool clamp_y_axis = false;
3499         vector<int32_t>  height_list(512) ;
3500         vector<int32_t>::iterator j;
3501         AudioTimeAxisView* tv;
3502
3503         possibly_copy_regions_during_grab (event);
3504
3505         if (!check_region_drag_possible (&tv)) {
3506                 return;
3507         }
3508
3509         original_pointer_order = drag_info.dest_trackview->order;
3510         
3511         /************************************************************
3512                  Y-Delta Computation
3513         ************************************************************/   
3514
3515         if (drag_info.brushing) {
3516                 clamp_y_axis = true;
3517                 pointer_y_span = 0;
3518                 goto y_axis_done;
3519         }
3520
3521         if ((pointer_y_span = (drag_info.dest_trackview->order - tv->order)) != 0) {
3522
3523                 int32_t children = 0, numtracks = 0;
3524                 // XXX hard coding track limit, oh my, so very very bad
3525                 bitset <1024> tracks (0x00);
3526                 /* get a bitmask representing the visible tracks */
3527
3528                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3529                         TimeAxisView *tracklist_timeview;
3530                         tracklist_timeview = (*i);
3531                         AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
3532                         list<TimeAxisView*> children_list;
3533               
3534                         /* zeroes are audio tracks. ones are other types. */
3535               
3536                         if (!atv2->hidden()) {
3537                                 
3538                                 if (visible_y_high < atv2->order) {
3539                                         visible_y_high = atv2->order;
3540                                 }
3541                                 if (visible_y_low > atv2->order) {
3542                                         visible_y_low = atv2->order;
3543                                 }
3544                 
3545                                 if (!atv2->is_audio_track()) {                            
3546                                         tracks = tracks |= (0x01 << atv2->order);
3547                                 }
3548         
3549                                 height_list[atv2->order] = (*i)->current_height();
3550                                 children = 1;
3551                                 if ((children_list = atv2->get_child_list()).size() > 0) {
3552                                         for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) { 
3553                                                 tracks = tracks |= (0x01 << (atv2->order + children));
3554                                                 height_list[atv2->order + children] =  (*j)->current_height();
3555                                                 numtracks++;
3556                                                 children++;     
3557                                         }
3558                                 }
3559                                 numtracks++;        
3560                         }
3561                 }
3562                 /* find the actual span according to the canvas */
3563
3564                 canvas_pointer_y_span = pointer_y_span;
3565                 if (drag_info.dest_trackview->order >= tv->order) {
3566                         int32_t y;
3567                         for (y = tv->order; y < drag_info.dest_trackview->order; y++) {
3568                                 if (height_list[y] == 0 ) {
3569                                         canvas_pointer_y_span--;
3570                                 }
3571                         }
3572                 } else {
3573                         int32_t y;
3574                         for (y = drag_info.dest_trackview->order;y <= tv->order; y++) {
3575                                 if (    height_list[y] == 0 ) {
3576                                         canvas_pointer_y_span++;
3577                                 }
3578                         }
3579                 }
3580
3581                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3582                         RegionView* rv2 = (*i);
3583                         double ix1, ix2, iy1, iy2;
3584                         int32_t n = 0;
3585
3586                         if (rv2->region()->locked()) {
3587                                 continue;
3588                         }
3589
3590                         rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3591                         rv2->get_canvas_group()->i2w (ix1, iy1);
3592                         iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
3593
3594                         TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3595                         RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3596
3597                         if (atv2->order != original_pointer_order) {    
3598                                 /* this isn't the pointer track */      
3599
3600                                 if (canvas_pointer_y_span > 0) {
3601
3602                                         /* moving up the canvas */
3603                                         if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
3604         
3605                                                 int32_t visible_tracks = 0;
3606                                                 while (visible_tracks < canvas_pointer_y_span ) {
3607                                                         visible_tracks++;
3608                   
3609                                                         while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3610                                                                 /* we're passing through a hidden track */
3611                                                                 n--;
3612                                                         }                 
3613                                                 }
3614                  
3615                                                 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {                  
3616                                                         clamp_y_axis = true;
3617                                                 }
3618                     
3619                                         } else {
3620                                                 clamp_y_axis = true;
3621                                         }                 
3622                   
3623                                 } else if (canvas_pointer_y_span < 0) {
3624
3625                                         /*moving down the canvas*/
3626
3627                                         if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3628                     
3629                     
3630                                                 int32_t visible_tracks = 0;
3631                     
3632                                                 while (visible_tracks > canvas_pointer_y_span ) {
3633                                                         visible_tracks--;
3634                       
3635                                                         while (height_list[atv2->order - (visible_tracks - n)] == 0) {             
3636                                                                 n++;
3637                                                         }                
3638                                                 }
3639                                                 if (  tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3640                                                         clamp_y_axis = true;
3641                             
3642                                                 }
3643                                         } else {
3644                           
3645                                                 clamp_y_axis = true;
3646                                         }
3647                                 }               
3648                   
3649                         } else {
3650                       
3651                                 /* this is the pointer's track */
3652                                 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow 
3653                                         clamp_y_axis = true;
3654                                 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3655                                         clamp_y_axis = true;
3656                                 }
3657                         }             
3658                         if (clamp_y_axis) {
3659                                 break;
3660                         }
3661                 }
3662
3663         } else  if (drag_info.dest_trackview == tv) {
3664                 clamp_y_axis = true;
3665         }         
3666
3667   y_axis_done:
3668         if (!clamp_y_axis) {
3669                 drag_info.dest_trackview = tv;        
3670         }
3671           
3672         /************************************************************
3673                         X DELTA COMPUTATION
3674         ************************************************************/
3675
3676         /* compute the amount of pointer motion in frames, and where
3677            the region would be if we moved it by that much.
3678         */
3679         if ( drag_info.move_threshold_passed ) {
3680
3681                 if (drag_info.current_pointer_frame >= drag_info.pointer_frame_offset) {
3682
3683                         nframes64_t sync_frame;
3684                         nframes64_t sync_offset;
3685                         int32_t sync_dir;
3686
3687                         pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3688
3689                         sync_offset = clicked_regionview->region()->sync_offset (sync_dir);
3690
3691                         /* we don't handle a sync point that lies before zero.
3692                          */
3693                         if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) {
3694                                 sync_frame = pending_region_position + (sync_dir*sync_offset);
3695
3696                                 /* we snap if the snap modifier is not enabled.
3697                                  */
3698             
3699                                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3700                                         snap_to (sync_frame);   
3701                                 }
3702             
3703                                 pending_region_position = clicked_regionview->region()->adjust_to_sync (sync_frame);
3704
3705                         } else {
3706                                 pending_region_position = drag_info.last_frame_position;
3707                         }
3708             
3709                 } else {
3710                         pending_region_position = 0;
3711                 }
3712           
3713                 if (pending_region_position > max_frames - clicked_regionview->region()->length()) {
3714                         pending_region_position = drag_info.last_frame_position;
3715                 }
3716
3717                 // printf ("3: pending_region_position= %lu    %lu\n", pending_region_position, drag_info.last_frame_position );
3718
3719                 bool x_move_allowed;
3720                 
3721                 if (Config->get_edit_mode() == Lock) {
3722                         if (drag_info.copy) {
3723                                 x_move_allowed = !drag_info.x_constrained;
3724                         } else {
3725                                 /* in locked edit mode, reverse the usual meaning of x_constrained */
3726                                 x_move_allowed = drag_info.x_constrained;
3727                         }
3728                 } else {
3729                         x_move_allowed = !drag_info.x_constrained;
3730                 }
3731
3732                 if (( pending_region_position != drag_info.last_frame_position) && x_move_allowed ) {
3733
3734                         /* now compute the canvas unit distance we need to move the regionview
3735                            to make it appear at the new location.
3736                         */
3737
3738                         if (pending_region_position > drag_info.last_frame_position) {
3739                                 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3740                         } else {
3741                                 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3742                                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3743
3744                                         RegionView* rv2 = (*i);
3745
3746                                         // If any regionview is at zero, we need to know so we can stop further leftward motion.
3747         
3748                                         double ix1, ix2, iy1, iy2;
3749                                         rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3750                                         rv2->get_canvas_group()->i2w (ix1, iy1);
3751                         
3752                                         if (-x_delta > ix1 + horizontal_adjustment.get_value()) {
3753                                                 x_delta = 0;
3754                                                 pending_region_position = drag_info.last_frame_position;
3755                                                 break;
3756                                         }
3757                                 }
3758
3759                         }
3760                 
3761                         drag_info.last_frame_position = pending_region_position;
3762
3763                 } else {
3764                         x_delta = 0;
3765                 }
3766
3767         } else {
3768                 /* threshold not passed */
3769
3770                 x_delta = 0;
3771         }
3772         
3773         /*************************************************************
3774                         PREPARE TO MOVE
3775         ************************************************************/
3776
3777         if (x_delta == 0 && (pointer_y_span == 0)) {
3778                 /* haven't reached next snap point, and we're not switching
3779                    trackviews. nothing to do.
3780                 */
3781                 return;
3782         }
3783
3784         /*************************************************************
3785                         MOTION                                                                
3786         ************************************************************/
3787         bool do_move = true;
3788         if (drag_info.first_move) {
3789                 if (!drag_info.move_threshold_passed) {
3790                         do_move = false;
3791                 }
3792         }
3793
3794         if (do_move) {
3795
3796                 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3797                 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3798                 
3799                 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3800             
3801                         RegionView* rv = (*i);
3802                         double ix1, ix2, iy1, iy2;
3803                         int32_t temp_pointer_y_span = pointer_y_span;
3804
3805                         if (rv->region()->locked()) {
3806                                 continue;
3807                         }
3808
3809                         /* get item BBox, which will be relative to parent. so we have
3810                            to query on a child, then convert to world coordinates using
3811                            the parent.
3812                         */
3813
3814                         rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3815                         rv->get_canvas_group()->i2w (ix1, iy1);
3816
3817                         /* for evaluation of the track position of iy1, we have to adjust 
3818                            to allow for the vertical scrolling adjustment and the height of the timebars.
3819                         */
3820
3821                         iy1 += get_trackview_group_vertical_offset ();
3822                         if (drag_info.first_move) {
3823
3824                                 // hide any dependent views 
3825         
3826                                 rv->get_time_axis_view().hide_dependent_views (*rv);
3827
3828                                 /* 
3829                                    reparent to a non scrolling group so that we can keep the 
3830                                    region selection above all time axis views.
3831                                    reparenting means we have to move the rv as the two 
3832                                    parent groups have different coordinates.
3833                                 */
3834
3835                                 rv->get_canvas_group()->property_y() = iy1 - 1;
3836                                 rv->get_canvas_group()->reparent(*_region_motion_group);
3837
3838                                 rv->fake_set_opaque (true);
3839                         }
3840
3841                         TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3842                         AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3843                         AudioTimeAxisView* temp_atv;
3844
3845                         if ((pointer_y_span != 0) && !clamp_y_axis) {
3846                                 y_delta = 0;
3847                                 int32_t x = 0;
3848                                 for (j = height_list.begin(); j!= height_list.end(); j++) {     
3849                                         if (x == canvas_atv->order) {
3850                                                 /* we found the track the region is on */
3851                                                 if (x != original_pointer_order) {
3852                                                         /*this isn't from the same track we're dragging from */
3853                                                         temp_pointer_y_span = canvas_pointer_y_span;
3854                                                 }                 
3855                                                 while (temp_pointer_y_span > 0) {
3856                                                         /* we're moving up canvas-wise,
3857                                                            so  we need to find the next track height
3858                                                         */
3859                                                         if (j != height_list.begin()) {           
3860                                                                 j--;
3861                                                         }
3862                                                         if (x != original_pointer_order) { 
3863                                                                 /* we're not from the dragged track, so ignore hidden tracks. */              
3864                                                                 if ((*j) == 0) {
3865                                                                         temp_pointer_y_span++;
3866                                                                 }
3867                                                         }          
3868                                                         y_delta -= (*j);        
3869                                                         temp_pointer_y_span--;  
3870                                                 }
3871
3872                                                 while (temp_pointer_y_span < 0) {                 
3873                                                         y_delta += (*j);
3874                                                         if (x != original_pointer_order) { 
3875                                                                 if ((*j) == 0) {
3876                                                                         temp_pointer_y_span--;
3877                                                                 }
3878                                                         }          
3879                     
3880                                                         if (j != height_list.end()) {                 
3881                                                                 j++;
3882                                                         }
3883                                                         temp_pointer_y_span++;
3884                                                 }
3885                                                 /* find out where we'll be when we move and set height accordingly */
3886                   
3887                                                 tvp2 = trackview_by_y_position (iy1 + y_delta);
3888                                                 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3889                                                 rv->set_height (temp_atv->current_height());
3890         
3891                                                 /*   if you un-comment the following, the region colours will follow the track colours whilst dragging,
3892                                                      personally, i think this can confuse things, but never mind.
3893                                                 */
3894                                   
3895                                                 //const GdkColor& col (temp_atv->view->get_region_color());
3896                                                 //rv->set_color (const_cast<GdkColor&>(col));
3897                                                 break;          
3898                                         }
3899                                         x++;
3900                                 }
3901                         }
3902
3903                         if (drag_info.brushing) {
3904                                 mouse_brush_insert_region (rv, pending_region_position);
3905                         } else {
3906                                 rv->move (x_delta, y_delta);
3907                         }
3908
3909                 } /* foreach region */
3910
3911         } /* if do_move */
3912
3913         if (drag_info.first_move && drag_info.move_threshold_passed) {
3914                 cursor_group->raise_to_top();
3915                 drag_info.first_move = false;
3916         }
3917
3918         if (x_delta != 0 && !drag_info.brushing) {
3919                 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3920         }
3921
3922
3923 void
3924 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3925 {
3926         bool nocommit = true;
3927         vector<RegionView*> copies;
3928         RouteTimeAxisView* source_tv;
3929         boost::shared_ptr<Diskstream> ds;
3930         boost::shared_ptr<Playlist> from_playlist;
3931         vector<RegionView*> new_selection;
3932         typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
3933         PlaylistSet modified_playlists;
3934         PlaylistSet frozen_playlists;
3935         list <sigc::connection> modified_playlist_connections;
3936         pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
3937         nframes64_t drag_delta;
3938         bool changed_tracks, changed_position;
3939
3940         /* first_move is set to false if the regionview has been moved in the 
3941            motion handler. 
3942         */
3943
3944         if (drag_info.first_move) {
3945                 /* just a click */
3946                 goto out;
3947         }
3948
3949         nocommit = false;
3950
3951         if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
3952                 selection->set (pre_drag_region_selection);
3953                 pre_drag_region_selection.clear ();
3954         }
3955
3956         if (drag_info.brushing) {
3957                 /* all changes were made during motion event handlers */
3958                 
3959                 if (drag_info.copy) {
3960                         for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3961                                 copies.push_back (*i);
3962                         }
3963                 }
3964
3965                 goto out;
3966         }
3967
3968         char* op_string;
3969
3970         /* reverse this here so that we have the correct logic to finalize
3971            the drag.
3972         */
3973         
3974         if (Config->get_edit_mode() == Lock && !drag_info.copy) {
3975                 drag_info.x_constrained = !drag_info.x_constrained;
3976         }
3977
3978         if (drag_info.copy) {
3979                 if (drag_info.x_constrained) {
3980                         op_string = _("fixed time region copy");
3981                 } else {
3982                         op_string = _("region copy");
3983                 } 
3984         } else {
3985                 if (drag_info.x_constrained) {
3986                         op_string = _("fixed time region drag");
3987                 } else {
3988                         op_string = _("region drag");
3989                 }
3990         }
3991
3992         begin_reversible_command (op_string);
3993         changed_position = (drag_info.last_frame_position != (nframes64_t) (clicked_regionview->region()->position()));
3994         changed_tracks = (trackview_by_y_position (drag_info.current_pointer_y) != &clicked_regionview->get_time_axis_view());
3995
3996         drag_delta = clicked_regionview->region()->position() - drag_info.last_frame_position;
3997
3998         track_canvas->update_now ();
3999
4000         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
4001                         
4002                 RegionView* rv = (*i);              
4003                 double ix1, ix2, iy1, iy2;
4004                 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
4005                 rv->get_canvas_group()->i2w (ix1, iy1);
4006                 iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
4007
4008                 TimeAxisView* dest_tv = trackview_by_y_position (iy1);
4009                 AudioTimeAxisView* dest_atv = dynamic_cast<AudioTimeAxisView*>(dest_tv);
4010                 nframes64_t where;
4011
4012                 if (rv->region()->locked()) {
4013                         ++i;
4014                         continue;
4015                 }
4016
4017                 if (changed_position && !drag_info.x_constrained) {
4018                         where = rv->region()->position() - drag_delta;
4019                 } else {
4020                         where = rv->region()->position();
4021                 }
4022                         
4023                 boost::shared_ptr<Region> new_region;
4024
4025                 if (drag_info.copy) {
4026                         /* we already made a copy */
4027                         new_region = rv->region();
4028
4029                         /* undo the previous hide_dependent_views so that xfades don't
4030                            disappear on copying regions 
4031                         */
4032                 
4033                         //rv->get_time_axis_view().reveal_dependent_views (*rv);
4034                 
4035                 } else if (changed_tracks) {
4036                         new_region = RegionFactory::create (rv->region());
4037                 }
4038
4039                 if (changed_tracks || drag_info.copy) {
4040
4041                         boost::shared_ptr<Playlist> to_playlist = dest_atv->playlist();
4042
4043                         latest_regionviews.clear ();
4044
4045                         sigc::connection c = dest_atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4046                         
4047                         insert_result = modified_playlists.insert (to_playlist);
4048                         if (insert_result.second) {
4049                                 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
4050                         }
4051
4052                         to_playlist->add_region (new_region, where);
4053
4054                         c.disconnect ();
4055                                                               
4056                         if (!latest_regionviews.empty()) {
4057                                 // XXX why just the first one ? we only expect one
4058                                 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
4059                                 new_selection.push_back (latest_regionviews.front());
4060                         }
4061
4062                 } else {
4063                         /* 
4064                            motion on the same track. plonk the previously reparented region 
4065                            back to its original canvas group (its streamview).
4066                            No need to do anything for copies as they are fake regions which will be deleted.
4067                         */
4068
4069                         RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (dest_atv);
4070                         rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
4071                         rv->get_canvas_group()->property_y() = 0;
4072                   
4073                         /* just change the model */
4074                         
4075                         boost::shared_ptr<Playlist> playlist = dest_atv->playlist();
4076
4077                         insert_result = modified_playlists.insert (playlist);
4078                         if (insert_result.second) {
4079                                 session->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
4080                         }
4081                         /* freeze to avoid lots of relayering in the case of a multi-region drag */
4082                         frozen_insert_result = frozen_playlists.insert(playlist);
4083                         if (frozen_insert_result.second) {
4084                                 playlist->freeze();
4085                         }
4086
4087                         rv->region()->set_position (where, (void*) this);
4088                 }
4089
4090                 if (changed_tracks && !drag_info.copy) {
4091
4092                         /* get the playlist where this drag started. we can't use rv->region()->playlist()
4093                            because we may have copied the region and it has not been attached to a playlist.
4094                         */
4095                         
4096                         assert ((source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view())));
4097                         assert ((ds = source_tv->get_diskstream()));
4098                         assert ((from_playlist = ds->playlist()));
4099
4100                         /* moved to a different audio track, without copying */
4101
4102                         /* the region that used to be in the old playlist is not
4103                            moved to the new one - we use a copy of it. as a result,
4104                            any existing editor for the region should no longer be
4105                            visible.
4106                         */ 
4107             
4108                         rv->hide_region_editor();
4109                         rv->fake_set_opaque (false);
4110                         
4111                         /* remove the region from the old playlist */
4112
4113                         insert_result = modified_playlists.insert (from_playlist);
4114                         if (insert_result.second) {
4115                                 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
4116                         }
4117
4118                         from_playlist->remove_region ((rv->region()));
4119                         
4120                         /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
4121                            was selected in all of them, then removing it from a playlist will have removed all
4122                            trace of it from the selection (i.e. there were N regions selected, we removed 1,
4123                            but since its the same playlist for N tracks, all N tracks updated themselves, removed the
4124                            corresponding regionview, and the selection is now empty).
4125
4126                            this could have invalidated any and all iterators into the region selection.
4127
4128                            the heuristic we use here is: if the region selection is empty, break out of the loop
4129                            here. if the region selection is not empty, then restart the loop because we know that
4130                            we must have removed at least the region(view) we've just been working on as well as any
4131                            that we processed on previous iterations.
4132
4133                            EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
4134                            we can just iterate.
4135                         */
4136
4137                         if (selection->regions.empty()) {
4138                                 break;
4139                         } else { 
4140                                 i = selection->regions.by_layer().begin();
4141                         }
4142
4143                 } else {
4144                         ++i;
4145                 }
4146                 
4147                 if (drag_info.copy) {
4148                         copies.push_back (rv);
4149                 }
4150         }
4151         
4152         if (new_selection.empty()) {
4153                 if (drag_info.copy) {
4154                         /* the region(view)s that are selected and being dragged around
4155                            are copies and do not belong to any track. remove them
4156                            from the selection right here.
4157                         */
4158                         selection->clear_regions();
4159                 }
4160         } else {
4161                 /* this will clear any existing selection that would have been
4162                    cleared in the other clause above
4163                 */
4164                 selection->set (new_selection);
4165         }
4166
4167         for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
4168                 (*p)->thaw();
4169         }
4170                         
4171   out:
4172         if (!nocommit) {
4173                 for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
4174                         session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));      
4175                 }
4176                 commit_reversible_command ();
4177         }
4178
4179         for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
4180                 delete *x;
4181         }
4182
4183 }
4184
4185 void
4186 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
4187 {
4188         /* Either add to or set the set the region selection, unless
4189            this is an alignment click (control used)
4190         */
4191         
4192         if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
4193                 TimeAxisView* tv = &rv.get_time_axis_view();
4194                 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
4195                 double speed = 1.0;
4196                 if (atv && atv->is_audio_track()) {
4197                         speed = atv->get_diskstream()->speed();
4198                 }
4199
4200                 nframes64_t where = get_preferred_edit_position();
4201
4202                 if (where >= 0) {
4203
4204                         if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
4205                                 
4206                                 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
4207                                 
4208                         } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
4209                                 
4210                                 align_region (rv.region(), End, (nframes64_t) (where * speed));
4211                                 
4212                         } else {
4213                                 
4214                                 align_region (rv.region(), Start, (nframes64_t) (where * speed));
4215                         }
4216                 }
4217         }
4218 }
4219
4220 void
4221 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos) 
4222 {
4223         char buf[128];
4224         SMPTE::Time smpte;
4225         BBT_Time bbt;
4226         int hours, mins;
4227         nframes64_t frame_rate;
4228         float secs;
4229
4230         if (session == 0) {
4231                 return;
4232         }
4233
4234         AudioClock::Mode m;
4235
4236         if (Profile->get_sae() || Profile->get_small_screen()) {
4237                 m = ARDOUR_UI::instance()->primary_clock.mode();
4238         } else {
4239                 m = ARDOUR_UI::instance()->secondary_clock.mode();
4240         }
4241
4242         switch (m) {
4243         case AudioClock::BBT:
4244                 session->bbt_time (frame, bbt);
4245                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
4246                 break;
4247                 
4248         case AudioClock::SMPTE:
4249                 session->smpte_time (frame, smpte);
4250                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4251                 break;
4252
4253         case AudioClock::MinSec:
4254                 /* XXX this is copied from show_verbose_duration_cursor() */
4255                 frame_rate = session->frame_rate();
4256                 hours = frame / (frame_rate * 3600);
4257                 frame = frame % (frame_rate * 3600);
4258                 mins = frame / (frame_rate * 60);
4259                 frame = frame % (frame_rate * 60);
4260                 secs = (float) frame / (float) frame_rate;
4261                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4262                 break;
4263
4264         default:
4265                 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
4266                 break;
4267         }
4268
4269         if (xpos >= 0 && ypos >=0) {
4270                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4271         }
4272         else {
4273                 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset - horizontal_adjustment.get_value(), drag_info.current_pointer_y + offset - vertical_adjustment.get_value() + canvas_timebars_vsize);
4274         }
4275         show_verbose_canvas_cursor ();
4276 }
4277
4278 void
4279 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos) 
4280 {
4281         char buf[128];
4282         SMPTE::Time smpte;
4283         BBT_Time sbbt;
4284         BBT_Time ebbt;
4285         int hours, mins;
4286         nframes64_t distance, frame_rate;
4287         float secs;
4288         Meter meter_at_start(session->tempo_map().meter_at(start));
4289
4290         if (session == 0) {
4291                 return;
4292         }
4293
4294         AudioClock::Mode m;
4295
4296         if (Profile->get_sae() || Profile->get_small_screen()) {
4297                 m = ARDOUR_UI::instance()->primary_clock.mode ();
4298         } else {
4299                 m = ARDOUR_UI::instance()->secondary_clock.mode ();
4300         }
4301
4302         switch (m) {
4303         case AudioClock::BBT:
4304                 session->bbt_time (start, sbbt);
4305                 session->bbt_time (end, ebbt);
4306
4307                 /* subtract */
4308                 /* XXX this computation won't work well if the
4309                 user makes a selection that spans any meter changes.
4310                 */
4311
4312                 ebbt.bars -= sbbt.bars;
4313                 if (ebbt.beats >= sbbt.beats) {
4314                         ebbt.beats -= sbbt.beats;
4315                 } else {
4316                         ebbt.bars--;
4317                         ebbt.beats =  int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
4318                 }
4319                 if (ebbt.ticks >= sbbt.ticks) {
4320                         ebbt.ticks -= sbbt.ticks;
4321                 } else {
4322                         ebbt.beats--;
4323                         ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
4324                 }
4325                 
4326                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
4327                 break;
4328                 
4329         case AudioClock::SMPTE:
4330                 session->smpte_duration (end - start, smpte);
4331                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4332                 break;
4333
4334         case AudioClock::MinSec:
4335                 /* XXX this stuff should be elsewhere.. */
4336                 distance = end - start;
4337                 frame_rate = session->frame_rate();
4338                 hours = distance / (frame_rate * 3600);
4339                 distance = distance % (frame_rate * 3600);
4340                 mins = distance / (frame_rate * 60);
4341                 distance = distance % (frame_rate * 60);
4342                 secs = (float) distance / (float) frame_rate;
4343                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4344                 break;
4345
4346         default:
4347                 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
4348                 break;
4349         }
4350
4351         if (xpos >= 0 && ypos >=0) {
4352                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4353         }
4354         else {
4355                 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4356         }
4357
4358         show_verbose_canvas_cursor ();
4359 }
4360
4361 void
4362 Editor::collect_new_region_view (RegionView* rv)
4363 {
4364         latest_regionviews.push_back (rv);
4365 }
4366
4367 void
4368 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
4369 {
4370         if (clicked_regionview == 0) {
4371                 return;
4372         }
4373
4374         /* lets try to create new Region for the selection */
4375
4376         vector<boost::shared_ptr<AudioRegion> > new_regions;
4377         create_region_from_selection (new_regions);
4378
4379         if (new_regions.empty()) {
4380                 return;
4381         }
4382
4383         /* XXX fix me one day to use all new regions */
4384         
4385         boost::shared_ptr<Region> region (new_regions.front());
4386
4387         /* add it to the current stream/playlist.
4388
4389            tricky: the streamview for the track will add a new regionview. we will
4390            catch the signal it sends when it creates the regionview to
4391            set the regionview we want to then drag.
4392         */
4393         
4394         latest_regionviews.clear();
4395         sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4396         
4397         /* A selection grab currently creates two undo/redo operations, one for 
4398            creating the new region and another for moving it.
4399         */
4400
4401         begin_reversible_command (_("selection grab"));
4402
4403         boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
4404
4405         XMLNode *before = &(playlist->get_state());
4406         clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
4407         XMLNode *after = &(playlist->get_state());
4408         session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4409
4410         commit_reversible_command ();
4411         
4412         c.disconnect ();
4413         
4414         if (latest_regionviews.empty()) {
4415                 /* something went wrong */
4416                 return;
4417         }
4418
4419         /* we need to deselect all other regionviews, and select this one
4420            i'm ignoring undo stuff, because the region creation will take care of it 
4421         */
4422         selection->set (latest_regionviews);
4423         
4424         drag_info.item = latest_regionviews.front()->get_canvas_group();
4425         drag_info.data = latest_regionviews.front();
4426         drag_info.motion_callback = &Editor::region_drag_motion_callback;
4427         drag_info.finished_callback = &Editor::region_drag_finished_callback;
4428
4429         start_grab (event);
4430         
4431         drag_info.source_trackview = clicked_trackview;
4432         drag_info.dest_trackview = drag_info.source_trackview;
4433         drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4434         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4435         
4436         show_verbose_time_cursor (drag_info.last_frame_position, 10);
4437 }
4438
4439 void
4440 Editor::cancel_selection ()
4441 {
4442         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4443                 (*i)->hide_selection ();
4444         }
4445         selection->clear ();
4446         clicked_selection = 0;
4447 }       
4448
4449 void
4450 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4451 {
4452         nframes64_t start = 0;
4453         nframes64_t end = 0;
4454
4455         if (session == 0) {
4456                 return;
4457         }
4458
4459         drag_info.item = item;
4460         drag_info.motion_callback = &Editor::drag_selection;
4461         drag_info.finished_callback = &Editor::end_selection_op;
4462
4463         selection_op = op;
4464
4465         switch (op) {
4466         case CreateSelection:
4467                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4468                         drag_info.copy = true;
4469                 } else {
4470                         drag_info.copy = false;
4471                 }
4472                 start_grab (event, selector_cursor);
4473                 break;
4474
4475         case SelectionStartTrim:
4476                 if (clicked_trackview) {
4477                         clicked_trackview->order_selection_trims (item, true);
4478                 } 
4479                 start_grab (event, trimmer_cursor);
4480                 start = selection->time[clicked_selection].start;
4481                 drag_info.pointer_frame_offset = drag_info.grab_frame - start;  
4482                 break;
4483                 
4484         case SelectionEndTrim:
4485                 if (clicked_trackview) {
4486                         clicked_trackview->order_selection_trims (item, false);
4487                 }
4488                 start_grab (event, trimmer_cursor);
4489                 end = selection->time[clicked_selection].end;
4490                 drag_info.pointer_frame_offset = drag_info.grab_frame - end;    
4491                 break;
4492
4493         case SelectionMove:
4494                 start = selection->time[clicked_selection].start;
4495                 start_grab (event);
4496                 drag_info.pointer_frame_offset = drag_info.grab_frame - start;  
4497                 break;
4498         }
4499
4500         if (selection_op == SelectionMove) {
4501                 show_verbose_time_cursor(start, 10);    
4502         } else {
4503                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4504         }
4505 }
4506
4507 void
4508 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4509 {
4510         nframes64_t start = 0;
4511         nframes64_t end = 0;
4512         nframes64_t length;
4513         nframes64_t pending_position;
4514
4515         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4516                 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4517         } else {
4518                 pending_position = 0;
4519         }
4520         
4521         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4522                 snap_to (pending_position);
4523         }
4524
4525         /* only alter selection if the current frame is 
4526            different from the last frame position (adjusted)
4527          */
4528         
4529         if (pending_position == drag_info.last_pointer_frame) return;
4530         
4531         switch (selection_op) {
4532         case CreateSelection:
4533                 
4534                 if (drag_info.first_move) {
4535                         snap_to (drag_info.grab_frame);
4536                 }
4537                 
4538                 if (pending_position < drag_info.grab_frame) {
4539                         start = pending_position;
4540                         end = drag_info.grab_frame;
4541                 } else {
4542                         end = pending_position;
4543                         start = drag_info.grab_frame;
4544                 }
4545                 
4546                 /* first drag: Either add to the selection
4547                    or create a new selection->
4548                 */
4549                 
4550                 if (drag_info.first_move) {
4551                         
4552                         begin_reversible_command (_("range selection"));
4553                         
4554                         if (drag_info.copy) {
4555                                 /* adding to the selection */
4556                                 clicked_selection = selection->add (start, end);
4557                                 drag_info.copy = false;
4558                         } else {
4559                                 /* new selection-> */
4560                                 clicked_selection = selection->set (clicked_trackview, start, end);
4561                         }
4562                 } 
4563                 break;
4564                 
4565         case SelectionStartTrim:
4566                 
4567                 if (drag_info.first_move) {
4568                         begin_reversible_command (_("trim selection start"));
4569                 }
4570                 
4571                 start = selection->time[clicked_selection].start;
4572                 end = selection->time[clicked_selection].end;
4573
4574                 if (pending_position > end) {
4575                         start = end;
4576                 } else {
4577                         start = pending_position;
4578                 }
4579                 break;
4580                 
4581         case SelectionEndTrim:
4582                 
4583                 if (drag_info.first_move) {
4584                         begin_reversible_command (_("trim selection end"));
4585                 }
4586                 
4587                 start = selection->time[clicked_selection].start;
4588                 end = selection->time[clicked_selection].end;
4589
4590                 if (pending_position < start) {
4591                         end = start;
4592                 } else {
4593                         end = pending_position;
4594                 }
4595                 
4596                 break;
4597                 
4598         case SelectionMove:
4599                 
4600                 if (drag_info.first_move) {
4601                         begin_reversible_command (_("move selection"));
4602                 }
4603                 
4604                 start = selection->time[clicked_selection].start;
4605                 end = selection->time[clicked_selection].end;
4606                 
4607                 length = end - start;
4608                 
4609                 start = pending_position;
4610                 snap_to (start);
4611                 
4612                 end = start + length;
4613                 
4614                 break;
4615         }
4616         
4617         if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4618                 start_canvas_autoscroll (1, 0);
4619         }
4620
4621         if (start != end) {
4622                 selection->replace (clicked_selection, start, end);
4623         }
4624
4625         drag_info.last_pointer_frame = pending_position;
4626         drag_info.first_move = false;
4627
4628         if (selection_op == SelectionMove) {
4629                 show_verbose_time_cursor(start, 10);    
4630         } else {
4631                 show_verbose_time_cursor(pending_position, 10); 
4632         }
4633 }
4634
4635 void
4636 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4637 {
4638         if (!drag_info.first_move) {
4639                 drag_selection (item, event);
4640                 /* XXX this is not object-oriented programming at all. ick */
4641                 if (selection->time.consolidate()) {
4642                         selection->TimeChanged ();
4643                 }
4644                 commit_reversible_command ();
4645         } else {
4646                 /* just a click, no pointer movement.*/
4647
4648                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4649
4650                         selection->clear_time();
4651
4652                 } 
4653         }
4654
4655         /* XXX what happens if its a music selection? */
4656         session->set_audio_range (selection->time);
4657         stop_canvas_autoscroll ();
4658 }
4659
4660 void
4661 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4662 {
4663         double speed = 1.0;
4664         TimeAxisView* tvp = clicked_trackview;
4665         AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4666
4667         if (tv && tv->is_audio_track()) {
4668                 speed = tv->get_diskstream()->speed();
4669         }
4670         
4671         nframes64_t region_start = (nframes64_t) (clicked_regionview->region()->position() / speed);
4672         nframes64_t region_end = (nframes64_t) (clicked_regionview->region()->last_frame() / speed);
4673         nframes64_t region_length = (nframes64_t) (clicked_regionview->region()->length() / speed);
4674
4675         //drag_info.item = clicked_regionview->get_name_highlight();
4676         drag_info.item = item;
4677         drag_info.motion_callback = &Editor::trim_motion_callback;
4678         drag_info.finished_callback = &Editor::trim_finished_callback;
4679
4680         start_grab (event, trimmer_cursor);
4681         
4682         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4683                 trim_op = ContentsTrim;
4684         } else {
4685                 /* These will get overridden for a point trim.*/
4686                 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4687                         /* closer to start */
4688                         trim_op = StartTrim;
4689                 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4690                         /* closer to end */
4691                         trim_op = EndTrim;
4692                 }
4693         }
4694
4695         switch (trim_op) {
4696         case StartTrim:
4697                 show_verbose_time_cursor(region_start, 10);     
4698                 break;
4699         case EndTrim:
4700                 show_verbose_time_cursor(region_end, 10);       
4701                 break;
4702         case ContentsTrim:
4703                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4704                 break;
4705         }
4706 }
4707
4708 void
4709 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4710 {
4711         RegionView* rv = clicked_regionview;
4712         nframes64_t frame_delta = 0;
4713         bool left_direction;
4714         bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4715
4716         /* snap modifier works differently here..
4717            its' current state has to be passed to the 
4718            various trim functions in order to work properly 
4719         */ 
4720
4721         double speed = 1.0;
4722         TimeAxisView* tvp = clicked_trackview;
4723         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4724         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4725
4726         if (tv && tv->is_audio_track()) {
4727                 speed = tv->get_diskstream()->speed();
4728         }
4729         
4730         if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4731                 left_direction = true;
4732         } else {
4733                 left_direction = false;
4734         }
4735
4736         if (obey_snap) {
4737                 snap_to (drag_info.current_pointer_frame);
4738         }
4739
4740         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4741                 return;
4742         }
4743
4744         if (drag_info.first_move) {
4745         
4746                 string trim_type;
4747
4748                 switch (trim_op) {
4749                 case StartTrim:
4750                         trim_type = "Region start trim";
4751                         break;
4752                 case EndTrim:
4753                         trim_type = "Region end trim";
4754                         break;
4755                 case ContentsTrim:
4756                         trim_type = "Region content trim";
4757                         break;
4758                 }
4759
4760                 begin_reversible_command (trim_type);
4761
4762                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4763                         (*i)->fake_set_opaque(false);                   
4764                         (*i)->region()->freeze ();
4765                 
4766                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4767                         if (arv)
4768                                 arv->temporarily_hide_envelope ();
4769
4770                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4771                         insert_result = motion_frozen_playlists.insert (pl);
4772                         if (insert_result.second) {
4773                                 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4774                         }
4775                 }
4776         }
4777
4778         if (left_direction) {
4779                 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4780         } else {
4781                 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4782         }
4783
4784         switch (trim_op) {              
4785         case StartTrim:
4786                 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4787                         break;
4788                 } else {
4789                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4790                                 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4791                         }
4792                         break;
4793                 }
4794                 
4795         case EndTrim:
4796                 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes64_t) (rv->region()->last_frame()/speed))) {
4797                         break;
4798                 } else {
4799                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4800                                 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4801                         }
4802                         break;
4803                 }
4804                 
4805         case ContentsTrim:
4806                 {
4807                         bool swap_direction = false;
4808
4809                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4810                                 swap_direction = true;
4811                         }
4812                         
4813                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4814                              i != selection->regions.by_layer().end(); ++i)
4815                         {
4816                                 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4817                         }
4818                 }
4819                 break;
4820         }
4821
4822         switch (trim_op) {
4823         case StartTrim:
4824                 show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);   
4825                 break;
4826         case EndTrim:
4827                 show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10); 
4828                 break;
4829         case ContentsTrim:
4830                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4831                 break;
4832         }
4833
4834         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4835         drag_info.first_move = false;
4836 }
4837
4838 void
4839 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4840 {
4841         boost::shared_ptr<Region> region (rv.region());
4842
4843         if (region->locked()) {
4844                 return;
4845         }
4846
4847         nframes64_t new_bound;
4848
4849         double speed = 1.0;
4850         TimeAxisView* tvp = clicked_trackview;
4851         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4852
4853         if (tv && tv->is_audio_track()) {
4854                 speed = tv->get_diskstream()->speed();
4855         }
4856         
4857         if (left_direction) {
4858                 if (swap_direction) {
4859                         new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4860                 } else {
4861                         new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4862                 }
4863         } else {
4864                 if (swap_direction) {
4865                         new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4866                 } else {
4867                         new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4868                 }
4869         }
4870
4871         if (obey_snap) {
4872                 snap_to (new_bound);
4873         }
4874         region->trim_start ((nframes64_t) (new_bound * speed), this);   
4875         rv.region_changed (StartChanged);
4876 }
4877
4878 void
4879 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
4880 {
4881         boost::shared_ptr<Region> region (rv.region()); 
4882
4883         if (region->locked()) {
4884                 return;
4885         }
4886
4887         nframes64_t new_bound;
4888
4889         double speed = 1.0;
4890         TimeAxisView* tvp = clicked_trackview;
4891         AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4892
4893         if (tv && tv->is_audio_track()) {
4894                 speed = tv->get_diskstream()->speed();
4895         }
4896         
4897         if (left_direction) {
4898                 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4899         } else {
4900                 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4901         }
4902
4903         if (obey_snap) {
4904                 snap_to (new_bound, (left_direction ? 0 : 1));  
4905         }
4906
4907         region->trim_front ((nframes64_t) (new_bound * speed), this);
4908
4909         rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4910 }
4911
4912 void
4913 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
4914 {
4915         boost::shared_ptr<Region> region (rv.region());
4916
4917         if (region->locked()) {
4918                 return;
4919         }
4920
4921         nframes64_t new_bound;
4922
4923         double speed = 1.0;
4924         TimeAxisView* tvp = clicked_trackview;
4925         AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4926
4927         if (tv && tv->is_audio_track()) {
4928                 speed = tv->get_diskstream()->speed();
4929         }
4930         
4931         if (left_direction) {
4932                 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
4933         } else {
4934                 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
4935         }
4936
4937         if (obey_snap) {
4938                 snap_to (new_bound);
4939         }
4940         region->trim_end ((nframes64_t) (new_bound * speed), this);
4941         rv.region_changed (LengthChanged);
4942 }
4943         
4944 void
4945 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4946 {
4947         if (!drag_info.first_move) {
4948                 trim_motion_callback (item, event);
4949                 
4950                 if (!selection->selected (clicked_regionview)) {
4951                         thaw_region_after_trim (*clicked_regionview);           
4952                 } else {
4953                         
4954                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4955                              i != selection->regions.by_layer().end(); ++i)
4956                         {
4957                                 thaw_region_after_trim (**i);
4958                                 (*i)->fake_set_opaque (true);
4959                         }
4960                 }
4961                 
4962                 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4963                         //(*p)->thaw ();
4964                         session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4965                 }
4966                 
4967                 motion_frozen_playlists.clear ();
4968
4969                 commit_reversible_command();
4970         } else {
4971                 /* no mouse movement */
4972                 point_trim (event);
4973         }
4974 }
4975
4976 void
4977 Editor::point_trim (GdkEvent* event)
4978 {
4979         RegionView* rv = clicked_regionview;
4980         nframes64_t new_bound = drag_info.current_pointer_frame;
4981
4982         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4983                 snap_to (new_bound);
4984         }
4985
4986         /* Choose action dependant on which button was pressed */
4987         switch (event->button.button) {
4988         case 1:
4989                 trim_op = StartTrim;
4990                 begin_reversible_command (_("Start point trim"));
4991
4992                 if (selection->selected (rv)) {
4993
4994                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4995                              i != selection->regions.by_layer().end(); ++i)
4996                         {
4997                                 if (!(*i)->region()->locked()) {
4998                                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4999                                         XMLNode &before = pl->get_state();
5000                                         (*i)->region()->trim_front (new_bound, this);   
5001                                         XMLNode &after = pl->get_state();
5002                                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5003                                 }
5004                         }
5005
5006                 } else {
5007
5008                         if (!rv->region()->locked()) {
5009                                 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5010                                 XMLNode &before = pl->get_state();
5011                                 rv->region()->trim_front (new_bound, this);     
5012                                 XMLNode &after = pl->get_state();
5013                                 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5014                         }
5015                 }
5016
5017                 commit_reversible_command();
5018         
5019                 break;
5020         case 2:
5021                 trim_op = EndTrim;
5022                 begin_reversible_command (_("End point trim"));
5023
5024                 if (selection->selected (rv)) {
5025                         
5026                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
5027                         {
5028                                 if (!(*i)->region()->locked()) {
5029                                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5030                                         XMLNode &before = pl->get_state();
5031                                         (*i)->region()->trim_end (new_bound, this);
5032                                         XMLNode &after = pl->get_state();
5033                                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5034                                 }
5035                         }
5036
5037                 } else {
5038
5039                         if (!rv->region()->locked()) {
5040                                 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5041                                 XMLNode &before = pl->get_state();
5042                                 rv->region()->trim_end (new_bound, this);
5043                                 XMLNode &after = pl->get_state();
5044                                 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
5045                         }
5046                 }
5047
5048                 commit_reversible_command();
5049         
5050                 break;
5051         default:
5052                 break;
5053         }
5054 }
5055
5056 void
5057 Editor::thaw_region_after_trim (RegionView& rv)
5058 {
5059         boost::shared_ptr<Region> region (rv.region());
5060
5061         if (region->locked()) {
5062                 return;
5063         }
5064
5065         region->thaw (_("trimmed region"));
5066         XMLNode &after = region->playlist()->get_state();
5067         session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
5068
5069         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
5070         if (arv)
5071                 arv->unhide_envelope ();
5072 }
5073
5074 void
5075 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
5076 {
5077         Marker* marker;
5078         bool is_start;
5079
5080         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
5081                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
5082                 /*NOTREACHED*/
5083         }
5084
5085         Location* location = find_location_from_marker (marker, is_start);      
5086         location->set_hidden (true, this);
5087 }
5088
5089
5090 void
5091 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
5092 {
5093         if (session == 0) {
5094                 return;
5095         }
5096
5097         drag_info.item = item;
5098         drag_info.motion_callback = &Editor::drag_range_markerbar_op;
5099         drag_info.finished_callback = &Editor::end_range_markerbar_op;
5100
5101         range_marker_op = op;
5102
5103         if (!temp_location) {
5104                 temp_location = new Location;
5105         }
5106         
5107         switch (op) {
5108         case CreateRangeMarker:
5109         case CreateTransportMarker:
5110         case CreateCDMarker:
5111         
5112                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
5113                         drag_info.copy = true;
5114                 } else {
5115                         drag_info.copy = false;
5116                 }
5117                 start_grab (event, selector_cursor);
5118                 break;
5119         }
5120
5121         show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
5122         
5123 }
5124
5125 void
5126 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5127 {
5128         nframes64_t start = 0;
5129         nframes64_t end = 0;
5130         ArdourCanvas::SimpleRect *crect;
5131
5132         switch (range_marker_op) {
5133         case CreateRangeMarker:
5134                 crect = range_bar_drag_rect;
5135                 break;
5136         case CreateTransportMarker:
5137                 crect = transport_bar_drag_rect;
5138                 break;
5139         case CreateCDMarker:
5140                 crect = cd_marker_bar_drag_rect;
5141                 break;
5142         default:
5143                 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
5144                 return;
5145                 break;
5146         }
5147         
5148         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5149                 snap_to (drag_info.current_pointer_frame);
5150         }
5151
5152         /* only alter selection if the current frame is 
5153            different from the last frame position.
5154          */
5155         
5156         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5157         
5158         switch (range_marker_op) {
5159         case CreateRangeMarker:
5160         case CreateTransportMarker:
5161         case CreateCDMarker:
5162                 if (drag_info.first_move) {
5163                         snap_to (drag_info.grab_frame);
5164                 }
5165                 
5166                 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5167                         start = drag_info.current_pointer_frame;
5168                         end = drag_info.grab_frame;
5169                 } else {
5170                         end = drag_info.current_pointer_frame;
5171                         start = drag_info.grab_frame;
5172                 }
5173                 
5174                 /* first drag: Either add to the selection
5175                    or create a new selection.
5176                 */
5177                 
5178                 if (drag_info.first_move) {
5179                         
5180                         temp_location->set (start, end);
5181                         
5182                         crect->show ();
5183
5184                         update_marker_drag_item (temp_location);
5185                         range_marker_drag_rect->show();
5186                         //range_marker_drag_rect->raise_to_top();
5187                         
5188                 } 
5189                 break;          
5190         }
5191         
5192         if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
5193                 start_canvas_autoscroll (1, 0);
5194         }
5195         
5196         if (start != end) {
5197                 temp_location->set (start, end);
5198
5199                 double x1 = frame_to_pixel (start);
5200                 double x2 = frame_to_pixel (end);
5201                 crect->property_x1() = x1;
5202                 crect->property_x2() = x2;
5203
5204                 update_marker_drag_item (temp_location);
5205         }
5206
5207         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5208         drag_info.first_move = false;
5209
5210         show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
5211         
5212 }
5213
5214 void
5215 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5216 {
5217         Location * newloc = 0;
5218         string rangename;
5219         int flags;
5220         
5221         if (!drag_info.first_move) {
5222                 drag_range_markerbar_op (item, event);
5223
5224                 switch (range_marker_op) {
5225                 case CreateRangeMarker:
5226                 case CreateCDMarker:
5227                     {
5228                         begin_reversible_command (_("new range marker"));
5229                         XMLNode &before = session->locations()->get_state();
5230                         session->locations()->next_available_name(rangename,"unnamed");
5231                         if (range_marker_op == CreateCDMarker) {
5232                                 flags =  Location::IsRangeMarker|Location::IsCDMarker;
5233                                 cd_marker_bar_drag_rect->hide();
5234                         }
5235                         else {
5236                                 flags =  Location::IsRangeMarker;
5237                                 range_bar_drag_rect->hide();
5238                         }
5239                         newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
5240                         session->locations()->add (newloc, true);
5241                         XMLNode &after = session->locations()->get_state();
5242                         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
5243                         commit_reversible_command ();
5244                         
5245                         range_marker_drag_rect->hide();
5246                         break;
5247                     }
5248
5249                 case CreateTransportMarker:
5250                         // popup menu to pick loop or punch
5251                         new_transport_marker_context_menu (&event->button, item);
5252                         
5253                         break;
5254                 }
5255         } else {
5256                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5257
5258                 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
5259
5260                         nframes64_t start;
5261                         nframes64_t end;
5262
5263                         start = session->locations()->first_mark_before (drag_info.grab_frame);
5264                         end = session->locations()->first_mark_after (drag_info.grab_frame);
5265                         
5266                         if (end == max_frames) {
5267                                 end = session->current_end_frame ();
5268                         }
5269
5270                         if (start == 0) {
5271                                 start = session->current_start_frame ();
5272                         }
5273
5274                         switch (mouse_mode) {
5275                         case MouseObject:
5276                                 /* find the two markers on either side and then make the selection from it */
5277                                 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
5278                                 break;
5279
5280                         case MouseRange:
5281                                 /* find the two markers on either side of the click and make the range out of it */
5282                                 selection->set (0, start, end);
5283                                 break;
5284
5285                         default:
5286                                 break;
5287                         }
5288                 } 
5289         }
5290
5291         stop_canvas_autoscroll ();
5292 }
5293
5294
5295
5296 void
5297 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5298 {
5299         drag_info.item = item;
5300         drag_info.motion_callback = &Editor::drag_mouse_zoom;
5301         drag_info.finished_callback = &Editor::end_mouse_zoom;
5302
5303         start_grab (event, zoom_cursor);
5304
5305         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5306 }
5307
5308 void
5309 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5310 {
5311         nframes64_t start;
5312         nframes64_t end;
5313
5314         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5315                 snap_to (drag_info.current_pointer_frame);
5316                 
5317                 if (drag_info.first_move) {
5318                         snap_to (drag_info.grab_frame);
5319                 }
5320         }
5321                 
5322         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5323
5324         /* base start and end on initial click position */
5325         if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5326                 start = drag_info.current_pointer_frame;
5327                 end = drag_info.grab_frame;
5328         } else {
5329                 end = drag_info.current_pointer_frame;
5330                 start = drag_info.grab_frame;
5331         }
5332         
5333         if (start != end) {
5334
5335                 if (drag_info.first_move) {
5336                         zoom_rect->show();
5337                         zoom_rect->raise_to_top();
5338                 }
5339
5340                 reposition_zoom_rect(start, end);
5341
5342                 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5343                 drag_info.first_move = false;
5344
5345                 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5346         }
5347 }
5348
5349 void
5350 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5351 {
5352         if (!drag_info.first_move) {
5353                 drag_mouse_zoom (item, event);
5354                 
5355                 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5356                         temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
5357                 } else {
5358                         temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
5359                 }               
5360         } else {
5361                 temporal_zoom_to_frame (false, drag_info.grab_frame);
5362                 /*
5363                 temporal_zoom_step (false);
5364                 center_screen (drag_info.grab_frame);
5365                 */
5366         }
5367
5368         zoom_rect->hide();
5369 }
5370
5371 void
5372 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
5373 {
5374         double x1 = frame_to_pixel (start);
5375         double x2 = frame_to_pixel (end);
5376         double y2 = full_canvas_height - 1.0;
5377
5378         zoom_rect->property_x1() = x1;
5379         zoom_rect->property_y1() = 1.0;
5380         zoom_rect->property_x2() = x2;
5381         zoom_rect->property_y2() = y2;
5382 }
5383
5384 void
5385 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5386 {
5387         drag_info.item = item;
5388         drag_info.motion_callback = &Editor::drag_rubberband_select;
5389         drag_info.finished_callback = &Editor::end_rubberband_select;
5390
5391         start_grab (event, cross_hair_cursor);
5392
5393         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5394 }
5395
5396 void
5397 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5398 {
5399         nframes64_t start;
5400         nframes64_t end;
5401         double y1;
5402         double y2;
5403
5404         /* use a bigger drag threshold than the default */
5405
5406         if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
5407                 return;
5408         }
5409
5410         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) {
5411                 if (drag_info.first_move) {
5412                         snap_to (drag_info.grab_frame);
5413                 } 
5414                 snap_to (drag_info.current_pointer_frame);
5415         }
5416
5417         /* base start and end on initial click position */
5418
5419         if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5420                 start = drag_info.current_pointer_frame;
5421                 end = drag_info.grab_frame;
5422         } else {
5423                 end = drag_info.current_pointer_frame;
5424                 start = drag_info.grab_frame;
5425         }
5426
5427         if (drag_info.current_pointer_y < drag_info.grab_y) {
5428                 y1 = drag_info.current_pointer_y;
5429                 y2 = drag_info.grab_y;
5430         } else {
5431                 y2 = drag_info.current_pointer_y;
5432                 y1 = drag_info.grab_y;
5433         }
5434
5435         
5436         if (start != end || y1 != y2) {
5437
5438                 double x1 = frame_to_pixel (start);
5439                 double x2 = frame_to_pixel (end);
5440                 
5441                 rubberband_rect->property_x1() = x1;
5442                 rubberband_rect->property_y1() = y1;
5443                 rubberband_rect->property_x2() = x2;
5444                 rubberband_rect->property_y2() = y2;
5445
5446                 rubberband_rect->show();
5447                 rubberband_rect->raise_to_top();
5448                 
5449                 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5450                 drag_info.first_move = false;
5451
5452                 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5453         }
5454 }
5455
5456 void
5457 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5458 {
5459         if (!drag_info.first_move) {
5460
5461                 drag_rubberband_select (item, event);
5462
5463                 double y1,y2;
5464                 if (drag_info.current_pointer_y < drag_info.grab_y) {
5465                         y1 = drag_info.current_pointer_y;
5466                         y2 = drag_info.grab_y;
5467                 } else {
5468                         y2 = drag_info.current_pointer_y;
5469                         y1 = drag_info.grab_y;
5470                 }
5471
5472
5473                 Selection::Operation op = Keyboard::selection_type (event->button.state);
5474                 bool commit;
5475
5476                 begin_reversible_command (_("rubberband selection"));
5477
5478                 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5479                         commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5480                 } else {
5481                         commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5482                 }               
5483
5484                 if (commit) {
5485                         commit_reversible_command ();
5486                 }
5487                 
5488         } else {
5489                 if (!getenv("ARDOUR_SAE")) {
5490                         selection->clear_tracks();
5491                 }
5492                 selection->clear_regions();
5493                 selection->clear_points ();
5494                 selection->clear_lines ();
5495         }
5496
5497         rubberband_rect->hide();
5498 }
5499
5500
5501 gint
5502 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5503 {
5504         using namespace Gtkmm2ext;
5505
5506         ArdourPrompter prompter (false);
5507
5508         prompter.set_prompt (_("Name for region:"));
5509         prompter.set_initial_text (clicked_regionview->region()->name());
5510         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5511         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5512         prompter.show_all ();
5513         switch (prompter.run ()) {
5514         case Gtk::RESPONSE_ACCEPT:
5515         string str;
5516                 prompter.get_result(str);
5517                 if (str.length()) {
5518                         clicked_regionview->region()->set_name (str);
5519                 }
5520                 break;
5521         }
5522         return true;
5523 }
5524
5525 void
5526 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5527 {
5528         drag_info.item = item;
5529         drag_info.motion_callback = &Editor::time_fx_motion;
5530         drag_info.finished_callback = &Editor::end_time_fx;
5531
5532         start_grab (event);
5533
5534         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5535 }
5536
5537 void
5538 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5539 {
5540         RegionView* rv = clicked_regionview;
5541
5542         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5543                 snap_to (drag_info.current_pointer_frame);
5544         }
5545
5546         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5547                 return;
5548         }
5549
5550         if (drag_info.current_pointer_frame > rv->region()->position()) {
5551                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5552         }
5553
5554         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5555         drag_info.first_move = false;
5556
5557         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5558 }
5559
5560 void
5561 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5562 {
5563         clicked_regionview->get_time_axis_view().hide_timestretch ();
5564
5565         if (drag_info.first_move) {
5566                 return;
5567         }
5568
5569         if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5570                 /* backwards drag of the left edge - not usable */
5571                 return;
5572         }
5573         
5574         nframes64_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5575 #ifdef USE_RUBBERBAND
5576         float percentage = (float) ((double) newlen / (double) clicked_regionview->region()->length());
5577 #else
5578         float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5579 #endif  
5580
5581         begin_reversible_command (_("timestretch"));
5582
5583         // XXX how do timeFX on multiple regions ?
5584
5585         RegionSelection rs;
5586         rs.add (clicked_regionview);
5587
5588         if (time_stretch (rs, percentage) == 0) {
5589                 session->commit_reversible_command ();
5590         }
5591 }
5592
5593 void
5594 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
5595 {
5596         /* no brushing without a useful snap setting */
5597
5598         // FIXME
5599         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
5600         assert(arv);
5601
5602         switch (snap_mode) {
5603         case SnapMagnetic:
5604                 return; /* can't work because it allows region to be placed anywhere */
5605         default:
5606                 break; /* OK */
5607         }
5608
5609         switch (snap_type) {
5610         case SnapToMark:
5611                 return;
5612
5613         default:
5614                 break;
5615         }
5616
5617         /* don't brush a copy over the original */
5618         
5619         if (pos == rv->region()->position()) {
5620                 return;
5621         }
5622
5623         RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
5624
5625         if (atv == 0 || !atv->is_audio_track()) {
5626                 return;
5627         }
5628
5629         boost::shared_ptr<Playlist> playlist = atv->playlist();
5630         double speed = atv->get_diskstream()->speed();
5631         
5632         XMLNode &before = playlist->get_state();
5633         playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes64_t) (pos * speed));
5634         XMLNode &after = playlist->get_state();
5635         session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5636         
5637         // playlist is frozen, so we have to update manually
5638         
5639         playlist->Modified(); /* EMIT SIGNAL */
5640 }
5641
5642 gint
5643 Editor::track_height_step_timeout ()
5644 {
5645         if (get_microseconds() - last_track_height_step_timestamp < 250000) {
5646                 current_stepping_trackview = 0;
5647                 return false;
5648         }
5649         return true;
5650 }
5651