make step edit cursor follow zoom (and change its color a bit)
[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         /* 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         /* edit events get handled here */
1130
1131         if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1132                 switch (item_type) {
1133                 case RegionItem:
1134                         edit_region ();
1135                         break;
1136
1137                 case TempoMarkerItem:
1138                         edit_tempo_marker (item);
1139                         break;
1140
1141                 case MeterMarkerItem:
1142                         edit_meter_marker (item);
1143                         break;
1144
1145                 case RegionViewName:
1146                         if (clicked_regionview->name_active()) {
1147                                 return mouse_rename_region (item, event);
1148                         }
1149                         break;
1150
1151                 case ControlPointItem:
1152                         edit_control_point (item);
1153                         break;
1154
1155                 default:
1156                         break;
1157                 }
1158                 return true;
1159         }
1160
1161         /* context menu events get handled here */
1162
1163         if (Keyboard::is_context_menu_event (&event->button)) {
1164
1165                 if (!_drags->active ()) {
1166
1167                         /* no matter which button pops up the context menu, tell the menu
1168                            widget to use button 1 to drive menu selection.
1169                         */
1170
1171                         switch (item_type) {
1172                         case FadeInItem:
1173                         case FadeInHandleItem:
1174                         case FadeOutItem:
1175                         case FadeOutHandleItem:
1176                                 popup_fade_context_menu (1, event->button.time, item, item_type);
1177                                 break;
1178
1179                         case StreamItem:
1180                                 popup_track_context_menu (1, event->button.time, item_type, false, where);
1181                                 break;
1182
1183                         case RegionItem:
1184                         case RegionViewNameHighlight:
1185                         case LeftFrameHandle:
1186                         case RightFrameHandle:
1187                         case RegionViewName:
1188                                 popup_track_context_menu (1, event->button.time, item_type, false, where);
1189                                 break;
1190
1191                         case SelectionItem:
1192                                 popup_track_context_menu (1, event->button.time, item_type, true, where);
1193                                 break;
1194
1195                         case AutomationTrackItem:
1196                                 popup_track_context_menu (1, event->button.time, item_type, false, where);
1197                                 break;
1198
1199                         case MarkerBarItem:
1200                         case RangeMarkerBarItem:
1201                         case TransportMarkerBarItem:
1202                         case CdMarkerBarItem:
1203                         case TempoBarItem:
1204                         case MeterBarItem:
1205                                 popup_ruler_menu (where, item_type);
1206                                 break;
1207
1208                         case MarkerItem:
1209                                 marker_context_menu (&event->button, item);
1210                                 break;
1211
1212                         case TempoMarkerItem:
1213                                 tempo_or_meter_marker_context_menu (&event->button, item);
1214                                 break;
1215
1216                         case MeterMarkerItem:
1217                                 tempo_or_meter_marker_context_menu (&event->button, item);
1218                                 break;
1219
1220                         case CrossfadeViewItem:
1221                                 popup_track_context_menu (1, event->button.time, item_type, false, where);
1222                                 break;
1223
1224 #ifdef WITH_CMT
1225                         case ImageFrameItem:
1226                                 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1227                                 break ;
1228                         case ImageFrameTimeAxisItem:
1229                                 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1230                                 break ;
1231                         case MarkerViewItem:
1232                                 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1233                                 break ;
1234                         case MarkerTimeAxisItem:
1235                                 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1236                                 break ;
1237 #endif
1238
1239                         default:
1240                                 break;
1241                         }
1242
1243                         return true;
1244                 }
1245         }
1246
1247         /* delete events get handled here */
1248
1249         Editing::MouseMode const eff = effective_mouse_mode ();
1250
1251         if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1252
1253                 switch (item_type) {
1254                 case TempoMarkerItem:
1255                         remove_tempo_marker (item);
1256                         break;
1257
1258                 case MeterMarkerItem:
1259                         remove_meter_marker (item);
1260                         break;
1261
1262                 case MarkerItem:
1263                         remove_marker (*item, event);
1264                         break;
1265
1266                 case RegionItem:
1267                         if (eff == MouseObject) {
1268                                 remove_clicked_region ();
1269                         }
1270                         break;
1271
1272                 case ControlPointItem:
1273                         if (eff == MouseGain) {
1274                                 remove_gain_control_point (item, event);
1275                         } else {
1276                                 remove_control_point (item, event);
1277                         }
1278                         break;
1279
1280                 case NoteItem:
1281                         remove_midi_note (item, event);
1282                         break;
1283
1284                 default:
1285                         break;
1286                 }
1287                 return true;
1288         }
1289
1290         switch (event->button.button) {
1291         case 1:
1292
1293                 switch (item_type) {
1294                 /* see comments in button_press_handler */
1295                 case PlayheadCursorItem:
1296                 case MarkerItem:
1297                 case GainLineItem:
1298                 case AutomationLineItem:
1299                 case StartSelectionTrimItem:
1300                 case EndSelectionTrimItem:
1301                         return true;
1302
1303                 case MarkerBarItem:
1304                         if (!_dragging_playhead) {
1305                                 snap_to_with_modifier (where, event, 0, true);
1306                                 mouse_add_new_marker (where);
1307                         }
1308                         return true;
1309
1310                 case CdMarkerBarItem:
1311                         if (!_dragging_playhead) {
1312                                 // if we get here then a dragged range wasn't done
1313                                 snap_to_with_modifier (where, event, 0, true);
1314                                 mouse_add_new_marker (where, true);
1315                         }
1316                         return true;
1317
1318                 case TempoBarItem:
1319                         if (!_dragging_playhead) {
1320                                 snap_to_with_modifier (where, event);
1321                                 mouse_add_new_tempo_event (where);
1322                         }
1323                         return true;
1324
1325                 case MeterBarItem:
1326                         if (!_dragging_playhead) {
1327                                 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1328                         }
1329                         return true;
1330                         break;
1331
1332                 default:
1333                         break;
1334                 }
1335
1336                 switch (eff) {
1337                 case MouseObject:
1338                         switch (item_type) {
1339                         case AutomationTrackItem:
1340                                 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1341                                 if (atv) {
1342                                         atv->add_automation_event (item, event, where, event->button.y);
1343                                 }
1344                                 return true;
1345                                 break;
1346
1347                         default:
1348                                 break;
1349                         }
1350                         break;
1351
1352                 case MouseGain:
1353                         switch (item_type) {
1354                         case RegionItem:
1355                         {
1356                                 /* check that we didn't drag before releasing, since
1357                                    its really annoying to create new control
1358                                    points when doing this.
1359                                 */
1360                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1361                                 if (were_dragging && arv) {
1362                                         arv->add_gain_point_event (item, event);
1363                                 }
1364                                 return true;
1365                                 break;
1366                         }
1367
1368                         case AutomationTrackItem:
1369                                 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1370                                         add_automation_event (item, event, where, event->button.y);
1371                                 return true;
1372                                 break;
1373                         default:
1374                                 break;
1375                         }
1376                         break;
1377
1378                 case MouseAudition:
1379                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1380                         if (scrubbing_direction == 0) {
1381                                 /* no drag, just a click */
1382                                 switch (item_type) {
1383                                 case RegionItem:
1384                                         play_selected_region ();
1385                                         break;
1386                                 default:
1387                                         break;
1388                                 }
1389                         } else {
1390                                 /* make sure we stop */
1391                                 _session->request_transport_speed (0.0);
1392                         }
1393                         break;
1394
1395                 default:
1396                         break;
1397
1398                 }
1399
1400                 return true;
1401                 break;
1402
1403
1404         case 2:
1405                 switch (eff) {
1406
1407                 case MouseObject:
1408                         switch (item_type) {
1409                         case RegionItem:
1410                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1411                                         raise_region ();
1412                                 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1413                                         lower_region ();
1414                                 } else {
1415                                         // Button2 click is unused
1416                                 }
1417                                 return true;
1418
1419                                 break;
1420
1421                         default:
1422                                 break;
1423                         }
1424                         break;
1425
1426                 case MouseRange:
1427
1428                         // x_style_paste (where, 1.0);
1429                         return true;
1430                         break;
1431
1432                 default:
1433                         break;
1434                 }
1435
1436                 break;
1437
1438         case 3:
1439                 break;
1440
1441         default:
1442                 break;
1443         }
1444         return false;
1445 }
1446
1447 bool
1448 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1449 {
1450         ControlPoint* cp;
1451         Marker * marker;
1452         double fraction;
1453         bool ret = true;
1454
1455         if (last_item_entered != item) {
1456                 last_item_entered = item;
1457                 last_item_entered_n = 0;
1458         }
1459
1460         switch (item_type) {
1461         case ControlPointItem:
1462                 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1463                         cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1464                         cp->set_visible (true);
1465
1466                         double at_x, at_y;
1467                         at_x = cp->get_x();
1468                         at_y = cp->get_y ();
1469                         cp->i2w (at_x, at_y);
1470                         at_x += 10.0;
1471                         at_y += 10.0;
1472
1473                         fraction = 1.0 - (cp->get_y() / cp->line().height());
1474
1475                         if (is_drawable() && !_drags->active ()) {
1476                                 track_canvas->get_window()->set_cursor (*fader_cursor);
1477                         }
1478
1479                         last_item_entered_n++;
1480                         set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1481                         if (last_item_entered_n < 10) {
1482                                 show_verbose_canvas_cursor ();
1483                         }
1484                 }
1485                 break;
1486
1487         case GainLineItem:
1488                 if (mouse_mode == MouseGain) {
1489                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1490                         if (line)
1491                                 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1492                         if (is_drawable()) {
1493                                 track_canvas->get_window()->set_cursor (*fader_cursor);
1494                         }
1495                 }
1496                 break;
1497
1498         case AutomationLineItem:
1499                 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1500                         {
1501                                 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1502                                 if (line)
1503                                         line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1504                         }
1505                         if (is_drawable()) {
1506                                 track_canvas->get_window()->set_cursor (*fader_cursor);
1507                         }
1508                 }
1509                 break;
1510
1511         case RegionViewNameHighlight:
1512                 if (is_drawable() && mouse_mode == MouseObject && !internal_editing()) {
1513                         track_canvas->get_window()->set_cursor (*trimmer_cursor);
1514                 }
1515                 break;
1516
1517         case LeftFrameHandle:
1518                 if (is_drawable() && mouse_mode == MouseObject && !internal_editing()) {
1519                         track_canvas->get_window()->set_cursor (*left_side_trim_cursor);
1520                 }
1521                 break;
1522
1523         case RightFrameHandle:
1524                 if (is_drawable() && mouse_mode == MouseObject && !internal_editing()) {
1525                         track_canvas->get_window()->set_cursor (*right_side_trim_cursor);
1526                 }
1527                 break;
1528
1529         case StartSelectionTrimItem:
1530         case EndSelectionTrimItem:
1531
1532 #ifdef WITH_CMT
1533         case ImageFrameHandleStartItem:
1534         case ImageFrameHandleEndItem:
1535         case MarkerViewHandleStartItem:
1536         case MarkerViewHandleEndItem:
1537 #endif
1538
1539                 if (is_drawable()) {
1540                         track_canvas->get_window()->set_cursor (*trimmer_cursor);
1541                 }
1542                 break;
1543
1544         case PlayheadCursorItem:
1545                 if (is_drawable()) {
1546                         switch (_edit_point) {
1547                         case EditAtMouse:
1548                                 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1549                                 break;
1550                         default:
1551                                 track_canvas->get_window()->set_cursor (*grabber_cursor);
1552                                 break;
1553                         }
1554                 }
1555                 break;
1556
1557         case RegionViewName:
1558
1559                 /* when the name is not an active item, the entire name highlight is for trimming */
1560
1561                 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1562                         if (mouse_mode == MouseObject && is_drawable()) {
1563                                 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1564                         }
1565                 }
1566                 break;
1567
1568
1569         case AutomationTrackItem:
1570                 if (is_drawable()) {
1571                         Gdk::Cursor *cursor;
1572                         switch (mouse_mode) {
1573                         case MouseRange:
1574                                 cursor = selector_cursor;
1575                                 break;
1576                         case MouseZoom:
1577                                 cursor = zoom_cursor;
1578                                 break;
1579                         default:
1580                                 cursor = cross_hair_cursor;
1581                                 break;
1582                         }
1583
1584                         track_canvas->get_window()->set_cursor (*cursor);
1585
1586                         AutomationTimeAxisView* atv;
1587                         if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1588                                 clear_entered_track = false;
1589                                 set_entered_track (atv);
1590                         }
1591                 }
1592                 break;
1593
1594         case MarkerBarItem:
1595         case RangeMarkerBarItem:
1596         case TransportMarkerBarItem:
1597         case CdMarkerBarItem:
1598         case MeterBarItem:
1599         case TempoBarItem:
1600                 if (is_drawable()) {
1601                         track_canvas->get_window()->set_cursor (*timebar_cursor);
1602                 }
1603                 break;
1604
1605         case MarkerItem:
1606                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1607                         break;
1608                 }
1609                 entered_marker = marker;
1610                 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1611                 // fall through
1612         case MeterMarkerItem:
1613         case TempoMarkerItem:
1614                 if (is_drawable()) {
1615                         track_canvas->get_window()->set_cursor (*timebar_cursor);
1616                 }
1617                 break;
1618
1619         case FadeInHandleItem:
1620                 if (mouse_mode == MouseObject && !internal_editing()) {
1621                         ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1622                         if (rect) {
1623                                 rect->property_fill_color_rgba() = 0;
1624                                 rect->property_outline_pixels() = 1;
1625                         }
1626                         track_canvas->get_window()->set_cursor (*fade_in_cursor);
1627                 }
1628                 break;
1629
1630         case FadeOutHandleItem:
1631                 if (mouse_mode == MouseObject && !internal_editing()) {
1632                         ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1633                         if (rect) {
1634                                 rect->property_fill_color_rgba() = 0;
1635                                 rect->property_outline_pixels() = 1;
1636                         }
1637                         track_canvas->get_window()->set_cursor (*fade_out_cursor);
1638                 }
1639                 break;
1640         case FeatureLineItem:
1641                 {
1642                         ArdourCanvas::SimpleLine *line = dynamic_cast<ArdourCanvas::SimpleLine *> (item);
1643                         line->property_color_rgba() = 0xFF0000FF;
1644                 }
1645                 break;
1646         default:
1647                 break;
1648         }
1649
1650         /* second pass to handle entered track status in a comprehensible way.
1651          */
1652
1653         switch (item_type) {
1654         case GainLineItem:
1655         case AutomationLineItem:
1656         case ControlPointItem:
1657                 /* these do not affect the current entered track state */
1658                 clear_entered_track = false;
1659                 break;
1660
1661         case AutomationTrackItem:
1662                 /* handled above already */
1663                 break;
1664
1665         default:
1666                 set_entered_track (0);
1667                 break;
1668         }
1669
1670         return ret;
1671 }
1672
1673 bool
1674 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1675 {
1676         AutomationLine* al;
1677         ControlPoint* cp;
1678         Marker *marker;
1679         Location *loc;
1680         RegionView* rv;
1681         bool is_start;
1682         bool ret = true;
1683
1684         switch (item_type) {
1685         case ControlPointItem:
1686                 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1687                 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1688                         if (cp->line().npoints() > 1 && !cp->get_selected()) {
1689                                 cp->set_visible (false);
1690                         }
1691                 }
1692
1693                 if (is_drawable()) {
1694                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1695                 }
1696
1697                 hide_verbose_canvas_cursor ();
1698                 break;
1699
1700         case RegionViewNameHighlight:
1701         case LeftFrameHandle:
1702         case RightFrameHandle:
1703         case StartSelectionTrimItem:
1704         case EndSelectionTrimItem:
1705         case PlayheadCursorItem:
1706
1707 #ifdef WITH_CMT
1708         case ImageFrameHandleStartItem:
1709         case ImageFrameHandleEndItem:
1710         case MarkerViewHandleStartItem:
1711         case MarkerViewHandleEndItem:
1712 #endif
1713
1714                 if (is_drawable()) {
1715                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1716                 }
1717                 break;
1718
1719         case GainLineItem:
1720         case AutomationLineItem:
1721                 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1722                 {
1723                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1724                         if (line)
1725                                 line->property_fill_color_rgba() = al->get_line_color();
1726                 }
1727                 if (is_drawable()) {
1728                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1729                 }
1730                 break;
1731
1732         case RegionViewName:
1733                 /* see enter_handler() for notes */
1734                 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1735                         if (is_drawable() && mouse_mode == MouseObject) {
1736                                 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1737                         }
1738                 }
1739                 break;
1740
1741         case RangeMarkerBarItem:
1742         case TransportMarkerBarItem:
1743         case CdMarkerBarItem:
1744         case MeterBarItem:
1745         case TempoBarItem:
1746         case MarkerBarItem:
1747                 if (is_drawable()) {
1748                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1749                 }
1750                 break;
1751
1752         case MarkerItem:
1753                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1754                         break;
1755                 }
1756                 entered_marker = 0;
1757                 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1758                         location_flags_changed (loc, this);
1759                 }
1760                 // fall through
1761         case MeterMarkerItem:
1762         case TempoMarkerItem:
1763
1764                 if (is_drawable()) {
1765                         track_canvas->get_window()->set_cursor (*timebar_cursor);
1766                 }
1767
1768                 break;
1769
1770         case FadeInHandleItem:
1771         case FadeOutHandleItem:
1772                 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1773                 {
1774                         ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1775                         if (rect) {
1776                                 rect->property_fill_color_rgba() = rv->get_fill_color();
1777                                 rect->property_outline_pixels() = 0;
1778                         }
1779                 }
1780                 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1781                 break;
1782
1783         case AutomationTrackItem:
1784                 if (is_drawable()) {
1785                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1786                         clear_entered_track = true;
1787                         Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
1788                 }
1789                 break;
1790         case FeatureLineItem:
1791                 {
1792                         ArdourCanvas::SimpleLine *line = dynamic_cast<ArdourCanvas::SimpleLine *> (item);
1793                         line->property_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
1794                 }
1795                 break;
1796
1797         default:
1798                 break;
1799         }
1800
1801         return ret;
1802 }
1803
1804 gint
1805 Editor::left_automation_track ()
1806 {
1807         if (clear_entered_track) {
1808                 set_entered_track (0);
1809                 clear_entered_track = false;
1810         }
1811         return false;
1812 }
1813
1814 void
1815 Editor::scrub (nframes64_t frame, double current_x)
1816 {
1817         double delta;
1818
1819         if (scrubbing_direction == 0) {
1820                 /* first move */
1821                 _session->request_locate (frame, false);
1822                 _session->request_transport_speed (0.1);
1823                 scrubbing_direction = 1;
1824
1825         } else {
1826
1827                 if (last_scrub_x > current_x) {
1828
1829                         /* pointer moved to the left */
1830
1831                         if (scrubbing_direction > 0) {
1832
1833                                 /* we reversed direction to go backwards */
1834
1835                                 scrub_reversals++;
1836                                 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1837
1838                         } else {
1839
1840                                 /* still moving to the left (backwards) */
1841
1842                                 scrub_reversals = 0;
1843                                 scrub_reverse_distance = 0;
1844
1845                                 delta = 0.01 * (last_scrub_x - current_x);
1846                                 _session->request_transport_speed (_session->transport_speed() - delta);
1847                         }
1848
1849                 } else {
1850                         /* pointer moved to the right */
1851
1852                         if (scrubbing_direction < 0) {
1853                                 /* we reversed direction to go forward */
1854
1855                                 scrub_reversals++;
1856                                 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1857
1858                         } else {
1859                                 /* still moving to the right */
1860
1861                                 scrub_reversals = 0;
1862                                 scrub_reverse_distance = 0;
1863
1864                                 delta = 0.01 * (current_x - last_scrub_x);
1865                                 _session->request_transport_speed (_session->transport_speed() + delta);
1866                         }
1867                 }
1868
1869                 /* if there have been more than 2 opposite motion moves detected, or one that moves
1870                    back more than 10 pixels, reverse direction
1871                 */
1872
1873                 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1874
1875                         if (scrubbing_direction > 0) {
1876                                 /* was forwards, go backwards */
1877                                 _session->request_transport_speed (-0.1);
1878                                 scrubbing_direction = -1;
1879                         } else {
1880                                 /* was backwards, go forwards */
1881                                 _session->request_transport_speed (0.1);
1882                                 scrubbing_direction = 1;
1883                         }
1884
1885                         scrub_reverse_distance = 0;
1886                         scrub_reversals = 0;
1887                 }
1888         }
1889
1890         last_scrub_x = current_x;
1891 }
1892
1893 bool
1894 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1895 {
1896         if (event->motion.is_hint) {
1897                 gint x, y;
1898
1899                 /* We call this so that MOTION_NOTIFY events continue to be
1900                    delivered to the canvas. We need to do this because we set
1901                    Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1902                    the density of the events, at the expense of a round-trip
1903                    to the server. Given that this will mostly occur on cases
1904                    where DISPLAY = :0.0, and given the cost of what the motion
1905                    event might do, its a good tradeoff.
1906                 */
1907
1908                 track_canvas->get_pointer (x, y);
1909         }
1910
1911         if (current_stepping_trackview) {
1912                 /* don't keep the persistent stepped trackview if the mouse moves */
1913                 current_stepping_trackview = 0;
1914                 step_timeout.disconnect ();
1915         }
1916
1917         if (_session && _session->actively_recording()) {
1918                 /* Sorry. no dragging stuff around while we record */
1919                 return true;
1920         }
1921
1922         JoinObjectRangeState const old = _join_object_range_state;
1923         update_join_object_range_location (event->motion.x, event->motion.y);
1924         if (_join_object_range_state != old) {
1925                 set_canvas_cursor ();
1926         }
1927
1928         bool handled = false;
1929         if (_drags->active ()) {
1930                 handled = _drags->motion_handler (event, from_autoscroll);
1931         }
1932         
1933         if (!handled) {
1934                 return false;
1935         }
1936
1937         track_canvas_motion (event);
1938         return true;
1939 }
1940
1941 void
1942 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
1943 {
1944         ControlPoint* control_point;
1945
1946         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1947                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1948                 /*NOTREACHED*/
1949         }
1950
1951         // We shouldn't remove the first or last gain point
1952         if (control_point->line().is_last_point(*control_point) ||
1953                 control_point->line().is_first_point(*control_point)) {
1954                 return;
1955         }
1956
1957         control_point->line().remove_point (*control_point);
1958 }
1959
1960 void
1961 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/)
1962 {
1963         ControlPoint* control_point;
1964
1965         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1966                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1967                 /*NOTREACHED*/
1968         }
1969
1970         control_point->line().remove_point (*control_point);
1971 }
1972
1973 void
1974 Editor::edit_control_point (ArdourCanvas::Item* item)
1975 {
1976         ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1977
1978         if (p == 0) {
1979                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1980                 /*NOTREACHED*/
1981         }
1982
1983         ControlPointDialog d (p);
1984         d.set_position (Gtk::WIN_POS_MOUSE);
1985         ensure_float (d);
1986
1987         if (d.run () != RESPONSE_ACCEPT) {
1988                 return;
1989         }
1990
1991         p->line().modify_point_y (*p, d.get_y_fraction ());
1992 }
1993
1994
1995 void
1996 Editor::visible_order_range (int* low, int* high) const
1997 {
1998         *low = TimeAxisView::max_order ();
1999         *high = 0;
2000
2001         for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2002
2003                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2004
2005                 if (!rtv->hidden()) {
2006
2007                         if (*high < rtv->order()) {
2008                                 *high = rtv->order ();
2009                         }
2010
2011                         if (*low > rtv->order()) {
2012                                 *low = rtv->order ();
2013                         }
2014                 }
2015         }
2016 }
2017
2018 void
2019 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2020 {
2021         /* Either add to or set the set the region selection, unless
2022            this is an alignment click (control used)
2023         */
2024
2025         if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2026                 TimeAxisView* tv = &rv.get_time_axis_view();
2027                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2028                 double speed = 1.0;
2029                 if (rtv && rtv->is_track()) {
2030                         speed = rtv->track()->speed();
2031                 }
2032
2033                 nframes64_t where = get_preferred_edit_position();
2034
2035                 if (where >= 0) {
2036
2037                         if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2038
2039                                 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
2040
2041                         } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2042
2043                                 align_region (rv.region(), End, (nframes64_t) (where * speed));
2044
2045                         } else {
2046
2047                                 align_region (rv.region(), Start, (nframes64_t) (where * speed));
2048                         }
2049                 }
2050         }
2051 }
2052
2053 void
2054 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
2055 {
2056         char buf[128];
2057         Timecode::Time timecode;
2058         BBT_Time bbt;
2059         int hours, mins;
2060         nframes64_t frame_rate;
2061         float secs;
2062
2063         if (_session == 0) {
2064                 return;
2065         }
2066
2067         AudioClock::Mode m;
2068
2069         if (Profile->get_sae() || Profile->get_small_screen()) {
2070                 m = ARDOUR_UI::instance()->primary_clock.mode();
2071         } else {
2072                 m = ARDOUR_UI::instance()->secondary_clock.mode();
2073         }
2074
2075         switch (m) {
2076         case AudioClock::BBT:
2077                 _session->bbt_time (frame, bbt);
2078                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
2079                 break;
2080
2081         case AudioClock::Timecode:
2082                 _session->timecode_time (frame, timecode);
2083                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2084                 break;
2085
2086         case AudioClock::MinSec:
2087                 /* XXX this is copied from show_verbose_duration_cursor() */
2088                 frame_rate = _session->frame_rate();
2089                 hours = frame / (frame_rate * 3600);
2090                 frame = frame % (frame_rate * 3600);
2091                 mins = frame / (frame_rate * 60);
2092                 frame = frame % (frame_rate * 60);
2093                 secs = (float) frame / (float) frame_rate;
2094                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%07.4f", hours, mins, secs);
2095                 break;
2096
2097         default:
2098                 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
2099                 break;
2100         }
2101
2102         if (xpos >= 0 && ypos >=0) {
2103                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2104         } else {
2105                 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset - horizontal_position(), _drags->current_pointer_y() + offset - vertical_adjustment.get_value() + canvas_timebars_vsize);
2106         }
2107         show_verbose_canvas_cursor ();
2108 }
2109
2110 void
2111 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
2112 {
2113         char buf[128];
2114         Timecode::Time timecode;
2115         BBT_Time sbbt;
2116         BBT_Time ebbt;
2117         int hours, mins;
2118         nframes64_t distance, frame_rate;
2119         float secs;
2120         Meter meter_at_start(_session->tempo_map().meter_at(start));
2121
2122         if (_session == 0) {
2123                 return;
2124         }
2125
2126         AudioClock::Mode m;
2127
2128         if (Profile->get_sae() || Profile->get_small_screen()) {
2129                 m = ARDOUR_UI::instance()->primary_clock.mode ();
2130         } else {
2131                 m = ARDOUR_UI::instance()->secondary_clock.mode ();
2132         }
2133
2134         switch (m) {
2135         case AudioClock::BBT:
2136                 _session->bbt_time (start, sbbt);
2137                 _session->bbt_time (end, ebbt);
2138
2139                 /* subtract */
2140                 /* XXX this computation won't work well if the
2141                 user makes a selection that spans any meter changes.
2142                 */
2143
2144                 ebbt.bars -= sbbt.bars;
2145                 if (ebbt.beats >= sbbt.beats) {
2146                         ebbt.beats -= sbbt.beats;
2147                 } else {
2148                         ebbt.bars--;
2149                         ebbt.beats =  int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
2150                 }
2151                 if (ebbt.ticks >= sbbt.ticks) {
2152                         ebbt.ticks -= sbbt.ticks;
2153                 } else {
2154                         ebbt.beats--;
2155                         ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2156                 }
2157
2158                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2159                 break;
2160
2161         case AudioClock::Timecode:
2162                 _session->timecode_duration (end - start, timecode);
2163                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2164                 break;
2165
2166         case AudioClock::MinSec:
2167                 /* XXX this stuff should be elsewhere.. */
2168                 distance = end - start;
2169                 frame_rate = _session->frame_rate();
2170                 hours = distance / (frame_rate * 3600);
2171                 distance = distance % (frame_rate * 3600);
2172                 mins = distance / (frame_rate * 60);
2173                 distance = distance % (frame_rate * 60);
2174                 secs = (float) distance / (float) frame_rate;
2175                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%07.4f", hours, mins, secs);
2176                 break;
2177
2178         default:
2179                 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2180                 break;
2181         }
2182
2183         if (xpos >= 0 && ypos >=0) {
2184                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2185         }
2186         else {
2187                 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset, _drags->current_pointer_y() + offset);
2188         }
2189
2190         show_verbose_canvas_cursor ();
2191 }
2192
2193 void
2194 Editor::collect_new_region_view (RegionView* rv)
2195 {
2196         latest_regionviews.push_back (rv);
2197 }
2198
2199 void
2200 Editor::collect_and_select_new_region_view (RegionView* rv)
2201 {
2202         selection->add(rv);
2203         latest_regionviews.push_back (rv);
2204 }
2205
2206 void
2207 Editor::cancel_selection ()
2208 {
2209         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2210                 (*i)->hide_selection ();
2211         }
2212
2213         selection->clear ();
2214         clicked_selection = 0;
2215 }
2216
2217
2218 void
2219 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction)
2220 {
2221         boost::shared_ptr<Region> region (rv.region());
2222
2223         if (region->locked()) {
2224                 return;
2225         }
2226
2227         nframes64_t new_bound;
2228
2229         double speed = 1.0;
2230         TimeAxisView* tvp = clicked_axisview;
2231         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2232
2233         if (tv && tv->is_track()) {
2234                 speed = tv->track()->speed();
2235         }
2236
2237         if (left_direction) {
2238                 if (swap_direction) {
2239                         new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2240                 } else {
2241                         new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2242                 }
2243         } else {
2244                 if (swap_direction) {
2245                         new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2246                 } else {
2247                         new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2248                 }
2249         }
2250
2251         region->trim_start ((nframes64_t) (new_bound * speed), this);
2252         rv.region_changed (PropertyChange (ARDOUR::Properties::start));
2253 }
2254
2255 void
2256 Editor::single_start_trim (RegionView& rv, nframes64_t new_bound, bool no_overlap)
2257 {
2258         boost::shared_ptr<Region> region (rv.region());
2259
2260         if (region->locked()) {
2261                 return;
2262         }
2263
2264         double speed = 1.0;
2265         TimeAxisView* tvp = clicked_axisview;
2266         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2267
2268         if (tv && tv->is_track()) {
2269                 speed = tv->track()->speed();
2270         }
2271
2272         nframes64_t pre_trim_first_frame = region->first_frame();
2273
2274         region->trim_front ((nframes64_t) (new_bound * speed), this);
2275
2276         if (no_overlap) {
2277                 //Get the next region on the left of this region and shrink/expand it.
2278                 boost::shared_ptr<Playlist> playlist (region->playlist());
2279                 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
2280
2281                 bool regions_touching = false;
2282
2283                 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){
2284                     regions_touching = true;
2285                 }
2286
2287                 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2288                 if (region_left != 0 &&
2289                         (region_left->last_frame() > region->first_frame() || regions_touching))
2290                 {
2291                         region_left->trim_end(region->first_frame() - 1, this);
2292                 }
2293         }
2294
2295         rv.region_changed (ARDOUR::bounds_change);
2296 }
2297
2298 void
2299 Editor::single_end_trim (RegionView& rv, nframes64_t new_bound, bool no_overlap)
2300 {
2301         boost::shared_ptr<Region> region (rv.region());
2302
2303         if (region->locked()) {
2304                 return;
2305         }
2306
2307         double speed = 1.0;
2308         TimeAxisView* tvp = clicked_axisview;
2309         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2310
2311         if (tv && tv->is_track()) {
2312                 speed = tv->track()->speed();
2313         }
2314
2315         nframes64_t pre_trim_last_frame = region->last_frame();
2316
2317         region->trim_end ((nframes64_t) (new_bound * speed), this);
2318
2319         if (no_overlap) {
2320                 //Get the next region on the right of this region and shrink/expand it.
2321                 boost::shared_ptr<Playlist> playlist (region->playlist());
2322                 boost::shared_ptr<Region> region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1);
2323
2324                 bool regions_touching = false;
2325
2326                 if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)) {
2327                         regions_touching = true;
2328                 }
2329
2330                 //Only trim region on the right if the last frame has gone beyond the right region's first frame.
2331                 if (region_right != 0 &&
2332                         (region_right->first_frame() < region->last_frame() || regions_touching))
2333                 {
2334                         region_right->trim_front(region->last_frame() + 1, this);
2335                 }
2336
2337                 rv.region_changed (ARDOUR::bounds_change);
2338                         
2339         } else {
2340                 rv.region_changed (PropertyChange (ARDOUR::Properties::length));
2341         }
2342 }
2343
2344
2345 void
2346 Editor::point_trim (GdkEvent* event, nframes64_t new_bound)
2347 {
2348         RegionView* rv = clicked_regionview;
2349
2350         /* Choose action dependant on which button was pressed */
2351         switch (event->button.button) {
2352         case 1:
2353                 begin_reversible_command (_("Start point trim"));
2354
2355                 if (selection->selected (rv)) {
2356                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2357                              i != selection->regions.by_layer().end(); ++i)
2358                         {
2359                                 if ( (*i) == NULL){
2360                                     cerr << "region view contains null region" << endl;
2361                                 }
2362
2363                                 if (!(*i)->region()->locked()) {
2364                                         (*i)->region()->clear_history ();
2365                                         (*i)->region()->trim_front (new_bound, this);
2366                                         _session->add_command(new StatefulDiffCommand ((*i)->region()));
2367                                 }
2368                         }
2369
2370                 } else {
2371                         if (!rv->region()->locked()) {
2372                                 rv->region()->clear_history ();
2373                                 rv->region()->trim_front (new_bound, this);
2374                                 _session->add_command(new StatefulDiffCommand (rv->region()));
2375                         }
2376                 }
2377
2378                 commit_reversible_command();
2379
2380                 break;
2381         case 2:
2382                 begin_reversible_command (_("End point trim"));
2383
2384                 if (selection->selected (rv)) {
2385
2386                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2387                         {
2388                                 if (!(*i)->region()->locked()) {
2389                                         (*i)->region()->clear_history();
2390                                         (*i)->region()->trim_end (new_bound, this);
2391                                         _session->add_command(new StatefulDiffCommand ((*i)->region()));
2392                                 }
2393                         }
2394
2395                 } else {
2396
2397                         if (!rv->region()->locked()) {
2398                                 rv->region()->clear_history ();
2399                                 rv->region()->trim_end (new_bound, this);
2400                                 _session->add_command (new StatefulDiffCommand (rv->region()));
2401                         }
2402                 }
2403
2404                 commit_reversible_command();
2405
2406                 break;
2407         default:
2408                 break;
2409         }
2410 }
2411
2412 void
2413 Editor::thaw_region_after_trim (RegionView& rv)
2414 {
2415         boost::shared_ptr<Region> region (rv.region());
2416
2417         if (region->locked()) {
2418                 return;
2419         }
2420
2421         region->resume_property_changes ();
2422
2423         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
2424
2425         if (arv) {
2426                 arv->unhide_envelope ();
2427         }
2428 }
2429
2430 void
2431 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2432 {
2433         Marker* marker;
2434         bool is_start;
2435
2436         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2437                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2438                 /*NOTREACHED*/
2439         }
2440
2441         Location* location = find_location_from_marker (marker, is_start);
2442         location->set_hidden (true, this);
2443 }
2444
2445
2446 void
2447 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
2448 {
2449         double x1 = frame_to_pixel (start);
2450         double x2 = frame_to_pixel (end);
2451         double y2 = full_canvas_height - 1.0;
2452
2453         zoom_rect->property_x1() = x1;
2454         zoom_rect->property_y1() = 1.0;
2455         zoom_rect->property_x2() = x2;
2456         zoom_rect->property_y2() = y2;
2457 }
2458
2459
2460 gint
2461 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2462 {
2463         using namespace Gtkmm2ext;
2464
2465         ArdourPrompter prompter (false);
2466
2467         prompter.set_prompt (_("Name for region:"));
2468         prompter.set_initial_text (clicked_regionview->region()->name());
2469         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2470         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2471         prompter.show_all ();
2472         switch (prompter.run ()) {
2473         case Gtk::RESPONSE_ACCEPT:
2474                 string str;
2475                 prompter.get_result(str);
2476                 if (str.length()) {
2477                         clicked_regionview->region()->set_name (str);
2478                 }
2479                 break;
2480         }
2481         return true;
2482 }
2483
2484
2485 void
2486 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
2487 {
2488         /* no brushing without a useful snap setting */
2489
2490         switch (_snap_mode) {
2491         case SnapMagnetic:
2492                 return; /* can't work because it allows region to be placed anywhere */
2493         default:
2494                 break; /* OK */
2495         }
2496
2497         switch (_snap_type) {
2498         case SnapToMark:
2499                 return;
2500
2501         default:
2502                 break;
2503         }
2504
2505         /* don't brush a copy over the original */
2506
2507         if (pos == rv->region()->position()) {
2508                 return;
2509         }
2510
2511         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2512
2513         if (rtv == 0 || !rtv->is_track()) {
2514                 return;
2515         }
2516
2517         boost::shared_ptr<Playlist> playlist = rtv->playlist();
2518         double speed = rtv->track()->speed();
2519
2520         playlist->clear_history ();
2521         boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
2522         playlist->add_region (new_region, (nframes64_t) (pos * speed));
2523         _session->add_command (new StatefulDiffCommand (playlist));
2524
2525         // playlist is frozen, so we have to update manually XXX this is disgusting
2526
2527         playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2528 }
2529
2530 gint
2531 Editor::track_height_step_timeout ()
2532 {
2533         if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2534                 current_stepping_trackview = 0;
2535                 return false;
2536         }
2537         return true;
2538 }
2539
2540 void
2541 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2542 {
2543         assert (region_view);
2544
2545         _region_motion_group->raise_to_top ();
2546
2547         if (Config->get_edit_mode() == Splice) {
2548                 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2549         } else {
2550                 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2551                 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2552         }
2553
2554         /* sync the canvas to what we think is its current state */
2555         update_canvas_now();
2556 }
2557
2558 void
2559 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2560 {
2561         assert (region_view);
2562
2563         _region_motion_group->raise_to_top ();
2564
2565         RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2566         _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2567 }
2568
2569 void
2570 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2571 {
2572         assert (region_view);
2573
2574         if (Config->get_edit_mode() == Splice) {
2575                 return;
2576         }
2577
2578         RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2579         _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2580
2581         begin_reversible_command (_("Drag region brush"));
2582 }
2583
2584 /** Start a grab where a time range is selected, track(s) are selected, and the
2585  *  user clicks and drags a region with a modifier in order to create a new region containing
2586  *  the section of the clicked region that lies within the time range.
2587  */
2588 void
2589 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2590 {
2591         if (clicked_regionview == 0) {
2592                 return;
2593         }
2594
2595         /* lets try to create new Region for the selection */
2596
2597         vector<boost::shared_ptr<Region> > new_regions;
2598         create_region_from_selection (new_regions);
2599
2600         if (new_regions.empty()) {
2601                 return;
2602         }
2603
2604         /* XXX fix me one day to use all new regions */
2605
2606         boost::shared_ptr<Region> region (new_regions.front());
2607
2608         /* add it to the current stream/playlist.
2609
2610            tricky: the streamview for the track will add a new regionview. we will
2611            catch the signal it sends when it creates the regionview to
2612            set the regionview we want to then drag.
2613         */
2614
2615         latest_regionviews.clear();
2616         sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2617
2618         /* A selection grab currently creates two undo/redo operations, one for
2619            creating the new region and another for moving it.
2620         */
2621
2622         begin_reversible_command (_("selection grab"));
2623
2624         boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2625
2626         playlist->clear_history ();
2627         clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2628         _session->add_command(new StatefulDiffCommand (playlist));
2629
2630         commit_reversible_command ();
2631
2632         c.disconnect ();
2633
2634         if (latest_regionviews.empty()) {
2635                 /* something went wrong */
2636                 return;
2637         }
2638
2639         /* we need to deselect all other regionviews, and select this one
2640            i'm ignoring undo stuff, because the region creation will take care of it
2641         */
2642         selection->set (latest_regionviews);
2643
2644         _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2645 }
2646
2647 void
2648 Editor::escape ()
2649 {
2650         if (_drags->active ()) {
2651                 _drags->abort ();
2652         } else {
2653                 selection->clear ();
2654         }
2655 }
2656
2657 void
2658 Editor::set_internal_edit (bool yn)
2659 {
2660         _internal_editing = yn;
2661
2662         if (yn) {
2663                 mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2664                 mouse_select_button.get_image ()->show ();
2665                 ARDOUR_UI::instance()->tooltips().set_tip (mouse_select_button, _("Draw/Edit MIDI Notes"));
2666                 set_canvas_cursor ();
2667
2668                 /* deselect everything to avoid confusion when e.g. we can't now cut a previously selected
2669                    region because cut means "cut note" rather than "cut region".
2670                 */
2671                 selection->clear ();
2672
2673         } else {
2674
2675                 mouse_select_button.set_image (*(manage (new Image (::get_icon("tool_range")))));
2676                 mouse_select_button.get_image ()->show ();
2677                 ARDOUR_UI::instance()->tooltips().set_tip (mouse_select_button, _("Select/Move Ranges"));
2678                 mouse_mode_toggled (mouse_mode); // sets cursor
2679         }
2680 }
2681
2682 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2683  *  used by the `join object/range' tool mode.
2684  */
2685 void
2686 Editor::update_join_object_range_location (double x, double y)
2687 {
2688         /* XXX: actually, this decides based on whether the mouse is in the top or bottom half of a RouteTimeAxisView;
2689            entered_{track,regionview} is not always setup (e.g. if the mouse is over a TimeSelection), and to get a Region
2690            that we're over requires searching the playlist.
2691         */
2692            
2693         if (join_object_range_button.get_active() == false || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2694                 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2695                 return;
2696         }
2697         
2698         if (mouse_mode == MouseObject) {
2699                 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2700         } else if (mouse_mode == MouseRange) {
2701                 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2702         }
2703
2704         /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2705         pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2706         
2707         if (tvp.first) {
2708
2709                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2710                 if (rtv) {
2711
2712                         double cx = 0;
2713                         double cy = y;
2714                         rtv->canvas_display()->w2i (cx, cy);
2715
2716                         bool const top_half = cy < rtv->current_height () / 2;
2717                         
2718                         _join_object_range_state = top_half ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2719                 }
2720         }
2721 }
2722
2723 Editing::MouseMode
2724 Editor::effective_mouse_mode () const
2725 {
2726         if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2727                 return MouseObject;
2728         } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2729                 return MouseRange;
2730         }
2731
2732         return mouse_mode;
2733 }
2734
2735 void
2736 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2737 {
2738         ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2739         assert (e);
2740
2741         e->region_view().delete_note (e->note ());
2742 }