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