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