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