Fix some unused parameter warnings.
[ardour.git] / gtk2_ardour / editor_mouse.cc
1 /*
2     Copyright (C) 2000-2001 Paul Davis 
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <cassert>
21 #include <cstdlib>
22 #include <stdint.h>
23 #include <cmath>
24 #include <set>
25 #include <string>
26 #include <algorithm>
27
28 #include "pbd/error.h"
29 #include <gtkmm2ext/utils.h>
30 #include <gtkmm2ext/tearoff.h>
31 #include "pbd/memento_command.h"
32 #include "pbd/basename.h"
33
34 #include "ardour_ui.h"
35 #include "editor.h"
36 #include "time_axis_view.h"
37 #include "audio_time_axis.h"
38 #include "audio_region_view.h"
39 #include "midi_region_view.h"
40 #include "marker.h"
41 #include "streamview.h"
42 #include "region_gain_line.h"
43 #include "automation_time_axis.h"
44 #include "control_point.h"
45 #include "prompter.h"
46 #include "utils.h"
47 #include "selection.h"
48 #include "keyboard.h"
49 #include "editing.h"
50 #include "rgb_macros.h"
51 #include "control_point_dialog.h"
52 #include "editor_drag.h"
53
54 #include "ardour/types.h"
55 #include "ardour/profile.h"
56 #include "ardour/route.h"
57 #include "ardour/audio_track.h"
58 #include "ardour/audio_diskstream.h"
59 #include "ardour/midi_diskstream.h"
60 #include "ardour/playlist.h"
61 #include "ardour/audioplaylist.h"
62 #include "ardour/audioregion.h"
63 #include "ardour/midi_region.h"
64 #include "ardour/dB.h"
65 #include "ardour/utils.h"
66 #include "ardour/region_factory.h"
67 #include "ardour/source_factory.h"
68
69 #include <bitset>
70
71 #include "i18n.h"
72
73 using namespace std;
74 using namespace ARDOUR;
75 using namespace PBD;
76 using namespace sigc;
77 using namespace Gtk;
78 using namespace Editing;
79
80 bool
81 Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
82 {
83         int x, y;
84         double wx, wy;
85         Gdk::ModifierType mask;
86         Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
87         Glib::RefPtr<const Gdk::Window> pointer_window;
88
89         if (!canvas_window) {
90                 return false;
91         }
92         
93         pointer_window = canvas_window->get_pointer (x, y, mask);
94
95         if (pointer_window == track_canvas->get_bin_window()) {
96                 wx = x;
97                 wy = y;
98                 in_track_canvas = true;
99
100         } else {
101                 in_track_canvas = false;
102                         return false;
103         }
104
105         GdkEvent event;
106         event.type = GDK_BUTTON_RELEASE;
107         event.button.x = wx;
108         event.button.y = wy;
109         
110         where = event_frame (&event, 0, 0);
111         return true;
112 }
113
114 nframes64_t
115 Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
116 {
117         double cx, cy;
118
119         if (pcx == 0) {
120                 pcx = &cx;
121         }
122         if (pcy == 0) {
123                 pcy = &cy;
124         }
125
126         *pcx = 0;
127         *pcy = 0;
128
129         switch (event->type) {
130         case GDK_BUTTON_RELEASE:
131         case GDK_BUTTON_PRESS:
132         case GDK_2BUTTON_PRESS:
133         case GDK_3BUTTON_PRESS:
134
135                 *pcx = event->button.x;
136                 *pcy = event->button.y;
137                 _trackview_group->w2i(*pcx, *pcy);
138                 break;
139         case GDK_MOTION_NOTIFY:
140         
141                 *pcx = event->motion.x;
142                 *pcy = event->motion.y;
143                 _trackview_group->w2i(*pcx, *pcy);
144                 break;
145         case GDK_ENTER_NOTIFY:
146         case GDK_LEAVE_NOTIFY:
147                 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
148                 break;
149         case GDK_KEY_PRESS:
150         case GDK_KEY_RELEASE:
151                 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
152                 break;
153         default:
154                 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
155                 break;
156         }
157
158         /* note that pixel_to_frame() never returns less than zero, so even if the pixel
159            position is negative (as can be the case with motion events in particular),
160            the frame location is always positive.
161         */
162         
163         return pixel_to_frame (*pcx);
164 }
165
166 void
167 Editor::mouse_mode_toggled (MouseMode m)
168 {
169         if (ignore_mouse_mode_toggle) {
170                 return;
171         }
172
173         switch (m) {
174         case MouseRange:
175                 if (mouse_select_button.get_active()) {
176                         set_mouse_mode (m);
177                 }
178                 break;
179
180         case MouseObject:
181                 if (mouse_move_button.get_active()) {
182                         set_mouse_mode (m);
183                 }
184                 break;
185
186         case MouseGain:
187                 if (mouse_gain_button.get_active()) {
188                         set_mouse_mode (m);
189                 }
190                 break;
191
192         case MouseZoom:
193                 if (mouse_zoom_button.get_active()) {
194                         set_mouse_mode (m);
195                 }
196                 break;
197
198         case MouseTimeFX:
199                 if (mouse_timefx_button.get_active()) {
200                         set_mouse_mode (m);
201                 }
202                 break;
203
204         case MouseAudition:
205                 if (mouse_audition_button.get_active()) {
206                         set_mouse_mode (m);
207                 }
208                 break;
209         
210         case MouseNote:
211                 if (mouse_note_button.get_active()) {
212                         set_mouse_mode (m);
213                 }
214                 break;
215
216         default:
217                 break;
218         }
219 }       
220
221 Gdk::Cursor*
222 Editor::which_grabber_cursor ()
223 {
224         switch (_edit_point) {
225         case EditAtMouse:
226                 return grabber_edit_point_cursor;
227                 break;
228         default:
229                 break;
230         }
231         return grabber_cursor;
232 }
233
234 void
235 Editor::set_canvas_cursor ()
236 {
237         switch (mouse_mode) {
238         case MouseRange:
239                 current_canvas_cursor = selector_cursor;
240                 break;
241
242         case MouseObject:
243                 current_canvas_cursor = which_grabber_cursor();
244                 break;
245
246         case MouseGain:
247                 current_canvas_cursor = cross_hair_cursor;
248                 break;
249
250         case MouseZoom:
251                 current_canvas_cursor = zoom_cursor;
252                 break;
253
254         case MouseTimeFX:
255                 current_canvas_cursor = time_fx_cursor; // just use playhead
256                 break;
257
258         case MouseAudition:
259                 current_canvas_cursor = speaker_cursor;
260                 break;
261         
262         case MouseNote:
263                 set_midi_edit_cursor (current_midi_edit_mode());
264                 break;
265         }
266
267         if (is_drawable()) {
268                 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
269         }
270 }
271
272 void
273 Editor::set_mouse_mode (MouseMode m, bool force)
274 {
275         if (_drag) {
276                 return;
277         }
278
279         if (!force && m == mouse_mode) {
280                 return;
281         }
282         
283         mouse_mode = m;
284
285         instant_save ();
286
287         if (mouse_mode != MouseRange) {
288
289                 /* in all modes except range, hide the range selection,
290                    show the object (region) selection.
291                 */
292
293                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
294                         (*i)->set_should_show_selection (true);
295                 }
296                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
297                         (*i)->hide_selection ();
298                 }
299
300         } else {
301
302                 /* 
303                    in range mode,show the range selection.
304                 */
305
306                 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
307                         if ((*i)->get_selected()) {
308                                 (*i)->show_selection (selection->time);
309                         }
310                 }
311         }
312
313         /* XXX the hack of unsetting all other buttons should go 
314            away once GTK2 allows us to use regular radio buttons drawn like
315            normal buttons, rather than my silly GroupedButton hack.
316         */
317         
318         ignore_mouse_mode_toggle = true;
319
320         switch (mouse_mode) {
321         case MouseRange:
322                 mouse_select_button.set_active (true);
323                 break;
324
325         case MouseObject:
326                 mouse_move_button.set_active (true);
327                 break;
328
329         case MouseGain:
330                 mouse_gain_button.set_active (true);
331                 break;
332
333         case MouseZoom:
334                 mouse_zoom_button.set_active (true);
335                 break;
336
337         case MouseTimeFX:
338                 mouse_timefx_button.set_active (true);
339                 break;
340
341         case MouseAudition:
342                 mouse_audition_button.set_active (true);
343                 break;
344         
345         case MouseNote:
346                 mouse_note_button.set_active (true);
347                 set_midi_edit_cursor (current_midi_edit_mode());
348                 break;
349         }
350
351         if (midi_tools_tearoff) {
352                 if (mouse_mode == MouseNote) {
353                         midi_tools_tearoff->show();
354                 } else {
355                         midi_tools_tearoff->hide();
356                 }
357         }
358         
359         ignore_mouse_mode_toggle = false;
360         
361         set_canvas_cursor ();
362 }
363
364 void
365 Editor::step_mouse_mode (bool next)
366 {
367         switch (current_mouse_mode()) {
368         case MouseObject:
369                 if (next) {
370                         if (Profile->get_sae()) {
371                                 set_mouse_mode (MouseZoom);
372                         } else {
373                                 set_mouse_mode (MouseRange);
374                         }
375                 } else {
376                         set_mouse_mode (MouseTimeFX);
377                 }
378                 break;
379
380         case MouseRange:
381                 if (next) set_mouse_mode (MouseZoom);
382                 else set_mouse_mode (MouseObject);
383                 break;
384
385         case MouseZoom:
386                 if (next) {
387                         if (Profile->get_sae()) {
388                                 set_mouse_mode (MouseTimeFX);
389                         } else {
390                                 set_mouse_mode (MouseGain);
391                         }
392                 } else {
393                         if (Profile->get_sae()) {
394                                 set_mouse_mode (MouseObject);
395                         } else {
396                                 set_mouse_mode (MouseRange);
397                         }
398                 }
399                 break;
400         
401         case MouseGain:
402                 if (next) set_mouse_mode (MouseTimeFX);
403                 else set_mouse_mode (MouseZoom);
404                 break;
405         
406         case MouseTimeFX:
407                 if (next) {
408                         set_mouse_mode (MouseAudition);
409                 } else {
410                         if (Profile->get_sae()) {
411                                 set_mouse_mode (MouseZoom);
412                         } else {
413                                 set_mouse_mode (MouseGain);
414                         }
415                 }
416                 break;
417
418         case MouseAudition:
419                 if (next) set_mouse_mode (MouseObject);
420                 else set_mouse_mode (MouseTimeFX);
421                 break;
422         
423         case MouseNote:
424                 if (next) set_mouse_mode (MouseObject);
425                 else set_mouse_mode (MouseAudition);
426                 break;
427         }
428 }
429
430 void
431 Editor::midi_edit_mode_toggled (MidiEditMode m)
432 {
433         if (ignore_midi_edit_mode_toggle) {
434                 return;
435         }
436
437         switch (m) {
438         case MidiEditPencil:
439                 if (midi_tool_pencil_button.get_active())
440                         set_midi_edit_mode (m);
441                 break;
442
443         case MidiEditSelect:
444                 if (midi_tool_select_button.get_active())
445                         set_midi_edit_mode (m);
446                 break;
447
448         case MidiEditResize:
449                 if (midi_tool_resize_button.get_active())
450                         set_midi_edit_mode (m);
451                 break;
452
453         case MidiEditErase:
454                 if (midi_tool_erase_button.get_active())
455                         set_midi_edit_mode (m);
456                 break;
457
458         default:
459                 break;
460         }
461
462         set_midi_edit_cursor(m);
463 }       
464
465
466 void
467 Editor::set_midi_edit_mode (MidiEditMode m, bool force)
468 {
469         if (_drag) {
470                 return;
471         }
472
473         if (!force && m == midi_edit_mode) {
474                 return;
475         }
476         
477         midi_edit_mode = m;
478
479         instant_save ();
480         
481         ignore_midi_edit_mode_toggle = true;
482
483         switch (midi_edit_mode) {
484         case MidiEditPencil:
485                 midi_tool_pencil_button.set_active (true);
486                 break;
487
488         case MidiEditSelect:
489                 midi_tool_select_button.set_active (true);
490                 break;
491
492         case MidiEditResize:
493                 midi_tool_resize_button.set_active (true);
494                 break;
495
496         case MidiEditErase:
497                 midi_tool_erase_button.set_active (true);
498                 break;
499         }
500
501         ignore_midi_edit_mode_toggle = false;
502
503         set_midi_edit_cursor (current_midi_edit_mode());
504
505         if (is_drawable()) {
506                 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
507         }
508 }
509
510 void
511 Editor::set_midi_edit_cursor (MidiEditMode)
512 {
513         switch (midi_edit_mode) {
514         case MidiEditPencil:
515                 current_canvas_cursor = midi_pencil_cursor;
516                 break;
517
518         case MidiEditSelect:
519                 current_canvas_cursor = midi_select_cursor;
520                 break;
521
522         case MidiEditResize:
523                 current_canvas_cursor = midi_resize_cursor;
524                 break;
525
526         case MidiEditErase:
527                 current_canvas_cursor = midi_erase_cursor;
528                 break;
529         }
530 }
531
532 void
533 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
534 {
535         /* in object/audition/timefx/gain-automation mode,
536            any button press sets the selection if the object
537            can be selected. this is a bit of hack, because
538            we want to avoid this if the mouse operation is a
539            region alignment.
540
541            note: not dbl-click or triple-click
542         */
543
544         if (((mouse_mode != MouseObject) &&
545              (mouse_mode != MouseAudition || item_type != RegionItem) &&
546              (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
547              (mouse_mode != MouseGain) &&
548              (mouse_mode != MouseRange)) ||
549
550             ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
551                 
552                 return;
553         }
554
555         if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
556
557                 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
558                         
559                         /* almost no selection action on modified button-2 or button-3 events */
560                 
561                         if (item_type != RegionItem && event->button.button != 2) {
562                                 return;
563                         }
564                 }
565         }
566
567         Selection::Operation op = Keyboard::selection_type (event->button.state);
568         bool press = (event->type == GDK_BUTTON_PRESS);
569
570         // begin_reversible_command (_("select on click"));
571         
572         switch (item_type) {
573         case RegionItem:
574                 if (mouse_mode != MouseRange) {
575                         set_selected_regionview_from_click (press, op, true);
576                 } else if (event->type == GDK_BUTTON_PRESS) {
577                         set_selected_track_as_side_effect ();
578                 }
579                 break;
580                 
581         case RegionViewNameHighlight:
582         case RegionViewName:
583                 if (mouse_mode != MouseRange) {
584                         set_selected_regionview_from_click (press, op, true);
585                 } else if (event->type == GDK_BUTTON_PRESS) {
586                         set_selected_track_as_side_effect ();
587                 }
588                 break;
589
590
591         case FadeInHandleItem:
592         case FadeInItem:
593         case FadeOutHandleItem:
594         case FadeOutItem:
595                 if (mouse_mode != MouseRange) {
596                         set_selected_regionview_from_click (press, op, true);
597                 } else if (event->type == GDK_BUTTON_PRESS) {
598                         set_selected_track_as_side_effect ();
599                 }
600                 break;
601
602         case ControlPointItem:
603                 set_selected_track_as_side_effect ();
604                 if (mouse_mode != MouseRange) {
605                         set_selected_control_point_from_click (op, false);
606                 }
607                 break;
608                 
609         case StreamItem:
610                 /* for context click or range selection, select track */
611                 if (event->button.button == 3) {
612                         set_selected_track_as_side_effect ();
613                 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
614                         set_selected_track_as_side_effect ();
615                 }
616                 break;
617                     
618         case AutomationTrackItem:
619                 set_selected_track_as_side_effect (true);
620                 break;
621                 
622         default:
623                 break;
624         }
625 }
626
627 bool
628 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
629 {
630         Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
631
632         if (canvas_window) {
633                 Glib::RefPtr<const Gdk::Window> pointer_window;
634                 int x, y;
635                 double wx, wy;
636                 Gdk::ModifierType mask;
637
638                 pointer_window = canvas_window->get_pointer (x, y, mask);
639                 
640                 if (pointer_window == track_canvas->get_bin_window()) {
641                         track_canvas->window_to_world (x, y, wx, wy);
642                         allow_vertical_scroll = true;
643                 } else {
644                         allow_vertical_scroll = false;
645                 }
646         }
647
648         track_canvas->grab_focus();
649
650         if (session && session->actively_recording()) {
651                 return true;
652         }
653
654         button_selection (item, event, item_type);
655
656         if (_drag == 0 &&
657             (Keyboard::is_delete_event (&event->button) ||
658              Keyboard::is_context_menu_event (&event->button) ||
659              Keyboard::is_edit_event (&event->button))) {
660                 
661                 /* handled by button release */
662                 return true;
663         }
664
665         switch (event->button.button) {
666         case 1:
667
668                 if (event->type == GDK_BUTTON_PRESS) {
669
670                         if (_drag) {
671                                 _drag->item()->ungrab (event->button.time);
672                         }
673
674                         /* single mouse clicks on any of these item types operate
675                            independent of mouse mode, mostly because they are
676                            not on the main track canvas or because we want
677                            them to be modeless.
678                         */
679
680                         switch (item_type) {
681                         case PlayheadCursorItem:
682                                 assert (_drag == 0);
683                                 _drag = new CursorDrag (this, item, true);
684                                 _drag->start_grab (event);
685                                 return true;
686
687                         case MarkerItem:
688                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
689                                         hide_marker (item, event);
690                                 } else {
691                                         assert (_drag == 0);
692                                         _drag = new MarkerDrag (this, item);
693                                         _drag->start_grab (event);
694                                 }
695                                 return true;
696
697                         case TempoMarkerItem:
698                                 assert (_drag == 0);
699                                 _drag = new TempoMarkerDrag (
700                                         this,
701                                         item,
702                                         Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
703                                         );
704                                 _drag->start_grab (event);
705                                 return true;
706
707                         case MeterMarkerItem:
708                                 assert (_drag == 0);
709
710                                 _drag = new MeterMarkerDrag (
711                                         this,
712                                         item, 
713                                         Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
714                                         );
715                                 
716                                 _drag->start_grab (event);
717                                 return true;
718
719                         case MarkerBarItem:
720                         case TempoBarItem:
721                         case MeterBarItem:
722                                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
723                                         assert (_drag == 0);
724                                         _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
725                                         _drag->start_grab (event);
726                                 }
727                                 return true;
728                                 break;
729
730                                 
731                         case RangeMarkerBarItem:
732                                 assert (_drag == 0);
733                                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
734                                         _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
735                                 } else {
736                                         _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker); 
737                                 }       
738                                 _drag->start_grab (event);
739                                 return true;
740                                 break;
741
742                         case CdMarkerBarItem:
743                                 assert (_drag == 0);
744                                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
745                                         _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
746                                 } else {
747                                         _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker);
748                                 }
749                                 _drag->start_grab (event);
750                                 return true;
751                                 break;
752
753                         case TransportMarkerBarItem:
754                                 assert (_drag == 0);
755                                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
756                                         _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
757                                 } else {
758                                         _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker);
759                                 }
760                                 _drag->start_grab (event);
761                                 return true;
762                                 break;
763                                 
764                         default:
765                                 break;
766                         }
767                 }
768
769                 switch (mouse_mode) {
770                 case MouseRange:
771                         switch (item_type) {
772                         case StartSelectionTrimItem:
773                                 assert (_drag == 0);
774                                 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim);
775                                 _drag->start_grab (event);
776                                 break;
777                                 
778                         case EndSelectionTrimItem:
779                                 assert (_drag == 0);
780                                 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim);
781                                 _drag->start_grab (event);
782                                 break;
783
784                         case SelectionItem:
785                                 if (Keyboard::modifier_state_contains 
786                                     (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) {
787                                         // contains and not equals because I can't use alt as a modifier alone.
788                                         start_selection_grab (item, event);
789                                 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
790                                         /* grab selection for moving */
791                                         assert (_drag == 0);
792                                         _drag = new SelectionDrag (this, item, SelectionDrag::SelectionMove);
793                                         _drag->start_grab (event);
794                                 } else {
795                                         /* this was debated, but decided the more common action was to
796                                            make a new selection */
797                                         assert (_drag == 0);
798                                         _drag = new SelectionDrag (this, item, SelectionDrag::CreateSelection);
799                                         _drag->start_grab (event);
800                                 }
801                                 break;
802
803                         default:
804                                 assert (_drag == 0);
805                                 _drag = new SelectionDrag (this, item, SelectionDrag::CreateSelection);
806                                 _drag->start_grab (event);
807                         }
808                         return true;
809                         break;
810                         
811                 case MouseObject:
812                         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
813                             event->type == GDK_BUTTON_PRESS) {
814
815                                 assert (_drag == 0);
816                                 _drag = new RubberbandSelectDrag (this, item);
817                                 _drag->start_grab (event);
818
819                         } else if (event->type == GDK_BUTTON_PRESS) {
820
821                                 switch (item_type) {
822                                 case FadeInHandleItem:
823                                 {
824                                         assert (_drag == 0);
825                                         RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
826                                         _drag = new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s);
827                                         _drag->start_grab (event);
828                                         return true;
829                                 }
830                                         
831                                 case FadeOutHandleItem:
832                                 {
833                                         assert (_drag == 0);
834                                         RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
835                                         _drag = new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s);
836                                         _drag->start_grab (event);
837                                         return true;
838                                 }
839
840                                 case RegionItem:
841                                         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
842                                                 start_region_copy_grab (item, event, clicked_regionview);
843                                         } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
844                                                 start_region_brush_grab (item, event, clicked_regionview);
845                                         } else {
846                                                 start_region_grab (item, event, clicked_regionview);
847                                         }
848                                         break;
849                                         
850                                 case RegionViewNameHighlight:
851                                 {
852                                         assert (_drag == 0);
853                                         RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
854                                         _drag = new TrimDrag (this, item, clicked_regionview, s.by_layer());
855                                         _drag->start_grab (event);
856                                         return true;
857                                         break;
858                                 }
859                                         
860                                 case RegionViewName:
861                                 {
862                                         /* rename happens on edit clicks */
863                                         assert (_drag == 0);
864                                         RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
865                                         _drag = new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer());
866                                         _drag->start_grab (event);
867                                         return true;
868                                         break;
869                                 }
870
871                                 case ControlPointItem:
872                                         assert (_drag == 0);
873                                         _drag = new ControlPointDrag (this, item);
874                                         _drag->start_grab (event);
875                                         return true;
876                                         break;
877                                         
878                                 case AutomationLineItem:
879                                         assert (_drag == 0);
880                                         _drag = new LineDrag (this, item);
881                                         _drag->start_grab (event);
882                                         return true;
883                                         break;
884
885                                 case StreamItem:
886                                 case AutomationTrackItem:
887                                         assert (_drag == 0);
888                                         _drag = new RubberbandSelectDrag (this, item);
889                                         _drag->start_grab (event);
890                                         break;
891                                         
892 #ifdef WITH_CMT
893                                 case ImageFrameHandleStartItem:
894                                         imageframe_start_handle_op(item, event) ;
895                                         return(true) ;
896                                         break ;
897                                 case ImageFrameHandleEndItem:
898                                         imageframe_end_handle_op(item, event) ;
899                                         return(true) ;
900                                         break ;
901                                 case MarkerViewHandleStartItem:
902                                         markerview_item_start_handle_op(item, event) ;
903                                         return(true) ;
904                                         break ;
905                                 case MarkerViewHandleEndItem:
906                                         markerview_item_end_handle_op(item, event) ;
907                                         return(true) ;
908                                         break ;
909                                 case MarkerViewItem:
910                                         start_markerview_grab(item, event) ;
911                                         break ;
912                                 case ImageFrameItem:
913                                         start_imageframe_grab(item, event) ;
914                                         break ;
915 #endif
916
917                                 case MarkerBarItem:
918                                         
919                                         break;
920
921                                 default:
922                                         break;
923                                 }
924                         }
925                         return true;
926                         break;
927                         
928                 case MouseGain:
929                         switch (item_type) {
930                         case RegionItem:
931                                 /* start a grab so that if we finish after moving
932                                    we can tell what happened.
933                                 */
934                                 assert (_drag == 0);
935                                 _drag = new RegionGainDrag (this, item);
936                                 _drag->start_grab (event, current_canvas_cursor);
937                                 break;
938
939                         case GainLineItem:
940                                 assert (_drag == 0);
941                                 _drag = new LineDrag (this, item);
942                                 _drag->start_grab (event);
943                                 return true;
944
945                         case ControlPointItem:
946                                 assert (_drag == 0);
947                                 _drag = new ControlPointDrag (this, item);
948                                 _drag->start_grab (event);
949                                 return true;
950                                 break;
951
952                         default:
953                                 break;
954                         }
955                         return true;
956                         break;
957
958                         switch (item_type) {
959                         case ControlPointItem:
960                                 assert (_drag == 0);
961                                 _drag = new ControlPointDrag (this, item);
962                                 _drag->start_grab (event);
963                                 break;
964
965                         case AutomationLineItem:
966                                 assert (_drag == 0);
967                                 _drag = new LineDrag (this, item);
968                                 _drag->start_grab (event);
969                                 break;
970
971                         case RegionItem:
972                                 // XXX need automation mode to identify which
973                                 // line to use
974                                 // start_line_grab_from_regionview (item, event);
975                                 break;
976
977                         default:
978                                 break;
979                         }
980                         return true;
981                         break;
982
983                 case MouseZoom:
984                         if (event->type == GDK_BUTTON_PRESS) {
985                                 assert (_drag == 0);
986                                 _drag = new MouseZoomDrag (this, item);
987                                 _drag->start_grab (event);
988                         }
989
990                         return true;
991                         break;
992
993                 case MouseTimeFX:
994                         if (item_type == RegionItem) {
995                                 assert (_drag == 0);
996                                 _drag = new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer());
997                                 _drag->start_grab (event);
998                         }
999                         break;
1000
1001                 case MouseAudition:
1002                         _drag = new ScrubDrag (this, item);
1003                         _drag->start_grab (event);
1004                         scrub_reversals = 0;
1005                         scrub_reverse_distance = 0;
1006                         last_scrub_x = event->button.x;
1007                         scrubbing_direction = 0;
1008                         track_canvas->get_window()->set_cursor (*transparent_cursor);
1009                         break;
1010
1011                 case MouseNote:
1012                         assert (_drag == 0);
1013                         _drag = new RegionCreateDrag (this, item, clicked_axisview);
1014                         _drag->start_grab (event);
1015                         break;
1016                 
1017                 default:
1018                         break;
1019                 }
1020                 break;
1021
1022         case 2:
1023                 switch (mouse_mode) {
1024                 case MouseObject:
1025                         if (event->type == GDK_BUTTON_PRESS) {
1026                                 switch (item_type) {
1027                                 case RegionItem:
1028                                         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1029                                                 start_region_copy_grab (item, event, clicked_regionview);
1030                                         } else {
1031                                                 start_region_grab (item, event, clicked_regionview);
1032                                         }
1033                                         return true;
1034                                         break;
1035                                 case ControlPointItem:
1036                                         assert (_drag == 0);
1037                                         _drag = new ControlPointDrag (this, item);
1038                                         _drag->start_grab (event);
1039                                         return true;
1040                                         break;
1041                                         
1042                                 default:
1043                                         break;
1044                                 }
1045                         }
1046                         
1047                         
1048                         switch (item_type) {
1049                         case RegionViewNameHighlight:
1050                                 assert (_drag == 0);
1051                                 _drag = new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer());
1052                                 _drag->start_grab (event);
1053                                 return true;
1054                                 break;
1055                                 
1056                         case RegionViewName:
1057                                 assert (_drag == 0);
1058                                 _drag = new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer());
1059                                 _drag->start_grab (event);
1060                                 return true;
1061                                 break;
1062                                 
1063                         default:
1064                                 break;
1065                         }
1066                         
1067                         break;
1068
1069                 case MouseRange:
1070                         if (event->type == GDK_BUTTON_PRESS) {
1071                                 /* relax till release */
1072                         }
1073                         return true;
1074                         break;
1075                                         
1076                                 
1077                 case MouseZoom:
1078                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1079                                 temporal_zoom_session();
1080                         } else {
1081                                 temporal_zoom_to_frame (true, event_frame(event));
1082                         }
1083                         return true;
1084                         break;
1085
1086                 default:
1087                         break;
1088                 }
1089
1090                 break;
1091
1092         case 3:
1093                 break;
1094
1095         default:
1096                 break;
1097
1098         }
1099
1100         return false;
1101 }
1102
1103 bool
1104 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1105 {
1106         nframes64_t where = event_frame (event, 0, 0);
1107         AutomationTimeAxisView* atv = 0;
1108         
1109         /* no action if we're recording */
1110                                                 
1111         if (session && session->actively_recording()) {
1112                 return true;
1113         }
1114
1115         /* first, see if we're finishing a drag ... */
1116
1117         bool were_dragging = false;
1118         if (_drag) {
1119                 bool const r = _drag->end_grab (event);
1120                 delete _drag;
1121                 _drag = 0;
1122                 if (r) {
1123                         /* grab dragged, so do nothing else */
1124                         return true;
1125                 }
1126
1127                 were_dragging = true;
1128         }
1129         
1130         button_selection (item, event, item_type);
1131
1132         /* edit events get handled here */
1133
1134         if (_drag == 0 && Keyboard::is_edit_event (&event->button)) {
1135                 switch (item_type) {
1136                 case RegionItem:
1137                         edit_region ();
1138                         break;
1139
1140                 case TempoMarkerItem:
1141                         edit_tempo_marker (item);
1142                         break;
1143                         
1144                 case MeterMarkerItem:
1145                         edit_meter_marker (item);
1146                         break;
1147                         
1148                 case RegionViewName:
1149                         if (clicked_regionview->name_active()) {
1150                                 return mouse_rename_region (item, event);
1151                         }
1152                         break;
1153
1154                 case ControlPointItem:
1155                         edit_control_point (item);
1156                         break;
1157
1158                 default:
1159                         break;
1160                 }
1161                 return true;
1162         }
1163
1164         /* context menu events get handled here */
1165
1166         if (Keyboard::is_context_menu_event (&event->button)) {
1167
1168                 if (_drag == 0) {
1169
1170                         /* no matter which button pops up the context menu, tell the menu
1171                            widget to use button 1 to drive menu selection.
1172                         */
1173
1174                         switch (item_type) {
1175                         case FadeInItem:
1176                         case FadeInHandleItem:
1177                         case FadeOutItem:
1178                         case FadeOutHandleItem:
1179                                 popup_fade_context_menu (1, event->button.time, item, item_type);
1180                                 break;
1181                         
1182                         case StreamItem:
1183                                 popup_track_context_menu (1, event->button.time, item_type, false, where);
1184                                 break;
1185                                 
1186                         case RegionItem:
1187                         case RegionViewNameHighlight:
1188                         case RegionViewName:
1189                                 popup_track_context_menu (1, event->button.time, item_type, false, where);
1190                                 break;
1191                                 
1192                         case SelectionItem:
1193                                 popup_track_context_menu (1, event->button.time, item_type, true, where);
1194                                 break;
1195
1196                         case AutomationTrackItem:
1197                                 popup_track_context_menu (1, event->button.time, item_type, false, where);
1198                                 break;
1199
1200                         case MarkerBarItem: 
1201                         case RangeMarkerBarItem: 
1202                         case TransportMarkerBarItem:
1203                         case CdMarkerBarItem:
1204                         case TempoBarItem:
1205                         case MeterBarItem:
1206                                 popup_ruler_menu (where, item_type);
1207                                 break;
1208
1209                         case MarkerItem:
1210                                 marker_context_menu (&event->button, item);
1211                                 break;
1212
1213                         case TempoMarkerItem:
1214                                 tm_marker_context_menu (&event->button, item);
1215                                 break;
1216                                 
1217                         case MeterMarkerItem:
1218                                 tm_marker_context_menu (&event->button, item);
1219                                 break;
1220                         
1221                         case CrossfadeViewItem:
1222                                 popup_track_context_menu (1, event->button.time, item_type, false, where);
1223                                 break;
1224
1225 #ifdef WITH_CMT
1226                         case ImageFrameItem:
1227                                 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1228                                 break ;
1229                         case ImageFrameTimeAxisItem:
1230                                 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1231                                 break ;
1232                         case MarkerViewItem:
1233                                 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1234                                 break ;
1235                         case MarkerTimeAxisItem:
1236                                 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1237                                 break ;
1238 #endif
1239                                 
1240                         default:
1241                                 break;
1242                         }
1243
1244                         return true;
1245                 }
1246         }
1247
1248         /* delete events get handled here */
1249
1250         if (_drag == 0 && Keyboard::is_delete_event (&event->button)) {
1251
1252                 switch (item_type) {
1253                 case TempoMarkerItem:
1254                         remove_tempo_marker (item);
1255                         break;
1256                         
1257                 case MeterMarkerItem:
1258                         remove_meter_marker (item);
1259                         break;
1260
1261                 case MarkerItem:
1262                         remove_marker (*item, event);
1263                         break;
1264
1265                 case RegionItem:
1266                         if (mouse_mode == MouseObject) {
1267                                 remove_clicked_region ();
1268                         }
1269                         break;
1270                         
1271                 case ControlPointItem:
1272                         if (mouse_mode == MouseGain) {
1273                                 remove_gain_control_point (item, event);
1274                         } else {
1275                                 remove_control_point (item, event);
1276                         }
1277                         break;
1278
1279                 default:
1280                         break;
1281                 }
1282                 return true;
1283         }
1284
1285         switch (event->button.button) {
1286         case 1:
1287
1288                 switch (item_type) {
1289                 /* see comments in button_press_handler */
1290                 case PlayheadCursorItem:
1291                 case MarkerItem:
1292                 case GainLineItem:
1293                 case AutomationLineItem:
1294                 case StartSelectionTrimItem:
1295                 case EndSelectionTrimItem:
1296                         return true;
1297
1298                 case MarkerBarItem:
1299                         if (!_dragging_playhead) {
1300                                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1301                                         snap_to (where, 0, true);
1302                                 }
1303                                 mouse_add_new_marker (where);
1304                         }
1305                         return true;
1306
1307                 case CdMarkerBarItem:
1308                         if (!_dragging_playhead) {
1309                                 // if we get here then a dragged range wasn't done
1310                                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1311                                         snap_to (where, 0, true);
1312                                 }
1313                                 mouse_add_new_marker (where, true);
1314                         }
1315                         return true;
1316
1317                 case TempoBarItem:
1318                         if (!_dragging_playhead) {
1319                                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1320                                         snap_to (where);
1321                                 }
1322                                 mouse_add_new_tempo_event (where);
1323                         }
1324                         return true;
1325                         
1326                 case MeterBarItem:
1327                         if (!_dragging_playhead) {
1328                                 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1329                         } 
1330                         return true;
1331                         break;
1332
1333                 default:
1334                         break;
1335                 }
1336                 
1337                 switch (mouse_mode) {
1338                 case MouseObject:
1339                         switch (item_type) {
1340                         case AutomationTrackItem:
1341                                 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1342                                 if (atv) {
1343                                         atv->add_automation_event (item, event, where, event->button.y);
1344                                 } 
1345                                 return true;
1346                                 
1347                                 break;
1348                                 
1349                         default:
1350                                 break;
1351                         }
1352                         break;
1353
1354                 case MouseGain:
1355                         // Gain only makes sense for audio regions
1356
1357                         if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1358                                 break;
1359                         }
1360
1361                         switch (item_type) {
1362                         case RegionItem:
1363                                 /* check that we didn't drag before releasing, since
1364                                    its really annoying to create new control
1365                                    points when doing this.
1366                                 */
1367                                 if (were_dragging) {
1368                                         dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1369                                 }
1370                                 return true;
1371                                 break;
1372                                 
1373                         case AutomationTrackItem:
1374                                 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1375                                         add_automation_event (item, event, where, event->button.y);
1376                                 return true;
1377                                 break;
1378                         default:
1379                                 break;
1380                         }
1381                         break;
1382                         
1383                 case MouseAudition:
1384                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1385                         if (scrubbing_direction == 0) {
1386                                 /* no drag, just a click */
1387                                 switch (item_type) {
1388                                 case RegionItem:
1389                                         play_selected_region ();
1390                                         break;
1391                                 default:
1392                                         break;
1393                                 }
1394                         } else {
1395                                 /* make sure we stop */
1396                                 session->request_transport_speed (0.0);
1397                         }
1398                         break;
1399                         
1400                 default:
1401                         break;
1402
1403                 }
1404
1405                 return true;
1406                 break;
1407
1408
1409         case 2:
1410                 switch (mouse_mode) {
1411                         
1412                 case MouseObject:
1413                         switch (item_type) {
1414                         case RegionItem:
1415                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1416                                         raise_region ();
1417                                 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1418                                         lower_region ();
1419                                 } else {
1420                                         // Button2 click is unused
1421                                 }
1422                                 return true;
1423                                 
1424                                 break;
1425                                 
1426                         default:
1427                                 break;
1428                         }
1429                         break;
1430                         
1431                 case MouseRange:
1432                         
1433                         // x_style_paste (where, 1.0);
1434                         return true;
1435                         break;
1436                         
1437                 default:
1438                         break;
1439                 }
1440
1441                 break;
1442         
1443         case 3:
1444                 break;
1445                 
1446         default:
1447                 break;
1448         }
1449         return false;
1450 }
1451
1452 bool
1453 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* /*event*/, ItemType item_type)
1454 {
1455         ControlPoint* cp;
1456         Marker * marker;
1457         double fraction;
1458         
1459         if (last_item_entered != item) {
1460                 last_item_entered = item;
1461                 last_item_entered_n = 0;
1462         }
1463
1464         switch (item_type) {
1465         case ControlPointItem:
1466                 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1467                         cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1468                         cp->set_visible (true);
1469
1470                         double at_x, at_y;
1471                         at_x = cp->get_x();
1472                         at_y = cp->get_y ();
1473                         cp->item()->i2w (at_x, at_y);
1474                         at_x += 10.0;
1475                         at_y += 10.0;
1476
1477                         fraction = 1.0 - (cp->get_y() / cp->line().height());
1478
1479                         if (is_drawable() && dynamic_cast<ScrubDrag*> (_drag) == 0) {
1480                                 track_canvas->get_window()->set_cursor (*fader_cursor);
1481                         }
1482
1483                         last_item_entered_n++;
1484                         set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1485                         if (last_item_entered_n < 10) {
1486                                 show_verbose_canvas_cursor ();
1487                         }
1488                 }
1489                 break;
1490
1491         case GainLineItem:
1492                 if (mouse_mode == MouseGain) {
1493                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1494                         if (line)
1495                                 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1496                         if (is_drawable()) {
1497                                 track_canvas->get_window()->set_cursor (*fader_cursor);
1498                         }
1499                 }
1500                 break;
1501                         
1502         case AutomationLineItem:
1503                 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1504                         {
1505                                 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1506                                 if (line)
1507                                         line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1508                         }
1509                         if (is_drawable()) {
1510                                 track_canvas->get_window()->set_cursor (*fader_cursor);
1511                         }
1512                 }
1513                 break;
1514                 
1515         case RegionViewNameHighlight:
1516                 if (is_drawable() && mouse_mode == MouseObject) {
1517                         track_canvas->get_window()->set_cursor (*trimmer_cursor);
1518                 }
1519                 break;
1520
1521         case StartSelectionTrimItem:
1522         case EndSelectionTrimItem:
1523
1524 #ifdef WITH_CMT
1525         case ImageFrameHandleStartItem:
1526         case ImageFrameHandleEndItem:
1527         case MarkerViewHandleStartItem:
1528         case MarkerViewHandleEndItem:
1529 #endif
1530
1531                 if (is_drawable()) {
1532                         track_canvas->get_window()->set_cursor (*trimmer_cursor);
1533                 }
1534                 break;
1535
1536         case PlayheadCursorItem:
1537                 if (is_drawable()) {
1538                         switch (_edit_point) {
1539                         case EditAtMouse:
1540                                 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1541                                 break;
1542                         default:
1543                                 track_canvas->get_window()->set_cursor (*grabber_cursor);
1544                                 break;
1545                         }
1546                 }
1547                 break;
1548
1549         case RegionViewName:
1550                 
1551                 /* when the name is not an active item, the entire name highlight is for trimming */
1552
1553                 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1554                         if (mouse_mode == MouseObject && is_drawable()) {
1555                                 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1556                         }
1557                 } 
1558                 break;
1559
1560
1561         case AutomationTrackItem:
1562                 if (is_drawable()) {
1563                         Gdk::Cursor *cursor;
1564                         switch (mouse_mode) {
1565                         case MouseRange:
1566                                 cursor = selector_cursor;
1567                                 break;
1568                         case MouseZoom:
1569                                 cursor = zoom_cursor;
1570                                 break;
1571                         default:
1572                                 cursor = cross_hair_cursor;
1573                                 break;
1574                         }
1575
1576                         track_canvas->get_window()->set_cursor (*cursor);
1577
1578                         AutomationTimeAxisView* atv;
1579                         if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1580                                 clear_entered_track = false;
1581                                 set_entered_track (atv);
1582                         }
1583                 }
1584                 break;
1585
1586         case MarkerBarItem:
1587         case RangeMarkerBarItem:
1588         case TransportMarkerBarItem:
1589         case CdMarkerBarItem:
1590         case MeterBarItem:
1591         case TempoBarItem:
1592                 if (is_drawable()) {
1593                         track_canvas->get_window()->set_cursor (*timebar_cursor);
1594                 }
1595                 break;
1596
1597         case MarkerItem:
1598                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1599                         break;
1600                 }
1601                 entered_marker = marker;
1602                 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1603                 // fall through
1604         case MeterMarkerItem:
1605         case TempoMarkerItem:
1606                 if (is_drawable()) {
1607                         track_canvas->get_window()->set_cursor (*timebar_cursor);
1608                 }
1609                 break;
1610         case FadeInHandleItem:
1611         case FadeOutHandleItem:
1612                 if (mouse_mode == MouseObject) {
1613                         ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1614                         if (rect) {
1615                                 rect->property_fill_color_rgba() = 0;
1616                                 rect->property_outline_pixels() = 1;
1617                         }
1618                 }
1619                 break;
1620
1621         default:
1622                 break;
1623         }
1624
1625         /* second pass to handle entered track status in a comprehensible way.
1626          */
1627
1628         switch (item_type) {
1629         case GainLineItem:
1630         case AutomationLineItem:
1631         case ControlPointItem:
1632                 /* these do not affect the current entered track state */
1633                 clear_entered_track = false;
1634                 break;
1635
1636         case AutomationTrackItem:
1637                 /* handled above already */
1638                 break;
1639
1640         default:
1641                 set_entered_track (0);
1642                 break;
1643         }
1644
1645         return false;
1646 }
1647
1648 bool
1649 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* /*event*/, ItemType item_type)
1650 {
1651         AutomationLine* al;
1652         ControlPoint* cp;
1653         Marker *marker;
1654         Location *loc;
1655         RegionView* rv;
1656         bool is_start;
1657
1658         switch (item_type) {
1659         case ControlPointItem:
1660                 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1661                 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1662                         if (cp->line().npoints() > 1 && !cp->selected()) {
1663                                 cp->set_visible (false);
1664                         }
1665                 }
1666                 
1667                 if (is_drawable()) {
1668                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1669                 }
1670
1671                 hide_verbose_canvas_cursor ();
1672                 break;
1673                 
1674         case RegionViewNameHighlight:
1675         case StartSelectionTrimItem:
1676         case EndSelectionTrimItem:
1677         case PlayheadCursorItem:
1678
1679 #ifdef WITH_CMT
1680         case ImageFrameHandleStartItem:
1681         case ImageFrameHandleEndItem:
1682         case MarkerViewHandleStartItem:
1683         case MarkerViewHandleEndItem:
1684 #endif
1685
1686                 if (is_drawable()) {
1687                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1688                 }
1689                 break;
1690
1691         case GainLineItem:
1692         case AutomationLineItem:
1693                 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1694                 {
1695                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1696                         if (line)
1697                                 line->property_fill_color_rgba() = al->get_line_color();
1698                 }
1699                 if (is_drawable()) {
1700                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1701                 }
1702                 break;
1703
1704         case RegionViewName:
1705                 /* see enter_handler() for notes */
1706                 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1707                         if (is_drawable() && mouse_mode == MouseObject) {
1708                                 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1709                         }
1710                 }
1711                 break;
1712
1713         case RangeMarkerBarItem:
1714         case TransportMarkerBarItem:
1715         case CdMarkerBarItem:
1716         case MeterBarItem:
1717         case TempoBarItem:
1718         case MarkerBarItem:
1719                 if (is_drawable()) {
1720                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1721                 }
1722                 break;
1723                 
1724         case MarkerItem:
1725                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1726                         break;
1727                 }
1728                 entered_marker = 0;
1729                 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1730                         location_flags_changed (loc, this);
1731                 }
1732                 // fall through
1733         case MeterMarkerItem:
1734         case TempoMarkerItem:
1735                 
1736                 if (is_drawable()) {
1737                         track_canvas->get_window()->set_cursor (*timebar_cursor);
1738                 }
1739
1740                 break;
1741
1742         case FadeInHandleItem:
1743         case FadeOutHandleItem:
1744                 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1745                 {
1746                         ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1747                         if (rect) {
1748                                 rect->property_fill_color_rgba() = rv->get_fill_color();
1749                                 rect->property_outline_pixels() = 0;
1750                         }
1751                 }
1752                 break;
1753
1754         case AutomationTrackItem:
1755                 if (is_drawable()) {
1756                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1757                         clear_entered_track = true;
1758                         Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1759                 }
1760                 break;
1761                 
1762         default:
1763                 break;
1764         }
1765
1766         return false;
1767 }
1768
1769 gint
1770 Editor::left_automation_track ()
1771 {
1772         if (clear_entered_track) {
1773                 set_entered_track (0);
1774                 clear_entered_track = false;
1775         }
1776         return false;
1777 }
1778
1779 void
1780 Editor::scrub ()
1781 {
1782         double delta;
1783         
1784         if (scrubbing_direction == 0) {
1785                 /* first move */
1786                 session->request_locate (_drag->current_pointer_frame(), false);
1787                 session->request_transport_speed (0.1);
1788                 scrubbing_direction = 1;
1789                 
1790         } else {
1791                 
1792                 if (last_scrub_x > _drag->current_pointer_x()) {
1793                         
1794                         /* pointer moved to the left */
1795                         
1796                         if (scrubbing_direction > 0) {
1797                                 
1798                                 /* we reversed direction to go backwards */
1799                                 
1800                                 scrub_reversals++;
1801                                 scrub_reverse_distance += (int) (last_scrub_x - _drag->current_pointer_x());
1802                                 
1803                         } else {
1804                                 
1805                                 /* still moving to the left (backwards) */
1806                                 
1807                                 scrub_reversals = 0;
1808                                 scrub_reverse_distance = 0;
1809                                 
1810                                 delta = 0.01 * (last_scrub_x - _drag->current_pointer_x());
1811                                 session->request_transport_speed (session->transport_speed() - delta);
1812                         }
1813                         
1814                 } else {
1815                         /* pointer moved to the right */
1816                         
1817                         if (scrubbing_direction < 0) {
1818                                 /* we reversed direction to go forward */
1819                                 
1820                                 scrub_reversals++;
1821                                 scrub_reverse_distance += (int) (_drag->current_pointer_x() - last_scrub_x);
1822                                 
1823                         } else {
1824                                 /* still moving to the right */
1825                                 
1826                                 scrub_reversals = 0;
1827                                 scrub_reverse_distance = 0;
1828                                 
1829                                 delta = 0.01 * (_drag->current_pointer_x() - last_scrub_x);
1830                                 session->request_transport_speed (session->transport_speed() + delta);
1831                         }
1832                 }
1833                 
1834                 /* if there have been more than 2 opposite motion moves detected, or one that moves
1835                    back more than 10 pixels, reverse direction
1836                 */
1837                 
1838                 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1839                         
1840                         if (scrubbing_direction > 0) {
1841                                 /* was forwards, go backwards */
1842                                 session->request_transport_speed (-0.1);
1843                                 scrubbing_direction = -1;
1844                         } else {
1845                                 /* was backwards, go forwards */
1846                                 session->request_transport_speed (0.1);
1847                                 scrubbing_direction = 1;
1848                         }
1849                         
1850                         scrub_reverse_distance = 0;
1851                         scrub_reversals = 0;
1852                 }
1853         }
1854         
1855         last_scrub_x = _drag->current_pointer_x();
1856 }
1857
1858 bool
1859 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1860 {
1861         if (event->motion.is_hint) {
1862                 gint x, y;
1863                 
1864                 /* We call this so that MOTION_NOTIFY events continue to be
1865                    delivered to the canvas. We need to do this because we set
1866                    Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1867                    the density of the events, at the expense of a round-trip
1868                    to the server. Given that this will mostly occur on cases
1869                    where DISPLAY = :0.0, and given the cost of what the motion
1870                    event might do, its a good tradeoff.  
1871                 */
1872
1873                 track_canvas->get_pointer (x, y);
1874         } 
1875
1876         if (current_stepping_trackview) {
1877                 /* don't keep the persistent stepped trackview if the mouse moves */
1878                 current_stepping_trackview = 0;
1879                 step_timeout.disconnect ();
1880         }
1881
1882         if (session && session->actively_recording()) {
1883                 /* Sorry. no dragging stuff around while we record */
1884                 return true;
1885         }
1886
1887         bool handled = false;
1888         if (_drag) {
1889                 handled = _drag->motion_handler (event, from_autoscroll);
1890         }
1891
1892         if (!handled) {
1893                 return false;
1894         }
1895
1896         track_canvas_motion (event);
1897         return true;
1898 }
1899
1900 void
1901 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
1902 {
1903         ControlPoint* control_point;
1904
1905         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1906                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1907                 /*NOTREACHED*/
1908         }
1909
1910         // We shouldn't remove the first or last gain point
1911         if (control_point->line().is_last_point(*control_point) ||
1912                 control_point->line().is_first_point(*control_point)) { 
1913                 return;
1914         }
1915
1916         control_point->line().remove_point (*control_point);
1917 }
1918
1919 void
1920 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/)
1921 {
1922         ControlPoint* control_point;
1923
1924         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1925                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1926                 /*NOTREACHED*/
1927         }
1928
1929         control_point->line().remove_point (*control_point);
1930 }
1931
1932 void
1933 Editor::edit_control_point (ArdourCanvas::Item* item)
1934 {
1935         ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1936
1937         if (p == 0) {
1938                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1939                 /*NOTREACHED*/
1940         }
1941
1942         ControlPointDialog d (p);
1943         d.set_position (Gtk::WIN_POS_MOUSE);
1944         ensure_float (d);
1945
1946         if (d.run () != RESPONSE_ACCEPT) {
1947                 return;
1948         }
1949
1950         p->line().modify_point_y (*p, d.get_y_fraction ());
1951 }
1952
1953
1954 void
1955 Editor::visible_order_range (int* low, int* high) const
1956 {
1957         *low = TimeAxisView::max_order ();
1958         *high = 0;
1959         
1960         for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1961
1962                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1963                 
1964                 if (!rtv->hidden()) {
1965                         
1966                         if (*high < rtv->order()) {
1967                                 *high = rtv->order ();
1968                         }
1969                         
1970                         if (*low > rtv->order()) {
1971                                 *low = rtv->order ();
1972                         }
1973                 }
1974         }
1975 }
1976
1977 void
1978 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1979 {
1980         /* Either add to or set the set the region selection, unless
1981            this is an alignment click (control used)
1982         */
1983         
1984         if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1985                 TimeAxisView* tv = &rv.get_time_axis_view();
1986                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1987                 double speed = 1.0;
1988                 if (rtv && rtv->is_track()) {
1989                         speed = rtv->get_diskstream()->speed();
1990                 }
1991
1992                 nframes64_t where = get_preferred_edit_position();
1993
1994                 if (where >= 0) {
1995
1996                         if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
1997                                 
1998                                 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
1999                                 
2000                         } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2001                                 
2002                                 align_region (rv.region(), End, (nframes64_t) (where * speed));
2003                                 
2004                         } else {
2005                                 
2006                                 align_region (rv.region(), Start, (nframes64_t) (where * speed));
2007                         }
2008                 }
2009         }
2010 }
2011
2012 void
2013 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos) 
2014 {
2015         char buf[128];
2016         SMPTE::Time smpte;
2017         BBT_Time bbt;
2018         int hours, mins;
2019         nframes64_t frame_rate;
2020         float secs;
2021
2022         if (session == 0) {
2023                 return;
2024         }
2025
2026         AudioClock::Mode m;
2027
2028         if (Profile->get_sae() || Profile->get_small_screen()) {
2029                 m = ARDOUR_UI::instance()->primary_clock.mode();
2030         } else {
2031                 m = ARDOUR_UI::instance()->secondary_clock.mode();
2032         }
2033
2034         switch (m) {
2035         case AudioClock::BBT:
2036                 session->bbt_time (frame, bbt);
2037                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
2038                 break;
2039                 
2040         case AudioClock::SMPTE:
2041                 session->smpte_time (frame, smpte);
2042                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
2043                 break;
2044
2045         case AudioClock::MinSec:
2046                 /* XXX this is copied from show_verbose_duration_cursor() */
2047                 frame_rate = session->frame_rate();
2048                 hours = frame / (frame_rate * 3600);
2049                 frame = frame % (frame_rate * 3600);
2050                 mins = frame / (frame_rate * 60);
2051                 frame = frame % (frame_rate * 60);
2052                 secs = (float) frame / (float) frame_rate;
2053                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
2054                 break;
2055
2056         default:
2057                 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
2058                 break;
2059         }
2060
2061         if (xpos >= 0 && ypos >=0) {
2062                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2063         }
2064         else {
2065                 set_verbose_canvas_cursor (buf, _drag->current_pointer_x() + offset - horizontal_adjustment.get_value(), _drag->current_pointer_y() + offset - vertical_adjustment.get_value() + canvas_timebars_vsize);
2066         }
2067         show_verbose_canvas_cursor ();
2068 }
2069
2070 void
2071 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos) 
2072 {
2073         char buf[128];
2074         SMPTE::Time smpte;
2075         BBT_Time sbbt;
2076         BBT_Time ebbt;
2077         int hours, mins;
2078         nframes64_t distance, frame_rate;
2079         float secs;
2080         Meter meter_at_start(session->tempo_map().meter_at(start));
2081
2082         if (session == 0) {
2083                 return;
2084         }
2085
2086         AudioClock::Mode m;
2087
2088         if (Profile->get_sae() || Profile->get_small_screen()) {
2089                 m = ARDOUR_UI::instance()->primary_clock.mode ();
2090         } else {
2091                 m = ARDOUR_UI::instance()->secondary_clock.mode ();
2092         }
2093
2094         switch (m) {
2095         case AudioClock::BBT:
2096                 session->bbt_time (start, sbbt);
2097                 session->bbt_time (end, ebbt);
2098
2099                 /* subtract */
2100                 /* XXX this computation won't work well if the
2101                 user makes a selection that spans any meter changes.
2102                 */
2103
2104                 ebbt.bars -= sbbt.bars;
2105                 if (ebbt.beats >= sbbt.beats) {
2106                         ebbt.beats -= sbbt.beats;
2107                 } else {
2108                         ebbt.bars--;
2109                         ebbt.beats =  int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
2110                 }
2111                 if (ebbt.ticks >= sbbt.ticks) {
2112                         ebbt.ticks -= sbbt.ticks;
2113                 } else {
2114                         ebbt.beats--;
2115                         ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2116                 }
2117                 
2118                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2119                 break;
2120                 
2121         case AudioClock::SMPTE:
2122                 session->smpte_duration (end - start, smpte);
2123                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
2124                 break;
2125
2126         case AudioClock::MinSec:
2127                 /* XXX this stuff should be elsewhere.. */
2128                 distance = end - start;
2129                 frame_rate = session->frame_rate();
2130                 hours = distance / (frame_rate * 3600);
2131                 distance = distance % (frame_rate * 3600);
2132                 mins = distance / (frame_rate * 60);
2133                 distance = distance % (frame_rate * 60);
2134                 secs = (float) distance / (float) frame_rate;
2135                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
2136                 break;
2137
2138         default:
2139                 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2140                 break;
2141         }
2142
2143         if (xpos >= 0 && ypos >=0) {
2144                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2145         }
2146         else {
2147                 set_verbose_canvas_cursor (buf, _drag->current_pointer_x() + offset, _drag->current_pointer_y() + offset);
2148         }
2149
2150         show_verbose_canvas_cursor ();
2151 }
2152
2153 void
2154 Editor::collect_new_region_view (RegionView* rv)
2155 {
2156         latest_regionviews.push_back (rv);
2157 }
2158
2159 void
2160 Editor::collect_and_select_new_region_view (RegionView* rv)
2161 {
2162         selection->add(rv);
2163         latest_regionviews.push_back (rv);
2164 }
2165
2166 void
2167 Editor::cancel_selection ()
2168 {
2169         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2170                 (*i)->hide_selection ();
2171         }
2172
2173         selection->clear ();
2174         clicked_selection = 0;
2175 }       
2176
2177
2178 void
2179 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
2180 {
2181         boost::shared_ptr<Region> region (rv.region());
2182
2183         if (region->locked()) {
2184                 return;
2185         }
2186
2187         nframes64_t new_bound;
2188
2189         double speed = 1.0;
2190         TimeAxisView* tvp = clicked_axisview;
2191         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2192
2193         if (tv && tv->is_track()) {
2194                 speed = tv->get_diskstream()->speed();
2195         }
2196         
2197         if (left_direction) {
2198                 if (swap_direction) {
2199                         new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2200                 } else {
2201                         new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2202                 }
2203         } else {
2204                 if (swap_direction) {
2205                         new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2206                 } else {
2207                         new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2208                 }
2209         }
2210
2211         if (obey_snap) {
2212                 snap_to (new_bound);
2213         }
2214         region->trim_start ((nframes64_t) (new_bound * speed), this);   
2215         rv.region_changed (StartChanged);
2216 }
2217
2218 void
2219 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2220 {
2221         boost::shared_ptr<Region> region (rv.region()); 
2222
2223         if (region->locked()) {
2224                 return;
2225         }
2226
2227         nframes64_t new_bound;
2228
2229         double speed = 1.0;
2230         TimeAxisView* tvp = clicked_axisview;
2231         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2232
2233         if (tv && tv->is_track()) {
2234                 speed = tv->get_diskstream()->speed();
2235         }
2236         
2237         if (left_direction) {
2238                 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2239         } else {
2240                 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2241         }
2242
2243         if (obey_snap) {
2244                 snap_to (new_bound, (left_direction ? 0 : 1));  
2245         }
2246         
2247         nframes64_t pre_trim_first_frame = region->first_frame();
2248
2249         region->trim_front ((nframes64_t) (new_bound * speed), this);
2250   
2251         if (no_overlap) {
2252                 //Get the next region on the left of this region and shrink/expand it.
2253                 boost::shared_ptr<Playlist> playlist (region->playlist());
2254                 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
2255                 
2256                 bool regions_touching = false;
2257
2258                 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){
2259                     regions_touching = true;
2260                 }
2261
2262                 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2263                 if (region_left != 0 && 
2264                         (region_left->last_frame() > region->first_frame() || regions_touching)) 
2265                 {
2266                         region_left->trim_end(region->first_frame(), this);
2267                 }
2268         }
2269
2270         
2271
2272         rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
2273 }
2274
2275 void
2276 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2277 {
2278         boost::shared_ptr<Region> region (rv.region());
2279
2280         if (region->locked()) {
2281                 return;
2282         }
2283
2284         nframes64_t new_bound;
2285
2286         double speed = 1.0;
2287         TimeAxisView* tvp = clicked_axisview;
2288         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2289
2290         if (tv && tv->is_track()) {
2291                 speed = tv->get_diskstream()->speed();
2292         }
2293
2294         if (left_direction) {
2295                 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
2296         } else {
2297                 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
2298         }
2299
2300         if (obey_snap) {
2301                 snap_to (new_bound);
2302         }
2303
2304         nframes64_t pre_trim_last_frame = region->last_frame();
2305
2306         region->trim_end ((nframes64_t) (new_bound * speed), this);
2307
2308         if (no_overlap) {
2309                 //Get the next region on the right of this region and shrink/expand it.
2310                 boost::shared_ptr<Playlist> playlist (region->playlist());
2311                 boost::shared_ptr<Region> region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1);
2312
2313                 bool regions_touching = false;
2314
2315                 if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)){
2316                     regions_touching = true;
2317                 }
2318
2319                 //Only trim region on the right if the last frame has gone beyond the right region's first frame.
2320                 if (region_right != 0 &&
2321                         (region_right->first_frame() < region->last_frame() || regions_touching)) 
2322                 {
2323                         region_right->trim_front(region->last_frame() + 1, this);
2324                 }
2325                 
2326                 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
2327         }
2328         else {
2329                 rv.region_changed (LengthChanged);
2330         }
2331 }
2332
2333
2334 void
2335 Editor::point_trim (GdkEvent* event)
2336 {
2337         RegionView* rv = clicked_regionview;
2338
2339         nframes64_t new_bound = _drag->current_pointer_frame();
2340
2341         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2342                 snap_to (new_bound);
2343         }
2344
2345         /* Choose action dependant on which button was pressed */
2346         switch (event->button.button) {
2347         case 1:
2348                 begin_reversible_command (_("Start point trim"));
2349
2350                 if (selection->selected (rv)) {
2351                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2352                              i != selection->regions.by_layer().end(); ++i)
2353                         {
2354                                 if ( (*i) == NULL){
2355                                     cerr << "region view contains null region" << endl;
2356                                 }
2357
2358                                 if (!(*i)->region()->locked()) {
2359                                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
2360                                         XMLNode &before = pl->get_state();
2361
2362                                         (*i)->region()->trim_front (new_bound, this);
2363
2364                                         XMLNode &after = pl->get_state();
2365                                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2366                                 }
2367                         }
2368
2369                 } else {
2370                         if (!rv->region()->locked()) {
2371                                 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2372                                 XMLNode &before = pl->get_state();
2373                                 rv->region()->trim_front (new_bound, this);
2374                                 XMLNode &after = pl->get_state();
2375                                 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2376                         }
2377                 }
2378
2379                 commit_reversible_command();
2380         
2381                 break;
2382         case 2:
2383                 begin_reversible_command (_("End point trim"));
2384
2385                 if (selection->selected (rv)) {
2386                         
2387                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2388                         {
2389                                 if (!(*i)->region()->locked()) {
2390                                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
2391                                         XMLNode &before = pl->get_state();
2392                                         (*i)->region()->trim_end (new_bound, this);
2393                                         XMLNode &after = pl->get_state();
2394                                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2395                                 }
2396                         }
2397
2398                 } else {
2399
2400                         if (!rv->region()->locked()) {
2401                                 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2402                                 XMLNode &before = pl->get_state();
2403                                 rv->region()->trim_end (new_bound, this);
2404                                 XMLNode &after = pl->get_state();
2405                                 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
2406                         }
2407                 }
2408
2409                 commit_reversible_command();
2410         
2411                 break;
2412         default:
2413                 break;
2414         }
2415 }
2416
2417 void
2418 Editor::thaw_region_after_trim (RegionView& rv)
2419 {
2420         boost::shared_ptr<Region> region (rv.region());
2421
2422         if (region->locked()) {
2423                 return;
2424         }
2425
2426         region->thaw (_("trimmed region"));
2427
2428         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
2429         
2430         if (arv) {
2431                 arv->unhide_envelope ();
2432         }
2433 }
2434
2435 void
2436 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2437 {
2438         Marker* marker;
2439         bool is_start;
2440
2441         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2442                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2443                 /*NOTREACHED*/
2444         }
2445
2446         Location* location = find_location_from_marker (marker, is_start);      
2447         location->set_hidden (true, this);
2448 }
2449
2450
2451 void
2452 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
2453 {
2454         double x1 = frame_to_pixel (start);
2455         double x2 = frame_to_pixel (end);
2456         double y2 = full_canvas_height - 1.0;
2457
2458         zoom_rect->property_x1() = x1;
2459         zoom_rect->property_y1() = 1.0;
2460         zoom_rect->property_x2() = x2;
2461         zoom_rect->property_y2() = y2;
2462 }
2463
2464
2465 gint
2466 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2467 {
2468         using namespace Gtkmm2ext;
2469
2470         ArdourPrompter prompter (false);
2471
2472         prompter.set_prompt (_("Name for region:"));
2473         prompter.set_initial_text (clicked_regionview->region()->name());
2474         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2475         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2476         prompter.show_all ();
2477         switch (prompter.run ()) {
2478         case Gtk::RESPONSE_ACCEPT:
2479                 string str;
2480                 prompter.get_result(str);
2481                 if (str.length()) {
2482                         clicked_regionview->region()->set_name (str);
2483                 }
2484                 break;
2485         }
2486         return true;
2487 }
2488
2489
2490 void
2491 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
2492 {
2493         /* no brushing without a useful snap setting */
2494
2495         switch (snap_mode) {
2496         case SnapMagnetic:
2497                 return; /* can't work because it allows region to be placed anywhere */
2498         default:
2499                 break; /* OK */
2500         }
2501
2502         switch (snap_type) {
2503         case SnapToMark:
2504                 return;
2505
2506         default:
2507                 break;
2508         }
2509
2510         /* don't brush a copy over the original */
2511         
2512         if (pos == rv->region()->position()) {
2513                 return;
2514         }
2515
2516         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2517
2518         if (rtv == 0 || !rtv->is_track()) {
2519                 return;
2520         }
2521
2522         boost::shared_ptr<Playlist> playlist = rtv->playlist();
2523         double speed = rtv->get_diskstream()->speed();
2524         
2525         XMLNode &before = playlist->get_state();
2526         playlist->add_region (RegionFactory::create (rv->region()), (nframes64_t) (pos * speed));
2527         XMLNode &after = playlist->get_state();
2528         session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
2529         
2530         // playlist is frozen, so we have to update manually
2531         
2532         playlist->Modified(); /* EMIT SIGNAL */
2533 }
2534
2535 gint
2536 Editor::track_height_step_timeout ()
2537 {
2538         if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2539                 current_stepping_trackview = 0;
2540                 return false;
2541         }
2542         return true;
2543 }
2544
2545 void
2546 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2547 {
2548         assert (region_view);
2549
2550         _region_motion_group->raise_to_top ();
2551         
2552         assert (_drag == 0);
2553         
2554         if (Config->get_edit_mode() == Splice) {
2555                 _drag = new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer());
2556         } else {
2557                 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2558                 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false);
2559         }
2560         
2561         _drag->start_grab (event);
2562
2563         begin_reversible_command (_("move region(s)"));
2564
2565         /* sync the canvas to what we think is its current state */
2566         update_canvas_now();
2567 }
2568
2569 void
2570 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2571 {
2572         assert (region_view);
2573         assert (_drag == 0);
2574         
2575         _region_motion_group->raise_to_top ();
2576
2577         RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2578         _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true);
2579         _drag->start_grab(event);
2580 }
2581
2582 void
2583 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2584 {
2585         assert (region_view);
2586         assert (_drag == 0);
2587         
2588         if (Config->get_edit_mode() == Splice) {
2589                 return;
2590         }
2591
2592         RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2593         _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false);
2594         _drag->start_grab (event);
2595         
2596         begin_reversible_command (_("Drag region brush"));
2597 }
2598
2599 /** Start a grab where a time range is selected, track(s) are selected, and the
2600  *  user clicks and drags a region with a modifier in order to create a new region containing
2601  *  the section of the clicked region that lies within the time range.
2602  */
2603 void
2604 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2605 {
2606         if (clicked_regionview == 0) {
2607                 return;
2608         }
2609
2610         /* lets try to create new Region for the selection */
2611
2612         vector<boost::shared_ptr<Region> > new_regions;
2613         create_region_from_selection (new_regions);
2614
2615         if (new_regions.empty()) {
2616                 return;
2617         }
2618
2619         /* XXX fix me one day to use all new regions */
2620         
2621         boost::shared_ptr<Region> region (new_regions.front());
2622
2623         /* add it to the current stream/playlist.
2624
2625            tricky: the streamview for the track will add a new regionview. we will
2626            catch the signal it sends when it creates the regionview to
2627            set the regionview we want to then drag.
2628         */
2629         
2630         latest_regionviews.clear();
2631         sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2632         
2633         /* A selection grab currently creates two undo/redo operations, one for 
2634            creating the new region and another for moving it.
2635         */
2636
2637         begin_reversible_command (_("selection grab"));
2638
2639         boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2640
2641         XMLNode *before = &(playlist->get_state());
2642         clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2643         XMLNode *after = &(playlist->get_state());
2644         session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
2645
2646         commit_reversible_command ();
2647         
2648         c.disconnect ();
2649         
2650         if (latest_regionviews.empty()) {
2651                 /* something went wrong */
2652                 return;
2653         }
2654
2655         /* we need to deselect all other regionviews, and select this one
2656            i'm ignoring undo stuff, because the region creation will take care of it 
2657         */
2658         selection->set (latest_regionviews);
2659         
2660         assert (_drag == 0);
2661         _drag = new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false);
2662         _drag->start_grab (event);
2663 }
2664
2665 void
2666 Editor::break_drag ()
2667 {
2668         if (_drag) {
2669                 _drag->break_drag ();
2670         }
2671 }