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