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