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