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