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