remove UUIDs as implemention of PBD::ID, use static counter (not finished - counter...
[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         session->add_undo (arv->region.get_memento());
1816         arv->region.set_fade_in_length (fade_length);
1817         session->add_redo_no_execute (arv->region.get_memento());
1818         commit_reversible_command ();
1819         fade_in_drag_motion_callback (item, event);
1820 }
1821
1822 void
1823 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1824 {
1825         drag_info.item = item;
1826         drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1827         drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1828
1829         start_grab (event);
1830
1831         if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1832                 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1833                 /*NOTREACHED*/
1834         }
1835
1836         AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1837
1838         drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region.length() - (jack_nframes_t) arv->region.fade_out().back()->when + arv->region.position()); 
1839 }
1840
1841 void
1842 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1843 {
1844         AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1845         jack_nframes_t pos;
1846         jack_nframes_t fade_length;
1847
1848         if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1849                 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1850         }
1851         else {
1852                 pos = 0;
1853         }
1854
1855         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1856                 snap_to (pos);
1857         }
1858
1859         if (pos > (arv->region.last_frame() - 64)) {
1860                 fade_length = 64; // this should really be a minimum fade defined somewhere
1861         }
1862         else if (pos < arv->region.position()) {
1863                 fade_length = arv->region.length();
1864         }
1865         else {
1866                 fade_length = arv->region.last_frame() - pos;
1867         }
1868         
1869         arv->reset_fade_out_shape_width (fade_length);
1870
1871         show_verbose_duration_cursor (arv->region.last_frame() - fade_length, arv->region.last_frame(), 10);
1872
1873         drag_info.first_move = false;
1874 }
1875
1876 void
1877 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1878 {
1879         if (drag_info.first_move) return;
1880
1881         AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1882         jack_nframes_t pos;
1883         jack_nframes_t fade_length;
1884
1885         if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1886                 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1887         }
1888         else {
1889                 pos = 0;
1890         }
1891
1892         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1893                 snap_to (pos);
1894         }
1895
1896         if (pos > (arv->region.last_frame() - 64)) {
1897                 fade_length = 64; // this should really be a minimum fade defined somewhere
1898         }
1899         else if (pos < arv->region.position()) {
1900                 fade_length = arv->region.length();
1901         }
1902         else {
1903                 fade_length = arv->region.last_frame() - pos;
1904         }
1905
1906         begin_reversible_command (_("change fade out length"));
1907         session->add_undo (arv->region.get_memento());
1908         arv->region.set_fade_out_length (fade_length);
1909         session->add_redo_no_execute (arv->region.get_memento());
1910         commit_reversible_command ();
1911
1912         fade_out_drag_motion_callback (item, event);
1913 }
1914
1915 void
1916 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1917 {
1918         drag_info.item = item;
1919         drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1920         drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1921
1922         start_grab (event);
1923
1924         if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1925                 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1926                 /*NOTREACHED*/
1927         }
1928
1929         Cursor* cursor = (Cursor *) drag_info.data;
1930
1931         if (session && cursor == playhead_cursor) {
1932                 if (drag_info.was_rolling) {
1933                         session->request_stop ();
1934                 } 
1935         }
1936
1937         drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;  
1938         
1939         show_verbose_time_cursor (cursor->current_frame, 10);
1940 }
1941
1942 void
1943 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1944 {
1945         Cursor* cursor = (Cursor *) drag_info.data;
1946         jack_nframes_t adjusted_frame;
1947         
1948         if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1949                 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1950         }
1951         else {
1952                 adjusted_frame = 0;
1953         }
1954         
1955         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1956                 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1957                         snap_to (adjusted_frame);
1958                 }
1959         }
1960         
1961         if (adjusted_frame == drag_info.last_pointer_frame) return;
1962
1963         cursor->set_position (adjusted_frame);
1964         
1965         if (cursor == edit_cursor) {
1966                 edit_cursor_clock.set (cursor->current_frame);
1967         }
1968
1969         show_verbose_time_cursor (cursor->current_frame, 10);
1970
1971         drag_info.last_pointer_frame = adjusted_frame;
1972         drag_info.first_move = false;
1973 }
1974
1975 void
1976 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1977 {
1978         if (drag_info.first_move) return;
1979         
1980         cursor_drag_motion_callback (item, event);
1981         
1982         if (item == &playhead_cursor->canvas_item) {
1983                 if (session) {
1984                         session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1985                 }
1986         } else if (item == &edit_cursor->canvas_item) {
1987                 edit_cursor->set_position (edit_cursor->current_frame);
1988                 edit_cursor_clock.set (edit_cursor->current_frame);
1989         } 
1990 }
1991
1992 void
1993 Editor::update_marker_drag_item (Location *location)
1994 {
1995         double x1 = frame_to_pixel (location->start());
1996         double x2 = frame_to_pixel (location->end());
1997
1998         if (location->is_mark()) {
1999                 marker_drag_line_points.front().set_x(x1);
2000                 marker_drag_line_points.back().set_x(x1);
2001                 marker_drag_line->property_points() = marker_drag_line_points;
2002         }
2003         else {
2004                 range_marker_drag_rect->property_x1() = x1;
2005                 range_marker_drag_rect->property_x2() = x2;
2006         }
2007 }
2008
2009 void
2010 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2011 {
2012         Marker* marker;
2013
2014         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2015                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2016                 /*NOTREACHED*/
2017         }
2018
2019         bool is_start;
2020
2021         Location  *location = find_location_from_marker (marker, is_start);
2022
2023         drag_info.item = item;
2024         drag_info.data = marker;
2025         drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2026         drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2027
2028         start_grab (event);
2029
2030         drag_info.copied_location = new Location (*location);
2031         drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());       
2032         
2033         update_marker_drag_item (location);
2034
2035         if (location->is_mark()) {
2036                 marker_drag_line->show();
2037                 marker_drag_line->raise_to_top();
2038         }
2039         else {
2040                 range_marker_drag_rect->show();
2041                 range_marker_drag_rect->raise_to_top();
2042         }
2043         
2044         if (is_start) show_verbose_time_cursor (location->start(), 10);
2045         else show_verbose_time_cursor (location->end(), 10);
2046 }
2047
2048 void
2049 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2050 {
2051         jack_nframes_t f_delta; 
2052         Marker* marker = (Marker *) drag_info.data;
2053         Location  *real_location;
2054         Location  *copy_location;
2055         bool is_start;
2056         bool move_both = false;
2057
2058
2059         jack_nframes_t newframe;
2060         if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2061                 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2062         }
2063         else {
2064                 newframe = 0;
2065         }
2066
2067         jack_nframes_t next = newframe;
2068
2069         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2070                 snap_to (newframe, 0, true);
2071         }
2072         
2073         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) { 
2074                 return;
2075         }
2076
2077         /* call this to find out if its the start or end */
2078         
2079         real_location = find_location_from_marker (marker, is_start);
2080
2081         /* use the copy that we're "dragging" around */
2082         
2083         copy_location = drag_info.copied_location;
2084
2085         f_delta = copy_location->end() - copy_location->start();
2086         
2087         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2088                 move_both = true;
2089         }
2090
2091         if (is_start) { // start marker
2092
2093                 if (move_both) {
2094                         copy_location->set_start (newframe);
2095                         copy_location->set_end (newframe + f_delta);
2096                 } else  if (newframe < copy_location->end()) {
2097                         copy_location->set_start (newframe);
2098                 } else { 
2099                         snap_to (next, 1, true);
2100                         copy_location->set_end (next);
2101                         copy_location->set_start (newframe);
2102                 }
2103
2104         } else { // end marker
2105
2106                 if (move_both) {
2107                         copy_location->set_end (newframe);
2108                         copy_location->set_start (newframe - f_delta);
2109                 } else if (newframe > copy_location->start()) {
2110                         copy_location->set_end (newframe);
2111                         
2112                 } else if (newframe > 0) {
2113                         snap_to (next, -1, true);
2114                         copy_location->set_start (next);
2115                         copy_location->set_end (newframe);
2116                 }
2117         }
2118
2119         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2120         drag_info.first_move = false;
2121
2122         update_marker_drag_item (copy_location);
2123
2124         LocationMarkers* lm = find_location_markers (real_location);
2125         lm->set_position (copy_location->start(), copy_location->end());
2126         
2127         show_verbose_time_cursor (newframe, 10);
2128 }
2129
2130 void
2131 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2132 {
2133         if (drag_info.first_move) {
2134                 marker_drag_motion_callback (item, event);
2135
2136         }
2137         
2138         Marker* marker = (Marker *) drag_info.data;
2139         bool is_start;
2140
2141
2142         begin_reversible_command ( _("move marker") );
2143         session->add_undo( session->locations()->get_memento() );
2144         
2145         Location * location = find_location_from_marker (marker, is_start);
2146         
2147         if (location) {
2148                 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2149         }
2150
2151         session->add_redo_no_execute( session->locations()->get_memento() );
2152         commit_reversible_command ();
2153         
2154         marker_drag_line->hide();
2155         range_marker_drag_rect->hide();
2156 }
2157
2158 void
2159 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2160 {
2161         Marker* marker;
2162         MeterMarker* meter_marker;
2163
2164         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2165                 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2166                 /*NOTREACHED*/
2167         }
2168
2169         meter_marker = dynamic_cast<MeterMarker*> (marker);
2170
2171         MetricSection& section (meter_marker->meter());
2172
2173         if (!section.movable()) {
2174                 return;
2175         }
2176
2177         drag_info.item = item;
2178         drag_info.data = marker;
2179         drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2180         drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2181
2182         start_grab (event);
2183
2184         drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();  
2185
2186         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2187 }
2188
2189 void
2190 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2191 {
2192         Marker* marker;
2193         MeterMarker* meter_marker;
2194
2195         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2196                 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2197                 /*NOTREACHED*/
2198         }
2199
2200         meter_marker = dynamic_cast<MeterMarker*> (marker);
2201         
2202         // create a dummy marker for visual representation of moving the copy.
2203         // The actual copying is not done before we reach the finish callback.
2204         char name[64];
2205         snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2206         MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name, 
2207                                                   *new MeterSection(meter_marker->meter()));
2208
2209         drag_info.item = &new_marker->the_item();
2210         drag_info.copy = true;
2211         drag_info.data = new_marker;
2212         drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2213         drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2214
2215         start_grab (event);
2216
2217         drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();  
2218
2219         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2220 }
2221
2222 void
2223 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2224 {
2225         MeterMarker* marker = (MeterMarker *) drag_info.data;
2226         jack_nframes_t adjusted_frame;
2227
2228         if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2229                 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2230         }
2231         else {
2232                 adjusted_frame = 0;
2233         }
2234         
2235         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2236                 snap_to (adjusted_frame);
2237         }
2238         
2239         if (adjusted_frame == drag_info.last_pointer_frame) return;
2240
2241         marker->set_position (adjusted_frame);
2242         
2243         
2244         drag_info.last_pointer_frame = adjusted_frame;
2245         drag_info.first_move = false;
2246
2247         show_verbose_time_cursor (adjusted_frame, 10);
2248 }
2249
2250 void
2251 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2252 {
2253         if (drag_info.first_move) return;
2254
2255         meter_marker_drag_motion_callback (drag_info.item, event);
2256         
2257         MeterMarker* marker = (MeterMarker *) drag_info.data;
2258         BBT_Time when;
2259         
2260         TempoMap& map (session->tempo_map());
2261         map.bbt_time (drag_info.last_pointer_frame, when);
2262         
2263         if (drag_info.copy == true) {
2264                 begin_reversible_command (_("copy meter mark"));
2265                 session->add_undo (map.get_memento());
2266                 map.add_meter (marker->meter(), when);
2267                 session->add_redo_no_execute (map.get_memento());
2268                 commit_reversible_command ();
2269                 
2270                 // delete the dummy marker we used for visual representation of copying.
2271                 // a new visual marker will show up automatically.
2272                 delete marker;
2273         } else {
2274                 begin_reversible_command (_("move meter mark"));
2275                 session->add_undo (map.get_memento());
2276                 map.move_meter (marker->meter(), when);
2277                 session->add_redo_no_execute (map.get_memento());
2278                 commit_reversible_command ();
2279         }
2280 }
2281
2282 void
2283 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2284 {
2285         Marker* marker;
2286         TempoMarker* tempo_marker;
2287
2288         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2289                 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2290                 /*NOTREACHED*/
2291         }
2292
2293         if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2294                 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2295                 /*NOTREACHED*/
2296         }
2297
2298         MetricSection& section (tempo_marker->tempo());
2299
2300         if (!section.movable()) {
2301                 return;
2302         }
2303
2304         drag_info.item = item;
2305         drag_info.data = marker;
2306         drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2307         drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2308
2309         start_grab (event);
2310
2311         drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();  
2312         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2313 }
2314
2315 void
2316 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2317 {
2318         Marker* marker;
2319         TempoMarker* tempo_marker;
2320
2321         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2322                 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2323                 /*NOTREACHED*/
2324         }
2325
2326         if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2327                 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2328                 /*NOTREACHED*/
2329         }
2330
2331         // create a dummy marker for visual representation of moving the copy.
2332         // The actual copying is not done before we reach the finish callback.
2333         char name[64];
2334         snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2335         TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name, 
2336                                                   *new TempoSection(tempo_marker->tempo()));
2337
2338         drag_info.item = &new_marker->the_item();
2339         drag_info.copy = true;
2340         drag_info.data = new_marker;
2341         drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2342         drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2343
2344         start_grab (event);
2345
2346         drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2347
2348         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2349 }
2350
2351 void
2352 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2353 {
2354         TempoMarker* marker = (TempoMarker *) drag_info.data;
2355         jack_nframes_t adjusted_frame;
2356         
2357         if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2358                 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2359         }
2360         else {
2361                 adjusted_frame = 0;
2362         }
2363
2364         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2365                 snap_to (adjusted_frame);
2366         }
2367         
2368         if (adjusted_frame == drag_info.last_pointer_frame) return;
2369
2370         /* OK, we've moved far enough to make it worth actually move the thing. */
2371                 
2372         marker->set_position (adjusted_frame);
2373         
2374         show_verbose_time_cursor (adjusted_frame, 10);
2375
2376         drag_info.last_pointer_frame = adjusted_frame;
2377         drag_info.first_move = false;
2378 }
2379
2380 void
2381 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2382 {
2383         if (drag_info.first_move) return;
2384         
2385         tempo_marker_drag_motion_callback (drag_info.item, event);
2386         
2387         TempoMarker* marker = (TempoMarker *) drag_info.data;
2388         BBT_Time when;
2389         
2390         TempoMap& map (session->tempo_map());
2391         map.bbt_time (drag_info.last_pointer_frame, when);
2392         
2393         if (drag_info.copy == true) {
2394                 begin_reversible_command (_("copy tempo mark"));
2395                 session->add_undo (map.get_memento());
2396                 map.add_tempo (marker->tempo(), when);
2397                 session->add_redo_no_execute (map.get_memento());
2398                 commit_reversible_command ();
2399                 
2400                 // delete the dummy marker we used for visual representation of copying.
2401                 // a new visual marker will show up automatically.
2402                 delete marker;
2403         } else {
2404                 begin_reversible_command (_("move tempo mark"));
2405                 session->add_undo (map.get_memento());
2406                 map.move_tempo (marker->tempo(), when);
2407                 session->add_redo_no_execute (map.get_memento());
2408                 commit_reversible_command ();
2409         }
2410 }
2411
2412 void
2413 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2414 {
2415         ControlPoint* control_point;
2416
2417         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2418                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2419                 /*NOTREACHED*/
2420         }
2421
2422         // We shouldn't remove the first or last gain point
2423         if (control_point->line.is_last_point(*control_point) ||
2424                 control_point->line.is_first_point(*control_point)) {   
2425                 return;
2426         }
2427
2428         control_point->line.remove_point (*control_point);
2429 }
2430
2431 void
2432 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2433 {
2434         ControlPoint* control_point;
2435
2436         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2437                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2438                 /*NOTREACHED*/
2439         }
2440
2441         control_point->line.remove_point (*control_point);
2442 }
2443
2444 void
2445 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2446 {
2447         ControlPoint* control_point;
2448         
2449         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2450                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2451                 /*NOTREACHED*/
2452         }
2453
2454         drag_info.item = item;
2455         drag_info.data = control_point;
2456         drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2457         drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2458
2459         start_grab (event, fader_cursor);
2460
2461         control_point->line.start_drag (control_point, 0);
2462
2463         float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2464         set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction), 
2465                                    drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2466
2467         show_verbose_canvas_cursor ();
2468 }
2469
2470 void
2471 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2472 {
2473         ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2474
2475         double cx = drag_info.current_pointer_x;
2476         double cy = drag_info.current_pointer_y;
2477
2478         drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2479         drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2480
2481         if (drag_info.x_constrained) {
2482                 cx = drag_info.grab_x;
2483         }
2484         if (drag_info.y_constrained) {
2485                 cy = drag_info.grab_y;
2486         }
2487
2488         cp->line.parent_group().w2i (cx, cy);
2489
2490         cx = max (0.0, cx);
2491         cy = max (0.0, cy);
2492         cy = min ((double) cp->line.height(), cy);
2493
2494         //translate cx to frames
2495         jack_nframes_t cx_frames = unit_to_frame (cx);
2496
2497         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2498                 snap_to (cx_frames);
2499         }
2500
2501         float fraction = 1.0 - (cy / cp->line.height());
2502         
2503         bool push;
2504
2505         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2506                 push = true;
2507         } else {
2508                 push = false;
2509         }
2510
2511         cp->line.point_drag (*cp, cx_frames , fraction, push);
2512         
2513         set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2514
2515         drag_info.first_move = false;
2516 }
2517
2518 void
2519 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2520 {
2521         ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2522
2523         if (drag_info.first_move) {
2524
2525                 /* just a click */
2526                 
2527                 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2528                         reset_point_selection ();
2529                 }
2530
2531         } else {
2532                 control_point_drag_motion_callback (item, event);
2533         }
2534         cp->line.end_drag (cp);
2535 }
2536
2537 void
2538 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2539 {
2540         switch (mouse_mode) {
2541         case MouseGain:
2542                 start_line_grab (clicked_regionview->get_gain_line(), event);
2543                 break;
2544         default:
2545                 break;
2546         }
2547 }
2548
2549 void
2550 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2551 {
2552         AutomationLine* al;
2553         
2554         if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2555                 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2556                 /*NOTREACHED*/
2557         }
2558
2559         start_line_grab (al, event);
2560 }
2561
2562 void
2563 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2564 {
2565         double cx;
2566         double cy;
2567         jack_nframes_t frame_within_region;
2568
2569         /* need to get x coordinate in terms of parent (TimeAxisItemView)
2570            origin.
2571         */
2572
2573         cx = event->button.x;
2574         cy = event->button.y;
2575         line->parent_group().w2i (cx, cy);
2576         frame_within_region = (jack_nframes_t) floor (cx * frames_per_unit);
2577
2578         if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before, 
2579                                             current_line_drag_info.after)) {
2580                 /* no adjacent points */
2581                 return;
2582         }
2583
2584         drag_info.item = &line->grab_item();
2585         drag_info.data = line;
2586         drag_info.motion_callback = &Editor::line_drag_motion_callback;
2587         drag_info.finished_callback = &Editor::line_drag_finished_callback;
2588
2589         start_grab (event, fader_cursor);
2590
2591         double fraction = 1.0 - (cy / line->height());
2592
2593         line->start_drag (0, fraction);
2594         
2595         set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2596                                    drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2597         show_verbose_canvas_cursor ();
2598 }
2599
2600 void
2601 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2602 {
2603         AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2604         double cx = drag_info.current_pointer_x;
2605         double cy = drag_info.current_pointer_y;
2606
2607         line->parent_group().w2i (cx, cy);
2608         
2609         double fraction;
2610         fraction = 1.0 - (cy / line->height());
2611
2612         bool push;
2613
2614         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2615                 push = false;
2616         } else {
2617                 push = true;
2618         }
2619
2620         line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2621         
2622         set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2623 }
2624
2625 void
2626 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2627 {
2628         AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2629         line_drag_motion_callback (item, event);
2630         line->end_drag (0);
2631 }
2632
2633 void
2634 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2635 {
2636         if (selection->audio_regions.empty() || clicked_regionview == 0) {
2637                 return;
2638         }
2639
2640         drag_info.copy = false;
2641         drag_info.item = item;
2642         drag_info.data = clicked_regionview;
2643         drag_info.motion_callback = &Editor::region_drag_motion_callback;
2644         drag_info.finished_callback = &Editor::region_drag_finished_callback;
2645
2646         start_grab (event);
2647
2648         double speed = 1.0;
2649         TimeAxisView* tvp = clicked_trackview;
2650         AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2651
2652         if (tv && tv->is_audio_track()) {
2653                 speed = tv->get_diskstream()->speed();
2654         }
2655         
2656         drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2657         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2658         drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2659         // we want a move threshold
2660         drag_info.want_move_threshold = true;
2661         
2662         show_verbose_time_cursor (drag_info.last_frame_position, 10);
2663
2664         begin_reversible_command (_("move region(s)"));
2665 }
2666
2667 void
2668 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2669 {
2670         if (selection->audio_regions.empty() || clicked_regionview == 0) {
2671                 return;
2672         }
2673
2674         drag_info.copy = true;
2675         drag_info.item = item;
2676         drag_info.data = clicked_regionview;    
2677
2678         start_grab(event);
2679
2680         TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2681         AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
2682         double speed = 1.0;
2683
2684         if (atv && atv->is_audio_track()) {
2685                 speed = atv->get_diskstream()->speed();
2686         }
2687         
2688         drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2689         drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2690         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2691         // we want a move threshold
2692         drag_info.want_move_threshold = true;
2693         drag_info.motion_callback = &Editor::region_drag_motion_callback;
2694         drag_info.finished_callback = &Editor::region_drag_finished_callback;
2695 }
2696
2697 void
2698 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2699 {
2700         if (selection->audio_regions.empty() || clicked_regionview == 0) {
2701                 return;
2702         }
2703
2704         drag_info.copy = false;
2705         drag_info.item = item;
2706         drag_info.data = clicked_regionview;
2707         drag_info.motion_callback = &Editor::region_drag_motion_callback;
2708         drag_info.finished_callback = &Editor::region_drag_finished_callback;
2709
2710         start_grab (event);
2711
2712         double speed = 1.0;
2713         TimeAxisView* tvp = clicked_trackview;
2714         AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2715
2716         if (tv && tv->is_audio_track()) {
2717                 speed = tv->get_diskstream()->speed();
2718         }
2719         
2720         drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2721         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2722         drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2723         // we want a move threshold
2724         drag_info.want_move_threshold = true;
2725         drag_info.brushing = true;
2726         
2727         begin_reversible_command (_("Drag region brush"));
2728 }
2729
2730 void
2731 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2732 {
2733         double x_delta;
2734         double y_delta = 0;
2735         AudioRegionView *rv = reinterpret_cast<AudioRegionView*> (drag_info.data); 
2736         jack_nframes_t pending_region_position = 0;
2737         int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2738         int32_t visible_y_high = 0, visible_y_low = 512;  //high meaning higher numbered.. not the height on the screen
2739         bool clamp_y_axis = false;
2740         vector<int32_t>  height_list(512) ;
2741         vector<int32_t>::iterator j;
2742
2743         show_verbose_time_cursor (drag_info.last_frame_position, 10);
2744
2745         if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2746
2747                 drag_info.want_move_threshold = false; // don't copy again
2748
2749                 /* this is committed in the grab finished callback. */
2750                 
2751                 begin_reversible_command (_("Drag region copy"));
2752                 
2753                 /* duplicate the region(s) */
2754                 
2755                 vector<AudioRegionView*> new_regionviews;
2756                 
2757                 set<Playlist*> affected_playlists;
2758                 pair<set<Playlist*>::iterator,bool> insert_result;
2759                 
2760                 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
2761                         AudioRegionView* rv;
2762                         
2763                         rv = (*i);
2764                         
2765                         Playlist* to_playlist = rv->region.playlist();
2766                         AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(&rv->get_time_axis_view());
2767                         
2768                         insert_result = affected_playlists.insert (to_playlist);
2769                         if (insert_result.second) {
2770                                 session->add_undo (to_playlist->get_memento ());
2771                         }
2772                         
2773                         latest_regionview = 0;
2774                         
2775                         sigc::connection c = atv->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2776                         
2777                         /* create a new region with the same name.
2778                          */
2779                         
2780                         AudioRegion* newregion = new AudioRegion (rv->region);
2781                         
2782                         /* if the original region was locked, we don't care */
2783                         
2784                         newregion->set_locked (false);
2785                         
2786                         to_playlist->add_region (*newregion, (jack_nframes_t) (rv->region.position() * atv->get_diskstream()->speed()));
2787                         
2788                         c.disconnect ();
2789                         
2790                         if (latest_regionview) {
2791                                 new_regionviews.push_back (latest_regionview);
2792                         }
2793                 }
2794                 
2795                 if (new_regionviews.empty()) {
2796                         return;
2797                 }
2798                 
2799                 /* reset selection to new regionviews */
2800                 
2801                 selection->set (new_regionviews);
2802                 
2803                 /* reset drag_info data to reflect the fact that we are dragging the copies */
2804                 
2805                 drag_info.data = new_regionviews.front();
2806                 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2807         }
2808
2809         /* Which trackview is this ? */
2810
2811         TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2812         AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2813
2814         /* The region motion is only processed if the pointer is over
2815            an audio track.
2816         */
2817         
2818         if (!tv || !tv->is_audio_track()) {
2819                 /* To make sure we hide the verbose canvas cursor when the mouse is 
2820                    not held over and audiotrack. 
2821                 */
2822                 hide_verbose_canvas_cursor ();
2823                 return;
2824         }
2825         
2826         original_pointer_order = drag_info.last_trackview->order;
2827                 
2828         /************************************************************
2829                  Y-Delta Computation
2830         ************************************************************/   
2831
2832         if (drag_info.brushing) {
2833                 clamp_y_axis = true;
2834                 pointer_y_span = 0;
2835                 goto y_axis_done;
2836         }
2837         
2838         if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2839
2840                 int32_t children = 0, numtracks = 0;
2841                 // XXX hard coding track limit, oh my, so very very bad
2842                 bitset <1024> tracks (0x00);
2843                 /* get a bitmask representing the visible tracks */
2844
2845                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2846                         TimeAxisView *tracklist_timeview;
2847                         tracklist_timeview = (*i);
2848                         AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2849                         list<TimeAxisView*> children_list;
2850               
2851                         /* zeroes are audio tracks. ones are other types. */
2852               
2853                         if (!atv2->hidden()) {
2854                                 
2855                                 if (visible_y_high < atv2->order) {
2856                                         visible_y_high = atv2->order;
2857                                 }
2858                                 if (visible_y_low > atv2->order) {
2859                                         visible_y_low = atv2->order;
2860                                 }
2861                 
2862                                 if (!atv2->is_audio_track()) {                            
2863                                         tracks = tracks |= (0x01 << atv2->order);
2864                                 }
2865         
2866                                 height_list[atv2->order] = (*i)->height;
2867                                 children = 1;
2868                                 if ((children_list = atv2->get_child_list()).size() > 0) {
2869                                         for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) { 
2870                                                 tracks = tracks |= (0x01 << (atv2->order + children));
2871                                                 height_list[atv2->order + children] =  (*j)->height;                
2872                                                 numtracks++;
2873                                                 children++;     
2874                                         }
2875                                 }
2876                                 numtracks++;        
2877                         }
2878                 }
2879                 /* find the actual span according to the canvas */
2880
2881                 canvas_pointer_y_span = pointer_y_span;
2882                 if (drag_info.last_trackview->order >= tv->order) {
2883                         int32_t y;
2884                         for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2885                                 if (height_list[y] == 0 ) {
2886                                         canvas_pointer_y_span--;
2887                                 }
2888                         }
2889                 } else {
2890                         int32_t y;
2891                         for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2892                                 if (    height_list[y] == 0 ) {
2893                                         canvas_pointer_y_span++;
2894                                 }
2895                         }
2896                 }
2897
2898                 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
2899                         AudioRegionView* rv2;
2900                         rv2 = (*i);
2901                         double ix1, ix2, iy1, iy2;
2902                         int32_t n = 0;
2903
2904                         rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2905                         rv2->get_canvas_group()->i2w (ix1, iy1);
2906                         TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2907                         AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
2908
2909                         if (atv2->order != original_pointer_order) {    
2910                                 /* this isn't the pointer track */      
2911
2912                                 if (canvas_pointer_y_span > 0) {
2913
2914                                         /* moving up the canvas */
2915                                         if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
2916         
2917                                                 int32_t visible_tracks = 0;
2918                                                 while (visible_tracks < canvas_pointer_y_span ) {
2919                                                         visible_tracks++;
2920                   
2921                                                         while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2922                                                                 /* we're passing through a hidden track */
2923                                                                 n--;
2924                                                         }                 
2925                                                 }
2926                  
2927                                                 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {                  
2928                                                         clamp_y_axis = true;
2929                                                 }
2930                     
2931                                         } else {
2932                                                 clamp_y_axis = true;
2933                                         }                 
2934                   
2935                                 } else if (canvas_pointer_y_span < 0) {
2936
2937                                         /*moving down the canvas*/
2938
2939                                         if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2940                     
2941                     
2942                                                 int32_t visible_tracks = 0;
2943                     
2944                                                 while (visible_tracks > canvas_pointer_y_span ) {
2945                                                         visible_tracks--;
2946                       
2947                                                         while (height_list[atv2->order - (visible_tracks - n)] == 0) {             
2948                                                                 n++;
2949                                                         }                
2950                                                 }
2951                                                 if (  tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2952                                                         clamp_y_axis = true;
2953                             
2954                                                 }
2955                                         } else {
2956                           
2957                                                 clamp_y_axis = true;
2958                                         }
2959                                 }               
2960                   
2961                         } else {
2962                       
2963                                 /* this is the pointer's track */
2964                                 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow 
2965                                         clamp_y_axis = true;
2966                                 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2967                                         clamp_y_axis = true;
2968                                 }
2969                         }             
2970                         if (clamp_y_axis) {
2971                                 break;
2972                         }
2973                 }
2974
2975         } else  if (drag_info.last_trackview == tv) {
2976                 clamp_y_axis = true;
2977         }         
2978
2979   y_axis_done:
2980         if (!clamp_y_axis) {
2981                 drag_info.last_trackview = tv;        
2982         }
2983           
2984         /************************************************************
2985                         X DELTA COMPUTATION
2986         ************************************************************/
2987
2988         /* compute the amount of pointer motion in frames, and where
2989            the region would be if we moved it by that much.
2990         */
2991
2992         if (drag_info.move_threshold_passed) {
2993
2994                 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2995
2996                         jack_nframes_t sync_frame;
2997                         jack_nframes_t sync_offset;
2998                         int32_t sync_dir;
2999             
3000                         pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3001             
3002                         sync_offset = rv->region.sync_offset (sync_dir);
3003                         sync_frame = rv->region.adjust_to_sync (pending_region_position);
3004
3005                         /* we snap if the snap modifier is not enabled.
3006                          */
3007             
3008                         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3009                                 snap_to (sync_frame);   
3010                         }
3011             
3012                         if (sync_frame - sync_offset <= sync_frame) {
3013                                 pending_region_position = sync_frame - (sync_dir*sync_offset);
3014                         } else {
3015                                 pending_region_position = 0;
3016                         }
3017             
3018                 } else {
3019                         pending_region_position = 0;
3020                 }
3021           
3022                 if (pending_region_position > max_frames - rv->region.length()) {
3023                         pending_region_position = drag_info.last_frame_position;
3024                 }
3025           
3026                 // printf ("3: pending_region_position= %lu    %lu\n", pending_region_position, drag_info.last_frame_position );
3027           
3028                 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3029
3030                         /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3031                            to make it appear at the new location.
3032                         */
3033             
3034                         if (pending_region_position > drag_info.last_frame_position) {
3035                                 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3036                         } else {
3037                                 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3038                         }
3039             
3040                         drag_info.last_frame_position = pending_region_position;
3041             
3042                 } else {
3043                         x_delta = 0;
3044                 }
3045
3046         } else {
3047                 /* threshold not passed */
3048
3049                 x_delta = 0;
3050         }
3051
3052         /*************************************************************
3053                         PREPARE TO MOVE
3054         ************************************************************/
3055
3056         if (x_delta == 0 && (pointer_y_span == 0)) {
3057                 /* haven't reached next snap point, and we're not switching
3058                    trackviews. nothing to do.
3059                 */
3060                 return;
3061         } 
3062
3063         if (x_delta < 0) {
3064                 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3065
3066                         AudioRegionView* rv2;
3067                         rv2 = (*i);
3068
3069                         /* if any regionview is at zero, we need to know so we can 
3070                            stop further leftward motion.
3071                         */
3072                         
3073                         double ix1, ix2, iy1, iy2;
3074                         rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3075                         rv2->get_canvas_group()->i2w (ix1, iy1);
3076
3077                         if (ix1 <= 1) {
3078                                 x_delta = 0;
3079                                 break;
3080                         }
3081                 }
3082         }
3083
3084         /*************************************************************
3085                         MOTION                                                                
3086         ************************************************************/
3087
3088         pair<set<Playlist*>::iterator,bool> insert_result;
3089         const list<AudioRegionView*>& layered_regions = selection->audio_regions.by_layer();
3090
3091         for (list<AudioRegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3092             
3093                 AudioRegionView* rv;
3094                 rv = (*i);
3095                 double ix1, ix2, iy1, iy2;
3096                 int32_t temp_pointer_y_span = pointer_y_span;
3097
3098                 /* get item BBox, which will be relative to parent. so we have
3099                    to query on a child, then convert to world coordinates using
3100                    the parent.
3101                 */
3102
3103                 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3104                 rv->get_canvas_group()->i2w (ix1, iy1);
3105                 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3106                 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3107                 AudioTimeAxisView* temp_atv;
3108
3109                 if ((pointer_y_span != 0) && !clamp_y_axis) {
3110                         y_delta = 0;
3111                         int32_t x = 0;
3112                         for (j = height_list.begin(); j!= height_list.end(); j++) {     
3113                                 if (x == canvas_atv->order) {
3114                                         /* we found the track the region is on */
3115                                         if (x != original_pointer_order) {
3116                                                 /*this isn't from the same track we're dragging from */
3117                                                 temp_pointer_y_span = canvas_pointer_y_span;
3118                                         }                 
3119                                         while (temp_pointer_y_span > 0) {
3120                                                 /* we're moving up canvas-wise,
3121                                                    so  we need to find the next track height
3122                                                 */
3123                                                 if (j != height_list.begin()) {           
3124                                                         j--;
3125                                                 }
3126                                                 if (x != original_pointer_order) { 
3127                                                         /* we're not from the dragged track, so ignore hidden tracks. */              
3128                                                         if ((*j) == 0) {
3129                                                                 temp_pointer_y_span++;
3130                                                         }
3131                                                 }          
3132                                                 y_delta -= (*j);        
3133                                                 temp_pointer_y_span--;  
3134                                         }
3135                                         while (temp_pointer_y_span < 0) {                 
3136                                                 y_delta += (*j);
3137                                                 if (x != original_pointer_order) { 
3138                                                         if ((*j) == 0) {
3139                                                                 temp_pointer_y_span--;
3140                                                         }
3141                                                 }          
3142                     
3143                                                 if (j != height_list.end()) {                 
3144                                                         j++;
3145                                                 }
3146                                                 temp_pointer_y_span++;
3147                                         }
3148                                         /* find out where we'll be when we move and set height accordingly */
3149                   
3150                                         tvp2 = trackview_by_y_position (iy1 + y_delta);
3151                                         temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3152                                         rv->set_height (temp_atv->height);
3153         
3154                                         /*   if you un-comment the following, the region colours will follow the track colours whilst dragging,
3155                                              personally, i think this can confuse things, but never mind.
3156                                         */
3157                                   
3158                                         //const GdkColor& col (temp_atv->view->get_region_color());
3159                                         //rv->set_color (const_cast<GdkColor&>(col));
3160                                         break;          
3161                                 }
3162                                 x++;
3163                         }
3164                 }
3165           
3166                 /* prevent the regionview from being moved to before 
3167                    the zero position on the canvas.
3168                 */
3169                 /* clamp */
3170                 
3171                 if (x_delta < 0) {
3172                         if (-x_delta > ix1) {
3173                                 x_delta = -ix1;
3174                         }
3175                 } else if ((x_delta > 0) &&(rv->region.last_frame() > max_frames - x_delta)) {
3176                         x_delta = max_frames - rv->region.last_frame();
3177                 }
3178                         
3179                 if (drag_info.first_move) {
3180
3181                         /* hide any dependent views */
3182
3183 //                      rv->get_time_axis_view().hide_dependent_views (*rv);
3184                                 
3185                         /* this is subtle. raising the regionview itself won't help,
3186                            because raise_to_top() just puts the item on the top of
3187                            its parent's stack. so, we need to put the trackview canvas_display group
3188                            on the top, since its parent is the whole canvas.
3189                         */
3190
3191                         rv->get_canvas_group()->raise_to_top();
3192                         rv->get_time_axis_view().canvas_display->raise_to_top();
3193                         cursor_group->raise_to_top();
3194
3195                         /* freeze the playlists from notifying till
3196                            the motion is done.
3197                         */
3198
3199                         AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&rv->get_time_axis_view());
3200                         if (atv && atv->is_audio_track()) {
3201                                 AudioPlaylist* pl = atv->get_diskstream()->playlist();
3202                                 if (pl) {
3203                                         /* only freeze and capture state once */
3204
3205                                         insert_result = motion_frozen_playlists.insert (pl);
3206                                         if (insert_result.second) {
3207                                                 pl->freeze();
3208                                                 session->add_undo(pl->get_memento());
3209                                         }
3210                                 }
3211                         }
3212                 }
3213
3214                 if (drag_info.brushing) {
3215                         mouse_brush_insert_region (rv, pending_region_position);
3216                 } else {
3217                         rv->move (x_delta, y_delta);                    
3218                 }
3219         }
3220                 
3221         if (drag_info.first_move) {
3222                 cursor_group->raise_to_top();
3223         }
3224                 
3225         drag_info.first_move = false;
3226                 
3227         if (x_delta != 0 && !drag_info.brushing) {
3228                 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3229         }
3230                 
3231
3232
3233 void
3234 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3235 {
3236         jack_nframes_t where;
3237         AudioRegionView* rv = reinterpret_cast<AudioRegionView *> (drag_info.data);
3238         pair<set<Playlist*>::iterator,bool> insert_result;
3239         bool nocommit = true;
3240         double speed;
3241         AudioTimeAxisView* atv;
3242         bool regionview_y_movement;
3243         bool regionview_x_movement;
3244
3245         /* first_move is set to false if the regionview has been moved in the 
3246            motion handler. 
3247         */
3248
3249         if (drag_info.first_move) {
3250                 /* just a click */
3251                 goto out;
3252         }
3253
3254         nocommit = false;
3255
3256         /* The regionview has been moved at some stage during the grab so we need
3257            to account for any mouse movement between this event and the last one. 
3258         */      
3259
3260         region_drag_motion_callback (item, event);
3261
3262         if (drag_info.brushing) {
3263                 /* all changes were made during motion event handlers */
3264                 goto out;
3265         }
3266
3267         /* adjust for track speed */
3268         speed = 1.0;
3269
3270         atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3271         if (atv && atv->get_diskstream()) {
3272                 speed = atv->get_diskstream()->speed();
3273         }
3274         
3275         regionview_x_movement = (drag_info.last_frame_position != (jack_nframes_t) (rv->region.position()/speed));
3276         regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3277
3278         //printf ("last_frame: %s position is %lu  %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed); 
3279         //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str()); 
3280         
3281         if (regionview_y_movement) {
3282
3283                 /* motion between tracks */
3284
3285                 list<AudioRegionView*> new_selection;
3286         
3287                 /* moved to a different audio track. */
3288
3289                 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ) {
3290             
3291                         AudioRegionView* rv2 = (*i);                
3292             
3293                         /* the region that used to be in the old playlist is not
3294                            moved to the new one - we make a copy of it. as a result,
3295                            any existing editor for the region should no longer be
3296                            visible.
3297                         */ 
3298             
3299                         if (!drag_info.copy) {
3300                                 rv2->hide_region_editor();
3301                         }           
3302                         new_selection.push_back (rv2);      
3303                         i++;
3304                 }
3305
3306                 /* first, freeze the target tracks */
3307
3308                 for (list<AudioRegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3309
3310                         Playlist* from_playlist;
3311                         Playlist* to_playlist;
3312                                 
3313                         double ix1, ix2, iy1, iy2;
3314             
3315                         (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3316                         (*i)->get_canvas_group()->i2w (ix1, iy1);
3317                         TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3318                         AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3319             
3320                         from_playlist = (*i)->region.playlist();
3321                         to_playlist = atv2->playlist();
3322
3323                         /* the from_playlist was frozen in the "first_move" case 
3324                            of the motion handler. the insert can fail, 
3325                            but that doesn't matter. it just means
3326                            we already have the playlist in the list.
3327                         */
3328                         
3329                         motion_frozen_playlists.insert (from_playlist);
3330
3331                         /* only freeze the to_playlist once */
3332
3333                         insert_result = motion_frozen_playlists.insert(to_playlist);
3334                         if (insert_result.second) {
3335                                 to_playlist->freeze();
3336                                 session->add_undo(to_playlist->get_memento());
3337                         }
3338
3339                 }
3340
3341                 /* now do it again with the actual operations */
3342
3343                 for (list<AudioRegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3344
3345                         Playlist* from_playlist;
3346                         Playlist* to_playlist;
3347                                 
3348                         double ix1, ix2, iy1, iy2;
3349             
3350                         (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3351                         (*i)->get_canvas_group()->i2w (ix1, iy1);
3352                         TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3353                         AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3354             
3355                         from_playlist = (*i)->region.playlist();
3356                         to_playlist = atv2->playlist();
3357
3358                         latest_regionview = 0;
3359             
3360                         where = (jack_nframes_t) (unit_to_frame (ix1) * speed);
3361                         Region* new_region = createRegion ((*i)->region);
3362
3363                         from_playlist->remove_region (&((*i)->region));
3364           
3365                         sigc::connection c = atv2->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3366                         to_playlist->add_region (*new_region, where);
3367                         c.disconnect ();
3368                         
3369                         if (latest_regionview) {
3370                                 selection->add (latest_regionview);
3371                         }
3372                 } 
3373
3374         } else {
3375
3376                 /* motion within a single track */
3377                 
3378                 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3379
3380                         rv = (*i);
3381
3382                         if (rv->region.locked()) {
3383                                 continue;
3384                         }
3385                         
3386                         if (regionview_x_movement) {
3387                                 double ownspeed = 1.0;
3388                                 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3389
3390                                 if (atv && atv->get_diskstream()) {
3391                                         ownspeed = atv->get_diskstream()->speed();
3392                                 }
3393                                 
3394                                 /* base the new region position on the current position of the regionview.*/
3395                                 
3396                                 double ix1, ix2, iy1, iy2;
3397                                 
3398                                 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3399                                 rv->get_canvas_group()->i2w (ix1, iy1);
3400                                 where = (jack_nframes_t) (unit_to_frame (ix1) * ownspeed);
3401                                 
3402                         } else {
3403                                 
3404                                 where = rv->region.position();
3405                         }
3406
3407                         rv->get_time_axis_view().reveal_dependent_views (*rv);
3408
3409                         /* no need to add an undo here, we did that when we added this playlist to motion_frozen playlists */
3410                         
3411                         rv->region.set_position (where, (void *) this);
3412                 }
3413         }
3414
3415   out:
3416         for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
3417                 (*p)->thaw ();
3418                 session->add_redo_no_execute ((*p)->get_memento());
3419         }
3420
3421         motion_frozen_playlists.clear ();
3422
3423         if (!nocommit) {
3424                 commit_reversible_command ();
3425         }
3426 }
3427
3428 void
3429 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3430 {
3431         /* Either add to or set the set the region selection, unless
3432            this is an alignment click (control used)
3433         */
3434         
3435         if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3436                 TimeAxisView* tv = &rv.get_time_axis_view();
3437                 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3438                 double speed = 1.0;
3439                 if (atv && atv->is_audio_track()) {
3440                         speed = atv->get_diskstream()->speed();
3441                 }
3442
3443                 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3444
3445                         align_region (rv.region, SyncPoint, (jack_nframes_t) (edit_cursor->current_frame * speed));
3446
3447                 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3448
3449                         align_region (rv.region, End, (jack_nframes_t) (edit_cursor->current_frame * speed));
3450
3451                 } else {
3452
3453                         align_region (rv.region, Start, (jack_nframes_t) (edit_cursor->current_frame * speed));
3454                 }
3455         }
3456 }
3457
3458 void
3459 Editor::show_verbose_time_cursor (jack_nframes_t frame, double offset, double xpos, double ypos) 
3460 {
3461         char buf[128];
3462         SMPTE::Time smpte;
3463         BBT_Time bbt;
3464         float secs;
3465
3466         if (session == 0) {
3467                 return;
3468         }
3469
3470         switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3471         case AudioClock::BBT:
3472                 session->bbt_time (frame, bbt);
3473                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3474                 break;
3475                 
3476         case AudioClock::SMPTE:
3477                 session->smpte_time (frame, smpte);
3478                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3479                 break;
3480
3481         case AudioClock::MinSec:
3482                 /* XXX fix this to compute min/sec properly */
3483                 session->smpte_time (frame, smpte);
3484                 secs = smpte.seconds + ((float) smpte.frames / session->smpte_frames_per_second);
3485                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", smpte.hours, smpte.minutes, secs);
3486                 break;
3487
3488         default:
3489                 snprintf (buf, sizeof(buf), "%u", frame);
3490                 break;
3491         }
3492
3493         if (xpos >= 0 && ypos >=0) {
3494                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3495         }
3496         else {
3497                 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3498         }
3499         show_verbose_canvas_cursor ();
3500 }
3501
3502 void
3503 Editor::show_verbose_duration_cursor (jack_nframes_t start, jack_nframes_t end, double offset, double xpos, double ypos) 
3504 {
3505         char buf[128];
3506         SMPTE::Time smpte;
3507         BBT_Time sbbt;
3508         BBT_Time ebbt;
3509         float secs;
3510         Meter meter_at_start(session->tempo_map().meter_at(start));
3511
3512         if (session == 0) {
3513                 return;
3514         }
3515
3516         switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3517         case AudioClock::BBT:
3518                 session->bbt_time (start, sbbt);
3519                 session->bbt_time (end, ebbt);
3520
3521                 /* subtract */
3522                 /* XXX this computation won't work well if the
3523                 user makes a selection that spans any meter changes.
3524                 */
3525
3526                 ebbt.bars -= sbbt.bars;
3527                 if (ebbt.beats >= sbbt.beats) {
3528                         ebbt.beats -= sbbt.beats;
3529                 } else {
3530                         ebbt.bars--;
3531                         ebbt.beats =  int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3532                 }
3533                 if (ebbt.ticks >= sbbt.ticks) {
3534                         ebbt.ticks -= sbbt.ticks;
3535                 } else {
3536                         ebbt.beats--;
3537                         ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3538                 }
3539                 
3540                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3541                 break;
3542                 
3543         case AudioClock::SMPTE:
3544                 session->smpte_duration (end - start, smpte);
3545                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3546                 break;
3547
3548         case AudioClock::MinSec:
3549                 /* XXX fix this to compute min/sec properly */
3550                 session->smpte_duration (end - start, smpte);
3551                 secs = smpte.seconds + ((float) smpte.frames / session->smpte_frames_per_second);
3552                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", smpte.hours, smpte.minutes, secs);
3553                 break;
3554
3555         default:
3556                 snprintf (buf, sizeof(buf), "%u", end - start);
3557                 break;
3558         }
3559
3560         if (xpos >= 0 && ypos >=0) {
3561                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3562         }
3563         else {
3564                 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3565         }
3566         show_verbose_canvas_cursor ();
3567 }
3568
3569 void
3570 Editor::collect_new_region_view (AudioRegionView* rv)
3571 {
3572         latest_regionview = rv;
3573 }
3574
3575 void
3576 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3577 {
3578         if (clicked_regionview == 0) {
3579                 return;
3580         }
3581
3582         /* lets try to create new Region for the selection */
3583
3584         vector<AudioRegion*> new_regions;
3585         create_region_from_selection (new_regions);
3586
3587         if (new_regions.empty()) {
3588                 return;
3589         }
3590
3591         /* XXX fix me one day to use all new regions */
3592         
3593         Region* region = new_regions.front();
3594
3595         /* add it to the current stream/playlist.
3596
3597            tricky: the streamview for the track will add a new regionview. we will
3598            catch the signal it sends when it creates the regionview to
3599            set the regionview we want to then drag.
3600         */
3601         
3602         latest_regionview = 0;
3603         sigc::connection c = clicked_audio_trackview->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3604         
3605         /* A selection grab currently creates two undo/redo operations, one for 
3606            creating the new region and another for moving it.
3607         */
3608
3609         begin_reversible_command (_("selection grab"));
3610
3611         Playlist* playlist = clicked_trackview->playlist();
3612
3613         session->add_undo (playlist->get_memento ());
3614         clicked_trackview->playlist()->add_region (*region, selection->time[clicked_selection].start);
3615         session->add_redo_no_execute (playlist->get_memento ());
3616
3617         commit_reversible_command ();
3618         
3619         c.disconnect ();
3620         
3621         if (latest_regionview == 0) {
3622                 /* something went wrong */
3623                 return;
3624         }
3625
3626         /* we need to deselect all other regionviews, and select this one
3627            i'm ignoring undo stuff, because the region creation will take care of it */
3628         selection->set (latest_regionview);
3629         
3630         drag_info.item = latest_regionview->get_canvas_group();
3631         drag_info.data = latest_regionview;
3632         drag_info.motion_callback = &Editor::region_drag_motion_callback;
3633         drag_info.finished_callback = &Editor::region_drag_finished_callback;
3634
3635         start_grab (event);
3636         
3637         drag_info.last_trackview = clicked_trackview;
3638         drag_info.last_frame_position = latest_regionview->region.position();
3639         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3640         
3641         show_verbose_time_cursor (drag_info.last_frame_position, 10);
3642 }
3643
3644 void
3645 Editor::cancel_selection ()
3646 {
3647         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3648                 (*i)->hide_selection ();
3649         }
3650         begin_reversible_command (_("cancel selection"));
3651         selection->clear ();
3652         clicked_selection = 0;
3653         commit_reversible_command ();
3654 }       
3655
3656 void
3657 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3658 {
3659         jack_nframes_t start = 0;
3660         jack_nframes_t end = 0;
3661
3662         if (session == 0) {
3663                 return;
3664         }
3665
3666         drag_info.item = item;
3667         drag_info.motion_callback = &Editor::drag_selection;
3668         drag_info.finished_callback = &Editor::end_selection_op;
3669
3670         selection_op = op;
3671
3672         switch (op) {
3673         case CreateSelection:
3674                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3675                         drag_info.copy = true;
3676                 } else {
3677                         drag_info.copy = false;
3678                 }
3679                 start_grab (event, selector_cursor);
3680                 break;
3681
3682         case SelectionStartTrim:
3683                 if (clicked_trackview) {
3684                         clicked_trackview->order_selection_trims (item, true);
3685                 } 
3686                 start_grab (event, trimmer_cursor);
3687                 start = selection->time[clicked_selection].start;
3688                 drag_info.pointer_frame_offset = drag_info.grab_frame - start;  
3689                 break;
3690                 
3691         case SelectionEndTrim:
3692                 if (clicked_trackview) {
3693                         clicked_trackview->order_selection_trims (item, false);
3694                 }
3695                 start_grab (event, trimmer_cursor);
3696                 end = selection->time[clicked_selection].end;
3697                 drag_info.pointer_frame_offset = drag_info.grab_frame - end;    
3698                 break;
3699
3700         case SelectionMove:
3701                 start = selection->time[clicked_selection].start;
3702                 start_grab (event);
3703                 drag_info.pointer_frame_offset = drag_info.grab_frame - start;  
3704                 break;
3705         }
3706
3707         if (selection_op == SelectionMove) {
3708                 show_verbose_time_cursor(start, 10);    
3709         } else {
3710                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
3711         }
3712 }
3713
3714 void
3715 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3716 {
3717         jack_nframes_t start = 0;
3718         jack_nframes_t end = 0;
3719         jack_nframes_t length;
3720         jack_nframes_t pending_position;
3721
3722         if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3723                 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3724         }
3725         else {
3726                 pending_position = 0;
3727         }
3728         
3729         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3730                 snap_to (pending_position);
3731         }
3732
3733         /* only alter selection if the current frame is 
3734            different from the last frame position (adjusted)
3735          */
3736         
3737         if (pending_position == drag_info.last_pointer_frame) return;
3738         
3739         switch (selection_op) {
3740         case CreateSelection:
3741                 
3742                 if (drag_info.first_move) {
3743                         snap_to (drag_info.grab_frame);
3744                 }
3745                 
3746                 if (pending_position < drag_info.grab_frame) {
3747                         start = pending_position;
3748                         end = drag_info.grab_frame;
3749                 } else {
3750                         end = pending_position;
3751                         start = drag_info.grab_frame;
3752                 }
3753                 
3754                 /* first drag: Either add to the selection
3755                    or create a new selection->
3756                 */
3757                 
3758                 if (drag_info.first_move) {
3759                         
3760                         begin_reversible_command (_("range selection"));
3761                         
3762                         if (drag_info.copy) {
3763                                 /* adding to the selection */
3764                                 clicked_selection = selection->add (start, end);
3765                                 drag_info.copy = false;
3766                         } else {
3767                                 /* new selection-> */
3768                                 clicked_selection = selection->set (clicked_trackview, start, end);
3769                         }
3770                 } 
3771                 break;
3772                 
3773         case SelectionStartTrim:
3774                 
3775                 if (drag_info.first_move) {
3776                         begin_reversible_command (_("trim selection start"));
3777                 }
3778                 
3779                 start = selection->time[clicked_selection].start;
3780                 end = selection->time[clicked_selection].end;
3781
3782                 if (pending_position > end) {
3783                         start = end;
3784                 } else {
3785                         start = pending_position;
3786                 }
3787                 break;
3788                 
3789         case SelectionEndTrim:
3790                 
3791                 if (drag_info.first_move) {
3792                         begin_reversible_command (_("trim selection end"));
3793                 }
3794                 
3795                 start = selection->time[clicked_selection].start;
3796                 end = selection->time[clicked_selection].end;
3797
3798                 if (pending_position < start) {
3799                         end = start;
3800                 } else {
3801                         end = pending_position;
3802                 }
3803                 
3804                 break;
3805                 
3806         case SelectionMove:
3807                 
3808                 if (drag_info.first_move) {
3809                         begin_reversible_command (_("move selection"));
3810                 }
3811                 
3812                 start = selection->time[clicked_selection].start;
3813                 end = selection->time[clicked_selection].end;
3814                 
3815                 length = end - start;
3816                 
3817                 start = pending_position;
3818                 snap_to (start);
3819                 
3820                 end = start + length;
3821                 
3822                 break;
3823         }
3824         
3825         if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3826                 start_canvas_autoscroll (1);
3827         }
3828
3829         if (start != end) {
3830                 selection->replace (clicked_selection, start, end);
3831         }
3832
3833         drag_info.last_pointer_frame = pending_position;
3834         drag_info.first_move = false;
3835
3836         if (selection_op == SelectionMove) {
3837                 show_verbose_time_cursor(start, 10);    
3838         } else {
3839                 show_verbose_time_cursor(pending_position, 10); 
3840         }
3841 }
3842
3843 void
3844 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3845 {
3846         if (!drag_info.first_move) {
3847                 drag_selection (item, event);
3848                 /* XXX this is not object-oriented programming at all. ick */
3849                 if (selection->time.consolidate()) {
3850                         selection->TimeChanged ();
3851                 }
3852                 commit_reversible_command ();
3853         } else {
3854                 /* just a click, no pointer movement.*/
3855
3856                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3857
3858                         selection->clear_time();
3859
3860                 } 
3861         }
3862
3863         /* XXX what happens if its a music selection? */
3864         session->set_audio_range (selection->time);
3865         stop_canvas_autoscroll ();
3866 }
3867
3868 void
3869 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3870 {
3871         double speed = 1.0;
3872         TimeAxisView* tvp = clicked_trackview;
3873         AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3874
3875         if (tv && tv->is_audio_track()) {
3876                 speed = tv->get_diskstream()->speed();
3877         }
3878         
3879         jack_nframes_t region_start = (jack_nframes_t) (clicked_regionview->region.position() / speed);
3880         jack_nframes_t region_end = (jack_nframes_t) (clicked_regionview->region.last_frame() / speed);
3881         jack_nframes_t region_length = (jack_nframes_t) (clicked_regionview->region.length() / speed);
3882
3883         motion_frozen_playlists.clear();
3884         
3885         //drag_info.item = clicked_regionview->get_name_highlight();
3886         drag_info.item = item;
3887         drag_info.motion_callback = &Editor::trim_motion_callback;
3888         drag_info.finished_callback = &Editor::trim_finished_callback;
3889
3890         start_grab (event, trimmer_cursor);
3891         
3892         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3893                 trim_op = ContentsTrim;
3894         } else {
3895                 /* These will get overridden for a point trim.*/
3896                 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3897                         /* closer to start */
3898                         trim_op = StartTrim;
3899                 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3900                         /* closer to end */
3901                         trim_op = EndTrim;
3902                 }
3903         }
3904
3905         switch (trim_op) {
3906         case StartTrim:
3907                 show_verbose_time_cursor(region_start, 10);     
3908                 break;
3909         case EndTrim:
3910                 show_verbose_time_cursor(region_end, 10);       
3911                 break;
3912         case ContentsTrim:
3913                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
3914                 break;
3915         }
3916 }
3917
3918 void
3919 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3920 {
3921         AudioRegionView* rv = clicked_regionview;
3922         jack_nframes_t frame_delta = 0;
3923         bool left_direction;
3924         bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3925
3926         /* snap modifier works differently here..
3927            its' current state has to be passed to the 
3928            various trim functions in order to work properly 
3929         */ 
3930
3931         double speed = 1.0;
3932         TimeAxisView* tvp = clicked_trackview;
3933         AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3934         pair<set<Playlist*>::iterator,bool> insert_result;
3935
3936         if (tv && tv->is_audio_track()) {
3937                 speed = tv->get_diskstream()->speed();
3938         }
3939         
3940         if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
3941                 left_direction = true;
3942         } else {
3943                 left_direction = false;
3944         }
3945
3946         if (obey_snap) {
3947                 snap_to (drag_info.current_pointer_frame);
3948         }
3949
3950         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
3951                 return;
3952         }
3953
3954         if (drag_info.first_move) {
3955         
3956                 string trim_type;
3957
3958                 switch (trim_op) {
3959                 case StartTrim:
3960                         trim_type = "Region start trim";
3961                         break;
3962                 case EndTrim:
3963                         trim_type = "Region end trim";
3964                         break;
3965                 case ContentsTrim:
3966                         trim_type = "Region content trim";
3967                         break;
3968                 }
3969
3970                 begin_reversible_command (trim_type);
3971
3972                 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3973                         (*i)->region.freeze ();
3974                         (*i)->temporarily_hide_envelope ();
3975
3976                         Playlist * pl = (*i)->region.playlist();
3977                         insert_result = motion_frozen_playlists.insert (pl);
3978                         if (insert_result.second) {
3979                                 session->add_undo (pl->get_memento());
3980                         }
3981                 }
3982         }
3983
3984         if (left_direction) {
3985                 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
3986         } else {
3987                 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
3988         }
3989
3990         switch (trim_op) {              
3991         case StartTrim:
3992                 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region.first_frame()/speed)) {
3993                         break;
3994                 } else {
3995                         for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3996                                 single_start_trim (**i, frame_delta, left_direction, obey_snap);
3997                         }
3998                         break;
3999                 }
4000                 
4001         case EndTrim:
4002                 if ((left_direction == true) && (drag_info.current_pointer_frame > (jack_nframes_t) (rv->region.last_frame()/speed))) {
4003                         break;
4004                 } else {
4005                         for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
4006                                 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4007                         }
4008                         break;
4009                 }
4010                 
4011         case ContentsTrim:
4012                 {
4013                         bool swap_direction = false;
4014
4015                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4016                                 swap_direction = true;
4017                         }
4018                         
4019                         for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
4020                              i != selection->audio_regions.by_layer().end(); ++i)
4021                         {
4022                                 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4023                         }
4024                 }
4025                 break;
4026         }
4027
4028         switch (trim_op) {
4029         case StartTrim:
4030                 show_verbose_time_cursor((jack_nframes_t) (rv->region.position()/speed), 10);   
4031                 break;
4032         case EndTrim:
4033                 show_verbose_time_cursor((jack_nframes_t) (rv->region.last_frame()/speed), 10); 
4034                 break;
4035         case ContentsTrim:
4036                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4037                 break;
4038         }
4039
4040         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4041         drag_info.first_move = false;
4042 }
4043
4044 void
4045 Editor::single_contents_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4046 {
4047         Region& region (rv.region);
4048
4049         if (region.locked()) {
4050                 return;
4051         }
4052
4053         jack_nframes_t new_bound;
4054
4055         double speed = 1.0;
4056         TimeAxisView* tvp = clicked_trackview;
4057         AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4058
4059         if (tv && tv->is_audio_track()) {
4060                 speed = tv->get_diskstream()->speed();
4061         }
4062         
4063         if (left_direction) {
4064                 if (swap_direction) {
4065                         new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4066                 } else {
4067                         new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4068                 }
4069         } else {
4070                 if (swap_direction) {
4071                         new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4072                 } else {
4073                         new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4074                 }
4075         }
4076
4077         if (obey_snap) {
4078                 snap_to (new_bound);
4079         }
4080         region.trim_start ((jack_nframes_t) (new_bound * speed), this); 
4081         rv.region_changed (StartChanged);
4082 }
4083
4084 void
4085 Editor::single_start_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool obey_snap)
4086 {
4087         Region& region (rv.region);     
4088
4089         if (region.locked()) {
4090                 return;
4091         }
4092
4093         jack_nframes_t new_bound;
4094
4095         double speed = 1.0;
4096         TimeAxisView* tvp = clicked_trackview;
4097         AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4098
4099         if (tv && tv->is_audio_track()) {
4100                 speed = tv->get_diskstream()->speed();
4101         }
4102         
4103         if (left_direction) {
4104                 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4105         } else {
4106                 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4107         }
4108
4109         if (obey_snap) {
4110                 snap_to (new_bound, (left_direction ? 0 : 1));  
4111         }
4112
4113         region.trim_front ((jack_nframes_t) (new_bound * speed), this);
4114
4115         rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4116 }
4117
4118 void
4119 Editor::single_end_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool obey_snap)
4120 {
4121         Region& region (rv.region);
4122
4123         if (region.locked()) {
4124                 return;
4125         }
4126
4127         jack_nframes_t new_bound;
4128
4129         double speed = 1.0;
4130         TimeAxisView* tvp = clicked_trackview;
4131         AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4132
4133         if (tv && tv->is_audio_track()) {
4134                 speed = tv->get_diskstream()->speed();
4135         }
4136         
4137         if (left_direction) {
4138                 new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) - frame_delta;
4139         } else {
4140                 new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) + frame_delta;
4141         }
4142
4143         if (obey_snap) {
4144                 snap_to (new_bound);
4145         }
4146         region.trim_end ((jack_nframes_t) (new_bound * speed), this);
4147         rv.region_changed (LengthChanged);
4148 }
4149         
4150 void
4151 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4152 {
4153         if (!drag_info.first_move) {
4154                 trim_motion_callback (item, event);
4155                 
4156                 if (!clicked_regionview->get_selected()) {
4157                         thaw_region_after_trim (*clicked_regionview);           
4158                 } else {
4159                         
4160                         for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
4161                              i != selection->audio_regions.by_layer().end(); ++i)
4162                         {
4163                                 thaw_region_after_trim (**i);
4164                         }
4165                 }
4166                 
4167                 for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4168                         //(*p)->thaw ();
4169                         session->add_redo_no_execute ((*p)->get_memento());
4170                 }
4171                 
4172                 motion_frozen_playlists.clear ();
4173
4174                 commit_reversible_command();
4175         } else {
4176                 /* no mouse movement */
4177                 point_trim (event);
4178         }
4179 }
4180
4181 void
4182 Editor::point_trim (GdkEvent* event)
4183 {
4184         AudioRegionView* rv = clicked_regionview;
4185         jack_nframes_t new_bound = drag_info.current_pointer_frame;
4186
4187         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4188                 snap_to (new_bound);
4189         }
4190
4191         /* Choose action dependant on which button was pressed */
4192         switch (event->button.button) {
4193         case 1:
4194                 trim_op = StartTrim;
4195                 begin_reversible_command (_("Start point trim"));
4196
4197                 if (rv->get_selected()) {
4198
4199                         for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
4200                              i != selection->audio_regions.by_layer().end(); ++i)
4201                         {
4202                                 if (!(*i)->region.locked()) {
4203                                         session->add_undo ((*i)->region.playlist()->get_memento());
4204                                         (*i)->region.trim_front (new_bound, this);      
4205                                         session->add_redo_no_execute ((*i)->region.playlist()->get_memento());
4206                                 }
4207                         }
4208
4209                 } else {
4210
4211                         if (!rv->region.locked()) {
4212                                 session->add_undo (rv->region.playlist()->get_memento());
4213                                 rv->region.trim_front (new_bound, this);        
4214                                 session->add_redo_no_execute (rv->region.playlist()->get_memento());
4215                         }
4216                 }
4217
4218                 commit_reversible_command();
4219         
4220                 break;
4221         case 2:
4222                 trim_op = EndTrim;
4223                 begin_reversible_command (_("End point trim"));
4224
4225                 if (rv->get_selected()) {
4226                         
4227                         for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i)
4228                         {
4229                                 if (!(*i)->region.locked()) {
4230                                         session->add_undo ((*i)->region.playlist()->get_memento());
4231                                         (*i)->region.trim_end (new_bound, this);
4232                                         session->add_redo_no_execute ((*i)->region.playlist()->get_memento());  
4233                                 }
4234                         }
4235
4236                 } else {
4237
4238                         if (!rv->region.locked()) {
4239                                 session->add_undo (rv->region.playlist()->get_memento());
4240                                 rv->region.trim_end (new_bound, this);
4241                                 session->add_redo_no_execute (rv->region.playlist()->get_memento());    
4242                         }
4243                 }
4244
4245                 commit_reversible_command();
4246         
4247                 break;
4248         default:
4249                 break;
4250         }
4251 }
4252
4253 void
4254 Editor::thaw_region_after_trim (AudioRegionView& rv)
4255 {
4256         Region& region (rv.region);
4257
4258         if (region.locked()) {
4259                 return;
4260         }
4261
4262         region.thaw (_("trimmed region"));
4263         session->add_redo_no_execute (region.playlist()->get_memento());
4264
4265         rv.unhide_envelope ();
4266 }
4267
4268 void
4269 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4270 {
4271         Marker* marker;
4272         bool is_start;
4273
4274         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4275                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4276                 /*NOTREACHED*/
4277         }
4278
4279         Location* location = find_location_from_marker (marker, is_start);      
4280         location->set_hidden (true, this);
4281 }
4282
4283
4284 void
4285 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4286 {
4287         if (session == 0) {
4288                 return;
4289         }
4290
4291         drag_info.item = item;
4292         drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4293         drag_info.finished_callback = &Editor::end_range_markerbar_op;
4294
4295         range_marker_op = op;
4296
4297         if (!temp_location) {
4298                 temp_location = new Location;
4299         }
4300         
4301         switch (op) {
4302         case CreateRangeMarker:
4303         case CreateTransportMarker:
4304                 
4305                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4306                         drag_info.copy = true;
4307                 } else {
4308                         drag_info.copy = false;
4309                 }
4310                 start_grab (event, selector_cursor);
4311                 break;
4312         }
4313
4314         show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4315         
4316 }
4317
4318 void
4319 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4320 {
4321         jack_nframes_t start = 0;
4322         jack_nframes_t end = 0;
4323         ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4324         
4325         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4326                 snap_to (drag_info.current_pointer_frame);
4327         }
4328
4329         /* only alter selection if the current frame is 
4330            different from the last frame position.
4331          */
4332         
4333         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4334         
4335         switch (range_marker_op) {
4336         case CreateRangeMarker:
4337         case CreateTransportMarker:
4338                 if (drag_info.first_move) {
4339                         snap_to (drag_info.grab_frame);
4340                 }
4341                 
4342                 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4343                         start = drag_info.current_pointer_frame;
4344                         end = drag_info.grab_frame;
4345                 } else {
4346                         end = drag_info.current_pointer_frame;
4347                         start = drag_info.grab_frame;
4348                 }
4349                 
4350                 /* first drag: Either add to the selection
4351                    or create a new selection.
4352                 */
4353                 
4354                 if (drag_info.first_move) {
4355                         
4356                         temp_location->set (start, end);
4357                         
4358                         crect->show ();
4359
4360                         update_marker_drag_item (temp_location);
4361                         range_marker_drag_rect->show();
4362                         range_marker_drag_rect->raise_to_top();
4363                         
4364                 } 
4365                 break;          
4366         }
4367         
4368         if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4369                 start_canvas_autoscroll (1);
4370         }
4371         
4372         if (start != end) {
4373                 temp_location->set (start, end);
4374
4375                 double x1 = frame_to_pixel (start);
4376                 double x2 = frame_to_pixel (end);
4377                 crect->property_x1() = x1;
4378                 crect->property_x2() = x2;
4379
4380                 update_marker_drag_item (temp_location);
4381         }
4382
4383         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4384         drag_info.first_move = false;
4385
4386         show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4387         
4388 }
4389
4390 void
4391 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4392 {
4393         Location * newloc = 0;
4394         
4395         if (!drag_info.first_move) {
4396                 drag_range_markerbar_op (item, event);
4397
4398                 switch (range_marker_op) {
4399                 case CreateRangeMarker:
4400                         begin_reversible_command (_("new range marker"));
4401                         session->add_undo (session->locations()->get_memento());
4402                         newloc = new Location(temp_location->start(), temp_location->end(), "unnamed", Location::IsRangeMarker);
4403                         session->locations()->add (newloc, true);
4404                         session->add_redo_no_execute (session->locations()->get_memento());
4405                         commit_reversible_command ();
4406                         
4407                         range_bar_drag_rect->hide();
4408                         range_marker_drag_rect->hide();
4409                         break;
4410
4411                 case CreateTransportMarker:
4412                         // popup menu to pick loop or punch
4413                         new_transport_marker_context_menu (&event->button, item);
4414                         
4415                         break;
4416                 }
4417         } else {
4418                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4419
4420                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4421
4422                         jack_nframes_t start;
4423                         jack_nframes_t end;
4424
4425                         start = session->locations()->first_mark_before (drag_info.grab_frame);
4426                         end = session->locations()->first_mark_after (drag_info.grab_frame);
4427                         
4428                         if (end == max_frames) {
4429                                 end = session->current_end_frame ();
4430                         }
4431
4432                         if (start == 0) {
4433                                 start = session->current_start_frame ();
4434                         }
4435
4436                         switch (mouse_mode) {
4437                         case MouseObject:
4438                                 /* find the two markers on either side and then make the selection from it */
4439                                 cerr << "select between " << start << " .. " << end << endl;
4440                                 select_all_within (start, end, 0.0f, FLT_MAX, Selection::Set);
4441                                 break;
4442
4443                         case MouseRange:
4444                                 /* find the two markers on either side of the click and make the range out of it */
4445                                 selection->set (0, start, end);
4446                                 break;
4447
4448                         default:
4449                                 break;
4450                         }
4451                 } 
4452         }
4453
4454         stop_canvas_autoscroll ();
4455 }
4456
4457
4458
4459 void
4460 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4461 {
4462         drag_info.item = item;
4463         drag_info.motion_callback = &Editor::drag_mouse_zoom;
4464         drag_info.finished_callback = &Editor::end_mouse_zoom;
4465
4466         start_grab (event, zoom_cursor);
4467
4468         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4469 }
4470
4471 void
4472 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4473 {
4474         jack_nframes_t start;
4475         jack_nframes_t end;
4476
4477         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4478                 snap_to (drag_info.current_pointer_frame);
4479                 
4480                 if (drag_info.first_move) {
4481                         snap_to (drag_info.grab_frame);
4482                 }
4483         }
4484                 
4485         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4486
4487         /* base start and end on initial click position */
4488         if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4489                 start = drag_info.current_pointer_frame;
4490                 end = drag_info.grab_frame;
4491         } else {
4492                 end = drag_info.current_pointer_frame;
4493                 start = drag_info.grab_frame;
4494         }
4495         
4496         if (start != end) {
4497
4498                 if (drag_info.first_move) {
4499                         zoom_rect->show();
4500                         zoom_rect->raise_to_top();
4501                 }
4502
4503                 reposition_zoom_rect(start, end);
4504
4505                 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4506                 drag_info.first_move = false;
4507
4508                 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4509         }
4510 }
4511
4512 void
4513 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4514 {
4515         if (!drag_info.first_move) {
4516                 drag_mouse_zoom (item, event);
4517                 
4518                 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4519                         temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4520                 } else {
4521                         temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4522                 }               
4523         } else {
4524                 temporal_zoom_to_frame (false, drag_info.grab_frame);
4525                 /*
4526                 temporal_zoom_step (false);
4527                 center_screen (drag_info.grab_frame);
4528                 */
4529         }
4530
4531         zoom_rect->hide();
4532 }
4533
4534 void
4535 Editor::reposition_zoom_rect (jack_nframes_t start, jack_nframes_t end)
4536 {
4537         double x1 = frame_to_pixel (start);
4538         double x2 = frame_to_pixel (end);
4539         double y2 = canvas_height - 2;
4540
4541         zoom_rect->property_x1() = x1;
4542         zoom_rect->property_y1() = 1.0;
4543         zoom_rect->property_x2() = x2;
4544         zoom_rect->property_y2() = y2;
4545 }
4546
4547 void
4548 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4549 {
4550         drag_info.item = item;
4551         drag_info.motion_callback = &Editor::drag_rubberband_select;
4552         drag_info.finished_callback = &Editor::end_rubberband_select;
4553
4554         start_grab (event, cross_hair_cursor);
4555
4556         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4557 }
4558
4559 void
4560 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4561 {
4562         jack_nframes_t start;
4563         jack_nframes_t end;
4564         double y1;
4565         double y2;
4566
4567         /* use a bigger drag threshold than the default */
4568
4569         if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4570                 return;
4571         }
4572
4573 //      if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4574 //              snap_to (drag_info.current_pointer_frame);
4575                 
4576 //              if (drag_info.first_move) {
4577 //                      snap_to (drag_info.grab_frame);
4578 //              }
4579 //      }
4580                 
4581
4582         /* base start and end on initial click position */
4583         if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4584                 start = drag_info.current_pointer_frame;
4585                 end = drag_info.grab_frame;
4586         } else {
4587                 end = drag_info.current_pointer_frame;
4588                 start = drag_info.grab_frame;
4589         }
4590
4591         if (drag_info.current_pointer_y < drag_info.grab_y) {
4592                 y1 = drag_info.current_pointer_y;
4593                 y2 = drag_info.grab_y;
4594         }
4595         else {
4596                 y2 = drag_info.current_pointer_y;
4597                 y1 = drag_info.grab_y;
4598         }
4599
4600         
4601         if (start != end || y1 != y2) {
4602
4603                 double x1 = frame_to_pixel (start);
4604                 double x2 = frame_to_pixel (end);
4605                 
4606                 rubberband_rect->property_x1() = x1;
4607                 rubberband_rect->property_y1() = y1;
4608                 rubberband_rect->property_x2() = x2;
4609                 rubberband_rect->property_y2() = y2;
4610
4611                 rubberband_rect->show();
4612                 rubberband_rect->raise_to_top();
4613                 
4614                 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4615                 drag_info.first_move = false;
4616
4617                 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4618         }
4619 }
4620
4621 void
4622 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4623 {
4624         if (!drag_info.first_move) {
4625
4626                 drag_rubberband_select (item, event);
4627
4628                 double y1,y2;
4629                 if (drag_info.current_pointer_y < drag_info.grab_y) {
4630                         y1 = drag_info.current_pointer_y;
4631                         y2 = drag_info.grab_y;
4632                 }
4633                 else {
4634                         y2 = drag_info.current_pointer_y;
4635                         y1 = drag_info.grab_y;
4636                 }
4637
4638
4639                 Selection::Operation op = Keyboard::selection_type (event->button.state);
4640                 bool commit;
4641
4642                 begin_reversible_command (_("select regions"));
4643
4644                 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4645                         commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4646                 } else {
4647                         commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4648                 }               
4649
4650                 if (commit) {
4651                         commit_reversible_command ();
4652                 }
4653                 
4654         } else {
4655                 selection->clear_audio_regions();
4656                 selection->clear_points ();
4657                 selection->clear_lines ();
4658         }
4659
4660         rubberband_rect->hide();
4661 }
4662
4663
4664 gint
4665 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4666 {
4667         using namespace Gtkmm2ext;
4668
4669         ArdourPrompter prompter (false);
4670
4671         prompter.set_prompt (_("Name for region:"));
4672         prompter.set_initial_text (clicked_regionview->region.name());
4673         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4674         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4675         prompter.show_all ();
4676         switch (prompter.run ()) {
4677         case Gtk::RESPONSE_ACCEPT:
4678         string str;
4679                 prompter.get_result(str);
4680                 if (str.length()) {
4681                 clicked_regionview->region.set_name (str);
4682                 }
4683                 break;
4684         }
4685         return true;
4686 }
4687
4688 void
4689 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4690 {
4691         drag_info.item = item;
4692         drag_info.motion_callback = &Editor::time_fx_motion;
4693         drag_info.finished_callback = &Editor::end_time_fx;
4694
4695         start_grab (event);
4696
4697         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4698 }
4699
4700 void
4701 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4702 {
4703         AudioRegionView* rv = clicked_regionview;
4704
4705         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4706                 snap_to (drag_info.current_pointer_frame);
4707         }
4708
4709         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4710                 return;
4711         }
4712
4713         if (drag_info.current_pointer_frame > rv->region.position()) {
4714                 rv->get_time_axis_view().show_timestretch (rv->region.position(), drag_info.current_pointer_frame);
4715         }
4716
4717         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4718         drag_info.first_move = false;
4719
4720         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4721 }
4722
4723 void
4724 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4725 {
4726         clicked_regionview->get_time_axis_view().hide_timestretch ();
4727
4728         if (drag_info.first_move) {
4729                 return;
4730         }
4731         
4732         jack_nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region.position();
4733         float percentage = (float) ((double) newlen - (double) clicked_regionview->region.length()) / ((double) newlen) * 100.0f;
4734         
4735         begin_reversible_command (_("timestretch"));
4736
4737         if (run_timestretch (selection->audio_regions, percentage) == 0) {
4738                 session->commit_reversible_command ();
4739         }
4740 }
4741
4742 void
4743 Editor::mouse_brush_insert_region (AudioRegionView* rv, jack_nframes_t pos)
4744 {
4745         /* no brushing without a useful snap setting */
4746
4747         switch (snap_mode) {
4748         case SnapMagnetic:
4749                 return; /* can't work because it allows region to be placed anywhere */
4750         default:
4751                 break; /* OK */
4752         }
4753
4754         switch (snap_type) {
4755         case SnapToFrame:
4756         case SnapToMark:
4757         case SnapToEditCursor:
4758                 return;
4759
4760         default:
4761                 break;
4762         }
4763
4764         /* don't brush a copy over the original */
4765         
4766         if (pos == rv->region.position()) {
4767                 return;
4768         }
4769
4770         AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(&rv->get_time_axis_view());
4771
4772         if (atv == 0 || !atv->is_audio_track()) {
4773                 return;
4774         }
4775
4776         Playlist* playlist = atv->playlist();
4777         double speed = atv->get_diskstream()->speed();
4778         
4779         session->add_undo (playlist->get_memento());
4780         playlist->add_region (*(new AudioRegion (rv->region)), (jack_nframes_t) (pos * speed));
4781         session->add_redo_no_execute (playlist->get_memento());
4782         
4783         // playlist is frozen, so we have to update manually
4784         
4785         playlist->StateChanged (Change (~0)); /* EMIT SIGNAL */
4786 }
4787
4788 gint
4789 Editor::track_height_step_timeout ()
4790 {
4791         struct timeval now;
4792         struct timeval delta;
4793         
4794         gettimeofday (&now, 0);
4795         timersub (&now, &last_track_height_step_timestamp, &delta);
4796         
4797         if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4798                 current_stepping_trackview = 0;
4799                 return false;
4800         }
4801         return true;
4802 }