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