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