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