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