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