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