Comment and (hopefully) simplify Editor::region_drag_motion_callback, fixing problems...
[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 = 0;
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.source_layer = clicked_regionview->region()->layer();
3389         drag_info.dest_trackview = drag_info.source_trackview;
3390         drag_info.dest_layer = drag_info.source_layer;
3391         // we want a move threshold
3392         drag_info.want_move_threshold = true;
3393         show_verbose_time_cursor (drag_info.last_frame_position, 10);
3394
3395         begin_reversible_command (_("move region(s)"));
3396
3397         /* sync the canvas to what we think is its current state */
3398         track_canvas->update_now();
3399 }
3400
3401 void
3402 Editor::start_create_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3403 {
3404         drag_info.copy = false;
3405         drag_info.item = item;
3406         drag_info.data = clicked_axisview;
3407         drag_info.source_trackview = clicked_axisview;
3408         drag_info.dest_trackview = drag_info.source_trackview;
3409         drag_info.dest_layer = drag_info.source_layer;
3410         drag_info.motion_callback = &Editor::create_region_drag_motion_callback;
3411         drag_info.finished_callback = &Editor::create_region_drag_finished_callback;
3412
3413         start_grab (event);
3414 }
3415
3416 void
3417 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
3418 {
3419         if (selection->regions.empty() || clicked_regionview == 0) {
3420                 return;
3421         }
3422         _region_motion_group->raise_to_top ();
3423         drag_info.copy = true;
3424         drag_info.item = item;
3425         drag_info.data = clicked_regionview;    
3426
3427         start_grab(event);
3428
3429         TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
3430         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3431         double speed = 1.0;
3432
3433         if (rtv && rtv->is_track()) {
3434                 speed = rtv->get_diskstream()->speed();
3435         }
3436         
3437         drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3438         drag_info.dest_trackview = drag_info.source_trackview;
3439         drag_info.dest_layer = drag_info.source_layer;
3440         drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3441         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3442         // we want a move threshold
3443         drag_info.want_move_threshold = true;
3444         drag_info.motion_callback = &Editor::region_drag_motion_callback;
3445         drag_info.finished_callback = &Editor::region_drag_finished_callback;
3446         show_verbose_time_cursor (drag_info.last_frame_position, 10);
3447 }
3448
3449 void
3450 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
3451 {
3452         if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) {
3453                 return;
3454         }
3455
3456         drag_info.copy = false;
3457         drag_info.item = item;
3458         drag_info.data = clicked_regionview;
3459         drag_info.motion_callback = &Editor::region_drag_motion_callback;
3460         drag_info.finished_callback = &Editor::region_drag_finished_callback;
3461
3462         start_grab (event);
3463
3464         double speed = 1.0;
3465         TimeAxisView* tvp = clicked_axisview;
3466         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3467
3468         if (tv && tv->is_track()) {
3469                 speed = tv->get_diskstream()->speed();
3470         }
3471         
3472         drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3473         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3474         drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3475         drag_info.dest_trackview = drag_info.source_trackview;
3476         drag_info.dest_layer = drag_info.source_layer;
3477         // we want a move threshold
3478         drag_info.want_move_threshold = true;
3479         drag_info.brushing = true;
3480         
3481         begin_reversible_command (_("Drag region brush"));
3482 }
3483
3484 void
3485 Editor::possibly_copy_regions_during_grab (GdkEvent* event)
3486 {
3487         if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
3488
3489                 drag_info.want_move_threshold = false; // don't copy again
3490
3491                 /* duplicate the regionview(s) and region(s) */
3492
3493                 vector<RegionView*> new_regionviews;
3494                 
3495                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3496                         RegionView* rv;
3497                         RegionView* nrv;
3498
3499                         rv = (*i);
3500                         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
3501                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
3502                         
3503                         const boost::shared_ptr<const Region> original = rv->region();
3504                         boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
3505
3506                         if (arv) {
3507                                 boost::shared_ptr<AudioRegion> audioregion_copy
3508                                         = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
3509                                 nrv = new AudioRegionView (*arv, audioregion_copy);
3510                         } else if (mrv) {
3511                                 boost::shared_ptr<MidiRegion> midiregion_copy
3512                                         = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
3513                                 nrv = new MidiRegionView (*mrv, midiregion_copy);
3514                         } else {
3515                                 continue;
3516                         }
3517
3518                         nrv->get_canvas_group()->show ();
3519                         new_regionviews.push_back (nrv);
3520                 }
3521
3522                 if (new_regionviews.empty()) {
3523                         return;
3524                 }
3525
3526                 /* reset selection to new regionviews. This will not set selection visual status for 
3527                    these regionviews since they don't belong to a track, so do that by hand too.
3528                  */
3529
3530                 selection->set (new_regionviews);
3531
3532                 for (vector<RegionView*>::iterator i = new_regionviews.begin(); i != new_regionviews.end(); ++i) {
3533                         (*i)->set_selected (true);
3534                 }
3535
3536                 /* reset drag_info data to reflect the fact that we are dragging the copies */
3537                 
3538                 drag_info.data = new_regionviews.front();
3539
3540                 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3541                 /* 
3542                    sync the canvas to what we think is its current state 
3543                    without it, the canvas seems to 
3544                    "forget" to update properly after the upcoming reparent() 
3545                    ..only if the mouse is in rapid motion at the time of the grab. 
3546                    something to do with regionview creation raking so long?
3547                  */
3548                 track_canvas->update_now();
3549         }
3550 }
3551
3552 bool
3553 Editor::check_region_drag_possible (RouteTimeAxisView** tv, layer_t* layer)
3554 {
3555         /* Which trackview is this ? */
3556
3557         std::pair<TimeAxisView*, int> const tvp = trackview_by_y_position (drag_info.current_pointer_y);
3558         (*tv) = dynamic_cast<RouteTimeAxisView*> (tvp.first);
3559         (*layer) = tvp.second;
3560
3561         /* The region motion is only processed if the pointer is over
3562            an audio track.
3563         */
3564         
3565         if (!(*tv) || !(*tv)->is_track()) {
3566                 /* To make sure we hide the verbose canvas cursor when the mouse is 
3567                    not held over and audiotrack. 
3568                 */
3569                 hide_verbose_canvas_cursor ();
3570                 return false;
3571         }
3572
3573         return true;
3574 }
3575
3576 struct RegionSelectionByPosition {
3577     bool operator() (RegionView*a, RegionView* b) {
3578             return a->region()->position () < b->region()->position();
3579     }
3580 };
3581
3582 void
3583 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3584 {
3585         RouteTimeAxisView* tv;
3586         layer_t layer;
3587         
3588         if (!check_region_drag_possible (&tv, &layer)) {
3589                 return;
3590         }
3591
3592         if (!drag_info.move_threshold_passed) {
3593                 return;
3594         }
3595
3596         int dir;
3597
3598         if (drag_info.current_pointer_x - drag_info.grab_x > 0) {
3599                 dir = 1;
3600         } else {
3601                 dir = -1;
3602         }
3603
3604         RegionSelection copy (selection->regions);
3605
3606         RegionSelectionByPosition cmp;
3607         copy.sort (cmp);
3608
3609         for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
3610
3611                 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
3612
3613                 if (!atv) {
3614                         continue;
3615                 }
3616
3617                 boost::shared_ptr<Playlist> playlist;
3618
3619                 if ((playlist = atv->playlist()) == 0) {
3620                         continue;
3621                 }
3622
3623                 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
3624                         continue;
3625                 } 
3626
3627                 if (dir > 0) {
3628                         if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) {
3629                                 continue;
3630                         }
3631                 } else {
3632                         if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) {
3633                                 continue;
3634                         }
3635                 }
3636
3637                 
3638                 playlist->shuffle ((*i)->region(), dir);
3639
3640                 drag_info.grab_x = drag_info.current_pointer_x;
3641         }
3642 }
3643
3644 void
3645 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3646 {
3647 }
3648
3649 void
3650 Editor::visible_order_range (int* low, int* high) const
3651 {
3652         *low = TimeAxisView::max_order ();
3653         *high = 0;
3654         
3655         for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
3656
3657                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3658                 
3659                 if (!rtv->hidden()) {
3660                         
3661                         if (*high < rtv->order()) {
3662                                 *high = rtv->order ();
3663                         }
3664                         
3665                         if (*low > rtv->order()) {
3666                                 *low = rtv->order ();
3667                         }
3668                 }
3669         }
3670 }
3671
3672 /** @param new_order New track order.
3673  *  @param old_order Old track order.
3674  *  @param visible_y_low Lowest visible order.
3675  *  @param visible_y_high Highest visible order.
3676  *  @param tracks Bitset of tracks indexed by order; 0 means a audio/MIDI track, 1 means something else.
3677  *  @param heigh_list Heights of tracks indexed by order.
3678  *  @return true if y movement should not happen, otherwise false.
3679  */
3680 bool
3681 Editor::y_movement_disallowed (
3682         int new_order, int old_order, int y_span, int visible_y_low, int visible_y_high,
3683         bitset<512> const & tracks, vector<int32_t> const & height_list
3684         ) const
3685 {
3686         if (new_order != old_order) {
3687
3688                 /* this isn't the pointer track */      
3689
3690                 if (y_span > 0) {
3691
3692                         /* moving up the canvas */
3693                         if ( (new_order - y_span) >= visible_y_low) {
3694
3695                                 int32_t n = 0;
3696
3697                                 /* work out where we'll end up with this y span, taking hidden TimeAxisViews into account */
3698                                 int32_t visible_tracks = 0;
3699                                 while (visible_tracks < y_span ) {
3700                                         visible_tracks++;
3701                                         while (height_list[new_order - (visible_tracks - n)] == 0) {
3702                                                 /* passing through a hidden track */
3703                                                 n--;
3704                                         }                 
3705                                 }
3706                  
3707                                 if (tracks[new_order - (y_span - n)] != 0x00) {
3708                                         /* moving to a non-track; disallow */
3709                                         return true;
3710                                 }
3711                                 
3712
3713                         } else {
3714                                 /* moving beyond the lowest visible track; disallow */
3715                                 return true;
3716                         }                 
3717                   
3718                 } else if (y_span < 0) {
3719
3720                         /* moving down the canvas */
3721                         if ((new_order - y_span) <= visible_y_high) {
3722
3723                                 int32_t visible_tracks = 0;
3724                                 int32_t n = 0;
3725                                 while (visible_tracks > y_span ) {
3726                                         visible_tracks--;
3727                       
3728                                         while (height_list[new_order - (visible_tracks - n)] == 0) {
3729                                                 /* passing through a hidden track */
3730                                                 n++;
3731                                         }                
3732                                 }
3733                                                 
3734                                 if (tracks[new_order - (y_span - n)] != 0x00) {
3735                                         /* moving to a non-track; disallow */
3736                                         return true;
3737                                 }
3738
3739                                 
3740                         } else {
3741
3742                                 /* moving beyond the highest visible track; disallow */
3743                                 return true;
3744                         }
3745                 }               
3746                 
3747         } else {
3748                 
3749                 /* this is the pointer's track */
3750                 
3751                 if ((new_order - y_span) > visible_y_high) {
3752                         /* we will overflow */
3753                         return true;
3754                 } else if ((new_order - y_span) < visible_y_low) {
3755                         /* we will overflow */
3756                         return true;
3757                 }
3758         }
3759
3760         return false;
3761 }
3762
3763
3764 void
3765 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3766 {
3767         double x_delta;
3768         double y_delta = 0;
3769         nframes64_t pending_region_position = 0;
3770         int32_t pointer_y_span = 0, canvas_pointer_y_span = 0;
3771         int32_t layer_span = 0;
3772         
3773         bool clamp_y_axis = false;
3774         vector<int32_t>::iterator j;
3775
3776         possibly_copy_regions_during_grab (event);
3777
3778         /* tv will become the TimeAxisView that we're currently pointing at, and
3779            current_layer the current layer on that TimeAxisView */
3780         RouteTimeAxisView* tv;
3781         layer_t current_layer;
3782         if (!check_region_drag_possible (&tv, &current_layer)) {
3783                 return;
3784         }
3785
3786         /* the order of the track that we were pointing at last time we entered this method */
3787         int32_t const last_pointer_order = drag_info.dest_trackview->order ();
3788         /* the layer that we were pointing at last time we entered this method */
3789         layer_t const last_pointer_layer = drag_info.dest_layer;
3790         
3791         /************************************************************
3792              Y DELTA COMPUTATION
3793         ************************************************************/   
3794
3795         /* Height of TimeAxisViews, indexed by order */
3796         /* XXX: hard-coded limit of TimeAxisViews */
3797         vector<int32_t> height_list (512);
3798         
3799         if (drag_info.brushing) {
3800                 clamp_y_axis = true;
3801                 pointer_y_span = 0;
3802                 goto y_axis_done;
3803         }
3804
3805         /* the change in track order between this callback and the last */
3806         pointer_y_span = drag_info.dest_trackview->order() - tv->order();
3807         /* the change in layer between this callback and the last */
3808         layer_span = drag_info.dest_layer - current_layer;
3809         
3810         if (pointer_y_span != 0) {
3811
3812                 int32_t children = 0;
3813                 /* XXX: hard-coded limit of tracks */
3814                 bitset <512> tracks (0x00);
3815
3816                 int visible_y_high;
3817                 int visible_y_low;
3818                 visible_order_range (&visible_y_low, &visible_y_high);
3819                 
3820                 /* get a bitmask representing the visible tracks */
3821
3822                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3823                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3824                         TimeAxisView::Children children_list;
3825               
3826                         /* zeroes are audio/MIDI tracks. ones are other types. */
3827               
3828                         if (!rtv->hidden()) {
3829                                 
3830                                 if (!rtv->is_track()) {
3831                                         /* not an audio nor MIDI track */
3832                                         tracks = tracks |= (0x01 << rtv->order());
3833                                 }
3834         
3835                                 height_list[rtv->order()] = (*i)->current_height();
3836                                 children = 1;
3837
3838                                 if ((children_list = rtv->get_child_list()).size() > 0) {
3839                                         for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) { 
3840                                                 tracks = tracks |= (0x01 << (rtv->order() + children));
3841                                                 height_list[rtv->order() + children] = (*j)->current_height();
3842                                                 children++;     
3843                                         }
3844                                 }
3845                         }
3846                 }
3847                 
3848                 /* find the actual span, in terms of the number of visible tracks;
3849                    to do this, we reduce |pointer_y_span| by the number of hidden tracks */
3850
3851                 canvas_pointer_y_span = pointer_y_span;
3852                 if (drag_info.dest_trackview->order() >= tv->order()) {
3853                         for (int32_t y = tv->order(); y < drag_info.dest_trackview->order(); y++) {
3854                                 if (height_list[y] == 0) {
3855                                         canvas_pointer_y_span--;
3856                                 }
3857                         }
3858                 } else {
3859                         for (int32_t y = drag_info.dest_trackview->order(); y <= tv->order(); y++) {
3860                                 if (height_list[y] == 0) {
3861                                         canvas_pointer_y_span++;
3862                                 }
3863                         }
3864                 }
3865
3866                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3867                         
3868                         RegionView* rv = (*i);
3869
3870                         if (rv->region()->locked()) {
3871                                 continue;
3872                         }
3873
3874                         double ix1, ix2, iy1, iy2;
3875                         rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3876                         rv->get_canvas_frame()->i2w (ix1, iy1);
3877                         iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
3878
3879                         std::pair<TimeAxisView*, int> const tvp = trackview_by_y_position (iy1);
3880                         assert (tvp.first);
3881                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
3882
3883                         /* I know this method has a slightly excessive argument list, but I think
3884                            it's nice to separate the code out all the same, since it has such a simple result,
3885                            and it makes it clear that there are no other side-effects.
3886                         */
3887                         clamp_y_axis = y_movement_disallowed (
3888                                 rtv->order(), last_pointer_order, canvas_pointer_y_span, visible_y_low, visible_y_high,
3889                                 tracks, height_list
3890                                 );
3891
3892                         if (clamp_y_axis) {
3893                                 break;
3894                         }
3895                 }
3896
3897         } else if (drag_info.dest_trackview == tv) {
3898
3899                 if (current_layer == last_pointer_layer) {
3900                         /* No movement; clamp */
3901                         clamp_y_axis = true;
3902                 } 
3903         }
3904
3905   y_axis_done:
3906         if (!clamp_y_axis) {
3907                 drag_info.dest_trackview = tv;
3908                 drag_info.dest_layer = current_layer;
3909         }
3910           
3911         /************************************************************
3912             X DELTA COMPUTATION
3913         ************************************************************/
3914
3915         /* compute the amount of pointer motion in frames, and where
3916            the region would be if we moved it by that much.
3917         */
3918         if ( drag_info.move_threshold_passed ) {
3919
3920                 if (drag_info.current_pointer_frame >= drag_info.pointer_frame_offset) {
3921
3922                         nframes64_t sync_frame;
3923                         nframes64_t sync_offset;
3924                         int32_t sync_dir;
3925
3926                         pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3927
3928                         sync_offset = clicked_regionview->region()->sync_offset (sync_dir);
3929
3930                         /* we don't handle a sync point that lies before zero.
3931                          */
3932                         if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) {
3933                                 sync_frame = pending_region_position + (sync_dir*sync_offset);
3934
3935                                 /* we snap if the snap modifier is not enabled.
3936                                  */
3937             
3938                                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3939                                         snap_to (sync_frame);   
3940                                 }
3941             
3942                                 pending_region_position = clicked_regionview->region()->adjust_to_sync (sync_frame);
3943
3944                         } else {
3945                                 pending_region_position = drag_info.last_frame_position;
3946                         }
3947             
3948                 } else {
3949                         pending_region_position = 0;
3950                 }
3951           
3952                 if (pending_region_position > max_frames - clicked_regionview->region()->length()) {
3953                         pending_region_position = drag_info.last_frame_position;
3954                 }
3955
3956                 bool x_move_allowed;
3957                 
3958                 if (Config->get_edit_mode() == Lock) {
3959                         if (drag_info.copy) {
3960                                 x_move_allowed = !drag_info.x_constrained;
3961                         } else {
3962                                 /* in locked edit mode, reverse the usual meaning of x_constrained */
3963                                 x_move_allowed = drag_info.x_constrained;
3964                         }
3965                 } else {
3966                         x_move_allowed = !drag_info.x_constrained;
3967                 }
3968
3969                 if (( pending_region_position != drag_info.last_frame_position) && x_move_allowed ) {
3970
3971                         /* now compute the canvas unit distance we need to move the regionview
3972                            to make it appear at the new location.
3973                         */
3974
3975                         if (pending_region_position > drag_info.last_frame_position) {
3976                                 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3977                         } else {
3978                                 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3979                                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3980
3981                                         RegionView* rv = (*i);
3982
3983                                         // If any regionview is at zero, we need to know so we can stop further leftward motion.
3984         
3985                                         double ix1, ix2, iy1, iy2;
3986                                         rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3987                                         rv->get_canvas_frame()->i2w (ix1, iy1);
3988
3989                                         if (-x_delta > ix1 + horizontal_adjustment.get_value()) {
3990                                                 x_delta = 0;
3991                                                 pending_region_position = drag_info.last_frame_position;
3992                                                 break;
3993                                         }
3994                                 }
3995
3996                         }
3997                 
3998                         drag_info.last_frame_position = pending_region_position;
3999
4000                 } else {
4001                         x_delta = 0;
4002                 }
4003
4004         } else {
4005                 /* threshold not passed */
4006
4007                 x_delta = 0;
4008         }
4009         
4010         /*************************************************************
4011             PREPARE TO MOVE
4012         ************************************************************/
4013
4014         if (x_delta == 0 && pointer_y_span == 0 && layer_span == 0) {
4015                 /* haven't reached next snap point, and we're not switching
4016                    trackviews nor layers. nothing to do.
4017                 */
4018                 return;
4019         }
4020
4021         /*************************************************************
4022             MOTION                                                                    
4023         ************************************************************/
4024         bool do_move = true;
4025         if (drag_info.first_move) {
4026                 if (!drag_info.move_threshold_passed) {
4027                         do_move = false;
4028                 }
4029         }
4030
4031         if (do_move) {
4032
4033                 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4034                 const list<RegionView*>& layered_regions = selection->regions.by_layer();
4035                 
4036                 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
4037             
4038                         RegionView* rv = (*i);
4039
4040                         if (rv->region()->locked()) {
4041                                 continue;
4042                         }
4043
4044                         /* here we are calculating the y distance from the
4045                            top of the first track view to the top of the region
4046                            area of the track view that we're working on */
4047
4048                         /* this x value is just a dummy value so that we have something
4049                            to pass to i2w () */
4050
4051                         double ix1 = 0;
4052
4053                         /* distance from the top of this track view to the region area
4054                            of our track view is always 1 */
4055                         
4056                         double iy1 = 1;
4057
4058                         /* convert to world coordinates, ie distance from the top of
4059                            the ruler section */
4060                         
4061                         rv->get_canvas_frame()->i2w (ix1, iy1);
4062
4063                         /* compensate for the ruler section and the vertical scrollbar position */
4064                         iy1 += get_trackview_group_vertical_offset ();
4065
4066                         if (drag_info.first_move) {
4067
4068                                 // hide any dependent views 
4069         
4070                                 rv->get_time_axis_view().hide_dependent_views (*rv);
4071
4072                                 /* 
4073                                    reparent to a non scrolling group so that we can keep the 
4074                                    region selection above all time axis views.
4075                                    reparenting means we have to move the rv as the two 
4076                                    parent groups have different coordinates.
4077                                 */
4078
4079                                 rv->get_canvas_group()->property_y() = iy1 - 1;
4080                                 rv->get_canvas_group()->reparent(*_region_motion_group);
4081
4082                                 rv->fake_set_opaque (true);
4083                         }
4084
4085                         std::pair<TimeAxisView*, int> pos = trackview_by_y_position (iy1);
4086                         RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
4087
4088                         if (pointer_y_span != 0 && !clamp_y_axis) {
4089
4090                                 /* INTER-TRACK MOVEMENT */
4091
4092                                 /* move through the height list to the track that the region is going to */
4093                                 vector<int32_t>::iterator j = height_list.begin ();
4094                                 int32_t x = 0;
4095                                 while (j != height_list.end () && x != canvas_rtv->order ()) {
4096                                         ++x;
4097                                         ++j;
4098                                 }
4099
4100                                 y_delta = 0;
4101                                 int32_t temp_pointer_y_span = pointer_y_span;
4102
4103                                 if (j != height_list.end ()) {
4104                                 
4105                                         while (temp_pointer_y_span > 0) {
4106                                                 /* we're moving up canvas-wise,
4107                                                    so we need to find the next track height
4108                                                 */
4109                                                 if (j != height_list.begin()) {           
4110                                                         j--;
4111                                                 }
4112                                                 
4113                                                 y_delta -= (*j);
4114                                                 temp_pointer_y_span--;  
4115                                         }
4116                                         
4117                                         while (temp_pointer_y_span < 0) {
4118
4119                                                 y_delta += (*j);
4120                                                 
4121                                                 if (j != height_list.end()) {                 
4122                                                         j++;
4123                                                 }
4124                                                 
4125                                                 temp_pointer_y_span++;
4126                                         }
4127                                         
4128                                         /* find out where we'll be when we move and set height accordingly */
4129
4130                                         std::pair<TimeAxisView*, int> const pos = trackview_by_y_position (iy1 + y_delta);
4131                                         RouteTimeAxisView const * temp_rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
4132                                         rv->set_height (temp_rtv->view()->child_height());
4133                                 
4134                                         /* if you un-comment the following, the region colours will follow
4135                                            the track colours whilst dragging; personally
4136                                            i think this can confuse things, but never mind.
4137                                         */
4138                                         
4139                                         //const GdkColor& col (temp_rtv->view->get_region_color());
4140                                         //rv->set_color (const_cast<GdkColor&>(col));
4141                                 }
4142                         }
4143
4144                         if (pointer_y_span == 0 && layer_span != 0 && !clamp_y_axis) {
4145
4146                                 /* INTER-LAYER MOVEMENT in the same track */
4147                                 assert (tv->view ());
4148                                 y_delta = tv->view()->child_height () * layer_span;
4149                         }
4150
4151
4152                         if (drag_info.brushing) {
4153                                 mouse_brush_insert_region (rv, pending_region_position);
4154                         } else {
4155                                 rv->move (x_delta, y_delta);
4156                         }
4157
4158                 } /* foreach region */
4159
4160         } /* if do_move */
4161
4162         if (drag_info.first_move && drag_info.move_threshold_passed) {
4163                 cursor_group->raise_to_top();
4164                 drag_info.first_move = false;
4165         }
4166
4167         if (x_delta != 0 && !drag_info.brushing) {
4168                 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4169         }
4170
4171
4172 void
4173 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4174 {
4175         bool nocommit = true;
4176         vector<RegionView*> copies;
4177         RouteTimeAxisView* source_tv;
4178         boost::shared_ptr<Diskstream> ds;
4179         boost::shared_ptr<Playlist> from_playlist;
4180         vector<RegionView*> new_selection;
4181         typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
4182         PlaylistSet modified_playlists;
4183         PlaylistSet frozen_playlists;
4184         list <sigc::connection> modified_playlist_connections;
4185         pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
4186         nframes64_t drag_delta;
4187         bool changed_tracks, changed_position;
4188         std::pair<TimeAxisView*, int> tvp;
4189
4190         /* first_move is set to false if the regionview has been moved in the 
4191            motion handler. 
4192         */
4193
4194         if (drag_info.first_move) {
4195                 /* just a click */
4196                 goto out;
4197         }
4198
4199         nocommit = false;
4200
4201         if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
4202                 selection->set (pre_drag_region_selection);
4203                 pre_drag_region_selection.clear ();
4204         }
4205
4206         if (drag_info.brushing) {
4207                 /* all changes were made during motion event handlers */
4208                 
4209                 if (drag_info.copy) {
4210                         for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4211                                 copies.push_back (*i);
4212                         }
4213                 }
4214
4215                 goto out;
4216         }
4217
4218         char* op_string;
4219
4220         /* reverse this here so that we have the correct logic to finalize
4221            the drag.
4222         */
4223         
4224         if (Config->get_edit_mode() == Lock && !drag_info.copy) {
4225                 drag_info.x_constrained = !drag_info.x_constrained;
4226         }
4227
4228         if (drag_info.copy) {
4229                 if (drag_info.x_constrained) {
4230                         op_string = _("fixed time region copy");
4231                 } else {
4232                         op_string = _("region copy");
4233                 } 
4234         } else {
4235                 if (drag_info.x_constrained) {
4236                         op_string = _("fixed time region drag");
4237                 } else {
4238                         op_string = _("region drag");
4239                 }
4240         }
4241
4242         begin_reversible_command (op_string);
4243         changed_position = (drag_info.last_frame_position != (nframes64_t) (clicked_regionview->region()->position()));
4244         tvp = trackview_by_y_position (drag_info.current_pointer_y);
4245         changed_tracks = (tvp.first != &clicked_regionview->get_time_axis_view());
4246
4247         drag_delta = clicked_regionview->region()->position() - drag_info.last_frame_position;
4248
4249         track_canvas->update_now ();
4250
4251         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
4252                         
4253                 RegionView* rv = (*i);              
4254                 double ix1, ix2, iy1, iy2;
4255                 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
4256                 rv->get_canvas_frame()->i2w (ix1, iy1);
4257                 iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
4258
4259                 std::pair<TimeAxisView*, int> const dest_tv = trackview_by_y_position (iy1);
4260                 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (dest_tv.first);
4261
4262                 nframes64_t where;
4263
4264                 if (rv->region()->locked()) {
4265                         ++i;
4266                         continue;
4267                 }
4268
4269                 if (changed_position && !drag_info.x_constrained) {
4270                         where = rv->region()->position() - drag_delta;
4271                 } else {
4272                         where = rv->region()->position();
4273                 }
4274                         
4275                 boost::shared_ptr<Region> new_region;
4276
4277                 if (drag_info.copy) {
4278                         /* we already made a copy */
4279                         new_region = rv->region();
4280
4281                         /* undo the previous hide_dependent_views so that xfades don't
4282                            disappear on copying regions 
4283                         */
4284                 
4285                         //rv->get_time_axis_view().reveal_dependent_views (*rv);
4286                 
4287                 } else if (changed_tracks && dest_rtv->playlist()) {
4288                         new_region = RegionFactory::create (rv->region());
4289                 }
4290
4291                 if (changed_tracks || drag_info.copy) {
4292
4293                         boost::shared_ptr<Playlist> to_playlist = dest_rtv->playlist();
4294                         if (!to_playlist) {
4295                                 ++i;
4296                                 continue;
4297                         }
4298
4299                         latest_regionviews.clear ();
4300
4301                         sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4302                         
4303                         insert_result = modified_playlists.insert (to_playlist);
4304                         if (insert_result.second) {
4305                                 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
4306                         }
4307
4308                         to_playlist->add_region (new_region, where);
4309
4310                         c.disconnect ();
4311                                                               
4312                         if (!latest_regionviews.empty()) {
4313                                 // XXX why just the first one ? we only expect one
4314                                 // commented out in nick_m's canvas reworking. is that intended?
4315                                 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
4316                                 new_selection.push_back (latest_regionviews.front());
4317                         }
4318
4319                 } else {
4320                         /* 
4321                            motion on the same track. plonk the previously reparented region 
4322                            back to its original canvas group (its streamview).
4323                            No need to do anything for copies as they are fake regions which will be deleted.
4324                         */
4325
4326                         rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
4327                         rv->get_canvas_group()->property_y() = 0;
4328                   
4329                         /* just change the model */
4330                         
4331                         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
4332
4333                         insert_result = modified_playlists.insert (playlist);
4334                         if (insert_result.second) {
4335                                 session->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
4336                         }
4337                         /* freeze to avoid lots of relayering in the case of a multi-region drag */
4338                         frozen_insert_result = frozen_playlists.insert(playlist);
4339                         if (frozen_insert_result.second) {
4340                                 playlist->freeze();
4341                         }
4342
4343                         rv->region()->set_position (where, (void*) this);
4344                 }
4345
4346                 if (changed_tracks && !drag_info.copy) {
4347
4348                         /* get the playlist where this drag started. we can't use rv->region()->playlist()
4349                            because we may have copied the region and it has not been attached to a playlist.
4350                         */
4351
4352                         assert ((source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view())));
4353                         assert ((ds = source_tv->get_diskstream()));
4354                         assert ((from_playlist = ds->playlist()));
4355
4356                         /* moved to a different audio track, without copying */
4357
4358                         /* the region that used to be in the old playlist is not
4359                            moved to the new one - we use a copy of it. as a result,
4360                            any existing editor for the region should no longer be
4361                            visible.
4362                         */ 
4363             
4364                         rv->hide_region_editor();
4365                         rv->fake_set_opaque (false);
4366                         
4367                         /* remove the region from the old playlist */
4368
4369                         insert_result = modified_playlists.insert (from_playlist);
4370                         if (insert_result.second) {
4371                                 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
4372                         }
4373
4374                         from_playlist->remove_region ((rv->region()));
4375                         
4376                         /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
4377                            was selected in all of them, then removing it from a playlist will have removed all
4378                            trace of it from the selection (i.e. there were N regions selected, we removed 1,
4379                            but since its the same playlist for N tracks, all N tracks updated themselves, removed the
4380                            corresponding regionview, and the selection is now empty).
4381
4382                            this could have invalidated any and all iterators into the region selection.
4383
4384                            the heuristic we use here is: if the region selection is empty, break out of the loop
4385                            here. if the region selection is not empty, then restart the loop because we know that
4386                            we must have removed at least the region(view) we've just been working on as well as any
4387                            that we processed on previous iterations.
4388
4389                            EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
4390                            we can just iterate.
4391                         */
4392
4393                         if (selection->regions.empty()) {
4394                                 break;
4395                         } else { 
4396                                 i = selection->regions.by_layer().begin();
4397                         }
4398
4399                 } else {
4400                         ++i;
4401                 }
4402                 
4403                 if (drag_info.copy) {
4404                         copies.push_back (rv);
4405                 }
4406         }
4407         
4408         if (new_selection.empty()) {
4409                 if (drag_info.copy) {
4410                         /* the region(view)s that are selected and being dragged around
4411                            are copies and do not belong to any track. remove them
4412                            from the selection right here.
4413                         */
4414                         selection->clear_regions();
4415                 }
4416         } else {
4417                 /* this will clear any existing selection that would have been
4418                    cleared in the other clause above
4419                 */
4420                 selection->set (new_selection);
4421         }
4422
4423         for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
4424                 (*p)->thaw();
4425         }
4426                         
4427   out:
4428         if (!nocommit) {
4429                 for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
4430                         session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));      
4431                 }
4432                 commit_reversible_command ();
4433         }
4434
4435         for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
4436                 delete *x;
4437         }
4438
4439 }
4440         
4441 void
4442 Editor::create_region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4443 {
4444         if (drag_info.move_threshold_passed) {
4445                 if (drag_info.first_move) {
4446                         // TODO: create region-create-drag region view here
4447                         drag_info.first_move = false;
4448                 }
4449
4450                 // TODO: resize region-create-drag region view here
4451         }
4452
4453
4454 void
4455 Editor::create_region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4456 {
4457         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (drag_info.dest_trackview);
4458         if (!mtv)
4459                 return;
4460
4461         const boost::shared_ptr<MidiDiskstream> diskstream =
4462                 boost::dynamic_pointer_cast<MidiDiskstream>(mtv->view()->trackview().track()->diskstream());
4463         
4464         if (!diskstream) {
4465                 warning << "Cannot create non-MIDI region" << endl;
4466                 return;
4467         }
4468
4469         if (drag_info.first_move) {
4470                 begin_reversible_command (_("create region"));
4471                 XMLNode &before = mtv->playlist()->get_state();
4472
4473                 nframes64_t start = drag_info.grab_frame;
4474                 snap_to (start, -1);
4475                 const Meter& m = session->tempo_map().meter_at(start);
4476                 const Tempo& t = session->tempo_map().tempo_at(start);
4477                 double length = floor (m.frames_per_bar(t, session->frame_rate()));
4478
4479                 boost::shared_ptr<Source> src = session->create_midi_source_for_session(*diskstream.get());
4480                                 
4481                 mtv->playlist()->add_region (boost::dynamic_pointer_cast<MidiRegion>
4482                                              (RegionFactory::create(src, 0, (nframes_t) length, 
4483                                                                     PBD::basename_nosuffix(src->name()))), start);
4484                 XMLNode &after = mtv->playlist()->get_state();
4485                 session->add_command(new MementoCommand<Playlist>(*mtv->playlist().get(), &before, &after));
4486                 commit_reversible_command();
4487
4488         } else {
4489                 create_region_drag_motion_callback (item, event);
4490                 // TODO: create region-create-drag region here
4491         }
4492 }
4493
4494 void
4495 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
4496 {
4497         /* Either add to or set the set the region selection, unless
4498            this is an alignment click (control used)
4499         */
4500         
4501         if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
4502                 TimeAxisView* tv = &rv.get_time_axis_view();
4503                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
4504                 double speed = 1.0;
4505                 if (rtv && rtv->is_track()) {
4506                         speed = rtv->get_diskstream()->speed();
4507                 }
4508
4509                 nframes64_t where = get_preferred_edit_position();
4510
4511                 if (where >= 0) {
4512
4513                         if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
4514                                 
4515                                 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
4516                                 
4517                         } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
4518                                 
4519                                 align_region (rv.region(), End, (nframes64_t) (where * speed));
4520                                 
4521                         } else {
4522                                 
4523                                 align_region (rv.region(), Start, (nframes64_t) (where * speed));
4524                         }
4525                 }
4526         }
4527 }
4528
4529 void
4530 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos) 
4531 {
4532         char buf[128];
4533         SMPTE::Time smpte;
4534         BBT_Time bbt;
4535         int hours, mins;
4536         nframes64_t frame_rate;
4537         float secs;
4538
4539         if (session == 0) {
4540                 return;
4541         }
4542
4543         AudioClock::Mode m;
4544
4545         if (Profile->get_sae() || Profile->get_small_screen()) {
4546                 m = ARDOUR_UI::instance()->primary_clock.mode();
4547         } else {
4548                 m = ARDOUR_UI::instance()->secondary_clock.mode();
4549         }
4550
4551         switch (m) {
4552         case AudioClock::BBT:
4553                 session->bbt_time (frame, bbt);
4554                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
4555                 break;
4556                 
4557         case AudioClock::SMPTE:
4558                 session->smpte_time (frame, smpte);
4559                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4560                 break;
4561
4562         case AudioClock::MinSec:
4563                 /* XXX this is copied from show_verbose_duration_cursor() */
4564                 frame_rate = session->frame_rate();
4565                 hours = frame / (frame_rate * 3600);
4566                 frame = frame % (frame_rate * 3600);
4567                 mins = frame / (frame_rate * 60);
4568                 frame = frame % (frame_rate * 60);
4569                 secs = (float) frame / (float) frame_rate;
4570                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4571                 break;
4572
4573         default:
4574                 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
4575                 break;
4576         }
4577
4578         if (xpos >= 0 && ypos >=0) {
4579                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4580         }
4581         else {
4582                 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);
4583         }
4584         show_verbose_canvas_cursor ();
4585 }
4586
4587 void
4588 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos) 
4589 {
4590         char buf[128];
4591         SMPTE::Time smpte;
4592         BBT_Time sbbt;
4593         BBT_Time ebbt;
4594         int hours, mins;
4595         nframes64_t distance, frame_rate;
4596         float secs;
4597         Meter meter_at_start(session->tempo_map().meter_at(start));
4598
4599         if (session == 0) {
4600                 return;
4601         }
4602
4603         AudioClock::Mode m;
4604
4605         if (Profile->get_sae() || Profile->get_small_screen()) {
4606                 m = ARDOUR_UI::instance()->primary_clock.mode ();
4607         } else {
4608                 m = ARDOUR_UI::instance()->secondary_clock.mode ();
4609         }
4610
4611         switch (m) {
4612         case AudioClock::BBT:
4613                 session->bbt_time (start, sbbt);
4614                 session->bbt_time (end, ebbt);
4615
4616                 /* subtract */
4617                 /* XXX this computation won't work well if the
4618                 user makes a selection that spans any meter changes.
4619                 */
4620
4621                 ebbt.bars -= sbbt.bars;
4622                 if (ebbt.beats >= sbbt.beats) {
4623                         ebbt.beats -= sbbt.beats;
4624                 } else {
4625                         ebbt.bars--;
4626                         ebbt.beats =  int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
4627                 }
4628                 if (ebbt.ticks >= sbbt.ticks) {
4629                         ebbt.ticks -= sbbt.ticks;
4630                 } else {
4631                         ebbt.beats--;
4632                         ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
4633                 }
4634                 
4635                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
4636                 break;
4637                 
4638         case AudioClock::SMPTE:
4639                 session->smpte_duration (end - start, smpte);
4640                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4641                 break;
4642
4643         case AudioClock::MinSec:
4644                 /* XXX this stuff should be elsewhere.. */
4645                 distance = end - start;
4646                 frame_rate = session->frame_rate();
4647                 hours = distance / (frame_rate * 3600);
4648                 distance = distance % (frame_rate * 3600);
4649                 mins = distance / (frame_rate * 60);
4650                 distance = distance % (frame_rate * 60);
4651                 secs = (float) distance / (float) frame_rate;
4652                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4653                 break;
4654
4655         default:
4656                 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
4657                 break;
4658         }
4659
4660         if (xpos >= 0 && ypos >=0) {
4661                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4662         }
4663         else {
4664                 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4665         }
4666
4667         show_verbose_canvas_cursor ();
4668 }
4669
4670 void
4671 Editor::collect_new_region_view (RegionView* rv)
4672 {
4673         latest_regionviews.push_back (rv);
4674 }
4675
4676 void
4677 Editor::collect_and_select_new_region_view (RegionView* rv)
4678 {
4679         selection->add(rv);
4680         latest_regionviews.push_back (rv);
4681 }
4682
4683 void
4684 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
4685 {
4686         if (clicked_regionview == 0) {
4687                 return;
4688         }
4689
4690         /* lets try to create new Region for the selection */
4691
4692         vector<boost::shared_ptr<Region> > new_regions;
4693         create_region_from_selection (new_regions);
4694
4695         if (new_regions.empty()) {
4696                 return;
4697         }
4698
4699         /* XXX fix me one day to use all new regions */
4700         
4701         boost::shared_ptr<Region> region (new_regions.front());
4702
4703         /* add it to the current stream/playlist.
4704
4705            tricky: the streamview for the track will add a new regionview. we will
4706            catch the signal it sends when it creates the regionview to
4707            set the regionview we want to then drag.
4708         */
4709         
4710         latest_regionviews.clear();
4711         sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4712         
4713         /* A selection grab currently creates two undo/redo operations, one for 
4714            creating the new region and another for moving it.
4715         */
4716
4717         begin_reversible_command (_("selection grab"));
4718
4719         boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
4720
4721         XMLNode *before = &(playlist->get_state());
4722         clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
4723         XMLNode *after = &(playlist->get_state());
4724         session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4725
4726         commit_reversible_command ();
4727         
4728         c.disconnect ();
4729         
4730         if (latest_regionviews.empty()) {
4731                 /* something went wrong */
4732                 return;
4733         }
4734
4735         /* we need to deselect all other regionviews, and select this one
4736            i'm ignoring undo stuff, because the region creation will take care of it 
4737         */
4738         selection->set (latest_regionviews);
4739         
4740         drag_info.item = latest_regionviews.front()->get_canvas_group();
4741         drag_info.data = latest_regionviews.front();
4742         drag_info.motion_callback = &Editor::region_drag_motion_callback;
4743         drag_info.finished_callback = &Editor::region_drag_finished_callback;
4744
4745         start_grab (event);
4746         
4747         drag_info.source_trackview = clicked_routeview;
4748         drag_info.dest_trackview = drag_info.source_trackview;
4749         drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4750         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4751         
4752         show_verbose_time_cursor (drag_info.last_frame_position, 10);
4753 }
4754
4755 void
4756 Editor::cancel_selection ()
4757 {
4758         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4759                 (*i)->hide_selection ();
4760         }
4761         selection->clear ();
4762         clicked_selection = 0;
4763 }       
4764
4765 void
4766 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4767 {
4768         nframes64_t start = 0;
4769         nframes64_t end = 0;
4770
4771         if (session == 0) {
4772                 return;
4773         }
4774
4775         drag_info.item = item;
4776         drag_info.motion_callback = &Editor::drag_selection;
4777         drag_info.finished_callback = &Editor::end_selection_op;
4778
4779         selection_op = op;
4780
4781         switch (op) {
4782         case CreateSelection:
4783                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4784                         drag_info.copy = true;
4785                 } else {
4786                         drag_info.copy = false;
4787                 }
4788                 start_grab (event, selector_cursor);
4789                 break;
4790
4791         case SelectionStartTrim:
4792                 if (clicked_axisview) {
4793                         clicked_axisview->order_selection_trims (item, true);
4794                 } 
4795                 start_grab (event, trimmer_cursor);
4796                 start = selection->time[clicked_selection].start;
4797                 drag_info.pointer_frame_offset = drag_info.grab_frame - start;  
4798                 break;
4799                 
4800         case SelectionEndTrim:
4801                 if (clicked_axisview) {
4802                         clicked_axisview->order_selection_trims (item, false);
4803                 }
4804                 start_grab (event, trimmer_cursor);
4805                 end = selection->time[clicked_selection].end;
4806                 drag_info.pointer_frame_offset = drag_info.grab_frame - end;    
4807                 break;
4808
4809         case SelectionMove:
4810                 start = selection->time[clicked_selection].start;
4811                 start_grab (event);
4812                 drag_info.pointer_frame_offset = drag_info.grab_frame - start;  
4813                 break;
4814         }
4815
4816         if (selection_op == SelectionMove) {
4817                 show_verbose_time_cursor(start, 10);    
4818         } else {
4819                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4820         }
4821 }
4822
4823 void
4824 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4825 {
4826         nframes64_t start = 0;
4827         nframes64_t end = 0;
4828         nframes64_t length;
4829         nframes64_t pending_position;
4830
4831         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4832                 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4833         } else {
4834                 pending_position = 0;
4835         }
4836         
4837         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4838                 snap_to (pending_position);
4839         }
4840
4841         /* only alter selection if the current frame is 
4842            different from the last frame position (adjusted)
4843          */
4844         
4845         if (pending_position == drag_info.last_pointer_frame) return;
4846         
4847         switch (selection_op) {
4848         case CreateSelection:
4849                 
4850                 if (drag_info.first_move) {
4851                         snap_to (drag_info.grab_frame);
4852                 }
4853                 
4854                 if (pending_position < drag_info.grab_frame) {
4855                         start = pending_position;
4856                         end = drag_info.grab_frame;
4857                 } else {
4858                         end = pending_position;
4859                         start = drag_info.grab_frame;
4860                 }
4861                 
4862                 /* first drag: Either add to the selection
4863                    or create a new selection->
4864                 */
4865                 
4866                 if (drag_info.first_move) {
4867                         
4868                         begin_reversible_command (_("range selection"));
4869                         
4870                         if (drag_info.copy) {
4871                                 /* adding to the selection */
4872                                 clicked_selection = selection->add (start, end);
4873                                 drag_info.copy = false;
4874                         } else {
4875                                 /* new selection-> */
4876                                 clicked_selection = selection->set (clicked_axisview, start, end);
4877                         }
4878                 } 
4879                 break;
4880                 
4881         case SelectionStartTrim:
4882                 
4883                 if (drag_info.first_move) {
4884                         begin_reversible_command (_("trim selection start"));
4885                 }
4886                 
4887                 start = selection->time[clicked_selection].start;
4888                 end = selection->time[clicked_selection].end;
4889
4890                 if (pending_position > end) {
4891                         start = end;
4892                 } else {
4893                         start = pending_position;
4894                 }
4895                 break;
4896                 
4897         case SelectionEndTrim:
4898                 
4899                 if (drag_info.first_move) {
4900                         begin_reversible_command (_("trim selection end"));
4901                 }
4902                 
4903                 start = selection->time[clicked_selection].start;
4904                 end = selection->time[clicked_selection].end;
4905
4906                 if (pending_position < start) {
4907                         end = start;
4908                 } else {
4909                         end = pending_position;
4910                 }
4911                 
4912                 break;
4913                 
4914         case SelectionMove:
4915                 
4916                 if (drag_info.first_move) {
4917                         begin_reversible_command (_("move selection"));
4918                 }
4919                 
4920                 start = selection->time[clicked_selection].start;
4921                 end = selection->time[clicked_selection].end;
4922                 
4923                 length = end - start;
4924                 
4925                 start = pending_position;
4926                 snap_to (start);
4927                 
4928                 end = start + length;
4929                 
4930                 break;
4931         }
4932         
4933         if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4934                 start_canvas_autoscroll (1, 0);
4935         }
4936
4937         if (start != end) {
4938                 selection->replace (clicked_selection, start, end);
4939         }
4940
4941         drag_info.last_pointer_frame = pending_position;
4942         drag_info.first_move = false;
4943
4944         if (selection_op == SelectionMove) {
4945                 show_verbose_time_cursor(start, 10);    
4946         } else {
4947                 show_verbose_time_cursor(pending_position, 10); 
4948         }
4949 }
4950
4951 void
4952 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4953 {
4954         if (!drag_info.first_move) {
4955                 drag_selection (item, event);
4956                 /* XXX this is not object-oriented programming at all. ick */
4957                 if (selection->time.consolidate()) {
4958                         selection->TimeChanged ();
4959                 }
4960                 commit_reversible_command ();
4961         } else {
4962                 /* just a click, no pointer movement.*/
4963
4964                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4965
4966                         selection->clear_time();
4967
4968                 } 
4969         }
4970
4971         /* XXX what happens if its a music selection? */
4972         session->set_audio_range (selection->time);
4973         stop_canvas_autoscroll ();
4974 }
4975
4976 void
4977 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4978 {
4979         double speed = 1.0;
4980         TimeAxisView* tvp = clicked_axisview;
4981         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4982
4983         if (tv && tv->is_track()) {
4984                 speed = tv->get_diskstream()->speed();
4985         }
4986         
4987         nframes64_t region_start = (nframes64_t) (clicked_regionview->region()->position() / speed);
4988         nframes64_t region_end = (nframes64_t) (clicked_regionview->region()->last_frame() / speed);
4989         nframes64_t region_length = (nframes64_t) (clicked_regionview->region()->length() / speed);
4990
4991         //drag_info.item = clicked_regionview->get_name_highlight();
4992         drag_info.item = item;
4993         drag_info.motion_callback = &Editor::trim_motion_callback;
4994         drag_info.finished_callback = &Editor::trim_finished_callback;
4995
4996         start_grab (event, trimmer_cursor);
4997         
4998         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4999                 trim_op = ContentsTrim;
5000         } else {
5001                 /* These will get overridden for a point trim.*/
5002                 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
5003                         /* closer to start */
5004                         trim_op = StartTrim;
5005                 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
5006                         /* closer to end */
5007                         trim_op = EndTrim;
5008                 }
5009         }
5010
5011         switch (trim_op) {
5012         case StartTrim:
5013                 show_verbose_time_cursor(region_start, 10);     
5014                 break;
5015         case EndTrim:
5016                 show_verbose_time_cursor(region_end, 10);       
5017                 break;
5018         case ContentsTrim:
5019                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
5020                 break;
5021         }
5022 }
5023
5024 void
5025 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
5026 {
5027         RegionView* rv = clicked_regionview;
5028         nframes64_t frame_delta = 0;
5029         bool left_direction;
5030         bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
5031
5032         /* snap modifier works differently here..
5033            its' current state has to be passed to the 
5034            various trim functions in order to work properly 
5035         */ 
5036
5037         double speed = 1.0;
5038         TimeAxisView* tvp = clicked_axisview;
5039         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
5040         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
5041
5042         if (tv && tv->is_track()) {
5043                 speed = tv->get_diskstream()->speed();
5044         }
5045         
5046         if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
5047                 left_direction = true;
5048         } else {
5049                 left_direction = false;
5050         }
5051
5052         if (obey_snap) {
5053                 snap_to (drag_info.current_pointer_frame);
5054         }
5055
5056         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5057                 return;
5058         }
5059
5060         if (drag_info.first_move) {
5061         
5062                 string trim_type;
5063
5064                 switch (trim_op) {
5065                 case StartTrim:
5066                         trim_type = "Region start trim";
5067                         break;
5068                 case EndTrim:
5069                         trim_type = "Region end trim";
5070                         break;
5071                 case ContentsTrim:
5072                         trim_type = "Region content trim";
5073                         break;
5074                 }
5075
5076                 begin_reversible_command (trim_type);
5077
5078                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
5079                         (*i)->fake_set_opaque(false);
5080                         (*i)->region()->freeze ();
5081                 
5082                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5083                         if (arv)
5084                                 arv->temporarily_hide_envelope ();
5085
5086                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5087                         insert_result = motion_frozen_playlists.insert (pl);
5088                         if (insert_result.second) {
5089                                 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
5090                                 pl->freeze();
5091                         }
5092                 }
5093         }
5094
5095         if (left_direction) {
5096                 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
5097         } else {
5098                 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
5099         }
5100
5101         switch (trim_op) {              
5102         case StartTrim:
5103                 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
5104                         break;
5105                 } else {
5106                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
5107                                 single_start_trim (**i, frame_delta, left_direction, obey_snap);
5108                         }
5109                         break;
5110                 }
5111                 
5112         case EndTrim:
5113                 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes64_t) (rv->region()->last_frame()/speed))) {
5114                         break;
5115                 } else {
5116                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
5117                                 single_end_trim (**i, frame_delta, left_direction, obey_snap);
5118                         }
5119                         break;
5120                 }
5121                 
5122         case ContentsTrim:
5123                 {
5124                         bool swap_direction = false;
5125
5126                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
5127                                 swap_direction = true;
5128                         }
5129                         
5130                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5131                              i != selection->regions.by_layer().end(); ++i)
5132                         {
5133                                 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
5134                         }
5135                 }
5136                 break;
5137         }
5138
5139         switch (trim_op) {
5140         case StartTrim:
5141                 show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);   
5142                 break;
5143         case EndTrim:
5144                 show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10); 
5145                 break;
5146         case ContentsTrim:
5147                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
5148                 break;
5149         }
5150
5151         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5152         drag_info.first_move = false;
5153 }
5154
5155 void
5156 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
5157 {
5158         boost::shared_ptr<Region> region (rv.region());
5159
5160         if (region->locked()) {
5161                 return;
5162         }
5163
5164         nframes64_t new_bound;
5165
5166         double speed = 1.0;
5167         TimeAxisView* tvp = clicked_axisview;
5168         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
5169
5170         if (tv && tv->is_track()) {
5171                 speed = tv->get_diskstream()->speed();
5172         }
5173         
5174         if (left_direction) {
5175                 if (swap_direction) {
5176                         new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
5177                 } else {
5178                         new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
5179                 }
5180         } else {
5181                 if (swap_direction) {
5182                         new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
5183                 } else {
5184                         new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
5185                 }
5186         }
5187
5188         if (obey_snap) {
5189                 snap_to (new_bound);
5190         }
5191         region->trim_start ((nframes64_t) (new_bound * speed), this);   
5192         rv.region_changed (StartChanged);
5193 }
5194
5195 void
5196 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
5197 {
5198         boost::shared_ptr<Region> region (rv.region()); 
5199
5200         if (region->locked()) {
5201                 return;
5202         }
5203
5204         nframes64_t new_bound;
5205
5206         double speed = 1.0;
5207         TimeAxisView* tvp = clicked_axisview;
5208         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
5209
5210         if (tv && tv->is_track()) {
5211                 speed = tv->get_diskstream()->speed();
5212         }
5213         
5214         if (left_direction) {
5215                 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
5216         } else {
5217                 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
5218         }
5219
5220         if (obey_snap) {
5221                 snap_to (new_bound, (left_direction ? 0 : 1));  
5222         }
5223
5224         region->trim_front ((nframes64_t) (new_bound * speed), this);
5225
5226         rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
5227 }
5228
5229 void
5230 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
5231 {
5232         boost::shared_ptr<Region> region (rv.region());
5233
5234         if (region->locked()) {
5235                 return;
5236         }
5237
5238         nframes64_t new_bound;
5239
5240         double speed = 1.0;
5241         TimeAxisView* tvp = clicked_axisview;
5242         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
5243
5244         if (tv && tv->is_track()) {
5245                 speed = tv->get_diskstream()->speed();
5246         }
5247         
5248         if (left_direction) {
5249                 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
5250         } else {
5251                 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
5252         }
5253
5254         if (obey_snap) {
5255                 snap_to (new_bound);
5256         }
5257         region->trim_end ((nframes64_t) (new_bound * speed), this);
5258         rv.region_changed (LengthChanged);
5259 }
5260         
5261 void
5262 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
5263 {
5264         if (!drag_info.first_move) {
5265                 trim_motion_callback (item, event);
5266                 
5267                 if (!selection->selected (clicked_regionview)) {
5268                         thaw_region_after_trim (*clicked_regionview);           
5269                 } else {
5270                         
5271                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5272                              i != selection->regions.by_layer().end(); ++i)
5273                         {
5274                                 thaw_region_after_trim (**i);
5275                                 (*i)->fake_set_opaque (true);
5276                         }
5277                 }
5278                 
5279                 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
5280                         (*p)->thaw ();
5281                         session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
5282                 }
5283                 
5284                 motion_frozen_playlists.clear ();
5285
5286                 commit_reversible_command();
5287         } else {
5288                 /* no mouse movement */
5289                 point_trim (event);
5290         }
5291 }
5292
5293 void
5294 Editor::point_trim (GdkEvent* event)
5295 {
5296         RegionView* rv = clicked_regionview;
5297         nframes64_t new_bound = drag_info.current_pointer_frame;
5298
5299         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5300                 snap_to (new_bound);
5301         }
5302
5303         /* Choose action dependant on which button was pressed */
5304         switch (event->button.button) {
5305         case 1:
5306                 trim_op = StartTrim;
5307                 begin_reversible_command (_("Start point trim"));
5308
5309                 if (selection->selected (rv)) {
5310
5311                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5312                              i != selection->regions.by_layer().end(); ++i)
5313                         {
5314                                 if (!(*i)->region()->locked()) {
5315                                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5316                                         XMLNode &before = pl->get_state();
5317                                         (*i)->region()->trim_front (new_bound, this);   
5318                                         XMLNode &after = pl->get_state();
5319                                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5320                                 }
5321                         }
5322
5323                 } else {
5324
5325                         if (!rv->region()->locked()) {
5326                                 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5327                                 XMLNode &before = pl->get_state();
5328                                 rv->region()->trim_front (new_bound, this);     
5329                                 XMLNode &after = pl->get_state();
5330                                 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5331                         }
5332                 }
5333
5334                 commit_reversible_command();
5335         
5336                 break;
5337         case 2:
5338                 trim_op = EndTrim;
5339                 begin_reversible_command (_("End point trim"));
5340
5341                 if (selection->selected (rv)) {
5342                         
5343                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
5344                         {
5345                                 if (!(*i)->region()->locked()) {
5346                                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5347                                         XMLNode &before = pl->get_state();
5348                                         (*i)->region()->trim_end (new_bound, this);
5349                                         XMLNode &after = pl->get_state();
5350                                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5351                                 }
5352                         }
5353
5354                 } else {
5355
5356                         if (!rv->region()->locked()) {
5357                                 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5358                                 XMLNode &before = pl->get_state();
5359                                 rv->region()->trim_end (new_bound, this);
5360                                 XMLNode &after = pl->get_state();
5361                                 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
5362                         }
5363                 }
5364
5365                 commit_reversible_command();
5366         
5367                 break;
5368         default:
5369                 break;
5370         }
5371 }
5372
5373 void
5374 Editor::thaw_region_after_trim (RegionView& rv)
5375 {
5376         boost::shared_ptr<Region> region (rv.region());
5377
5378         if (region->locked()) {
5379                 return;
5380         }
5381
5382         region->thaw (_("trimmed region"));
5383         XMLNode &after = region->playlist()->get_state();
5384         session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
5385
5386         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
5387         if (arv)
5388                 arv->unhide_envelope ();
5389 }
5390
5391 void
5392 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
5393 {
5394         Marker* marker;
5395         bool is_start;
5396
5397         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
5398                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
5399                 /*NOTREACHED*/
5400         }
5401
5402         Location* location = find_location_from_marker (marker, is_start);      
5403         location->set_hidden (true, this);
5404 }
5405
5406
5407 void
5408 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
5409 {
5410         if (session == 0) {
5411                 return;
5412         }
5413
5414         drag_info.item = item;
5415         drag_info.motion_callback = &Editor::drag_range_markerbar_op;
5416         drag_info.finished_callback = &Editor::end_range_markerbar_op;
5417
5418         range_marker_op = op;
5419
5420         if (!temp_location) {
5421                 temp_location = new Location;
5422         }
5423         
5424         switch (op) {
5425         case CreateRangeMarker:
5426         case CreateTransportMarker:
5427         case CreateCDMarker:
5428         
5429                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
5430                         drag_info.copy = true;
5431                 } else {
5432                         drag_info.copy = false;
5433                 }
5434                 start_grab (event, selector_cursor);
5435                 break;
5436         }
5437
5438         show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
5439         
5440 }
5441
5442 void
5443 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5444 {
5445         nframes64_t start = 0;
5446         nframes64_t end = 0;
5447         ArdourCanvas::SimpleRect *crect;
5448
5449         switch (range_marker_op) {
5450         case CreateRangeMarker:
5451                 crect = range_bar_drag_rect;
5452                 break;
5453         case CreateTransportMarker:
5454                 crect = transport_bar_drag_rect;
5455                 break;
5456         case CreateCDMarker:
5457                 crect = cd_marker_bar_drag_rect;
5458                 break;
5459         default:
5460                 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
5461                 return;
5462                 break;
5463         }
5464         
5465         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5466                 snap_to (drag_info.current_pointer_frame);
5467         }
5468
5469         /* only alter selection if the current frame is 
5470            different from the last frame position.
5471          */
5472         
5473         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5474         
5475         switch (range_marker_op) {
5476         case CreateRangeMarker:
5477         case CreateTransportMarker:
5478         case CreateCDMarker:
5479                 if (drag_info.first_move) {
5480                         snap_to (drag_info.grab_frame);
5481                 }
5482                 
5483                 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5484                         start = drag_info.current_pointer_frame;
5485                         end = drag_info.grab_frame;
5486                 } else {
5487                         end = drag_info.current_pointer_frame;
5488                         start = drag_info.grab_frame;
5489                 }
5490                 
5491                 /* first drag: Either add to the selection
5492                    or create a new selection.
5493                 */
5494                 
5495                 if (drag_info.first_move) {
5496                         
5497                         temp_location->set (start, end);
5498                         
5499                         crect->show ();
5500
5501                         update_marker_drag_item (temp_location);
5502                         range_marker_drag_rect->show();
5503                         //range_marker_drag_rect->raise_to_top();
5504                         
5505                 } 
5506                 break;          
5507         }
5508         
5509         if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
5510                 start_canvas_autoscroll (1, 0);
5511         }
5512         
5513         if (start != end) {
5514                 temp_location->set (start, end);
5515
5516                 double x1 = frame_to_pixel (start);
5517                 double x2 = frame_to_pixel (end);
5518                 crect->property_x1() = x1;
5519                 crect->property_x2() = x2;
5520
5521                 update_marker_drag_item (temp_location);
5522         }
5523
5524         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5525         drag_info.first_move = false;
5526
5527         show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
5528         
5529 }
5530
5531 void
5532 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5533 {
5534         Location * newloc = 0;
5535         string rangename;
5536         int flags;
5537         
5538         if (!drag_info.first_move) {
5539                 drag_range_markerbar_op (item, event);
5540
5541                 switch (range_marker_op) {
5542                 case CreateRangeMarker:
5543                 case CreateCDMarker:
5544                     {
5545                         begin_reversible_command (_("new range marker"));
5546                         XMLNode &before = session->locations()->get_state();
5547                         session->locations()->next_available_name(rangename,"unnamed");
5548                         if (range_marker_op == CreateCDMarker) {
5549                                 flags =  Location::IsRangeMarker|Location::IsCDMarker;
5550                                 cd_marker_bar_drag_rect->hide();
5551                         }
5552                         else {
5553                                 flags =  Location::IsRangeMarker;
5554                                 range_bar_drag_rect->hide();
5555                         }
5556                         newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
5557                         session->locations()->add (newloc, true);
5558                         XMLNode &after = session->locations()->get_state();
5559                         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
5560                         commit_reversible_command ();
5561                         
5562                         range_marker_drag_rect->hide();
5563                         break;
5564                     }
5565
5566                 case CreateTransportMarker:
5567                         // popup menu to pick loop or punch
5568                         new_transport_marker_context_menu (&event->button, item);
5569                         
5570                         break;
5571                 }
5572         } else {
5573                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5574
5575                 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
5576
5577                         nframes64_t start;
5578                         nframes64_t end;
5579
5580                         start = session->locations()->first_mark_before (drag_info.grab_frame);
5581                         end = session->locations()->first_mark_after (drag_info.grab_frame);
5582                         
5583                         if (end == max_frames) {
5584                                 end = session->current_end_frame ();
5585                         }
5586
5587                         if (start == 0) {
5588                                 start = session->current_start_frame ();
5589                         }
5590
5591                         switch (mouse_mode) {
5592                         case MouseObject:
5593                                 /* find the two markers on either side and then make the selection from it */
5594                                 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
5595                                 break;
5596
5597                         case MouseRange:
5598                                 /* find the two markers on either side of the click and make the range out of it */
5599                                 selection->set (0, start, end);
5600                                 break;
5601
5602                         default:
5603                                 break;
5604                         }
5605                 } 
5606         }
5607
5608         stop_canvas_autoscroll ();
5609 }
5610
5611
5612
5613 void
5614 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5615 {
5616         drag_info.item = item;
5617         drag_info.motion_callback = &Editor::drag_mouse_zoom;
5618         drag_info.finished_callback = &Editor::end_mouse_zoom;
5619
5620         start_grab (event, zoom_cursor);
5621
5622         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5623 }
5624
5625 void
5626 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5627 {
5628         nframes64_t start;
5629         nframes64_t end;
5630
5631         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5632                 snap_to (drag_info.current_pointer_frame);
5633                 
5634                 if (drag_info.first_move) {
5635                         snap_to (drag_info.grab_frame);
5636                 }
5637         }
5638                 
5639         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5640
5641         /* base start and end on initial click position */
5642         if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5643                 start = drag_info.current_pointer_frame;
5644                 end = drag_info.grab_frame;
5645         } else {
5646                 end = drag_info.current_pointer_frame;
5647                 start = drag_info.grab_frame;
5648         }
5649         
5650         if (start != end) {
5651
5652                 if (drag_info.first_move) {
5653                         zoom_rect->show();
5654                         zoom_rect->raise_to_top();
5655                 }
5656
5657                 reposition_zoom_rect(start, end);
5658
5659                 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5660                 drag_info.first_move = false;
5661
5662                 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5663         }
5664 }
5665
5666 void
5667 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5668 {
5669         if (!drag_info.first_move) {
5670                 drag_mouse_zoom (item, event);
5671                 
5672                 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5673                         temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
5674                 } else {
5675                         temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
5676                 }               
5677         } else {
5678                 temporal_zoom_to_frame (false, drag_info.grab_frame);
5679                 /*
5680                 temporal_zoom_step (false);
5681                 center_screen (drag_info.grab_frame);
5682                 */
5683         }
5684
5685         zoom_rect->hide();
5686 }
5687
5688 void
5689 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
5690 {
5691         double x1 = frame_to_pixel (start);
5692         double x2 = frame_to_pixel (end);
5693         double y2 = full_canvas_height - 1.0;
5694
5695         zoom_rect->property_x1() = x1;
5696         zoom_rect->property_y1() = 1.0;
5697         zoom_rect->property_x2() = x2;
5698         zoom_rect->property_y2() = y2;
5699 }
5700
5701 void
5702 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5703 {
5704         drag_info.item = item;
5705         drag_info.motion_callback = &Editor::drag_rubberband_select;
5706         drag_info.finished_callback = &Editor::end_rubberband_select;
5707
5708         start_grab (event, cross_hair_cursor);
5709
5710         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5711 }
5712
5713 void
5714 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5715 {
5716         nframes64_t start;
5717         nframes64_t end;
5718         double y1;
5719         double y2;
5720
5721         /* use a bigger drag threshold than the default */
5722
5723         if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
5724                 return;
5725         }
5726
5727         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) {
5728                 if (drag_info.first_move) {
5729                         snap_to (drag_info.grab_frame);
5730                 } 
5731                 snap_to (drag_info.current_pointer_frame);
5732         }
5733
5734         /* base start and end on initial click position */
5735
5736         if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5737                 start = drag_info.current_pointer_frame;
5738                 end = drag_info.grab_frame;
5739         } else {
5740                 end = drag_info.current_pointer_frame;
5741                 start = drag_info.grab_frame;
5742         }
5743
5744         if (drag_info.current_pointer_y < drag_info.grab_y) {
5745                 y1 = drag_info.current_pointer_y;
5746                 y2 = drag_info.grab_y;
5747         } else {
5748                 y2 = drag_info.current_pointer_y;
5749                 y1 = drag_info.grab_y;
5750         }
5751
5752         
5753         if (start != end || y1 != y2) {
5754
5755                 double x1 = frame_to_pixel (start);
5756                 double x2 = frame_to_pixel (end);
5757                 
5758                 rubberband_rect->property_x1() = x1;
5759                 rubberband_rect->property_y1() = y1;
5760                 rubberband_rect->property_x2() = x2;
5761                 rubberband_rect->property_y2() = y2;
5762
5763                 rubberband_rect->show();
5764                 rubberband_rect->raise_to_top();
5765                 
5766                 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5767                 drag_info.first_move = false;
5768
5769                 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5770         }
5771 }
5772
5773 void
5774 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5775 {
5776         if (!drag_info.first_move) {
5777
5778                 drag_rubberband_select (item, event);
5779
5780                 double y1,y2;
5781                 if (drag_info.current_pointer_y < drag_info.grab_y) {
5782                         y1 = drag_info.current_pointer_y;
5783                         y2 = drag_info.grab_y;
5784                 } else {
5785                         y2 = drag_info.current_pointer_y;
5786                         y1 = drag_info.grab_y;
5787                 }
5788
5789
5790                 Selection::Operation op = Keyboard::selection_type (event->button.state);
5791                 bool commit;
5792
5793                 begin_reversible_command (_("rubberband selection"));
5794
5795                 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5796                         commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5797                 } else {
5798                         commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5799                 }               
5800
5801                 if (commit) {
5802                         commit_reversible_command ();
5803                 }
5804                 
5805         } else {
5806                 if (!getenv("ARDOUR_SAE")) {
5807                         selection->clear_tracks();
5808                 }
5809                 selection->clear_regions();
5810                 selection->clear_points ();
5811                 selection->clear_lines ();
5812         }
5813
5814         rubberband_rect->hide();
5815 }
5816
5817
5818 gint
5819 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5820 {
5821         using namespace Gtkmm2ext;
5822
5823         ArdourPrompter prompter (false);
5824
5825         prompter.set_prompt (_("Name for region:"));
5826         prompter.set_initial_text (clicked_regionview->region()->name());
5827         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5828         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5829         prompter.show_all ();
5830         switch (prompter.run ()) {
5831         case Gtk::RESPONSE_ACCEPT:
5832                 string str;
5833                 prompter.get_result(str);
5834                 if (str.length()) {
5835                         clicked_regionview->region()->set_name (str);
5836                 }
5837                 break;
5838         }
5839         return true;
5840 }
5841
5842 void
5843 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5844 {
5845         drag_info.item = item;
5846         drag_info.motion_callback = &Editor::time_fx_motion;
5847         drag_info.finished_callback = &Editor::end_time_fx;
5848
5849         start_grab (event);
5850
5851         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5852 }
5853
5854 void
5855 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5856 {
5857         RegionView* rv = clicked_regionview;
5858
5859         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5860                 snap_to (drag_info.current_pointer_frame);
5861         }
5862
5863         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5864                 return;
5865         }
5866
5867         if (drag_info.current_pointer_frame > rv->region()->position()) {
5868                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5869         }
5870
5871         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5872         drag_info.first_move = false;
5873
5874         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5875 }
5876
5877 void
5878 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5879 {
5880         clicked_regionview->get_time_axis_view().hide_timestretch ();
5881
5882         if (drag_info.first_move) {
5883                 return;
5884         }
5885
5886         if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5887                 /* backwards drag of the left edge - not usable */
5888                 return;
5889         }
5890         
5891         nframes64_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5892
5893         float percentage = (double) newlen / (double) clicked_regionview->region()->length();
5894         
5895 #ifndef USE_RUBBERBAND
5896         // Soundtouch uses percentage / 100 instead of normal (/ 1) 
5897         if (clicked_regionview->region()->data_type() == DataType::AUDIO) {
5898                 percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5899         }
5900 #endif  
5901         
5902         begin_reversible_command (_("timestretch"));
5903         
5904         // XXX how do timeFX on multiple regions ?
5905         
5906         RegionSelection rs;
5907         rs.add (clicked_regionview);
5908
5909         if (time_stretch (rs, percentage) == 0) {
5910                 session->commit_reversible_command ();
5911         }
5912 }
5913
5914 void
5915 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
5916 {
5917         /* no brushing without a useful snap setting */
5918
5919         switch (snap_mode) {
5920         case SnapMagnetic:
5921                 return; /* can't work because it allows region to be placed anywhere */
5922         default:
5923                 break; /* OK */
5924         }
5925
5926         switch (snap_type) {
5927         case SnapToMark:
5928                 return;
5929
5930         default:
5931                 break;
5932         }
5933
5934         /* don't brush a copy over the original */
5935         
5936         if (pos == rv->region()->position()) {
5937                 return;
5938         }
5939
5940         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
5941
5942         if (rtv == 0 || !rtv->is_track()) {
5943                 return;
5944         }
5945
5946         boost::shared_ptr<Playlist> playlist = rtv->playlist();
5947         double speed = rtv->get_diskstream()->speed();
5948         
5949         XMLNode &before = playlist->get_state();
5950         playlist->add_region (RegionFactory::create (rv->region()), (nframes64_t) (pos * speed));
5951         XMLNode &after = playlist->get_state();
5952         session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5953         
5954         // playlist is frozen, so we have to update manually
5955         
5956         playlist->Modified(); /* EMIT SIGNAL */
5957 }
5958
5959 gint
5960 Editor::track_height_step_timeout ()
5961 {
5962         if (get_microseconds() - last_track_height_step_timestamp < 250000) {
5963                 current_stepping_trackview = 0;
5964                 return false;
5965         }
5966         return true;
5967 }
5968