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