use filechooser widget in export dialog, selected files set format combos, hide progr...
[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
2902                         RegionView* rv;
2903                         RegionView* nrv;
2904
2905                         rv = (*i);
2906
2907                         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
2908                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
2909
2910                         if (arv) {
2911                                 nrv = new AudioRegionView (*arv);
2912                         } else if (mrv) {
2913                                 nrv = new MidiRegionView (*mrv);
2914                         } else {
2915                                 continue;
2916                         }
2917
2918                         nrv->get_canvas_group()->show ();
2919
2920                         new_regionviews.push_back (nrv);
2921                 }
2922
2923                 if (new_regionviews.empty()) {
2924                         return;
2925                 }
2926
2927                 /* reset selection to new regionviews */
2928
2929                 selection->set (new_regionviews);
2930                 
2931                 /* reset drag_info data to reflect the fact that we are dragging the copies */
2932                 
2933                 drag_info.data = new_regionviews.front();
2934
2935                 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2936         }
2937
2938         /* Which trackview is this ? */
2939
2940         TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2941         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2942
2943         /* The region motion is only processed if the pointer is over
2944            an audio track.
2945         */
2946         
2947         if (!tv || !tv->is_track()) {
2948                 /* To make sure we hide the verbose canvas cursor when the mouse is 
2949                    not held over a track. 
2950                 */
2951                 hide_verbose_canvas_cursor ();
2952                 return;
2953         }
2954         
2955         original_pointer_order = drag_info.last_trackview->order;
2956                 
2957         /************************************************************
2958              Y-Delta Computation
2959         ************************************************************/   
2960
2961         if (drag_info.brushing) {
2962                 clamp_y_axis = true;
2963                 pointer_y_span = 0;
2964                 goto y_axis_done;
2965         }
2966         
2967         if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2968
2969                 int32_t children = 0, numtracks = 0;
2970                 // XXX hard coding track limit, oh my, so very very bad
2971                 bitset <1024> tracks (0x00);
2972                 /* get a bitmask representing the visible tracks */
2973
2974                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2975                         TimeAxisView *tracklist_timeview;
2976                         tracklist_timeview = (*i);
2977                         RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
2978                         TimeAxisView::Children children_list;
2979               
2980                         /* zeroes are audio tracks. ones are other types. */
2981               
2982                         if (!rtv2->hidden()) {
2983                                 
2984                                 if (visible_y_high < rtv2->order) {
2985                                         visible_y_high = rtv2->order;
2986                                 }
2987                                 if (visible_y_low > rtv2->order) {
2988                                         visible_y_low = rtv2->order;
2989                                 }
2990                 
2991                                 if (!rtv2->is_track()) {                                  
2992                                         tracks = tracks |= (0x01 << rtv2->order);
2993                                 }
2994         
2995                                 height_list[rtv2->order] = (*i)->height;
2996                                 children = 1;
2997                                 if ((children_list = rtv2->get_child_list()).size() > 0) {
2998                                         for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) { 
2999                                                 tracks = tracks |= (0x01 << (rtv2->order + children));
3000                                                 height_list[rtv2->order + children] =  (*j)->height;                
3001                                                 numtracks++;
3002                                                 children++;     
3003                                         }
3004                                 }
3005                                 numtracks++;        
3006                         }
3007                 }
3008                 /* find the actual span according to the canvas */
3009
3010                 canvas_pointer_y_span = pointer_y_span;
3011                 if (drag_info.last_trackview->order >= tv->order) {
3012                         int32_t y;
3013                         for (y = tv->order; y < drag_info.last_trackview->order; y++) {
3014                                 if (height_list[y] == 0 ) {
3015                                         canvas_pointer_y_span--;
3016                                 }
3017                         }
3018                 } else {
3019                         int32_t y;
3020                         for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
3021                                 if (    height_list[y] == 0 ) {
3022                                         canvas_pointer_y_span++;
3023                                 }
3024                         }
3025                 }
3026
3027                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3028                         RegionView* rv2 = (*i);
3029                         double ix1, ix2, iy1, iy2;
3030                         int32_t n = 0;
3031
3032                         rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3033                         rv2->get_canvas_group()->i2w (ix1, iy1);
3034                         TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3035                         RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3036
3037                         if (rtv2->order != original_pointer_order) {    
3038                                 /* this isn't the pointer track */      
3039
3040                                 if (canvas_pointer_y_span > 0) {
3041
3042                                         /* moving up the canvas */
3043                                         if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
3044         
3045                                                 int32_t visible_tracks = 0;
3046                                                 while (visible_tracks < canvas_pointer_y_span ) {
3047                                                         visible_tracks++;
3048                   
3049                                                         while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3050                                                                 /* we're passing through a hidden track */
3051                                                                 n--;
3052                                                         }                 
3053                                                 }
3054                  
3055                                                 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {                  
3056                                                         clamp_y_axis = true;
3057                                                 }
3058                     
3059                                         } else {
3060                                                 clamp_y_axis = true;
3061                                         }                 
3062                   
3063                                 } else if (canvas_pointer_y_span < 0) {
3064
3065                                         /*moving down the canvas*/
3066
3067                                         if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3068                     
3069                     
3070                                                 int32_t visible_tracks = 0;
3071                     
3072                                                 while (visible_tracks > canvas_pointer_y_span ) {
3073                                                         visible_tracks--;
3074                       
3075                                                         while (height_list[rtv2->order - (visible_tracks - n)] == 0) {             
3076                                                                 n++;
3077                                                         }                
3078                                                 }
3079                                                 if (  tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3080                                                         clamp_y_axis = true;
3081                             
3082                                                 }
3083                                         } else {
3084                           
3085                                                 clamp_y_axis = true;
3086                                         }
3087                                 }               
3088                   
3089                         } else {
3090                       
3091                                 /* this is the pointer's track */
3092                                 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow 
3093                                         clamp_y_axis = true;
3094                                 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3095                                         clamp_y_axis = true;
3096                                 }
3097                         }             
3098                         if (clamp_y_axis) {
3099                                 break;
3100                         }
3101                 }
3102
3103         } else  if (drag_info.last_trackview == tv) {
3104                 clamp_y_axis = true;
3105         }         
3106
3107   y_axis_done:
3108         if (!clamp_y_axis) {
3109                 drag_info.last_trackview = tv;        
3110         }
3111           
3112         /************************************************************
3113             X DELTA COMPUTATION
3114         ************************************************************/
3115
3116         /* compute the amount of pointer motion in frames, and where
3117            the region would be if we moved it by that much.
3118         */
3119
3120         if (drag_info.move_threshold_passed) {
3121
3122                 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3123
3124                         nframes_t sync_frame;
3125                         nframes_t sync_offset;
3126                         int32_t sync_dir;
3127             
3128                         pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3129
3130                         sync_offset = rv->region()->sync_offset (sync_dir);
3131                         sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3132
3133                         /* we snap if the snap modifier is not enabled.
3134                          */
3135             
3136                         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3137                                 snap_to (sync_frame);   
3138                         }
3139             
3140                         if (sync_frame - sync_offset <= sync_frame) {
3141                                 pending_region_position = sync_frame - (sync_dir*sync_offset);
3142                         } else {
3143                                 pending_region_position = 0;
3144                         }
3145             
3146                 } else {
3147                         pending_region_position = 0;
3148                 }
3149           
3150                 if (pending_region_position > max_frames - rv->region()->length()) {
3151                         pending_region_position = drag_info.last_frame_position;
3152                 }
3153           
3154                 // printf ("3: pending_region_position= %lu    %lu\n", pending_region_position, drag_info.last_frame_position );
3155           
3156                 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3157
3158                         /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3159                            to make it appear at the new location.
3160                         */
3161
3162                         if (pending_region_position > drag_info.last_frame_position) {
3163                                 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3164                         } else {
3165                                 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3166                         }
3167
3168                         drag_info.last_frame_position = pending_region_position;
3169             
3170                 } else {
3171                         x_delta = 0;
3172                 }
3173
3174         } else {
3175                 /* threshold not passed */
3176
3177                 x_delta = 0;
3178         }
3179
3180         /*************************************************************
3181             PREPARE TO MOVE
3182         ************************************************************/
3183
3184         if (x_delta == 0 && (pointer_y_span == 0)) {
3185                 /* haven't reached next snap point, and we're not switching
3186                    trackviews. nothing to do.
3187                 */
3188                 return;
3189         } 
3190
3191
3192         if (x_delta < 0) {
3193                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3194
3195                         RegionView* rv2 = (*i);
3196
3197                         // If any regionview is at zero, we need to know so we can stop further leftward motion.
3198                         
3199                         double ix1, ix2, iy1, iy2;
3200                         rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3201                         rv2->get_canvas_group()->i2w (ix1, iy1);
3202
3203                         if (ix1 <= 1) {
3204                                 x_delta = 0;
3205                                 break;
3206                         }
3207                 }
3208         }
3209
3210         /*************************************************************
3211             MOTION                                                                    
3212         ************************************************************/
3213
3214         bool do_move;
3215
3216         if (drag_info.first_move) {
3217                 if (drag_info.move_threshold_passed) {
3218                         do_move = true;
3219                 } else {
3220                         do_move = false;
3221                 }
3222         } else {
3223                 do_move = true;
3224         }
3225
3226         if (do_move) {
3227
3228                 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3229                 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3230
3231                 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3232             
3233                         RegionView* rv = (*i);
3234                         double ix1, ix2, iy1, iy2;
3235                         int32_t temp_pointer_y_span = pointer_y_span;
3236
3237                         /* get item BBox, which will be relative to parent. so we have
3238                            to query on a child, then convert to world coordinates using
3239                            the parent.
3240                         */
3241
3242                         rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3243                         rv->get_canvas_group()->i2w (ix1, iy1);
3244                         TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3245                         RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3246                         RouteTimeAxisView* temp_rtv;
3247
3248                         if ((pointer_y_span != 0) && !clamp_y_axis) {
3249                                 y_delta = 0;
3250                                 int32_t x = 0;
3251                                 for (j = height_list.begin(); j!= height_list.end(); j++) {     
3252                                         if (x == canvas_rtv->order) {
3253                                                 /* we found the track the region is on */
3254                                                 if (x != original_pointer_order) {
3255                                                         /*this isn't from the same track we're dragging from */
3256                                                         temp_pointer_y_span = canvas_pointer_y_span;
3257                                                 }                 
3258                                                 while (temp_pointer_y_span > 0) {
3259                                                         /* we're moving up canvas-wise,
3260                                                            so  we need to find the next track height
3261                                                         */
3262                                                         if (j != height_list.begin()) {           
3263                                                                 j--;
3264                                                         }
3265                                                         if (x != original_pointer_order) { 
3266                                                                 /* we're not from the dragged track, so ignore hidden tracks. */              
3267                                                                 if ((*j) == 0) {
3268                                                                         temp_pointer_y_span++;
3269                                                                 }
3270                                                         }          
3271                                                         y_delta -= (*j);        
3272                                                         temp_pointer_y_span--;  
3273                                                 }
3274                                                 while (temp_pointer_y_span < 0) {                 
3275                                                         y_delta += (*j);
3276                                                         if (x != original_pointer_order) { 
3277                                                                 if ((*j) == 0) {
3278                                                                         temp_pointer_y_span--;
3279                                                                 }
3280                                                         }          
3281                     
3282                                                         if (j != height_list.end()) {                 
3283                                                                 j++;
3284                                                         }
3285                                                         temp_pointer_y_span++;
3286                                                 }
3287                                                 /* find out where we'll be when we move and set height accordingly */
3288                   
3289                                                 tvp2 = trackview_by_y_position (iy1 + y_delta);
3290                                                 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3291                                                 rv->set_y_position_and_height (0, temp_rtv->height);
3292
3293                                                 /*   if you un-comment the following, the region colours will follow the track colours whilst dragging,
3294                                                      personally, i think this can confuse things, but never mind.
3295                                                 */
3296                                   
3297                                                 //const GdkColor& col (temp_rtv->view->get_region_color());
3298                                                 //rv->set_color (const_cast<GdkColor&>(col));
3299                                                 break;          
3300                                         }
3301                                         x++;
3302                                 }
3303                         }
3304           
3305                         /* prevent the regionview from being moved to before 
3306                            the zero position on the canvas.
3307                         */
3308                         /* clamp */
3309                 
3310                         if (x_delta < 0) {
3311                                 if (-x_delta > ix1) {
3312                                         x_delta = -ix1;
3313                                 }
3314                         } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3315                                 x_delta = max_frames - rv->region()->last_frame();
3316                         }
3317
3318
3319                         if (drag_info.first_move) {
3320
3321                                 /* hide any dependent views */
3322                         
3323                                 rv->get_time_axis_view().hide_dependent_views (*rv);
3324                         
3325                                 /* this is subtle. raising the regionview itself won't help,
3326                                    because raise_to_top() just puts the item on the top of
3327                                    its parent's stack. so, we need to put the trackview canvas_display group
3328                                    on the top, since its parent is the whole canvas.
3329                                 */
3330                         
3331                                 rv->get_canvas_group()->raise_to_top();
3332                                 rv->get_time_axis_view().canvas_display->raise_to_top();
3333                                 cursor_group->raise_to_top();
3334
3335                                 rv->fake_set_opaque (true);
3336                         }
3337                         
3338                         if (drag_info.brushing) {
3339                                 mouse_brush_insert_region (rv, pending_region_position);
3340                         } else {
3341                                 rv->move (x_delta, y_delta);                    
3342                         }
3343
3344                 } /* foreach region */
3345
3346         } /* if do_move */
3347
3348         if (drag_info.first_move && drag_info.move_threshold_passed) {
3349                 cursor_group->raise_to_top();
3350                 drag_info.first_move = false;
3351         }
3352
3353         if (x_delta != 0 && !drag_info.brushing) {
3354                 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3355         }
3356
3357
3358 void
3359 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3360 {
3361         nframes_t where;
3362         RegionView* rvdi = reinterpret_cast<RegionView *> (drag_info.data);
3363         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3364         bool nocommit = true;
3365         double speed;
3366         RouteTimeAxisView* rtv;
3367         bool regionview_y_movement;
3368         bool regionview_x_movement;
3369         vector<RegionView*> copies;
3370         list <boost::shared_ptr<Playlist > > used_playlists;
3371         list <sigc::connection > used_connections;
3372         bool preserve_selection = false;
3373
3374         /* first_move is set to false if the regionview has been moved in the 
3375            motion handler. 
3376         */
3377
3378         if (drag_info.first_move) {
3379                 /* just a click */
3380                 goto out;
3381         }
3382
3383         nocommit = false;
3384
3385         /* The regionview has been moved at some stage during the grab so we need
3386            to account for any mouse movement between this event and the last one. 
3387         */      
3388
3389         region_drag_motion_callback (item, event);
3390
3391         if (drag_info.brushing) {
3392                 /* all changes were made during motion event handlers */
3393                 
3394                 if (drag_info.copy) {
3395                         for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3396                                 copies.push_back (*i);
3397                         }
3398                 }
3399
3400                 goto out;
3401         }
3402
3403         /* adjust for track speed */
3404         speed = 1.0;
3405
3406         rtv = dynamic_cast<RouteTimeAxisView*> (drag_info.last_trackview);
3407         if (rtv && rtv->get_diskstream()) {
3408                 speed = rtv->get_diskstream()->speed();
3409         }
3410         
3411         regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rvdi->region()->position()/speed));
3412         regionview_y_movement = (drag_info.last_trackview != &rvdi->get_time_axis_view());
3413
3414         //printf ("last_frame: %s position is %lu  %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed); 
3415         //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str()); 
3416         
3417         char* op_string;
3418
3419         if (drag_info.copy) {
3420                 if (drag_info.x_constrained) {
3421                         op_string = _("fixed time region copy");
3422                 } else {
3423                         op_string = _("region copy");
3424                 } 
3425         } else {
3426                 if (drag_info.x_constrained) {
3427                         op_string = _("fixed time region drag");
3428                 } else {
3429                         op_string = _("region drag");
3430                 }
3431         }
3432
3433         begin_reversible_command (op_string);
3434
3435         if (regionview_y_movement) {
3436
3437                 /* moved to a different audio track. */
3438                 
3439                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3440                         
3441                         RegionView* rv = (*i);      
3442
3443                         double ix1, ix2, iy1, iy2;
3444                         
3445                         rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3446                         rv->get_canvas_group()->i2w (ix1, iy1);
3447
3448                         RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(trackview_by_y_position (iy1));
3449
3450                         boost::shared_ptr<Playlist> to_playlist = rtv2->playlist();
3451
3452                         if (! to_playlist->frozen()) {
3453                                 /* 
3454                                    we haven't seen this playlist before. 
3455                                    we want to freeze it because we don't want to relayer per-region. 
3456                                    its much better to do that just once if the playlist is large. 
3457                                 */
3458
3459                                 /*
3460                                    connect so the selection is changed when the new regionview finally appears (after thaw). 
3461                                    keep track of it so we can disconnect later. 
3462                                 */
3463
3464                                 sigc::connection c = rtv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_and_select_new_region_view));
3465                                 used_connections.push_back (c);
3466
3467                                 /* undo */
3468                                 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3469
3470                                 /* remember used playlists so we can thaw them later */ 
3471                                 used_playlists.push_back(to_playlist);
3472                                 to_playlist->freeze();
3473                         }
3474                         
3475                         where = (nframes_t) (unit_to_frame (ix1) * speed);
3476                         boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3477
3478                         if (!drag_info.copy) {
3479
3480
3481                                 /* the region that used to be in the old playlist is not
3482                                    moved to the new one - we make a copy of it. as a result,
3483                                    any existing editor for the region should no longer be
3484                                    visible.
3485                                 */ 
3486
3487                                 RouteTimeAxisView* from_playlist_rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_trackview());
3488                                 boost::shared_ptr<Playlist> from_playlist = from_playlist_rtv->playlist();
3489
3490                                 if (! from_playlist->frozen()) {
3491                                         from_playlist->freeze();
3492                                         used_playlists.push_back(from_playlist);
3493
3494                                         sigc::connection c = rtv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_and_select_new_region_view));
3495                                         used_connections.push_back (c);
3496
3497                                         session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));    
3498                                 }
3499
3500                                 rv->hide_region_editor();
3501                                 rv->fake_set_opaque (false);
3502
3503                                 from_playlist->remove_region ((rv->region()));
3504
3505                         } else {
3506
3507                                 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3508
3509                                 copies.push_back (rv);
3510                         }
3511
3512                         latest_regionview = 0;
3513
3514                         to_playlist->add_region (new_region, where);
3515
3516                         /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3517                            was selected in all of them, then removing it from the playlist will have removed all
3518                            trace of it from the selection (i.e. there were N regions selected, we removed 1,
3519                            but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3520                            corresponding regionview, and the selection is now empty).
3521
3522                            this could have invalidated any and all iterators into the region selection.
3523
3524                            the heuristic we use here is: if the region selection is empty, break out of the loop
3525                            here. if the region selection is not empty, then restart the loop because we know that
3526                            we must have removed at least the region(view) we've just been working on as well as any
3527                            that we processed on previous iterations.
3528
3529                            EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3530                            we can just iterate.
3531
3532                         */
3533
3534                         if (drag_info.copy) {
3535                                 ++i;
3536                         } else {
3537                                 if (selection->regions.empty()) {
3538                                         break;
3539                                 } else { 
3540                                   /*
3541                                     XXX see above .. but we just froze the playlists.. we have to keep iterating, right? 
3542                                   */
3543
3544                                   //i = selection->regions.by_layer().begin();
3545                                   ++i;
3546                                 }
3547                         }
3548                 }
3549
3550         } else {
3551
3552                 /* motion within a single track */
3553
3554                 list<RegionView*> regions = selection->regions.by_layer();
3555
3556                 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3557
3558                         RegionView* rv = (*i);
3559                         boost::shared_ptr<Playlist> to_playlist = (*i)->region()->playlist();
3560                         RouteTimeAxisView* from_rtv = dynamic_cast<RouteTimeAxisView*> (&(rv->get_time_axis_view()));
3561
3562                         if (!rv->region()->can_move()) {
3563                                 continue;
3564                         }
3565
3566                         if (regionview_x_movement) {
3567                                 double ownspeed = 1.0;
3568
3569                                 if (from_rtv && from_rtv->get_diskstream()) {
3570                                         ownspeed = from_rtv->get_diskstream()->speed();
3571                                 }
3572                                 
3573                                 /* base the new region position on the current position of the regionview.*/
3574                                 
3575                                 double ix1, ix2, iy1, iy2;
3576                                 
3577                                 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3578                                 rv->get_canvas_group()->i2w (ix1, iy1);
3579                                 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3580                                 
3581                         } else {
3582                                 
3583                                 where = rv->region()->position();
3584                         }
3585
3586                         if (! to_playlist->frozen()) {
3587                                 sigc::connection c = from_rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_and_select_new_region_view));
3588                                 used_connections.push_back (c);
3589
3590                                 /* add the undo */      
3591                                 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3592
3593                                 used_playlists.push_back(to_playlist);
3594                                 to_playlist->freeze();
3595                         }
3596
3597                         if (drag_info.copy) {
3598
3599                                 boost::shared_ptr<Region> newregion;
3600                                 boost::shared_ptr<Region> ar;
3601                                 boost::shared_ptr<Region> mr;
3602
3603                                 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3604                                         newregion = RegionFactory::create (ar);
3605                                 } else if ((mr = boost::dynamic_pointer_cast<MidiRegion>(rv->region())) != 0) {
3606                                         newregion = RegionFactory::create (mr);
3607                                 }
3608
3609                                 /* add it */
3610
3611                                 to_playlist->add_region (newregion, (nframes_t) (where * from_rtv->get_diskstream()->speed()));
3612
3613                                 /* if the original region was locked, we don't care for the new one */
3614                                 
3615                                 newregion->set_locked (false);
3616                                 copies.push_back (rv);
3617                                 
3618                         } else {
3619
3620                                 /* just change the model */
3621
3622                                 rv->region()->set_position (where, (void*) this);
3623                                 preserve_selection = true;
3624
3625                         }
3626
3627                 }
3628
3629         }
3630         if (! preserve_selection) {
3631           //selection->clear_regions();
3632         }
3633         while (used_playlists.size() > 0) {
3634
3635                 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
3636                 (*i)->thaw();
3637
3638                 if (used_connections.size()) {
3639                         sigc::connection c = used_connections.front();
3640                         c.disconnect();
3641                         used_connections.pop_front();
3642                 }
3643                 /* add the redo */
3644
3645                 session->add_command (new MementoCommand<Playlist>(*(*i), 0, &(*i)->get_state()));
3646                 used_playlists.pop_front();
3647         }
3648
3649   out:
3650         
3651         if (!nocommit) {
3652                 commit_reversible_command ();
3653         }
3654
3655         for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3656                 delete *x;
3657         }
3658 }
3659
3660         
3661 void
3662 Editor::create_region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3663 {
3664         if (drag_info.move_threshold_passed) {
3665                 if (drag_info.first_move) {
3666                         // TODO: create region-create-drag region view here
3667                         drag_info.first_move = false;
3668                 }
3669
3670                 // TODO: resize region-create-drag region view here
3671         }
3672
3673
3674 void
3675 Editor::create_region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3676 {
3677         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (drag_info.last_trackview);
3678         if (!mtv)
3679                 return;
3680
3681         const boost::shared_ptr<MidiDiskstream> diskstream =
3682                 boost::dynamic_pointer_cast<MidiDiskstream>(mtv->view()->trackview().track()->diskstream());
3683         
3684         if (!diskstream) {
3685                 warning << "Cannot create non-MIDI region" << endl;
3686                 return;
3687         }
3688
3689         if (drag_info.first_move) {
3690                 begin_reversible_command (_("create region"));
3691                 XMLNode &before = mtv->playlist()->get_state();
3692
3693                 nframes_t start = drag_info.grab_frame;
3694                 snap_to (start, -1);
3695                 const Meter& m = session->tempo_map().meter_at(start);
3696                 const Tempo& t = session->tempo_map().tempo_at(start);
3697                 double length = m.frames_per_bar(t, session->frame_rate());
3698
3699                 boost::shared_ptr<Source> src = session->create_midi_source_for_session(*diskstream.get());
3700                                 
3701                 mtv->playlist()->add_region (boost::dynamic_pointer_cast<MidiRegion>(RegionFactory::create(
3702                                 src, 0, length, PBD::basename_nosuffix(src->name()))), start);
3703                 XMLNode &after = mtv->playlist()->get_state();
3704                 session->add_command(new MementoCommand<Playlist>(*mtv->playlist().get(), &before, &after));
3705                 commit_reversible_command();
3706
3707         } else {
3708                 create_region_drag_motion_callback (item, event);
3709                 // TODO: create region-create-drag region here
3710         }
3711 }
3712
3713 void
3714 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3715 {
3716         /* Either add to or set the set the region selection, unless
3717            this is an alignment click (control used)
3718         */
3719         
3720         if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3721                 TimeAxisView* tv = &rv.get_time_axis_view();
3722                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3723                 double speed = 1.0;
3724                 if (rtv && rtv->is_track()) {
3725                         speed = rtv->get_diskstream()->speed();
3726                 }
3727
3728                 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3729
3730                         align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3731
3732                 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3733
3734                         align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3735
3736                 } else {
3737
3738                         align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3739                 }
3740         }
3741 }
3742
3743 void
3744 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos) 
3745 {
3746         char buf[128];
3747         SMPTE::Time smpte;
3748         BBT_Time bbt;
3749         int hours, mins;
3750         nframes_t frame_rate;
3751         float secs;
3752
3753         if (session == 0) {
3754                 return;
3755         }
3756
3757         switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3758         case AudioClock::BBT:
3759                 session->bbt_time (frame, bbt);
3760                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3761                 break;
3762                 
3763         case AudioClock::SMPTE:
3764                 session->smpte_time (frame, smpte);
3765                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3766                 break;
3767
3768         case AudioClock::MinSec:
3769                 /* XXX this is copied from show_verbose_duration_cursor() */
3770                 frame_rate = session->frame_rate();
3771                 hours = frame / (frame_rate * 3600);
3772                 frame = frame % (frame_rate * 3600);
3773                 mins = frame / (frame_rate * 60);
3774                 frame = frame % (frame_rate * 60);
3775                 secs = (float) frame / (float) frame_rate;
3776                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3777                 break;
3778
3779         default:
3780                 snprintf (buf, sizeof(buf), "%u", frame);
3781                 break;
3782         }
3783
3784         if (xpos >= 0 && ypos >=0) {
3785                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3786         }
3787         else {
3788                 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3789         }
3790         show_verbose_canvas_cursor ();
3791 }
3792
3793 void
3794 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos) 
3795 {
3796         char buf[128];
3797         SMPTE::Time smpte;
3798         BBT_Time sbbt;
3799         BBT_Time ebbt;
3800         int hours, mins;
3801         nframes_t distance, frame_rate;
3802         float secs;
3803         Meter meter_at_start(session->tempo_map().meter_at(start));
3804
3805         if (session == 0) {
3806                 return;
3807         }
3808
3809         switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3810         case AudioClock::BBT:
3811                 session->bbt_time (start, sbbt);
3812                 session->bbt_time (end, ebbt);
3813
3814                 /* subtract */
3815                 /* XXX this computation won't work well if the
3816                 user makes a selection that spans any meter changes.
3817                 */
3818
3819                 ebbt.bars -= sbbt.bars;
3820                 if (ebbt.beats >= sbbt.beats) {
3821                         ebbt.beats -= sbbt.beats;
3822                 } else {
3823                         ebbt.bars--;
3824                         ebbt.beats =  int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3825                 }
3826                 if (ebbt.ticks >= sbbt.ticks) {
3827                         ebbt.ticks -= sbbt.ticks;
3828                 } else {
3829                         ebbt.beats--;
3830                         ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3831                 }
3832                 
3833                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3834                 break;
3835                 
3836         case AudioClock::SMPTE:
3837                 session->smpte_duration (end - start, smpte);
3838                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3839                 break;
3840
3841         case AudioClock::MinSec:
3842                 /* XXX this stuff should be elsewhere.. */
3843                 distance = end - start;
3844                 frame_rate = session->frame_rate();
3845                 hours = distance / (frame_rate * 3600);
3846                 distance = distance % (frame_rate * 3600);
3847                 mins = distance / (frame_rate * 60);
3848                 distance = distance % (frame_rate * 60);
3849                 secs = (float) distance / (float) frame_rate;
3850                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3851                 break;
3852
3853         default:
3854                 snprintf (buf, sizeof(buf), "%u", end - start);
3855                 break;
3856         }
3857
3858         if (xpos >= 0 && ypos >=0) {
3859                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3860         }
3861         else {
3862                 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3863         }
3864         show_verbose_canvas_cursor ();
3865 }
3866
3867 void
3868 Editor::collect_new_region_view (RegionView* rv)
3869 {
3870         latest_regionview = rv;
3871 }
3872
3873 void
3874 Editor::collect_and_select_new_region_view (RegionView* rv)
3875 {
3876         selection->add(rv);
3877         latest_regionview = rv;
3878 }
3879
3880 void
3881 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3882 {
3883         if (clicked_regionview == 0) {
3884                 return;
3885         }
3886
3887         /* lets try to create new Region for the selection */
3888
3889         vector<boost::shared_ptr<AudioRegion> > new_regions;
3890         create_region_from_selection (new_regions);
3891
3892         if (new_regions.empty()) {
3893                 return;
3894         }
3895
3896         /* XXX fix me one day to use all new regions */
3897         
3898         boost::shared_ptr<Region> region (new_regions.front());
3899
3900         /* add it to the current stream/playlist.
3901
3902            tricky: the streamview for the track will add a new regionview. we will
3903            catch the signal it sends when it creates the regionview to
3904            set the regionview we want to then drag.
3905         */
3906         
3907         latest_regionview = 0;
3908         sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3909         
3910         /* A selection grab currently creates two undo/redo operations, one for 
3911            creating the new region and another for moving it.
3912         */
3913
3914         begin_reversible_command (_("selection grab"));
3915
3916         boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
3917
3918         XMLNode *before = &(playlist->get_state());
3919         clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
3920         XMLNode *after = &(playlist->get_state());
3921         session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3922
3923         commit_reversible_command ();
3924         
3925         c.disconnect ();
3926         
3927         if (latest_regionview == 0) {
3928                 /* something went wrong */
3929                 return;
3930         }
3931
3932         /* we need to deselect all other regionviews, and select this one
3933            i'm ignoring undo stuff, because the region creation will take care of it */
3934         //selection->set (latest_regionview);
3935         
3936         drag_info.item = latest_regionview->get_canvas_group();
3937         drag_info.data = latest_regionview;
3938         drag_info.motion_callback = &Editor::region_drag_motion_callback;
3939         drag_info.finished_callback = &Editor::region_drag_finished_callback;
3940
3941         start_grab (event);
3942         
3943         drag_info.last_trackview = clicked_axisview;
3944         drag_info.last_frame_position = latest_regionview->region()->position();
3945         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3946         
3947         show_verbose_time_cursor (drag_info.last_frame_position, 10);
3948 }
3949
3950 void
3951 Editor::cancel_selection ()
3952 {
3953         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3954                 (*i)->hide_selection ();
3955         }
3956         begin_reversible_command (_("cancel selection"));
3957         selection->clear ();
3958         clicked_selection = 0;
3959         commit_reversible_command ();
3960 }       
3961
3962 void
3963 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3964 {
3965         nframes_t start = 0;
3966         nframes_t end = 0;
3967
3968         if (session == 0) {
3969                 return;
3970         }
3971
3972         drag_info.item = item;
3973         drag_info.motion_callback = &Editor::drag_selection;
3974         drag_info.finished_callback = &Editor::end_selection_op;
3975
3976         selection_op = op;
3977
3978         switch (op) {
3979         case CreateSelection:
3980                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3981                         drag_info.copy = true;
3982                 } else {
3983                         drag_info.copy = false;
3984                 }
3985                 start_grab (event, selector_cursor);
3986                 break;
3987
3988         case SelectionStartTrim:
3989                 if (clicked_axisview) {
3990                         clicked_axisview->order_selection_trims (item, true);
3991                 } 
3992                 start_grab (event, trimmer_cursor);
3993                 start = selection->time[clicked_selection].start;
3994                 drag_info.pointer_frame_offset = drag_info.grab_frame - start;  
3995                 break;
3996                 
3997         case SelectionEndTrim:
3998                 if (clicked_axisview) {
3999                         clicked_axisview->order_selection_trims (item, false);
4000                 }
4001                 start_grab (event, trimmer_cursor);
4002                 end = selection->time[clicked_selection].end;
4003                 drag_info.pointer_frame_offset = drag_info.grab_frame - end;    
4004                 break;
4005
4006         case SelectionMove:
4007                 start = selection->time[clicked_selection].start;
4008                 start_grab (event);
4009                 drag_info.pointer_frame_offset = drag_info.grab_frame - start;  
4010                 break;
4011         }
4012
4013         if (selection_op == SelectionMove) {
4014                 show_verbose_time_cursor(start, 10);    
4015         } else {
4016                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4017         }
4018 }
4019
4020 void
4021 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4022 {
4023         nframes_t start = 0;
4024         nframes_t end = 0;
4025         nframes_t length;
4026         nframes_t pending_position;
4027
4028         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4029                 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4030         } else {
4031                 pending_position = 0;
4032         }
4033         
4034         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4035                 snap_to (pending_position);
4036         }
4037
4038         /* only alter selection if the current frame is 
4039            different from the last frame position (adjusted)
4040          */
4041         
4042         if (pending_position == drag_info.last_pointer_frame) return;
4043         
4044         switch (selection_op) {
4045         case CreateSelection:
4046                 
4047                 if (drag_info.first_move) {
4048                         snap_to (drag_info.grab_frame);
4049                 }
4050                 
4051                 if (pending_position < drag_info.grab_frame) {
4052                         start = pending_position;
4053                         end = drag_info.grab_frame;
4054                 } else {
4055                         end = pending_position;
4056                         start = drag_info.grab_frame;
4057                 }
4058                 
4059                 /* first drag: Either add to the selection
4060                    or create a new selection->
4061                 */
4062                 
4063                 if (drag_info.first_move) {
4064                         
4065                         begin_reversible_command (_("range selection"));
4066                         
4067                         if (drag_info.copy) {
4068                                 /* adding to the selection */
4069                                 clicked_selection = selection->add (start, end);
4070                                 drag_info.copy = false;
4071                         } else {
4072                                 /* new selection-> */
4073                                 clicked_selection = selection->set (clicked_axisview, start, end);
4074                         }
4075                 } 
4076                 break;
4077                 
4078         case SelectionStartTrim:
4079                 
4080                 if (drag_info.first_move) {
4081                         begin_reversible_command (_("trim selection start"));
4082                 }
4083                 
4084                 start = selection->time[clicked_selection].start;
4085                 end = selection->time[clicked_selection].end;
4086
4087                 if (pending_position > end) {
4088                         start = end;
4089                 } else {
4090                         start = pending_position;
4091                 }
4092                 break;
4093                 
4094         case SelectionEndTrim:
4095                 
4096                 if (drag_info.first_move) {
4097                         begin_reversible_command (_("trim selection end"));
4098                 }
4099                 
4100                 start = selection->time[clicked_selection].start;
4101                 end = selection->time[clicked_selection].end;
4102
4103                 if (pending_position < start) {
4104                         end = start;
4105                 } else {
4106                         end = pending_position;
4107                 }
4108                 
4109                 break;
4110                 
4111         case SelectionMove:
4112                 
4113                 if (drag_info.first_move) {
4114                         begin_reversible_command (_("move selection"));
4115                 }
4116                 
4117                 start = selection->time[clicked_selection].start;
4118                 end = selection->time[clicked_selection].end;
4119                 
4120                 length = end - start;
4121                 
4122                 start = pending_position;
4123                 snap_to (start);
4124                 
4125                 end = start + length;
4126                 
4127                 break;
4128         }
4129         
4130         if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4131                 start_canvas_autoscroll (1);
4132         }
4133
4134         if (start != end) {
4135                 selection->replace (clicked_selection, start, end);
4136         }
4137
4138         drag_info.last_pointer_frame = pending_position;
4139         drag_info.first_move = false;
4140
4141         if (selection_op == SelectionMove) {
4142                 show_verbose_time_cursor(start, 10);    
4143         } else {
4144                 show_verbose_time_cursor(pending_position, 10); 
4145         }
4146 }
4147
4148 void
4149 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4150 {
4151         if (!drag_info.first_move) {
4152                 drag_selection (item, event);
4153                 /* XXX this is not object-oriented programming at all. ick */
4154                 if (selection->time.consolidate()) {
4155                         selection->TimeChanged ();
4156                 }
4157                 commit_reversible_command ();
4158         } else {
4159                 /* just a click, no pointer movement.*/
4160
4161                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4162
4163                         selection->clear_time();
4164
4165                 } 
4166         }
4167
4168         /* XXX what happens if its a music selection? */
4169         session->set_audio_range (selection->time);
4170         stop_canvas_autoscroll ();
4171 }
4172
4173 void
4174 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4175 {
4176         double speed = 1.0;
4177         TimeAxisView* tvp = clicked_axisview;
4178         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4179
4180         if (tv && tv->is_track()) {
4181                 speed = tv->get_diskstream()->speed();
4182         }
4183         
4184         nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
4185         nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
4186         nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
4187
4188         //drag_info.item = clicked_regionview->get_name_highlight();
4189         drag_info.item = item;
4190         drag_info.motion_callback = &Editor::trim_motion_callback;
4191         drag_info.finished_callback = &Editor::trim_finished_callback;
4192
4193         start_grab (event, trimmer_cursor);
4194         
4195         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4196                 trim_op = ContentsTrim;
4197         } else {
4198                 /* These will get overridden for a point trim.*/
4199                 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4200                         /* closer to start */
4201                         trim_op = StartTrim;
4202                 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4203                         /* closer to end */
4204                         trim_op = EndTrim;
4205                 }
4206         }
4207
4208         switch (trim_op) {
4209         case StartTrim:
4210                 show_verbose_time_cursor(region_start, 10);     
4211                 break;
4212         case EndTrim:
4213                 show_verbose_time_cursor(region_end, 10);       
4214                 break;
4215         case ContentsTrim:
4216                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4217                 break;
4218         }
4219 }
4220
4221 void
4222 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4223 {
4224         RegionView* rv = clicked_regionview;
4225         nframes_t frame_delta = 0;
4226         bool left_direction;
4227         bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4228
4229         /* snap modifier works differently here..
4230            its' current state has to be passed to the 
4231            various trim functions in order to work properly 
4232         */ 
4233
4234         double speed = 1.0;
4235         TimeAxisView* tvp = clicked_axisview;
4236         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4237         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4238
4239         if (tv && tv->is_track()) {
4240                 speed = tv->get_diskstream()->speed();
4241         }
4242         
4243         if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4244                 left_direction = true;
4245         } else {
4246                 left_direction = false;
4247         }
4248
4249         if (obey_snap) {
4250                 snap_to (drag_info.current_pointer_frame);
4251         }
4252
4253         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4254                 return;
4255         }
4256
4257         if (drag_info.first_move) {
4258         
4259                 string trim_type;
4260
4261                 switch (trim_op) {
4262                 case StartTrim:
4263                         trim_type = "Region start trim";
4264                         break;
4265                 case EndTrim:
4266                         trim_type = "Region end trim";
4267                         break;
4268                 case ContentsTrim:
4269                         trim_type = "Region content trim";
4270                         break;
4271                 }
4272
4273                 begin_reversible_command (trim_type);
4274
4275                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4276                         (*i)->fake_set_opaque(false);
4277                         (*i)->region()->freeze ();
4278                 
4279                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4280                         if (arv)
4281                                 arv->temporarily_hide_envelope ();
4282
4283                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4284                         insert_result = motion_frozen_playlists.insert (pl);
4285                         if (insert_result.second) {
4286                                 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4287                                 pl->freeze();
4288                         }
4289                 }
4290         }
4291
4292         if (left_direction) {
4293                 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4294         } else {
4295                 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4296         }
4297
4298         switch (trim_op) {              
4299         case StartTrim:
4300                 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4301                         break;
4302                 } else {
4303                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4304                                 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4305                         }
4306                         break;
4307                 }
4308                 
4309         case EndTrim:
4310                 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4311                         break;
4312                 } else {
4313                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4314                                 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4315                         }
4316                         break;
4317                 }
4318                 
4319         case ContentsTrim:
4320                 {
4321                         bool swap_direction = false;
4322
4323                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4324                                 swap_direction = true;
4325                         }
4326                         
4327                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4328                              i != selection->regions.by_layer().end(); ++i)
4329                         {
4330                                 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4331                         }
4332                 }
4333                 break;
4334         }
4335
4336         switch (trim_op) {
4337         case StartTrim:
4338                 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);     
4339                 break;
4340         case EndTrim:
4341                 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);   
4342                 break;
4343         case ContentsTrim:
4344                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4345                 break;
4346         }
4347
4348         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4349         drag_info.first_move = false;
4350 }
4351
4352 void
4353 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_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                 if (swap_direction) {
4373                         new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4374                 } else {
4375                         new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4376                 }
4377         } else {
4378                 if (swap_direction) {
4379                         new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4380                 } else {
4381                         new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4382                 }
4383         }
4384
4385         if (obey_snap) {
4386                 snap_to (new_bound);
4387         }
4388         region->trim_start ((nframes_t) (new_bound * speed), this);     
4389         rv.region_changed (StartChanged);
4390 }
4391
4392 void
4393 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4394 {
4395         boost::shared_ptr<Region> region (rv.region()); 
4396
4397         if (region->locked()) {
4398                 return;
4399         }
4400
4401         nframes_t new_bound;
4402
4403         double speed = 1.0;
4404         TimeAxisView* tvp = clicked_axisview;
4405         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4406
4407         if (tv && tv->is_track()) {
4408                 speed = tv->get_diskstream()->speed();
4409         }
4410         
4411         if (left_direction) {
4412                 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4413         } else {
4414                 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4415         }
4416
4417         if (obey_snap) {
4418                 snap_to (new_bound, (left_direction ? 0 : 1));  
4419         }
4420
4421         region->trim_front ((nframes_t) (new_bound * speed), this);
4422
4423         rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4424 }
4425
4426 void
4427 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4428 {
4429         boost::shared_ptr<Region> region (rv.region());
4430
4431         if (region->locked()) {
4432                 return;
4433         }
4434
4435         nframes_t new_bound;
4436
4437         double speed = 1.0;
4438         TimeAxisView* tvp = clicked_axisview;
4439         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4440
4441         if (tv && tv->is_track()) {
4442                 speed = tv->get_diskstream()->speed();
4443         }
4444         
4445         if (left_direction) {
4446                 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4447         } else {
4448                 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4449         }
4450
4451         if (obey_snap) {
4452                 snap_to (new_bound);
4453         }
4454         region->trim_end ((nframes_t) (new_bound * speed), this);
4455         rv.region_changed (LengthChanged);
4456 }
4457         
4458 void
4459 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4460 {
4461         if (!drag_info.first_move) {
4462                 trim_motion_callback (item, event);
4463                 
4464                 if (!clicked_regionview->get_selected()) {
4465                         thaw_region_after_trim (*clicked_regionview);           
4466                 } else {
4467                         
4468                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4469                              i != selection->regions.by_layer().end(); ++i)
4470                         {
4471                                 thaw_region_after_trim (**i);
4472                                 (*i)->fake_set_opaque (true);
4473                         }
4474                 }
4475                 
4476                 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4477                         (*p)->thaw ();
4478                         session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4479                 }
4480                 
4481                 motion_frozen_playlists.clear ();
4482
4483                 commit_reversible_command();
4484         } else {
4485                 /* no mouse movement */
4486                 point_trim (event);
4487         }
4488 }
4489
4490 void
4491 Editor::point_trim (GdkEvent* event)
4492 {
4493         RegionView* rv = clicked_regionview;
4494         nframes_t new_bound = drag_info.current_pointer_frame;
4495
4496         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4497                 snap_to (new_bound);
4498         }
4499
4500         /* Choose action dependant on which button was pressed */
4501         switch (event->button.button) {
4502         case 1:
4503                 trim_op = StartTrim;
4504                 begin_reversible_command (_("Start point trim"));
4505
4506                 if (rv->get_selected()) {
4507
4508                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4509                              i != selection->regions.by_layer().end(); ++i)
4510                         {
4511                                 if (!(*i)->region()->locked()) {
4512                                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4513                                         XMLNode &before = pl->get_state();
4514                                         (*i)->region()->trim_front (new_bound, this);   
4515                                         XMLNode &after = pl->get_state();
4516                                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4517                                 }
4518                         }
4519
4520                 } else {
4521
4522                         if (!rv->region()->locked()) {
4523                                 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4524                                 XMLNode &before = pl->get_state();
4525                                 rv->region()->trim_front (new_bound, this);     
4526                                 XMLNode &after = pl->get_state();
4527                                 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4528                         }
4529                 }
4530
4531                 commit_reversible_command();
4532         
4533                 break;
4534         case 2:
4535                 trim_op = EndTrim;
4536                 begin_reversible_command (_("End point trim"));
4537
4538                 if (rv->get_selected()) {
4539                         
4540                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4541                         {
4542                                 if (!(*i)->region()->locked()) {
4543                                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4544                                         XMLNode &before = pl->get_state();
4545                                         (*i)->region()->trim_end (new_bound, this);
4546                                         XMLNode &after = pl->get_state();
4547                                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4548                                 }
4549                         }
4550
4551                 } else {
4552
4553                         if (!rv->region()->locked()) {
4554                                 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4555                                 XMLNode &before = pl->get_state();
4556                                 rv->region()->trim_end (new_bound, this);
4557                                 XMLNode &after = pl->get_state();
4558                                 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4559                         }
4560                 }
4561
4562                 commit_reversible_command();
4563         
4564                 break;
4565         default:
4566                 break;
4567         }
4568 }
4569
4570 void
4571 Editor::thaw_region_after_trim (RegionView& rv)
4572 {
4573         boost::shared_ptr<Region> region (rv.region());
4574
4575         if (region->locked()) {
4576                 return;
4577         }
4578
4579         region->thaw (_("trimmed region"));
4580         XMLNode &after = region->playlist()->get_state();
4581         session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4582
4583         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4584         if (arv)
4585                 arv->unhide_envelope ();
4586 }
4587
4588 void
4589 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4590 {
4591         Marker* marker;
4592         bool is_start;
4593
4594         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4595                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4596                 /*NOTREACHED*/
4597         }
4598
4599         Location* location = find_location_from_marker (marker, is_start);      
4600         location->set_hidden (true, this);
4601 }
4602
4603
4604 void
4605 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4606 {
4607         if (session == 0) {
4608                 return;
4609         }
4610
4611         drag_info.item = item;
4612         drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4613         drag_info.finished_callback = &Editor::end_range_markerbar_op;
4614
4615         range_marker_op = op;
4616
4617         if (!temp_location) {
4618                 temp_location = new Location;
4619         }
4620         
4621         switch (op) {
4622         case CreateRangeMarker:
4623         case CreateTransportMarker:
4624                 
4625                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4626                         drag_info.copy = true;
4627                 } else {
4628                         drag_info.copy = false;
4629                 }
4630                 start_grab (event, selector_cursor);
4631                 break;
4632         }
4633
4634         show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4635         
4636 }
4637
4638 void
4639 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4640 {
4641         nframes_t start = 0;
4642         nframes_t end = 0;
4643         ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4644         
4645         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4646                 snap_to (drag_info.current_pointer_frame);
4647         }
4648
4649         /* only alter selection if the current frame is 
4650            different from the last frame position.
4651          */
4652         
4653         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4654         
4655         switch (range_marker_op) {
4656         case CreateRangeMarker:
4657         case CreateTransportMarker:
4658                 if (drag_info.first_move) {
4659                         snap_to (drag_info.grab_frame);
4660                 }
4661                 
4662                 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4663                         start = drag_info.current_pointer_frame;
4664                         end = drag_info.grab_frame;
4665                 } else {
4666                         end = drag_info.current_pointer_frame;
4667                         start = drag_info.grab_frame;
4668                 }
4669                 
4670                 /* first drag: Either add to the selection
4671                    or create a new selection.
4672                 */
4673                 
4674                 if (drag_info.first_move) {
4675                         
4676                         temp_location->set (start, end);
4677                         
4678                         crect->show ();
4679
4680                         update_marker_drag_item (temp_location);
4681                         range_marker_drag_rect->show();
4682                         range_marker_drag_rect->raise_to_top();
4683                         
4684                 } 
4685                 break;          
4686         }
4687         
4688         if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4689                 start_canvas_autoscroll (1);
4690         }
4691         
4692         if (start != end) {
4693                 temp_location->set (start, end);
4694
4695                 double x1 = frame_to_pixel (start);
4696                 double x2 = frame_to_pixel (end);
4697                 crect->property_x1() = x1;
4698                 crect->property_x2() = x2;
4699
4700                 update_marker_drag_item (temp_location);
4701         }
4702
4703         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4704         drag_info.first_move = false;
4705
4706         show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4707         
4708 }
4709
4710 void
4711 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4712 {
4713         Location * newloc = 0;
4714         string rangename;
4715         
4716         if (!drag_info.first_move) {
4717                 drag_range_markerbar_op (item, event);
4718
4719                 switch (range_marker_op) {
4720                 case CreateRangeMarker:
4721                     {
4722                         begin_reversible_command (_("new range marker"));
4723                         XMLNode &before = session->locations()->get_state();
4724                         session->locations()->next_available_name(rangename,"unnamed");
4725                         newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4726                         session->locations()->add (newloc, true);
4727                         XMLNode &after = session->locations()->get_state();
4728                         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4729                         commit_reversible_command ();
4730                         
4731                         range_bar_drag_rect->hide();
4732                         range_marker_drag_rect->hide();
4733                         break;
4734                     }
4735
4736                 case CreateTransportMarker:
4737                         // popup menu to pick loop or punch
4738                         new_transport_marker_context_menu (&event->button, item);
4739                         
4740                         break;
4741                 }
4742         } else {
4743                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4744
4745                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4746
4747                         nframes_t start;
4748                         nframes_t end;
4749
4750                         start = session->locations()->first_mark_before (drag_info.grab_frame);
4751                         end = session->locations()->first_mark_after (drag_info.grab_frame);
4752                         
4753                         if (end == max_frames) {
4754                                 end = session->current_end_frame ();
4755                         }
4756
4757                         if (start == 0) {
4758                                 start = session->current_start_frame ();
4759                         }
4760
4761                         switch (mouse_mode) {
4762                         case MouseObject:
4763                                 /* find the two markers on either side and then make the selection from it */
4764                                 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4765                                 break;
4766
4767                         case MouseRange:
4768                                 /* find the two markers on either side of the click and make the range out of it */
4769                                 selection->set (0, start, end);
4770                                 break;
4771
4772                         default:
4773                                 break;
4774                         }
4775                 } 
4776         }
4777
4778         stop_canvas_autoscroll ();
4779 }
4780
4781
4782
4783 void
4784 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4785 {
4786         drag_info.item = item;
4787         drag_info.motion_callback = &Editor::drag_mouse_zoom;
4788         drag_info.finished_callback = &Editor::end_mouse_zoom;
4789
4790         start_grab (event, zoom_cursor);
4791
4792         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4793 }
4794
4795 void
4796 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4797 {
4798         nframes_t start;
4799         nframes_t end;
4800
4801         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4802                 snap_to (drag_info.current_pointer_frame);
4803                 
4804                 if (drag_info.first_move) {
4805                         snap_to (drag_info.grab_frame);
4806                 }
4807         }
4808                 
4809         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4810
4811         /* base start and end on initial click position */
4812         if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4813                 start = drag_info.current_pointer_frame;
4814                 end = drag_info.grab_frame;
4815         } else {
4816                 end = drag_info.current_pointer_frame;
4817                 start = drag_info.grab_frame;
4818         }
4819         
4820         if (start != end) {
4821
4822                 if (drag_info.first_move) {
4823                         zoom_rect->show();
4824                         zoom_rect->raise_to_top();
4825                 }
4826
4827                 reposition_zoom_rect(start, end);
4828
4829                 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4830                 drag_info.first_move = false;
4831
4832                 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4833         }
4834 }
4835
4836 void
4837 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4838 {
4839         if (!drag_info.first_move) {
4840                 drag_mouse_zoom (item, event);
4841                 
4842                 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4843                         temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4844                 } else {
4845                         temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4846                 }               
4847         } else {
4848                 temporal_zoom_to_frame (false, drag_info.grab_frame);
4849                 /*
4850                 temporal_zoom_step (false);
4851                 center_screen (drag_info.grab_frame);
4852                 */
4853         }
4854
4855         zoom_rect->hide();
4856 }
4857
4858 void
4859 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4860 {
4861         double x1 = frame_to_pixel (start);
4862         double x2 = frame_to_pixel (end);
4863         double y2 = full_canvas_height - 1.0;
4864
4865         zoom_rect->property_x1() = x1;
4866         zoom_rect->property_y1() = 1.0;
4867         zoom_rect->property_x2() = x2;
4868         zoom_rect->property_y2() = y2;
4869 }
4870
4871 void
4872 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4873 {
4874         drag_info.item = item;
4875         drag_info.motion_callback = &Editor::drag_rubberband_select;
4876         drag_info.finished_callback = &Editor::end_rubberband_select;
4877
4878         start_grab (event, cross_hair_cursor);
4879
4880         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4881 }
4882
4883 void
4884 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4885 {
4886         nframes_t start;
4887         nframes_t end;
4888         double y1;
4889         double y2;
4890
4891         /* use a bigger drag threshold than the default */
4892
4893         if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4894                 return;
4895         }
4896
4897         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4898                 if (drag_info.first_move) {
4899                         snap_to (drag_info.grab_frame);
4900                 } 
4901                 snap_to (drag_info.current_pointer_frame);
4902         }
4903
4904         /* base start and end on initial click position */
4905
4906         if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4907                 start = drag_info.current_pointer_frame;
4908                 end = drag_info.grab_frame;
4909         } else {
4910                 end = drag_info.current_pointer_frame;
4911                 start = drag_info.grab_frame;
4912         }
4913
4914         if (drag_info.current_pointer_y < drag_info.grab_y) {
4915                 y1 = drag_info.current_pointer_y;
4916                 y2 = drag_info.grab_y;
4917         } else {
4918                 y2 = drag_info.current_pointer_y;
4919                 y1 = drag_info.grab_y;
4920         }
4921
4922         
4923         if (start != end || y1 != y2) {
4924
4925                 double x1 = frame_to_pixel (start);
4926                 double x2 = frame_to_pixel (end);
4927                 
4928                 rubberband_rect->property_x1() = x1;
4929                 rubberband_rect->property_y1() = y1;
4930                 rubberband_rect->property_x2() = x2;
4931                 rubberband_rect->property_y2() = y2;
4932
4933                 rubberband_rect->show();
4934                 rubberband_rect->raise_to_top();
4935                 
4936                 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4937                 drag_info.first_move = false;
4938
4939                 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4940         }
4941 }
4942
4943 void
4944 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4945 {
4946         if (!drag_info.first_move) {
4947
4948                 drag_rubberband_select (item, event);
4949
4950                 double y1,y2;
4951                 if (drag_info.current_pointer_y < drag_info.grab_y) {
4952                         y1 = drag_info.current_pointer_y;
4953                         y2 = drag_info.grab_y;
4954                 }
4955                 else {
4956                         y2 = drag_info.current_pointer_y;
4957                         y1 = drag_info.grab_y;
4958                 }
4959
4960
4961                 Selection::Operation op = Keyboard::selection_type (event->button.state);
4962                 bool commit;
4963
4964                 begin_reversible_command (_("rubberband selection"));
4965
4966                 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4967                         commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
4968                 } else {
4969                         commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
4970                 }               
4971
4972                 if (commit) {
4973                         commit_reversible_command ();
4974                 }
4975                 
4976         } else {
4977                 selection->clear_tracks();
4978                 selection->clear_regions();
4979                 selection->clear_points ();
4980                 selection->clear_lines ();
4981         }
4982
4983         rubberband_rect->hide();
4984 }
4985
4986
4987 gint
4988 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4989 {
4990         using namespace Gtkmm2ext;
4991
4992         ArdourPrompter prompter (false);
4993
4994         prompter.set_prompt (_("Name for region:"));
4995         prompter.set_initial_text (clicked_regionview->region()->name());
4996         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4997         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4998         prompter.show_all ();
4999         switch (prompter.run ()) {
5000         case Gtk::RESPONSE_ACCEPT:
5001                 string str;
5002                 prompter.get_result(str);
5003                 if (str.length()) {
5004                         clicked_regionview->region()->set_name (str);
5005                 }
5006                 break;
5007         }
5008         return true;
5009 }
5010
5011 void
5012 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5013 {
5014         drag_info.item = item;
5015         drag_info.motion_callback = &Editor::time_fx_motion;
5016         drag_info.finished_callback = &Editor::end_time_fx;
5017
5018         start_grab (event);
5019
5020         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5021 }
5022
5023 void
5024 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5025 {
5026         RegionView* rv = clicked_regionview;
5027
5028         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5029                 snap_to (drag_info.current_pointer_frame);
5030         }
5031
5032         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5033                 return;
5034         }
5035
5036         if (drag_info.current_pointer_frame > rv->region()->position()) {
5037                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5038         }
5039
5040         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5041         drag_info.first_move = false;
5042
5043         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5044 }
5045
5046 void
5047 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5048 {
5049         clicked_regionview->get_time_axis_view().hide_timestretch ();
5050
5051         if (drag_info.first_move) {
5052                 return;
5053         }
5054
5055         if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5056                 /* backwards drag of the left edge - not usable */
5057                 return;
5058         }
5059         
5060         nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5061         float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5062         
5063         begin_reversible_command (_("timestretch"));
5064
5065         if (run_timestretch (selection->regions, percentage) == 0) {
5066                 session->commit_reversible_command ();
5067         }
5068 }
5069
5070 void
5071 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
5072 {
5073         /* no brushing without a useful snap setting */
5074
5075         // FIXME
5076         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
5077         assert(arv);
5078
5079         switch (snap_mode) {
5080         case SnapMagnetic:
5081                 return; /* can't work because it allows region to be placed anywhere */
5082         default:
5083                 break; /* OK */
5084         }
5085
5086         switch (snap_type) {
5087         case SnapToFrame:
5088         case SnapToMark:
5089         case SnapToEditCursor:
5090                 return;
5091
5092         default:
5093                 break;
5094         }
5095
5096         /* don't brush a copy over the original */
5097         
5098         if (pos == rv->region()->position()) {
5099                 return;
5100         }
5101
5102         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
5103
5104         if (rtv == 0 || !rtv->is_track()) {
5105                 return;
5106         }
5107
5108         boost::shared_ptr<Playlist> playlist = rtv->playlist();
5109         double speed = rtv->get_diskstream()->speed();
5110         
5111         XMLNode &before = playlist->get_state();
5112         playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
5113         XMLNode &after = playlist->get_state();
5114         session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5115         
5116         // playlist is frozen, so we have to update manually
5117         
5118         playlist->Modified(); /* EMIT SIGNAL */
5119 }
5120
5121 gint
5122 Editor::track_height_step_timeout ()
5123 {
5124         struct timeval now;
5125         struct timeval delta;
5126         
5127         gettimeofday (&now, 0);
5128         timersub (&now, &last_track_height_step_timestamp, &delta);
5129         
5130         if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
5131                 current_stepping_trackview = 0;
5132                 return false;
5133         }
5134         return true;
5135 }
5136
5137
5138 void
5139 Editor::add_mouse_speed (double speed, double dir)
5140 {
5141         size_t index;
5142
5143         mouse_direction = dir;
5144
5145         index = mouse_speed_entries;
5146
5147         if (++index >= mouse_speed_size) {
5148                 index = 0;
5149                 have_full_mouse_speed = true;
5150         }
5151         
5152         mouse_speed[index] = speed;
5153         mouse_speed_entries = index;
5154 }
5155
5156 double
5157 Editor::compute_mouse_speed ()
5158 {
5159         double total = 0;
5160
5161         if (!have_full_mouse_speed) {
5162
5163                 /* partial speed buffer, just use whatever we have so far */
5164
5165                 if (mouse_speed_entries == 0 ) {
5166                         return 0.0;
5167                 }
5168
5169                 for (size_t n = 0; n < mouse_speed_entries; ++n) {
5170                         total += mouse_speed[n];
5171                 }
5172                 
5173                 return mouse_direction * total/mouse_speed_entries;
5174         }
5175
5176         /* compute the average (effectively low-pass filtering) mouse speed
5177            across the entire buffer.
5178         */
5179
5180         for (size_t n = 0; n < mouse_speed_size; ++n) {
5181                 total += mouse_speed[n];
5182         }
5183
5184
5185         return mouse_direction * total/mouse_speed_size;
5186 }
5187
5188 bool
5189 Editor::update_mouse_speed ()
5190 {
5191         double speed;
5192
5193         if (!_scrubbing) {
5194                 session->request_transport_speed (0.0);
5195                 mouse_speed_update = -1;
5196                 return false;
5197         }
5198
5199         speed = compute_mouse_speed ();
5200
5201         struct timeval tmnow;
5202         
5203         gettimeofday (&tmnow, 0);
5204         double now = (tmnow.tv_sec * 1000000.0) + tmnow.tv_usec;
5205
5206         if (now - last_scrub_time > 250000) {
5207             
5208                 // 0.25 seconds since last mouse motion, start to brake
5209
5210                 if (fabs (speed) < 0.1) {
5211                         /* don't asymptotically approach zero */
5212                         memset (mouse_speed, 0, sizeof (double) * mouse_speed_size);
5213                         speed = 0.0;
5214                 } else if (fabs (speed) < 0.25) {
5215                         add_mouse_speed (fabs (speed * 0.2), mouse_direction);
5216                 } else {
5217                         add_mouse_speed (fabs (speed * 0.6), mouse_direction);
5218                 }
5219         } 
5220
5221         session->request_transport_speed (speed);
5222         return _scrubbing;
5223 }