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