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