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