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