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