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