d1b678625bea4b4f7a855f8d524168e2aecbc3cb
[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 (Config->get_edit_mode() == Splice && drag_info.first_move && drag_info.move_threshold_passed && pre_drag_region_selection.empty()) {
2952                 pre_drag_region_selection = selection->regions;
2953                 RegionSelection all_after = get_regions_after (clicked_regionview->region()->position(), selection->tracks);
2954                 selection->set (all_after);
2955         }
2956
2957         if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2958
2959                 drag_info.want_move_threshold = false; // don't copy again
2960
2961                 /* duplicate the region(s) */
2962
2963                 vector<RegionView*> new_regionviews;
2964                 
2965                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2966                         RegionView* rv;
2967                         RegionView* nrv;
2968                         AudioRegionView* arv;
2969
2970                         rv = (*i);
2971
2972                         
2973                         if ((arv = dynamic_cast<AudioRegionView*>(rv)) == 0) {
2974                                 /* XXX handle MIDI here */
2975                                 continue;
2976                         }
2977
2978                         nrv = new AudioRegionView (*arv);
2979                         nrv->get_canvas_group()->show ();
2980
2981                         new_regionviews.push_back (nrv);
2982                 }
2983
2984                 if (new_regionviews.empty()) {
2985                         return;
2986                 }
2987
2988                 /* reset selection to new regionviews */
2989                 
2990                 selection->set (new_regionviews);
2991                 
2992                 /* reset drag_info data to reflect the fact that we are dragging the copies */
2993                 
2994                 drag_info.data = new_regionviews.front();
2995
2996                 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2997         }
2998
2999         /* Which trackview is this ? */
3000
3001         TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
3002         AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3003
3004         /* The region motion is only processed if the pointer is over
3005            an audio track.
3006         */
3007         
3008         if (!tv || !tv->is_audio_track()) {
3009                 /* To make sure we hide the verbose canvas cursor when the mouse is 
3010                    not held over and audiotrack. 
3011                 */
3012                 hide_verbose_canvas_cursor ();
3013                 return;
3014         }
3015         
3016         original_pointer_order = drag_info.last_trackview->order;
3017                 
3018         /************************************************************
3019                  Y-Delta Computation
3020         ************************************************************/   
3021
3022         if (drag_info.brushing) {
3023                 clamp_y_axis = true;
3024                 pointer_y_span = 0;
3025                 goto y_axis_done;
3026         }
3027         
3028         if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
3029
3030                 /* drop any splice-induced selection madness */
3031
3032                 if (!pre_drag_region_selection.empty()) {
3033                         selection->set (pre_drag_region_selection);
3034                         pre_drag_region_selection.clear ();
3035                 }
3036
3037                 int32_t children = 0, numtracks = 0;
3038                 // XXX hard coding track limit, oh my, so very very bad
3039                 bitset <1024> tracks (0x00);
3040                 /* get a bitmask representing the visible tracks */
3041
3042                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3043                         TimeAxisView *tracklist_timeview;
3044                         tracklist_timeview = (*i);
3045                         AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
3046                         list<TimeAxisView*> children_list;
3047               
3048                         /* zeroes are audio tracks. ones are other types. */
3049               
3050                         if (!atv2->hidden()) {
3051                                 
3052                                 if (visible_y_high < atv2->order) {
3053                                         visible_y_high = atv2->order;
3054                                 }
3055                                 if (visible_y_low > atv2->order) {
3056                                         visible_y_low = atv2->order;
3057                                 }
3058                 
3059                                 if (!atv2->is_audio_track()) {                            
3060                                         tracks = tracks |= (0x01 << atv2->order);
3061                                 }
3062         
3063                                 height_list[atv2->order] = (*i)->height;
3064                                 children = 1;
3065                                 if ((children_list = atv2->get_child_list()).size() > 0) {
3066                                         for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) { 
3067                                                 tracks = tracks |= (0x01 << (atv2->order + children));
3068                                                 height_list[atv2->order + children] =  (*j)->height;                
3069                                                 numtracks++;
3070                                                 children++;     
3071                                         }
3072                                 }
3073                                 numtracks++;        
3074                         }
3075                 }
3076                 /* find the actual span according to the canvas */
3077
3078                 canvas_pointer_y_span = pointer_y_span;
3079                 if (drag_info.last_trackview->order >= tv->order) {
3080                         int32_t y;
3081                         for (y = tv->order; y < drag_info.last_trackview->order; y++) {
3082                                 if (height_list[y] == 0 ) {
3083                                         canvas_pointer_y_span--;
3084                                 }
3085                         }
3086                 } else {
3087                         int32_t y;
3088                         for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
3089                                 if (    height_list[y] == 0 ) {
3090                                         canvas_pointer_y_span++;
3091                                 }
3092                         }
3093                 }
3094
3095                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3096                         RegionView* rv2 = (*i);
3097                         double ix1, ix2, iy1, iy2;
3098                         int32_t n = 0;
3099
3100                         rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3101                         rv2->get_canvas_group()->i2w (ix1, iy1);
3102                         TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3103                         RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3104
3105                         if (atv2->order != original_pointer_order) {    
3106                                 /* this isn't the pointer track */      
3107
3108                                 if (canvas_pointer_y_span > 0) {
3109
3110                                         /* moving up the canvas */
3111                                         if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
3112         
3113                                                 int32_t visible_tracks = 0;
3114                                                 while (visible_tracks < canvas_pointer_y_span ) {
3115                                                         visible_tracks++;
3116                   
3117                                                         while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3118                                                                 /* we're passing through a hidden track */
3119                                                                 n--;
3120                                                         }                 
3121                                                 }
3122                  
3123                                                 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {                  
3124                                                         clamp_y_axis = true;
3125                                                 }
3126                     
3127                                         } else {
3128                                                 clamp_y_axis = true;
3129                                         }                 
3130                   
3131                                 } else if (canvas_pointer_y_span < 0) {
3132
3133                                         /*moving down the canvas*/
3134
3135                                         if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3136                     
3137                     
3138                                                 int32_t visible_tracks = 0;
3139                     
3140                                                 while (visible_tracks > canvas_pointer_y_span ) {
3141                                                         visible_tracks--;
3142                       
3143                                                         while (height_list[atv2->order - (visible_tracks - n)] == 0) {             
3144                                                                 n++;
3145                                                         }                
3146                                                 }
3147                                                 if (  tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3148                                                         clamp_y_axis = true;
3149                             
3150                                                 }
3151                                         } else {
3152                           
3153                                                 clamp_y_axis = true;
3154                                         }
3155                                 }               
3156                   
3157                         } else {
3158                       
3159                                 /* this is the pointer's track */
3160                                 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow 
3161                                         clamp_y_axis = true;
3162                                 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3163                                         clamp_y_axis = true;
3164                                 }
3165                         }             
3166                         if (clamp_y_axis) {
3167                                 break;
3168                         }
3169                 }
3170
3171         } else  if (drag_info.last_trackview == tv) {
3172                 clamp_y_axis = true;
3173         }         
3174
3175   y_axis_done:
3176         if (!clamp_y_axis) {
3177                 drag_info.last_trackview = tv;        
3178         }
3179           
3180         /************************************************************
3181                         X DELTA COMPUTATION
3182         ************************************************************/
3183
3184         /* compute the amount of pointer motion in frames, and where
3185            the region would be if we moved it by that much.
3186         */
3187
3188         if (drag_info.move_threshold_passed) {
3189
3190                 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3191
3192                         nframes_t sync_frame;
3193                         nframes_t sync_offset;
3194                         int32_t sync_dir;
3195             
3196                         pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3197
3198                         sync_offset = rv->region()->sync_offset (sync_dir);
3199                         sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3200
3201                         /* we snap if the snap modifier is not enabled.
3202                          */
3203             
3204                         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3205                                 snap_to (sync_frame);   
3206                         }
3207             
3208                         if (sync_frame - sync_offset <= sync_frame) {
3209                                 pending_region_position = sync_frame - (sync_dir*sync_offset);
3210                         } else {
3211                                 pending_region_position = 0;
3212                         }
3213             
3214                 } else {
3215                         pending_region_position = 0;
3216                 }
3217           
3218                 if (pending_region_position > max_frames - rv->region()->length()) {
3219                         pending_region_position = drag_info.last_frame_position;
3220                 }
3221           
3222                 // printf ("3: pending_region_position= %lu    %lu\n", pending_region_position, drag_info.last_frame_position );
3223           
3224                 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3225
3226                         /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3227                            to make it appear at the new location.
3228                         */
3229
3230                         if (pending_region_position > drag_info.last_frame_position) {
3231                                 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3232                         } else {
3233                                 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3234                         }
3235
3236                         drag_info.last_frame_position = pending_region_position;
3237             
3238                 } else {
3239                         x_delta = 0;
3240                 }
3241
3242         } else {
3243                 /* threshold not passed */
3244
3245                 x_delta = 0;
3246         }
3247
3248         /*************************************************************
3249                         PREPARE TO MOVE
3250         ************************************************************/
3251
3252         if (x_delta == 0 && (pointer_y_span == 0)) {
3253                 /* haven't reached next snap point, and we're not switching
3254                    trackviews. nothing to do.
3255                 */
3256                 return;
3257         } 
3258
3259
3260         if (x_delta < 0) {
3261                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3262
3263                         RegionView* rv2 = (*i);
3264
3265                         // If any regionview is at zero, we need to know so we can stop further leftward motion.
3266                         
3267                         double ix1, ix2, iy1, iy2;
3268                         rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3269                         rv2->get_canvas_group()->i2w (ix1, iy1);
3270
3271                         if (ix1 <= 1) {
3272                                 x_delta = 0;
3273                                 break;
3274                         }
3275                 }
3276         }
3277
3278         /*************************************************************
3279                         MOTION                                                                
3280         ************************************************************/
3281
3282         bool do_move;
3283
3284         if (drag_info.first_move) {
3285                 if (drag_info.move_threshold_passed) {
3286                         do_move = true;
3287                 } else {
3288                         do_move = false;
3289                 }
3290         } else {
3291                 do_move = true;
3292         }
3293
3294         if (do_move) {
3295
3296                 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3297                 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3298
3299                 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3300             
3301                         RegionView* rv = (*i);
3302                         double ix1, ix2, iy1, iy2;
3303                         int32_t temp_pointer_y_span = pointer_y_span;
3304
3305                         /* get item BBox, which will be relative to parent. so we have
3306                            to query on a child, then convert to world coordinates using
3307                            the parent.
3308                         */
3309
3310                         rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3311                         rv->get_canvas_group()->i2w (ix1, iy1);
3312                         TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3313                         AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3314                         AudioTimeAxisView* temp_atv;
3315
3316                         if ((pointer_y_span != 0) && !clamp_y_axis) {
3317                                 y_delta = 0;
3318                                 int32_t x = 0;
3319                                 for (j = height_list.begin(); j!= height_list.end(); j++) {     
3320                                         if (x == canvas_atv->order) {
3321                                                 /* we found the track the region is on */
3322                                                 if (x != original_pointer_order) {
3323                                                         /*this isn't from the same track we're dragging from */
3324                                                         temp_pointer_y_span = canvas_pointer_y_span;
3325                                                 }                 
3326                                                 while (temp_pointer_y_span > 0) {
3327                                                         /* we're moving up canvas-wise,
3328                                                            so  we need to find the next track height
3329                                                         */
3330                                                         if (j != height_list.begin()) {           
3331                                                                 j--;
3332                                                         }
3333                                                         if (x != original_pointer_order) { 
3334                                                                 /* we're not from the dragged track, so ignore hidden tracks. */              
3335                                                                 if ((*j) == 0) {
3336                                                                         temp_pointer_y_span++;
3337                                                                 }
3338                                                         }          
3339                                                         y_delta -= (*j);        
3340                                                         temp_pointer_y_span--;  
3341                                                 }
3342                                                 while (temp_pointer_y_span < 0) {                 
3343                                                         y_delta += (*j);
3344                                                         if (x != original_pointer_order) { 
3345                                                                 if ((*j) == 0) {
3346                                                                         temp_pointer_y_span--;
3347                                                                 }
3348                                                         }          
3349                     
3350                                                         if (j != height_list.end()) {                 
3351                                                                 j++;
3352                                                         }
3353                                                         temp_pointer_y_span++;
3354                                                 }
3355                                                 /* find out where we'll be when we move and set height accordingly */
3356                   
3357                                                 tvp2 = trackview_by_y_position (iy1 + y_delta);
3358                                                 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3359                                                 rv->set_height (temp_atv->height);
3360         
3361                                                 /*   if you un-comment the following, the region colours will follow the track colours whilst dragging,
3362                                                      personally, i think this can confuse things, but never mind.
3363                                                 */
3364                                   
3365                                                 //const GdkColor& col (temp_atv->view->get_region_color());
3366                                                 //rv->set_color (const_cast<GdkColor&>(col));
3367                                                 break;          
3368                                         }
3369                                         x++;
3370                                 }
3371                         }
3372           
3373                         /* prevent the regionview from being moved to before 
3374                            the zero position on the canvas.
3375                         */
3376                         /* clamp */
3377                 
3378                         if (x_delta < 0) {
3379                                 if (-x_delta > ix1) {
3380                                         x_delta = -ix1;
3381                                 }
3382                         } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3383                                 x_delta = max_frames - rv->region()->last_frame();
3384                         }
3385
3386
3387                         if (drag_info.first_move) {
3388
3389                                 /* hide any dependent views */
3390                         
3391                                 rv->get_time_axis_view().hide_dependent_views (*rv);
3392                         
3393                                 /* this is subtle. raising the regionview itself won't help,
3394                                    because raise_to_top() just puts the item on the top of
3395                                    its parent's stack. so, we need to put the trackview canvas_display group
3396                                    on the top, since its parent is the whole canvas.
3397                                 */
3398                         
3399                                 rv->get_canvas_group()->raise_to_top();
3400                                 rv->get_time_axis_view().canvas_display->raise_to_top();
3401                                 cursor_group->raise_to_top();
3402                                 rv->fake_set_opaque (true);
3403                         }
3404                         
3405                         if (drag_info.brushing) {
3406                                 mouse_brush_insert_region (rv, pending_region_position);
3407                         } else {
3408                                 rv->move (x_delta, y_delta);                    
3409                         }
3410
3411                 } /* foreach region */
3412
3413         } /* if do_move */
3414
3415         if (drag_info.first_move && drag_info.move_threshold_passed) {
3416                 cursor_group->raise_to_top();
3417                 drag_info.first_move = false;
3418         }
3419
3420         if (x_delta != 0 && !drag_info.brushing) {
3421                 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3422         }
3423
3424
3425 void
3426 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3427 {
3428         nframes_t where;
3429         RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3430         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3431         bool nocommit = true;
3432         double speed;
3433         RouteTimeAxisView* atv;
3434         bool regionview_y_movement;
3435         bool regionview_x_movement;
3436         vector<RegionView*> copies;
3437
3438         /* first_move is set to false if the regionview has been moved in the 
3439            motion handler. 
3440         */
3441
3442         if (drag_info.first_move) {
3443                 /* just a click */
3444                 goto out;
3445         }
3446
3447         nocommit = false;
3448
3449         /* The regionview has been moved at some stage during the grab so we need
3450            to account for any mouse movement between this event and the last one. 
3451         */      
3452
3453         region_drag_motion_callback (item, event);
3454
3455         if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
3456                 selection->set (pre_drag_region_selection);
3457                 pre_drag_region_selection.clear ();
3458         }
3459
3460         if (drag_info.brushing) {
3461                 /* all changes were made during motion event handlers */
3462                 
3463                 if (drag_info.copy) {
3464                         for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3465                                 copies.push_back (*i);
3466                         }
3467                 }
3468
3469                 goto out;
3470         }
3471
3472         /* adjust for track speed */
3473         speed = 1.0;
3474
3475         atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3476         if (atv && atv->get_diskstream()) {
3477                 speed = atv->get_diskstream()->speed();
3478         }
3479         
3480         regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3481         regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3482
3483         //printf ("last_frame: %s position is %lu  %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed); 
3484         //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str()); 
3485         
3486         char* op_string;
3487
3488         if (drag_info.copy) {
3489                 if (drag_info.x_constrained) {
3490                         op_string = _("fixed time region copy");
3491                 } else {
3492                         op_string = _("region copy");
3493                 } 
3494         } else {
3495                 if (drag_info.x_constrained) {
3496                         op_string = _("fixed time region drag");
3497                 } else {
3498                         op_string = _("region drag");
3499                 }
3500         }
3501
3502         begin_reversible_command (op_string);
3503
3504         if (regionview_y_movement) {
3505
3506                 /* moved to a different audio track. */
3507                 
3508                 vector<RegionView*> new_selection;
3509
3510                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3511                         
3512                         RegionView* rv = (*i);              
3513
3514                         double ix1, ix2, iy1, iy2;
3515                         
3516                         rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3517                         rv->get_canvas_group()->i2w (ix1, iy1);
3518                         TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3519                         AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3520
3521                         boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3522                         boost::shared_ptr<Playlist> to_playlist = atv2->playlist();
3523
3524                         where = (nframes_t) (unit_to_frame (ix1) * speed);
3525                         boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3526
3527                         /* undo the previous hide_dependent_views so that xfades don't
3528                            disappear on copying regions 
3529                         */
3530
3531                         rv->get_time_axis_view().reveal_dependent_views (*rv);
3532
3533                         if (!drag_info.copy) {
3534                                 
3535                                 /* the region that used to be in the old playlist is not
3536                                    moved to the new one - we make a copy of it. as a result,
3537                                    any existing editor for the region should no longer be
3538                                    visible.
3539                                 */ 
3540             
3541                                 rv->hide_region_editor();
3542                                 rv->fake_set_opaque (false);
3543
3544                                 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));    
3545                                 from_playlist->remove_region ((rv->region()));
3546                                 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));    
3547
3548                         } else {
3549
3550                                 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3551                                 
3552                                 copies.push_back (rv);
3553                         }
3554
3555                         latest_regionviews.clear ();
3556                         
3557                         sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3558                         session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));        
3559                         to_playlist->add_region (new_region, where);
3560                         session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));        
3561                         c.disconnect ();
3562                                                               
3563                         if (!latest_regionviews.empty()) {
3564                                 new_selection.insert (new_selection.end(), latest_regionviews.begin(), latest_regionviews.end());
3565                         }
3566
3567                         /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3568                            was selected in all of them, then removing it from the playlist will have removed all
3569                            trace of it from the selection (i.e. there were N regions selected, we removed 1,
3570                            but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3571                            corresponding regionview, and the selection is now empty).
3572
3573                            this could have invalidated any and all iterators into the region selection.
3574
3575                            the heuristic we use here is: if the region selection is empty, break out of the loop
3576                            here. if the region selection is not empty, then restart the loop because we know that
3577                            we must have removed at least the region(view) we've just been working on as well as any
3578                            that we processed on previous iterations.
3579
3580                            EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3581                            we can just iterate.
3582                         */
3583
3584                         if (drag_info.copy) {
3585                                 ++i;
3586                         } else {
3587                                 if (selection->regions.empty()) {
3588                                         break;
3589                                 } else { 
3590                                         i = selection->regions.by_layer().begin();
3591                                 }
3592                         }
3593                 } 
3594
3595                 selection->set (new_selection);
3596
3597         } else {
3598
3599                 /* motion within a single track */
3600
3601                 list<RegionView*> regions = selection->regions.by_layer();
3602
3603                 if (drag_info.copy) {
3604                         selection->clear_regions();
3605                 }
3606                 
3607                 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3608
3609                         rv = (*i);
3610
3611                         if (rv->region()->locked()) {
3612                                 continue;
3613                         }
3614                         
3615
3616                         if (regionview_x_movement) {
3617                                 double ownspeed = 1.0;
3618                                 atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3619
3620                                 if (atv && atv->get_diskstream()) {
3621                                         ownspeed = atv->get_diskstream()->speed();
3622                                 }
3623                                 
3624                                 /* base the new region position on the current position of the regionview.*/
3625                                 
3626                                 double ix1, ix2, iy1, iy2;
3627                                 
3628                                 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3629                                 rv->get_canvas_group()->i2w (ix1, iy1);
3630                                 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3631                                 
3632                         } else {
3633                                 
3634                                 where = rv->region()->position();
3635                         }
3636
3637                         boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3638
3639                         assert (to_playlist);
3640
3641                         /* add the undo */
3642
3643                         session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));        
3644
3645                         if (drag_info.copy) {
3646
3647                                 boost::shared_ptr<Region> newregion;
3648                                 boost::shared_ptr<Region> ar;
3649
3650                                 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3651                                         newregion = RegionFactory::create (ar);
3652                                 } else {
3653                                         /* XXX MIDI HERE drobilla */
3654                                         continue;
3655                                 }
3656
3657                                 /* add it */
3658
3659                                 latest_regionviews.clear ();
3660                                 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3661                                 to_playlist->add_region (newregion, (nframes_t) (where * atv->get_diskstream()->speed()));
3662                                 c.disconnect ();
3663                                 
3664                                 if (!latest_regionviews.empty()) {
3665                                         // XXX why just the first one ? we only expect one
3666                                         atv->reveal_dependent_views (*latest_regionviews.front());
3667                                         selection->add (latest_regionviews);
3668                                 }
3669                                 
3670                                 /* if the original region was locked, we don't care for the new one */
3671                                 
3672                                 newregion->set_locked (false);                  
3673
3674                         } else {
3675
3676                                 /* just change the model */
3677
3678                                 rv->region()->set_position (where, (void*) this);
3679
3680                         }
3681
3682                         /* add the redo */
3683
3684                         session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3685
3686                         if (drag_info.copy) {
3687                                 copies.push_back (rv);
3688                         }
3689                 }
3690         }
3691
3692   out:
3693         
3694         if (!nocommit) {
3695                 commit_reversible_command ();
3696         }
3697
3698         for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3699                 delete *x;
3700         }
3701 }
3702
3703 void
3704 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3705 {
3706         /* Either add to or set the set the region selection, unless
3707            this is an alignment click (control used)
3708         */
3709         
3710         if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3711                 TimeAxisView* tv = &rv.get_time_axis_view();
3712                 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3713                 double speed = 1.0;
3714                 if (atv && atv->is_audio_track()) {
3715                         speed = atv->get_diskstream()->speed();
3716                 }
3717
3718                 nframes64_t where = get_preferred_edit_position();
3719
3720                 if (where >= 0) {
3721
3722                         if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3723                                 
3724                                 align_region (rv.region(), SyncPoint, (nframes_t) (where * speed));
3725                                 
3726                         } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3727                                 
3728                                 align_region (rv.region(), End, (nframes_t) (where * speed));
3729                                 
3730                         } else {
3731                                 
3732                                 align_region (rv.region(), Start, (nframes_t) (where * speed));
3733                         }
3734                 }
3735         }
3736 }
3737
3738 void
3739 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos) 
3740 {
3741         char buf[128];
3742         SMPTE::Time smpte;
3743         BBT_Time bbt;
3744         int hours, mins;
3745         nframes_t frame_rate;
3746         float secs;
3747
3748         if (session == 0) {
3749                 return;
3750         }
3751
3752         switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3753         case AudioClock::BBT:
3754                 session->bbt_time (frame, bbt);
3755                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3756                 break;
3757                 
3758         case AudioClock::SMPTE:
3759                 session->smpte_time (frame, smpte);
3760                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3761                 break;
3762
3763         case AudioClock::MinSec:
3764                 /* XXX this is copied from show_verbose_duration_cursor() */
3765                 frame_rate = session->frame_rate();
3766                 hours = frame / (frame_rate * 3600);
3767                 frame = frame % (frame_rate * 3600);
3768                 mins = frame / (frame_rate * 60);
3769                 frame = frame % (frame_rate * 60);
3770                 secs = (float) frame / (float) frame_rate;
3771                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3772                 break;
3773
3774         default:
3775                 snprintf (buf, sizeof(buf), "%u", frame);
3776                 break;
3777         }
3778
3779         if (xpos >= 0 && ypos >=0) {
3780                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3781         }
3782         else {
3783                 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3784         }
3785         show_verbose_canvas_cursor ();
3786 }
3787
3788 void
3789 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos) 
3790 {
3791         char buf[128];
3792         SMPTE::Time smpte;
3793         BBT_Time sbbt;
3794         BBT_Time ebbt;
3795         int hours, mins;
3796         nframes_t distance, frame_rate;
3797         float secs;
3798         Meter meter_at_start(session->tempo_map().meter_at(start));
3799
3800         if (session == 0) {
3801                 return;
3802         }
3803
3804         switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3805         case AudioClock::BBT:
3806                 session->bbt_time (start, sbbt);
3807                 session->bbt_time (end, ebbt);
3808
3809                 /* subtract */
3810                 /* XXX this computation won't work well if the
3811                 user makes a selection that spans any meter changes.
3812                 */
3813
3814                 ebbt.bars -= sbbt.bars;
3815                 if (ebbt.beats >= sbbt.beats) {
3816                         ebbt.beats -= sbbt.beats;
3817                 } else {
3818                         ebbt.bars--;
3819                         ebbt.beats =  int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3820                 }
3821                 if (ebbt.ticks >= sbbt.ticks) {
3822                         ebbt.ticks -= sbbt.ticks;
3823                 } else {
3824                         ebbt.beats--;
3825                         ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3826                 }
3827                 
3828                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3829                 break;
3830                 
3831         case AudioClock::SMPTE:
3832                 session->smpte_duration (end - start, smpte);
3833                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3834                 break;
3835
3836         case AudioClock::MinSec:
3837                 /* XXX this stuff should be elsewhere.. */
3838                 distance = end - start;
3839                 frame_rate = session->frame_rate();
3840                 hours = distance / (frame_rate * 3600);
3841                 distance = distance % (frame_rate * 3600);
3842                 mins = distance / (frame_rate * 60);
3843                 distance = distance % (frame_rate * 60);
3844                 secs = (float) distance / (float) frame_rate;
3845                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3846                 break;
3847
3848         default:
3849                 snprintf (buf, sizeof(buf), "%u", end - start);
3850                 break;
3851         }
3852
3853         if (xpos >= 0 && ypos >=0) {
3854                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3855         }
3856         else {
3857                 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3858         }
3859         show_verbose_canvas_cursor ();
3860 }
3861
3862 void
3863 Editor::collect_new_region_view (RegionView* rv)
3864 {
3865         latest_regionviews.push_back (rv);
3866 }
3867
3868 void
3869 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3870 {
3871         if (clicked_regionview == 0) {
3872                 return;
3873         }
3874
3875         /* lets try to create new Region for the selection */
3876
3877         vector<boost::shared_ptr<AudioRegion> > new_regions;
3878         create_region_from_selection (new_regions);
3879
3880         if (new_regions.empty()) {
3881                 return;
3882         }
3883
3884         /* XXX fix me one day to use all new regions */
3885         
3886         boost::shared_ptr<Region> region (new_regions.front());
3887
3888         /* add it to the current stream/playlist.
3889
3890            tricky: the streamview for the track will add a new regionview. we will
3891            catch the signal it sends when it creates the regionview to
3892            set the regionview we want to then drag.
3893         */
3894         
3895         latest_regionviews.clear();
3896         sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3897         
3898         /* A selection grab currently creates two undo/redo operations, one for 
3899            creating the new region and another for moving it.
3900         */
3901
3902         begin_reversible_command (_("selection grab"));
3903
3904         boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
3905
3906         XMLNode *before = &(playlist->get_state());
3907         clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
3908         XMLNode *after = &(playlist->get_state());
3909         session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3910
3911         commit_reversible_command ();
3912         
3913         c.disconnect ();
3914         
3915         if (latest_regionviews.empty()) {
3916                 /* something went wrong */
3917                 return;
3918         }
3919
3920         /* we need to deselect all other regionviews, and select this one
3921            i'm ignoring undo stuff, because the region creation will take care of it 
3922         */
3923         selection->set (latest_regionviews);
3924         
3925         drag_info.item = latest_regionviews.front()->get_canvas_group();
3926         drag_info.data = latest_regionviews.front();
3927         drag_info.motion_callback = &Editor::region_drag_motion_callback;
3928         drag_info.finished_callback = &Editor::region_drag_finished_callback;
3929
3930         start_grab (event);
3931         
3932         drag_info.last_trackview = clicked_trackview;
3933         drag_info.last_frame_position = latest_regionviews.front()->region()->position();
3934         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3935         
3936         show_verbose_time_cursor (drag_info.last_frame_position, 10);
3937 }
3938
3939 void
3940 Editor::cancel_selection ()
3941 {
3942         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3943                 (*i)->hide_selection ();
3944         }
3945         begin_reversible_command (_("cancel selection"));
3946         selection->clear ();
3947         clicked_selection = 0;
3948         commit_reversible_command ();
3949 }       
3950
3951 void
3952 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3953 {
3954         nframes_t start = 0;
3955         nframes_t end = 0;
3956
3957         if (session == 0) {
3958                 return;
3959         }
3960
3961         drag_info.item = item;
3962         drag_info.motion_callback = &Editor::drag_selection;
3963         drag_info.finished_callback = &Editor::end_selection_op;
3964
3965         selection_op = op;
3966
3967         switch (op) {
3968         case CreateSelection:
3969                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3970                         drag_info.copy = true;
3971                 } else {
3972                         drag_info.copy = false;
3973                 }
3974                 start_grab (event, selector_cursor);
3975                 break;
3976
3977         case SelectionStartTrim:
3978                 if (clicked_trackview) {
3979                         clicked_trackview->order_selection_trims (item, true);
3980                 } 
3981                 start_grab (event, trimmer_cursor);
3982                 start = selection->time[clicked_selection].start;
3983                 drag_info.pointer_frame_offset = drag_info.grab_frame - start;  
3984                 break;
3985                 
3986         case SelectionEndTrim:
3987                 if (clicked_trackview) {
3988                         clicked_trackview->order_selection_trims (item, false);
3989                 }
3990                 start_grab (event, trimmer_cursor);
3991                 end = selection->time[clicked_selection].end;
3992                 drag_info.pointer_frame_offset = drag_info.grab_frame - end;    
3993                 break;
3994
3995         case SelectionMove:
3996                 start = selection->time[clicked_selection].start;
3997                 start_grab (event);
3998                 drag_info.pointer_frame_offset = drag_info.grab_frame - start;  
3999                 break;
4000         }
4001
4002         if (selection_op == SelectionMove) {
4003                 show_verbose_time_cursor(start, 10);    
4004         } else {
4005                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4006         }
4007 }
4008
4009 void
4010 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4011 {
4012         nframes_t start = 0;
4013         nframes_t end = 0;
4014         nframes_t length;
4015         nframes_t pending_position;
4016
4017         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4018                 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4019         } else {
4020                 pending_position = 0;
4021         }
4022         
4023         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4024                 snap_to (pending_position);
4025         }
4026
4027         /* only alter selection if the current frame is 
4028            different from the last frame position (adjusted)
4029          */
4030         
4031         if (pending_position == drag_info.last_pointer_frame) return;
4032         
4033         switch (selection_op) {
4034         case CreateSelection:
4035                 
4036                 if (drag_info.first_move) {
4037                         snap_to (drag_info.grab_frame);
4038                 }
4039                 
4040                 if (pending_position < drag_info.grab_frame) {
4041                         start = pending_position;
4042                         end = drag_info.grab_frame;
4043                 } else {
4044                         end = pending_position;
4045                         start = drag_info.grab_frame;
4046                 }
4047                 
4048                 /* first drag: Either add to the selection
4049                    or create a new selection->
4050                 */
4051                 
4052                 if (drag_info.first_move) {
4053                         
4054                         begin_reversible_command (_("range selection"));
4055                         
4056                         if (drag_info.copy) {
4057                                 /* adding to the selection */
4058                                 clicked_selection = selection->add (start, end);
4059                                 drag_info.copy = false;
4060                         } else {
4061                                 /* new selection-> */
4062                                 clicked_selection = selection->set (clicked_trackview, start, end);
4063                         }
4064                 } 
4065                 break;
4066                 
4067         case SelectionStartTrim:
4068                 
4069                 if (drag_info.first_move) {
4070                         begin_reversible_command (_("trim selection start"));
4071                 }
4072                 
4073                 start = selection->time[clicked_selection].start;
4074                 end = selection->time[clicked_selection].end;
4075
4076                 if (pending_position > end) {
4077                         start = end;
4078                 } else {
4079                         start = pending_position;
4080                 }
4081                 break;
4082                 
4083         case SelectionEndTrim:
4084                 
4085                 if (drag_info.first_move) {
4086                         begin_reversible_command (_("trim selection end"));
4087                 }
4088                 
4089                 start = selection->time[clicked_selection].start;
4090                 end = selection->time[clicked_selection].end;
4091
4092                 if (pending_position < start) {
4093                         end = start;
4094                 } else {
4095                         end = pending_position;
4096                 }
4097                 
4098                 break;
4099                 
4100         case SelectionMove:
4101                 
4102                 if (drag_info.first_move) {
4103                         begin_reversible_command (_("move selection"));
4104                 }
4105                 
4106                 start = selection->time[clicked_selection].start;
4107                 end = selection->time[clicked_selection].end;
4108                 
4109                 length = end - start;
4110                 
4111                 start = pending_position;
4112                 snap_to (start);
4113                 
4114                 end = start + length;
4115                 
4116                 break;
4117         }
4118         
4119         if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4120                 start_canvas_autoscroll (1);
4121         }
4122
4123         if (start != end) {
4124                 selection->replace (clicked_selection, start, end);
4125         }
4126
4127         drag_info.last_pointer_frame = pending_position;
4128         drag_info.first_move = false;
4129
4130         if (selection_op == SelectionMove) {
4131                 show_verbose_time_cursor(start, 10);    
4132         } else {
4133                 show_verbose_time_cursor(pending_position, 10); 
4134         }
4135 }
4136
4137 void
4138 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4139 {
4140         if (!drag_info.first_move) {
4141                 drag_selection (item, event);
4142                 /* XXX this is not object-oriented programming at all. ick */
4143                 if (selection->time.consolidate()) {
4144                         selection->TimeChanged ();
4145                 }
4146                 commit_reversible_command ();
4147         } else {
4148                 /* just a click, no pointer movement.*/
4149
4150                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4151
4152                         selection->clear_time();
4153
4154                 } 
4155         }
4156
4157         /* XXX what happens if its a music selection? */
4158         session->set_audio_range (selection->time);
4159         stop_canvas_autoscroll ();
4160 }
4161
4162 void
4163 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4164 {
4165         double speed = 1.0;
4166         TimeAxisView* tvp = clicked_trackview;
4167         AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4168
4169         if (tv && tv->is_audio_track()) {
4170                 speed = tv->get_diskstream()->speed();
4171         }
4172         
4173         nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
4174         nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
4175         nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
4176
4177         //drag_info.item = clicked_regionview->get_name_highlight();
4178         drag_info.item = item;
4179         drag_info.motion_callback = &Editor::trim_motion_callback;
4180         drag_info.finished_callback = &Editor::trim_finished_callback;
4181
4182         start_grab (event, trimmer_cursor);
4183         
4184         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4185                 trim_op = ContentsTrim;
4186         } else {
4187                 /* These will get overridden for a point trim.*/
4188                 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4189                         /* closer to start */
4190                         trim_op = StartTrim;
4191                 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4192                         /* closer to end */
4193                         trim_op = EndTrim;
4194                 }
4195         }
4196
4197         switch (trim_op) {
4198         case StartTrim:
4199                 show_verbose_time_cursor(region_start, 10);     
4200                 break;
4201         case EndTrim:
4202                 show_verbose_time_cursor(region_end, 10);       
4203                 break;
4204         case ContentsTrim:
4205                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4206                 break;
4207         }
4208 }
4209
4210 void
4211 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4212 {
4213         RegionView* rv = clicked_regionview;
4214         nframes_t frame_delta = 0;
4215         bool left_direction;
4216         bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4217
4218         /* snap modifier works differently here..
4219            its' current state has to be passed to the 
4220            various trim functions in order to work properly 
4221         */ 
4222
4223         double speed = 1.0;
4224         TimeAxisView* tvp = clicked_trackview;
4225         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4226         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4227
4228         if (tv && tv->is_audio_track()) {
4229                 speed = tv->get_diskstream()->speed();
4230         }
4231         
4232         if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4233                 left_direction = true;
4234         } else {
4235                 left_direction = false;
4236         }
4237
4238         if (obey_snap) {
4239                 snap_to (drag_info.current_pointer_frame);
4240         }
4241
4242         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4243                 return;
4244         }
4245
4246         if (drag_info.first_move) {
4247         
4248                 string trim_type;
4249
4250                 switch (trim_op) {
4251                 case StartTrim:
4252                         trim_type = "Region start trim";
4253                         break;
4254                 case EndTrim:
4255                         trim_type = "Region end trim";
4256                         break;
4257                 case ContentsTrim:
4258                         trim_type = "Region content trim";
4259                         break;
4260                 }
4261
4262                 begin_reversible_command (trim_type);
4263
4264                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4265                         (*i)->fake_set_opaque(false);                   
4266                         (*i)->region()->freeze ();
4267                 
4268                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4269                         if (arv)
4270                                 arv->temporarily_hide_envelope ();
4271
4272                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4273                         insert_result = motion_frozen_playlists.insert (pl);
4274                         if (insert_result.second) {
4275                                 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4276                         }
4277                 }
4278         }
4279
4280         if (left_direction) {
4281                 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4282         } else {
4283                 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4284         }
4285
4286         switch (trim_op) {              
4287         case StartTrim:
4288                 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4289                         break;
4290                 } else {
4291                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4292                                 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4293                         }
4294                         break;
4295                 }
4296                 
4297         case EndTrim:
4298                 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4299                         break;
4300                 } else {
4301                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4302                                 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4303                         }
4304                         break;
4305                 }
4306                 
4307         case ContentsTrim:
4308                 {
4309                         bool swap_direction = false;
4310
4311                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4312                                 swap_direction = true;
4313                         }
4314                         
4315                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4316                              i != selection->regions.by_layer().end(); ++i)
4317                         {
4318                                 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4319                         }
4320                 }
4321                 break;
4322         }
4323
4324         switch (trim_op) {
4325         case StartTrim:
4326                 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);     
4327                 break;
4328         case EndTrim:
4329                 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);   
4330                 break;
4331         case ContentsTrim:
4332                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4333                 break;
4334         }
4335
4336         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4337         drag_info.first_move = false;
4338 }
4339
4340 void
4341 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4342 {
4343         boost::shared_ptr<Region> region (rv.region());
4344
4345         if (region->locked()) {
4346                 return;
4347         }
4348
4349         nframes_t new_bound;
4350
4351         double speed = 1.0;
4352         TimeAxisView* tvp = clicked_trackview;
4353         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4354
4355         if (tv && tv->is_audio_track()) {
4356                 speed = tv->get_diskstream()->speed();
4357         }
4358         
4359         if (left_direction) {
4360                 if (swap_direction) {
4361                         new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4362                 } else {
4363                         new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4364                 }
4365         } else {
4366                 if (swap_direction) {
4367                         new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4368                 } else {
4369                         new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4370                 }
4371         }
4372
4373         if (obey_snap) {
4374                 snap_to (new_bound);
4375         }
4376         region->trim_start ((nframes_t) (new_bound * speed), this);     
4377         rv.region_changed (StartChanged);
4378 }
4379
4380 void
4381 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4382 {
4383         boost::shared_ptr<Region> region (rv.region()); 
4384
4385         if (region->locked()) {
4386                 return;
4387         }
4388
4389         nframes_t new_bound;
4390
4391         double speed = 1.0;
4392         TimeAxisView* tvp = clicked_trackview;
4393         AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4394
4395         if (tv && tv->is_audio_track()) {
4396                 speed = tv->get_diskstream()->speed();
4397         }
4398         
4399         if (left_direction) {
4400                 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4401         } else {
4402                 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4403         }
4404
4405         if (obey_snap) {
4406                 snap_to (new_bound, (left_direction ? 0 : 1));  
4407         }
4408
4409         region->trim_front ((nframes_t) (new_bound * speed), this);
4410
4411         rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4412 }
4413
4414 void
4415 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4416 {
4417         boost::shared_ptr<Region> region (rv.region());
4418
4419         if (region->locked()) {
4420                 return;
4421         }
4422
4423         nframes_t new_bound;
4424
4425         double speed = 1.0;
4426         TimeAxisView* tvp = clicked_trackview;
4427         AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4428
4429         if (tv && tv->is_audio_track()) {
4430                 speed = tv->get_diskstream()->speed();
4431         }
4432         
4433         if (left_direction) {
4434                 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4435         } else {
4436                 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4437         }
4438
4439         if (obey_snap) {
4440                 snap_to (new_bound);
4441         }
4442         region->trim_end ((nframes_t) (new_bound * speed), this);
4443         rv.region_changed (LengthChanged);
4444 }
4445         
4446 void
4447 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4448 {
4449         if (!drag_info.first_move) {
4450                 trim_motion_callback (item, event);
4451                 
4452                 if (!selection->selected (clicked_regionview)) {
4453                         thaw_region_after_trim (*clicked_regionview);           
4454                 } else {
4455                         
4456                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4457                              i != selection->regions.by_layer().end(); ++i)
4458                         {
4459                                 thaw_region_after_trim (**i);
4460                                 (*i)->fake_set_opaque (true);
4461                         }
4462                 }
4463                 
4464                 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4465                         //(*p)->thaw ();
4466                         session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4467                 }
4468                 
4469                 motion_frozen_playlists.clear ();
4470
4471                 commit_reversible_command();
4472         } else {
4473                 /* no mouse movement */
4474                 point_trim (event);
4475         }
4476 }
4477
4478 void
4479 Editor::point_trim (GdkEvent* event)
4480 {
4481         RegionView* rv = clicked_regionview;
4482         nframes_t new_bound = drag_info.current_pointer_frame;
4483
4484         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4485                 snap_to (new_bound);
4486         }
4487
4488         /* Choose action dependant on which button was pressed */
4489         switch (event->button.button) {
4490         case 1:
4491                 trim_op = StartTrim;
4492                 begin_reversible_command (_("Start point trim"));
4493
4494                 if (selection->selected (rv)) {
4495
4496                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4497                              i != selection->regions.by_layer().end(); ++i)
4498                         {
4499                                 if (!(*i)->region()->locked()) {
4500                                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4501                                         XMLNode &before = pl->get_state();
4502                                         (*i)->region()->trim_front (new_bound, this);   
4503                                         XMLNode &after = pl->get_state();
4504                                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4505                                 }
4506                         }
4507
4508                 } else {
4509
4510                         if (!rv->region()->locked()) {
4511                                 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4512                                 XMLNode &before = pl->get_state();
4513                                 rv->region()->trim_front (new_bound, this);     
4514                                 XMLNode &after = pl->get_state();
4515                                 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4516                         }
4517                 }
4518
4519                 commit_reversible_command();
4520         
4521                 break;
4522         case 2:
4523                 trim_op = EndTrim;
4524                 begin_reversible_command (_("End point trim"));
4525
4526                 if (selection->selected (rv)) {
4527                         
4528                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4529                         {
4530                                 if (!(*i)->region()->locked()) {
4531                                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4532                                         XMLNode &before = pl->get_state();
4533                                         (*i)->region()->trim_end (new_bound, this);
4534                                         XMLNode &after = pl->get_state();
4535                                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4536                                 }
4537                         }
4538
4539                 } else {
4540
4541                         if (!rv->region()->locked()) {
4542                                 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4543                                 XMLNode &before = pl->get_state();
4544                                 rv->region()->trim_end (new_bound, this);
4545                                 XMLNode &after = pl->get_state();
4546                                 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4547                         }
4548                 }
4549
4550                 commit_reversible_command();
4551         
4552                 break;
4553         default:
4554                 break;
4555         }
4556 }
4557
4558 void
4559 Editor::thaw_region_after_trim (RegionView& rv)
4560 {
4561         boost::shared_ptr<Region> region (rv.region());
4562
4563         if (region->locked()) {
4564                 return;
4565         }
4566
4567         region->thaw (_("trimmed region"));
4568         XMLNode &after = region->playlist()->get_state();
4569         session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4570
4571         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4572         if (arv)
4573                 arv->unhide_envelope ();
4574 }
4575
4576 void
4577 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4578 {
4579         Marker* marker;
4580         bool is_start;
4581
4582         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4583                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4584                 /*NOTREACHED*/
4585         }
4586
4587         Location* location = find_location_from_marker (marker, is_start);      
4588         location->set_hidden (true, this);
4589 }
4590
4591
4592 void
4593 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4594 {
4595         if (session == 0) {
4596                 return;
4597         }
4598
4599         drag_info.item = item;
4600         drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4601         drag_info.finished_callback = &Editor::end_range_markerbar_op;
4602
4603         range_marker_op = op;
4604
4605         if (!temp_location) {
4606                 temp_location = new Location;
4607         }
4608         
4609         switch (op) {
4610         case CreateRangeMarker:
4611         case CreateTransportMarker:
4612                 
4613                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4614                         drag_info.copy = true;
4615                 } else {
4616                         drag_info.copy = false;
4617                 }
4618                 start_grab (event, selector_cursor);
4619                 break;
4620         }
4621
4622         show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4623         
4624 }
4625
4626 void
4627 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4628 {
4629         nframes_t start = 0;
4630         nframes_t end = 0;
4631         ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4632         
4633         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4634                 snap_to (drag_info.current_pointer_frame);
4635         }
4636
4637         /* only alter selection if the current frame is 
4638            different from the last frame position.
4639          */
4640         
4641         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4642         
4643         switch (range_marker_op) {
4644         case CreateRangeMarker:
4645         case CreateTransportMarker:
4646                 if (drag_info.first_move) {
4647                         snap_to (drag_info.grab_frame);
4648                 }
4649                 
4650                 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4651                         start = drag_info.current_pointer_frame;
4652                         end = drag_info.grab_frame;
4653                 } else {
4654                         end = drag_info.current_pointer_frame;
4655                         start = drag_info.grab_frame;
4656                 }
4657                 
4658                 /* first drag: Either add to the selection
4659                    or create a new selection.
4660                 */
4661                 
4662                 if (drag_info.first_move) {
4663                         
4664                         temp_location->set (start, end);
4665                         
4666                         crect->show ();
4667
4668                         update_marker_drag_item (temp_location);
4669                         range_marker_drag_rect->show();
4670                         range_marker_drag_rect->raise_to_top();
4671                         
4672                 } 
4673                 break;          
4674         }
4675         
4676         if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4677                 start_canvas_autoscroll (1);
4678         }
4679         
4680         if (start != end) {
4681                 temp_location->set (start, end);
4682
4683                 double x1 = frame_to_pixel (start);
4684                 double x2 = frame_to_pixel (end);
4685                 crect->property_x1() = x1;
4686                 crect->property_x2() = x2;
4687
4688                 update_marker_drag_item (temp_location);
4689         }
4690
4691         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4692         drag_info.first_move = false;
4693
4694         show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4695         
4696 }
4697
4698 void
4699 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4700 {
4701         Location * newloc = 0;
4702         string rangename;
4703         
4704         if (!drag_info.first_move) {
4705                 drag_range_markerbar_op (item, event);
4706
4707                 switch (range_marker_op) {
4708                 case CreateRangeMarker:
4709                     {
4710                         begin_reversible_command (_("new range marker"));
4711                         XMLNode &before = session->locations()->get_state();
4712                         session->locations()->next_available_name(rangename,"unnamed");
4713                         newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4714                         session->locations()->add (newloc, true);
4715                         XMLNode &after = session->locations()->get_state();
4716                         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4717                         commit_reversible_command ();
4718                         
4719                         range_bar_drag_rect->hide();
4720                         range_marker_drag_rect->hide();
4721                         break;
4722                     }
4723
4724                 case CreateTransportMarker:
4725                         // popup menu to pick loop or punch
4726                         new_transport_marker_context_menu (&event->button, item);
4727                         
4728                         break;
4729                 }
4730         } else {
4731                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4732
4733                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4734
4735                         nframes_t start;
4736                         nframes_t end;
4737
4738                         start = session->locations()->first_mark_before (drag_info.grab_frame);
4739                         end = session->locations()->first_mark_after (drag_info.grab_frame);
4740                         
4741                         if (end == max_frames) {
4742                                 end = session->current_end_frame ();
4743                         }
4744
4745                         if (start == 0) {
4746                                 start = session->current_start_frame ();
4747                         }
4748
4749                         switch (mouse_mode) {
4750                         case MouseObject:
4751                                 /* find the two markers on either side and then make the selection from it */
4752                                 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4753                                 break;
4754
4755                         case MouseRange:
4756                                 /* find the two markers on either side of the click and make the range out of it */
4757                                 selection->set (0, start, end);
4758                                 break;
4759
4760                         default:
4761                                 break;
4762                         }
4763                 } 
4764         }
4765
4766         stop_canvas_autoscroll ();
4767 }
4768
4769
4770
4771 void
4772 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4773 {
4774         drag_info.item = item;
4775         drag_info.motion_callback = &Editor::drag_mouse_zoom;
4776         drag_info.finished_callback = &Editor::end_mouse_zoom;
4777
4778         start_grab (event, zoom_cursor);
4779
4780         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4781 }
4782
4783 void
4784 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4785 {
4786         nframes_t start;
4787         nframes_t end;
4788
4789         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4790                 snap_to (drag_info.current_pointer_frame);
4791                 
4792                 if (drag_info.first_move) {
4793                         snap_to (drag_info.grab_frame);
4794                 }
4795         }
4796                 
4797         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4798
4799         /* base start and end on initial click position */
4800         if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4801                 start = drag_info.current_pointer_frame;
4802                 end = drag_info.grab_frame;
4803         } else {
4804                 end = drag_info.current_pointer_frame;
4805                 start = drag_info.grab_frame;
4806         }
4807         
4808         if (start != end) {
4809
4810                 if (drag_info.first_move) {
4811                         zoom_rect->show();
4812                         zoom_rect->raise_to_top();
4813                 }
4814
4815                 reposition_zoom_rect(start, end);
4816
4817                 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4818                 drag_info.first_move = false;
4819
4820                 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4821         }
4822 }
4823
4824 void
4825 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4826 {
4827         if (!drag_info.first_move) {
4828                 drag_mouse_zoom (item, event);
4829                 
4830                 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4831                         temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4832                 } else {
4833                         temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4834                 }               
4835         } else {
4836                 temporal_zoom_to_frame (false, drag_info.grab_frame);
4837                 /*
4838                 temporal_zoom_step (false);
4839                 center_screen (drag_info.grab_frame);
4840                 */
4841         }
4842
4843         zoom_rect->hide();
4844 }
4845
4846 void
4847 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4848 {
4849         double x1 = frame_to_pixel (start);
4850         double x2 = frame_to_pixel (end);
4851         double y2 = full_canvas_height - 1.0;
4852
4853         zoom_rect->property_x1() = x1;
4854         zoom_rect->property_y1() = 1.0;
4855         zoom_rect->property_x2() = x2;
4856         zoom_rect->property_y2() = y2;
4857 }
4858
4859 void
4860 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4861 {
4862         drag_info.item = item;
4863         drag_info.motion_callback = &Editor::drag_rubberband_select;
4864         drag_info.finished_callback = &Editor::end_rubberband_select;
4865
4866         start_grab (event, cross_hair_cursor);
4867
4868         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4869 }
4870
4871 void
4872 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4873 {
4874         nframes_t start;
4875         nframes_t end;
4876         double y1;
4877         double y2;
4878
4879         /* use a bigger drag threshold than the default */
4880
4881         if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4882                 return;
4883         }
4884
4885         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4886                 if (drag_info.first_move) {
4887                         snap_to (drag_info.grab_frame);
4888                 } 
4889                 snap_to (drag_info.current_pointer_frame);
4890         }
4891
4892         /* base start and end on initial click position */
4893
4894         if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4895                 start = drag_info.current_pointer_frame;
4896                 end = drag_info.grab_frame;
4897         } else {
4898                 end = drag_info.current_pointer_frame;
4899                 start = drag_info.grab_frame;
4900         }
4901
4902         if (drag_info.current_pointer_y < drag_info.grab_y) {
4903                 y1 = drag_info.current_pointer_y;
4904                 y2 = drag_info.grab_y;
4905         } else {
4906                 y2 = drag_info.current_pointer_y;
4907                 y1 = drag_info.grab_y;
4908         }
4909
4910         
4911         if (start != end || y1 != y2) {
4912
4913                 double x1 = frame_to_pixel (start);
4914                 double x2 = frame_to_pixel (end);
4915                 
4916                 rubberband_rect->property_x1() = x1;
4917                 rubberband_rect->property_y1() = y1;
4918                 rubberband_rect->property_x2() = x2;
4919                 rubberband_rect->property_y2() = y2;
4920
4921                 rubberband_rect->show();
4922                 rubberband_rect->raise_to_top();
4923                 
4924                 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4925                 drag_info.first_move = false;
4926
4927                 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4928         }
4929 }
4930
4931 void
4932 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4933 {
4934         if (!drag_info.first_move) {
4935
4936                 drag_rubberband_select (item, event);
4937
4938                 double y1,y2;
4939                 if (drag_info.current_pointer_y < drag_info.grab_y) {
4940                         y1 = drag_info.current_pointer_y;
4941                         y2 = drag_info.grab_y;
4942                 }
4943                 else {
4944                         y2 = drag_info.current_pointer_y;
4945                         y1 = drag_info.grab_y;
4946                 }
4947
4948
4949                 Selection::Operation op = Keyboard::selection_type (event->button.state);
4950                 bool commit;
4951
4952                 begin_reversible_command (_("rubberband selection"));
4953
4954                 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4955                         commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
4956                 } else {
4957                         commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
4958                 }               
4959
4960                 if (commit) {
4961                         commit_reversible_command ();
4962                 }
4963                 
4964         } else {
4965                 selection->clear_tracks();
4966                 selection->clear_regions();
4967                 selection->clear_points ();
4968                 selection->clear_lines ();
4969         }
4970
4971         rubberband_rect->hide();
4972 }
4973
4974
4975 gint
4976 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4977 {
4978         using namespace Gtkmm2ext;
4979
4980         ArdourPrompter prompter (false);
4981
4982         prompter.set_prompt (_("Name for region:"));
4983         prompter.set_initial_text (clicked_regionview->region()->name());
4984         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4985         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4986         prompter.show_all ();
4987         switch (prompter.run ()) {
4988         case Gtk::RESPONSE_ACCEPT:
4989         string str;
4990                 prompter.get_result(str);
4991                 if (str.length()) {
4992                         clicked_regionview->region()->set_name (str);
4993                 }
4994                 break;
4995         }
4996         return true;
4997 }
4998
4999 void
5000 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5001 {
5002         drag_info.item = item;
5003         drag_info.motion_callback = &Editor::time_fx_motion;
5004         drag_info.finished_callback = &Editor::end_time_fx;
5005
5006         start_grab (event);
5007
5008         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5009 }
5010
5011 void
5012 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5013 {
5014         RegionView* rv = clicked_regionview;
5015
5016         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5017                 snap_to (drag_info.current_pointer_frame);
5018         }
5019
5020         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5021                 return;
5022         }
5023
5024         if (drag_info.current_pointer_frame > rv->region()->position()) {
5025                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5026         }
5027
5028         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5029         drag_info.first_move = false;
5030
5031         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5032 }
5033
5034 void
5035 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5036 {
5037         clicked_regionview->get_time_axis_view().hide_timestretch ();
5038
5039         if (drag_info.first_move) {
5040                 return;
5041         }
5042
5043         if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5044                 /* backwards drag of the left edge - not usable */
5045                 return;
5046         }
5047         
5048         nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5049 #ifdef USE_RUBBERBAND
5050         float percentage = (float) ((double) newlen / (double) clicked_regionview->region()->length());
5051 #else
5052         float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5053 #endif  
5054
5055         begin_reversible_command (_("timestretch"));
5056
5057         if (run_timestretch (selection->regions, percentage) == 0) {
5058                 session->commit_reversible_command ();
5059         }
5060 }
5061
5062 void
5063 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
5064 {
5065         /* no brushing without a useful snap setting */
5066
5067         // FIXME
5068         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
5069         assert(arv);
5070
5071         switch (snap_mode) {
5072         case SnapMagnetic:
5073                 return; /* can't work because it allows region to be placed anywhere */
5074         default:
5075                 break; /* OK */
5076         }
5077
5078         switch (snap_type) {
5079         case SnapToMark:
5080                 return;
5081
5082         default:
5083                 break;
5084         }
5085
5086         /* don't brush a copy over the original */
5087         
5088         if (pos == rv->region()->position()) {
5089                 return;
5090         }
5091
5092         RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
5093
5094         if (atv == 0 || !atv->is_audio_track()) {
5095                 return;
5096         }
5097
5098         boost::shared_ptr<Playlist> playlist = atv->playlist();
5099         double speed = atv->get_diskstream()->speed();
5100         
5101         XMLNode &before = playlist->get_state();
5102         playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
5103         XMLNode &after = playlist->get_state();
5104         session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5105         
5106         // playlist is frozen, so we have to update manually
5107         
5108         playlist->Modified(); /* EMIT SIGNAL */
5109 }
5110
5111 gint
5112 Editor::track_height_step_timeout ()
5113 {
5114         struct timeval now;
5115         struct timeval delta;
5116         
5117         gettimeofday (&now, 0);
5118         timersub (&now, &last_track_height_step_timestamp, &delta);
5119         
5120         if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
5121                 current_stepping_trackview = 0;
5122                 return false;
5123         }
5124         return true;
5125 }
5126