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