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