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