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