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