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