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