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