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