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