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