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