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