Create the session range location as and when the session first gets some content...
[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                                 /* Note: we don't get here if not in internal_editing() mode */
673                                 _drags->set (new NoteDrag (this, item), event);
674                                 return true;
675                         }
676                         break;
677                         
678                 default:
679                         break;
680                 }
681
682                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
683                     event->type == GDK_BUTTON_PRESS) {
684
685                         _drags->set (new RubberbandSelectDrag (this, item), event);
686
687                 } else if (event->type == GDK_BUTTON_PRESS) {
688
689                         switch (item_type) {
690                         case FadeInHandleItem:
691                         {
692                                 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
693                                 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event);
694                                 return true;
695                         }
696
697                         case FadeOutHandleItem:
698                         {
699                                 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
700                                 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event);
701                                 return true;
702                         }
703
704                         case RegionItem:
705                                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
706                                         add_region_copy_drag (item, event, clicked_regionview);
707                                 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
708                                         add_region_brush_drag (item, event, clicked_regionview);
709                                 } else {
710                                         add_region_drag (item, event, clicked_regionview);
711                                 }
712
713                                 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
714                                         _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
715                                 }
716
717                                 _drags->start_grab (event);
718                                 break;
719
720                         case RegionViewNameHighlight:
721                         {
722                                 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
723                                 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
724                                 return true;
725                                 break;
726                         }
727
728                         case RegionViewName:
729                         {
730                                 /* rename happens on edit clicks */
731                                 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
732                                 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
733                                 return true;
734                                 break;
735                         }
736
737                         case ControlPointItem:
738                                 _drags->set (new ControlPointDrag (this, item), event);
739                                 return true;
740                                 break;
741
742                         case AutomationLineItem:
743                                 _drags->set (new LineDrag (this, item), event);
744                                 return true;
745                                 break;
746
747                         case StreamItem:
748                                 if (internal_editing()) {
749                                         _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
750                                         return true;
751                                 } else {
752                                         _drags->set (new RubberbandSelectDrag (this, item), event);
753                                 }
754                                 break;
755                                 
756                         case AutomationTrackItem:
757                                 /* rubberband drag to select automation points */
758                                 _drags->set (new RubberbandSelectDrag (this, item), event);
759                                 break;
760
761                         case SelectionItem:
762                         {
763                                 if (join_object_range_button.get_active()) {
764                                         /* we're in "smart" joined mode, and we've clicked on a Selection */
765                                         double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
766                                         pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
767                                         if (tvp.first) {
768                                                 /* if we're over an automation track, start a drag of its data */
769                                                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
770                                                 if (atv) {
771                                                         _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event);
772                                                 }
773
774                                                 /* if we're over a track and a region, and in the `object' part of a region,
775                                                    put a selection around the region and drag both
776                                                 */
777                                                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
778                                                 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
779                                                         boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
780                                                         if (t) {
781                                                                 boost::shared_ptr<Playlist> pl = t->playlist ();
782                                                                 if (pl) {
783                                                                         
784                                                                         boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
785                                                                         if (r) {
786                                                                                 RegionView* rv = rtv->view()->find_view (r);
787                                                                                 clicked_selection = select_range_around_region (rv);
788                                                                                 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
789                                                                                 list<RegionView*> rvs;
790                                                                                 rvs.push_back (rv);
791                                                                                 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
792                                                                                 _drags->start_grab (event);
793                                                                         }
794                                                                 }
795                                                         }
796                                                 }
797                                         }
798                                 }
799                                 break;
800                         }
801
802 #ifdef WITH_CMT
803                         case ImageFrameHandleStartItem:
804                                 imageframe_start_handle_op(item, event) ;
805                                 return(true) ;
806                                 break ;
807                         case ImageFrameHandleEndItem:
808                                 imageframe_end_handle_op(item, event) ;
809                                 return(true) ;
810                                 break ;
811                         case MarkerViewHandleStartItem:
812                                 markerview_item_start_handle_op(item, event) ;
813                                 return(true) ;
814                                 break ;
815                         case MarkerViewHandleEndItem:
816                                 markerview_item_end_handle_op(item, event) ;
817                                 return(true) ;
818                                 break ;
819                         case MarkerViewItem:
820                                 start_markerview_grab(item, event) ;
821                                 break ;
822                         case ImageFrameItem:
823                                 start_imageframe_grab(item, event) ;
824                                 break ;
825 #endif
826
827                         case MarkerBarItem:
828
829                                 break;
830
831                         default:
832                                 break;
833                         }
834                 }
835                 return true;
836                 break;
837
838         case MouseGain:
839                 switch (item_type) {
840                 case RegionItem:
841                         /* start a grab so that if we finish after moving
842                            we can tell what happened.
843                         */
844                         _drags->set (new RegionGainDrag (this, item), event, current_canvas_cursor);
845                         break;
846
847                 case GainLineItem:
848                         _drags->set (new LineDrag (this, item), event);
849                         return true;
850
851                 case ControlPointItem:
852                         _drags->set (new ControlPointDrag (this, item), event);
853                         return true;
854                         break;
855
856                 default:
857                         break;
858                 }
859                 return true;
860                 break;
861
862                 switch (item_type) {
863                 case ControlPointItem:
864                         _drags->set (new ControlPointDrag (this, item), event);
865                         break;
866
867                 case AutomationLineItem:
868                         _drags->set (new LineDrag (this, item), event);
869                         break;
870
871                 case RegionItem:
872                         // XXX need automation mode to identify which
873                         // line to use
874                         // start_line_grab_from_regionview (item, event);
875                         break;
876
877                 default:
878                         break;
879                 }
880                 return true;
881                 break;
882
883         case MouseZoom:
884                 if (event->type == GDK_BUTTON_PRESS) {
885                         _drags->set (new MouseZoomDrag (this, item), event);
886                 }
887
888                 return true;
889                 break;
890
891         case MouseTimeFX:
892                 if (internal_editing() && item_type == NoteItem) {
893                         /* drag notes if we're in internal edit mode */
894                         _drags->set (new NoteResizeDrag (this, item), event);
895                         return true;
896                 } else if (!internal_editing() || dynamic_cast<AudioRegionView*> (clicked_regionview)) {
897                         /* do time-FX if we're not in internal edit mode, or we are but we clicked on an audio region */
898                         _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
899                         return true;
900                 }
901                 break;
902
903         case MouseAudition:
904                 _drags->set (new ScrubDrag (this, item), event);
905                 scrub_reversals = 0;
906                 scrub_reverse_distance = 0;
907                 last_scrub_x = event->button.x;
908                 scrubbing_direction = 0;
909                 track_canvas->get_window()->set_cursor (*transparent_cursor);
910                 return true;
911                 break;
912
913         default:
914                 break;
915         }
916
917         return false;
918 }
919
920 bool
921 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
922 {
923         Editing::MouseMode const eff = effective_mouse_mode ();
924         switch (eff) {
925         case MouseObject:
926                 switch (item_type) {
927                 case RegionItem:
928                         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
929                                 add_region_copy_drag (item, event, clicked_regionview);
930                         } else {
931                                 add_region_drag (item, event, clicked_regionview);
932                         }
933                         _drags->start_grab (event);
934                         return true;
935                         break;
936                 case ControlPointItem:
937                         _drags->set (new ControlPointDrag (this, item), event);
938                         return true;
939                         break;
940
941                 default:
942                         break;
943                 }
944
945                 switch (item_type) {
946                 case RegionViewNameHighlight:
947                         _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
948                         return true;
949                         break;
950
951                 case RegionViewName:
952                         _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
953                         return true;
954                         break;
955
956                 default:
957                         break;
958                 }
959
960                 break;
961
962         case MouseRange:
963                 /* relax till release */
964                 return true;
965                 break;
966
967
968         case MouseZoom:
969                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
970                         temporal_zoom_session();
971                 } else {
972                         temporal_zoom_to_frame (true, event_frame(event));
973                 }
974                 return true;
975                 break;
976
977         default:
978                 break;
979         }
980
981         return false;
982 }
983
984 bool
985 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
986 {
987         if (event->type != GDK_BUTTON_PRESS) {
988                 return false;
989         }
990
991         Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
992
993         if (canvas_window) {
994                 Glib::RefPtr<const Gdk::Window> pointer_window;
995                 int x, y;
996                 double wx, wy;
997                 Gdk::ModifierType mask;
998
999                 pointer_window = canvas_window->get_pointer (x, y, mask);
1000
1001                 if (pointer_window == track_canvas->get_bin_window()) {
1002                         track_canvas->window_to_world (x, y, wx, wy);
1003                         allow_vertical_scroll = true;
1004                 } else {
1005                         allow_vertical_scroll = false;
1006                 }
1007         }
1008
1009         track_canvas->grab_focus();
1010
1011         if (_session && _session->actively_recording()) {
1012                 return true;
1013         }
1014
1015         button_selection (item, event, item_type);
1016
1017         if (!_drags->active () &&
1018             (Keyboard::is_delete_event (&event->button) ||
1019              Keyboard::is_context_menu_event (&event->button) ||
1020              Keyboard::is_edit_event (&event->button))) {
1021
1022                 /* handled by button release */
1023                 return true;
1024         }
1025
1026         switch (event->button.button) {
1027         case 1:
1028                 return button_press_handler_1 (item, event, item_type);
1029                 break;
1030
1031         case 2:
1032                 return button_press_handler_2 (item, event, item_type);
1033                 break;
1034
1035         case 3:
1036                 break;
1037
1038         default:
1039                 break;
1040
1041         }
1042
1043         return false;
1044 }
1045
1046 bool
1047 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1048 {
1049         nframes64_t where = event_frame (event, 0, 0);
1050         AutomationTimeAxisView* atv = 0;
1051
1052         /* no action if we're recording */
1053
1054         if (_session && _session->actively_recording()) {
1055                 return true;
1056         }
1057
1058         /* first, see if we're finishing a drag ... */
1059
1060         bool were_dragging = false;
1061         if (_drags->active ()) {
1062                 bool const r = _drags->end_grab (event);
1063                 if (r) {
1064                         /* grab dragged, so do nothing else */
1065                         return true;
1066                 }
1067
1068                 were_dragging = true;
1069         }
1070
1071         button_selection (item, event, item_type);
1072
1073         /* edit events get handled here */
1074
1075         if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1076                 switch (item_type) {
1077                 case RegionItem:
1078                         edit_region ();
1079                         break;
1080
1081                 case TempoMarkerItem:
1082                         edit_tempo_marker (item);
1083                         break;
1084
1085                 case MeterMarkerItem:
1086                         edit_meter_marker (item);
1087                         break;
1088
1089                 case RegionViewName:
1090                         if (clicked_regionview->name_active()) {
1091                                 return mouse_rename_region (item, event);
1092                         }
1093                         break;
1094
1095                 case ControlPointItem:
1096                         edit_control_point (item);
1097                         break;
1098
1099                 default:
1100                         break;
1101                 }
1102                 return true;
1103         }
1104
1105         /* context menu events get handled here */
1106
1107         if (Keyboard::is_context_menu_event (&event->button)) {
1108
1109                 if (!_drags->active ()) {
1110
1111                         /* no matter which button pops up the context menu, tell the menu
1112                            widget to use button 1 to drive menu selection.
1113                         */
1114
1115                         switch (item_type) {
1116                         case FadeInItem:
1117                         case FadeInHandleItem:
1118                         case FadeOutItem:
1119                         case FadeOutHandleItem:
1120                                 popup_fade_context_menu (1, event->button.time, item, item_type);
1121                                 break;
1122
1123                         case StreamItem:
1124                                 popup_track_context_menu (1, event->button.time, item_type, false, where);
1125                                 break;
1126
1127                         case RegionItem:
1128                         case RegionViewNameHighlight:
1129                         case RegionViewName:
1130                                 popup_track_context_menu (1, event->button.time, item_type, false, where);
1131                                 break;
1132
1133                         case SelectionItem:
1134                                 popup_track_context_menu (1, event->button.time, item_type, true, where);
1135                                 break;
1136
1137                         case AutomationTrackItem:
1138                                 popup_track_context_menu (1, event->button.time, item_type, false, where);
1139                                 break;
1140
1141                         case MarkerBarItem:
1142                         case RangeMarkerBarItem:
1143                         case TransportMarkerBarItem:
1144                         case CdMarkerBarItem:
1145                         case TempoBarItem:
1146                         case MeterBarItem:
1147                                 popup_ruler_menu (where, item_type);
1148                                 break;
1149
1150                         case MarkerItem:
1151                                 marker_context_menu (&event->button, item);
1152                                 break;
1153
1154                         case TempoMarkerItem:
1155                                 tempo_or_meter_marker_context_menu (&event->button, item);
1156                                 break;
1157
1158                         case MeterMarkerItem:
1159                                 tempo_or_meter_marker_context_menu (&event->button, item);
1160                                 break;
1161
1162                         case CrossfadeViewItem:
1163                                 popup_track_context_menu (1, event->button.time, item_type, false, where);
1164                                 break;
1165
1166 #ifdef WITH_CMT
1167                         case ImageFrameItem:
1168                                 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1169                                 break ;
1170                         case ImageFrameTimeAxisItem:
1171                                 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1172                                 break ;
1173                         case MarkerViewItem:
1174                                 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1175                                 break ;
1176                         case MarkerTimeAxisItem:
1177                                 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1178                                 break ;
1179 #endif
1180
1181                         default:
1182                                 break;
1183                         }
1184
1185                         return true;
1186                 }
1187         }
1188
1189         /* delete events get handled here */
1190
1191         Editing::MouseMode const eff = effective_mouse_mode ();
1192
1193         if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1194
1195                 switch (item_type) {
1196                 case TempoMarkerItem:
1197                         remove_tempo_marker (item);
1198                         break;
1199
1200                 case MeterMarkerItem:
1201                         remove_meter_marker (item);
1202                         break;
1203
1204                 case MarkerItem:
1205                         remove_marker (*item, event);
1206                         break;
1207
1208                 case RegionItem:
1209                         if (eff == MouseObject) {
1210                                 remove_clicked_region ();
1211                         }
1212                         break;
1213
1214                 case ControlPointItem:
1215                         if (eff == MouseGain) {
1216                                 remove_gain_control_point (item, event);
1217                         } else {
1218                                 remove_control_point (item, event);
1219                         }
1220                         break;
1221
1222                 default:
1223                         break;
1224                 }
1225                 return true;
1226         }
1227
1228         switch (event->button.button) {
1229         case 1:
1230
1231                 switch (item_type) {
1232                 /* see comments in button_press_handler */
1233                 case PlayheadCursorItem:
1234                 case MarkerItem:
1235                 case GainLineItem:
1236                 case AutomationLineItem:
1237                 case StartSelectionTrimItem:
1238                 case EndSelectionTrimItem:
1239                         return true;
1240
1241                 case MarkerBarItem:
1242                         if (!_dragging_playhead) {
1243                                 snap_to_with_modifier (where, event, 0, true);
1244                                 mouse_add_new_marker (where);
1245                         }
1246                         return true;
1247
1248                 case CdMarkerBarItem:
1249                         if (!_dragging_playhead) {
1250                                 // if we get here then a dragged range wasn't done
1251                                 snap_to_with_modifier (where, event, 0, true);
1252                                 mouse_add_new_marker (where, true);
1253                         }
1254                         return true;
1255
1256                 case TempoBarItem:
1257                         if (!_dragging_playhead) {
1258                                 snap_to_with_modifier (where, event);
1259                                 mouse_add_new_tempo_event (where);
1260                         }
1261                         return true;
1262
1263                 case MeterBarItem:
1264                         if (!_dragging_playhead) {
1265                                 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1266                         }
1267                         return true;
1268                         break;
1269
1270                 default:
1271                         break;
1272                 }
1273
1274                 switch (eff) {
1275                 case MouseObject:
1276                         switch (item_type) {
1277                         case AutomationTrackItem:
1278                                 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1279                                 if (atv) {
1280                                         atv->add_automation_event (item, event, where, event->button.y);
1281                                 }
1282                                 return true;
1283                                 break;
1284
1285                         default:
1286                                 break;
1287                         }
1288                         break;
1289
1290                 case MouseGain:
1291                         // Gain only makes sense for audio regions
1292
1293                         if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1294                                 break;
1295                         }
1296
1297                         switch (item_type) {
1298                         case RegionItem:
1299                                 /* check that we didn't drag before releasing, since
1300                                    its really annoying to create new control
1301                                    points when doing this.
1302                                 */
1303                                 if (were_dragging) {
1304                                         dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1305                                 }
1306                                 return true;
1307                                 break;
1308
1309                         case AutomationTrackItem:
1310                                 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1311                                         add_automation_event (item, event, where, event->button.y);
1312                                 return true;
1313                                 break;
1314                         default:
1315                                 break;
1316                         }
1317                         break;
1318
1319                 case MouseAudition:
1320                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1321                         if (scrubbing_direction == 0) {
1322                                 /* no drag, just a click */
1323                                 switch (item_type) {
1324                                 case RegionItem:
1325                                         play_selected_region ();
1326                                         break;
1327                                 default:
1328                                         break;
1329                                 }
1330                         } else {
1331                                 /* make sure we stop */
1332                                 _session->request_transport_speed (0.0);
1333                         }
1334                         break;
1335
1336                 default:
1337                         break;
1338
1339                 }
1340
1341                 return true;
1342                 break;
1343
1344
1345         case 2:
1346                 switch (eff) {
1347
1348                 case MouseObject:
1349                         switch (item_type) {
1350                         case RegionItem:
1351                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1352                                         raise_region ();
1353                                 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1354                                         lower_region ();
1355                                 } else {
1356                                         // Button2 click is unused
1357                                 }
1358                                 return true;
1359
1360                                 break;
1361
1362                         default:
1363                                 break;
1364                         }
1365                         break;
1366
1367                 case MouseRange:
1368
1369                         // x_style_paste (where, 1.0);
1370                         return true;
1371                         break;
1372
1373                 default:
1374                         break;
1375                 }
1376
1377                 break;
1378
1379         case 3:
1380                 break;
1381
1382         default:
1383                 break;
1384         }
1385         return false;
1386 }
1387
1388 bool
1389 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1390 {
1391         ControlPoint* cp;
1392         Marker * marker;
1393         double fraction;
1394
1395         if (last_item_entered != item) {
1396                 last_item_entered = item;
1397                 last_item_entered_n = 0;
1398         }
1399
1400         switch (item_type) {
1401         case ControlPointItem:
1402                 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1403                         cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1404                         cp->set_visible (true);
1405
1406                         double at_x, at_y;
1407                         at_x = cp->get_x();
1408                         at_y = cp->get_y ();
1409                         cp->i2w (at_x, at_y);
1410                         at_x += 10.0;
1411                         at_y += 10.0;
1412
1413                         fraction = 1.0 - (cp->get_y() / cp->line().height());
1414
1415                         if (is_drawable() && !_drags->active ()) {
1416                                 track_canvas->get_window()->set_cursor (*fader_cursor);
1417                         }
1418
1419                         last_item_entered_n++;
1420                         set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1421                         if (last_item_entered_n < 10) {
1422                                 show_verbose_canvas_cursor ();
1423                         }
1424                 }
1425                 break;
1426
1427         case GainLineItem:
1428                 if (mouse_mode == MouseGain) {
1429                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1430                         if (line)
1431                                 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1432                         if (is_drawable()) {
1433                                 track_canvas->get_window()->set_cursor (*fader_cursor);
1434                         }
1435                 }
1436                 break;
1437
1438         case AutomationLineItem:
1439                 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1440                         {
1441                                 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1442                                 if (line)
1443                                         line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1444                         }
1445                         if (is_drawable()) {
1446                                 track_canvas->get_window()->set_cursor (*fader_cursor);
1447                         }
1448                 }
1449                 break;
1450
1451         case RegionViewNameHighlight:
1452                 if (is_drawable() && mouse_mode == MouseObject) {
1453                         track_canvas->get_window()->set_cursor (*trimmer_cursor);
1454                 }
1455                 break;
1456
1457         case StartSelectionTrimItem:
1458         case EndSelectionTrimItem:
1459
1460 #ifdef WITH_CMT
1461         case ImageFrameHandleStartItem:
1462         case ImageFrameHandleEndItem:
1463         case MarkerViewHandleStartItem:
1464         case MarkerViewHandleEndItem:
1465 #endif
1466
1467                 if (is_drawable()) {
1468                         track_canvas->get_window()->set_cursor (*trimmer_cursor);
1469                 }
1470                 break;
1471
1472         case PlayheadCursorItem:
1473                 if (is_drawable()) {
1474                         switch (_edit_point) {
1475                         case EditAtMouse:
1476                                 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1477                                 break;
1478                         default:
1479                                 track_canvas->get_window()->set_cursor (*grabber_cursor);
1480                                 break;
1481                         }
1482                 }
1483                 break;
1484
1485         case RegionViewName:
1486
1487                 /* when the name is not an active item, the entire name highlight is for trimming */
1488
1489                 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1490                         if (mouse_mode == MouseObject && is_drawable()) {
1491                                 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1492                         }
1493                 }
1494                 break;
1495
1496
1497         case AutomationTrackItem:
1498                 if (is_drawable()) {
1499                         Gdk::Cursor *cursor;
1500                         switch (mouse_mode) {
1501                         case MouseRange:
1502                                 cursor = selector_cursor;
1503                                 break;
1504                         case MouseZoom:
1505                                 cursor = zoom_cursor;
1506                                 break;
1507                         default:
1508                                 cursor = cross_hair_cursor;
1509                                 break;
1510                         }
1511
1512                         track_canvas->get_window()->set_cursor (*cursor);
1513
1514                         AutomationTimeAxisView* atv;
1515                         if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1516                                 clear_entered_track = false;
1517                                 set_entered_track (atv);
1518                         }
1519                 }
1520                 break;
1521
1522         case MarkerBarItem:
1523         case RangeMarkerBarItem:
1524         case TransportMarkerBarItem:
1525         case CdMarkerBarItem:
1526         case MeterBarItem:
1527         case TempoBarItem:
1528                 if (is_drawable()) {
1529                         track_canvas->get_window()->set_cursor (*timebar_cursor);
1530                 }
1531                 break;
1532
1533         case MarkerItem:
1534                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1535                         break;
1536                 }
1537                 entered_marker = marker;
1538                 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1539                 // fall through
1540         case MeterMarkerItem:
1541         case TempoMarkerItem:
1542                 if (is_drawable()) {
1543                         track_canvas->get_window()->set_cursor (*timebar_cursor);
1544                 }
1545                 break;
1546         case FadeInHandleItem:
1547         case FadeOutHandleItem:
1548                 if (mouse_mode == MouseObject) {
1549                         ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1550                         if (rect) {
1551                                 rect->property_fill_color_rgba() = 0;
1552                                 rect->property_outline_pixels() = 1;
1553                         }
1554                         track_canvas->get_window()->set_cursor (*grabber_cursor);
1555                 }
1556                 break;
1557
1558         default:
1559                 break;
1560         }
1561
1562         /* second pass to handle entered track status in a comprehensible way.
1563          */
1564
1565         switch (item_type) {
1566         case GainLineItem:
1567         case AutomationLineItem:
1568         case ControlPointItem:
1569                 /* these do not affect the current entered track state */
1570                 clear_entered_track = false;
1571                 break;
1572
1573         case AutomationTrackItem:
1574                 /* handled above already */
1575                 break;
1576
1577         default:
1578                 set_entered_track (0);
1579                 break;
1580         }
1581
1582         return false;
1583 }
1584
1585 bool
1586 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1587 {
1588         AutomationLine* al;
1589         ControlPoint* cp;
1590         Marker *marker;
1591         Location *loc;
1592         RegionView* rv;
1593         bool is_start;
1594
1595         switch (item_type) {
1596         case ControlPointItem:
1597                 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1598                 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1599                         if (cp->line().npoints() > 1 && !cp->selected()) {
1600                                 cp->set_visible (false);
1601                         }
1602                 }
1603
1604                 if (is_drawable()) {
1605                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1606                 }
1607
1608                 hide_verbose_canvas_cursor ();
1609                 break;
1610
1611         case RegionViewNameHighlight:
1612         case StartSelectionTrimItem:
1613         case EndSelectionTrimItem:
1614         case PlayheadCursorItem:
1615
1616 #ifdef WITH_CMT
1617         case ImageFrameHandleStartItem:
1618         case ImageFrameHandleEndItem:
1619         case MarkerViewHandleStartItem:
1620         case MarkerViewHandleEndItem:
1621 #endif
1622
1623                 if (is_drawable()) {
1624                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1625                 }
1626                 break;
1627
1628         case GainLineItem:
1629         case AutomationLineItem:
1630                 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1631                 {
1632                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1633                         if (line)
1634                                 line->property_fill_color_rgba() = al->get_line_color();
1635                 }
1636                 if (is_drawable()) {
1637                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1638                 }
1639                 break;
1640
1641         case RegionViewName:
1642                 /* see enter_handler() for notes */
1643                 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1644                         if (is_drawable() && mouse_mode == MouseObject) {
1645                                 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1646                         }
1647                 }
1648                 break;
1649
1650         case RangeMarkerBarItem:
1651         case TransportMarkerBarItem:
1652         case CdMarkerBarItem:
1653         case MeterBarItem:
1654         case TempoBarItem:
1655         case MarkerBarItem:
1656                 if (is_drawable()) {
1657                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1658                 }
1659                 break;
1660
1661         case MarkerItem:
1662                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1663                         break;
1664                 }
1665                 entered_marker = 0;
1666                 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1667                         location_flags_changed (loc, this);
1668                 }
1669                 // fall through
1670         case MeterMarkerItem:
1671         case TempoMarkerItem:
1672
1673                 if (is_drawable()) {
1674                         track_canvas->get_window()->set_cursor (*timebar_cursor);
1675                 }
1676
1677                 break;
1678
1679         case FadeInHandleItem:
1680         case FadeOutHandleItem:
1681                 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1682                 {
1683                         ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1684                         if (rect) {
1685                                 rect->property_fill_color_rgba() = rv->get_fill_color();
1686                                 rect->property_outline_pixels() = 0;
1687                         }
1688                 }
1689                 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1690                 break;
1691
1692         case AutomationTrackItem:
1693                 if (is_drawable()) {
1694                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1695                         clear_entered_track = true;
1696                         Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
1697                 }
1698                 break;
1699
1700         default:
1701                 break;
1702         }
1703
1704         return false;
1705 }
1706
1707 gint
1708 Editor::left_automation_track ()
1709 {
1710         if (clear_entered_track) {
1711                 set_entered_track (0);
1712                 clear_entered_track = false;
1713         }
1714         return false;
1715 }
1716
1717 void
1718 Editor::scrub (nframes64_t frame, double current_x)
1719 {
1720         double delta;
1721
1722         if (scrubbing_direction == 0) {
1723                 /* first move */
1724                 _session->request_locate (frame, false);
1725                 _session->request_transport_speed (0.1);
1726                 scrubbing_direction = 1;
1727
1728         } else {
1729
1730                 if (last_scrub_x > current_x) {
1731
1732                         /* pointer moved to the left */
1733
1734                         if (scrubbing_direction > 0) {
1735
1736                                 /* we reversed direction to go backwards */
1737
1738                                 scrub_reversals++;
1739                                 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1740
1741                         } else {
1742
1743                                 /* still moving to the left (backwards) */
1744
1745                                 scrub_reversals = 0;
1746                                 scrub_reverse_distance = 0;
1747
1748                                 delta = 0.01 * (last_scrub_x - current_x);
1749                                 _session->request_transport_speed (_session->transport_speed() - delta);
1750                         }
1751
1752                 } else {
1753                         /* pointer moved to the right */
1754
1755                         if (scrubbing_direction < 0) {
1756                                 /* we reversed direction to go forward */
1757
1758                                 scrub_reversals++;
1759                                 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1760
1761                         } else {
1762                                 /* still moving to the right */
1763
1764                                 scrub_reversals = 0;
1765                                 scrub_reverse_distance = 0;
1766
1767                                 delta = 0.01 * (current_x - last_scrub_x);
1768                                 _session->request_transport_speed (_session->transport_speed() + delta);
1769                         }
1770                 }
1771
1772                 /* if there have been more than 2 opposite motion moves detected, or one that moves
1773                    back more than 10 pixels, reverse direction
1774                 */
1775
1776                 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1777
1778                         if (scrubbing_direction > 0) {
1779                                 /* was forwards, go backwards */
1780                                 _session->request_transport_speed (-0.1);
1781                                 scrubbing_direction = -1;
1782                         } else {
1783                                 /* was backwards, go forwards */
1784                                 _session->request_transport_speed (0.1);
1785                                 scrubbing_direction = 1;
1786                         }
1787
1788                         scrub_reverse_distance = 0;
1789                         scrub_reversals = 0;
1790                 }
1791         }
1792
1793         last_scrub_x = current_x;
1794 }
1795
1796 bool
1797 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1798 {
1799         if (event->motion.is_hint) {
1800                 gint x, y;
1801
1802                 /* We call this so that MOTION_NOTIFY events continue to be
1803                    delivered to the canvas. We need to do this because we set
1804                    Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1805                    the density of the events, at the expense of a round-trip
1806                    to the server. Given that this will mostly occur on cases
1807                    where DISPLAY = :0.0, and given the cost of what the motion
1808                    event might do, its a good tradeoff.
1809                 */
1810
1811                 track_canvas->get_pointer (x, y);
1812         }
1813
1814         if (current_stepping_trackview) {
1815                 /* don't keep the persistent stepped trackview if the mouse moves */
1816                 current_stepping_trackview = 0;
1817                 step_timeout.disconnect ();
1818         }
1819
1820         if (_session && _session->actively_recording()) {
1821                 /* Sorry. no dragging stuff around while we record */
1822                 return true;
1823         }
1824
1825         JoinObjectRangeState const old = _join_object_range_state;
1826         update_join_object_range_location (event->motion.x, event->motion.y);
1827         if (_join_object_range_state != old) {
1828                 set_canvas_cursor ();
1829         }
1830
1831         bool handled = false;
1832         if (_drags->active ()) {
1833                 handled = _drags->motion_handler (event, from_autoscroll);
1834         }
1835
1836         if (!handled) {
1837                 return false;
1838         }
1839
1840         track_canvas_motion (event);
1841         return true;
1842 }
1843
1844 void
1845 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
1846 {
1847         ControlPoint* control_point;
1848
1849         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1850                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1851                 /*NOTREACHED*/
1852         }
1853
1854         // We shouldn't remove the first or last gain point
1855         if (control_point->line().is_last_point(*control_point) ||
1856                 control_point->line().is_first_point(*control_point)) {
1857                 return;
1858         }
1859
1860         control_point->line().remove_point (*control_point);
1861 }
1862
1863 void
1864 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/)
1865 {
1866         ControlPoint* control_point;
1867
1868         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1869                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1870                 /*NOTREACHED*/
1871         }
1872
1873         control_point->line().remove_point (*control_point);
1874 }
1875
1876 void
1877 Editor::edit_control_point (ArdourCanvas::Item* item)
1878 {
1879         ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1880
1881         if (p == 0) {
1882                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1883                 /*NOTREACHED*/
1884         }
1885
1886         ControlPointDialog d (p);
1887         d.set_position (Gtk::WIN_POS_MOUSE);
1888         ensure_float (d);
1889
1890         if (d.run () != RESPONSE_ACCEPT) {
1891                 return;
1892         }
1893
1894         p->line().modify_point_y (*p, d.get_y_fraction ());
1895 }
1896
1897
1898 void
1899 Editor::visible_order_range (int* low, int* high) const
1900 {
1901         *low = TimeAxisView::max_order ();
1902         *high = 0;
1903
1904         for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1905
1906                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1907
1908                 if (!rtv->hidden()) {
1909
1910                         if (*high < rtv->order()) {
1911                                 *high = rtv->order ();
1912                         }
1913
1914                         if (*low > rtv->order()) {
1915                                 *low = rtv->order ();
1916                         }
1917                 }
1918         }
1919 }
1920
1921 void
1922 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1923 {
1924         /* Either add to or set the set the region selection, unless
1925            this is an alignment click (control used)
1926         */
1927
1928         if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1929                 TimeAxisView* tv = &rv.get_time_axis_view();
1930                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1931                 double speed = 1.0;
1932                 if (rtv && rtv->is_track()) {
1933                         speed = rtv->track()->speed();
1934                 }
1935
1936                 nframes64_t where = get_preferred_edit_position();
1937
1938                 if (where >= 0) {
1939
1940                         if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
1941
1942                                 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
1943
1944                         } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
1945
1946                                 align_region (rv.region(), End, (nframes64_t) (where * speed));
1947
1948                         } else {
1949
1950                                 align_region (rv.region(), Start, (nframes64_t) (where * speed));
1951                         }
1952                 }
1953         }
1954 }
1955
1956 void
1957 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
1958 {
1959         char buf[128];
1960         Timecode::Time timecode;
1961         BBT_Time bbt;
1962         int hours, mins;
1963         nframes64_t frame_rate;
1964         float secs;
1965
1966         if (_session == 0) {
1967                 return;
1968         }
1969
1970         AudioClock::Mode m;
1971
1972         if (Profile->get_sae() || Profile->get_small_screen()) {
1973                 m = ARDOUR_UI::instance()->primary_clock.mode();
1974         } else {
1975                 m = ARDOUR_UI::instance()->secondary_clock.mode();
1976         }
1977
1978         switch (m) {
1979         case AudioClock::BBT:
1980                 _session->bbt_time (frame, bbt);
1981                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
1982                 break;
1983
1984         case AudioClock::Timecode:
1985                 _session->timecode_time (frame, timecode);
1986                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
1987                 break;
1988
1989         case AudioClock::MinSec:
1990                 /* XXX this is copied from show_verbose_duration_cursor() */
1991                 frame_rate = _session->frame_rate();
1992                 hours = frame / (frame_rate * 3600);
1993                 frame = frame % (frame_rate * 3600);
1994                 mins = frame / (frame_rate * 60);
1995                 frame = frame % (frame_rate * 60);
1996                 secs = (float) frame / (float) frame_rate;
1997                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
1998                 break;
1999
2000         default:
2001                 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
2002                 break;
2003         }
2004
2005         if (xpos >= 0 && ypos >=0) {
2006                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2007         } else {
2008                 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset - horizontal_position(), _drags->current_pointer_y() + offset - vertical_adjustment.get_value() + canvas_timebars_vsize);
2009         }
2010         show_verbose_canvas_cursor ();
2011 }
2012
2013 void
2014 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
2015 {
2016         char buf[128];
2017         Timecode::Time timecode;
2018         BBT_Time sbbt;
2019         BBT_Time ebbt;
2020         int hours, mins;
2021         nframes64_t distance, frame_rate;
2022         float secs;
2023         Meter meter_at_start(_session->tempo_map().meter_at(start));
2024
2025         if (_session == 0) {
2026                 return;
2027         }
2028
2029         AudioClock::Mode m;
2030
2031         if (Profile->get_sae() || Profile->get_small_screen()) {
2032                 m = ARDOUR_UI::instance()->primary_clock.mode ();
2033         } else {
2034                 m = ARDOUR_UI::instance()->secondary_clock.mode ();
2035         }
2036
2037         switch (m) {
2038         case AudioClock::BBT:
2039                 _session->bbt_time (start, sbbt);
2040                 _session->bbt_time (end, ebbt);
2041
2042                 /* subtract */
2043                 /* XXX this computation won't work well if the
2044                 user makes a selection that spans any meter changes.
2045                 */
2046
2047                 ebbt.bars -= sbbt.bars;
2048                 if (ebbt.beats >= sbbt.beats) {
2049                         ebbt.beats -= sbbt.beats;
2050                 } else {
2051                         ebbt.bars--;
2052                         ebbt.beats =  int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
2053                 }
2054                 if (ebbt.ticks >= sbbt.ticks) {
2055                         ebbt.ticks -= sbbt.ticks;
2056                 } else {
2057                         ebbt.beats--;
2058                         ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2059                 }
2060
2061                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2062                 break;
2063
2064         case AudioClock::Timecode:
2065                 _session->timecode_duration (end - start, timecode);
2066                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2067                 break;
2068
2069         case AudioClock::MinSec:
2070                 /* XXX this stuff should be elsewhere.. */
2071                 distance = end - start;
2072                 frame_rate = _session->frame_rate();
2073                 hours = distance / (frame_rate * 3600);
2074                 distance = distance % (frame_rate * 3600);
2075                 mins = distance / (frame_rate * 60);
2076                 distance = distance % (frame_rate * 60);
2077                 secs = (float) distance / (float) frame_rate;
2078                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
2079                 break;
2080
2081         default:
2082                 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2083                 break;
2084         }
2085
2086         if (xpos >= 0 && ypos >=0) {
2087                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2088         }
2089         else {
2090                 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset, _drags->current_pointer_y() + offset);
2091         }
2092
2093         show_verbose_canvas_cursor ();
2094 }
2095
2096 void
2097 Editor::collect_new_region_view (RegionView* rv)
2098 {
2099         latest_regionviews.push_back (rv);
2100 }
2101
2102 void
2103 Editor::collect_and_select_new_region_view (RegionView* rv)
2104 {
2105         selection->add(rv);
2106         latest_regionviews.push_back (rv);
2107 }
2108
2109 void
2110 Editor::cancel_selection ()
2111 {
2112         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2113                 (*i)->hide_selection ();
2114         }
2115
2116         selection->clear ();
2117         clicked_selection = 0;
2118 }
2119
2120
2121 void
2122 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction)
2123 {
2124         boost::shared_ptr<Region> region (rv.region());
2125
2126         if (region->locked()) {
2127                 return;
2128         }
2129
2130         nframes64_t new_bound;
2131
2132         double speed = 1.0;
2133         TimeAxisView* tvp = clicked_axisview;
2134         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2135
2136         if (tv && tv->is_track()) {
2137                 speed = tv->track()->speed();
2138         }
2139
2140         if (left_direction) {
2141                 if (swap_direction) {
2142                         new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2143                 } else {
2144                         new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2145                 }
2146         } else {
2147                 if (swap_direction) {
2148                         new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2149                 } else {
2150                         new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2151                 }
2152         }
2153
2154         region->trim_start ((nframes64_t) (new_bound * speed), this);
2155         rv.region_changed (PropertyChange (ARDOUR::Properties::start));
2156 }
2157
2158 void
2159 Editor::single_start_trim (RegionView& rv, nframes64_t new_bound, bool no_overlap)
2160 {
2161         boost::shared_ptr<Region> region (rv.region());
2162
2163         if (region->locked()) {
2164                 return;
2165         }
2166
2167         double speed = 1.0;
2168         TimeAxisView* tvp = clicked_axisview;
2169         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2170
2171         if (tv && tv->is_track()) {
2172                 speed = tv->track()->speed();
2173         }
2174
2175         nframes64_t pre_trim_first_frame = region->first_frame();
2176
2177         region->trim_front ((nframes64_t) (new_bound * speed), this);
2178
2179         if (no_overlap) {
2180                 //Get the next region on the left of this region and shrink/expand it.
2181                 boost::shared_ptr<Playlist> playlist (region->playlist());
2182                 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
2183
2184                 bool regions_touching = false;
2185
2186                 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){
2187                     regions_touching = true;
2188                 }
2189
2190                 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2191                 if (region_left != 0 &&
2192                         (region_left->last_frame() > region->first_frame() || regions_touching))
2193                 {
2194                         region_left->trim_end(region->first_frame() - 1, this);
2195                 }
2196         }
2197
2198         rv.region_changed (ARDOUR::bounds_change);
2199 }
2200
2201 void
2202 Editor::single_end_trim (RegionView& rv, nframes64_t new_bound, bool no_overlap)
2203 {
2204         boost::shared_ptr<Region> region (rv.region());
2205
2206         if (region->locked()) {
2207                 return;
2208         }
2209
2210         double speed = 1.0;
2211         TimeAxisView* tvp = clicked_axisview;
2212         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2213
2214         if (tv && tv->is_track()) {
2215                 speed = tv->track()->speed();
2216         }
2217
2218         nframes64_t pre_trim_last_frame = region->last_frame();
2219
2220         region->trim_end ((nframes64_t) (new_bound * speed), this);
2221
2222         if (no_overlap) {
2223                 //Get the next region on the right of this region and shrink/expand it.
2224                 boost::shared_ptr<Playlist> playlist (region->playlist());
2225                 boost::shared_ptr<Region> region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1);
2226
2227                 bool regions_touching = false;
2228
2229                 if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)) {
2230                         regions_touching = true;
2231                 }
2232
2233                 //Only trim region on the right if the last frame has gone beyond the right region's first frame.
2234                 if (region_right != 0 &&
2235                         (region_right->first_frame() < region->last_frame() || regions_touching))
2236                 {
2237                         region_right->trim_front(region->last_frame() + 1, this);
2238                 }
2239
2240                 rv.region_changed (ARDOUR::bounds_change);
2241                         
2242         } else {
2243                 rv.region_changed (PropertyChange (ARDOUR::Properties::length));
2244         }
2245 }
2246
2247
2248 void
2249 Editor::point_trim (GdkEvent* event, nframes64_t new_bound)
2250 {
2251         RegionView* rv = clicked_regionview;
2252
2253         /* Choose action dependant on which button was pressed */
2254         switch (event->button.button) {
2255         case 1:
2256                 begin_reversible_command (_("Start point trim"));
2257
2258                 if (selection->selected (rv)) {
2259                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2260                              i != selection->regions.by_layer().end(); ++i)
2261                         {
2262                                 if ( (*i) == NULL){
2263                                     cerr << "region view contains null region" << endl;
2264                                 }
2265
2266                                 if (!(*i)->region()->locked()) {
2267                                         (*i)->region()->clear_history ();
2268                                         (*i)->region()->trim_front (new_bound, this);
2269                                         _session->add_command(new StatefulDiffCommand ((*i)->region()));
2270                                 }
2271                         }
2272
2273                 } else {
2274                         if (!rv->region()->locked()) {
2275                                 rv->region()->clear_history ();
2276                                 rv->region()->trim_front (new_bound, this);
2277                                 _session->add_command(new StatefulDiffCommand (rv->region()));
2278                         }
2279                 }
2280
2281                 commit_reversible_command();
2282
2283                 break;
2284         case 2:
2285                 begin_reversible_command (_("End point trim"));
2286
2287                 if (selection->selected (rv)) {
2288
2289                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2290                         {
2291                                 if (!(*i)->region()->locked()) {
2292                                         (*i)->region()->clear_history();
2293                                         (*i)->region()->trim_end (new_bound, this);
2294                                         _session->add_command(new StatefulDiffCommand ((*i)->region()));
2295                                 }
2296                         }
2297
2298                 } else {
2299
2300                         if (!rv->region()->locked()) {
2301                                 rv->region()->clear_history ();
2302                                 rv->region()->trim_end (new_bound, this);
2303                                 _session->add_command (new StatefulDiffCommand (rv->region()));
2304                         }
2305                 }
2306
2307                 commit_reversible_command();
2308
2309                 break;
2310         default:
2311                 break;
2312         }
2313 }
2314
2315 void
2316 Editor::thaw_region_after_trim (RegionView& rv)
2317 {
2318         boost::shared_ptr<Region> region (rv.region());
2319
2320         if (region->locked()) {
2321                 return;
2322         }
2323
2324         region->resume_property_changes ();
2325
2326         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
2327
2328         if (arv) {
2329                 arv->unhide_envelope ();
2330         }
2331 }
2332
2333 void
2334 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2335 {
2336         Marker* marker;
2337         bool is_start;
2338
2339         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2340                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2341                 /*NOTREACHED*/
2342         }
2343
2344         Location* location = find_location_from_marker (marker, is_start);
2345         location->set_hidden (true, this);
2346 }
2347
2348
2349 void
2350 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
2351 {
2352         double x1 = frame_to_pixel (start);
2353         double x2 = frame_to_pixel (end);
2354         double y2 = full_canvas_height - 1.0;
2355
2356         zoom_rect->property_x1() = x1;
2357         zoom_rect->property_y1() = 1.0;
2358         zoom_rect->property_x2() = x2;
2359         zoom_rect->property_y2() = y2;
2360 }
2361
2362
2363 gint
2364 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2365 {
2366         using namespace Gtkmm2ext;
2367
2368         ArdourPrompter prompter (false);
2369
2370         prompter.set_prompt (_("Name for region:"));
2371         prompter.set_initial_text (clicked_regionview->region()->name());
2372         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2373         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2374         prompter.show_all ();
2375         switch (prompter.run ()) {
2376         case Gtk::RESPONSE_ACCEPT:
2377                 string str;
2378                 prompter.get_result(str);
2379                 if (str.length()) {
2380                         clicked_regionview->region()->set_name (str);
2381                 }
2382                 break;
2383         }
2384         return true;
2385 }
2386
2387
2388 void
2389 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
2390 {
2391         /* no brushing without a useful snap setting */
2392
2393         switch (_snap_mode) {
2394         case SnapMagnetic:
2395                 return; /* can't work because it allows region to be placed anywhere */
2396         default:
2397                 break; /* OK */
2398         }
2399
2400         switch (_snap_type) {
2401         case SnapToMark:
2402                 return;
2403
2404         default:
2405                 break;
2406         }
2407
2408         /* don't brush a copy over the original */
2409
2410         if (pos == rv->region()->position()) {
2411                 return;
2412         }
2413
2414         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2415
2416         if (rtv == 0 || !rtv->is_track()) {
2417                 return;
2418         }
2419
2420         boost::shared_ptr<Playlist> playlist = rtv->playlist();
2421         double speed = rtv->track()->speed();
2422
2423         playlist->clear_history ();
2424         boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
2425         playlist->add_region (new_region, (nframes64_t) (pos * speed));
2426         _session->add_command (new StatefulDiffCommand (playlist));
2427
2428         // playlist is frozen, so we have to update manually XXX this is disgusting
2429
2430         playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2431 }
2432
2433 gint
2434 Editor::track_height_step_timeout ()
2435 {
2436         if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2437                 current_stepping_trackview = 0;
2438                 return false;
2439         }
2440         return true;
2441 }
2442
2443 void
2444 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2445 {
2446         assert (region_view);
2447
2448         _region_motion_group->raise_to_top ();
2449
2450         if (Config->get_edit_mode() == Splice) {
2451                 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2452         } else {
2453                 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2454                 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2455         }
2456
2457         /* sync the canvas to what we think is its current state */
2458         update_canvas_now();
2459 }
2460
2461 void
2462 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2463 {
2464         assert (region_view);
2465
2466         _region_motion_group->raise_to_top ();
2467
2468         RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2469         _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2470 }
2471
2472 void
2473 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2474 {
2475         assert (region_view);
2476
2477         if (Config->get_edit_mode() == Splice) {
2478                 return;
2479         }
2480
2481         RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2482         _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2483
2484         begin_reversible_command (_("Drag region brush"));
2485 }
2486
2487 /** Start a grab where a time range is selected, track(s) are selected, and the
2488  *  user clicks and drags a region with a modifier in order to create a new region containing
2489  *  the section of the clicked region that lies within the time range.
2490  */
2491 void
2492 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2493 {
2494         if (clicked_regionview == 0) {
2495                 return;
2496         }
2497
2498         /* lets try to create new Region for the selection */
2499
2500         vector<boost::shared_ptr<Region> > new_regions;
2501         create_region_from_selection (new_regions);
2502
2503         if (new_regions.empty()) {
2504                 return;
2505         }
2506
2507         /* XXX fix me one day to use all new regions */
2508
2509         boost::shared_ptr<Region> region (new_regions.front());
2510
2511         /* add it to the current stream/playlist.
2512
2513            tricky: the streamview for the track will add a new regionview. we will
2514            catch the signal it sends when it creates the regionview to
2515            set the regionview we want to then drag.
2516         */
2517
2518         latest_regionviews.clear();
2519         sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2520
2521         /* A selection grab currently creates two undo/redo operations, one for
2522            creating the new region and another for moving it.
2523         */
2524
2525         begin_reversible_command (_("selection grab"));
2526
2527         boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2528
2529         playlist->clear_history ();
2530         clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2531         _session->add_command(new StatefulDiffCommand (playlist));
2532
2533         commit_reversible_command ();
2534
2535         c.disconnect ();
2536
2537         if (latest_regionviews.empty()) {
2538                 /* something went wrong */
2539                 return;
2540         }
2541
2542         /* we need to deselect all other regionviews, and select this one
2543            i'm ignoring undo stuff, because the region creation will take care of it
2544         */
2545         selection->set (latest_regionviews);
2546
2547         _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2548 }
2549
2550 void
2551 Editor::escape ()
2552 {
2553         if (_drags->active ()) {
2554                 _drags->abort ();
2555         } else {
2556                 selection->clear ();
2557         }
2558 }
2559
2560 void
2561 Editor::set_internal_edit (bool yn)
2562 {
2563         _internal_editing = yn;
2564
2565         if (yn) {
2566                 mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2567                 mouse_select_button.get_image ()->show ();
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                 start_step_editing ();
2576
2577         } else {
2578
2579                 mouse_select_button.set_image (*(manage (new Image (::get_icon("tool_range")))));
2580                 mouse_select_button.get_image ()->show ();
2581                 stop_step_editing ();
2582
2583                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2584                         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
2585                         if (mtv) {
2586                                 mtv->stop_step_editing ();
2587                         }
2588                 }
2589         }
2590
2591         set_canvas_cursor ();
2592
2593
2594 }
2595
2596 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2597  *  used by the `join object/range' tool mode.
2598  */
2599 void
2600 Editor::update_join_object_range_location (double x, double y)
2601 {
2602         /* XXX: actually, this decides based on whether the mouse is in the top or bottom half of a RouteTimeAxisView;
2603            entered_{track,regionview} is not always setup (e.g. if the mouse is over a TimeSelection), and to get a Region
2604            that we're over requires searching the playlist.
2605         */
2606            
2607         if (join_object_range_button.get_active() == false || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2608                 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2609                 return;
2610         }
2611         
2612         if (mouse_mode == MouseObject) {
2613                 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2614         } else if (mouse_mode == MouseRange) {
2615                 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2616         }
2617
2618         /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2619         pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2620         
2621         if (tvp.first) {
2622
2623                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2624                 if (rtv) {
2625
2626                         double cx = 0;
2627                         double cy = y;
2628                         rtv->canvas_display()->w2i (cx, cy);
2629
2630                         bool const top_half = cy < rtv->current_height () / 2;
2631                         
2632                         _join_object_range_state = top_half ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2633                 }
2634         }
2635 }
2636
2637 Editing::MouseMode
2638 Editor::effective_mouse_mode () const
2639 {
2640         if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2641                 return MouseObject;
2642         } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2643                 return MouseRange;
2644         }
2645
2646         return mouse_mode;
2647 }