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