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