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