Hopefully fix assertion failures in gnomecanvas when dragging track heights.
[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 m)
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                                         assert (_drag == 0);
824                                         _drag = new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions);
825                                         _drag->start_grab (event);
826                                         return true;
827                                         
828                                 case FadeOutHandleItem:
829                                         assert (_drag == 0);
830                                         _drag = new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions);
831                                         _drag->start_grab (event);
832                                         return true;
833
834                                 case RegionItem:
835                                         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
836                                                 start_region_copy_grab (item, event, clicked_regionview);
837                                         } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
838                                                 start_region_brush_grab (item, event, clicked_regionview);
839                                         } else {
840                                                 start_region_grab (item, event, clicked_regionview);
841                                         }
842                                         break;
843                                         
844                                 case RegionViewNameHighlight:
845                                         assert (_drag == 0);
846                                         _drag = new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer());
847                                         _drag->start_grab (event);
848                                         return true;
849                                         break;
850                                         
851                                 case RegionViewName:
852                                         /* rename happens on edit clicks */
853                                         assert (_drag == 0);
854                                         _drag = new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer());
855                                         _drag->start_grab (event);
856                                         return true;
857                                         break;
858
859                                 case ControlPointItem:
860                                         assert (_drag == 0);
861                                         _drag = new ControlPointDrag (this, item);
862                                         _drag->start_grab (event);
863                                         return true;
864                                         break;
865                                         
866                                 case AutomationLineItem:
867                                         assert (_drag == 0);
868                                         _drag = new LineDrag (this, item);
869                                         _drag->start_grab (event);
870                                         return true;
871                                         break;
872
873                                 case StreamItem:
874                                 case AutomationTrackItem:
875                                         assert (_drag == 0);
876                                         _drag = new RubberbandSelectDrag (this, item);
877                                         _drag->start_grab (event);
878                                         break;
879                                         
880 #ifdef WITH_CMT
881                                 case ImageFrameHandleStartItem:
882                                         imageframe_start_handle_op(item, event) ;
883                                         return(true) ;
884                                         break ;
885                                 case ImageFrameHandleEndItem:
886                                         imageframe_end_handle_op(item, event) ;
887                                         return(true) ;
888                                         break ;
889                                 case MarkerViewHandleStartItem:
890                                         markerview_item_start_handle_op(item, event) ;
891                                         return(true) ;
892                                         break ;
893                                 case MarkerViewHandleEndItem:
894                                         markerview_item_end_handle_op(item, event) ;
895                                         return(true) ;
896                                         break ;
897                                 case MarkerViewItem:
898                                         start_markerview_grab(item, event) ;
899                                         break ;
900                                 case ImageFrameItem:
901                                         start_imageframe_grab(item, event) ;
902                                         break ;
903 #endif
904
905                                 case MarkerBarItem:
906                                         
907                                         break;
908
909                                 default:
910                                         break;
911                                 }
912                         }
913                         return true;
914                         break;
915                         
916                 case MouseGain:
917                         switch (item_type) {
918                         case RegionItem:
919                                 /* start a grab so that if we finish after moving
920                                    we can tell what happened.
921                                 */
922                                 assert (_drag == 0);
923                                 _drag = new RegionGainDrag (this, item);
924                                 _drag->start_grab (event, current_canvas_cursor);
925                                 break;
926
927                         case GainLineItem:
928                                 assert (_drag == 0);
929                                 _drag = new LineDrag (this, item);
930                                 _drag->start_grab (event);
931                                 return true;
932
933                         case ControlPointItem:
934                                 assert (_drag == 0);
935                                 _drag = new ControlPointDrag (this, item);
936                                 _drag->start_grab (event);
937                                 return true;
938                                 break;
939
940                         default:
941                                 break;
942                         }
943                         return true;
944                         break;
945
946                         switch (item_type) {
947                         case ControlPointItem:
948                                 assert (_drag == 0);
949                                 _drag = new ControlPointDrag (this, item);
950                                 _drag->start_grab (event);
951                                 break;
952
953                         case AutomationLineItem:
954                                 assert (_drag == 0);
955                                 _drag = new LineDrag (this, item);
956                                 _drag->start_grab (event);
957                                 break;
958
959                         case RegionItem:
960                                 // XXX need automation mode to identify which
961                                 // line to use
962                                 // start_line_grab_from_regionview (item, event);
963                                 break;
964
965                         default:
966                                 break;
967                         }
968                         return true;
969                         break;
970
971                 case MouseZoom:
972                         if (event->type == GDK_BUTTON_PRESS) {
973                                 assert (_drag == 0);
974                                 _drag = new MouseZoomDrag (this, item);
975                                 _drag->start_grab (event);
976                         }
977
978                         return true;
979                         break;
980
981                 case MouseTimeFX:
982                         if (item_type == RegionItem) {
983                                 assert (_drag == 0);
984                                 _drag = new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer());
985                                 _drag->start_grab (event);
986                         }
987                         break;
988
989                 case MouseAudition:
990                         _drag = new ScrubDrag (this, item);
991                         _drag->start_grab (event);
992                         scrub_reversals = 0;
993                         scrub_reverse_distance = 0;
994                         last_scrub_x = event->button.x;
995                         scrubbing_direction = 0;
996                         track_canvas->get_window()->set_cursor (*transparent_cursor);
997                         break;
998
999                 case MouseNote:
1000                         assert (_drag == 0);
1001                         _drag = new RegionCreateDrag (this, item, clicked_axisview);
1002                         _drag->start_grab (event);
1003                         break;
1004                 
1005                 default:
1006                         break;
1007                 }
1008                 break;
1009
1010         case 2:
1011                 switch (mouse_mode) {
1012                 case MouseObject:
1013                         if (event->type == GDK_BUTTON_PRESS) {
1014                                 switch (item_type) {
1015                                 case RegionItem:
1016                                         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1017                                                 start_region_copy_grab (item, event, clicked_regionview);
1018                                         } else {
1019                                                 start_region_grab (item, event, clicked_regionview);
1020                                         }
1021                                         return true;
1022                                         break;
1023                                 case ControlPointItem:
1024                                         assert (_drag == 0);
1025                                         _drag = new ControlPointDrag (this, item);
1026                                         _drag->start_grab (event);
1027                                         return true;
1028                                         break;
1029                                         
1030                                 default:
1031                                         break;
1032                                 }
1033                         }
1034                         
1035                         
1036                         switch (item_type) {
1037                         case RegionViewNameHighlight:
1038                                 assert (_drag == 0);
1039                                 _drag = new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer());
1040                                 _drag->start_grab (event);
1041                                 return true;
1042                                 break;
1043                                 
1044                         case RegionViewName:
1045                                 assert (_drag == 0);
1046                                 _drag = new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer());
1047                                 _drag->start_grab (event);
1048                                 return true;
1049                                 break;
1050                                 
1051                         default:
1052                                 break;
1053                         }
1054                         
1055                         break;
1056
1057                 case MouseRange:
1058                         if (event->type == GDK_BUTTON_PRESS) {
1059                                 /* relax till release */
1060                         }
1061                         return true;
1062                         break;
1063                                         
1064                                 
1065                 case MouseZoom:
1066                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1067                                 temporal_zoom_session();
1068                         } else {
1069                                 temporal_zoom_to_frame (true, event_frame(event));
1070                         }
1071                         return true;
1072                         break;
1073
1074                 default:
1075                         break;
1076                 }
1077
1078                 break;
1079
1080         case 3:
1081                 break;
1082
1083         default:
1084                 break;
1085
1086         }
1087
1088         return false;
1089 }
1090
1091 bool
1092 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1093 {
1094         nframes64_t where = event_frame (event, 0, 0);
1095         AutomationTimeAxisView* atv = 0;
1096         
1097         /* no action if we're recording */
1098                                                 
1099         if (session && session->actively_recording()) {
1100                 return true;
1101         }
1102
1103         /* first, see if we're finishing a drag ... */
1104
1105         bool were_dragging = false;
1106         if (_drag) {
1107                 bool const r = _drag->end_grab (event);
1108                 delete _drag;
1109                 _drag = 0;
1110                 if (r) {
1111                         /* grab dragged, so do nothing else */
1112                         return true;
1113                 }
1114
1115                 were_dragging = true;
1116         }
1117         
1118         button_selection (item, event, item_type);
1119
1120         /* edit events get handled here */
1121
1122         if (_drag == 0 && Keyboard::is_edit_event (&event->button)) {
1123                 switch (item_type) {
1124                 case RegionItem:
1125                         edit_region ();
1126                         break;
1127
1128                 case TempoMarkerItem:
1129                         edit_tempo_marker (item);
1130                         break;
1131                         
1132                 case MeterMarkerItem:
1133                         edit_meter_marker (item);
1134                         break;
1135                         
1136                 case RegionViewName:
1137                         if (clicked_regionview->name_active()) {
1138                                 return mouse_rename_region (item, event);
1139                         }
1140                         break;
1141
1142                 case ControlPointItem:
1143                         edit_control_point (item);
1144                         break;
1145
1146                 default:
1147                         break;
1148                 }
1149                 return true;
1150         }
1151
1152         /* context menu events get handled here */
1153
1154         if (Keyboard::is_context_menu_event (&event->button)) {
1155
1156                 if (_drag == 0) {
1157
1158                         /* no matter which button pops up the context menu, tell the menu
1159                            widget to use button 1 to drive menu selection.
1160                         */
1161
1162                         switch (item_type) {
1163                         case FadeInItem:
1164                         case FadeInHandleItem:
1165                         case FadeOutItem:
1166                         case FadeOutHandleItem:
1167                                 popup_fade_context_menu (1, event->button.time, item, item_type);
1168                                 break;
1169                         
1170                         case StreamItem:
1171                                 popup_track_context_menu (1, event->button.time, item_type, false, where);
1172                                 break;
1173                                 
1174                         case RegionItem:
1175                         case RegionViewNameHighlight:
1176                         case RegionViewName:
1177                                 popup_track_context_menu (1, event->button.time, item_type, false, where);
1178                                 break;
1179                                 
1180                         case SelectionItem:
1181                                 popup_track_context_menu (1, event->button.time, item_type, true, where);
1182                                 break;
1183
1184                         case AutomationTrackItem:
1185                                 popup_track_context_menu (1, event->button.time, item_type, false, where);
1186                                 break;
1187
1188                         case MarkerBarItem: 
1189                         case RangeMarkerBarItem: 
1190                         case TransportMarkerBarItem:
1191                         case CdMarkerBarItem:
1192                         case TempoBarItem:
1193                         case MeterBarItem:
1194                                 popup_ruler_menu (where, item_type);
1195                                 break;
1196
1197                         case MarkerItem:
1198                                 marker_context_menu (&event->button, item);
1199                                 break;
1200
1201                         case TempoMarkerItem:
1202                                 tm_marker_context_menu (&event->button, item);
1203                                 break;
1204                                 
1205                         case MeterMarkerItem:
1206                                 tm_marker_context_menu (&event->button, item);
1207                                 break;
1208                         
1209                         case CrossfadeViewItem:
1210                                 popup_track_context_menu (1, event->button.time, item_type, false, where);
1211                                 break;
1212
1213 #ifdef WITH_CMT
1214                         case ImageFrameItem:
1215                                 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1216                                 break ;
1217                         case ImageFrameTimeAxisItem:
1218                                 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1219                                 break ;
1220                         case MarkerViewItem:
1221                                 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1222                                 break ;
1223                         case MarkerTimeAxisItem:
1224                                 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1225                                 break ;
1226 #endif
1227                                 
1228                         default:
1229                                 break;
1230                         }
1231
1232                         return true;
1233                 }
1234         }
1235
1236         /* delete events get handled here */
1237
1238         if (_drag == 0 && Keyboard::is_delete_event (&event->button)) {
1239
1240                 switch (item_type) {
1241                 case TempoMarkerItem:
1242                         remove_tempo_marker (item);
1243                         break;
1244                         
1245                 case MeterMarkerItem:
1246                         remove_meter_marker (item);
1247                         break;
1248
1249                 case MarkerItem:
1250                         remove_marker (*item, event);
1251                         break;
1252
1253                 case RegionItem:
1254                         if (mouse_mode == MouseObject) {
1255                                 remove_clicked_region ();
1256                         }
1257                         break;
1258                         
1259                 case ControlPointItem:
1260                         if (mouse_mode == MouseGain) {
1261                                 remove_gain_control_point (item, event);
1262                         } else {
1263                                 remove_control_point (item, event);
1264                         }
1265                         break;
1266
1267                 default:
1268                         break;
1269                 }
1270                 return true;
1271         }
1272
1273         switch (event->button.button) {
1274         case 1:
1275
1276                 switch (item_type) {
1277                 /* see comments in button_press_handler */
1278                 case PlayheadCursorItem:
1279                 case MarkerItem:
1280                 case GainLineItem:
1281                 case AutomationLineItem:
1282                 case StartSelectionTrimItem:
1283                 case EndSelectionTrimItem:
1284                         return true;
1285
1286                 case MarkerBarItem:
1287                         if (!_dragging_playhead) {
1288                                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1289                                         snap_to (where, 0, true);
1290                                 }
1291                                 mouse_add_new_marker (where);
1292                         }
1293                         return true;
1294
1295                 case CdMarkerBarItem:
1296                         if (!_dragging_playhead) {
1297                                 // if we get here then a dragged range wasn't done
1298                                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1299                                         snap_to (where, 0, true);
1300                                 }
1301                                 mouse_add_new_marker (where, true);
1302                         }
1303                         return true;
1304
1305                 case TempoBarItem:
1306                         if (!_dragging_playhead) {
1307                                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1308                                         snap_to (where);
1309                                 }
1310                                 mouse_add_new_tempo_event (where);
1311                         }
1312                         return true;
1313                         
1314                 case MeterBarItem:
1315                         if (!_dragging_playhead) {
1316                                 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1317                         } 
1318                         return true;
1319                         break;
1320
1321                 default:
1322                         break;
1323                 }
1324                 
1325                 switch (mouse_mode) {
1326                 case MouseObject:
1327                         switch (item_type) {
1328                         case AutomationTrackItem:
1329                                 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1330                                 if (atv) {
1331                                         atv->add_automation_event (item, event, where, event->button.y);
1332                                 } 
1333                                 return true;
1334                                 
1335                                 break;
1336                                 
1337                         default:
1338                                 break;
1339                         }
1340                         break;
1341
1342                 case MouseGain:
1343                         // Gain only makes sense for audio regions
1344
1345                         if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1346                                 break;
1347                         }
1348
1349                         switch (item_type) {
1350                         case RegionItem:
1351                                 /* check that we didn't drag before releasing, since
1352                                    its really annoying to create new control
1353                                    points when doing this.
1354                                 */
1355                                 if (were_dragging) {
1356                                         dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1357                                 }
1358                                 return true;
1359                                 break;
1360                                 
1361                         case AutomationTrackItem:
1362                                 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1363                                         add_automation_event (item, event, where, event->button.y);
1364                                 return true;
1365                                 break;
1366                         default:
1367                                 break;
1368                         }
1369                         break;
1370                         
1371                 case MouseAudition:
1372                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1373                         if (scrubbing_direction == 0) {
1374                                 /* no drag, just a click */
1375                                 switch (item_type) {
1376                                 case RegionItem:
1377                                         play_selected_region ();
1378                                         break;
1379                                 default:
1380                                         break;
1381                                 }
1382                         } else {
1383                                 /* make sure we stop */
1384                                 session->request_transport_speed (0.0);
1385                         }
1386                         break;
1387                         
1388                 default:
1389                         break;
1390
1391                 }
1392
1393                 return true;
1394                 break;
1395
1396
1397         case 2:
1398                 switch (mouse_mode) {
1399                         
1400                 case MouseObject:
1401                         switch (item_type) {
1402                         case RegionItem:
1403                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1404                                         raise_region ();
1405                                 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1406                                         lower_region ();
1407                                 } else {
1408                                         // Button2 click is unused
1409                                 }
1410                                 return true;
1411                                 
1412                                 break;
1413                                 
1414                         default:
1415                                 break;
1416                         }
1417                         break;
1418                         
1419                 case MouseRange:
1420                         
1421                         // x_style_paste (where, 1.0);
1422                         return true;
1423                         break;
1424                         
1425                 default:
1426                         break;
1427                 }
1428
1429                 break;
1430         
1431         case 3:
1432                 break;
1433                 
1434         default:
1435                 break;
1436         }
1437         return false;
1438 }
1439
1440 bool
1441 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1442 {
1443         ControlPoint* cp;
1444         Marker * marker;
1445         double fraction;
1446         
1447         if (last_item_entered != item) {
1448                 last_item_entered = item;
1449                 last_item_entered_n = 0;
1450         }
1451
1452         switch (item_type) {
1453         case ControlPointItem:
1454                 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1455                         cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1456                         cp->set_visible (true);
1457
1458                         double at_x, at_y;
1459                         at_x = cp->get_x();
1460                         at_y = cp->get_y ();
1461                         cp->item()->i2w (at_x, at_y);
1462                         at_x += 10.0;
1463                         at_y += 10.0;
1464
1465                         fraction = 1.0 - (cp->get_y() / cp->line().height());
1466
1467                         if (is_drawable() && dynamic_cast<ScrubDrag*> (_drag) == 0) {
1468                                 track_canvas->get_window()->set_cursor (*fader_cursor);
1469                         }
1470
1471                         last_item_entered_n++;
1472                         set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1473                         if (last_item_entered_n < 10) {
1474                                 show_verbose_canvas_cursor ();
1475                         }
1476                 }
1477                 break;
1478
1479         case GainLineItem:
1480                 if (mouse_mode == MouseGain) {
1481                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1482                         if (line)
1483                                 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1484                         if (is_drawable()) {
1485                                 track_canvas->get_window()->set_cursor (*fader_cursor);
1486                         }
1487                 }
1488                 break;
1489                         
1490         case AutomationLineItem:
1491                 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1492                         {
1493                                 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1494                                 if (line)
1495                                         line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1496                         }
1497                         if (is_drawable()) {
1498                                 track_canvas->get_window()->set_cursor (*fader_cursor);
1499                         }
1500                 }
1501                 break;
1502                 
1503         case RegionViewNameHighlight:
1504                 if (is_drawable() && mouse_mode == MouseObject) {
1505                         track_canvas->get_window()->set_cursor (*trimmer_cursor);
1506                 }
1507                 break;
1508
1509         case StartSelectionTrimItem:
1510         case EndSelectionTrimItem:
1511
1512 #ifdef WITH_CMT
1513         case ImageFrameHandleStartItem:
1514         case ImageFrameHandleEndItem:
1515         case MarkerViewHandleStartItem:
1516         case MarkerViewHandleEndItem:
1517 #endif
1518
1519                 if (is_drawable()) {
1520                         track_canvas->get_window()->set_cursor (*trimmer_cursor);
1521                 }
1522                 break;
1523
1524         case PlayheadCursorItem:
1525                 if (is_drawable()) {
1526                         switch (_edit_point) {
1527                         case EditAtMouse:
1528                                 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1529                                 break;
1530                         default:
1531                                 track_canvas->get_window()->set_cursor (*grabber_cursor);
1532                                 break;
1533                         }
1534                 }
1535                 break;
1536
1537         case RegionViewName:
1538                 
1539                 /* when the name is not an active item, the entire name highlight is for trimming */
1540
1541                 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1542                         if (mouse_mode == MouseObject && is_drawable()) {
1543                                 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1544                         }
1545                 } 
1546                 break;
1547
1548
1549         case AutomationTrackItem:
1550                 if (is_drawable()) {
1551                         Gdk::Cursor *cursor;
1552                         switch (mouse_mode) {
1553                         case MouseRange:
1554                                 cursor = selector_cursor;
1555                                 break;
1556                         case MouseZoom:
1557                                 cursor = zoom_cursor;
1558                                 break;
1559                         default:
1560                                 cursor = cross_hair_cursor;
1561                                 break;
1562                         }
1563
1564                         track_canvas->get_window()->set_cursor (*cursor);
1565
1566                         AutomationTimeAxisView* atv;
1567                         if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1568                                 clear_entered_track = false;
1569                                 set_entered_track (atv);
1570                         }
1571                 }
1572                 break;
1573
1574         case MarkerBarItem:
1575         case RangeMarkerBarItem:
1576         case TransportMarkerBarItem:
1577         case CdMarkerBarItem:
1578         case MeterBarItem:
1579         case TempoBarItem:
1580                 if (is_drawable()) {
1581                         track_canvas->get_window()->set_cursor (*timebar_cursor);
1582                 }
1583                 break;
1584
1585         case MarkerItem:
1586                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1587                         break;
1588                 }
1589                 entered_marker = marker;
1590                 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1591                 // fall through
1592         case MeterMarkerItem:
1593         case TempoMarkerItem:
1594                 if (is_drawable()) {
1595                         track_canvas->get_window()->set_cursor (*timebar_cursor);
1596                 }
1597                 break;
1598         case FadeInHandleItem:
1599         case FadeOutHandleItem:
1600                 if (mouse_mode == MouseObject) {
1601                         ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1602                         if (rect) {
1603                                 rect->property_fill_color_rgba() = 0;
1604                                 rect->property_outline_pixels() = 1;
1605                         }
1606                 }
1607                 break;
1608
1609         default:
1610                 break;
1611         }
1612
1613         /* second pass to handle entered track status in a comprehensible way.
1614          */
1615
1616         switch (item_type) {
1617         case GainLineItem:
1618         case AutomationLineItem:
1619         case ControlPointItem:
1620                 /* these do not affect the current entered track state */
1621                 clear_entered_track = false;
1622                 break;
1623
1624         case AutomationTrackItem:
1625                 /* handled above already */
1626                 break;
1627
1628         default:
1629                 set_entered_track (0);
1630                 break;
1631         }
1632
1633         return false;
1634 }
1635
1636 bool
1637 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1638 {
1639         AutomationLine* al;
1640         ControlPoint* cp;
1641         Marker *marker;
1642         Location *loc;
1643         RegionView* rv;
1644         bool is_start;
1645
1646         switch (item_type) {
1647         case ControlPointItem:
1648                 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1649                 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1650                         if (cp->line().npoints() > 1 && !cp->selected()) {
1651                                 cp->set_visible (false);
1652                         }
1653                 }
1654                 
1655                 if (is_drawable()) {
1656                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1657                 }
1658
1659                 hide_verbose_canvas_cursor ();
1660                 break;
1661                 
1662         case RegionViewNameHighlight:
1663         case StartSelectionTrimItem:
1664         case EndSelectionTrimItem:
1665         case PlayheadCursorItem:
1666
1667 #ifdef WITH_CMT
1668         case ImageFrameHandleStartItem:
1669         case ImageFrameHandleEndItem:
1670         case MarkerViewHandleStartItem:
1671         case MarkerViewHandleEndItem:
1672 #endif
1673
1674                 if (is_drawable()) {
1675                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1676                 }
1677                 break;
1678
1679         case GainLineItem:
1680         case AutomationLineItem:
1681                 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1682                 {
1683                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1684                         if (line)
1685                                 line->property_fill_color_rgba() = al->get_line_color();
1686                 }
1687                 if (is_drawable()) {
1688                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1689                 }
1690                 break;
1691
1692         case RegionViewName:
1693                 /* see enter_handler() for notes */
1694                 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1695                         if (is_drawable() && mouse_mode == MouseObject) {
1696                                 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1697                         }
1698                 }
1699                 break;
1700
1701         case RangeMarkerBarItem:
1702         case TransportMarkerBarItem:
1703         case CdMarkerBarItem:
1704         case MeterBarItem:
1705         case TempoBarItem:
1706         case MarkerBarItem:
1707                 if (is_drawable()) {
1708                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1709                 }
1710                 break;
1711                 
1712         case MarkerItem:
1713                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1714                         break;
1715                 }
1716                 entered_marker = 0;
1717                 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1718                         location_flags_changed (loc, this);
1719                 }
1720                 // fall through
1721         case MeterMarkerItem:
1722         case TempoMarkerItem:
1723                 
1724                 if (is_drawable()) {
1725                         track_canvas->get_window()->set_cursor (*timebar_cursor);
1726                 }
1727
1728                 break;
1729
1730         case FadeInHandleItem:
1731         case FadeOutHandleItem:
1732                 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1733                 {
1734                         ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1735                         if (rect) {
1736                                 rect->property_fill_color_rgba() = rv->get_fill_color();
1737                                 rect->property_outline_pixels() = 0;
1738                         }
1739                 }
1740                 break;
1741
1742         case AutomationTrackItem:
1743                 if (is_drawable()) {
1744                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1745                         clear_entered_track = true;
1746                         Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1747                 }
1748                 break;
1749                 
1750         default:
1751                 break;
1752         }
1753
1754         return false;
1755 }
1756
1757 gint
1758 Editor::left_automation_track ()
1759 {
1760         if (clear_entered_track) {
1761                 set_entered_track (0);
1762                 clear_entered_track = false;
1763         }
1764         return false;
1765 }
1766
1767 void
1768 Editor::scrub ()
1769 {
1770         double delta;
1771         
1772         if (scrubbing_direction == 0) {
1773                 /* first move */
1774                 session->request_locate (_drag->current_pointer_frame(), false);
1775                 session->request_transport_speed (0.1);
1776                 scrubbing_direction = 1;
1777                 
1778         } else {
1779                 
1780                 if (last_scrub_x > _drag->current_pointer_x()) {
1781                         
1782                         /* pointer moved to the left */
1783                         
1784                         if (scrubbing_direction > 0) {
1785                                 
1786                                 /* we reversed direction to go backwards */
1787                                 
1788                                 scrub_reversals++;
1789                                 scrub_reverse_distance += (int) (last_scrub_x - _drag->current_pointer_x());
1790                                 
1791                         } else {
1792                                 
1793                                 /* still moving to the left (backwards) */
1794                                 
1795                                 scrub_reversals = 0;
1796                                 scrub_reverse_distance = 0;
1797                                 
1798                                 delta = 0.01 * (last_scrub_x - _drag->current_pointer_x());
1799                                 session->request_transport_speed (session->transport_speed() - delta);
1800                         }
1801                         
1802                 } else {
1803                         /* pointer moved to the right */
1804                         
1805                         if (scrubbing_direction < 0) {
1806                                 /* we reversed direction to go forward */
1807                                 
1808                                 scrub_reversals++;
1809                                 scrub_reverse_distance += (int) (_drag->current_pointer_x() - last_scrub_x);
1810                                 
1811                         } else {
1812                                 /* still moving to the right */
1813                                 
1814                                 scrub_reversals = 0;
1815                                 scrub_reverse_distance = 0;
1816                                 
1817                                 delta = 0.01 * (_drag->current_pointer_x() - last_scrub_x);
1818                                 session->request_transport_speed (session->transport_speed() + delta);
1819                         }
1820                 }
1821                 
1822                 /* if there have been more than 2 opposite motion moves detected, or one that moves
1823                    back more than 10 pixels, reverse direction
1824                 */
1825                 
1826                 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1827                         
1828                         if (scrubbing_direction > 0) {
1829                                 /* was forwards, go backwards */
1830                                 session->request_transport_speed (-0.1);
1831                                 scrubbing_direction = -1;
1832                         } else {
1833                                 /* was backwards, go forwards */
1834                                 session->request_transport_speed (0.1);
1835                                 scrubbing_direction = 1;
1836                         }
1837                         
1838                         scrub_reverse_distance = 0;
1839                         scrub_reversals = 0;
1840                 }
1841         }
1842         
1843         last_scrub_x = _drag->current_pointer_x();
1844 }
1845
1846 bool
1847 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, bool from_autoscroll)
1848 {
1849         if (event->motion.is_hint) {
1850                 gint x, y;
1851                 
1852                 /* We call this so that MOTION_NOTIFY events continue to be
1853                    delivered to the canvas. We need to do this because we set
1854                    Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1855                    the density of the events, at the expense of a round-trip
1856                    to the server. Given that this will mostly occur on cases
1857                    where DISPLAY = :0.0, and given the cost of what the motion
1858                    event might do, its a good tradeoff.  
1859                 */
1860
1861                 track_canvas->get_pointer (x, y);
1862         } 
1863
1864         if (current_stepping_trackview) {
1865                 /* don't keep the persistent stepped trackview if the mouse moves */
1866                 current_stepping_trackview = 0;
1867                 step_timeout.disconnect ();
1868         }
1869
1870         if (session && session->actively_recording()) {
1871                 /* Sorry. no dragging stuff around while we record */
1872                 return true;
1873         }
1874
1875         bool handled = false;
1876         if (_drag) {
1877                 handled = _drag->motion_handler (event, from_autoscroll);
1878         }
1879
1880         if (!handled) {
1881                 return false;
1882         }
1883
1884         track_canvas_motion (event);
1885         return true;
1886 }
1887
1888 void
1889 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
1890 {
1891         ControlPoint* control_point;
1892
1893         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1894                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1895                 /*NOTREACHED*/
1896         }
1897
1898         // We shouldn't remove the first or last gain point
1899         if (control_point->line().is_last_point(*control_point) ||
1900                 control_point->line().is_first_point(*control_point)) { 
1901                 return;
1902         }
1903
1904         control_point->line().remove_point (*control_point);
1905 }
1906
1907 void
1908 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event)
1909 {
1910         ControlPoint* control_point;
1911
1912         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1913                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1914                 /*NOTREACHED*/
1915         }
1916
1917         control_point->line().remove_point (*control_point);
1918 }
1919
1920 void
1921 Editor::edit_control_point (ArdourCanvas::Item* item)
1922 {
1923         ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1924
1925         if (p == 0) {
1926                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1927                 /*NOTREACHED*/
1928         }
1929
1930         ControlPointDialog d (p);
1931         d.set_position (Gtk::WIN_POS_MOUSE);
1932         ensure_float (d);
1933
1934         if (d.run () != RESPONSE_ACCEPT) {
1935                 return;
1936         }
1937
1938         p->line().modify_point_y (*p, d.get_y_fraction ());
1939 }
1940
1941
1942 void
1943 Editor::visible_order_range (int* low, int* high) const
1944 {
1945         *low = TimeAxisView::max_order ();
1946         *high = 0;
1947         
1948         for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1949
1950                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1951                 
1952                 if (!rtv->hidden()) {
1953                         
1954                         if (*high < rtv->order()) {
1955                                 *high = rtv->order ();
1956                         }
1957                         
1958                         if (*low > rtv->order()) {
1959                                 *low = rtv->order ();
1960                         }
1961                 }
1962         }
1963 }
1964
1965 void
1966 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1967 {
1968         /* Either add to or set the set the region selection, unless
1969            this is an alignment click (control used)
1970         */
1971         
1972         if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1973                 TimeAxisView* tv = &rv.get_time_axis_view();
1974                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1975                 double speed = 1.0;
1976                 if (rtv && rtv->is_track()) {
1977                         speed = rtv->get_diskstream()->speed();
1978                 }
1979
1980                 nframes64_t where = get_preferred_edit_position();
1981
1982                 if (where >= 0) {
1983
1984                         if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
1985                                 
1986                                 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
1987                                 
1988                         } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
1989                                 
1990                                 align_region (rv.region(), End, (nframes64_t) (where * speed));
1991                                 
1992                         } else {
1993                                 
1994                                 align_region (rv.region(), Start, (nframes64_t) (where * speed));
1995                         }
1996                 }
1997         }
1998 }
1999
2000 void
2001 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos) 
2002 {
2003         char buf[128];
2004         SMPTE::Time smpte;
2005         BBT_Time bbt;
2006         int hours, mins;
2007         nframes64_t frame_rate;
2008         float secs;
2009
2010         if (session == 0) {
2011                 return;
2012         }
2013
2014         AudioClock::Mode m;
2015
2016         if (Profile->get_sae() || Profile->get_small_screen()) {
2017                 m = ARDOUR_UI::instance()->primary_clock.mode();
2018         } else {
2019                 m = ARDOUR_UI::instance()->secondary_clock.mode();
2020         }
2021
2022         switch (m) {
2023         case AudioClock::BBT:
2024                 session->bbt_time (frame, bbt);
2025                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
2026                 break;
2027                 
2028         case AudioClock::SMPTE:
2029                 session->smpte_time (frame, smpte);
2030                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
2031                 break;
2032
2033         case AudioClock::MinSec:
2034                 /* XXX this is copied from show_verbose_duration_cursor() */
2035                 frame_rate = session->frame_rate();
2036                 hours = frame / (frame_rate * 3600);
2037                 frame = frame % (frame_rate * 3600);
2038                 mins = frame / (frame_rate * 60);
2039                 frame = frame % (frame_rate * 60);
2040                 secs = (float) frame / (float) frame_rate;
2041                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
2042                 break;
2043
2044         default:
2045                 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
2046                 break;
2047         }
2048
2049         if (xpos >= 0 && ypos >=0) {
2050                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2051         }
2052         else {
2053                 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);
2054         }
2055         show_verbose_canvas_cursor ();
2056 }
2057
2058 void
2059 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos) 
2060 {
2061         char buf[128];
2062         SMPTE::Time smpte;
2063         BBT_Time sbbt;
2064         BBT_Time ebbt;
2065         int hours, mins;
2066         nframes64_t distance, frame_rate;
2067         float secs;
2068         Meter meter_at_start(session->tempo_map().meter_at(start));
2069
2070         if (session == 0) {
2071                 return;
2072         }
2073
2074         AudioClock::Mode m;
2075
2076         if (Profile->get_sae() || Profile->get_small_screen()) {
2077                 m = ARDOUR_UI::instance()->primary_clock.mode ();
2078         } else {
2079                 m = ARDOUR_UI::instance()->secondary_clock.mode ();
2080         }
2081
2082         switch (m) {
2083         case AudioClock::BBT:
2084                 session->bbt_time (start, sbbt);
2085                 session->bbt_time (end, ebbt);
2086
2087                 /* subtract */
2088                 /* XXX this computation won't work well if the
2089                 user makes a selection that spans any meter changes.
2090                 */
2091
2092                 ebbt.bars -= sbbt.bars;
2093                 if (ebbt.beats >= sbbt.beats) {
2094                         ebbt.beats -= sbbt.beats;
2095                 } else {
2096                         ebbt.bars--;
2097                         ebbt.beats =  int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
2098                 }
2099                 if (ebbt.ticks >= sbbt.ticks) {
2100                         ebbt.ticks -= sbbt.ticks;
2101                 } else {
2102                         ebbt.beats--;
2103                         ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2104                 }
2105                 
2106                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2107                 break;
2108                 
2109         case AudioClock::SMPTE:
2110                 session->smpte_duration (end - start, smpte);
2111                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
2112                 break;
2113
2114         case AudioClock::MinSec:
2115                 /* XXX this stuff should be elsewhere.. */
2116                 distance = end - start;
2117                 frame_rate = session->frame_rate();
2118                 hours = distance / (frame_rate * 3600);
2119                 distance = distance % (frame_rate * 3600);
2120                 mins = distance / (frame_rate * 60);
2121                 distance = distance % (frame_rate * 60);
2122                 secs = (float) distance / (float) frame_rate;
2123                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
2124                 break;
2125
2126         default:
2127                 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2128                 break;
2129         }
2130
2131         if (xpos >= 0 && ypos >=0) {
2132                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2133         }
2134         else {
2135                 set_verbose_canvas_cursor (buf, _drag->current_pointer_x() + offset, _drag->current_pointer_y() + offset);
2136         }
2137
2138         show_verbose_canvas_cursor ();
2139 }
2140
2141 void
2142 Editor::collect_new_region_view (RegionView* rv)
2143 {
2144         latest_regionviews.push_back (rv);
2145 }
2146
2147 void
2148 Editor::collect_and_select_new_region_view (RegionView* rv)
2149 {
2150         selection->add(rv);
2151         latest_regionviews.push_back (rv);
2152 }
2153
2154 void
2155 Editor::cancel_selection ()
2156 {
2157         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2158                 (*i)->hide_selection ();
2159         }
2160
2161         selection->clear ();
2162         clicked_selection = 0;
2163 }       
2164
2165
2166 void
2167 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
2168 {
2169         boost::shared_ptr<Region> region (rv.region());
2170
2171         if (region->locked()) {
2172                 return;
2173         }
2174
2175         nframes64_t new_bound;
2176
2177         double speed = 1.0;
2178         TimeAxisView* tvp = clicked_axisview;
2179         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2180
2181         if (tv && tv->is_track()) {
2182                 speed = tv->get_diskstream()->speed();
2183         }
2184         
2185         if (left_direction) {
2186                 if (swap_direction) {
2187                         new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2188                 } else {
2189                         new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2190                 }
2191         } else {
2192                 if (swap_direction) {
2193                         new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2194                 } else {
2195                         new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2196                 }
2197         }
2198
2199         if (obey_snap) {
2200                 snap_to (new_bound);
2201         }
2202         region->trim_start ((nframes64_t) (new_bound * speed), this);   
2203         rv.region_changed (StartChanged);
2204 }
2205
2206 void
2207 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2208 {
2209         boost::shared_ptr<Region> region (rv.region()); 
2210
2211         if (region->locked()) {
2212                 return;
2213         }
2214
2215         nframes64_t new_bound;
2216
2217         double speed = 1.0;
2218         TimeAxisView* tvp = clicked_axisview;
2219         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2220
2221         if (tv && tv->is_track()) {
2222                 speed = tv->get_diskstream()->speed();
2223         }
2224         
2225         if (left_direction) {
2226                 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2227         } else {
2228                 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2229         }
2230
2231         if (obey_snap) {
2232                 snap_to (new_bound, (left_direction ? 0 : 1));  
2233         }
2234         
2235         nframes64_t pre_trim_first_frame = region->first_frame();
2236
2237         region->trim_front ((nframes64_t) (new_bound * speed), this);
2238   
2239         if (no_overlap) {
2240                 //Get the next region on the left of this region and shrink/expand it.
2241                 boost::shared_ptr<Playlist> playlist (region->playlist());
2242                 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
2243                 
2244                 bool regions_touching = false;
2245
2246                 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){
2247                     regions_touching = true;
2248                 }
2249
2250                 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2251                 if (region_left != 0 && 
2252                         (region_left->last_frame() > region->first_frame() || regions_touching)) 
2253                 {
2254                         region_left->trim_end(region->first_frame(), this);
2255                 }
2256         }
2257
2258         
2259
2260         rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
2261 }
2262
2263 void
2264 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2265 {
2266         boost::shared_ptr<Region> region (rv.region());
2267
2268         if (region->locked()) {
2269                 return;
2270         }
2271
2272         nframes64_t new_bound;
2273
2274         double speed = 1.0;
2275         TimeAxisView* tvp = clicked_axisview;
2276         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2277
2278         if (tv && tv->is_track()) {
2279                 speed = tv->get_diskstream()->speed();
2280         }
2281
2282         if (left_direction) {
2283                 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
2284         } else {
2285                 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
2286         }
2287
2288         if (obey_snap) {
2289                 snap_to (new_bound);
2290         }
2291
2292         nframes64_t pre_trim_last_frame = region->last_frame();
2293
2294         region->trim_end ((nframes64_t) (new_bound * speed), this);
2295
2296         if (no_overlap) {
2297                 //Get the next region on the right of this region and shrink/expand it.
2298                 boost::shared_ptr<Playlist> playlist (region->playlist());
2299                 boost::shared_ptr<Region> region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1);
2300
2301                 bool regions_touching = false;
2302
2303                 if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)){
2304                     regions_touching = true;
2305                 }
2306
2307                 //Only trim region on the right if the last frame has gone beyond the right region's first frame.
2308                 if (region_right != 0 &&
2309                         (region_right->first_frame() < region->last_frame() || regions_touching)) 
2310                 {
2311                         region_right->trim_front(region->last_frame() + 1, this);
2312                 }
2313                 
2314                 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
2315         }
2316         else {
2317                 rv.region_changed (LengthChanged);
2318         }
2319 }
2320
2321
2322 void
2323 Editor::point_trim (GdkEvent* event)
2324 {
2325         RegionView* rv = clicked_regionview;
2326
2327         nframes64_t new_bound = _drag->current_pointer_frame();
2328
2329         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2330                 snap_to (new_bound);
2331         }
2332
2333         /* Choose action dependant on which button was pressed */
2334         switch (event->button.button) {
2335         case 1:
2336                 begin_reversible_command (_("Start point trim"));
2337
2338                 if (selection->selected (rv)) {
2339                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2340                              i != selection->regions.by_layer().end(); ++i)
2341                         {
2342                                 if ( (*i) == NULL){
2343                                     cerr << "region view contains null region" << endl;
2344                                 }
2345
2346                                 if (!(*i)->region()->locked()) {
2347                                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
2348                                         XMLNode &before = pl->get_state();
2349
2350                                         (*i)->region()->trim_front (new_bound, this);
2351
2352                                         XMLNode &after = pl->get_state();
2353                                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2354                                 }
2355                         }
2356
2357                 } else {
2358                         if (!rv->region()->locked()) {
2359                                 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2360                                 XMLNode &before = pl->get_state();
2361                                 rv->region()->trim_front (new_bound, this);
2362                                 XMLNode &after = pl->get_state();
2363                                 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2364                         }
2365                 }
2366
2367                 commit_reversible_command();
2368         
2369                 break;
2370         case 2:
2371                 begin_reversible_command (_("End point trim"));
2372
2373                 if (selection->selected (rv)) {
2374                         
2375                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2376                         {
2377                                 if (!(*i)->region()->locked()) {
2378                                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
2379                                         XMLNode &before = pl->get_state();
2380                                         (*i)->region()->trim_end (new_bound, this);
2381                                         XMLNode &after = pl->get_state();
2382                                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2383                                 }
2384                         }
2385
2386                 } else {
2387
2388                         if (!rv->region()->locked()) {
2389                                 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2390                                 XMLNode &before = pl->get_state();
2391                                 rv->region()->trim_end (new_bound, this);
2392                                 XMLNode &after = pl->get_state();
2393                                 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
2394                         }
2395                 }
2396
2397                 commit_reversible_command();
2398         
2399                 break;
2400         default:
2401                 break;
2402         }
2403 }
2404
2405 void
2406 Editor::thaw_region_after_trim (RegionView& rv)
2407 {
2408         boost::shared_ptr<Region> region (rv.region());
2409
2410         if (region->locked()) {
2411                 return;
2412         }
2413
2414         region->thaw (_("trimmed region"));
2415         XMLNode &after = region->playlist()->get_state();
2416         session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
2417
2418         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
2419         if (arv)
2420                 arv->unhide_envelope ();
2421 }
2422
2423 void
2424 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
2425 {
2426         Marker* marker;
2427         bool is_start;
2428
2429         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2430                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2431                 /*NOTREACHED*/
2432         }
2433
2434         Location* location = find_location_from_marker (marker, is_start);      
2435         location->set_hidden (true, this);
2436 }
2437
2438
2439 void
2440 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
2441 {
2442         double x1 = frame_to_pixel (start);
2443         double x2 = frame_to_pixel (end);
2444         double y2 = full_canvas_height - 1.0;
2445
2446         zoom_rect->property_x1() = x1;
2447         zoom_rect->property_y1() = 1.0;
2448         zoom_rect->property_x2() = x2;
2449         zoom_rect->property_y2() = y2;
2450 }
2451
2452
2453 gint
2454 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
2455 {
2456         using namespace Gtkmm2ext;
2457
2458         ArdourPrompter prompter (false);
2459
2460         prompter.set_prompt (_("Name for region:"));
2461         prompter.set_initial_text (clicked_regionview->region()->name());
2462         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2463         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2464         prompter.show_all ();
2465         switch (prompter.run ()) {
2466         case Gtk::RESPONSE_ACCEPT:
2467                 string str;
2468                 prompter.get_result(str);
2469                 if (str.length()) {
2470                         clicked_regionview->region()->set_name (str);
2471                 }
2472                 break;
2473         }
2474         return true;
2475 }
2476
2477
2478 void
2479 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
2480 {
2481         /* no brushing without a useful snap setting */
2482
2483         switch (snap_mode) {
2484         case SnapMagnetic:
2485                 return; /* can't work because it allows region to be placed anywhere */
2486         default:
2487                 break; /* OK */
2488         }
2489
2490         switch (snap_type) {
2491         case SnapToMark:
2492                 return;
2493
2494         default:
2495                 break;
2496         }
2497
2498         /* don't brush a copy over the original */
2499         
2500         if (pos == rv->region()->position()) {
2501                 return;
2502         }
2503
2504         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2505
2506         if (rtv == 0 || !rtv->is_track()) {
2507                 return;
2508         }
2509
2510         boost::shared_ptr<Playlist> playlist = rtv->playlist();
2511         double speed = rtv->get_diskstream()->speed();
2512         
2513         XMLNode &before = playlist->get_state();
2514         playlist->add_region (RegionFactory::create (rv->region()), (nframes64_t) (pos * speed));
2515         XMLNode &after = playlist->get_state();
2516         session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
2517         
2518         // playlist is frozen, so we have to update manually
2519         
2520         playlist->Modified(); /* EMIT SIGNAL */
2521 }
2522
2523 gint
2524 Editor::track_height_step_timeout ()
2525 {
2526         if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2527                 current_stepping_trackview = 0;
2528                 return false;
2529         }
2530         return true;
2531 }
2532
2533 void
2534 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2535 {
2536         assert (region_view);
2537
2538         _region_motion_group->raise_to_top ();
2539         
2540         assert (_drag == 0);
2541         
2542         if (Config->get_edit_mode() == Splice) {
2543                 _drag = new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer());
2544         } else {
2545                 _drag = new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false);
2546         }
2547         
2548         _drag->start_grab (event);
2549
2550         begin_reversible_command (_("move region(s)"));
2551
2552         /* sync the canvas to what we think is its current state */
2553         update_canvas_now();
2554 }
2555
2556 void
2557 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2558 {
2559         assert (region_view);
2560         assert (_drag == 0);
2561         
2562         _region_motion_group->raise_to_top ();
2563         _drag = new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true);
2564         _drag->start_grab(event);
2565 }
2566
2567 void
2568 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2569 {
2570         assert (region_view);
2571         assert (_drag == 0);
2572         
2573         if (Config->get_edit_mode() == Splice) {
2574                 return;
2575         }
2576
2577         _drag = new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false);
2578         _drag->start_grab (event);
2579         
2580         begin_reversible_command (_("Drag region brush"));
2581 }
2582
2583 /** Start a grab where a time range is selected, track(s) are selected, and the
2584  *  user clicks and drags a region with a modifier in order to create a new region containing
2585  *  the section of the clicked region that lies within the time range.
2586  */
2587 void
2588 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
2589 {
2590         if (clicked_regionview == 0) {
2591                 return;
2592         }
2593
2594         /* lets try to create new Region for the selection */
2595
2596         vector<boost::shared_ptr<Region> > new_regions;
2597         create_region_from_selection (new_regions);
2598
2599         if (new_regions.empty()) {
2600                 return;
2601         }
2602
2603         /* XXX fix me one day to use all new regions */
2604         
2605         boost::shared_ptr<Region> region (new_regions.front());
2606
2607         /* add it to the current stream/playlist.
2608
2609            tricky: the streamview for the track will add a new regionview. we will
2610            catch the signal it sends when it creates the regionview to
2611            set the regionview we want to then drag.
2612         */
2613         
2614         latest_regionviews.clear();
2615         sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2616         
2617         /* A selection grab currently creates two undo/redo operations, one for 
2618            creating the new region and another for moving it.
2619         */
2620
2621         begin_reversible_command (_("selection grab"));
2622
2623         boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2624
2625         XMLNode *before = &(playlist->get_state());
2626         clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2627         XMLNode *after = &(playlist->get_state());
2628         session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
2629
2630         commit_reversible_command ();
2631         
2632         c.disconnect ();
2633         
2634         if (latest_regionviews.empty()) {
2635                 /* something went wrong */
2636                 return;
2637         }
2638
2639         /* we need to deselect all other regionviews, and select this one
2640            i'm ignoring undo stuff, because the region creation will take care of it 
2641         */
2642         selection->set (latest_regionviews);
2643         
2644         assert (_drag == 0);
2645         _drag = new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false);
2646         _drag->start_grab (event);
2647 }
2648
2649 void
2650 Editor::break_drag ()
2651 {
2652         if (_drag) {
2653                 _drag->break_drag ();
2654         }
2655 }