significant changes in code to handle import/embedding - much cleaner and less code...
[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         selection->clear ();
3616         clicked_selection = 0;
3617 }       
3618
3619 void
3620 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3621 {
3622         jack_nframes_t start = 0;
3623         jack_nframes_t end = 0;
3624
3625         if (session == 0) {
3626                 return;
3627         }
3628
3629         drag_info.item = item;
3630         drag_info.motion_callback = &Editor::drag_selection;
3631         drag_info.finished_callback = &Editor::end_selection_op;
3632
3633         selection_op = op;
3634
3635         switch (op) {
3636         case CreateSelection:
3637                 
3638                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3639                         drag_info.copy = true;
3640                 } else {
3641                         drag_info.copy = false;
3642                 }
3643                 start_grab (event, selector_cursor);
3644                 break;
3645
3646         case SelectionStartTrim:
3647                 clicked_trackview->order_selection_trims (item, true);
3648                 start_grab (event, trimmer_cursor);
3649                 start = selection->time[clicked_selection].start;
3650                 drag_info.pointer_frame_offset = drag_info.grab_frame - start;  
3651                 break;
3652                 
3653         case SelectionEndTrim:
3654                 clicked_trackview->order_selection_trims (item, false);
3655                 start_grab (event, trimmer_cursor);
3656                 end = selection->time[clicked_selection].end;
3657                 drag_info.pointer_frame_offset = drag_info.grab_frame - end;    
3658                 break;
3659
3660         case SelectionMove:
3661                 start = selection->time[clicked_selection].start;
3662                 start_grab (event);
3663                 drag_info.pointer_frame_offset = drag_info.grab_frame - start;  
3664                 break;
3665         }
3666
3667         if (selection_op == SelectionMove) {
3668                 show_verbose_time_cursor(start, 10);    
3669         } else {
3670                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
3671         }
3672 }
3673
3674 void
3675 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3676 {
3677         jack_nframes_t start = 0;
3678         jack_nframes_t end = 0;
3679         jack_nframes_t length;
3680         jack_nframes_t pending_position;
3681
3682         if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3683                 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3684         }
3685         else {
3686                 pending_position = 0;
3687         }
3688         
3689         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3690                 snap_to (pending_position);
3691         }
3692
3693         /* only alter selection if the current frame is 
3694            different from the last frame position (adjusted)
3695          */
3696         
3697         if (pending_position == drag_info.last_pointer_frame) return;
3698         
3699         switch (selection_op) {
3700         case CreateSelection:
3701                 
3702                 if (drag_info.first_move) {
3703                         snap_to (drag_info.grab_frame);
3704                 }
3705                 
3706                 if (pending_position < drag_info.grab_frame) {
3707                         start = pending_position;
3708                         end = drag_info.grab_frame;
3709                 } else {
3710                         end = pending_position;
3711                         start = drag_info.grab_frame;
3712                 }
3713                 
3714                 /* first drag: Either add to the selection
3715                    or create a new selection->
3716                 */
3717                 
3718                 if (drag_info.first_move) {
3719                         
3720                         begin_reversible_command (_("range selection"));
3721                         
3722                         if (drag_info.copy) {
3723                                 /* adding to the selection */
3724                                 clicked_selection = selection->add (start, end);
3725                                 drag_info.copy = false;
3726                         } else {
3727                                 /* new selection-> */
3728                                 clicked_selection = selection->set (clicked_trackview, start, end);
3729                         }
3730                 } 
3731                 break;
3732                 
3733         case SelectionStartTrim:
3734                 
3735                 if (drag_info.first_move) {
3736                         begin_reversible_command (_("trim selection start"));
3737                 }
3738                 
3739                 start = selection->time[clicked_selection].start;
3740                 end = selection->time[clicked_selection].end;
3741
3742                 if (pending_position > end) {
3743                         start = end;
3744                 } else {
3745                         start = pending_position;
3746                 }
3747                 break;
3748                 
3749         case SelectionEndTrim:
3750                 
3751                 if (drag_info.first_move) {
3752                         begin_reversible_command (_("trim selection end"));
3753                 }
3754                 
3755                 start = selection->time[clicked_selection].start;
3756                 end = selection->time[clicked_selection].end;
3757
3758                 if (pending_position < start) {
3759                         end = start;
3760                 } else {
3761                         end = pending_position;
3762                 }
3763                 
3764                 break;
3765                 
3766         case SelectionMove:
3767                 
3768                 if (drag_info.first_move) {
3769                         begin_reversible_command (_("move selection"));
3770                 }
3771                 
3772                 start = selection->time[clicked_selection].start;
3773                 end = selection->time[clicked_selection].end;
3774                 
3775                 length = end - start;
3776                 
3777                 start = pending_position;
3778                 snap_to (start);
3779                 
3780                 end = start + length;
3781                 
3782                 break;
3783         }
3784         
3785         if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3786                 start_canvas_autoscroll (1);
3787         }
3788
3789         if (start != end) {
3790                 selection->replace (clicked_selection, start, end);
3791         }
3792
3793         drag_info.last_pointer_frame = pending_position;
3794         drag_info.first_move = false;
3795
3796         if (selection_op == SelectionMove) {
3797                 show_verbose_time_cursor(start, 10);    
3798         } else {
3799                 show_verbose_time_cursor(pending_position, 10); 
3800         }
3801 }
3802
3803 void
3804 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3805 {
3806         if (!drag_info.first_move) {
3807                 drag_selection (item, event);
3808                 /* XXX this is not object-oriented programming at all. ick */
3809                 if (selection->time.consolidate()) {
3810                         selection->TimeChanged ();
3811                 }
3812                 commit_reversible_command ();
3813         } else {
3814                 /* just a click, no pointer movement.*/
3815
3816                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3817
3818                         selection->clear_time();
3819
3820                 } 
3821         }
3822
3823         /* XXX what happens if its a music selection? */
3824         session->set_audio_range (selection->time);
3825         stop_canvas_autoscroll ();
3826 }
3827
3828 void
3829 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3830 {
3831         double speed = 1.0;
3832         TimeAxisView* tvp = clicked_trackview;
3833         AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3834
3835         if (tv && tv->is_audio_track()) {
3836                 speed = tv->get_diskstream()->speed();
3837         }
3838         
3839         jack_nframes_t region_start = (jack_nframes_t) (clicked_regionview->region.position() / speed);
3840         jack_nframes_t region_end = (jack_nframes_t) (clicked_regionview->region.last_frame() / speed);
3841         jack_nframes_t region_length = (jack_nframes_t) (clicked_regionview->region.length() / speed);
3842
3843         motion_frozen_playlists.clear();
3844         
3845         //drag_info.item = clicked_regionview->get_name_highlight();
3846         drag_info.item = item;
3847         drag_info.motion_callback = &Editor::trim_motion_callback;
3848         drag_info.finished_callback = &Editor::trim_finished_callback;
3849
3850         start_grab (event, trimmer_cursor);
3851         
3852         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3853                 trim_op = ContentsTrim;
3854         } else {
3855                 /* These will get overridden for a point trim.*/
3856                 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3857                         /* closer to start */
3858                         trim_op = StartTrim;
3859                 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3860                         /* closer to end */
3861                         trim_op = EndTrim;
3862                 }
3863         }
3864
3865         switch (trim_op) {
3866         case StartTrim:
3867                 show_verbose_time_cursor(region_start, 10);     
3868                 break;
3869         case EndTrim:
3870                 show_verbose_time_cursor(region_end, 10);       
3871                 break;
3872         case ContentsTrim:
3873                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
3874                 break;
3875         }
3876 }
3877
3878 void
3879 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3880 {
3881         AudioRegionView* rv = clicked_regionview;
3882         jack_nframes_t frame_delta = 0;
3883         bool left_direction;
3884         bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3885
3886         /* snap modifier works differently here..
3887            its' current state has to be passed to the 
3888            various trim functions in order to work properly 
3889         */ 
3890
3891         double speed = 1.0;
3892         TimeAxisView* tvp = clicked_trackview;
3893         AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3894         pair<set<Playlist*>::iterator,bool> insert_result;
3895
3896         if (tv && tv->is_audio_track()) {
3897                 speed = tv->get_diskstream()->speed();
3898         }
3899         
3900         if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
3901                 left_direction = true;
3902         } else {
3903                 left_direction = false;
3904         }
3905
3906         if (obey_snap) {
3907                 snap_to (drag_info.current_pointer_frame);
3908         }
3909
3910         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
3911                 return;
3912         }
3913
3914         if (drag_info.first_move) {
3915         
3916                 string trim_type;
3917
3918                 switch (trim_op) {
3919                 case StartTrim:
3920                         trim_type = "Region start trim";
3921                         break;
3922                 case EndTrim:
3923                         trim_type = "Region end trim";
3924                         break;
3925                 case ContentsTrim:
3926                         trim_type = "Region content trim";
3927                         break;
3928                 }
3929
3930                 begin_reversible_command (trim_type);
3931
3932                 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3933                         (*i)->region.freeze ();
3934                         (*i)->temporarily_hide_envelope ();
3935
3936                         Playlist * pl = (*i)->region.playlist();
3937                         insert_result = motion_frozen_playlists.insert (pl);
3938                         if (insert_result.second) {
3939                                 session->add_undo (pl->get_memento());
3940                         }
3941                 }
3942         }
3943
3944         if (left_direction) {
3945                 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
3946         } else {
3947                 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
3948         }
3949
3950         switch (trim_op) {              
3951         case StartTrim:
3952                 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region.first_frame()/speed)) {
3953                         break;
3954                 } else {
3955                         for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3956                                 single_start_trim (**i, frame_delta, left_direction, obey_snap);
3957                         }
3958                         break;
3959                 }
3960                 
3961         case EndTrim:
3962                 if ((left_direction == true) && (drag_info.current_pointer_frame > (jack_nframes_t) (rv->region.last_frame()/speed))) {
3963                         break;
3964                 } else {
3965                         for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3966                                 single_end_trim (**i, frame_delta, left_direction, obey_snap);
3967                         }
3968                         break;
3969                 }
3970                 
3971         case ContentsTrim:
3972                 {
3973                         bool swap_direction = false;
3974
3975                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3976                                 swap_direction = true;
3977                         }
3978                         
3979                         for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
3980                              i != selection->audio_regions.by_layer().end(); ++i)
3981                         {
3982                                 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
3983                         }
3984                 }
3985                 break;
3986         }
3987
3988         switch (trim_op) {
3989         case StartTrim:
3990                 show_verbose_time_cursor((jack_nframes_t) (rv->region.position()/speed), 10);   
3991                 break;
3992         case EndTrim:
3993                 show_verbose_time_cursor((jack_nframes_t) (rv->region.last_frame()/speed), 10); 
3994                 break;
3995         case ContentsTrim:
3996                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
3997                 break;
3998         }
3999
4000         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4001         drag_info.first_move = false;
4002 }
4003
4004 void
4005 Editor::single_contents_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4006 {
4007         Region& region (rv.region);
4008
4009         if (region.locked()) {
4010                 return;
4011         }
4012
4013         jack_nframes_t new_bound;
4014
4015         double speed = 1.0;
4016         TimeAxisView* tvp = clicked_trackview;
4017         AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4018
4019         if (tv && tv->is_audio_track()) {
4020                 speed = tv->get_diskstream()->speed();
4021         }
4022         
4023         if (left_direction) {
4024                 if (swap_direction) {
4025                         new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4026                 } else {
4027                         new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4028                 }
4029         } else {
4030                 if (swap_direction) {
4031                         new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4032                 } else {
4033                         new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4034                 }
4035         }
4036
4037         if (obey_snap) {
4038                 snap_to (new_bound);
4039         }
4040         region.trim_start ((jack_nframes_t) (new_bound * speed), this); 
4041         rv.region_changed (StartChanged);
4042 }
4043
4044 void
4045 Editor::single_start_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool obey_snap)
4046 {
4047         Region& region (rv.region);     
4048
4049         if (region.locked()) {
4050                 return;
4051         }
4052
4053         jack_nframes_t new_bound;
4054
4055         double speed = 1.0;
4056         TimeAxisView* tvp = clicked_trackview;
4057         AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4058
4059         if (tv && tv->is_audio_track()) {
4060                 speed = tv->get_diskstream()->speed();
4061         }
4062         
4063         if (left_direction) {
4064                 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4065         } else {
4066                 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4067         }
4068
4069         if (obey_snap) {
4070                 snap_to (new_bound, (left_direction ? 0 : 1));  
4071         }
4072
4073         region.trim_front ((jack_nframes_t) (new_bound * speed), this);
4074
4075         rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4076 }
4077
4078 void
4079 Editor::single_end_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool obey_snap)
4080 {
4081         Region& region (rv.region);
4082
4083         if (region.locked()) {
4084                 return;
4085         }
4086
4087         jack_nframes_t new_bound;
4088
4089         double speed = 1.0;
4090         TimeAxisView* tvp = clicked_trackview;
4091         AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4092
4093         if (tv && tv->is_audio_track()) {
4094                 speed = tv->get_diskstream()->speed();
4095         }
4096         
4097         if (left_direction) {
4098                 new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) - frame_delta;
4099         } else {
4100                 new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) + frame_delta;
4101         }
4102
4103         if (obey_snap) {
4104                 snap_to (new_bound);
4105         }
4106         region.trim_end ((jack_nframes_t) (new_bound * speed), this);
4107         rv.region_changed (LengthChanged);
4108 }
4109         
4110 void
4111 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4112 {
4113         if (!drag_info.first_move) {
4114                 trim_motion_callback (item, event);
4115                 
4116                 if (!clicked_regionview->get_selected()) {
4117                         thaw_region_after_trim (*clicked_regionview);           
4118                 } else {
4119                         
4120                         for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
4121                              i != selection->audio_regions.by_layer().end(); ++i)
4122                         {
4123                                 thaw_region_after_trim (**i);
4124                         }
4125                 }
4126                 
4127                 for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4128                         //(*p)->thaw ();
4129                         session->add_redo_no_execute ((*p)->get_memento());
4130                 }
4131                 
4132                 motion_frozen_playlists.clear ();
4133
4134                 commit_reversible_command();
4135         } else {
4136                 /* no mouse movement */
4137                 point_trim (event);
4138         }
4139 }
4140
4141 void
4142 Editor::point_trim (GdkEvent* event)
4143 {
4144         AudioRegionView* rv = clicked_regionview;
4145         jack_nframes_t new_bound = drag_info.current_pointer_frame;
4146
4147         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4148                 snap_to (new_bound);
4149         }
4150
4151         /* Choose action dependant on which button was pressed */
4152         switch (event->button.button) {
4153         case 1:
4154                 trim_op = StartTrim;
4155                 begin_reversible_command (_("Start point trim"));
4156
4157                 if (rv->get_selected()) {
4158
4159                         for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
4160                              i != selection->audio_regions.by_layer().end(); ++i)
4161                         {
4162                                 if (!(*i)->region.locked()) {
4163                                         session->add_undo ((*i)->region.playlist()->get_memento());
4164                                         (*i)->region.trim_front (new_bound, this);      
4165                                         session->add_redo_no_execute ((*i)->region.playlist()->get_memento());
4166                                 }
4167                         }
4168
4169                 } else {
4170
4171                         if (!rv->region.locked()) {
4172                                 session->add_undo (rv->region.playlist()->get_memento());
4173                                 rv->region.trim_front (new_bound, this);        
4174                                 session->add_redo_no_execute (rv->region.playlist()->get_memento());
4175                         }
4176                 }
4177
4178                 commit_reversible_command();
4179         
4180                 break;
4181         case 2:
4182                 trim_op = EndTrim;
4183                 begin_reversible_command (_("End point trim"));
4184
4185                 if (rv->get_selected()) {
4186                         
4187                         for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i)
4188                         {
4189                                 if (!(*i)->region.locked()) {
4190                                         session->add_undo ((*i)->region.playlist()->get_memento());
4191                                         (*i)->region.trim_end (new_bound, this);
4192                                         session->add_redo_no_execute ((*i)->region.playlist()->get_memento());  
4193                                 }
4194                         }
4195
4196                 } else {
4197
4198                         if (!rv->region.locked()) {
4199                                 session->add_undo (rv->region.playlist()->get_memento());
4200                                 rv->region.trim_end (new_bound, this);
4201                                 session->add_redo_no_execute (rv->region.playlist()->get_memento());    
4202                         }
4203                 }
4204
4205                 commit_reversible_command();
4206         
4207                 break;
4208         default:
4209                 break;
4210         }
4211 }
4212
4213 void
4214 Editor::thaw_region_after_trim (AudioRegionView& rv)
4215 {
4216         Region& region (rv.region);
4217
4218         if (region.locked()) {
4219                 return;
4220         }
4221
4222         region.thaw (_("trimmed region"));
4223         session->add_redo_no_execute (region.playlist()->get_memento());
4224
4225         rv.unhide_envelope ();
4226 }
4227
4228 void
4229 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4230 {
4231         Marker* marker;
4232         bool is_start;
4233
4234         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4235                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4236                 /*NOTREACHED*/
4237         }
4238
4239         Location* location = find_location_from_marker (marker, is_start);      
4240         location->set_hidden (true, this);
4241 }
4242
4243
4244 void
4245 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4246 {
4247
4248         if (session == 0) {
4249                 return;
4250         }
4251
4252         drag_info.item = item;
4253         drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4254         drag_info.finished_callback = &Editor::end_range_markerbar_op;
4255
4256         range_marker_op = op;
4257
4258         if (!temp_location) {
4259                 temp_location = new Location;
4260         }
4261         
4262         switch (op) {
4263         case CreateRangeMarker:
4264         case CreateTransportMarker:
4265                 
4266                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4267                         drag_info.copy = true;
4268                 } else {
4269                         drag_info.copy = false;
4270                 }
4271                 start_grab (event, selector_cursor);
4272                 break;
4273         }
4274
4275         show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4276         
4277 }
4278
4279 void
4280 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4281 {
4282         jack_nframes_t start = 0;
4283         jack_nframes_t end = 0;
4284         ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4285         
4286         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4287                 snap_to (drag_info.current_pointer_frame);
4288         }
4289
4290         /* only alter selection if the current frame is 
4291            different from the last frame position.
4292          */
4293         
4294         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4295         
4296         switch (range_marker_op) {
4297         case CreateRangeMarker:
4298         case CreateTransportMarker:
4299                 if (drag_info.first_move) {
4300                         snap_to (drag_info.grab_frame);
4301                 }
4302                 
4303                 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4304                         start = drag_info.current_pointer_frame;
4305                         end = drag_info.grab_frame;
4306                 } else {
4307                         end = drag_info.current_pointer_frame;
4308                         start = drag_info.grab_frame;
4309                 }
4310                 
4311                 /* first drag: Either add to the selection
4312                    or create a new selection->
4313                 */
4314                 
4315                 if (drag_info.first_move) {
4316                         
4317                         temp_location->set (start, end);
4318                         
4319                         crect->show ();
4320
4321                         update_marker_drag_item (temp_location);
4322                         range_marker_drag_rect->show();
4323                         range_marker_drag_rect->raise_to_top();
4324                         
4325                 } 
4326                 break;          
4327         }
4328         
4329         if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4330                 start_canvas_autoscroll (1);
4331         }
4332         
4333         if (start != end) {
4334                 temp_location->set (start, end);
4335
4336                 double x1 = frame_to_pixel (start);
4337                 double x2 = frame_to_pixel (end);
4338                 crect->property_x1() = x1;
4339                 crect->property_x2() = x2;
4340
4341                 update_marker_drag_item (temp_location);
4342         }
4343
4344         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4345         drag_info.first_move = false;
4346
4347         show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4348         
4349 }
4350
4351 void
4352 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4353 {
4354         Location * newloc = 0;
4355         
4356         if (!drag_info.first_move) {
4357                 drag_range_markerbar_op (item, event);
4358
4359                 switch (range_marker_op) {
4360                 case CreateRangeMarker:
4361                         begin_reversible_command (_("new range marker"));
4362                         session->add_undo (session->locations()->get_memento());
4363                         newloc = new Location(temp_location->start(), temp_location->end(), "unnamed", Location::IsRangeMarker);
4364                         session->locations()->add (newloc, true);
4365                         session->add_redo_no_execute (session->locations()->get_memento());
4366                         commit_reversible_command ();
4367                         
4368                         range_bar_drag_rect->hide();
4369                         range_marker_drag_rect->hide();
4370                         break;
4371
4372                 case CreateTransportMarker:
4373                         // popup menu to pick loop or punch
4374                         new_transport_marker_context_menu (&event->button, item);
4375                         
4376                         break;
4377                 }
4378         } else {
4379                 /* just a click, no pointer movement.*/
4380
4381                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4382
4383                         // nothing yet
4384
4385                 } 
4386         }
4387
4388         stop_canvas_autoscroll ();
4389 }
4390
4391
4392
4393 void
4394 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4395 {
4396         drag_info.item = item;
4397         drag_info.motion_callback = &Editor::drag_mouse_zoom;
4398         drag_info.finished_callback = &Editor::end_mouse_zoom;
4399
4400         start_grab (event, zoom_cursor);
4401
4402         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4403 }
4404
4405 void
4406 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4407 {
4408         jack_nframes_t start;
4409         jack_nframes_t end;
4410
4411         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4412                 snap_to (drag_info.current_pointer_frame);
4413                 
4414                 if (drag_info.first_move) {
4415                         snap_to (drag_info.grab_frame);
4416                 }
4417         }
4418                 
4419         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4420
4421         /* base start and end on initial click position */
4422         if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4423                 start = drag_info.current_pointer_frame;
4424                 end = drag_info.grab_frame;
4425         } else {
4426                 end = drag_info.current_pointer_frame;
4427                 start = drag_info.grab_frame;
4428         }
4429         
4430         if (start != end) {
4431
4432                 if (drag_info.first_move) {
4433                         zoom_rect->show();
4434                         zoom_rect->raise_to_top();
4435                 }
4436
4437                 reposition_zoom_rect(start, end);
4438
4439                 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4440                 drag_info.first_move = false;
4441
4442                 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4443         }
4444 }
4445
4446 void
4447 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4448 {
4449         if (!drag_info.first_move) {
4450                 drag_mouse_zoom (item, event);
4451                 
4452                 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4453                         temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4454                 } else {
4455                         temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4456                 }               
4457         } else {
4458                 temporal_zoom_to_frame (false, drag_info.grab_frame);
4459                 /*
4460                 temporal_zoom_step (false);
4461                 center_screen (drag_info.grab_frame);
4462                 */
4463         }
4464
4465         zoom_rect->hide();
4466 }
4467
4468 void
4469 Editor::reposition_zoom_rect (jack_nframes_t start, jack_nframes_t end)
4470 {
4471         double x1 = frame_to_pixel (start);
4472         double x2 = frame_to_pixel (end);
4473         double y2 = canvas_height - 2;
4474
4475         zoom_rect->property_x1() = x1;
4476         zoom_rect->property_y1() = 1.0;
4477         zoom_rect->property_x2() = x2;
4478         zoom_rect->property_y2() = y2;
4479 }
4480
4481 void
4482 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4483 {
4484         drag_info.item = item;
4485         drag_info.motion_callback = &Editor::drag_rubberband_select;
4486         drag_info.finished_callback = &Editor::end_rubberband_select;
4487
4488         start_grab (event, cross_hair_cursor);
4489
4490         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4491 }
4492
4493 void
4494 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4495 {
4496         jack_nframes_t start;
4497         jack_nframes_t end;
4498         double y1;
4499         double y2;
4500
4501         /* use a bigger drag threshold than the default */
4502
4503         if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4504                 return;
4505         }
4506
4507 //      if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4508 //              snap_to (drag_info.current_pointer_frame);
4509                 
4510 //              if (drag_info.first_move) {
4511 //                      snap_to (drag_info.grab_frame);
4512 //              }
4513 //      }
4514                 
4515
4516         /* base start and end on initial click position */
4517         if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4518                 start = drag_info.current_pointer_frame;
4519                 end = drag_info.grab_frame;
4520         } else {
4521                 end = drag_info.current_pointer_frame;
4522                 start = drag_info.grab_frame;
4523         }
4524
4525         if (drag_info.current_pointer_y < drag_info.grab_y) {
4526                 y1 = drag_info.current_pointer_y;
4527                 y2 = drag_info.grab_y;
4528         }
4529         else {
4530                 y2 = drag_info.current_pointer_y;
4531                 y1 = drag_info.grab_y;
4532         }
4533
4534         
4535         if (start != end || y1 != y2) {
4536
4537                 double x1 = frame_to_pixel (start);
4538                 double x2 = frame_to_pixel (end);
4539                 
4540                 rubberband_rect->property_x1() = x1;
4541                 rubberband_rect->property_y1() = y1;
4542                 rubberband_rect->property_x2() = x2;
4543                 rubberband_rect->property_y2() = y2;
4544
4545                 rubberband_rect->show();
4546                 rubberband_rect->raise_to_top();
4547                 
4548                 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4549                 drag_info.first_move = false;
4550
4551                 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4552         }
4553 }
4554
4555 void
4556 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4557 {
4558         if (!drag_info.first_move) {
4559
4560                 drag_rubberband_select (item, event);
4561
4562                 double y1,y2;
4563                 if (drag_info.current_pointer_y < drag_info.grab_y) {
4564                         y1 = drag_info.current_pointer_y;
4565                         y2 = drag_info.grab_y;
4566                 }
4567                 else {
4568                         y2 = drag_info.current_pointer_y;
4569                         y1 = drag_info.grab_y;
4570                 }
4571
4572
4573                 Selection::Operation op = Keyboard::selection_type (event->button.state);
4574                 bool commit;
4575
4576                 begin_reversible_command (_("select regions"));
4577
4578                 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4579                         commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4580                 } else {
4581                         commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4582                 }               
4583
4584                 if (commit) {
4585                         commit_reversible_command ();
4586                 }
4587                 
4588         } else {
4589                 selection->clear_audio_regions();
4590                 selection->clear_points ();
4591                 selection->clear_lines ();
4592         }
4593
4594         rubberband_rect->hide();
4595 }
4596
4597
4598 gint
4599 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4600 {
4601         using namespace Gtkmm2ext;
4602
4603         ArdourPrompter prompter (false);
4604
4605         prompter.set_prompt (_("Name for region:"));
4606         prompter.set_initial_text (clicked_regionview->region.name());
4607         prompter.show_all ();
4608         switch (prompter.run ()) {
4609         case Gtk::RESPONSE_ACCEPT:
4610         string str;
4611                 prompter.get_result(str);
4612                 if (str.length()) {
4613                 clicked_regionview->region.set_name (str);
4614                 }
4615                 break;
4616         }
4617         return true;
4618 }
4619
4620 void
4621 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4622 {
4623         drag_info.item = item;
4624         drag_info.motion_callback = &Editor::time_fx_motion;
4625         drag_info.finished_callback = &Editor::end_time_fx;
4626
4627         start_grab (event);
4628
4629         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4630 }
4631
4632 void
4633 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4634 {
4635         AudioRegionView* rv = clicked_regionview;
4636
4637         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4638                 snap_to (drag_info.current_pointer_frame);
4639         }
4640
4641         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4642                 return;
4643         }
4644
4645         if (drag_info.current_pointer_frame > rv->region.position()) {
4646                 rv->get_time_axis_view().show_timestretch (rv->region.position(), drag_info.current_pointer_frame);
4647         }
4648
4649         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4650         drag_info.first_move = false;
4651
4652         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4653 }
4654
4655 void
4656 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4657 {
4658         clicked_regionview->get_time_axis_view().hide_timestretch ();
4659
4660         if (drag_info.first_move) {
4661                 return;
4662         }
4663         
4664         jack_nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region.position();
4665         float percentage = (float) ((double) newlen - (double) clicked_regionview->region.length()) / ((double) newlen) * 100.0f;
4666         
4667         begin_reversible_command (_("timestretch"));
4668
4669         if (run_timestretch (selection->audio_regions, percentage) == 0) {
4670                 session->commit_reversible_command ();
4671         }
4672 }
4673
4674 void
4675 Editor::mouse_brush_insert_region (AudioRegionView* rv, jack_nframes_t pos)
4676 {
4677         /* no brushing without a useful snap setting */
4678
4679         switch (snap_mode) {
4680         case SnapMagnetic:
4681                 return; /* can't work because it allows region to be placed anywhere */
4682         default:
4683                 break; /* OK */
4684         }
4685
4686         switch (snap_type) {
4687         case SnapToFrame:
4688         case SnapToMark:
4689         case SnapToEditCursor:
4690                 return;
4691
4692         default:
4693                 break;
4694         }
4695
4696         /* don't brush a copy over the original */
4697         
4698         if (pos == rv->region.position()) {
4699                 return;
4700         }
4701
4702         AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(&rv->get_time_axis_view());
4703
4704         if (atv == 0 || !atv->is_audio_track()) {
4705                 return;
4706         }
4707
4708         Playlist* playlist = atv->playlist();
4709         double speed = atv->get_diskstream()->speed();
4710         
4711         session->add_undo (playlist->get_memento());
4712         playlist->add_region (*(new AudioRegion (rv->region)), (jack_nframes_t) (pos * speed));
4713         session->add_redo_no_execute (playlist->get_memento());
4714         
4715         // playlist is frozen, so we have to update manually
4716         
4717         playlist->StateChanged (Change (~0)); /* EMIT SIGNAL */
4718 }
4719
4720 gint
4721 Editor::track_height_step_timeout ()
4722 {
4723         struct timeval now;
4724         struct timeval delta;
4725         
4726         gettimeofday (&now, 0);
4727         timersub (&now, &last_track_height_step_timestamp, &delta);
4728         
4729         if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4730                 current_stepping_trackview = 0;
4731                 return false;
4732         }
4733         return true;
4734 }