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