mostly editor.cc & editor_mouse.cc for Gdk::Cursor, Gnome::Canvas::Item
[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                 track_canvas_scroller.get_window()->set_cursor(*current_canvas_cursor);
246         }
247 }
248
249 void
250 Editor::step_mouse_mode (bool next)
251 {
252         switch (current_mouse_mode()) {
253         case MouseObject:
254                 if (next) set_mouse_mode (MouseRange);
255                 else set_mouse_mode (MouseTimeFX);
256                 break;
257
258         case MouseRange:
259                 if (next) set_mouse_mode (MouseZoom);
260                 else set_mouse_mode (MouseObject);
261                 break;
262
263         case MouseZoom:
264                 if (next) set_mouse_mode (MouseGain);
265                 else set_mouse_mode (MouseRange);
266                 break;
267         
268         case MouseGain:
269                 if (next) set_mouse_mode (MouseTimeFX);
270                 else set_mouse_mode (MouseZoom);
271                 break;
272         
273         case MouseTimeFX:
274                 if (next) set_mouse_mode (MouseAudition);
275                 else set_mouse_mode (MouseGain);
276                 break;
277
278         case MouseAudition:
279                 if (next) set_mouse_mode (MouseObject);
280                 else set_mouse_mode (MouseTimeFX);
281                 break;
282         }
283 }
284
285 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                                                   step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
728                                                         current_stepping_trackview = clicked_trackview;
729                                                 }
730                                                 gettimeofday (&last_track_height_step_timestamp, 0);
731                                                 current_stepping_trackview->step_height (true);
732                                         }
733                                 }
734                                 else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
735                                         temporal_zoom_to_frame (true, where);
736                                 }
737                         }
738                 }
739                 break;
740
741         case 5:
742                 switch (mouse_mode) {
743                 case MouseZoom:
744                         // temporal_zoom_to_frame (false, where);
745                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
746                                 temporal_zoom_to_frame (false, where);
747                         }
748                         else {
749                                 temporal_zoom_step (false);
750                         }
751                         break;
752                 default:
753
754                         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
755                                 scroll_forward (0.6f);
756                         }
757                         else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
758                                 scroll_tracks_down_line ();
759                         } else {
760                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
761                                         if (clicked_trackview) {
762                                                 if (!current_stepping_trackview) {
763                                                   step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
764                                                         current_stepping_trackview = clicked_trackview;
765                                                 }
766                                                 gettimeofday (&last_track_height_step_timestamp, 0);
767                                                 current_stepping_trackview->step_height (false);
768                                         }
769                                 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
770                                         temporal_zoom_to_frame (false, where);
771                                 }
772                         }
773                 }
774                 break;
775
776         default:
777                 break;
778
779         }
780         return FALSE;
781 }
782
783 gint
784 Editor::button_release_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType item_type)
785 {
786         jack_nframes_t where = event_frame (event, 0, 0);
787
788         /* no action if we're recording */
789                                                 
790         if (session && session->actively_recording()) {
791                 return TRUE;
792         }
793
794         /* first, see if we're finishing a drag ... */
795
796         if (drag_info.item) {
797                 if (end_grab (item, event)) {
798                         /* grab dragged, so do nothing else */
799                         return TRUE;
800                 }
801         }
802         
803         /* edit events get handled here */
804         
805         if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
806                 switch (item_type) {
807                 case RegionItem:
808                         edit_region ();
809                         break;
810
811                 case TempoMarkerItem:
812                         edit_tempo_marker (item);
813                         break;
814                         
815                 case MeterMarkerItem:
816                         edit_meter_marker (item);
817                         break;
818                         
819                 case AudioRegionViewName:
820                         if (clicked_regionview->name_active()) {
821                                 return mouse_rename_region (item, event);
822                         }
823                         break;
824
825                 default:
826                         break;
827                 }
828                 return TRUE;
829         }
830
831         /* context menu events get handled here */
832
833         if (Keyboard::is_context_menu_event (&event->button)) {
834
835                 if (drag_info.item == 0) {
836
837                         /* no matter which button pops up the context menu, tell the menu
838                            widget to use button 1 to drive menu selection.
839                         */
840
841                         switch (item_type) {
842                         case FadeInItem:
843                         case FadeInHandleItem:
844                         case FadeOutItem:
845                         case FadeOutHandleItem:
846                                 popup_fade_context_menu (1, event->button.time, item, item_type);
847                                 break;
848
849                         case StreamItem:
850                                 popup_track_context_menu (1, event->button.time, item_type, false, where);
851                                 break;
852                                 
853                         case RegionItem:
854                         case AudioRegionViewNameHighlight:
855                         case AudioRegionViewName:
856                                 popup_track_context_menu (1, event->button.time, item_type, false, where);
857                                 break;
858                                 
859                         case SelectionItem:
860                                 popup_track_context_menu (1, event->button.time, item_type, true, where);
861                                 break;
862
863                         case AutomationTrackItem:
864                                 popup_track_context_menu (1, event->button.time, item_type, false, where);
865                                 break;
866
867                         case MarkerBarItem: 
868                         case RangeMarkerBarItem: 
869                         case TransportMarkerBarItem: 
870                         case TempoBarItem:
871                         case MeterBarItem:
872                                 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
873                                 break;
874
875                         case MarkerItem:
876                                 marker_context_menu (&event->button, item);
877                                 break;
878
879                         case TempoMarkerItem:
880                                 tm_marker_context_menu (&event->button, item);
881                                 break;
882                                 
883                         case MeterMarkerItem:
884                                 tm_marker_context_menu (&event->button, item);
885                                 break;
886
887                         case CrossfadeViewItem:
888                                 popup_track_context_menu (1, event->button.time, item_type, false, where);
889                                 break;
890
891                         /* <CMT Additions> */
892                         case ImageFrameItem:
893                                 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
894                                 break ;
895                         case ImageFrameTimeAxisItem:
896                                 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
897                                 break ;
898                         case MarkerViewItem:
899                                 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
900                                 break ;
901                         case MarkerTimeAxisItem:
902                                 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
903                                 break ;
904                         /* <CMT Additions> */
905
906                                 
907                         default:
908                                 break;
909                         }
910
911                         return TRUE;
912                 }
913         }
914
915         /* delete events get handled here */
916
917         if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
918
919                 switch (item_type) {
920                 case TempoMarkerItem:
921                         remove_tempo_marker (item);
922                         break;
923                         
924                 case MeterMarkerItem:
925                         remove_meter_marker (item);
926                         break;
927
928                 case MarkerItem:
929                         remove_marker (item, &event);
930                         break;
931
932                 case RegionItem:
933                         if (mouse_mode == MouseObject) {
934                                 remove_clicked_region ();
935                         }
936                         break;
937                         
938                 case GainControlPointItem:
939                         if (mouse_mode == MouseGain) {
940                                 remove_gain_control_point (item, event);
941                         }
942                         break;
943
944                 case GainAutomationControlPointItem:
945                 case PanAutomationControlPointItem:
946                 case RedirectAutomationControlPointItem:
947                         remove_control_point (item, event);
948                         break;
949
950                 default:
951                         break;
952                 }
953                 return TRUE;
954         }
955
956         switch (event->button.button) {
957         case 1:
958
959                 switch (item_type) {
960                 /* see comments in button_press_handler */
961                 case EditCursorItem:
962                 case PlayheadCursorItem:
963                 case MarkerItem:
964                 case GainLineItem:
965                 case GainAutomationLineItem:
966                 case PanAutomationLineItem:
967                 case RedirectAutomationLineItem:
968                 case StartSelectionTrimItem:
969                 case EndSelectionTrimItem:
970                         return TRUE;
971
972                 case MarkerBarItem:
973                         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
974                                 snap_to (where, 0, true);
975                         }
976                         mouse_add_new_marker (where);
977                         return TRUE;
978
979                 case TempoBarItem:
980                         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
981                                 snap_to (where);
982                         }
983                         mouse_add_new_tempo_event (where);
984                         return TRUE;
985                         
986                 case MeterBarItem:
987                         mouse_add_new_meter_event (pixel_to_frame (event->button.x));
988                         return TRUE;
989                         break;
990
991                 default:
992                         break;
993                 }
994
995                 switch (mouse_mode) {
996                 case MouseObject:
997                         switch (item_type) {
998                         case AutomationTrackItem:
999                                 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->add_automation_event 
1000                                         (item,
1001                                          event,
1002                                          where,
1003                                          event->button.y);
1004                                 return TRUE;
1005                                 break;
1006
1007                         default:
1008                                 break;
1009                         }
1010                         break;
1011
1012                 case MouseGain:
1013                         switch (item_type) {
1014                         case RegionItem:
1015                                 clicked_regionview->add_gain_point_event (item, event);
1016                                 return TRUE;
1017                                 break;
1018                                 
1019                         case AutomationTrackItem:
1020                                 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1021                                         add_automation_event (item, event, where, event->button.y);
1022                                 return TRUE;
1023                                 break;
1024                         default:
1025                                 break;
1026                         }
1027                         break;
1028                         
1029                 case MouseAudition:
1030                         switch (item_type) {
1031                         case RegionItem:
1032                                 audition_selected_region ();
1033                                 break;
1034                         default:
1035                                 break;
1036                         }
1037                         break;
1038
1039                 default:
1040                         break;
1041
1042                 }
1043
1044                 return TRUE;
1045                 break;
1046
1047
1048         case 2:
1049                 switch (mouse_mode) {
1050                         
1051                 case MouseObject:
1052                         switch (item_type) {
1053                         case RegionItem:
1054                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
1055                                         raise_region ();
1056                                 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) {
1057                                         lower_region ();
1058                                 } else {
1059                                         // Button2 click is unused
1060                                 }
1061                                 return TRUE;
1062                                 
1063                                 break;
1064                                 
1065                         default:
1066                                 break;
1067                         }
1068                         break;
1069                         
1070                 case MouseRange:
1071                         
1072                         // x_style_paste (where, 1.0);
1073                         return TRUE;
1074                         break;
1075                         
1076                 default:
1077                         break;
1078                 }
1079
1080                 break;
1081         
1082         case 3:
1083                 break;
1084                 
1085         default:
1086                 break;
1087         }
1088         return FALSE;
1089 }
1090
1091 void
1092 Editor::maybe_autoscroll (GdkEvent* event)
1093 {
1094         jack_nframes_t one_page = (jack_nframes_t) rint (canvas_width * frames_per_unit);
1095         jack_nframes_t rightmost_frame = leftmost_frame + one_page;
1096
1097         jack_nframes_t frame = drag_info.current_pointer_frame;
1098
1099         if (autoscroll_timeout_tag < 0) {
1100                 if (frame > rightmost_frame) {
1101                         if (rightmost_frame < max_frames) {
1102                                 start_canvas_autoscroll (1);
1103                         }
1104                 } else if (frame < leftmost_frame) {
1105                         if (leftmost_frame > 0) {
1106                                 start_canvas_autoscroll (-1);
1107                         }
1108                 } 
1109         } else {
1110                 if (frame >= leftmost_frame && frame < rightmost_frame) {
1111                         stop_canvas_autoscroll ();
1112                 }
1113         }
1114 }
1115
1116 gint
1117 Editor::enter_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType item_type)
1118 {
1119         ControlPoint* cp;
1120         Marker * marker;
1121         double fraction;
1122         
1123         switch (item_type) {
1124         case GainControlPointItem:
1125                 if (mouse_mode == MouseGain) {
1126                         cp = reinterpret_cast<ControlPoint*>(gtk_object_get_data (GTK_OBJECT(item), "control_point"));
1127                         cp->set_visible (true);
1128
1129                         double at_x, at_y;
1130                         at_x = cp->get_x();
1131                         at_y = cp->get_y ();
1132                         gnome_canvas_item_i2w (cp->item, &at_x, &at_y);
1133                         at_x += 20.0;
1134                         at_y += 20.0;
1135
1136                         fraction = 1.0 - (cp->get_y() / cp->line.height());
1137
1138                         set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1139                         show_verbose_canvas_cursor ();
1140
1141                         if (is_drawable()) {
1142                                 track_canvas_scroller.get_window()->set_cursor (*fader_cursor);
1143                         }
1144                 }
1145                 break;
1146
1147         case GainAutomationControlPointItem:
1148         case PanAutomationControlPointItem:
1149         case RedirectAutomationControlPointItem:
1150                 cp = reinterpret_cast<ControlPoint*>(gtk_object_get_data (GTK_OBJECT(item), "control_point"));
1151                 cp->set_visible (true);
1152                 
1153                 double at_x, at_y;
1154                 at_x = cp->get_x();
1155                 at_y = cp->get_y ();
1156                 gnome_canvas_item_i2w (cp->item, &at_x, &at_y);
1157                 at_x += 20.0;
1158                 at_y += 20.0;
1159
1160                 fraction = 1.0 - (cp->get_y() / cp->line.height());
1161         
1162                 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1163                 show_verbose_canvas_cursor ();
1164                 
1165                 if (is_drawable()) {
1166                         track_canvas_scroller.get_window()->set_cursor (*fader_cursor);
1167                 }
1168                 break;
1169                 
1170         case GainLineItem:
1171                 if (mouse_mode == MouseGain) {
1172                         gnome_canvas_item_set (item, "fill_color_rgba", color_map[cEnteredGainLine], NULL);
1173                         if (is_drawable()) {
1174                                 track_canvas_scroller.get_window()->set_cursor (*fader_cursor);
1175                         }
1176                 }
1177                 break;
1178                         
1179         case GainAutomationLineItem:
1180         case RedirectAutomationLineItem:
1181         case PanAutomationLineItem:
1182                 gnome_canvas_item_set (item, "fill_color_rgba", color_map[cEnteredAutomationLine], NULL);
1183                 if (is_drawable()) {
1184                         track_canvas_scroller.get_window()->set_cursor (*fader_cursor);
1185                 }
1186                 break;
1187                 
1188         case AudioRegionViewNameHighlight:
1189                 if (is_drawable() && mouse_mode == MouseObject) {
1190                         track_canvas_scroller.get_window()->set_cursor (*trimmer_cursor);
1191                 }
1192                 break;
1193
1194         case StartSelectionTrimItem:
1195         case EndSelectionTrimItem:
1196         /* <CMT Additions> */
1197         case ImageFrameHandleStartItem:
1198         case ImageFrameHandleEndItem:
1199         case MarkerViewHandleStartItem:
1200         case MarkerViewHandleEndItem:
1201         /* </CMT Additions> */
1202
1203                 if (is_drawable()) {
1204                         track_canvas_scroller.get_window()->set_cursor (*trimmer_cursor);
1205                 }
1206                 break;
1207
1208         case EditCursorItem:
1209         case PlayheadCursorItem:
1210                 if (is_drawable()) {
1211                         track_canvas_scroller.get_window()->set_cursor (*grabber_cursor);
1212                 }
1213                 break;
1214
1215         case AudioRegionViewName:
1216                 
1217                 /* when the name is not an active item, the entire name highlight is for trimming */
1218
1219                 if (!reinterpret_cast<AudioRegionView *> (gtk_object_get_data(GTK_OBJECT(item), "regionview"))->name_active()) {
1220                         if (mouse_mode == MouseObject && is_drawable()) {
1221                                 track_canvas_scroller.get_window()->set_cursor (*trimmer_cursor);
1222                         }
1223                 } 
1224                 break;
1225
1226
1227         case AutomationTrackItem:
1228                 if (is_drawable()) {
1229                         Gdk::Cursor *cursor;
1230                         switch (mouse_mode) {
1231                         case MouseRange:
1232                                 cursor = selector_cursor;
1233                                 break;
1234                         case MouseZoom:
1235                                 cursor = zoom_cursor;
1236                                 break;
1237                         default:
1238                                 cursor = cross_hair_cursor;
1239                                 break;
1240                         }
1241
1242                         track_canvas_scroller.get_window()->set_cursor (*cursor);
1243
1244                         AutomationTimeAxisView* atv;
1245                         if ((atv = static_cast<AutomationTimeAxisView*>(gtk_object_get_data(GTK_OBJECT(item), "trackview"))) != 0) {
1246                                 clear_entered_track = false;
1247                                 set_entered_track (atv);
1248                         }
1249                 }
1250                 break;
1251
1252         case MarkerBarItem:
1253         case RangeMarkerBarItem:
1254         case TransportMarkerBarItem:
1255         case MeterBarItem:
1256         case TempoBarItem:
1257                 if (is_drawable()) {
1258                         time_canvas_scroller.get_window()->set_cursor (*timebar_cursor);
1259                 }
1260                 break;
1261
1262         case MarkerItem:
1263                 if ((marker = static_cast<Marker *> (gtk_object_get_data (GTK_OBJECT(item), "marker"))) == 0) {
1264                         break;
1265                 }
1266                 marker->set_color_rgba (color_map[cEnteredMarker]);
1267                 // fall through
1268         case MeterMarkerItem:
1269         case TempoMarkerItem:
1270                 if (is_drawable()) {
1271                         time_canvas_scroller.get_window()->set_cursor (*timebar_cursor);
1272                 }
1273                 break;
1274         case FadeInHandleItem:
1275         case FadeOutHandleItem:
1276                 if (mouse_mode == MouseObject) {
1277                         gnome_canvas_item_set (item, "fill_color_rgba", 0, "outline_pixels", 1, NULL);
1278                 }
1279                 break;
1280
1281         default:
1282                 break;
1283         }
1284
1285         /* second pass to handle entered track status in a comprehensible way.
1286          */
1287
1288         switch (item_type) {
1289         case GainLineItem:
1290         case GainAutomationLineItem:
1291         case RedirectAutomationLineItem:
1292         case PanAutomationLineItem:
1293         case GainControlPointItem:
1294         case GainAutomationControlPointItem:
1295         case PanAutomationControlPointItem:
1296         case RedirectAutomationControlPointItem:
1297                 /* these do not affect the current entered track state */
1298                 clear_entered_track = false;
1299                 break;
1300
1301         case AutomationTrackItem:
1302                 /* handled above already */
1303                 break;
1304
1305         default:
1306                 set_entered_track (0);
1307                 break;
1308         }
1309
1310         return FALSE;
1311 }
1312
1313 gint
1314 Editor::leave_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType item_type)
1315 {
1316         AutomationLine* al;
1317         ControlPoint* cp;
1318         Marker *marker;
1319         Location *loc;
1320         AudioRegionView* rv;
1321         bool is_start;
1322
1323         switch (item_type) {
1324         case GainControlPointItem:
1325         case GainAutomationControlPointItem:
1326         case PanAutomationControlPointItem:
1327         case RedirectAutomationControlPointItem:
1328                 cp = reinterpret_cast<ControlPoint*>(gtk_object_get_data (GTK_OBJECT(item), "control_point"));
1329                 if (cp->line.npoints() > 1) {
1330                         if (!cp->selected) {
1331                                 cp->set_visible (false);
1332                         }
1333                 }
1334                 
1335                 if (is_drawable()) {
1336                         track_canvas_scroller.get_window()->set_cursor (*current_canvas_cursor);
1337                 }
1338
1339                 hide_verbose_canvas_cursor ();
1340                 break;
1341                 
1342         case AudioRegionViewNameHighlight:
1343         case StartSelectionTrimItem:
1344         case EndSelectionTrimItem:
1345         case EditCursorItem:
1346         case PlayheadCursorItem:
1347         /* <CMT Additions> */
1348         case ImageFrameHandleStartItem:
1349         case ImageFrameHandleEndItem:
1350         case MarkerViewHandleStartItem:
1351         case MarkerViewHandleEndItem:
1352         /* </CMT Additions> */
1353                 if (is_drawable()) {
1354                         track_canvas_scroller.get_window()->set_cursor (*current_canvas_cursor);
1355                 }
1356                 break;
1357
1358         case GainLineItem:
1359         case GainAutomationLineItem:
1360         case RedirectAutomationLineItem:
1361         case PanAutomationLineItem:
1362                 al = reinterpret_cast<AutomationLine*> (gtk_object_get_data (GTK_OBJECT(item),"line"));
1363                 gnome_canvas_item_set (item, "fill_color_rgba", al->get_line_color(), NULL);
1364                 if (is_drawable()) {
1365                         track_canvas_scroller.get_window()->set_cursor (*current_canvas_cursor);
1366                 }
1367                 break;
1368
1369         case AudioRegionViewName:
1370                 /* see enter_handler() for notes */
1371                 if (!reinterpret_cast<AudioRegionView *> (gtk_object_get_data(GTK_OBJECT(item), "regionview"))->name_active()) {
1372                         if (is_drawable() && mouse_mode == MouseObject) {
1373                                 track_canvas_scroller.get_window()->set_cursor (*current_canvas_cursor);
1374                         }
1375                 }
1376                 break;
1377
1378         case RangeMarkerBarItem:
1379         case TransportMarkerBarItem:
1380         case MeterBarItem:
1381         case TempoBarItem:
1382         case MarkerBarItem:
1383                 if (is_drawable()) {
1384                         time_canvas_scroller.get_window()->set_cursor (*timebar_cursor);
1385                 }
1386                 break;
1387                 
1388         case MarkerItem:
1389                 if ((marker = static_cast<Marker *> (gtk_object_get_data (GTK_OBJECT(item), "marker"))) == 0) {
1390                         break;
1391                 }
1392                 loc = find_location_from_marker (marker, is_start);
1393                 if (loc) location_flags_changed (loc, this);
1394                 // fall through
1395         case MeterMarkerItem:
1396         case TempoMarkerItem:
1397                 
1398                 if (is_drawable()) {
1399                         time_canvas_scroller.get_window()->set_cursor (*timebar_cursor);
1400                 }
1401
1402                 break;
1403
1404         case FadeInHandleItem:
1405         case FadeOutHandleItem:
1406                 rv = static_cast<AudioRegionView*>(gtk_object_get_data (GTK_OBJECT(item), "regionview"));
1407                 gnome_canvas_item_set (item, "fill_color_rgba", rv->get_fill_color(), "outline_pixels", 0, NULL);
1408                 break;
1409
1410         case AutomationTrackItem:
1411                 if (is_drawable()) {
1412                         track_canvas_scroller.get_window()->set_cursor (*current_canvas_cursor);
1413                         clear_entered_track = true;
1414                         Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1415                 }
1416                 break;
1417                 
1418         default:
1419                 break;
1420         }
1421
1422         return FALSE;
1423 }
1424
1425 gint
1426 Editor::left_automation_track ()
1427 {
1428         if (clear_entered_track) {
1429                 set_entered_track (0);
1430                 clear_entered_track = false;
1431         }
1432         return FALSE;
1433 }
1434
1435 gint
1436 Editor::motion_handler (GnomeCanvasItem* item, GdkEvent* event, ItemType item_type)
1437 {
1438         gint x, y;
1439         
1440         /* We call this so that MOTION_NOTIFY events continue to be
1441            delivered to the canvas. We need to do this because we set
1442            Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1443            the density of the events, at the expense of a round-trip
1444            to the server. Given that this will mostly occur on cases
1445            where DISPLAY = :0.0, and given the cost of what the motion
1446            event might do, its a good tradeoff.  
1447         */
1448
1449         track_canvas.get_pointer (x, y);
1450
1451         if (current_stepping_trackview) {
1452                 /* don't keep the persistent stepped trackview if the mouse moves */
1453                 current_stepping_trackview = 0;
1454                 step_timeout.disconnect ();
1455         }
1456
1457         if (session && session->actively_recording()) {
1458                 /* Sorry. no dragging stuff around while we record */
1459                 return TRUE;
1460         }
1461
1462         drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1463                                                        &drag_info.current_pointer_y);
1464         if (drag_info.item) {
1465                 /* item != 0 is the best test i can think of for 
1466                    dragging.
1467                 */
1468                 if (!drag_info.move_threshold_passsed)
1469                 {
1470                         drag_info.move_threshold_passsed = (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
1471
1472                         // and change the initial grab loc/frame if this drag info wants us to
1473                         if (drag_info.want_move_threshold && drag_info.move_threshold_passsed) {
1474                                 drag_info.grab_frame = drag_info.current_pointer_frame;
1475                                 drag_info.grab_x = drag_info.current_pointer_x;
1476                                 drag_info.grab_y = drag_info.current_pointer_y;
1477                                 drag_info.last_pointer_frame = drag_info.grab_frame;
1478                                 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1479                         }
1480                 }
1481         }
1482
1483         switch (item_type) {
1484         case PlayheadCursorItem:
1485         case EditCursorItem:
1486         case MarkerItem:
1487         case GainControlPointItem:
1488         case RedirectAutomationControlPointItem:
1489         case GainAutomationControlPointItem:
1490         case PanAutomationControlPointItem:
1491         case TempoMarkerItem:
1492         case MeterMarkerItem:
1493         case AudioRegionViewNameHighlight:
1494         case StartSelectionTrimItem:
1495         case EndSelectionTrimItem:
1496         case SelectionItem:
1497         case GainLineItem:
1498         case RedirectAutomationLineItem:
1499         case GainAutomationLineItem:
1500         case PanAutomationLineItem:
1501         case FadeInHandleItem:
1502         case FadeOutHandleItem:
1503         /* <CMT Additions> */
1504         case ImageFrameHandleStartItem:
1505         case ImageFrameHandleEndItem:
1506         case MarkerViewHandleStartItem:
1507         case MarkerViewHandleEndItem:
1508         /* </CMT Additions> */
1509                 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1510                                        (event->motion.state & GDK_BUTTON2_MASK))) {
1511                         maybe_autoscroll (event);
1512                         (this->*(drag_info.motion_callback)) (item, event);
1513                         goto handled;
1514                 }
1515                 goto not_handled;
1516
1517         default:
1518                 break;
1519         }
1520
1521         switch (mouse_mode) {
1522         case MouseObject:
1523         case MouseRange:
1524         case MouseZoom:
1525         case MouseTimeFX:
1526                 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1527                                        (event->motion.state & GDK_BUTTON2_MASK))) {
1528                         maybe_autoscroll (event);
1529                         (this->*(drag_info.motion_callback)) (item, event);
1530                         goto handled;
1531                 }
1532                 goto not_handled;
1533                 break;
1534
1535         default:
1536                 break;
1537         }
1538
1539   handled:
1540         track_canvas_motion (event);
1541         return TRUE;
1542         
1543   not_handled:
1544         return FALSE;
1545 }
1546
1547 void
1548 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1549 {
1550         if (drag_info.item == 0) {
1551                 fatal << _("programming error: start_grab called without drag item") << endmsg;
1552                 /*NOTREACHED*/
1553                 return;
1554         }
1555
1556         if (cursor == 0) {
1557                 cursor = grabber_cursor;
1558         }
1559
1560         // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1561
1562         if (event->button.button == 2) {
1563                 drag_info.x_constrained = true;
1564         } else {
1565                 drag_info.x_constrained = false;
1566         }
1567
1568         drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y);
1569         drag_info.last_pointer_frame = drag_info.grab_frame;
1570         drag_info.current_pointer_frame = drag_info.grab_frame;
1571         drag_info.current_pointer_x = drag_info.grab_x;
1572         drag_info.current_pointer_y = drag_info.grab_y;
1573         drag_info.cumulative_x_drag = 0;
1574         drag_info.cumulative_y_drag = 0;
1575         drag_info.first_move = true;
1576         drag_info.move_threshold_passsed = false;
1577         drag_info.want_move_threshold = false;
1578         drag_info.pointer_frame_offset = 0;
1579         drag_info.brushing = false;
1580         drag_info.copied_location = 0;
1581
1582         gnome_canvas_item_grab (drag_info.item,
1583                                 Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1584                                 cursor->gobj(),
1585                                 event->button.time);
1586         //drag_info.item->grab (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                 marker_drag_line->set_property ("points", marker_drag_line_points);
1947         }
1948         else {
1949                 range_marker_drag_rect->set_property ("x1", x1);
1950                 range_marker_drag_rect->set_property ("x2", x2);
1951         }
1952 }
1953
1954 void
1955 Editor::start_marker_grab (GnomeCanvasItem* item, GdkEvent* event)
1956 {
1957         Marker* marker;
1958
1959         if ((marker = static_cast<Marker *> (gtk_object_get_data (GTK_OBJECT(item), "marker"))) == 0) {
1960                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1961                 /*NOTREACHED*/
1962         }
1963
1964         bool is_start;
1965
1966         Location  *location = find_location_from_marker (marker, is_start);
1967
1968         drag_info.item = item;
1969         drag_info.data = marker;
1970         drag_info.motion_callback = &Editor::marker_drag_motion_callback;
1971         drag_info.finished_callback = &Editor::marker_drag_finished_callback;
1972
1973         start_grab (event);
1974
1975         drag_info.copied_location = new Location (*location);
1976         drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());       
1977         
1978         update_marker_drag_item (location);
1979
1980         if (location->is_mark()) {
1981                 marker_drag_line->show();
1982                 marker_drag_line->raise_to_top();
1983         }
1984         else {
1985                 range_marker_drag_rect->show();
1986                 range_marker_drag_rect->raise_to_top();
1987         }
1988         
1989         if (is_start) show_verbose_time_cursor (location->start(), 10);
1990         else show_verbose_time_cursor (location->end(), 10);
1991 }
1992
1993 void
1994 Editor::marker_drag_motion_callback (GnomeCanvasItem* item, GdkEvent* event)
1995 {
1996         jack_nframes_t f_delta; 
1997         Marker* marker = (Marker *) drag_info.data;
1998         Location  *real_location;
1999         Location  *copy_location;
2000         bool is_start;
2001         bool move_both = false;
2002
2003         jack_nframes_t newframe;
2004         if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2005                 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2006         }
2007         else {
2008                 newframe = 0;
2009         }
2010                 
2011         jack_nframes_t next = newframe;
2012
2013         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2014                 snap_to (newframe, 0, true);
2015         }
2016         
2017         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
2018
2019         /* call this to find out if its the start or end */
2020         
2021         real_location = find_location_from_marker (marker, is_start);
2022
2023         /* use the copy that we're "dragging" around */
2024         
2025         copy_location = drag_info.copied_location;
2026
2027         f_delta = copy_location->end() - copy_location->start();
2028         
2029         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2030                 move_both = true;
2031         }
2032
2033         if (is_start) { // start marker
2034
2035                 if (move_both) {
2036                         copy_location->set_start (newframe);
2037                         copy_location->set_end (newframe + f_delta);
2038                 } else  if (newframe < copy_location->end()) {
2039                         copy_location->set_start (newframe);
2040                 } else { 
2041                         snap_to (next, 1, true);
2042                         copy_location->set_end (next);
2043                         copy_location->set_start (newframe);
2044                 }
2045
2046         } else { // end marker
2047
2048                 if (move_both) {
2049                         copy_location->set_end (newframe);
2050                         copy_location->set_start (newframe - f_delta);
2051                 } else if (newframe > copy_location->start()) {
2052                         copy_location->set_end (newframe);
2053                         
2054                 } else if (newframe > 0) {
2055                         snap_to (next, -1, true);
2056                         copy_location->set_start (next);
2057                         copy_location->set_end (newframe);
2058                 }
2059         }
2060
2061         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2062         drag_info.first_move = false;
2063
2064         update_marker_drag_item (copy_location);
2065
2066         LocationMarkers* lm = find_location_markers (real_location);
2067         lm->set_position (copy_location->start(), copy_location->end());
2068         
2069         show_verbose_time_cursor (newframe, 10);
2070 }
2071
2072 void
2073 Editor::marker_drag_finished_callback (GnomeCanvasItem* item, GdkEvent* event)
2074 {
2075         if (drag_info.first_move) {
2076                 marker_drag_motion_callback (item, event);
2077
2078         }
2079         
2080         Marker* marker = (Marker *) drag_info.data;
2081         bool is_start;
2082         Location * location = find_location_from_marker (marker, is_start);
2083         if (location) {
2084                 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2085         }
2086         
2087         marker_drag_line->hide();
2088         range_marker_drag_rect->hide();
2089 }
2090
2091 void
2092 Editor::start_meter_marker_grab (GnomeCanvasItem* item, GdkEvent* event)
2093 {
2094         Marker* marker;
2095         MeterMarker* meter_marker;
2096
2097         if ((marker = reinterpret_cast<Marker *> (gtk_object_get_data (GTK_OBJECT(item), "marker"))) == 0) {
2098                 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2099                 /*NOTREACHED*/
2100         }
2101
2102         meter_marker = dynamic_cast<MeterMarker*> (marker);
2103
2104         MetricSection& section (meter_marker->meter());
2105
2106         if (!section.movable()) {
2107                 return;
2108         }
2109
2110         drag_info.item = item;
2111         drag_info.data = marker;
2112         drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2113         drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2114
2115         start_grab (event);
2116
2117         drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();  
2118
2119         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2120 }
2121
2122 void
2123 Editor::meter_marker_drag_motion_callback (GnomeCanvasItem* item, GdkEvent* event)
2124 {
2125         MeterMarker* marker = (MeterMarker *) drag_info.data;
2126         jack_nframes_t adjusted_frame;
2127
2128         if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2129                 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2130         }
2131         else {
2132                 adjusted_frame = 0;
2133         }
2134         
2135         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2136                 snap_to (adjusted_frame);
2137         }
2138         
2139         if (adjusted_frame == drag_info.last_pointer_frame) return;
2140
2141         marker->set_position (adjusted_frame);
2142         
2143         
2144         drag_info.last_pointer_frame = adjusted_frame;
2145         drag_info.first_move = false;
2146
2147         show_verbose_time_cursor (adjusted_frame, 10);
2148 }
2149
2150 void
2151 Editor::meter_marker_drag_finished_callback (GnomeCanvasItem* item, GdkEvent* event)
2152 {
2153         if (drag_info.first_move) return;
2154
2155         meter_marker_drag_motion_callback (item, event);
2156         
2157         MeterMarker* marker = (MeterMarker *) drag_info.data;
2158         BBT_Time when;
2159         
2160         TempoMap& map (session->tempo_map());
2161         map.bbt_time (drag_info.last_pointer_frame, when);
2162         
2163         begin_reversible_command (_("move meter mark"));
2164         session->add_undo (map.get_memento());
2165         map.move_meter (marker->meter(), when);
2166         session->add_redo_no_execute (map.get_memento());
2167         commit_reversible_command ();
2168 }
2169
2170 void
2171 Editor::start_tempo_marker_grab (GnomeCanvasItem* item, GdkEvent* event)
2172 {
2173         Marker* marker;
2174         TempoMarker* tempo_marker;
2175
2176         if ((marker = reinterpret_cast<Marker *> (gtk_object_get_data (GTK_OBJECT(item), "tempo_marker"))) == 0) {
2177                 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2178                 /*NOTREACHED*/
2179         }
2180
2181         if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2182                 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2183                 /*NOTREACHED*/
2184         }
2185
2186         MetricSection& section (tempo_marker->tempo());
2187
2188         if (!section.movable()) {
2189                 return;
2190         }
2191
2192         drag_info.item = item;
2193         drag_info.data = marker;
2194         drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2195         drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2196
2197         start_grab (event);
2198
2199         drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();  
2200         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2201 }
2202
2203 void
2204 Editor::tempo_marker_drag_motion_callback (GnomeCanvasItem* item, GdkEvent* event)
2205 {
2206         TempoMarker* marker = (TempoMarker *) drag_info.data;
2207         jack_nframes_t adjusted_frame;
2208         
2209         if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2210                 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2211         }
2212         else {
2213                 adjusted_frame = 0;
2214         }
2215
2216         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2217                 snap_to (adjusted_frame);
2218         }
2219         
2220         if (adjusted_frame == drag_info.last_pointer_frame) return;
2221
2222         /* OK, we've moved far enough to make it worth actually move the thing. */
2223                 
2224         marker->set_position (adjusted_frame);
2225         
2226         show_verbose_time_cursor (adjusted_frame, 10);
2227
2228         drag_info.last_pointer_frame = adjusted_frame;
2229         drag_info.first_move = false;
2230 }
2231
2232 void
2233 Editor::tempo_marker_drag_finished_callback (GnomeCanvasItem* item, GdkEvent* event)
2234 {
2235         if (drag_info.first_move) return;
2236         
2237         tempo_marker_drag_motion_callback (item, event);
2238         
2239         TempoMarker* marker = (TempoMarker *) drag_info.data;
2240         BBT_Time when;
2241         
2242         TempoMap& map (session->tempo_map());
2243         map.bbt_time (drag_info.last_pointer_frame, when);
2244         
2245         begin_reversible_command (_("move tempo mark"));
2246         session->add_undo (map.get_memento());
2247         map.move_tempo (marker->tempo(), when);
2248         session->add_redo_no_execute (map.get_memento());
2249         commit_reversible_command ();
2250 }
2251
2252 void
2253 Editor::remove_gain_control_point (GnomeCanvasItem*item, GdkEvent* event)
2254 {
2255         ControlPoint* control_point;
2256
2257         if ((control_point = reinterpret_cast<ControlPoint *> (gtk_object_get_data (GTK_OBJECT(item), "control_point"))) == 0) {
2258                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2259                 /*NOTREACHED*/
2260         }
2261
2262         // We shouldn't remove the first or last gain point
2263         if (control_point->line.is_last_point(*control_point) ||
2264                 control_point->line.is_first_point(*control_point)) {   
2265                 return;
2266         }
2267
2268         control_point->line.remove_point (*control_point);
2269 }
2270
2271 void
2272 Editor::remove_control_point (GnomeCanvasItem*item, GdkEvent* event)
2273 {
2274         ControlPoint* control_point;
2275
2276         if ((control_point = reinterpret_cast<ControlPoint *> (gtk_object_get_data (GTK_OBJECT(item), "control_point"))) == 0) {
2277                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2278                 /*NOTREACHED*/
2279         }
2280
2281         control_point->line.remove_point (*control_point);
2282 }
2283
2284 void
2285 Editor::start_control_point_grab (GnomeCanvasItem* item, GdkEvent* event)
2286 {
2287         ControlPoint* control_point;
2288         
2289         if ((control_point = reinterpret_cast<ControlPoint *> (gtk_object_get_data (GTK_OBJECT(item), "control_point"))) == 0) {
2290                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2291                 /*NOTREACHED*/
2292         }
2293
2294         drag_info.item = item;
2295         drag_info.data = control_point;
2296         drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2297         drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2298
2299         start_grab (event, fader_cursor);
2300
2301         control_point->line.start_drag (control_point, 0);
2302
2303         float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2304         set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction), 
2305                                    drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2306
2307         show_verbose_canvas_cursor ();
2308 }
2309
2310 void
2311 Editor::control_point_drag_motion_callback (GnomeCanvasItem* item, GdkEvent* event)
2312 {
2313         ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2314
2315         double cx = drag_info.current_pointer_x;
2316         double cy = drag_info.current_pointer_y;
2317
2318         drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2319         drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2320
2321         bool x_constrained = false;
2322
2323         if (drag_info.x_constrained) {
2324                 if (fabs(drag_info.cumulative_x_drag) < fabs(drag_info.cumulative_y_drag)) {
2325                         cx = drag_info.grab_x;
2326                         x_constrained = true;
2327
2328                 } else {
2329                         cy = drag_info.grab_y;
2330                 }
2331         
2332         } 
2333
2334         gnome_canvas_item_w2i (cp->line.parent_group(), &cx, &cy);
2335
2336         cx = max (0.0, cx);
2337         cy = max (0.0, cy);
2338         cy = min ((double) cp->line.height(), cy);
2339
2340         //translate cx to frames
2341         jack_nframes_t cx_frames = (jack_nframes_t) floor (cx * frames_per_unit);
2342
2343         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !x_constrained) {
2344                 snap_to (cx_frames);
2345         }
2346
2347         float fraction = 1.0 - (cy / cp->line.height());
2348         
2349         bool push;
2350
2351         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2352                 push = true;
2353         } else {
2354                 push = false;
2355         }
2356
2357         cp->line.point_drag (*cp, cx_frames , fraction, push);
2358         
2359         set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2360 }
2361
2362 void
2363 Editor::control_point_drag_finished_callback (GnomeCanvasItem* item, GdkEvent* event)
2364 {
2365         ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2366         control_point_drag_motion_callback (item, event);
2367         cp->line.end_drag (cp);
2368 }
2369
2370 void
2371 Editor::start_line_grab_from_regionview (GnomeCanvasItem* item, GdkEvent* event)
2372 {
2373         switch (mouse_mode) {
2374         case MouseGain:
2375                 start_line_grab (clicked_regionview->get_gain_line(), event);
2376                 break;
2377         default:
2378                 break;
2379         }
2380 }
2381
2382 void
2383 Editor::start_line_grab_from_line (GnomeCanvasItem* item, GdkEvent* event)
2384 {
2385         AutomationLine* al;
2386         
2387         if ((al = reinterpret_cast<AutomationLine*> (gtk_object_get_data (GTK_OBJECT(item), "line"))) == 0) {
2388                 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2389                 /*NOTREACHED*/
2390         }
2391
2392         start_line_grab (al, event);
2393 }
2394
2395 void
2396 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2397 {
2398         double cx;
2399         double cy;
2400         jack_nframes_t frame_within_region;
2401
2402         /* need to get x coordinate in terms of parent (TimeAxisItemView)
2403            origin.
2404         */
2405
2406         cx = event->button.x;
2407         cy = event->button.y;
2408         gnome_canvas_item_w2i (line->parent_group(), &cx, &cy);
2409         frame_within_region = (jack_nframes_t) floor (cx * frames_per_unit);
2410
2411         if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before, 
2412                                             current_line_drag_info.after)) {
2413                 /* no adjacent points */
2414                 return;
2415         }
2416
2417         drag_info.item = line->grab_item();
2418         drag_info.data = line;
2419         drag_info.motion_callback = &Editor::line_drag_motion_callback;
2420         drag_info.finished_callback = &Editor::line_drag_finished_callback;
2421
2422         start_grab (event, fader_cursor);
2423
2424         double fraction = 1.0 - (cy / line->height());
2425
2426         line->start_drag (0, fraction);
2427         
2428         set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2429                                    drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2430         show_verbose_canvas_cursor ();
2431 }
2432
2433 void
2434 Editor::line_drag_motion_callback (GnomeCanvasItem* item, GdkEvent* event)
2435 {
2436         AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2437         double cx = drag_info.current_pointer_x;
2438         double cy = drag_info.current_pointer_y;
2439
2440         gnome_canvas_item_w2i (line->parent_group(), &cx, &cy);
2441         
2442         double fraction;
2443         fraction = 1.0 - (cy / line->height());
2444
2445         bool push;
2446
2447         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2448                 push = false;
2449         } else {
2450                 push = true;
2451         }
2452
2453         line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2454         
2455         set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2456 }
2457
2458 void
2459 Editor::line_drag_finished_callback (GnomeCanvasItem* item, GdkEvent* event)
2460 {
2461         AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2462         line_drag_motion_callback (item, event);
2463         line->end_drag (0);
2464 }
2465
2466 void
2467 Editor::start_region_grab (GnomeCanvasItem* item, GdkEvent* event)
2468 {
2469         if (selection->audio_regions.empty() || clicked_regionview == 0) {
2470                 return;
2471         }
2472
2473         drag_info.copy = false;
2474         drag_info.item = item;
2475         drag_info.data = clicked_regionview;
2476         drag_info.motion_callback = &Editor::region_drag_motion_callback;
2477         drag_info.finished_callback = &Editor::region_drag_finished_callback;
2478
2479         start_grab (event);
2480
2481         double speed = 1.0;
2482         TimeAxisView* tvp = clicked_trackview;
2483         AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2484
2485         if (tv && tv->is_audio_track()) {
2486                 speed = tv->get_diskstream()->speed();
2487         }
2488         
2489         drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2490         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2491         drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2492         // we want a move threshold
2493         drag_info.want_move_threshold = true;
2494         
2495         show_verbose_time_cursor (drag_info.last_frame_position, 10);
2496
2497         begin_reversible_command (_("move region(s)"));
2498 }
2499
2500 void
2501 Editor::start_region_copy_grab (GnomeCanvasItem* item, GdkEvent* event)
2502 {
2503         if (selection->audio_regions.empty() || clicked_regionview == 0) {
2504                 return;
2505         }
2506
2507         /* this is committed in the grab finished callback. */
2508
2509         begin_reversible_command (_("Drag region copy"));
2510                                 
2511         /* duplicate the region(s) */
2512
2513         vector<AudioRegionView*> new_regionviews;
2514
2515         for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
2516                 AudioRegionView* rv;
2517
2518                 rv = (*i);
2519
2520                 Playlist* to_playlist = rv->region.playlist();
2521                 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(&rv->get_time_axis_view());
2522                 
2523                 session->add_undo (to_playlist->get_memento ());
2524                 latest_regionview = 0;
2525
2526                 sigc::connection c = atv->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2527                 
2528                 /* create a new region with the same name.
2529                  */
2530
2531                 AudioRegion* newregion = new AudioRegion (rv->region);
2532
2533                 /* if the original region was locked, we don't care */
2534
2535                 newregion->set_locked (false);
2536
2537                 to_playlist->add_region (*newregion, (jack_nframes_t) (rv->region.position() * atv->get_diskstream()->speed()));
2538                 
2539                 c.disconnect ();
2540                 
2541                 if (latest_regionview) {
2542                         new_regionviews.push_back (latest_regionview);
2543                 }
2544                 
2545         }
2546
2547         if (new_regionviews.empty()) {
2548                 return;
2549         }
2550
2551         /* reset selection to new regionviews */
2552
2553         selection->set (new_regionviews);
2554
2555         drag_info.item = new_regionviews.front()->get_canvas_group ();
2556         drag_info.copy = true;
2557         drag_info.data = new_regionviews.front();
2558         drag_info.motion_callback = &Editor::region_drag_motion_callback;
2559         drag_info.finished_callback = &Editor::region_drag_finished_callback;
2560
2561         start_grab(event);
2562
2563         TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2564         AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
2565         double speed = 1.0;
2566
2567         if (atv && atv->is_audio_track()) {
2568                 speed = atv->get_diskstream()->speed();
2569         }
2570         
2571         drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2572         drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2573         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2574         // we want a move threshold
2575         drag_info.want_move_threshold = true;
2576
2577         show_verbose_time_cursor (drag_info.last_frame_position, 10);
2578
2579         begin_reversible_command (_("copy region(s)"));
2580 }
2581
2582 void
2583 Editor::start_region_brush_grab (GnomeCanvasItem* item, GdkEvent* event)
2584 {
2585         if (selection->audio_regions.empty() || clicked_regionview == 0) {
2586                 return;
2587         }
2588
2589         drag_info.copy = false;
2590         drag_info.item = item;
2591         drag_info.data = clicked_regionview;
2592         drag_info.motion_callback = &Editor::region_drag_motion_callback;
2593         drag_info.finished_callback = &Editor::region_drag_finished_callback;
2594
2595         start_grab (event);
2596
2597         double speed = 1.0;
2598         TimeAxisView* tvp = clicked_trackview;
2599         AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2600
2601         if (tv && tv->is_audio_track()) {
2602                 speed = tv->get_diskstream()->speed();
2603         }
2604         
2605         drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2606         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2607         drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2608         // we want a move threshold
2609         drag_info.want_move_threshold = true;
2610         drag_info.brushing = true;
2611         
2612         begin_reversible_command (_("Drag region brush"));
2613 }
2614
2615 void
2616 Editor::region_drag_motion_callback (GnomeCanvasItem* item, GdkEvent* event)
2617 {
2618         double x_delta;
2619         double y_delta = 0;
2620         AudioRegionView *rv = reinterpret_cast<AudioRegionView*> (drag_info.data); 
2621         jack_nframes_t pending_region_position = 0;
2622         int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2623         int32_t visible_y_high = 0, visible_y_low = 512;  //high meaning higher numbered.. not the height on the screen
2624         bool clamp_y_axis = false;
2625         vector<int32_t>  height_list(512) ;
2626         vector<int32_t>::iterator j;
2627
2628         /* Which trackview is this ? */
2629
2630         TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2631         AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2632
2633         /* The region motion is only processed if the pointer is over
2634            an audio track.
2635         */
2636         
2637         if (!tv || !tv->is_audio_track()) {
2638                 /* To make sure we hide the verbose canvas cursor when the mouse is 
2639                    not held over and audiotrack. 
2640                 */
2641                 hide_verbose_canvas_cursor ();
2642                 return;
2643         }
2644         
2645         original_pointer_order = drag_info.last_trackview->order;
2646                 
2647         /************************************************************
2648                  Y-Delta Computation
2649         ************************************************************/   
2650
2651         if (drag_info.brushing) {
2652                 clamp_y_axis = true;
2653                 pointer_y_span = 0;
2654                 goto y_axis_done;
2655         }
2656         
2657         if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2658
2659                 int32_t children = 0, numtracks = 0;
2660                 bitset <512> tracks (0x00);
2661                 /* get a bitmask representing the visible tracks */
2662
2663                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2664                         TimeAxisView *tracklist_timeview;
2665                         tracklist_timeview = (*i);
2666                         AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2667                         list<TimeAxisView*> children_list;
2668               
2669                         /* zeroes are audio tracks. ones are other types. */
2670               
2671                         if (!atv2->hidden()) {
2672                                 
2673                                 if (visible_y_high < atv2->order) {
2674                                         visible_y_high = atv2->order;
2675                                 }
2676                                 if (visible_y_low > atv2->order) {
2677                                         visible_y_low = atv2->order;
2678                                 }
2679                 
2680                                 if (!atv2->is_audio_track()) {                            
2681                                         tracks = tracks |= (0x01 << atv2->order);
2682                                 }
2683         
2684                                 height_list[atv2->order] = (*i)->height;
2685                                 children = 1;
2686                                 if ((children_list = atv2->get_child_list()).size() > 0) {
2687                                         for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) { 
2688                                                 tracks = tracks |= (0x01 << (atv2->order + children));
2689                                                 height_list[atv2->order + children] =  (*j)->height;                
2690                                                 numtracks++;
2691                                                 children++;     
2692                                         }
2693                                 }
2694                                 numtracks++;        
2695                         }
2696                 }
2697                 /* find the actual span according to the canvas */
2698
2699                 canvas_pointer_y_span = pointer_y_span;
2700                 if (drag_info.last_trackview->order >= tv->order) {
2701                         int32_t y;
2702                         for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2703                                 if (height_list[y] == 0 ) {
2704                                         canvas_pointer_y_span--;
2705                                 }
2706                         }
2707                 } else {
2708                         int32_t y;
2709                         for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2710                                 if (    height_list[y] == 0 ) {
2711                                         canvas_pointer_y_span++;
2712                                 }
2713                         }
2714                 }
2715
2716                 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
2717                         AudioRegionView* rv2;
2718                         rv2 = (*i);
2719                         double ix1, ix2, iy1, iy2;
2720                         int32_t n = 0;
2721
2722                         gnome_canvas_item_get_bounds (rv2->get_canvas_frame(), &ix1, &iy1, &ix2, &iy2);
2723                         gnome_canvas_item_i2w (rv2->get_canvas_group(), &ix1, &iy1);
2724                         TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2725                         AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
2726
2727                         if (atv2->order != original_pointer_order) {    
2728                                 /* this isn't the pointer track */      
2729
2730                                 if (canvas_pointer_y_span > 0) {
2731
2732                                         /* moving up the canvas */
2733                                         if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
2734         
2735                                                 int32_t visible_tracks = 0;
2736                                                 while (visible_tracks < canvas_pointer_y_span ) {
2737                                                         visible_tracks++;
2738                   
2739                                                         while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2740                                                                 /* we're passing through a hidden track */
2741                                                                 n--;
2742                                                         }                 
2743                                                 }
2744                  
2745                                                 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {                  
2746                                                         clamp_y_axis = true;
2747                                                 }
2748                     
2749                                         } else {
2750                                                 clamp_y_axis = true;
2751                                         }                 
2752                   
2753                                 } else if (canvas_pointer_y_span < 0) {
2754
2755                                         /*moving down the canvas*/
2756
2757                                         if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2758                     
2759                     
2760                                                 int32_t visible_tracks = 0;
2761                     
2762                                                 while (visible_tracks > canvas_pointer_y_span ) {
2763                                                         visible_tracks--;
2764                       
2765                                                         while (height_list[atv2->order - (visible_tracks - n)] == 0) {             
2766                                                                 n++;
2767                                                         }                
2768                                                 }
2769                                                 if (  tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2770                                                         clamp_y_axis = true;
2771                             
2772                                                 }
2773                                         } else {
2774                           
2775                                                 clamp_y_axis = true;
2776                                         }
2777                                 }               
2778                   
2779                         } else {
2780                       
2781                                 /* this is the pointer's track */
2782                                 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow 
2783                                         clamp_y_axis = true;
2784                                 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2785                                         clamp_y_axis = true;
2786                                 }
2787                         }             
2788                         if (clamp_y_axis) {
2789                                 break;
2790                         }
2791                 }
2792
2793         } else  if (drag_info.last_trackview == tv) {
2794                 clamp_y_axis = true;
2795         }         
2796
2797   y_axis_done:
2798         if (!clamp_y_axis) {
2799                 drag_info.last_trackview = tv;        
2800         }
2801           
2802         /************************************************************
2803                         X DELTA COMPUTATION
2804         ************************************************************/
2805
2806         /* compute the amount of pointer motion in frames, and where
2807            the region would be if we moved it by that much.
2808         */
2809
2810         if (drag_info.move_threshold_passsed) {
2811
2812                 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2813
2814                         jack_nframes_t sync_frame;
2815                         jack_nframes_t sync_offset;
2816                         int32_t sync_dir;
2817             
2818                         pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2819             
2820                         sync_offset = rv->region.sync_offset (sync_dir);
2821                         sync_frame = rv->region.adjust_to_sync (pending_region_position);
2822
2823                         /* we snap if the snap modifier is not enabled.
2824                          */
2825             
2826                         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2827                                 snap_to (sync_frame);   
2828                         }
2829             
2830                         if (sync_frame - sync_offset <= sync_frame) {
2831                                 pending_region_position = sync_frame - (sync_dir*sync_offset);
2832                         } else {
2833                                 pending_region_position = 0;
2834                         }
2835             
2836                 } else {
2837                         pending_region_position = 0;
2838                 }
2839           
2840                 if (pending_region_position > max_frames - rv->region.length()) {
2841                         pending_region_position = drag_info.last_frame_position;
2842                 }
2843           
2844                 // printf ("3: pending_region_position= %lu    %lu\n", pending_region_position, drag_info.last_frame_position );
2845           
2846                 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
2847
2848                         /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
2849                            to make it appear at the new location.
2850                         */
2851             
2852                         if (pending_region_position > drag_info.last_frame_position) {
2853                                 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
2854                         } else {
2855                                 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
2856                         }
2857             
2858                         drag_info.last_frame_position = pending_region_position;
2859             
2860                 } else {
2861                         x_delta = 0;
2862                 }
2863
2864         } else {
2865                 /* threshold not passed */
2866
2867                 x_delta = 0;
2868         }
2869
2870         /*************************************************************
2871                         PREPARE TO MOVE
2872         ************************************************************/
2873
2874         if (x_delta == 0 && (pointer_y_span == 0)) {
2875                 /* haven't reached next snap point, and we're not switching
2876                    trackviews. nothing to do.
2877                 */
2878                 return;
2879         } 
2880
2881         if (x_delta < 0) {
2882                 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
2883
2884                         AudioRegionView* rv2;
2885                         rv2 = (*i);
2886
2887                         /* if any regionview is at zero, we need to know so we can 
2888                            stop further leftward motion.
2889                         */
2890                         
2891                         double ix1, ix2, iy1, iy2;
2892                         gnome_canvas_item_get_bounds (rv2->get_canvas_frame(), &ix1, &iy1, &ix2, &iy2);
2893                         gnome_canvas_item_i2w (rv2->get_canvas_group(), &ix1, &iy1);
2894
2895                         if (ix1 <= 1) {
2896                                 x_delta = 0;
2897                                 break;
2898                         }
2899                 }
2900         }
2901
2902         /*************************************************************
2903                         MOTION                                                                
2904         ************************************************************/
2905
2906         pair<set<Playlist*>::iterator,bool> insert_result;
2907
2908         for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
2909             
2910                 AudioRegionView* rv;
2911                 rv = (*i);
2912                 double ix1, ix2, iy1, iy2;
2913                 int32_t temp_pointer_y_span = pointer_y_span;
2914
2915                 /* get item BBox, which will be relative to parent. so we have
2916                    to query on a child, then convert to world coordinates using
2917                    the parent.
2918                 */
2919
2920                 gnome_canvas_item_get_bounds (rv->get_canvas_frame(), &ix1, &iy1, &ix2, &iy2);
2921                 gnome_canvas_item_i2w (rv->get_canvas_group(), &ix1, &iy1);
2922                 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2923                 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
2924                 AudioTimeAxisView* temp_atv;
2925
2926                 if ((pointer_y_span != 0) && !clamp_y_axis) {
2927                         y_delta = 0;
2928                         int32_t x = 0;
2929                         for (j = height_list.begin(); j!= height_list.end(); j++) {     
2930                                 if (x == canvas_atv->order) {
2931                                         /* we found the track the region is on */
2932                                         if (x != original_pointer_order) {
2933                                                 /*this isn't from the same track we're dragging from */
2934                                                 temp_pointer_y_span = canvas_pointer_y_span;
2935                                         }                 
2936                                         while (temp_pointer_y_span > 0) {
2937                                                 /* we're moving up canvas-wise,
2938                                                    so  we need to find the next track height
2939                                                 */
2940                                                 if (j != height_list.begin()) {           
2941                                                         j--;
2942                                                 }
2943                                                 if (x != original_pointer_order) { 
2944                                                         /* we're not from the dragged track, so ignore hidden tracks. */              
2945                                                         if ((*j) == 0) {
2946                                                                 temp_pointer_y_span++;
2947                                                         }
2948                                                 }          
2949                                                 y_delta -= (*j);        
2950                                                 temp_pointer_y_span--;  
2951                                         }
2952                                         while (temp_pointer_y_span < 0) {                 
2953                                                 y_delta += (*j);
2954                                                 if (x != original_pointer_order) { 
2955                                                         if ((*j) == 0) {
2956                                                                 temp_pointer_y_span--;
2957                                                         }
2958                                                 }          
2959                     
2960                                                 if (j != height_list.end()) {                 
2961                                                         j++;
2962                                                 }
2963                                                 temp_pointer_y_span++;
2964                                         }
2965                                         /* find out where we'll be when we move and set height accordingly */
2966                   
2967                                         tvp2 = trackview_by_y_position (iy1 + y_delta);
2968                                         temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
2969                                         rv->set_height (temp_atv->height);
2970         
2971                                         /*   if you un-comment the following, the region colours will follow the track colours whilst dragging,
2972                                              personally, i think this can confuse things, but never mind.
2973                                         */
2974                                   
2975                                         //const GdkColor& col (temp_atv->view->get_region_color());
2976                                         //rv->set_color (const_cast<GdkColor&>(col));
2977                                         break;          
2978                                 }
2979                                 x++;
2980                         }
2981                 }
2982           
2983                 /* prevent the regionview from being moved to before 
2984                    the zero position on the canvas.
2985                 */
2986                 /* clamp */
2987                 
2988                 if (x_delta < 0) {
2989                         if (-x_delta > ix1) {
2990                                 x_delta = -ix1;
2991                         }
2992                 } else if ((x_delta > 0) &&(rv->region.last_frame() > max_frames - x_delta)) {
2993                         x_delta = max_frames - rv->region.last_frame();
2994                 }
2995                         
2996                 if (drag_info.first_move) {
2997
2998                         /* hide any dependent views */
2999
3000 //                      rv->get_time_axis_view().hide_dependent_views (*rv);
3001                                 
3002                         /* this is subtle. raising the regionview itself won't help,
3003                            because raise_to_top() just puts the item on the top of
3004                            its parent's stack. so, we need to put the trackview canvas_display group
3005                            on the top, since its parent is the whole canvas.
3006                         */
3007
3008                         gnome_canvas_item_raise_to_top (rv->get_canvas_group());
3009                         gnome_canvas_item_raise_to_top (rv->get_time_axis_view().canvas_display);
3010                         cursor_group->raise_to_top();
3011
3012                         /* freeze the playlists from notifying till
3013                            the motion is done.
3014                         */
3015
3016                         AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&rv->get_time_axis_view());
3017                         if (atv && atv->is_audio_track()) {
3018                                 AudioPlaylist* pl = atv->get_diskstream()->playlist();
3019                                 if (pl) {
3020                                         /* only freeze and capture state once */
3021
3022                                         insert_result = motion_frozen_playlists.insert (pl);
3023                                         if (insert_result.second) {
3024                                                 pl->freeze();
3025                                                 session->add_undo(pl->get_memento());
3026                                         }
3027                                 }
3028                         }
3029                 }
3030
3031                 if (drag_info.brushing) {
3032                         mouse_brush_insert_region (rv, pending_region_position);
3033                 } else {
3034                         rv->move (x_delta, y_delta);                    
3035                 }
3036         }
3037                 
3038         if (drag_info.first_move) {
3039                 cursor_group->raise_to_top();
3040         }
3041                 
3042         drag_info.first_move = false;
3043                 
3044         if (x_delta != 0 && !drag_info.brushing) {
3045                 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3046         }
3047                 
3048
3049
3050 void
3051 Editor::region_drag_finished_callback (GnomeCanvasItem* item, GdkEvent* event)
3052 {
3053         jack_nframes_t where;
3054         AudioRegionView* rv = reinterpret_cast<AudioRegionView *> (drag_info.data);
3055         pair<set<Playlist*>::iterator,bool> insert_result;
3056         bool nocommit = true;
3057         double speed;
3058         AudioTimeAxisView* atv;
3059         bool regionview_y_movement;
3060         bool regionview_x_movement;
3061
3062         /* first_move is set to false if the regionview has been moved in the 
3063            motion handler. 
3064         */
3065
3066         if (drag_info.first_move) {
3067                 /* just a click */
3068                 goto out;
3069         }
3070
3071         nocommit = false;
3072
3073         /* The regionview has been moved at some stage during the grab so we need
3074            to account for any mouse movement between this event and the last one. 
3075         */      
3076
3077         region_drag_motion_callback (item, event);
3078
3079         if (drag_info.brushing) {
3080                 /* all changes were made during motion event handlers */
3081                 goto out;
3082         }
3083
3084         /* adjust for track speed */
3085         speed = 1.0;
3086
3087         atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3088         if (atv && atv->get_diskstream()) {
3089                 speed = atv->get_diskstream()->speed();
3090         }
3091         
3092         regionview_x_movement = (drag_info.last_frame_position != (jack_nframes_t) (rv->region.position()/speed));
3093         regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3094
3095         //printf ("last_frame: %s position is %lu  %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed); 
3096         //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str()); 
3097         
3098         if (regionview_y_movement) {
3099
3100                 /* motion between tracks */
3101
3102                 list<AudioRegionView*> new_selection;
3103         
3104                 /* moved to a different audio track. */
3105
3106                 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ) {
3107             
3108                         AudioRegionView* rv2 = (*i);                
3109             
3110                         /* the region that used to be in the old playlist is not
3111                            moved to the new one - we make a copy of it. as a result,
3112                            any existing editor for the region should no longer be
3113                            visible.
3114                         */ 
3115             
3116                         if (!drag_info.copy) {
3117                                 rv2->hide_region_editor();
3118                         }           
3119                         new_selection.push_back (rv2);      
3120                         i++;
3121                 }
3122
3123                 /* first, freeze the target tracks */
3124
3125                 for (list<AudioRegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3126
3127                         Playlist* from_playlist;
3128                         Playlist* to_playlist;
3129                                 
3130                         double ix1, ix2, iy1, iy2;
3131             
3132                         gnome_canvas_item_get_bounds ((*i)->get_canvas_frame(), &ix1, &iy1, &ix2, &iy2);
3133                         gnome_canvas_item_i2w ((*i)->get_canvas_group(), &ix1, &iy1);
3134                         TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3135                         AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3136             
3137                         from_playlist = (*i)->region.playlist();
3138                         to_playlist = atv2->playlist();
3139
3140                         /* the from_playlist was frozen in the "first_move" case 
3141                            of the motion handler. the insert can fail, 
3142                            but that doesn't matter. it just means
3143                            we already have the playlist in the list.
3144                         */
3145                         
3146                         motion_frozen_playlists.insert (from_playlist);
3147
3148                         /* only freeze the to_playlist once */
3149
3150                         insert_result = motion_frozen_playlists.insert(to_playlist);
3151                         if (insert_result.second) {
3152                                 to_playlist->freeze();
3153                                 session->add_undo(to_playlist->get_memento());
3154                         }
3155
3156                 }
3157
3158                 /* now do it again with the actual operations */
3159
3160                 for (list<AudioRegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3161
3162                         Playlist* from_playlist;
3163                         Playlist* to_playlist;
3164                                 
3165                         double ix1, ix2, iy1, iy2;
3166             
3167                         gnome_canvas_item_get_bounds ((*i)->get_canvas_frame(), &ix1, &iy1, &ix2, &iy2);
3168                         gnome_canvas_item_i2w ((*i)->get_canvas_group(), &ix1, &iy1);
3169                         TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3170                         AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3171             
3172                         from_playlist = (*i)->region.playlist();
3173                         to_playlist = atv2->playlist();
3174
3175                         latest_regionview = 0;
3176             
3177                         where = (jack_nframes_t) (unit_to_frame (ix1) * speed);
3178                         Region* new_region = createRegion ((*i)->region);
3179
3180                         from_playlist->remove_region (&((*i)->region));
3181           
3182                         sigc::connection c = atv2->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3183                         to_playlist->add_region (*new_region, where);
3184                         c.disconnect ();
3185                         
3186                         if (latest_regionview) {
3187                                 selection->add (latest_regionview);
3188                         }
3189                 } 
3190
3191         } else {
3192
3193                 /* motion within a single track */
3194
3195                 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3196
3197                         rv = (*i);
3198
3199                         if (rv->region.locked()) {
3200                                 continue;
3201                         }
3202                         
3203                         if (regionview_x_movement) {
3204                                 double ownspeed = 1.0;
3205                                 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3206
3207                                 if (atv && atv->get_diskstream()) {
3208                                         ownspeed = atv->get_diskstream()->speed();
3209                                 }
3210                                 
3211                                 /* base the new region position on the current position of the regionview.*/
3212                                 
3213                                 double ix1, ix2, iy1, iy2;
3214                                 
3215                                 gnome_canvas_item_get_bounds (rv->get_canvas_frame(), &ix1, &iy1, &ix2, &iy2);
3216                                 gnome_canvas_item_i2w (rv->get_canvas_group(), &ix1, &iy1);
3217                                 where = (jack_nframes_t) (unit_to_frame (ix1) * ownspeed);
3218                                 
3219                         } else {
3220                                 
3221                                 where = rv->region.position();
3222                         }
3223
3224                         rv->get_time_axis_view().reveal_dependent_views (*rv);
3225                         
3226                         session->add_undo (rv->region.playlist()->get_memento());
3227                         rv->region.set_position (where, (void *) this);
3228                 }
3229         }
3230
3231   out:
3232         for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
3233                 (*p)->thaw ();
3234                 session->add_redo_no_execute ((*p)->get_memento());
3235         }
3236
3237         motion_frozen_playlists.clear ();
3238
3239         if (!nocommit) {
3240                 commit_reversible_command ();
3241         }
3242 }
3243
3244 void
3245 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3246 {
3247         /* Either add to or set the set the region selection, unless
3248            this is an alignment click (control used)
3249         */
3250         
3251         if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3252                 TimeAxisView* tv = &rv.get_time_axis_view();
3253                 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3254                 double speed = 1.0;
3255                 if (atv && atv->is_audio_track()) {
3256                         speed = atv->get_diskstream()->speed();
3257                 }
3258
3259                 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3260
3261                         align_region (rv.region, SyncPoint, (jack_nframes_t) (edit_cursor->current_frame * speed));
3262
3263                 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3264
3265                         align_region (rv.region, End, (jack_nframes_t) (edit_cursor->current_frame * speed));
3266
3267                 } else {
3268
3269                         align_region (rv.region, Start, (jack_nframes_t) (edit_cursor->current_frame * speed));
3270                 }
3271         }
3272 }
3273
3274 void
3275 Editor::show_verbose_time_cursor (jack_nframes_t frame, double offset, double xpos, double ypos) 
3276 {
3277         char buf[128];
3278         SMPTE_Time smpte;
3279         BBT_Time bbt;
3280         float secs;
3281
3282         if (session == 0) {
3283                 return;
3284         }
3285
3286         switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3287         case AudioClock::BBT:
3288                 session->bbt_time (frame, bbt);
3289                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3290                 break;
3291                 
3292         case AudioClock::SMPTE:
3293                 session->smpte_time (frame, smpte);
3294                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3295                 break;
3296
3297         case AudioClock::MinSec:
3298                 /* XXX fix this to compute min/sec properly */
3299                 session->smpte_time (frame, smpte);
3300                 secs = smpte.seconds + ((float) smpte.frames / session->smpte_frames_per_second);
3301                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", smpte.hours, smpte.minutes, secs);
3302                 break;
3303
3304         default:
3305                 snprintf (buf, sizeof(buf), "%u", frame);
3306                 break;
3307         }
3308
3309         if (xpos >= 0 && ypos >=0) {
3310                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3311         }
3312         else {
3313                 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3314         }
3315         show_verbose_canvas_cursor ();
3316 }
3317
3318 void
3319 Editor::show_verbose_duration_cursor (jack_nframes_t start, jack_nframes_t end, double offset, double xpos, double ypos) 
3320 {
3321         char buf[128];
3322         SMPTE_Time smpte;
3323         BBT_Time sbbt;
3324         BBT_Time ebbt;
3325         float secs;
3326         Meter meter_at_start(session->tempo_map().meter_at(start));
3327
3328         if (session == 0) {
3329                 return;
3330         }
3331
3332         switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3333         case AudioClock::BBT:
3334                 session->bbt_time (start, sbbt);
3335                 session->bbt_time (end, ebbt);
3336
3337                 /* subtract */
3338                 /* XXX this computation won't work well if the
3339                 user makes a selection that spans any meter changes.
3340                 */
3341
3342                 ebbt.bars -= sbbt.bars;
3343                 if (ebbt.beats >= sbbt.beats) {
3344                         ebbt.beats -= sbbt.beats;
3345                 } else {
3346                         ebbt.bars--;
3347                         ebbt.beats =  int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3348                 }
3349                 if (ebbt.ticks >= sbbt.ticks) {
3350                         ebbt.ticks -= sbbt.ticks;
3351                 } else {
3352                         ebbt.beats--;
3353                         ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3354                 }
3355                 
3356                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3357                 break;
3358                 
3359         case AudioClock::SMPTE:
3360                 session->smpte_duration (end - start, smpte);
3361                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3362                 break;
3363
3364         case AudioClock::MinSec:
3365                 /* XXX fix this to compute min/sec properly */
3366                 session->smpte_duration (end - start, smpte);
3367                 secs = smpte.seconds + ((float) smpte.frames / session->smpte_frames_per_second);
3368                 snprintf (buf, sizeof (buf), "%02ld:%02ld:%.4f", smpte.hours, smpte.minutes, secs);
3369                 break;
3370
3371         default:
3372                 snprintf (buf, sizeof(buf), "%u", end - start);
3373                 break;
3374         }
3375
3376         if (xpos >= 0 && ypos >=0) {
3377                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3378         }
3379         else {
3380                 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3381         }
3382         show_verbose_canvas_cursor ();
3383 }
3384
3385 void
3386 Editor::collect_new_region_view (AudioRegionView* rv)
3387 {
3388         latest_regionview = rv;
3389 }
3390
3391 void
3392 Editor::start_selection_grab (GnomeCanvasItem* item, GdkEvent* event)
3393 {
3394         if (clicked_regionview == 0) {
3395                 return;
3396         }
3397
3398         /* lets try to create new Region for the selection */
3399
3400         vector<AudioRegion*> new_regions;
3401         create_region_from_selection (new_regions);
3402
3403         if (new_regions.empty()) {
3404                 return;
3405         }
3406
3407         /* XXX fix me one day to use all new regions */
3408         
3409         Region* region = new_regions.front();
3410
3411         /* add it to the current stream/playlist.
3412
3413            tricky: the streamview for the track will add a new regionview. we will
3414            catch the signal it sends when it creates the regionview to
3415            set the regionview we want to then drag.
3416         */
3417         
3418         latest_regionview = 0;
3419         sigc::connection c = clicked_audio_trackview->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3420         
3421         /* A selection grab currently creates two undo/redo operations, one for 
3422            creating the new region and another for moving it.
3423         */
3424
3425         begin_reversible_command (_("selection grab"));
3426
3427         Playlist* playlist = clicked_trackview->playlist();
3428
3429         session->add_undo (playlist->get_memento ());
3430         clicked_trackview->playlist()->add_region (*region, selection->time[clicked_selection].start);
3431         session->add_redo_no_execute (playlist->get_memento ());
3432
3433         commit_reversible_command ();
3434         
3435         c.disconnect ();
3436         
3437         if (latest_regionview == 0) {
3438                 /* something went wrong */
3439                 return;
3440         }
3441
3442         /* we need to deselect all other regionviews, and select this one
3443            i'm ignoring undo stuff, because the region creation will take care of it */
3444         selection->set (latest_regionview);
3445         
3446         drag_info.item = latest_regionview->get_canvas_group();
3447         drag_info.data = latest_regionview;
3448         drag_info.motion_callback = &Editor::region_drag_motion_callback;
3449         drag_info.finished_callback = &Editor::region_drag_finished_callback;
3450
3451         start_grab (event);
3452         
3453         drag_info.last_trackview = clicked_trackview;
3454         drag_info.last_frame_position = latest_regionview->region.position();
3455         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3456         
3457         show_verbose_time_cursor (drag_info.last_frame_position, 10);
3458 }
3459
3460 void
3461 Editor::cancel_selection ()
3462 {
3463         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3464                 (*i)->hide_selection ();
3465         }
3466         selection->clear ();
3467         clicked_selection = 0;
3468 }       
3469
3470 void
3471 Editor::start_selection_op (GnomeCanvasItem* item, GdkEvent* event, SelectionOp op)
3472 {
3473         jack_nframes_t start = 0;
3474         jack_nframes_t end = 0;
3475
3476         if (session == 0) {
3477                 return;
3478         }
3479
3480         drag_info.item = item;
3481         drag_info.motion_callback = &Editor::drag_selection;
3482         drag_info.finished_callback = &Editor::end_selection_op;
3483
3484         selection_op = op;
3485
3486         switch (op) {
3487         case CreateSelection:
3488                 
3489                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3490                         drag_info.copy = true;
3491                 } else {
3492                         drag_info.copy = false;
3493                 }
3494                 start_grab (event, selector_cursor);
3495                 break;
3496
3497         case SelectionStartTrim:
3498                 clicked_trackview->order_selection_trims (item, true);
3499                 start_grab (event, trimmer_cursor);
3500                 start = selection->time[clicked_selection].start;
3501                 drag_info.pointer_frame_offset = drag_info.grab_frame - start;  
3502                 break;
3503                 
3504         case SelectionEndTrim:
3505                 clicked_trackview->order_selection_trims (item, false);
3506                 start_grab (event, trimmer_cursor);
3507                 end = selection->time[clicked_selection].end;
3508                 drag_info.pointer_frame_offset = drag_info.grab_frame - end;    
3509                 break;
3510
3511         case SelectionMove:
3512                 start = selection->time[clicked_selection].start;
3513                 start_grab (event);
3514                 drag_info.pointer_frame_offset = drag_info.grab_frame - start;  
3515                 break;
3516         }
3517
3518         if (selection_op == SelectionMove) {
3519                 show_verbose_time_cursor(start, 10);    
3520         } else {
3521                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
3522         }
3523 }
3524
3525 void
3526 Editor::drag_selection (GnomeCanvasItem* item, GdkEvent* event)
3527 {
3528         jack_nframes_t start = 0;
3529         jack_nframes_t end = 0;
3530         jack_nframes_t length;
3531         jack_nframes_t pending_position;
3532
3533         if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3534                 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3535         }
3536         else {
3537                 pending_position = 0;
3538         }
3539         
3540         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3541                 snap_to (pending_position);
3542         }
3543
3544         /* only alter selection if the current frame is 
3545            different from the last frame position (adjusted)
3546          */
3547         
3548         if (pending_position == drag_info.last_pointer_frame) return;
3549         
3550         switch (selection_op) {
3551         case CreateSelection:
3552                 
3553                 if (drag_info.first_move) {
3554                         snap_to (drag_info.grab_frame);
3555                 }
3556                 
3557                 if (pending_position < drag_info.grab_frame) {
3558                         start = pending_position;
3559                         end = drag_info.grab_frame;
3560                 } else {
3561                         end = pending_position;
3562                         start = drag_info.grab_frame;
3563                 }
3564                 
3565                 /* first drag: Either add to the selection
3566                    or create a new selection->
3567                 */
3568                 
3569                 if (drag_info.first_move) {
3570                         
3571                         begin_reversible_command (_("range selection"));
3572                         
3573                         if (drag_info.copy) {
3574                                 /* adding to the selection */
3575                                 clicked_selection = selection->add (start, end);
3576                                 drag_info.copy = false;
3577                         } else {
3578                                 /* new selection-> */
3579                                 clicked_selection = selection->set (clicked_trackview, start, end);
3580                         }
3581                 } 
3582                 break;
3583                 
3584         case SelectionStartTrim:
3585                 
3586                 if (drag_info.first_move) {
3587                         begin_reversible_command (_("trim selection start"));
3588                 }
3589                 
3590                 start = selection->time[clicked_selection].start;
3591                 end = selection->time[clicked_selection].end;
3592
3593                 if (pending_position > end) {
3594                         start = end;
3595                 } else {
3596                         start = pending_position;
3597                 }
3598                 break;
3599                 
3600         case SelectionEndTrim:
3601                 
3602                 if (drag_info.first_move) {
3603                         begin_reversible_command (_("trim selection end"));
3604                 }
3605                 
3606                 start = selection->time[clicked_selection].start;
3607                 end = selection->time[clicked_selection].end;
3608
3609                 if (pending_position < start) {
3610                         end = start;
3611                 } else {
3612                         end = pending_position;
3613                 }
3614                 
3615                 break;
3616                 
3617         case SelectionMove:
3618                 
3619                 if (drag_info.first_move) {
3620                         begin_reversible_command (_("move selection"));
3621                 }
3622                 
3623                 start = selection->time[clicked_selection].start;
3624                 end = selection->time[clicked_selection].end;
3625                 
3626                 length = end - start;
3627                 
3628                 start = pending_position;
3629                 snap_to (start);
3630                 
3631                 end = start + length;
3632                 
3633                 break;
3634         }
3635         
3636         
3637         if (event->button.x >= track_canvas_scroller.get_hadjustment()->get_value() + canvas_width) {
3638                 start_canvas_autoscroll (1);
3639         }
3640
3641         if (start != end) {
3642                 selection->replace (clicked_selection, start, end);
3643         }
3644
3645         drag_info.last_pointer_frame = pending_position;
3646         drag_info.first_move = false;
3647
3648         if (selection_op == SelectionMove) {
3649                 show_verbose_time_cursor(start, 10);    
3650         } else {
3651                 show_verbose_time_cursor(pending_position, 10); 
3652         }
3653 }
3654
3655 void
3656 Editor::end_selection_op (GnomeCanvasItem* item, GdkEvent* event)
3657 {
3658         if (!drag_info.first_move) {
3659                 drag_selection (item, event);
3660                 /* XXX this is not object-oriented programming at all. ick */
3661                 if (selection->time.consolidate()) {
3662                         selection->TimeChanged ();
3663                 }
3664                 commit_reversible_command ();
3665         } else {
3666                 /* just a click, no pointer movement.*/
3667
3668                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3669
3670                         selection->clear_time();
3671
3672                 } 
3673         }
3674
3675         /* XXX what happens if its a music selection? */
3676         session->set_audio_range (selection->time);
3677         stop_canvas_autoscroll ();
3678 }
3679
3680 void
3681 Editor::start_trim (GnomeCanvasItem* item, GdkEvent* event)
3682 {
3683         double speed = 1.0;
3684         TimeAxisView* tvp = clicked_trackview;
3685         AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3686
3687         if (tv && tv->is_audio_track()) {
3688                 speed = tv->get_diskstream()->speed();
3689         }
3690         
3691         jack_nframes_t region_start = (jack_nframes_t) (clicked_regionview->region.position() / speed);
3692         jack_nframes_t region_end = (jack_nframes_t) (clicked_regionview->region.last_frame() / speed);
3693         jack_nframes_t region_length = (jack_nframes_t) (clicked_regionview->region.length() / speed);
3694         
3695         //drag_info.item = clicked_regionview->get_name_highlight();
3696         drag_info.item = item;
3697         drag_info.motion_callback = &Editor::trim_motion_callback;
3698         drag_info.finished_callback = &Editor::trim_finished_callback;
3699
3700         start_grab (event, trimmer_cursor);
3701         
3702         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3703                 trim_op = ContentsTrim;
3704         } else {
3705                 /* These will get overridden for a point trim.*/
3706                 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3707                         /* closer to start */
3708                         trim_op = StartTrim;
3709                 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3710                         /* closer to end */
3711                         trim_op = EndTrim;
3712                 }
3713         }
3714
3715         switch (trim_op) {
3716         case StartTrim:
3717                 show_verbose_time_cursor(region_start, 10);     
3718                 break;
3719         case EndTrim:
3720                 show_verbose_time_cursor(region_end, 10);       
3721                 break;
3722         case ContentsTrim:
3723                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
3724                 break;
3725         }
3726         
3727         flush_track_canvas ();
3728 }
3729
3730 void
3731 Editor::trim_motion_callback (GnomeCanvasItem* item, GdkEvent* event)
3732 {
3733         AudioRegionView* rv = clicked_regionview;
3734         jack_nframes_t frame_delta = 0;
3735         bool left_direction;
3736         bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3737
3738         /* snap modifier works differently here..
3739            its' current state has to be passed to the 
3740            various trim functions in order to work properly 
3741         */ 
3742
3743         double speed = 1.0;
3744         TimeAxisView* tvp = clicked_trackview;
3745         AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3746
3747         if (tv && tv->is_audio_track()) {
3748                 speed = tv->get_diskstream()->speed();
3749         }
3750         
3751         if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
3752                 left_direction = true;
3753         } else {
3754                 left_direction = false;
3755         }
3756
3757         if (obey_snap) {
3758                 snap_to (drag_info.current_pointer_frame);
3759         }
3760
3761         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
3762                 return;
3763         }
3764
3765         if (drag_info.first_move) {
3766         
3767                 string trim_type;
3768
3769                 switch (trim_op) {
3770                 case StartTrim:
3771                         trim_type = "Region start trim";
3772                         break;
3773                 case EndTrim:
3774                         trim_type = "Region end trim";
3775                         break;
3776                 case ContentsTrim:
3777                         trim_type = "Region content trim";
3778                         break;
3779                 }
3780
3781                 begin_reversible_command (trim_type);
3782
3783                 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3784                         (*i)->region.freeze ();
3785                         (*i)->temporarily_hide_envelope ();
3786                         session->add_undo ((*i)->region.playlist()->get_memento());
3787                 }
3788         }
3789
3790         if (left_direction) {
3791                 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
3792         } else {
3793                 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
3794         }
3795
3796         switch (trim_op) {              
3797         case StartTrim:
3798                 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region.first_frame()/speed)) {
3799                         break;
3800                 } else {
3801                         for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3802                                 single_start_trim (**i, frame_delta, left_direction, obey_snap);
3803                         }
3804                         break;
3805                 }
3806                 
3807         case EndTrim:
3808                 if ((left_direction == true) && (drag_info.current_pointer_frame > (jack_nframes_t) (rv->region.last_frame()/speed))) {
3809                         break;
3810                 } else {
3811                         for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3812                                 single_end_trim (**i, frame_delta, left_direction, obey_snap);
3813                         }
3814                         break;
3815                 }
3816                 
3817         case ContentsTrim:
3818                 {
3819                         bool swap_direction = false;
3820
3821                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3822                                 swap_direction = true;
3823                         }
3824                         
3825                         for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
3826                              i != selection->audio_regions.by_layer().end(); ++i)
3827                         {
3828                                 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
3829                         }
3830                 }
3831                 break;
3832         }
3833
3834         switch (trim_op) {
3835         case StartTrim:
3836                 show_verbose_time_cursor((jack_nframes_t) (rv->region.position()/speed), 10);   
3837                 break;
3838         case EndTrim:
3839                 show_verbose_time_cursor((jack_nframes_t) (rv->region.last_frame()/speed), 10); 
3840                 break;
3841         case ContentsTrim:
3842                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
3843                 break;
3844         }
3845
3846         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
3847         drag_info.first_move = false;
3848 }
3849
3850 void
3851 Editor::single_contents_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
3852 {
3853         Region& region (rv.region);
3854
3855         if (region.locked()) {
3856                 return;
3857         }
3858
3859         jack_nframes_t new_bound;
3860
3861         double speed = 1.0;
3862         TimeAxisView* tvp = clicked_trackview;
3863         AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3864
3865         if (tv && tv->is_audio_track()) {
3866                 speed = tv->get_diskstream()->speed();
3867         }
3868         
3869         if (left_direction) {
3870                 if (swap_direction) {
3871                         new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
3872                 } else {
3873                         new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
3874                 }
3875         } else {
3876                 if (swap_direction) {
3877                         new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
3878                 } else {
3879                         new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
3880                 }
3881         }
3882
3883         if (obey_snap) {
3884                 snap_to (new_bound);
3885         }
3886         region.trim_start ((jack_nframes_t) (new_bound * speed), this); 
3887         rv.region_changed (StartChanged);
3888 }
3889
3890 void
3891 Editor::single_start_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool obey_snap)
3892 {
3893         Region& region (rv.region);     
3894
3895         if (region.locked()) {
3896                 return;
3897         }
3898
3899         jack_nframes_t new_bound;
3900
3901         double speed = 1.0;
3902         TimeAxisView* tvp = clicked_trackview;
3903         AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3904
3905         if (tv && tv->is_audio_track()) {
3906                 speed = tv->get_diskstream()->speed();
3907         }
3908         
3909         if (left_direction) {
3910                 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
3911         } else {
3912                 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
3913         }
3914
3915         if (obey_snap) {
3916                 snap_to (new_bound, (left_direction ? 0 : 1));  
3917         }
3918
3919         region.trim_front ((jack_nframes_t) (new_bound * speed), this);
3920
3921         rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
3922 }
3923
3924 void
3925 Editor::single_end_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool obey_snap)
3926 {
3927         Region& region (rv.region);
3928
3929         if (region.locked()) {
3930                 return;
3931         }
3932
3933         jack_nframes_t new_bound;
3934
3935         double speed = 1.0;
3936         TimeAxisView* tvp = clicked_trackview;
3937         AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3938
3939         if (tv && tv->is_audio_track()) {
3940                 speed = tv->get_diskstream()->speed();
3941         }
3942         
3943         if (left_direction) {
3944                 new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) - frame_delta;
3945         } else {
3946                 new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) + frame_delta;
3947         }
3948
3949         if (obey_snap) {
3950                 snap_to (new_bound);
3951         }
3952         region.trim_end ((jack_nframes_t) (new_bound * speed), this);
3953         rv.region_changed (LengthChanged);
3954 }
3955         
3956 void
3957 Editor::trim_finished_callback (GnomeCanvasItem* item, GdkEvent* event)
3958 {
3959         if (!drag_info.first_move) {
3960                 trim_motion_callback (item, event);
3961                 
3962                 if (!clicked_regionview->get_selected()) {
3963                         thaw_region_after_trim (*clicked_regionview);           
3964                 } else {
3965                         
3966                         for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
3967                              i != selection->audio_regions.by_layer().end(); ++i)
3968                         {
3969                                 thaw_region_after_trim (**i);
3970                         }
3971                 }
3972                 commit_reversible_command();
3973         } else {
3974                 /* no mouse movement */
3975                 point_trim (event);
3976         }
3977         
3978         flush_track_canvas ();
3979 }
3980
3981 void
3982 Editor::point_trim (GdkEvent* event)
3983 {
3984         AudioRegionView* rv = clicked_regionview;
3985         jack_nframes_t new_bound = drag_info.current_pointer_frame;
3986
3987         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3988                 snap_to (new_bound);
3989         }
3990
3991         /* Choose action dependant on which button was pressed */
3992         switch (event->button.button) {
3993         case 1:
3994                 trim_op = StartTrim;
3995                 begin_reversible_command (_("Start point trim"));
3996
3997                 if (rv->get_selected()) {
3998
3999                         for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
4000                              i != selection->audio_regions.by_layer().end(); ++i)
4001                         {
4002                                 if (!(*i)->region.locked()) {
4003                                         session->add_undo ((*i)->region.playlist()->get_memento());
4004                                         (*i)->region.trim_front (new_bound, this);      
4005                                         session->add_redo_no_execute ((*i)->region.playlist()->get_memento());
4006                                 }
4007                         }
4008
4009                 } else {
4010
4011                         if (!rv->region.locked()) {
4012                                 session->add_undo (rv->region.playlist()->get_memento());
4013                                 rv->region.trim_front (new_bound, this);        
4014                                 session->add_redo_no_execute (rv->region.playlist()->get_memento());
4015                         }
4016                 }
4017
4018                 commit_reversible_command();
4019         
4020                 break;
4021         case 2:
4022                 trim_op = EndTrim;
4023                 begin_reversible_command (_("End point trim"));
4024
4025                 if (rv->get_selected()) {
4026                         
4027                         for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i)
4028                         {
4029                                 if (!(*i)->region.locked()) {
4030                                         session->add_undo ((*i)->region.playlist()->get_memento());
4031                                         (*i)->region.trim_end (new_bound, this);
4032                                         session->add_redo_no_execute ((*i)->region.playlist()->get_memento());  
4033                                 }
4034                         }
4035
4036                 } else {
4037
4038                         if (!rv->region.locked()) {
4039                                 session->add_undo (rv->region.playlist()->get_memento());
4040                                 rv->region.trim_end (new_bound, this);
4041                                 session->add_redo_no_execute (rv->region.playlist()->get_memento());    
4042                         }
4043                 }
4044
4045                 commit_reversible_command();
4046         
4047                 break;
4048         default:
4049                 break;
4050         }
4051 }
4052
4053 void
4054 Editor::thaw_region_after_trim (AudioRegionView& rv)
4055 {
4056         Region& region (rv.region);
4057
4058         if (region.locked()) {
4059                 return;
4060         }
4061
4062         region.thaw (_("trimmed region"));
4063         session->add_redo_no_execute (region.playlist()->get_memento());
4064
4065         rv.unhide_envelope ();
4066 }
4067
4068 void
4069 Editor::hide_marker (GnomeCanvasItem* item, GdkEvent* event)
4070 {
4071         Marker* marker;
4072         bool is_start;
4073
4074         if ((marker = static_cast<Marker *> (gtk_object_get_data (GTK_OBJECT(item), "marker"))) == 0) {
4075                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4076                 /*NOTREACHED*/
4077         }
4078
4079         Location* location = find_location_from_marker (marker, is_start);      
4080         location->set_hidden (true, this);
4081 }
4082
4083
4084 void
4085 Editor::start_range_markerbar_op (GnomeCanvasItem* item, GdkEvent* event, RangeMarkerOp op)
4086 {
4087
4088         if (session == 0) {
4089                 return;
4090         }
4091
4092         drag_info.item = item;
4093         drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4094         drag_info.finished_callback = &Editor::end_range_markerbar_op;
4095
4096         range_marker_op = op;
4097
4098         if (!temp_location) {
4099                 temp_location = new Location;
4100         }
4101         
4102         switch (op) {
4103         case CreateRangeMarker:
4104         case CreateTransportMarker:
4105                 
4106                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4107                         drag_info.copy = true;
4108                 } else {
4109                         drag_info.copy = false;
4110                 }
4111                 start_grab (event, selector_cursor);
4112                 break;
4113         }
4114
4115         show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4116         
4117 }
4118
4119 void
4120 Editor::drag_range_markerbar_op (GnomeCanvasItem* item, GdkEvent* event)
4121 {
4122         jack_nframes_t start = 0;
4123         jack_nframes_t end = 0;
4124         
4125         GnomeCanvasItem * crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4126         
4127         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4128                 snap_to (drag_info.current_pointer_frame);
4129         }
4130
4131         /* only alter selection if the current frame is 
4132            different from the last frame position.
4133          */
4134         
4135         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4136         
4137         switch (range_marker_op) {
4138         case CreateRangeMarker:
4139         case CreateTransportMarker:
4140                 if (drag_info.first_move) {
4141                         snap_to (drag_info.grab_frame);
4142                 }
4143                 
4144                 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4145                         start = drag_info.current_pointer_frame;
4146                         end = drag_info.grab_frame;
4147                 } else {
4148                         end = drag_info.current_pointer_frame;
4149                         start = drag_info.grab_frame;
4150                 }
4151                 
4152                 /* first drag: Either add to the selection
4153                    or create a new selection->
4154                 */
4155                 
4156                 if (drag_info.first_move) {
4157                         
4158                         temp_location->set (start, end);
4159                         
4160                         gnome_canvas_item_show (crect);
4161
4162                         update_marker_drag_item (temp_location);
4163                         range_marker_drag_rect->show();
4164                         range_marker_drag_rect->raise_to_top();
4165                         
4166                 } 
4167                 break;          
4168         }
4169         
4170         
4171         if (event->button.x >= track_canvas_scroller.get_hadjustment()->get_value() + canvas_width) {
4172                 start_canvas_autoscroll (1);
4173         }
4174         
4175         if (start != end) {
4176                 temp_location->set (start, end);
4177
4178                 double x1 = frame_to_pixel (start);
4179                 double x2 = frame_to_pixel (end);
4180                 gnome_canvas_item_set (crect, "x1", x1, "x2", x2, NULL);
4181
4182                 update_marker_drag_item (temp_location);
4183         }
4184
4185         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4186         drag_info.first_move = false;
4187
4188         show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4189         
4190 }
4191
4192 void
4193 Editor::end_range_markerbar_op (GnomeCanvasItem* item, GdkEvent* event)
4194 {
4195         Location * newloc = 0;
4196         
4197         if (!drag_info.first_move) {
4198                 drag_range_markerbar_op (item, event);
4199
4200                 switch (range_marker_op)
4201                 {
4202                 case CreateRangeMarker:
4203                         begin_reversible_command (_("new range marker"));
4204                         session->add_undo (session->locations()->get_memento());
4205                         newloc = new Location(temp_location->start(), temp_location->end(), "unnamed");
4206                         session->locations()->add (newloc, true);
4207                         session->add_redo_no_execute (session->locations()->get_memento());
4208                         commit_reversible_command ();
4209                         
4210                         range_bar_drag_rect->hide();
4211                         range_marker_drag_rect->hide();
4212                         break;
4213
4214                 case CreateTransportMarker:
4215                         // popup menu to pick loop or punch
4216                         new_transport_marker_context_menu (&event->button, item);
4217                         
4218                         break;
4219                 }
4220         } else {
4221                 /* just a click, no pointer movement.*/
4222
4223                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4224
4225                         // nothing yet
4226
4227                 } 
4228         }
4229
4230         stop_canvas_autoscroll ();
4231 }
4232
4233
4234
4235 void
4236 Editor::start_mouse_zoom (GnomeCanvasItem* item, GdkEvent* event)
4237 {
4238         drag_info.item = item;
4239         drag_info.motion_callback = &Editor::drag_mouse_zoom;
4240         drag_info.finished_callback = &Editor::end_mouse_zoom;
4241
4242         start_grab (event, zoom_cursor);
4243
4244         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4245 }
4246
4247 void
4248 Editor::drag_mouse_zoom (GnomeCanvasItem* item, GdkEvent* event)
4249 {
4250         jack_nframes_t start;
4251         jack_nframes_t end;
4252
4253         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4254                 snap_to (drag_info.current_pointer_frame);
4255                 
4256                 if (drag_info.first_move) {
4257                         snap_to (drag_info.grab_frame);
4258                 }
4259         }
4260                 
4261         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4262
4263         /* base start and end on initial click position */
4264         if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4265                 start = drag_info.current_pointer_frame;
4266                 end = drag_info.grab_frame;
4267         } else {
4268                 end = drag_info.current_pointer_frame;
4269                 start = drag_info.grab_frame;
4270         }
4271         
4272         if (start != end) {
4273
4274                 if (drag_info.first_move) {
4275                         zoom_rect->show();
4276                         zoom_rect->raise_to_top();
4277                 }
4278
4279                 reposition_zoom_rect(start, end);
4280
4281                 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4282                 drag_info.first_move = false;
4283
4284                 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4285         }
4286 }
4287
4288 void
4289 Editor::end_mouse_zoom (GnomeCanvasItem* item, GdkEvent* event)
4290 {
4291         if (!drag_info.first_move) {
4292                 drag_mouse_zoom (item, event);
4293                 
4294                 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4295                         temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4296                 } else {
4297                         temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4298                 }               
4299         } else {
4300                 temporal_zoom_to_frame (false, drag_info.grab_frame);
4301                 /*
4302                 temporal_zoom_step (false);
4303                 center_screen (drag_info.grab_frame);
4304                 */
4305         }
4306
4307         zoom_rect->hide();
4308 }
4309
4310 void
4311 Editor::reposition_zoom_rect (jack_nframes_t start, jack_nframes_t end)
4312 {
4313         double x1 = frame_to_pixel (start);
4314         double x2 = frame_to_pixel (end);
4315         double y2 = canvas_height - 2;
4316
4317         gtk_object_set (GTK_OBJECT(zoom_rect), 
4318                         "x1", x1,
4319                         "y1", 1.0,
4320                         "x2", x2,
4321                         "y2", y2,
4322                         NULL);
4323 }
4324
4325 void
4326 Editor::start_rubberband_select (GnomeCanvasItem* item, GdkEvent* event)
4327 {
4328         drag_info.item = item;
4329         drag_info.motion_callback = &Editor::drag_rubberband_select;
4330         drag_info.finished_callback = &Editor::end_rubberband_select;
4331
4332         start_grab (event, cross_hair_cursor);
4333
4334         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4335 }
4336
4337 void
4338 Editor::drag_rubberband_select (GnomeCanvasItem* item, GdkEvent* event)
4339 {
4340         jack_nframes_t start;
4341         jack_nframes_t end;
4342         double y1;
4343         double y2;
4344
4345         /* use a bigger drag threshold than the default */
4346
4347         if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4348                 return;
4349         }
4350
4351 //      if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4352 //              snap_to (drag_info.current_pointer_frame);
4353                 
4354 //              if (drag_info.first_move) {
4355 //                      snap_to (drag_info.grab_frame);
4356 //              }
4357 //      }
4358                 
4359
4360         /* base start and end on initial click position */
4361         if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4362                 start = drag_info.current_pointer_frame;
4363                 end = drag_info.grab_frame;
4364         } else {
4365                 end = drag_info.current_pointer_frame;
4366                 start = drag_info.grab_frame;
4367         }
4368
4369         if (drag_info.current_pointer_y < drag_info.grab_y) {
4370                 y1 = drag_info.current_pointer_y;
4371                 y2 = drag_info.grab_y;
4372         }
4373         else {
4374                 y2 = drag_info.current_pointer_y;
4375                 y1 = drag_info.grab_y;
4376         }
4377
4378         
4379         if (start != end || y1 != y2) {
4380
4381                 double x1 = frame_to_pixel (start);
4382                 double x2 = frame_to_pixel (end);
4383                 
4384                 gtk_object_set (GTK_OBJECT(rubberband_rect), 
4385                                 "x1", x1,
4386                                 "y1", y1,
4387                                 "x2", x2,
4388                                 "y2", y2,
4389                                 NULL);
4390
4391                 rubberband_rect->show();
4392                 rubberband_rect->raise_to_top();
4393                 
4394                 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4395                 drag_info.first_move = false;
4396
4397                 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4398         }
4399 }
4400
4401 void
4402 Editor::end_rubberband_select (GnomeCanvasItem* item, GdkEvent* event)
4403 {
4404         if (!drag_info.first_move) {
4405
4406                 drag_rubberband_select (item, event);
4407
4408                 double y1,y2;
4409                 if (drag_info.current_pointer_y < drag_info.grab_y) {
4410                         y1 = drag_info.current_pointer_y;
4411                         y2 = drag_info.grab_y;
4412                 }
4413                 else {
4414                         y2 = drag_info.current_pointer_y;
4415                         y1 = drag_info.grab_y;
4416                 }
4417
4418
4419                 bool add = Keyboard::modifier_state_contains (event->button.state, Keyboard::Shift);
4420                 bool commit;
4421
4422                 begin_reversible_command (_("select regions"));
4423
4424                 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4425                         commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, add);
4426                 } else {
4427                         commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, add);
4428                 }               
4429
4430                 if (commit) {
4431                         commit_reversible_command ();
4432                 }
4433                 
4434         } else {
4435                 selection->clear_audio_regions();
4436                 selection->clear_points ();
4437                 selection->clear_lines ();
4438         }
4439
4440         rubberband_rect->hide();
4441 }
4442
4443
4444 gint
4445 Editor::mouse_rename_region (GnomeCanvasItem* item, GdkEvent* event)
4446 {
4447         using namespace Gtkmm2ext;
4448
4449         ArdourPrompter prompter (false);
4450
4451         prompter.set_prompt (_("Name for region:"));
4452         prompter.set_initial_text (clicked_regionview->region.name());
4453         prompter.show_all ();
4454         prompter.chosen.connect(sigc::ptr_fun(Gtk::Main::quit));
4455         switch (prompter.run ()) {
4456         case GTK_RESPONSE_ACCEPT:
4457                 string str;
4458                 prompter.get_result(str);
4459                 if (str.length()) {
4460         
4461                         clicked_regionview->region.set_name (str);
4462                 }
4463                 break;
4464         }
4465         return TRUE;
4466 }
4467
4468 void
4469 Editor::start_time_fx (GnomeCanvasItem* item, GdkEvent* event)
4470 {
4471         drag_info.item = item;
4472         drag_info.motion_callback = &Editor::time_fx_motion;
4473         drag_info.finished_callback = &Editor::end_time_fx;
4474
4475         start_grab (event);
4476
4477         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4478 }
4479
4480 void
4481 Editor::time_fx_motion (GnomeCanvasItem *item, GdkEvent* event)
4482 {
4483         AudioRegionView* rv = clicked_regionview;
4484
4485         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4486                 snap_to (drag_info.current_pointer_frame);
4487         }
4488
4489         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4490                 return;
4491         }
4492
4493         if (drag_info.current_pointer_frame > rv->region.position()) {
4494                 rv->get_time_axis_view().show_timestretch (rv->region.position(), drag_info.current_pointer_frame);
4495         }
4496
4497         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4498         drag_info.first_move = false;
4499
4500         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4501 }
4502
4503 void
4504 Editor::end_time_fx (GnomeCanvasItem* item, GdkEvent* event)
4505 {
4506         clicked_regionview->get_time_axis_view().hide_timestretch ();
4507
4508         if (drag_info.first_move) {
4509                 return;
4510         }
4511         
4512         jack_nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region.position();
4513         float percentage = (float) ((double) newlen - (double) clicked_regionview->region.length()) / ((double) newlen) * 100.0f;
4514         
4515         begin_reversible_command (_("timestretch"));
4516
4517         if (run_timestretch (selection->audio_regions, percentage) == 0) {
4518                 session->commit_reversible_command ();
4519         }
4520 }
4521
4522 void
4523 Editor::mouse_brush_insert_region (AudioRegionView* rv, jack_nframes_t pos)
4524 {
4525         /* no brushing without a useful snap setting */
4526
4527         switch (snap_mode) {
4528         case SnapMagnetic:
4529                 return; /* can't work because it allows region to be placed anywhere */
4530         default:
4531                 break; /* OK */
4532         }
4533
4534         switch (snap_type) {
4535         case SnapToFrame:
4536         case SnapToMark:
4537         case SnapToEditCursor:
4538                 return;
4539
4540         default:
4541                 break;
4542         }
4543
4544         /* don't brush a copy over the original */
4545         
4546         if (pos == rv->region.position()) {
4547                 return;
4548         }
4549
4550         AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(&rv->get_time_axis_view());
4551
4552         if (atv == 0 || !atv->is_audio_track()) {
4553                 return;
4554         }
4555
4556         Playlist* playlist = atv->playlist();
4557         double speed = atv->get_diskstream()->speed();
4558         
4559         session->add_undo (playlist->get_memento());
4560         playlist->add_region (*(new AudioRegion (rv->region)), (jack_nframes_t) (pos * speed));
4561         session->add_redo_no_execute (playlist->get_memento());
4562         
4563         // playlist is frozen, so we have to update manually
4564         
4565         playlist->StateChanged (Change (~0)); /* EMIT SIGNAL */
4566 }
4567
4568 gint
4569 Editor::track_height_step_timeout ()
4570 {
4571         struct timeval now;
4572         struct timeval delta;
4573         
4574         gettimeofday (&now, 0);
4575         timersub (&now, &last_track_height_step_timestamp, &delta);
4576         
4577         if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4578                 current_stepping_trackview = 0;
4579                 return FALSE;
4580         }
4581         return TRUE;
4582 }