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