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