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