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