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