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