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