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