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