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