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