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