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