Fix displaying of notes in auto-created MIDI region when it's the first region in...
[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.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1504                                                        &drag_info.current_pointer_y);
1505
1506         switch (mouse_mode) {
1507         case MouseAudition:
1508                 if (_scrubbing) {
1509                         struct timeval tmnow;
1510
1511                         if (last_scrub_frame == 0) {
1512
1513                                 /* first motion, just set up the variables */
1514
1515                                 last_scrub_frame = (nframes64_t) drag_info.current_pointer_frame;
1516                                 gettimeofday (&tmnow, 0);
1517                                 last_scrub_time = tmnow.tv_sec * 1000000.0 + tmnow.tv_usec;
1518                                 session->request_locate (last_scrub_frame, true);
1519
1520                         } else {
1521                                 /* how fast is the mouse moving ? */
1522
1523                                 double speed;
1524                                 nframes_t distance;
1525                                 double time;
1526                                 double dir;
1527
1528 #if 1
1529                                 if (last_scrub_frame < (nframes64_t) drag_info.current_pointer_frame) {
1530                                         distance = (nframes64_t) drag_info.current_pointer_frame - last_scrub_frame;
1531                                         dir = 1.0;
1532                                 } else {
1533                                         distance = last_scrub_frame - (nframes64_t) drag_info.current_pointer_frame;
1534                                         dir = -1.0;
1535                                 }
1536 #else
1537                                 if (drag_info.grab_x < drag_info.current_pointer_x) {
1538                                         distance = drag_info.current_pointer_x - drag_info.grab_x;
1539                                         dir = -1.0;
1540                                 } else {
1541                                         distance = drag_info.grab_x - drag_info.current_pointer_x;
1542                                         dir = 1.0;
1543                                 }
1544 #endif
1545
1546                                 gettimeofday (&tmnow, 0);
1547                                 time = (tmnow.tv_sec * 1000000.0 + tmnow.tv_usec) - last_scrub_time;
1548                                 last_scrub_frame = drag_info.current_pointer_frame;
1549                                 last_scrub_time = (tmnow.tv_sec * 1000000.0) + tmnow.tv_usec;
1550                                 speed = ((double)distance/session->frame_rate()) / (time/1000000.0); // frames/sec
1551
1552                                 add_mouse_speed (speed, dir);
1553
1554                                 if (mouse_speed_update < 0) {
1555                                         mouse_speed_update = g_timeout_add (10, _update_mouse_speed, this);
1556                                         update_mouse_speed ();
1557                                 }
1558                         }
1559                 }
1560
1561         default:
1562                 break;
1563         }
1564
1565
1566         if (!from_autoscroll && drag_info.item) {
1567                 /* item != 0 is the best test i can think of for dragging.
1568                 */
1569                 if (!drag_info.move_threshold_passed) {
1570
1571                         bool x_threshold_passed =  (abs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1572                         bool y_threshold_passed =  (abs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1573                         
1574                         drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1575                         
1576                         // and change the initial grab loc/frame if this drag info wants us to
1577
1578                         if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1579                                 drag_info.grab_frame = drag_info.current_pointer_frame;
1580                                 drag_info.grab_x = drag_info.current_pointer_x;
1581                                 drag_info.grab_y = drag_info.current_pointer_y;
1582                                 drag_info.last_pointer_frame = drag_info.grab_frame;
1583                                 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1584                         }
1585                 }
1586         }
1587
1588         switch (item_type) {
1589         case PlayheadCursorItem:
1590         case EditCursorItem:
1591         case MarkerItem:
1592         case ControlPointItem:
1593         case TempoMarkerItem:
1594         case MeterMarkerItem:
1595         case RegionViewNameHighlight:
1596         case StartSelectionTrimItem:
1597         case EndSelectionTrimItem:
1598         case SelectionItem:
1599         case GainLineItem:
1600         case AutomationLineItem:
1601         case FadeInHandleItem:
1602         case FadeOutHandleItem:
1603
1604 #ifdef WITH_CMT
1605         case ImageFrameHandleStartItem:
1606         case ImageFrameHandleEndItem:
1607         case MarkerViewHandleStartItem:
1608         case MarkerViewHandleEndItem:
1609 #endif
1610
1611           if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1612                                  (event->motion.state & Gdk::BUTTON2_MASK))) {
1613                   if (!from_autoscroll) {
1614                           maybe_autoscroll (event);
1615                   }
1616                   (this->*(drag_info.motion_callback)) (item, event);
1617                   goto handled;
1618           }
1619           goto not_handled;
1620           
1621         default:
1622                 break;
1623         }
1624
1625         switch (mouse_mode) {
1626         case MouseObject:
1627         case MouseRange:
1628         case MouseZoom:
1629         case MouseTimeFX:
1630         case MouseNote:
1631                 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1632                                        (event->motion.state & GDK_BUTTON2_MASK))) {
1633                         if (!from_autoscroll) {
1634                                 maybe_autoscroll (event);
1635                         }
1636                         (this->*(drag_info.motion_callback)) (item, event);
1637                         goto handled;
1638                 }
1639                 goto not_handled;
1640                 break;
1641
1642         default:
1643                 break;
1644         }
1645
1646   handled:
1647         track_canvas_motion (event);
1648         // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1649         return true;
1650         
1651   not_handled:
1652         return false;
1653 }
1654
1655 void
1656 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1657 {
1658         if (drag_info.item == 0) {
1659                 fatal << _("programming error: start_grab called without drag item") << endmsg;
1660                 /*NOTREACHED*/
1661                 return;
1662         }
1663
1664         if (cursor == 0) {
1665                 cursor = grabber_cursor;
1666         }
1667
1668         // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1669
1670         if (event->button.button == 2) {
1671                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1672                         drag_info.y_constrained = true;
1673                         drag_info.x_constrained = false;
1674                 } else {
1675                         drag_info.y_constrained = false;
1676                         drag_info.x_constrained = true;
1677                 }
1678         } else {
1679                 drag_info.x_constrained = false;
1680                 drag_info.y_constrained = false;
1681         }
1682
1683         drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1684         drag_info.last_pointer_frame = drag_info.grab_frame;
1685         drag_info.current_pointer_frame = drag_info.grab_frame;
1686         drag_info.current_pointer_x = drag_info.grab_x;
1687         drag_info.current_pointer_y = drag_info.grab_y;
1688         drag_info.cumulative_x_drag = 0;
1689         drag_info.cumulative_y_drag = 0;
1690         drag_info.first_move = true;
1691         drag_info.move_threshold_passed = false;
1692         drag_info.want_move_threshold = false;
1693         drag_info.pointer_frame_offset = 0;
1694         drag_info.brushing = false;
1695         drag_info.copied_location = 0;
1696
1697         drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1698                               *cursor,
1699                               event->button.time);
1700
1701         if (session && session->transport_rolling()) {
1702                 drag_info.was_rolling = true;
1703         } else {
1704                 drag_info.was_rolling = false;
1705         }
1706
1707         switch (snap_type) {
1708         case SnapToRegionStart:
1709         case SnapToRegionEnd:
1710         case SnapToRegionSync:
1711         case SnapToRegionBoundary:
1712                 build_region_boundary_cache ();
1713                 break;
1714         default:
1715                 break;
1716         }
1717 }
1718
1719 void
1720 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1721 {
1722         drag_info.item->ungrab (0);
1723         drag_info.item = new_item;
1724
1725         if (cursor == 0) {
1726                 cursor = grabber_cursor;
1727         }
1728
1729         drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1730 }
1731
1732 bool
1733 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1734 {
1735         bool did_drag = false;
1736
1737         stop_canvas_autoscroll ();
1738
1739         if (drag_info.item == 0) {
1740                 return false;
1741         }
1742         
1743         drag_info.item->ungrab (event->button.time);
1744
1745         if (drag_info.finished_callback) {
1746                 (this->*(drag_info.finished_callback)) (item, event);
1747         }
1748
1749         did_drag = !drag_info.first_move;
1750
1751         hide_verbose_canvas_cursor();
1752
1753         drag_info.item = 0;
1754         drag_info.copy = false;
1755         drag_info.motion_callback = 0;
1756         drag_info.finished_callback = 0;
1757         drag_info.last_trackview = 0;
1758         drag_info.last_frame_position = 0;
1759         drag_info.grab_frame = 0;
1760         drag_info.last_pointer_frame = 0;
1761         drag_info.current_pointer_frame = 0;
1762         drag_info.brushing = false;
1763
1764         if (drag_info.copied_location) {
1765                 delete drag_info.copied_location;
1766                 drag_info.copied_location = 0;
1767         }
1768
1769         return did_drag;
1770 }
1771
1772 void
1773 Editor::set_edit_cursor (GdkEvent* event)
1774 {
1775         nframes_t pointer_frame = event_frame (event);
1776
1777         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1778                 if (snap_type != SnapToEditCursor) {
1779                         snap_to (pointer_frame);
1780                 }
1781         }
1782
1783         edit_cursor->set_position (pointer_frame);
1784         edit_cursor_clock.set (pointer_frame);
1785 }
1786
1787 void
1788 Editor::set_playhead_cursor (GdkEvent* event)
1789 {
1790         nframes_t pointer_frame = event_frame (event);
1791
1792         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1793                 snap_to (pointer_frame);
1794         }
1795
1796         if (session) {
1797                 session->request_locate (pointer_frame, session->transport_rolling());
1798         }
1799 }
1800
1801 void
1802 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1803 {
1804         drag_info.item = item;
1805         drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1806         drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1807
1808         start_grab (event);
1809
1810         if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1811                 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1812                 /*NOTREACHED*/
1813         }
1814
1815         AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1816
1817         drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in()->back()->when + arv->region()->position()); 
1818 }
1819
1820 void
1821 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1822 {
1823         AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1824         nframes_t pos;
1825         nframes_t fade_length;
1826
1827         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1828                 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1829         }
1830         else {
1831                 pos = 0;
1832         }
1833
1834         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1835                 snap_to (pos);
1836         }
1837
1838         if (pos < (arv->region()->position() + 64)) {
1839                 fade_length = 64; // this should be a minimum defined somewhere
1840         } else if (pos > arv->region()->last_frame()) {
1841                 fade_length = arv->region()->length();
1842         } else {
1843                 fade_length = pos - arv->region()->position();
1844         }               
1845         /* mapover the region selection */
1846
1847         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1848
1849                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1850                 
1851                 if (!tmp) {
1852                         continue;
1853                 }
1854         
1855                 tmp->reset_fade_in_shape_width (fade_length);
1856         }
1857
1858         show_verbose_duration_cursor (arv->region()->position(),  arv->region()->position() + fade_length, 10);
1859
1860         drag_info.first_move = false;
1861 }
1862
1863 void
1864 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1865 {
1866         AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1867         nframes_t pos;
1868         nframes_t fade_length;
1869
1870         if (drag_info.first_move) return;
1871
1872         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1873                 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1874         } else {
1875                 pos = 0;
1876         }
1877
1878         if (pos < (arv->region()->position() + 64)) {
1879                 fade_length = 64; // this should be a minimum defined somewhere
1880         } else if (pos > arv->region()->last_frame()) {
1881                 fade_length = arv->region()->length();
1882         } else {
1883                 fade_length = pos - arv->region()->position();
1884         }
1885                 
1886         begin_reversible_command (_("change fade in length"));
1887
1888         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1889
1890                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1891                 
1892                 if (!tmp) {
1893                         continue;
1894                 }
1895         
1896                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
1897                 XMLNode &before = alist->get_state();
1898
1899                 tmp->audio_region()->set_fade_in_length (fade_length);
1900                 
1901                 XMLNode &after = alist->get_state();
1902                 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
1903         }
1904
1905         commit_reversible_command ();
1906 }
1907
1908 void
1909 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1910 {
1911         drag_info.item = item;
1912         drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1913         drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1914
1915         start_grab (event);
1916
1917         if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1918                 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1919                 /*NOTREACHED*/
1920         }
1921
1922         AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1923
1924         drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out()->back()->when + arv->region()->position());      
1925 }
1926
1927 void
1928 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1929 {
1930         AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1931         nframes_t pos;
1932         nframes_t fade_length;
1933
1934         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1935                 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1936         } else {
1937                 pos = 0;
1938         }
1939
1940         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1941                 snap_to (pos);
1942         }
1943         
1944         if (pos > (arv->region()->last_frame() - 64)) {
1945                 fade_length = 64; // this should really be a minimum fade defined somewhere
1946         }
1947         else if (pos < arv->region()->position()) {
1948                 fade_length = arv->region()->length();
1949         }
1950         else {
1951                 fade_length = arv->region()->last_frame() - pos;
1952         }
1953                 
1954         /* mapover the region selection */
1955
1956         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1957
1958                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1959                 
1960                 if (!tmp) {
1961                         continue;
1962                 }
1963         
1964                 tmp->reset_fade_out_shape_width (fade_length);
1965         }
1966
1967         show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1968
1969         drag_info.first_move = false;
1970 }
1971
1972 void
1973 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1974 {
1975         if (drag_info.first_move) return;
1976
1977         AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1978         nframes_t pos;
1979         nframes_t fade_length;
1980
1981         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1982                 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1983         }
1984         else {
1985                 pos = 0;
1986         }
1987
1988         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1989                 snap_to (pos);
1990         }
1991
1992         if (pos > (arv->region()->last_frame() - 64)) {
1993                 fade_length = 64; // this should really be a minimum fade defined somewhere
1994         }
1995         else if (pos < arv->region()->position()) {
1996                 fade_length = arv->region()->length();
1997         }
1998         else {
1999                 fade_length = arv->region()->last_frame() - pos;
2000         }
2001
2002         begin_reversible_command (_("change fade out length"));
2003
2004         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2005
2006                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2007                 
2008                 if (!tmp) {
2009                         continue;
2010                 }
2011         
2012                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2013                 XMLNode &before = alist->get_state();
2014                 
2015                 tmp->audio_region()->set_fade_out_length (fade_length);
2016
2017                 XMLNode &after = alist->get_state();
2018                 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2019         }
2020
2021         commit_reversible_command ();
2022 }
2023
2024 void
2025 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2026 {
2027         drag_info.item = item;
2028         drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2029         drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2030
2031         start_grab (event);
2032
2033         if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2034                 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2035                 /*NOTREACHED*/
2036         }
2037
2038         Cursor* cursor = (Cursor *) drag_info.data;
2039
2040         if (cursor == playhead_cursor) {
2041                 _dragging_playhead = true;
2042                 
2043                 if (session && drag_info.was_rolling) {
2044                         session->request_stop ();
2045                 }
2046
2047                 if (session && session->is_auditioning()) {
2048                         session->cancel_audition ();
2049                 }
2050         }
2051
2052         drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;  
2053         
2054         show_verbose_time_cursor (cursor->current_frame, 10);
2055 }
2056
2057 void
2058 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2059 {
2060         Cursor* cursor = (Cursor *) drag_info.data;
2061         nframes_t adjusted_frame;
2062         
2063         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2064                 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2065         }
2066         else {
2067                 adjusted_frame = 0;
2068         }
2069         
2070         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2071                 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
2072                         snap_to (adjusted_frame);
2073                 }
2074         }
2075         
2076         if (adjusted_frame == drag_info.last_pointer_frame) return;
2077
2078         cursor->set_position (adjusted_frame);
2079         
2080         if (cursor == edit_cursor) {
2081                 edit_cursor_clock.set (cursor->current_frame);
2082         } else {
2083                 UpdateAllTransportClocks (cursor->current_frame);
2084         }
2085
2086         show_verbose_time_cursor (cursor->current_frame, 10);
2087
2088         drag_info.last_pointer_frame = adjusted_frame;
2089         drag_info.first_move = false;
2090 }
2091
2092 void
2093 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2094 {
2095         if (drag_info.first_move) return;
2096         
2097         cursor_drag_motion_callback (item, event);
2098
2099         _dragging_playhead = false;
2100         
2101         if (item == &playhead_cursor->canvas_item) {
2102                 if (session) {
2103                         session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2104                 }
2105         } else if (item == &edit_cursor->canvas_item) {
2106                 edit_cursor->set_position (edit_cursor->current_frame);
2107                 edit_cursor_clock.set (edit_cursor->current_frame);
2108         } 
2109 }
2110
2111 void
2112 Editor::update_marker_drag_item (Location *location)
2113 {
2114         double x1 = frame_to_pixel (location->start());
2115         double x2 = frame_to_pixel (location->end());
2116
2117         if (location->is_mark()) {
2118                 marker_drag_line_points.front().set_x(x1);
2119                 marker_drag_line_points.back().set_x(x1);
2120                 marker_drag_line->property_points() = marker_drag_line_points;
2121         }
2122         else {
2123                 range_marker_drag_rect->property_x1() = x1;
2124                 range_marker_drag_rect->property_x2() = x2;
2125         }
2126 }
2127
2128 void
2129 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2130 {
2131         Marker* marker;
2132
2133         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2134                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2135                 /*NOTREACHED*/
2136         }
2137
2138         bool is_start;
2139
2140         Location  *location = find_location_from_marker (marker, is_start);
2141
2142         drag_info.item = item;
2143         drag_info.data = marker;
2144         drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2145         drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2146
2147         start_grab (event);
2148
2149         drag_info.copied_location = new Location (*location);
2150         drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());       
2151
2152         update_marker_drag_item (location);
2153
2154         if (location->is_mark()) {
2155                 marker_drag_line->show();
2156                 marker_drag_line->raise_to_top();
2157         }
2158         else {
2159                 range_marker_drag_rect->show();
2160                 range_marker_drag_rect->raise_to_top();
2161         }
2162         
2163         if (is_start) show_verbose_time_cursor (location->start(), 10);
2164         else show_verbose_time_cursor (location->end(), 10);
2165 }
2166
2167 void
2168 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2169 {
2170         nframes_t f_delta;      
2171         Marker* marker = (Marker *) drag_info.data;
2172         Location  *real_location;
2173         Location  *copy_location;
2174         bool is_start;
2175         bool move_both = false;
2176
2177
2178         nframes_t newframe;
2179         if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2180                 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2181         } else {
2182                 newframe = 0;
2183         }
2184
2185         nframes_t next = newframe;
2186
2187         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2188                 snap_to (newframe, 0, true);
2189         }
2190         
2191         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) { 
2192                 return;
2193         }
2194
2195         /* call this to find out if its the start or end */
2196         
2197         real_location = find_location_from_marker (marker, is_start);
2198
2199         /* use the copy that we're "dragging" around */
2200         
2201         copy_location = drag_info.copied_location;
2202
2203         f_delta = copy_location->end() - copy_location->start();
2204         
2205         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2206                 move_both = true;
2207         }
2208
2209         if (copy_location->is_mark()) {
2210                 /* just move it */
2211
2212                 copy_location->set_start (newframe);
2213
2214         } else {
2215
2216                 if (is_start) { // start-of-range marker
2217                         
2218                         if (move_both) {
2219                                 copy_location->set_start (newframe);
2220                                 copy_location->set_end (newframe + f_delta);
2221                         } else  if (newframe < copy_location->end()) {
2222                                 copy_location->set_start (newframe);
2223                         } else { 
2224                                 snap_to (next, 1, true);
2225                                 copy_location->set_end (next);
2226                                 copy_location->set_start (newframe);
2227                         }
2228                         
2229                 } else { // end marker
2230                         
2231                         if (move_both) {
2232                                 copy_location->set_end (newframe);
2233                                 copy_location->set_start (newframe - f_delta);
2234                         } else if (newframe > copy_location->start()) {
2235                                 copy_location->set_end (newframe);
2236                                 
2237                         } else if (newframe > 0) {
2238                                 snap_to (next, -1, true);
2239                                 copy_location->set_start (next);
2240                                 copy_location->set_end (newframe);
2241                         }
2242                 }
2243         }
2244
2245         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2246         drag_info.first_move = false;
2247
2248         update_marker_drag_item (copy_location);
2249
2250         LocationMarkers* lm = find_location_markers (real_location);
2251         lm->set_position (copy_location->start(), copy_location->end());
2252         
2253         show_verbose_time_cursor (newframe, 10);
2254 }
2255
2256 void
2257 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2258 {
2259         if (drag_info.first_move) {
2260                 marker_drag_motion_callback (item, event);
2261
2262         }
2263         
2264         Marker* marker = (Marker *) drag_info.data;
2265         bool is_start;
2266
2267
2268         begin_reversible_command ( _("move marker") );
2269         XMLNode &before = session->locations()->get_state();
2270         
2271         Location * location = find_location_from_marker (marker, is_start);
2272         
2273         if (location) {
2274                 if (location->is_mark()) {
2275                         location->set_start (drag_info.copied_location->start());
2276                 } else {
2277                         location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2278                 }
2279         }
2280
2281         XMLNode &after = session->locations()->get_state();
2282         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2283         commit_reversible_command ();
2284         
2285         marker_drag_line->hide();
2286         range_marker_drag_rect->hide();
2287 }
2288
2289 void
2290 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2291 {
2292         Marker* marker;
2293         MeterMarker* meter_marker;
2294
2295         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2296                 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2297                 /*NOTREACHED*/
2298         }
2299
2300         meter_marker = dynamic_cast<MeterMarker*> (marker);
2301
2302         MetricSection& section (meter_marker->meter());
2303
2304         if (!section.movable()) {
2305                 return;
2306         }
2307
2308         drag_info.item = item;
2309         drag_info.data = marker;
2310         drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2311         drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2312
2313         start_grab (event);
2314
2315         drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();  
2316
2317         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2318 }
2319
2320 void
2321 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2322 {
2323         Marker* marker;
2324         MeterMarker* meter_marker;
2325
2326         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2327                 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2328                 /*NOTREACHED*/
2329         }
2330
2331         meter_marker = dynamic_cast<MeterMarker*> (marker);
2332         
2333         // create a dummy marker for visual representation of moving the copy.
2334         // The actual copying is not done before we reach the finish callback.
2335         char name[64];
2336         snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2337         MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name, 
2338                                                   *new MeterSection(meter_marker->meter()));
2339
2340         drag_info.item = &new_marker->the_item();
2341         drag_info.copy = true;
2342         drag_info.data = new_marker;
2343         drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2344         drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2345
2346         start_grab (event);
2347
2348         drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();  
2349
2350         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2351 }
2352
2353 void
2354 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2355 {
2356         MeterMarker* marker = (MeterMarker *) drag_info.data;
2357         nframes_t adjusted_frame;
2358
2359         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2360                 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2361         }
2362         else {
2363                 adjusted_frame = 0;
2364         }
2365         
2366         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2367                 snap_to (adjusted_frame);
2368         }
2369         
2370         if (adjusted_frame == drag_info.last_pointer_frame) return;
2371
2372         marker->set_position (adjusted_frame);
2373         
2374         
2375         drag_info.last_pointer_frame = adjusted_frame;
2376         drag_info.first_move = false;
2377
2378         show_verbose_time_cursor (adjusted_frame, 10);
2379 }
2380
2381 void
2382 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2383 {
2384         if (drag_info.first_move) return;
2385
2386         meter_marker_drag_motion_callback (drag_info.item, event);
2387         
2388         MeterMarker* marker = (MeterMarker *) drag_info.data;
2389         BBT_Time when;
2390         
2391         TempoMap& map (session->tempo_map());
2392         map.bbt_time (drag_info.last_pointer_frame, when);
2393         
2394         if (drag_info.copy == true) {
2395                 begin_reversible_command (_("copy meter mark"));
2396                 XMLNode &before = map.get_state();
2397                 map.add_meter (marker->meter(), when);
2398                 XMLNode &after = map.get_state();
2399                 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2400                 commit_reversible_command ();
2401
2402                 // delete the dummy marker we used for visual representation of copying.
2403                 // a new visual marker will show up automatically.
2404                 delete marker;
2405         } else {
2406                 begin_reversible_command (_("move meter mark"));
2407                 XMLNode &before = map.get_state();
2408                 map.move_meter (marker->meter(), when);
2409                 XMLNode &after = map.get_state();
2410                 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2411                 commit_reversible_command ();
2412         }
2413 }
2414
2415 void
2416 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2417 {
2418         Marker* marker;
2419         TempoMarker* tempo_marker;
2420
2421         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2422                 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2423                 /*NOTREACHED*/
2424         }
2425
2426         if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2427                 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2428                 /*NOTREACHED*/
2429         }
2430
2431         MetricSection& section (tempo_marker->tempo());
2432
2433         if (!section.movable()) {
2434                 return;
2435         }
2436
2437         drag_info.item = item;
2438         drag_info.data = marker;
2439         drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2440         drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2441
2442         start_grab (event);
2443
2444         drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();  
2445         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2446 }
2447
2448 void
2449 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2450 {
2451         Marker* marker;
2452         TempoMarker* tempo_marker;
2453
2454         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2455                 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2456                 /*NOTREACHED*/
2457         }
2458
2459         if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2460                 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2461                 /*NOTREACHED*/
2462         }
2463
2464         // create a dummy marker for visual representation of moving the copy.
2465         // The actual copying is not done before we reach the finish callback.
2466         char name[64];
2467         snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2468         TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name, 
2469                                                   *new TempoSection(tempo_marker->tempo()));
2470
2471         drag_info.item = &new_marker->the_item();
2472         drag_info.copy = true;
2473         drag_info.data = new_marker;
2474         drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2475         drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2476
2477         start_grab (event);
2478
2479         drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2480
2481         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2482 }
2483
2484 void
2485 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2486 {
2487         TempoMarker* marker = (TempoMarker *) drag_info.data;
2488         nframes_t adjusted_frame;
2489         
2490         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2491                 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2492         }
2493         else {
2494                 adjusted_frame = 0;
2495         }
2496
2497         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2498                 snap_to (adjusted_frame);
2499         }
2500         
2501         if (adjusted_frame == drag_info.last_pointer_frame) return;
2502
2503         /* OK, we've moved far enough to make it worth actually move the thing. */
2504                 
2505         marker->set_position (adjusted_frame);
2506         
2507         show_verbose_time_cursor (adjusted_frame, 10);
2508
2509         drag_info.last_pointer_frame = adjusted_frame;
2510         drag_info.first_move = false;
2511 }
2512
2513 void
2514 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2515 {
2516         if (drag_info.first_move) return;
2517         
2518         tempo_marker_drag_motion_callback (drag_info.item, event);
2519         
2520         TempoMarker* marker = (TempoMarker *) drag_info.data;
2521         BBT_Time when;
2522         
2523         TempoMap& map (session->tempo_map());
2524         map.bbt_time (drag_info.last_pointer_frame, when);
2525
2526         if (drag_info.copy == true) {
2527                 begin_reversible_command (_("copy tempo mark"));
2528                 XMLNode &before = map.get_state();
2529                 map.add_tempo (marker->tempo(), when);
2530                 XMLNode &after = map.get_state();
2531                 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2532                 commit_reversible_command ();
2533                 
2534                 // delete the dummy marker we used for visual representation of copying.
2535                 // a new visual marker will show up automatically.
2536                 delete marker;
2537         } else {
2538                 begin_reversible_command (_("move tempo mark"));
2539                 XMLNode &before = map.get_state();
2540                 map.move_tempo (marker->tempo(), when);
2541                 XMLNode &after = map.get_state();
2542                 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2543                 commit_reversible_command ();
2544         }
2545 }
2546
2547 void
2548 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2549 {
2550         ControlPoint* control_point;
2551
2552         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2553                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2554                 /*NOTREACHED*/
2555         }
2556
2557         // We shouldn't remove the first or last gain point
2558         if (control_point->line().is_last_point(*control_point) ||
2559                 control_point->line().is_first_point(*control_point)) { 
2560                 return;
2561         }
2562
2563         control_point->line().remove_point (*control_point);
2564 }
2565
2566 void
2567 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event)
2568 {
2569         ControlPoint* control_point;
2570
2571         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2572                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2573                 /*NOTREACHED*/
2574         }
2575
2576         control_point->line().remove_point (*control_point);
2577 }
2578
2579 void
2580 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2581 {
2582         ControlPoint* control_point;
2583
2584         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2585                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2586                 /*NOTREACHED*/
2587         }
2588
2589         drag_info.item = item;
2590         drag_info.data = control_point;
2591         drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2592         drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2593
2594         start_grab (event, fader_cursor);
2595
2596         control_point->line().start_drag (control_point, drag_info.grab_frame, 0);
2597
2598         double fraction = 1.0 - ((control_point->get_y() - control_point->line().y_position()) / (double)control_point->line().height());
2599         set_verbose_canvas_cursor (control_point->line().get_verbose_cursor_string (fraction), 
2600                                    drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2601
2602         show_verbose_canvas_cursor ();
2603 }
2604
2605 void
2606 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2607 {
2608         ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2609
2610         double cx = drag_info.current_pointer_x;
2611         double cy = drag_info.current_pointer_y;
2612
2613         drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2614         drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2615
2616         if (drag_info.x_constrained) {
2617                 cx = drag_info.grab_x;
2618         }
2619         if (drag_info.y_constrained) {
2620                 cy = drag_info.grab_y;
2621         }
2622
2623         cp->line().parent_group().w2i (cx, cy);
2624
2625         cx = max (0.0, cx);
2626         cy = max (0.0, cy);
2627         cy = min ((double) (cp->line().y_position() + cp->line().height()), cy);
2628
2629         //translate cx to frames
2630         nframes_t cx_frames = unit_to_frame (cx);
2631
2632         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2633                 snap_to (cx_frames);
2634         }
2635
2636         const double fraction = 1.0 - ((cy - cp->line().y_position()) / (double)cp->line().height());
2637
2638         bool push;
2639
2640         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2641                 push = true;
2642         } else {
2643                 push = false;
2644         }
2645
2646         cp->line().point_drag (*cp, cx_frames , fraction, push);
2647         
2648         set_verbose_canvas_cursor_text (cp->line().get_verbose_cursor_string (fraction));
2649
2650         drag_info.first_move = false;
2651 }
2652
2653 void
2654 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2655 {
2656         ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2657
2658         if (drag_info.first_move) {
2659
2660                 /* just a click */
2661                 
2662                 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2663                         reset_point_selection ();
2664                 }
2665
2666         } else {
2667                 control_point_drag_motion_callback (item, event);
2668         }
2669         cp->line().end_drag (cp);
2670 }
2671
2672 void
2673 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2674 {
2675         switch (mouse_mode) {
2676         case MouseGain:
2677                 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2678                 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2679                 break;
2680         default:
2681                 break;
2682         }
2683 }
2684
2685 void
2686 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2687 {
2688         AutomationLine* al;
2689         
2690         if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2691                 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2692                 /*NOTREACHED*/
2693         }
2694
2695         start_line_grab (al, event);
2696 }
2697
2698 void
2699 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2700 {
2701         double cx;
2702         double cy;
2703         nframes_t frame_within_region;
2704
2705         /* need to get x coordinate in terms of parent (TimeAxisItemView)
2706            origin.
2707         */
2708
2709         cx = event->button.x;
2710         cy = event->button.y;
2711         line->parent_group().w2i (cx, cy);
2712         frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2713
2714         if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before, 
2715                                             current_line_drag_info.after)) {
2716                 /* no adjacent points */
2717                 return;
2718         }
2719
2720         drag_info.item = &line->grab_item();
2721         drag_info.data = line;
2722         drag_info.motion_callback = &Editor::line_drag_motion_callback;
2723         drag_info.finished_callback = &Editor::line_drag_finished_callback;
2724
2725         start_grab (event, fader_cursor);
2726
2727         const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height());
2728
2729         line->start_drag (0, drag_info.grab_frame, fraction);
2730         
2731         set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2732                                    drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2733         show_verbose_canvas_cursor ();
2734 }
2735
2736 void
2737 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2738 {
2739         AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2740         double cx = drag_info.current_pointer_x;
2741         double cy = drag_info.current_pointer_y;
2742
2743         line->parent_group().w2i (cx, cy);
2744         
2745         const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height());
2746
2747         bool push;
2748
2749         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2750                 push = false;
2751         } else {
2752                 push = true;
2753         }
2754
2755         line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2756         
2757         set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2758 }
2759
2760 void
2761 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2762 {
2763         AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2764         line_drag_motion_callback (item, event);
2765         line->end_drag (0);
2766 }
2767
2768 void
2769 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2770 {
2771         if (selection->regions.empty() || clicked_regionview == 0) {
2772                 return;
2773         }
2774
2775         drag_info.copy = false;
2776         drag_info.item = item;
2777         drag_info.data = clicked_regionview;
2778         drag_info.motion_callback = &Editor::region_drag_motion_callback;
2779         drag_info.finished_callback = &Editor::region_drag_finished_callback;
2780
2781         start_grab (event);
2782
2783         double speed = 1.0;
2784         TimeAxisView* tvp = clicked_axisview;
2785         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2786
2787         if (tv && tv->is_track()) {
2788                 speed = tv->get_diskstream()->speed();
2789         }
2790         
2791         drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2792         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2793         drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2794         // we want a move threshold
2795         drag_info.want_move_threshold = true;
2796         
2797         show_verbose_time_cursor (drag_info.last_frame_position, 10);
2798
2799         begin_reversible_command (_("move region(s)"));
2800 }
2801
2802 void
2803 Editor::start_create_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2804 {
2805         drag_info.copy = false;
2806         drag_info.item = item;
2807         drag_info.data = clicked_axisview;
2808         drag_info.last_trackview = clicked_axisview;
2809         drag_info.motion_callback = &Editor::create_region_drag_motion_callback;
2810         drag_info.finished_callback = &Editor::create_region_drag_finished_callback;
2811
2812         start_grab (event);
2813 }
2814
2815 void
2816 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2817 {
2818         if (selection->regions.empty() || clicked_regionview == 0) {
2819                 return;
2820         }
2821
2822         drag_info.copy = true;
2823         drag_info.item = item;
2824         drag_info.data = clicked_regionview;    
2825
2826         start_grab(event);
2827
2828         TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2829         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2830         double speed = 1.0;
2831
2832         if (rtv && rtv->is_track()) {
2833                 speed = rtv->get_diskstream()->speed();
2834         }
2835         
2836         drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2837         drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2838         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2839         // we want a move threshold
2840         drag_info.want_move_threshold = true;
2841         drag_info.motion_callback = &Editor::region_drag_motion_callback;
2842         drag_info.finished_callback = &Editor::region_drag_finished_callback;
2843         show_verbose_time_cursor (drag_info.last_frame_position, 10);
2844 }
2845
2846 void
2847 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2848 {
2849         if (selection->regions.empty() || clicked_regionview == 0) {
2850                 return;
2851         }
2852
2853         drag_info.copy = false;
2854         drag_info.item = item;
2855         drag_info.data = clicked_regionview;
2856         drag_info.motion_callback = &Editor::region_drag_motion_callback;
2857         drag_info.finished_callback = &Editor::region_drag_finished_callback;
2858
2859         start_grab (event);
2860
2861         double speed = 1.0;
2862         TimeAxisView* tvp = clicked_axisview;
2863         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2864
2865         if (tv && tv->is_track()) {
2866                 speed = tv->get_diskstream()->speed();
2867         }
2868         
2869         drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2870         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2871         drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2872         // we want a move threshold
2873         drag_info.want_move_threshold = true;
2874         drag_info.brushing = true;
2875         
2876         begin_reversible_command (_("Drag region brush"));
2877 }
2878
2879 void
2880 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2881 {
2882         double x_delta;
2883         double y_delta = 0;
2884         RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data); 
2885         nframes_t pending_region_position = 0;
2886         int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2887         int32_t visible_y_high = 0, visible_y_low = 512;  //high meaning higher numbered.. not the height on the screen
2888         bool clamp_y_axis = false;
2889         vector<int32_t>  height_list(512) ;
2890         vector<int32_t>::iterator j;
2891
2892         if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2893
2894                 drag_info.want_move_threshold = false; // don't copy again
2895
2896                 /* duplicate the region(s) */
2897
2898                 vector<RegionView*> new_regionviews;
2899                 
2900                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2901                         RegionView* rv;
2902                         RegionView* nrv;
2903
2904                         rv = (*i);
2905
2906                         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
2907                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
2908
2909                         if (arv) {
2910                                 nrv = new AudioRegionView (*arv);
2911                         } else if (mrv) {
2912                                 nrv = new MidiRegionView (*mrv);
2913                         } else {
2914                                 continue;
2915                         }
2916
2917                         nrv->get_canvas_group()->show ();
2918
2919                         new_regionviews.push_back (nrv);
2920                 }
2921
2922                 if (new_regionviews.empty()) {
2923                         return;
2924                 }
2925
2926                 /* reset selection to new regionviews */
2927                 
2928                 selection->set (new_regionviews);
2929                 
2930                 /* reset drag_info data to reflect the fact that we are dragging the copies */
2931                 
2932                 drag_info.data = new_regionviews.front();
2933
2934                 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2935         }
2936
2937         /* Which trackview is this ? */
2938
2939         TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2940         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2941
2942         /* The region motion is only processed if the pointer is over
2943            an audio track.
2944         */
2945         
2946         if (!tv || !tv->is_track()) {
2947                 /* To make sure we hide the verbose canvas cursor when the mouse is 
2948                    not held over a track. 
2949                 */
2950                 hide_verbose_canvas_cursor ();
2951                 return;
2952         }
2953         
2954         original_pointer_order = drag_info.last_trackview->order;
2955                 
2956         /************************************************************
2957              Y-Delta Computation
2958         ************************************************************/   
2959
2960         if (drag_info.brushing) {
2961                 clamp_y_axis = true;
2962                 pointer_y_span = 0;
2963                 goto y_axis_done;
2964         }
2965         
2966         if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2967
2968                 int32_t children = 0, numtracks = 0;
2969                 // XXX hard coding track limit, oh my, so very very bad
2970                 bitset <1024> tracks (0x00);
2971                 /* get a bitmask representing the visible tracks */
2972
2973                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2974                         TimeAxisView *tracklist_timeview;
2975                         tracklist_timeview = (*i);
2976                         RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
2977                         TimeAxisView::Children children_list;
2978               
2979                         /* zeroes are audio tracks. ones are other types. */
2980               
2981                         if (!rtv2->hidden()) {
2982                                 
2983                                 if (visible_y_high < rtv2->order) {
2984                                         visible_y_high = rtv2->order;
2985                                 }
2986                                 if (visible_y_low > rtv2->order) {
2987                                         visible_y_low = rtv2->order;
2988                                 }
2989                 
2990                                 if (!rtv2->is_track()) {                                  
2991                                         tracks = tracks |= (0x01 << rtv2->order);
2992                                 }
2993         
2994                                 height_list[rtv2->order] = (*i)->height;
2995                                 children = 1;
2996                                 if ((children_list = rtv2->get_child_list()).size() > 0) {
2997                                         for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) { 
2998                                                 tracks = tracks |= (0x01 << (rtv2->order + children));
2999                                                 height_list[rtv2->order + children] =  (*j)->height;                
3000                                                 numtracks++;
3001                                                 children++;     
3002                                         }
3003                                 }
3004                                 numtracks++;        
3005                         }
3006                 }
3007                 /* find the actual span according to the canvas */
3008
3009                 canvas_pointer_y_span = pointer_y_span;
3010                 if (drag_info.last_trackview->order >= tv->order) {
3011                         int32_t y;
3012                         for (y = tv->order; y < drag_info.last_trackview->order; y++) {
3013                                 if (height_list[y] == 0 ) {
3014                                         canvas_pointer_y_span--;
3015                                 }
3016                         }
3017                 } else {
3018                         int32_t y;
3019                         for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
3020                                 if (    height_list[y] == 0 ) {
3021                                         canvas_pointer_y_span++;
3022                                 }
3023                         }
3024                 }
3025
3026                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3027                         RegionView* rv2 = (*i);
3028                         double ix1, ix2, iy1, iy2;
3029                         int32_t n = 0;
3030
3031                         rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3032                         rv2->get_canvas_group()->i2w (ix1, iy1);
3033                         TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3034                         RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3035
3036                         if (rtv2->order != original_pointer_order) {    
3037                                 /* this isn't the pointer track */      
3038
3039                                 if (canvas_pointer_y_span > 0) {
3040
3041                                         /* moving up the canvas */
3042                                         if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
3043         
3044                                                 int32_t visible_tracks = 0;
3045                                                 while (visible_tracks < canvas_pointer_y_span ) {
3046                                                         visible_tracks++;
3047                   
3048                                                         while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3049                                                                 /* we're passing through a hidden track */
3050                                                                 n--;
3051                                                         }                 
3052                                                 }
3053                  
3054                                                 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {                  
3055                                                         clamp_y_axis = true;
3056                                                 }
3057                     
3058                                         } else {
3059                                                 clamp_y_axis = true;
3060                                         }                 
3061                   
3062                                 } else if (canvas_pointer_y_span < 0) {
3063
3064                                         /*moving down the canvas*/
3065
3066                                         if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3067                     
3068                     
3069                                                 int32_t visible_tracks = 0;
3070                     
3071                                                 while (visible_tracks > canvas_pointer_y_span ) {
3072                                                         visible_tracks--;
3073                       
3074                                                         while (height_list[rtv2->order - (visible_tracks - n)] == 0) {             
3075                                                                 n++;
3076                                                         }                
3077                                                 }
3078                                                 if (  tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3079                                                         clamp_y_axis = true;
3080                             
3081                                                 }
3082                                         } else {
3083                           
3084                                                 clamp_y_axis = true;
3085                                         }
3086                                 }               
3087                   
3088                         } else {
3089                       
3090                                 /* this is the pointer's track */
3091                                 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow 
3092                                         clamp_y_axis = true;
3093                                 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3094                                         clamp_y_axis = true;
3095                                 }
3096                         }             
3097                         if (clamp_y_axis) {
3098                                 break;
3099                         }
3100                 }
3101
3102         } else  if (drag_info.last_trackview == tv) {
3103                 clamp_y_axis = true;
3104         }         
3105
3106   y_axis_done:
3107         if (!clamp_y_axis) {
3108                 drag_info.last_trackview = tv;        
3109         }
3110           
3111         /************************************************************
3112             X DELTA COMPUTATION
3113         ************************************************************/
3114
3115         /* compute the amount of pointer motion in frames, and where
3116            the region would be if we moved it by that much.
3117         */
3118
3119         if (drag_info.move_threshold_passed) {
3120
3121                 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3122
3123                         nframes_t sync_frame;
3124                         nframes_t sync_offset;
3125                         int32_t sync_dir;
3126             
3127                         pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3128
3129                         sync_offset = rv->region()->sync_offset (sync_dir);
3130                         sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3131
3132                         /* we snap if the snap modifier is not enabled.
3133                          */
3134             
3135                         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3136                                 snap_to (sync_frame);   
3137                         }
3138             
3139                         if (sync_frame - sync_offset <= sync_frame) {
3140                                 pending_region_position = sync_frame - (sync_dir*sync_offset);
3141                         } else {
3142                                 pending_region_position = 0;
3143                         }
3144             
3145                 } else {
3146                         pending_region_position = 0;
3147                 }
3148           
3149                 if (pending_region_position > max_frames - rv->region()->length()) {
3150                         pending_region_position = drag_info.last_frame_position;
3151                 }
3152           
3153                 // printf ("3: pending_region_position= %lu    %lu\n", pending_region_position, drag_info.last_frame_position );
3154           
3155                 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3156
3157                         /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3158                            to make it appear at the new location.
3159                         */
3160
3161                         if (pending_region_position > drag_info.last_frame_position) {
3162                                 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3163                         } else {
3164                                 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3165                         }
3166
3167                         drag_info.last_frame_position = pending_region_position;
3168             
3169                 } else {
3170                         x_delta = 0;
3171                 }
3172
3173         } else {
3174                 /* threshold not passed */
3175
3176                 x_delta = 0;
3177         }
3178
3179         /*************************************************************
3180             PREPARE TO MOVE
3181         ************************************************************/
3182
3183         if (x_delta == 0 && (pointer_y_span == 0)) {
3184                 /* haven't reached next snap point, and we're not switching
3185                    trackviews. nothing to do.
3186                 */
3187                 return;
3188         } 
3189
3190
3191         if (x_delta < 0) {
3192                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3193
3194                         RegionView* rv2 = (*i);
3195
3196                         // If any regionview is at zero, we need to know so we can stop further leftward motion.
3197                         
3198                         double ix1, ix2, iy1, iy2;
3199                         rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3200                         rv2->get_canvas_group()->i2w (ix1, iy1);
3201
3202                         if (ix1 <= 1) {
3203                                 x_delta = 0;
3204                                 break;
3205                         }
3206                 }
3207         }
3208
3209         /*************************************************************
3210             MOTION                                                                    
3211         ************************************************************/
3212
3213         bool do_move;
3214
3215         if (drag_info.first_move) {
3216                 if (drag_info.move_threshold_passed) {
3217                         do_move = true;
3218                 } else {
3219                         do_move = false;
3220                 }
3221         } else {
3222                 do_move = true;
3223         }
3224
3225         if (do_move) {
3226
3227                 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3228                 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3229
3230                 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3231             
3232                         RegionView* rv = (*i);
3233                         double ix1, ix2, iy1, iy2;
3234                         int32_t temp_pointer_y_span = pointer_y_span;
3235
3236                         /* get item BBox, which will be relative to parent. so we have
3237                            to query on a child, then convert to world coordinates using
3238                            the parent.
3239                         */
3240
3241                         rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3242                         rv->get_canvas_group()->i2w (ix1, iy1);
3243                         TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3244                         RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3245                         RouteTimeAxisView* temp_rtv;
3246
3247                         if ((pointer_y_span != 0) && !clamp_y_axis) {
3248                                 y_delta = 0;
3249                                 int32_t x = 0;
3250                                 for (j = height_list.begin(); j!= height_list.end(); j++) {     
3251                                         if (x == canvas_rtv->order) {
3252                                                 /* we found the track the region is on */
3253                                                 if (x != original_pointer_order) {
3254                                                         /*this isn't from the same track we're dragging from */
3255                                                         temp_pointer_y_span = canvas_pointer_y_span;
3256                                                 }                 
3257                                                 while (temp_pointer_y_span > 0) {
3258                                                         /* we're moving up canvas-wise,
3259                                                            so  we need to find the next track height
3260                                                         */
3261                                                         if (j != height_list.begin()) {           
3262                                                                 j--;
3263                                                         }
3264                                                         if (x != original_pointer_order) { 
3265                                                                 /* we're not from the dragged track, so ignore hidden tracks. */              
3266                                                                 if ((*j) == 0) {
3267                                                                         temp_pointer_y_span++;
3268                                                                 }
3269                                                         }          
3270                                                         y_delta -= (*j);        
3271                                                         temp_pointer_y_span--;  
3272                                                 }
3273                                                 while (temp_pointer_y_span < 0) {                 
3274                                                         y_delta += (*j);
3275                                                         if (x != original_pointer_order) { 
3276                                                                 if ((*j) == 0) {
3277                                                                         temp_pointer_y_span--;
3278                                                                 }
3279                                                         }          
3280                     
3281                                                         if (j != height_list.end()) {                 
3282                                                                 j++;
3283                                                         }
3284                                                         temp_pointer_y_span++;
3285                                                 }
3286                                                 /* find out where we'll be when we move and set height accordingly */
3287                   
3288                                                 tvp2 = trackview_by_y_position (iy1 + y_delta);
3289                                                 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3290                                                 rv->set_y_position_and_height (0, temp_rtv->height);
3291         
3292                                                 /*   if you un-comment the following, the region colours will follow the track colours whilst dragging,
3293                                                      personally, i think this can confuse things, but never mind.
3294                                                 */
3295                                   
3296                                                 //const GdkColor& col (temp_rtv->view->get_region_color());
3297                                                 //rv->set_color (const_cast<GdkColor&>(col));
3298                                                 break;          
3299                                         }
3300                                         x++;
3301                                 }
3302                         }
3303           
3304                         /* prevent the regionview from being moved to before 
3305                            the zero position on the canvas.
3306                         */
3307                         /* clamp */
3308                 
3309                         if (x_delta < 0) {
3310                                 if (-x_delta > ix1) {
3311                                         x_delta = -ix1;
3312                                 }
3313                         } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3314                                 x_delta = max_frames - rv->region()->last_frame();
3315                         }
3316
3317
3318                         if (drag_info.first_move) {
3319
3320                                 /* hide any dependent views */
3321                         
3322                                 rv->get_time_axis_view().hide_dependent_views (*rv);
3323                         
3324                                 /* this is subtle. raising the regionview itself won't help,
3325                                    because raise_to_top() just puts the item on the top of
3326                                    its parent's stack. so, we need to put the trackview canvas_display group
3327                                    on the top, since its parent is the whole canvas.
3328                                 */
3329                         
3330                                 rv->get_canvas_group()->raise_to_top();
3331                                 rv->get_time_axis_view().canvas_display->raise_to_top();
3332                                 cursor_group->raise_to_top();
3333
3334                                 rv->fake_set_opaque (true);
3335                         }
3336                         
3337                         if (drag_info.brushing) {
3338                                 mouse_brush_insert_region (rv, pending_region_position);
3339                         } else {
3340                                 rv->move (x_delta, y_delta);                    
3341                         }
3342
3343                 } /* foreach region */
3344
3345         } /* if do_move */
3346
3347         if (drag_info.first_move && drag_info.move_threshold_passed) {
3348                 cursor_group->raise_to_top();
3349                 drag_info.first_move = false;
3350         }
3351
3352         if (x_delta != 0 && !drag_info.brushing) {
3353                 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3354         }
3355
3356
3357 void
3358 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3359 {
3360         nframes_t where;
3361         RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3362         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3363         bool nocommit = true;
3364         double speed;
3365         RouteTimeAxisView* rtv;
3366         bool regionview_y_movement;
3367         bool regionview_x_movement;
3368         vector<RegionView*> copies;
3369
3370         /* first_move is set to false if the regionview has been moved in the 
3371            motion handler. 
3372         */
3373
3374         if (drag_info.first_move) {
3375                 /* just a click */
3376                 goto out;
3377         }
3378
3379         nocommit = false;
3380
3381         /* The regionview has been moved at some stage during the grab so we need
3382            to account for any mouse movement between this event and the last one. 
3383         */      
3384
3385         region_drag_motion_callback (item, event);
3386
3387         if (drag_info.brushing) {
3388                 /* all changes were made during motion event handlers */
3389                 
3390                 if (drag_info.copy) {
3391                         for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3392                                 copies.push_back (*i);
3393                         }
3394                 }
3395
3396                 goto out;
3397         }
3398
3399         /* adjust for track speed */
3400         speed = 1.0;
3401
3402         rtv = dynamic_cast<RouteTimeAxisView*> (drag_info.last_trackview);
3403         if (rtv && rtv->get_diskstream()) {
3404                 speed = rtv->get_diskstream()->speed();
3405         }
3406         
3407         regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3408         regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3409
3410         //printf ("last_frame: %s position is %lu  %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed); 
3411         //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str()); 
3412         
3413         char* op_string;
3414
3415         if (drag_info.copy) {
3416                 if (drag_info.x_constrained) {
3417                         op_string = _("fixed time region copy");
3418                 } else {
3419                         op_string = _("region copy");
3420                 } 
3421         } else {
3422                 if (drag_info.x_constrained) {
3423                         op_string = _("fixed time region drag");
3424                 } else {
3425                         op_string = _("region drag");
3426                 }
3427         }
3428
3429         begin_reversible_command (op_string);
3430
3431         if (regionview_y_movement) {
3432
3433                 /* moved to a different audio track. */
3434                 
3435                 vector<RegionView*> new_selection;
3436
3437                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3438                         
3439                         RegionView* rv = (*i);              
3440
3441                         double ix1, ix2, iy1, iy2;
3442                         
3443                         rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3444                         rv->get_canvas_group()->i2w (ix1, iy1);
3445                         TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3446                         RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3447
3448                         boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3449                         boost::shared_ptr<Playlist> to_playlist = rtv2->playlist();
3450
3451                         where = (nframes_t) (unit_to_frame (ix1) * speed);
3452                         boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3453
3454                         /* undo the previous hide_dependent_views so that xfades don't
3455                            disappear on copying regions 
3456                         */
3457
3458                         rv->get_time_axis_view().reveal_dependent_views (*rv);
3459
3460                         if (!drag_info.copy) {
3461                                 
3462                                 /* the region that used to be in the old playlist is not
3463                                    moved to the new one - we make a copy of it. as a result,
3464                                    any existing editor for the region should no longer be
3465                                    visible.
3466                                 */ 
3467             
3468                                 rv->hide_region_editor();
3469                                 rv->fake_set_opaque (false);
3470
3471                                 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));    
3472                                 from_playlist->remove_region ((rv->region()));
3473                                 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));    
3474
3475                         } else {
3476
3477                                 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3478                                 
3479                                 copies.push_back (rv);
3480                         }
3481
3482                         latest_regionview = 0;
3483                         
3484                         sigc::connection c = rtv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3485                         session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));        
3486                         to_playlist->add_region (new_region, where);
3487                         session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));        
3488                         c.disconnect ();
3489                                                               
3490                         if (latest_regionview) {
3491                                 new_selection.push_back (latest_regionview);
3492                         }
3493
3494                         /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3495                            was selected in all of them, then removing it from the playlist will have removed all
3496                            trace of it from the selection (i.e. there were N regions selected, we removed 1,
3497                            but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3498                            corresponding regionview, and the selection is now empty).
3499
3500                            this could have invalidated any and all iterators into the region selection.
3501
3502                            the heuristic we use here is: if the region selection is empty, break out of the loop
3503                            here. if the region selection is not empty, then restart the loop because we know that
3504                            we must have removed at least the region(view) we've just been working on as well as any
3505                            that we processed on previous iterations.
3506
3507                            EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3508                            we can just iterate.
3509                         */
3510
3511                         if (drag_info.copy) {
3512                                 ++i;
3513                         } else {
3514                                 if (selection->regions.empty()) {
3515                                         break;
3516                                 } else { 
3517                                         i = selection->regions.by_layer().begin();
3518                                 }
3519                         }
3520                 } 
3521
3522                 selection->set (new_selection);
3523
3524         } else {
3525
3526                 /* motion within a single track */
3527
3528                 list<RegionView*> regions = selection->regions.by_layer();
3529
3530                 if (drag_info.copy) {
3531                         selection->clear_regions();
3532                 }
3533                 
3534                 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3535
3536                         rv = (*i);
3537
3538                         if (!rv->region()->can_move()) {
3539                                 continue;
3540                         }
3541
3542                         if (regionview_x_movement) {
3543                                 double ownspeed = 1.0;
3544                                 rtv = dynamic_cast<RouteTimeAxisView*> (&(rv->get_time_axis_view()));
3545
3546                                 if (rtv && rtv->get_diskstream()) {
3547                                         ownspeed = rtv->get_diskstream()->speed();
3548                                 }
3549                                 
3550                                 /* base the new region position on the current position of the regionview.*/
3551                                 
3552                                 double ix1, ix2, iy1, iy2;
3553                                 
3554                                 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3555                                 rv->get_canvas_group()->i2w (ix1, iy1);
3556                                 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3557                                 
3558                         } else {
3559                                 
3560                                 where = rv->region()->position();
3561                         }
3562
3563                         boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3564
3565                         assert (to_playlist);
3566
3567                         /* add the undo */
3568
3569                         session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));        
3570
3571                         if (drag_info.copy) {
3572
3573                                 boost::shared_ptr<Region> newregion;
3574                                 boost::shared_ptr<Region> ar;
3575                                 boost::shared_ptr<Region> mr;
3576
3577                                 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3578                                         newregion = RegionFactory::create (ar);
3579                                 } else if ((mr = boost::dynamic_pointer_cast<MidiRegion>(rv->region())) != 0) {
3580                                         newregion = RegionFactory::create (mr);
3581                                 }
3582
3583                                 /* add it */
3584
3585                                 latest_regionview = 0;
3586                                 sigc::connection c = rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3587                                 to_playlist->add_region (newregion, (nframes_t) (where * rtv->get_diskstream()->speed()));
3588                                 c.disconnect ();
3589                                 
3590                                 if (latest_regionview) {
3591                                         rtv->reveal_dependent_views (*latest_regionview);
3592                                         selection->add (latest_regionview);
3593                                 }
3594
3595                                 /* if the original region was locked, we don't care for the new one */
3596                                 
3597                                 newregion->set_locked (false);                  
3598                                 
3599                         } else {
3600
3601                                 /* just change the model */
3602
3603                                 rv->region()->set_position (where, (void*) this);
3604
3605                         }
3606
3607                         /* add the redo */
3608
3609                         session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3610
3611                         if (drag_info.copy) {
3612                                 copies.push_back (rv);
3613                         }
3614                 }
3615         }
3616
3617   out:
3618         
3619         if (!nocommit) {
3620                 commit_reversible_command ();
3621         }
3622
3623         for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3624                 delete *x;
3625         }
3626 }
3627
3628         
3629 void
3630 Editor::create_region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3631 {
3632         if (drag_info.move_threshold_passed) {
3633                 if (drag_info.first_move) {
3634                         // TODO: create region-create-drag region view here
3635                         drag_info.first_move = false;
3636                 }
3637
3638                 // TODO: resize region-create-drag region view here
3639         }
3640
3641
3642 void
3643 Editor::create_region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3644 {
3645         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (drag_info.last_trackview);
3646         if (!mtv)
3647                 return;
3648
3649         const boost::shared_ptr<MidiDiskstream> diskstream =
3650                 boost::dynamic_pointer_cast<MidiDiskstream>(mtv->view()->trackview().track()->diskstream());
3651         
3652         if (!diskstream) {
3653                 warning << "Cannot create non-MIDI region" << endl;
3654                 return;
3655         }
3656
3657         if (drag_info.first_move) {
3658                 begin_reversible_command (_("create region"));
3659                 XMLNode &before = mtv->playlist()->get_state();
3660
3661                 nframes_t start = drag_info.grab_frame;
3662                 snap_to (start, -1);
3663                 const Meter& m = session->tempo_map().meter_at(start);
3664                 const Tempo& t = session->tempo_map().tempo_at(start);
3665                 double length = m.frames_per_bar(t, session->frame_rate());
3666
3667                 boost::shared_ptr<Source> src = session->create_midi_source_for_session(*diskstream.get());
3668                                 
3669                 mtv->playlist()->add_region (boost::dynamic_pointer_cast<MidiRegion>(RegionFactory::create(
3670                                 src, 0, length, PBD::basename_nosuffix(src->name()))), start);
3671                 XMLNode &after = mtv->playlist()->get_state();
3672                 session->add_command(new MementoCommand<Playlist>(*mtv->playlist().get(), &before, &after));
3673                 commit_reversible_command();
3674
3675         } else {
3676                 create_region_drag_motion_callback (item, event);
3677                 // TODO: create region-create-drag region here
3678         }
3679 }
3680
3681 void
3682 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3683 {
3684         /* Either add to or set the set the region selection, unless
3685            this is an alignment click (control used)
3686         */
3687         
3688         if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3689                 TimeAxisView* tv = &rv.get_time_axis_view();
3690                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3691                 double speed = 1.0;
3692                 if (rtv && rtv->is_track()) {
3693                         speed = rtv->get_diskstream()->speed();
3694                 }
3695
3696                 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3697
3698                         align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3699
3700                 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3701
3702                         align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3703
3704                 } else {
3705
3706                         align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3707                 }
3708         }
3709 }
3710
3711 void
3712 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos) 
3713 {
3714         char buf[128];
3715         SMPTE::Time smpte;
3716         BBT_Time bbt;
3717         int hours, mins;
3718         nframes_t frame_rate;
3719         float secs;
3720
3721         if (session == 0) {
3722                 return;
3723         }
3724
3725         switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3726         case AudioClock::BBT:
3727                 session->bbt_time (frame, bbt);
3728                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3729                 break;
3730                 
3731         case AudioClock::SMPTE:
3732                 session->smpte_time (frame, smpte);
3733                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3734                 break;
3735
3736         case AudioClock::MinSec:
3737                 /* XXX this is copied from show_verbose_duration_cursor() */
3738                 frame_rate = session->frame_rate();
3739                 hours = frame / (frame_rate * 3600);
3740                 frame = frame % (frame_rate * 3600);
3741                 mins = frame / (frame_rate * 60);
3742                 frame = frame % (frame_rate * 60);
3743                 secs = (float) frame / (float) frame_rate;
3744                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3745                 break;
3746
3747         default:
3748                 snprintf (buf, sizeof(buf), "%u", frame);
3749                 break;
3750         }
3751
3752         if (xpos >= 0 && ypos >=0) {
3753                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3754         }
3755         else {
3756                 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3757         }
3758         show_verbose_canvas_cursor ();
3759 }
3760
3761 void
3762 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos) 
3763 {
3764         char buf[128];
3765         SMPTE::Time smpte;
3766         BBT_Time sbbt;
3767         BBT_Time ebbt;
3768         int hours, mins;
3769         nframes_t distance, frame_rate;
3770         float secs;
3771         Meter meter_at_start(session->tempo_map().meter_at(start));
3772
3773         if (session == 0) {
3774                 return;
3775         }
3776
3777         switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3778         case AudioClock::BBT:
3779                 session->bbt_time (start, sbbt);
3780                 session->bbt_time (end, ebbt);
3781
3782                 /* subtract */
3783                 /* XXX this computation won't work well if the
3784                 user makes a selection that spans any meter changes.
3785                 */
3786
3787                 ebbt.bars -= sbbt.bars;
3788                 if (ebbt.beats >= sbbt.beats) {
3789                         ebbt.beats -= sbbt.beats;
3790                 } else {
3791                         ebbt.bars--;
3792                         ebbt.beats =  int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3793                 }
3794                 if (ebbt.ticks >= sbbt.ticks) {
3795                         ebbt.ticks -= sbbt.ticks;
3796                 } else {
3797                         ebbt.beats--;
3798                         ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3799                 }
3800                 
3801                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3802                 break;
3803                 
3804         case AudioClock::SMPTE:
3805                 session->smpte_duration (end - start, smpte);
3806                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3807                 break;
3808
3809         case AudioClock::MinSec:
3810                 /* XXX this stuff should be elsewhere.. */
3811                 distance = end - start;
3812                 frame_rate = session->frame_rate();
3813                 hours = distance / (frame_rate * 3600);
3814                 distance = distance % (frame_rate * 3600);
3815                 mins = distance / (frame_rate * 60);
3816                 distance = distance % (frame_rate * 60);
3817                 secs = (float) distance / (float) frame_rate;
3818                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3819                 break;
3820
3821         default:
3822                 snprintf (buf, sizeof(buf), "%u", end - start);
3823                 break;
3824         }
3825
3826         if (xpos >= 0 && ypos >=0) {
3827                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3828         }
3829         else {
3830                 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3831         }
3832         show_verbose_canvas_cursor ();
3833 }
3834
3835 void
3836 Editor::collect_new_region_view (RegionView* rv)
3837 {
3838         latest_regionview = rv;
3839 }
3840
3841 void
3842 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3843 {
3844         if (clicked_regionview == 0) {
3845                 return;
3846         }
3847
3848         /* lets try to create new Region for the selection */
3849
3850         vector<boost::shared_ptr<AudioRegion> > new_regions;
3851         create_region_from_selection (new_regions);
3852
3853         if (new_regions.empty()) {
3854                 return;
3855         }
3856
3857         /* XXX fix me one day to use all new regions */
3858         
3859         boost::shared_ptr<Region> region (new_regions.front());
3860
3861         /* add it to the current stream/playlist.
3862
3863            tricky: the streamview for the track will add a new regionview. we will
3864            catch the signal it sends when it creates the regionview to
3865            set the regionview we want to then drag.
3866         */
3867         
3868         latest_regionview = 0;
3869         sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3870         
3871         /* A selection grab currently creates two undo/redo operations, one for 
3872            creating the new region and another for moving it.
3873         */
3874
3875         begin_reversible_command (_("selection grab"));
3876
3877         boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
3878
3879         XMLNode *before = &(playlist->get_state());
3880         clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
3881         XMLNode *after = &(playlist->get_state());
3882         session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3883
3884         commit_reversible_command ();
3885         
3886         c.disconnect ();
3887         
3888         if (latest_regionview == 0) {
3889                 /* something went wrong */
3890                 return;
3891         }
3892
3893         /* we need to deselect all other regionviews, and select this one
3894            i'm ignoring undo stuff, because the region creation will take care of it */
3895         selection->set (latest_regionview);
3896         
3897         drag_info.item = latest_regionview->get_canvas_group();
3898         drag_info.data = latest_regionview;
3899         drag_info.motion_callback = &Editor::region_drag_motion_callback;
3900         drag_info.finished_callback = &Editor::region_drag_finished_callback;
3901
3902         start_grab (event);
3903         
3904         drag_info.last_trackview = clicked_axisview;
3905         drag_info.last_frame_position = latest_regionview->region()->position();
3906         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3907         
3908         show_verbose_time_cursor (drag_info.last_frame_position, 10);
3909 }
3910
3911 void
3912 Editor::cancel_selection ()
3913 {
3914         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3915                 (*i)->hide_selection ();
3916         }
3917         begin_reversible_command (_("cancel selection"));
3918         selection->clear ();
3919         clicked_selection = 0;
3920         commit_reversible_command ();
3921 }       
3922
3923 void
3924 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3925 {
3926         nframes_t start = 0;
3927         nframes_t end = 0;
3928
3929         if (session == 0) {
3930                 return;
3931         }
3932
3933         drag_info.item = item;
3934         drag_info.motion_callback = &Editor::drag_selection;
3935         drag_info.finished_callback = &Editor::end_selection_op;
3936
3937         selection_op = op;
3938
3939         switch (op) {
3940         case CreateSelection:
3941                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3942                         drag_info.copy = true;
3943                 } else {
3944                         drag_info.copy = false;
3945                 }
3946                 start_grab (event, selector_cursor);
3947                 break;
3948
3949         case SelectionStartTrim:
3950                 if (clicked_axisview) {
3951                         clicked_axisview->order_selection_trims (item, true);
3952                 } 
3953                 start_grab (event, trimmer_cursor);
3954                 start = selection->time[clicked_selection].start;
3955                 drag_info.pointer_frame_offset = drag_info.grab_frame - start;  
3956                 break;
3957                 
3958         case SelectionEndTrim:
3959                 if (clicked_axisview) {
3960                         clicked_axisview->order_selection_trims (item, false);
3961                 }
3962                 start_grab (event, trimmer_cursor);
3963                 end = selection->time[clicked_selection].end;
3964                 drag_info.pointer_frame_offset = drag_info.grab_frame - end;    
3965                 break;
3966
3967         case SelectionMove:
3968                 start = selection->time[clicked_selection].start;
3969                 start_grab (event);
3970                 drag_info.pointer_frame_offset = drag_info.grab_frame - start;  
3971                 break;
3972         }
3973
3974         if (selection_op == SelectionMove) {
3975                 show_verbose_time_cursor(start, 10);    
3976         } else {
3977                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
3978         }
3979 }
3980
3981 void
3982 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3983 {
3984         nframes_t start = 0;
3985         nframes_t end = 0;
3986         nframes_t length;
3987         nframes_t pending_position;
3988
3989         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3990                 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3991         } else {
3992                 pending_position = 0;
3993         }
3994         
3995         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3996                 snap_to (pending_position);
3997         }
3998
3999         /* only alter selection if the current frame is 
4000            different from the last frame position (adjusted)
4001          */
4002         
4003         if (pending_position == drag_info.last_pointer_frame) return;
4004         
4005         switch (selection_op) {
4006         case CreateSelection:
4007                 
4008                 if (drag_info.first_move) {
4009                         snap_to (drag_info.grab_frame);
4010                 }
4011                 
4012                 if (pending_position < drag_info.grab_frame) {
4013                         start = pending_position;
4014                         end = drag_info.grab_frame;
4015                 } else {
4016                         end = pending_position;
4017                         start = drag_info.grab_frame;
4018                 }
4019                 
4020                 /* first drag: Either add to the selection
4021                    or create a new selection->
4022                 */
4023                 
4024                 if (drag_info.first_move) {
4025                         
4026                         begin_reversible_command (_("range selection"));
4027                         
4028                         if (drag_info.copy) {
4029                                 /* adding to the selection */
4030                                 clicked_selection = selection->add (start, end);
4031                                 drag_info.copy = false;
4032                         } else {
4033                                 /* new selection-> */
4034                                 clicked_selection = selection->set (clicked_axisview, start, end);
4035                         }
4036                 } 
4037                 break;
4038                 
4039         case SelectionStartTrim:
4040                 
4041                 if (drag_info.first_move) {
4042                         begin_reversible_command (_("trim selection start"));
4043                 }
4044                 
4045                 start = selection->time[clicked_selection].start;
4046                 end = selection->time[clicked_selection].end;
4047
4048                 if (pending_position > end) {
4049                         start = end;
4050                 } else {
4051                         start = pending_position;
4052                 }
4053                 break;
4054                 
4055         case SelectionEndTrim:
4056                 
4057                 if (drag_info.first_move) {
4058                         begin_reversible_command (_("trim selection end"));
4059                 }
4060                 
4061                 start = selection->time[clicked_selection].start;
4062                 end = selection->time[clicked_selection].end;
4063
4064                 if (pending_position < start) {
4065                         end = start;
4066                 } else {
4067                         end = pending_position;
4068                 }
4069                 
4070                 break;
4071                 
4072         case SelectionMove:
4073                 
4074                 if (drag_info.first_move) {
4075                         begin_reversible_command (_("move selection"));
4076                 }
4077                 
4078                 start = selection->time[clicked_selection].start;
4079                 end = selection->time[clicked_selection].end;
4080                 
4081                 length = end - start;
4082                 
4083                 start = pending_position;
4084                 snap_to (start);
4085                 
4086                 end = start + length;
4087                 
4088                 break;
4089         }
4090         
4091         if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4092                 start_canvas_autoscroll (1);
4093         }
4094
4095         if (start != end) {
4096                 selection->replace (clicked_selection, start, end);
4097         }
4098
4099         drag_info.last_pointer_frame = pending_position;
4100         drag_info.first_move = false;
4101
4102         if (selection_op == SelectionMove) {
4103                 show_verbose_time_cursor(start, 10);    
4104         } else {
4105                 show_verbose_time_cursor(pending_position, 10); 
4106         }
4107 }
4108
4109 void
4110 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4111 {
4112         if (!drag_info.first_move) {
4113                 drag_selection (item, event);
4114                 /* XXX this is not object-oriented programming at all. ick */
4115                 if (selection->time.consolidate()) {
4116                         selection->TimeChanged ();
4117                 }
4118                 commit_reversible_command ();
4119         } else {
4120                 /* just a click, no pointer movement.*/
4121
4122                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4123
4124                         selection->clear_time();
4125
4126                 } 
4127         }
4128
4129         /* XXX what happens if its a music selection? */
4130         session->set_audio_range (selection->time);
4131         stop_canvas_autoscroll ();
4132 }
4133
4134 void
4135 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4136 {
4137         double speed = 1.0;
4138         TimeAxisView* tvp = clicked_axisview;
4139         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4140
4141         if (tv && tv->is_track()) {
4142                 speed = tv->get_diskstream()->speed();
4143         }
4144         
4145         nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
4146         nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
4147         nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
4148
4149         //drag_info.item = clicked_regionview->get_name_highlight();
4150         drag_info.item = item;
4151         drag_info.motion_callback = &Editor::trim_motion_callback;
4152         drag_info.finished_callback = &Editor::trim_finished_callback;
4153
4154         start_grab (event, trimmer_cursor);
4155         
4156         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4157                 trim_op = ContentsTrim;
4158         } else {
4159                 /* These will get overridden for a point trim.*/
4160                 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4161                         /* closer to start */
4162                         trim_op = StartTrim;
4163                 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4164                         /* closer to end */
4165                         trim_op = EndTrim;
4166                 }
4167         }
4168
4169         switch (trim_op) {
4170         case StartTrim:
4171                 show_verbose_time_cursor(region_start, 10);     
4172                 break;
4173         case EndTrim:
4174                 show_verbose_time_cursor(region_end, 10);       
4175                 break;
4176         case ContentsTrim:
4177                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4178                 break;
4179         }
4180 }
4181
4182 void
4183 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4184 {
4185         RegionView* rv = clicked_regionview;
4186         nframes_t frame_delta = 0;
4187         bool left_direction;
4188         bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4189
4190         /* snap modifier works differently here..
4191            its' current state has to be passed to the 
4192            various trim functions in order to work properly 
4193         */ 
4194
4195         double speed = 1.0;
4196         TimeAxisView* tvp = clicked_axisview;
4197         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4198         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4199
4200         if (tv && tv->is_track()) {
4201                 speed = tv->get_diskstream()->speed();
4202         }
4203         
4204         if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4205                 left_direction = true;
4206         } else {
4207                 left_direction = false;
4208         }
4209
4210         if (obey_snap) {
4211                 snap_to (drag_info.current_pointer_frame);
4212         }
4213
4214         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4215                 return;
4216         }
4217
4218         if (drag_info.first_move) {
4219         
4220                 string trim_type;
4221
4222                 switch (trim_op) {
4223                 case StartTrim:
4224                         trim_type = "Region start trim";
4225                         break;
4226                 case EndTrim:
4227                         trim_type = "Region end trim";
4228                         break;
4229                 case ContentsTrim:
4230                         trim_type = "Region content trim";
4231                         break;
4232                 }
4233
4234                 begin_reversible_command (trim_type);
4235
4236                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4237                         (*i)->fake_set_opaque(false);
4238                         (*i)->region()->freeze ();
4239                 
4240                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4241                         if (arv)
4242                                 arv->temporarily_hide_envelope ();
4243
4244                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4245                         insert_result = motion_frozen_playlists.insert (pl);
4246                         if (insert_result.second) {
4247                                 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4248                         }
4249                 }
4250         }
4251
4252         if (left_direction) {
4253                 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4254         } else {
4255                 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4256         }
4257
4258         switch (trim_op) {              
4259         case StartTrim:
4260                 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4261                         break;
4262                 } else {
4263                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4264                                 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4265                         }
4266                         break;
4267                 }
4268                 
4269         case EndTrim:
4270                 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4271                         break;
4272                 } else {
4273                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4274                                 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4275                         }
4276                         break;
4277                 }
4278                 
4279         case ContentsTrim:
4280                 {
4281                         bool swap_direction = false;
4282
4283                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4284                                 swap_direction = true;
4285                         }
4286                         
4287                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4288                              i != selection->regions.by_layer().end(); ++i)
4289                         {
4290                                 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4291                         }
4292                 }
4293                 break;
4294         }
4295
4296         switch (trim_op) {
4297         case StartTrim:
4298                 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);     
4299                 break;
4300         case EndTrim:
4301                 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);   
4302                 break;
4303         case ContentsTrim:
4304                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4305                 break;
4306         }
4307
4308         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4309         drag_info.first_move = false;
4310 }
4311
4312 void
4313 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4314 {
4315         boost::shared_ptr<Region> region (rv.region());
4316
4317         if (region->locked()) {
4318                 return;
4319         }
4320
4321         nframes_t new_bound;
4322
4323         double speed = 1.0;
4324         TimeAxisView* tvp = clicked_axisview;
4325         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4326
4327         if (tv && tv->is_track()) {
4328                 speed = tv->get_diskstream()->speed();
4329         }
4330         
4331         if (left_direction) {
4332                 if (swap_direction) {
4333                         new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4334                 } else {
4335                         new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4336                 }
4337         } else {
4338                 if (swap_direction) {
4339                         new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4340                 } else {
4341                         new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4342                 }
4343         }
4344
4345         if (obey_snap) {
4346                 snap_to (new_bound);
4347         }
4348         region->trim_start ((nframes_t) (new_bound * speed), this);     
4349         rv.region_changed (StartChanged);
4350 }
4351
4352 void
4353 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4354 {
4355         boost::shared_ptr<Region> region (rv.region()); 
4356
4357         if (region->locked()) {
4358                 return;
4359         }
4360
4361         nframes_t new_bound;
4362
4363         double speed = 1.0;
4364         TimeAxisView* tvp = clicked_axisview;
4365         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4366
4367         if (tv && tv->is_track()) {
4368                 speed = tv->get_diskstream()->speed();
4369         }
4370         
4371         if (left_direction) {
4372                 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4373         } else {
4374                 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4375         }
4376
4377         if (obey_snap) {
4378                 snap_to (new_bound, (left_direction ? 0 : 1));  
4379         }
4380
4381         region->trim_front ((nframes_t) (new_bound * speed), this);
4382
4383         rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4384 }
4385
4386 void
4387 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4388 {
4389         boost::shared_ptr<Region> region (rv.region());
4390
4391         if (region->locked()) {
4392                 return;
4393         }
4394
4395         nframes_t new_bound;
4396
4397         double speed = 1.0;
4398         TimeAxisView* tvp = clicked_axisview;
4399         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4400
4401         if (tv && tv->is_track()) {
4402                 speed = tv->get_diskstream()->speed();
4403         }
4404         
4405         if (left_direction) {
4406                 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4407         } else {
4408                 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4409         }
4410
4411         if (obey_snap) {
4412                 snap_to (new_bound);
4413         }
4414         region->trim_end ((nframes_t) (new_bound * speed), this);
4415         rv.region_changed (LengthChanged);
4416 }
4417         
4418 void
4419 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4420 {
4421         if (!drag_info.first_move) {
4422                 trim_motion_callback (item, event);
4423                 
4424                 if (!clicked_regionview->get_selected()) {
4425                         thaw_region_after_trim (*clicked_regionview);           
4426                 } else {
4427                         
4428                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4429                              i != selection->regions.by_layer().end(); ++i)
4430                         {
4431                                 thaw_region_after_trim (**i);
4432                                 (*i)->fake_set_opaque (true);
4433                         }
4434                 }
4435                 
4436                 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4437                         //(*p)->thaw ();
4438                         session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4439                 }
4440                 
4441                 motion_frozen_playlists.clear ();
4442
4443                 commit_reversible_command();
4444         } else {
4445                 /* no mouse movement */
4446                 point_trim (event);
4447         }
4448 }
4449
4450 void
4451 Editor::point_trim (GdkEvent* event)
4452 {
4453         RegionView* rv = clicked_regionview;
4454         nframes_t new_bound = drag_info.current_pointer_frame;
4455
4456         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4457                 snap_to (new_bound);
4458         }
4459
4460         /* Choose action dependant on which button was pressed */
4461         switch (event->button.button) {
4462         case 1:
4463                 trim_op = StartTrim;
4464                 begin_reversible_command (_("Start point trim"));
4465
4466                 if (rv->get_selected()) {
4467
4468                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4469                              i != selection->regions.by_layer().end(); ++i)
4470                         {
4471                                 if (!(*i)->region()->locked()) {
4472                                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4473                                         XMLNode &before = pl->get_state();
4474                                         (*i)->region()->trim_front (new_bound, this);   
4475                                         XMLNode &after = pl->get_state();
4476                                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4477                                 }
4478                         }
4479
4480                 } else {
4481
4482                         if (!rv->region()->locked()) {
4483                                 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4484                                 XMLNode &before = pl->get_state();
4485                                 rv->region()->trim_front (new_bound, this);     
4486                                 XMLNode &after = pl->get_state();
4487                                 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4488                         }
4489                 }
4490
4491                 commit_reversible_command();
4492         
4493                 break;
4494         case 2:
4495                 trim_op = EndTrim;
4496                 begin_reversible_command (_("End point trim"));
4497
4498                 if (rv->get_selected()) {
4499                         
4500                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4501                         {
4502                                 if (!(*i)->region()->locked()) {
4503                                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4504                                         XMLNode &before = pl->get_state();
4505                                         (*i)->region()->trim_end (new_bound, this);
4506                                         XMLNode &after = pl->get_state();
4507                                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4508                                 }
4509                         }
4510
4511                 } else {
4512
4513                         if (!rv->region()->locked()) {
4514                                 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4515                                 XMLNode &before = pl->get_state();
4516                                 rv->region()->trim_end (new_bound, this);
4517                                 XMLNode &after = pl->get_state();
4518                                 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4519                         }
4520                 }
4521
4522                 commit_reversible_command();
4523         
4524                 break;
4525         default:
4526                 break;
4527         }
4528 }
4529
4530 void
4531 Editor::thaw_region_after_trim (RegionView& rv)
4532 {
4533         boost::shared_ptr<Region> region (rv.region());
4534
4535         if (region->locked()) {
4536                 return;
4537         }
4538
4539         region->thaw (_("trimmed region"));
4540         XMLNode &after = region->playlist()->get_state();
4541         session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4542
4543         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4544         if (arv)
4545                 arv->unhide_envelope ();
4546 }
4547
4548 void
4549 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4550 {
4551         Marker* marker;
4552         bool is_start;
4553
4554         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4555                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4556                 /*NOTREACHED*/
4557         }
4558
4559         Location* location = find_location_from_marker (marker, is_start);      
4560         location->set_hidden (true, this);
4561 }
4562
4563
4564 void
4565 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4566 {
4567         if (session == 0) {
4568                 return;
4569         }
4570
4571         drag_info.item = item;
4572         drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4573         drag_info.finished_callback = &Editor::end_range_markerbar_op;
4574
4575         range_marker_op = op;
4576
4577         if (!temp_location) {
4578                 temp_location = new Location;
4579         }
4580         
4581         switch (op) {
4582         case CreateRangeMarker:
4583         case CreateTransportMarker:
4584                 
4585                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4586                         drag_info.copy = true;
4587                 } else {
4588                         drag_info.copy = false;
4589                 }
4590                 start_grab (event, selector_cursor);
4591                 break;
4592         }
4593
4594         show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4595         
4596 }
4597
4598 void
4599 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4600 {
4601         nframes_t start = 0;
4602         nframes_t end = 0;
4603         ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4604         
4605         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4606                 snap_to (drag_info.current_pointer_frame);
4607         }
4608
4609         /* only alter selection if the current frame is 
4610            different from the last frame position.
4611          */
4612         
4613         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4614         
4615         switch (range_marker_op) {
4616         case CreateRangeMarker:
4617         case CreateTransportMarker:
4618                 if (drag_info.first_move) {
4619                         snap_to (drag_info.grab_frame);
4620                 }
4621                 
4622                 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4623                         start = drag_info.current_pointer_frame;
4624                         end = drag_info.grab_frame;
4625                 } else {
4626                         end = drag_info.current_pointer_frame;
4627                         start = drag_info.grab_frame;
4628                 }
4629                 
4630                 /* first drag: Either add to the selection
4631                    or create a new selection.
4632                 */
4633                 
4634                 if (drag_info.first_move) {
4635                         
4636                         temp_location->set (start, end);
4637                         
4638                         crect->show ();
4639
4640                         update_marker_drag_item (temp_location);
4641                         range_marker_drag_rect->show();
4642                         range_marker_drag_rect->raise_to_top();
4643                         
4644                 } 
4645                 break;          
4646         }
4647         
4648         if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4649                 start_canvas_autoscroll (1);
4650         }
4651         
4652         if (start != end) {
4653                 temp_location->set (start, end);
4654
4655                 double x1 = frame_to_pixel (start);
4656                 double x2 = frame_to_pixel (end);
4657                 crect->property_x1() = x1;
4658                 crect->property_x2() = x2;
4659
4660                 update_marker_drag_item (temp_location);
4661         }
4662
4663         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4664         drag_info.first_move = false;
4665
4666         show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4667         
4668 }
4669
4670 void
4671 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4672 {
4673         Location * newloc = 0;
4674         string rangename;
4675         
4676         if (!drag_info.first_move) {
4677                 drag_range_markerbar_op (item, event);
4678
4679                 switch (range_marker_op) {
4680                 case CreateRangeMarker:
4681                     {
4682                         begin_reversible_command (_("new range marker"));
4683                         XMLNode &before = session->locations()->get_state();
4684                         session->locations()->next_available_name(rangename,"unnamed");
4685                         newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4686                         session->locations()->add (newloc, true);
4687                         XMLNode &after = session->locations()->get_state();
4688                         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4689                         commit_reversible_command ();
4690                         
4691                         range_bar_drag_rect->hide();
4692                         range_marker_drag_rect->hide();
4693                         break;
4694                     }
4695
4696                 case CreateTransportMarker:
4697                         // popup menu to pick loop or punch
4698                         new_transport_marker_context_menu (&event->button, item);
4699                         
4700                         break;
4701                 }
4702         } else {
4703                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4704
4705                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4706
4707                         nframes_t start;
4708                         nframes_t end;
4709
4710                         start = session->locations()->first_mark_before (drag_info.grab_frame);
4711                         end = session->locations()->first_mark_after (drag_info.grab_frame);
4712                         
4713                         if (end == max_frames) {
4714                                 end = session->current_end_frame ();
4715                         }
4716
4717                         if (start == 0) {
4718                                 start = session->current_start_frame ();
4719                         }
4720
4721                         switch (mouse_mode) {
4722                         case MouseObject:
4723                                 /* find the two markers on either side and then make the selection from it */
4724                                 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4725                                 break;
4726
4727                         case MouseRange:
4728                                 /* find the two markers on either side of the click and make the range out of it */
4729                                 selection->set (0, start, end);
4730                                 break;
4731
4732                         default:
4733                                 break;
4734                         }
4735                 } 
4736         }
4737
4738         stop_canvas_autoscroll ();
4739 }
4740
4741
4742
4743 void
4744 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4745 {
4746         drag_info.item = item;
4747         drag_info.motion_callback = &Editor::drag_mouse_zoom;
4748         drag_info.finished_callback = &Editor::end_mouse_zoom;
4749
4750         start_grab (event, zoom_cursor);
4751
4752         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4753 }
4754
4755 void
4756 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4757 {
4758         nframes_t start;
4759         nframes_t end;
4760
4761         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4762                 snap_to (drag_info.current_pointer_frame);
4763                 
4764                 if (drag_info.first_move) {
4765                         snap_to (drag_info.grab_frame);
4766                 }
4767         }
4768                 
4769         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4770
4771         /* base start and end on initial click position */
4772         if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4773                 start = drag_info.current_pointer_frame;
4774                 end = drag_info.grab_frame;
4775         } else {
4776                 end = drag_info.current_pointer_frame;
4777                 start = drag_info.grab_frame;
4778         }
4779         
4780         if (start != end) {
4781
4782                 if (drag_info.first_move) {
4783                         zoom_rect->show();
4784                         zoom_rect->raise_to_top();
4785                 }
4786
4787                 reposition_zoom_rect(start, end);
4788
4789                 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4790                 drag_info.first_move = false;
4791
4792                 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4793         }
4794 }
4795
4796 void
4797 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4798 {
4799         if (!drag_info.first_move) {
4800                 drag_mouse_zoom (item, event);
4801                 
4802                 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4803                         temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4804                 } else {
4805                         temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4806                 }               
4807         } else {
4808                 temporal_zoom_to_frame (false, drag_info.grab_frame);
4809                 /*
4810                 temporal_zoom_step (false);
4811                 center_screen (drag_info.grab_frame);
4812                 */
4813         }
4814
4815         zoom_rect->hide();
4816 }
4817
4818 void
4819 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4820 {
4821         double x1 = frame_to_pixel (start);
4822         double x2 = frame_to_pixel (end);
4823         double y2 = full_canvas_height - 1.0;
4824
4825         zoom_rect->property_x1() = x1;
4826         zoom_rect->property_y1() = 1.0;
4827         zoom_rect->property_x2() = x2;
4828         zoom_rect->property_y2() = y2;
4829 }
4830
4831 void
4832 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4833 {
4834         drag_info.item = item;
4835         drag_info.motion_callback = &Editor::drag_rubberband_select;
4836         drag_info.finished_callback = &Editor::end_rubberband_select;
4837
4838         start_grab (event, cross_hair_cursor);
4839
4840         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4841 }
4842
4843 void
4844 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4845 {
4846         nframes_t start;
4847         nframes_t end;
4848         double y1;
4849         double y2;
4850
4851         /* use a bigger drag threshold than the default */
4852
4853         if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4854                 return;
4855         }
4856
4857         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4858                 if (drag_info.first_move) {
4859                         snap_to (drag_info.grab_frame);
4860                 } 
4861                 snap_to (drag_info.current_pointer_frame);
4862         }
4863
4864         /* base start and end on initial click position */
4865
4866         if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4867                 start = drag_info.current_pointer_frame;
4868                 end = drag_info.grab_frame;
4869         } else {
4870                 end = drag_info.current_pointer_frame;
4871                 start = drag_info.grab_frame;
4872         }
4873
4874         if (drag_info.current_pointer_y < drag_info.grab_y) {
4875                 y1 = drag_info.current_pointer_y;
4876                 y2 = drag_info.grab_y;
4877         } else {
4878                 y2 = drag_info.current_pointer_y;
4879                 y1 = drag_info.grab_y;
4880         }
4881
4882         
4883         if (start != end || y1 != y2) {
4884
4885                 double x1 = frame_to_pixel (start);
4886                 double x2 = frame_to_pixel (end);
4887                 
4888                 rubberband_rect->property_x1() = x1;
4889                 rubberband_rect->property_y1() = y1;
4890                 rubberband_rect->property_x2() = x2;
4891                 rubberband_rect->property_y2() = y2;
4892
4893                 rubberband_rect->show();
4894                 rubberband_rect->raise_to_top();
4895                 
4896                 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4897                 drag_info.first_move = false;
4898
4899                 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4900         }
4901 }
4902
4903 void
4904 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4905 {
4906         if (!drag_info.first_move) {
4907
4908                 drag_rubberband_select (item, event);
4909
4910                 double y1,y2;
4911                 if (drag_info.current_pointer_y < drag_info.grab_y) {
4912                         y1 = drag_info.current_pointer_y;
4913                         y2 = drag_info.grab_y;
4914                 }
4915                 else {
4916                         y2 = drag_info.current_pointer_y;
4917                         y1 = drag_info.grab_y;
4918                 }
4919
4920
4921                 Selection::Operation op = Keyboard::selection_type (event->button.state);
4922                 bool commit;
4923
4924                 begin_reversible_command (_("rubberband selection"));
4925
4926                 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4927                         commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
4928                 } else {
4929                         commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
4930                 }               
4931
4932                 if (commit) {
4933                         commit_reversible_command ();
4934                 }
4935                 
4936         } else {
4937                 selection->clear_tracks();
4938                 selection->clear_regions();
4939                 selection->clear_points ();
4940                 selection->clear_lines ();
4941         }
4942
4943         rubberband_rect->hide();
4944 }
4945
4946
4947 gint
4948 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4949 {
4950         using namespace Gtkmm2ext;
4951
4952         ArdourPrompter prompter (false);
4953
4954         prompter.set_prompt (_("Name for region:"));
4955         prompter.set_initial_text (clicked_regionview->region()->name());
4956         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4957         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4958         prompter.show_all ();
4959         switch (prompter.run ()) {
4960         case Gtk::RESPONSE_ACCEPT:
4961                 string str;
4962                 prompter.get_result(str);
4963                 if (str.length()) {
4964                         clicked_regionview->region()->set_name (str);
4965                 }
4966                 break;
4967         }
4968         return true;
4969 }
4970
4971 void
4972 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4973 {
4974         drag_info.item = item;
4975         drag_info.motion_callback = &Editor::time_fx_motion;
4976         drag_info.finished_callback = &Editor::end_time_fx;
4977
4978         start_grab (event);
4979
4980         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4981 }
4982
4983 void
4984 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4985 {
4986         RegionView* rv = clicked_regionview;
4987
4988         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4989                 snap_to (drag_info.current_pointer_frame);
4990         }
4991
4992         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4993                 return;
4994         }
4995
4996         if (drag_info.current_pointer_frame > rv->region()->position()) {
4997                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4998         }
4999
5000         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5001         drag_info.first_move = false;
5002
5003         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5004 }
5005
5006 void
5007 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5008 {
5009         clicked_regionview->get_time_axis_view().hide_timestretch ();
5010
5011         if (drag_info.first_move) {
5012                 return;
5013         }
5014
5015         if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5016                 /* backwards drag of the left edge - not usable */
5017                 return;
5018         }
5019         
5020         nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5021         float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5022         
5023         begin_reversible_command (_("timestretch"));
5024
5025         if (run_timestretch (selection->regions, percentage) == 0) {
5026                 session->commit_reversible_command ();
5027         }
5028 }
5029
5030 void
5031 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
5032 {
5033         /* no brushing without a useful snap setting */
5034
5035         // FIXME
5036         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
5037         assert(arv);
5038
5039         switch (snap_mode) {
5040         case SnapMagnetic:
5041                 return; /* can't work because it allows region to be placed anywhere */
5042         default:
5043                 break; /* OK */
5044         }
5045
5046         switch (snap_type) {
5047         case SnapToFrame:
5048         case SnapToMark:
5049         case SnapToEditCursor:
5050                 return;
5051
5052         default:
5053                 break;
5054         }
5055
5056         /* don't brush a copy over the original */
5057         
5058         if (pos == rv->region()->position()) {
5059                 return;
5060         }
5061
5062         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
5063
5064         if (rtv == 0 || !rtv->is_track()) {
5065                 return;
5066         }
5067
5068         boost::shared_ptr<Playlist> playlist = rtv->playlist();
5069         double speed = rtv->get_diskstream()->speed();
5070         
5071         XMLNode &before = playlist->get_state();
5072         playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
5073         XMLNode &after = playlist->get_state();
5074         session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5075         
5076         // playlist is frozen, so we have to update manually
5077         
5078         playlist->Modified(); /* EMIT SIGNAL */
5079 }
5080
5081 gint
5082 Editor::track_height_step_timeout ()
5083 {
5084         struct timeval now;
5085         struct timeval delta;
5086         
5087         gettimeofday (&now, 0);
5088         timersub (&now, &last_track_height_step_timestamp, &delta);
5089         
5090         if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
5091                 current_stepping_trackview = 0;
5092                 return false;
5093         }
5094         return true;
5095 }
5096
5097
5098 void
5099 Editor::add_mouse_speed (double speed, double dir)
5100 {
5101         size_t index;
5102
5103         mouse_direction = dir;
5104
5105         index = mouse_speed_entries;
5106
5107         if (++index >= mouse_speed_size) {
5108                 index = 0;
5109                 have_full_mouse_speed = true;
5110         }
5111         
5112         mouse_speed[index] = speed;
5113         mouse_speed_entries = index;
5114 }
5115
5116 double
5117 Editor::compute_mouse_speed ()
5118 {
5119         double total = 0;
5120
5121         if (!have_full_mouse_speed) {
5122
5123                 /* partial speed buffer, just use whatever we have so far */
5124
5125                 if (mouse_speed_entries == 0 ) {
5126                         return 0.0;
5127                 }
5128
5129                 for (size_t n = 0; n < mouse_speed_entries; ++n) {
5130                         total += mouse_speed[n];
5131                 }
5132                 
5133                 return mouse_direction * total/mouse_speed_entries;
5134         }
5135
5136         /* compute the average (effectively low-pass filtering) mouse speed
5137            across the entire buffer.
5138         */
5139
5140         for (size_t n = 0; n < mouse_speed_size; ++n) {
5141                 total += mouse_speed[n];
5142         }
5143
5144
5145         return mouse_direction * total/mouse_speed_size;
5146 }
5147
5148 bool
5149 Editor::update_mouse_speed ()
5150 {
5151         double speed;
5152
5153         if (!_scrubbing) {
5154                 session->request_transport_speed (0.0);
5155                 mouse_speed_update = -1;
5156                 return false;
5157         }
5158
5159         speed = compute_mouse_speed ();
5160
5161         struct timeval tmnow;
5162         
5163         gettimeofday (&tmnow, 0);
5164         double now = (tmnow.tv_sec * 1000000.0) + tmnow.tv_usec;
5165
5166         if (now - last_scrub_time > 250000) {
5167             
5168                 // 0.25 seconds since last mouse motion, start to brake
5169
5170                 if (fabs (speed) < 0.1) {
5171                         /* don't asymptotically approach zero */
5172                         memset (mouse_speed, 0, sizeof (double) * mouse_speed_size);
5173                         speed = 0.0;
5174                 } else if (fabs (speed) < 0.25) {
5175                         add_mouse_speed (fabs (speed * 0.2), mouse_direction);
5176                 } else {
5177                         add_mouse_speed (fabs (speed * 0.6), mouse_direction);
5178                 }
5179         } 
5180
5181         session->request_transport_speed (speed);
5182         return _scrubbing;
5183 }