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