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