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