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