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