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