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