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