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