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