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