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