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