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