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