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