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