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