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