disable follow-edits with external sync #6577
[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 #include <bitset>
28
29 #include "pbd/error.h"
30 #include "pbd/enumwriter.h"
31 #include "pbd/memento_command.h"
32 #include "pbd/basename.h"
33 #include "pbd/stateful_diff_command.h"
34
35 #include "gtkmm2ext/bindings.h"
36 #include "gtkmm2ext/utils.h"
37 #include "gtkmm2ext/tearoff.h"
38
39 #include "canvas/canvas.h"
40
41 #include "ardour/audioregion.h"
42 #include "ardour/operations.h"
43 #include "ardour/playlist.h"
44 #include "ardour/profile.h"
45 #include "ardour/region_factory.h"
46 #include "ardour/route.h"
47 #include "ardour/session.h"
48 #include "ardour/types.h"
49
50 #include "actions.h"
51 #include "editor.h"
52 #include "time_axis_view.h"
53 #include "audio_time_axis.h"
54 #include "audio_region_view.h"
55 #include "midi_region_view.h"
56 #include "marker.h"
57 #include "streamview.h"
58 #include "region_gain_line.h"
59 #include "automation_time_axis.h"
60 #include "control_point.h"
61 #include "prompter.h"
62 #include "selection.h"
63 #include "keyboard.h"
64 #include "editing.h"
65 #include "rgb_macros.h"
66 #include "control_point_dialog.h"
67 #include "editor_drag.h"
68 #include "automation_region_view.h"
69 #include "edit_note_dialog.h"
70 #include "mouse_cursors.h"
71 #include "editor_cursors.h"
72 #include "verbose_cursor.h"
73 #include "note.h"
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 (framepos_t& where, bool& in_track_canvas) const
86 {
87         /* gdk_window_get_pointer() has X11's XQueryPointer semantics in that it only
88            pays attentions to subwindows. this means that menu windows are ignored, and
89            if the pointer is in a menu, the return window from the call will be the
90            the regular subwindow *under* the menu.
91
92            this matters quite a lot if the pointer is moving around in a menu that overlaps
93            the track canvas because we will believe that we are within the track canvas
94            when we are not. therefore, we track enter/leave events for the track canvas
95            and allow that to override the result of gdk_window_get_pointer().
96         */
97
98         if (!within_track_canvas) {
99                 return false;
100         }
101
102         int x, y;
103         Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas->get_window();
104
105         if (!canvas_window) {
106                 return false;
107         }
108
109         Glib::RefPtr<const Gdk::Window> pointer_window = Gdk::Display::get_default()->get_window_at_pointer (x, y);
110
111         if (!pointer_window) {
112                 return false;
113         }
114
115         if (pointer_window != canvas_window) {
116                 in_track_canvas = false;
117                 return false;
118         }
119
120         in_track_canvas = true;
121
122         GdkEvent event;
123         event.type = GDK_BUTTON_RELEASE;
124         event.button.x = x;
125         event.button.y = y;
126
127         where = window_event_sample (&event, 0, 0);
128
129         return true;
130 }
131
132 framepos_t
133 Editor::window_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
134 {
135         ArdourCanvas::Duple d;
136
137         if (!gdk_event_get_coords (event, &d.x, &d.y)) {
138                 return 0;
139         }
140
141         /* event coordinates are in window units, so convert to canvas
142          */
143
144         d = _track_canvas->window_to_canvas (d);
145
146         if (pcx) {
147                 *pcx = d.x;
148         }
149
150         if (pcy) {
151                 *pcy = d.y;
152         }
153
154         return pixel_to_sample (d.x);
155 }
156
157 framepos_t
158 Editor::canvas_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
159 {
160         double x;
161         double y;
162
163         /* event coordinates are already in canvas units */
164
165         if (!gdk_event_get_coords (event, &x, &y)) {
166                 cerr << "!NO c COORDS for event type " << event->type << endl;
167                 return 0;
168         }
169
170         if (pcx) {
171                 *pcx = x;
172         }
173
174         if (pcy) {
175                 *pcy = y;
176         }
177
178         /* note that pixel_to_sample_from_event() never returns less than zero, so even if the pixel
179            position is negative (as can be the case with motion events in particular),
180            the frame location is always positive.
181         */
182
183         return pixel_to_sample_from_event (x);
184 }
185
186 void
187 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
188 {
189         boost::shared_ptr<Trimmable> st = _trimmable.lock();
190
191         if (!st || st == t) {
192                 _trimmable = t;
193         }
194 }
195
196 void
197 Editor::set_current_movable (boost::shared_ptr<Movable> m)
198 {
199         boost::shared_ptr<Movable> sm = _movable.lock();
200
201         if (!sm || sm != m) {
202                 _movable = m;
203         }
204 }
205
206 void
207 Editor::mouse_mode_object_range_toggled()
208 {
209         MouseMode m = mouse_mode;
210
211         Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
212         assert (act);
213         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
214         assert (tact);
215
216         set_mouse_mode(m, true);  //call this so the button styles can get updated
217 }
218
219 static Glib::RefPtr<Action>
220 get_mouse_mode_action(MouseMode m)
221 {
222         switch (m) {
223         case MouseRange:
224                 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
225         case MouseObject:
226                 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
227         case MouseCut:
228                 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-cut"));
229         case MouseDraw:
230                 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
231         case MouseTimeFX:
232                 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
233         case MouseContent:
234                 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-content"));
235         case MouseAudition:
236                 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
237         }
238         return Glib::RefPtr<Action>();
239 }
240
241 void
242 Editor::set_mouse_mode (MouseMode m, bool force)
243 {
244         if (_drags->active ()) {
245                 return;
246         }
247
248         if (!force && m == mouse_mode) {
249                 return;
250         }
251
252         if (ARDOUR::Profile->get_mixbus()) {
253                 if ( m == MouseCut) m = MouseObject;
254         }
255
256         Glib::RefPtr<Action>       act  = get_mouse_mode_action(m);
257         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
258
259         /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
260         tact->set_active (false);
261         tact->set_active (true);
262
263         //NOTE:  this will result in a call to mouse_mode_toggled which does the heavy lifting
264 }
265
266 void
267 Editor::mouse_mode_toggled (MouseMode m)
268 {
269         if (ARDOUR::Profile->get_mixbus()) {
270                 if ( m == MouseCut)  m = MouseObject;
271         }
272
273         Glib::RefPtr<Action>       act  = get_mouse_mode_action(m);
274         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
275
276         if (!tact->get_active()) {
277                 /* this was just the notification that the old mode has been
278                  * left. we'll get called again with the new mode active in a
279                  * jiffy.
280                  */
281                 return;
282         }
283
284         if (_session && mouse_mode == MouseAudition) {
285                 /* stop transport and reset default speed to avoid oddness with
286                    auditioning */
287                 _session->request_transport_speed (0.0, true);
288         }
289
290         const bool was_internal = internal_editing();
291
292         mouse_mode = m;
293
294         /* Switch snap type/mode if we're moving to/from an internal tool.  Note
295            this must toggle the actions and not call set_snap_*() directly,
296            otherwise things get out of sync and the combo box stops working. */
297         if (!was_internal && internal_editing()) {
298                 snap_type_action(internal_snap_type)->set_active(true);
299                 snap_mode_action(internal_snap_mode)->set_active(true);
300         } else if (was_internal && !internal_editing()) {
301                 snap_type_action(pre_internal_snap_type)->set_active(true);
302                 snap_mode_action(pre_internal_snap_mode)->set_active(true);
303         }
304
305         instant_save ();
306
307         /* this should generate a new enter event which will
308            trigger the appropiate cursor.
309         */
310
311         if (_track_canvas) {
312                 _track_canvas->re_enter ();
313         }
314
315         set_gain_envelope_visibility ();
316
317         update_time_selection_display ();
318
319         update_all_enter_cursors ();
320
321         MouseModeChanged (); /* EMIT SIGNAL */
322 }
323
324 bool
325 Editor::internal_editing() const
326 {
327         return mouse_mode == Editing::MouseContent || mouse_mode == Editing::MouseDraw;
328 }
329
330 void
331 Editor::update_time_selection_display ()
332 {
333         switch (mouse_mode) {
334         case MouseRange:
335                 selection->clear_objects ();
336                 selection->ClearMidiNoteSelection ();  /* EMIT SIGNAL */
337                 break;
338         case MouseObject:
339                 selection->clear_time ();
340                 selection->clear_tracks ();
341                 selection->ClearMidiNoteSelection ();  /* EMIT SIGNAL */
342                 break;
343         case MouseDraw:
344                 /* Clear regions, but not time or tracks, since that
345                  would destroy the range selection rectangle, which we need to stick
346                  around for AutomationRangeDrag. */
347                 selection->clear_regions ();
348                 selection->clear_playlists ();
349                 break;
350         case MouseContent:
351                 /* This handles internal edit.
352                    Clear everything except points and notes.
353                 */
354                 selection->clear_regions();
355                 selection->clear_lines();
356                 selection->clear_playlists ();
357
358                 selection->clear_time ();
359                 selection->clear_tracks ();
360                 break;
361
362         case MouseTimeFX:
363                 /* We probably want to keep region selection */
364                 selection->clear_points ();
365                 selection->clear_lines();
366                 selection->clear_playlists ();
367
368                 selection->clear_time ();
369                 selection->clear_tracks ();
370                 break;
371
372         case MouseAudition:
373                 /*Don't lose lines or points if no action in this mode */
374                 selection->clear_regions ();
375                 selection->clear_playlists ();
376                 selection->clear_time ();
377                 selection->clear_tracks ();
378                 break;
379
380         default:
381                 /*Clear everything */
382                 selection->clear_objects();
383                 selection->clear_time ();
384                 selection->clear_tracks ();
385                 break;
386         }
387 }
388
389 void
390 Editor::step_mouse_mode (bool next)
391 {
392         const int n_mouse_modes = (int)MouseContent + 1;
393         int       current       = (int)current_mouse_mode();
394         if (next) {
395                 set_mouse_mode((MouseMode)((current + 1) % n_mouse_modes));
396         } else {
397                 set_mouse_mode((MouseMode)((current + n_mouse_modes - 1) % n_mouse_modes));
398         }
399 }
400
401 void
402 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
403 {
404         /* in object/audition/timefx/gain-automation mode,
405            any button press sets the selection if the object
406            can be selected. this is a bit of hack, because
407            we want to avoid this if the mouse operation is a
408            region alignment.
409
410            note: not dbl-click or triple-click
411
412            Also note that there is no region selection in internal edit mode, otherwise
413            for operations operating on the selection (e.g. cut) it is not obvious whether
414            to cut notes or regions.
415         */
416
417         MouseMode eff_mouse_mode = effective_mouse_mode ();
418
419         if (eff_mouse_mode == MouseCut) {
420                 /* never change selection in cut mode */
421                 return;
422         }
423
424         if (get_smart_mode() && eff_mouse_mode == MouseRange && event->button.button == 3 && item_type == RegionItem) {
425                 /* context clicks are always about object properties, even if
426                    we're in range mode within smart mode.
427                 */
428                 eff_mouse_mode = MouseObject;
429         }
430
431         /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
432         if (get_smart_mode()) {
433                 switch (item_type) {
434                   case FadeInHandleItem:
435                   case FadeInTrimHandleItem:
436                   case FadeOutHandleItem:
437                   case FadeOutTrimHandleItem:
438                           eff_mouse_mode = MouseObject;
439                           break;
440                 default:
441                         break;
442                 }
443         }
444
445         if (((mouse_mode != MouseObject) &&
446              (mouse_mode != MouseAudition || item_type != RegionItem) &&
447              (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
448              (mouse_mode != MouseDraw) &&
449              (mouse_mode != MouseContent || item_type == RegionItem)) ||
450             ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
451                 return;
452         }
453
454         if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
455
456                 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
457
458                         /* almost no selection action on modified button-2 or button-3 events */
459
460                         if (item_type != RegionItem && event->button.button != 2) {
461                                 return;
462                         }
463                 }
464         }
465
466         Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
467         bool press = (event->type == GDK_BUTTON_PRESS);
468
469         if (press) {
470                 _mouse_changed_selection = false;
471         }
472
473         switch (item_type) {
474         case RegionItem:
475                 if (press) {
476                         if (eff_mouse_mode != MouseRange) {
477                                 _mouse_changed_selection = set_selected_regionview_from_click (press, op);
478                         } else {
479                                 /* don't change the selection unless the
480                                    clicked track is not currently selected. if
481                                    so, "collapse" the selection to just this
482                                    track
483                                 */
484                                 if (!selection->selected (clicked_axisview)) {
485                                         set_selected_track_as_side_effect (Selection::Set);
486                                 }
487                         }
488                 } else {
489                         if (eff_mouse_mode != MouseRange) {
490                                 _mouse_changed_selection |= set_selected_regionview_from_click (press, op);
491                         }
492                 }
493                 break;
494
495         case RegionViewNameHighlight:
496         case RegionViewName:
497         case LeftFrameHandle:
498         case RightFrameHandle:
499         case FadeInHandleItem:
500         case FadeInTrimHandleItem:
501         case FadeInItem:
502         case FadeOutHandleItem:
503         case FadeOutTrimHandleItem:
504         case FadeOutItem:
505         case StartCrossFadeItem:
506         case EndCrossFadeItem:
507                 if (get_smart_mode() || eff_mouse_mode != MouseRange) {
508                         _mouse_changed_selection |= set_selected_regionview_from_click (press, op);
509                 } else if (event->type == GDK_BUTTON_PRESS) {
510                         set_selected_track_as_side_effect (op);
511                 }
512                 break;
513
514         case ControlPointItem:
515                 /* for object/track exclusivity, we don't call set_selected_track_as_side_effect (op); */
516
517                 if (eff_mouse_mode != MouseRange) {
518                         _mouse_changed_selection |= set_selected_control_point_from_click (press, op);
519                 }
520                 break;
521
522         case StreamItem:
523                 /* for context click, select track */
524                 if (event->button.button == 3) {
525                         selection->clear_tracks ();
526                         set_selected_track_as_side_effect (op);
527
528                         /* We won't get a release.*/
529                         begin_reversible_selection_op (X_("Button 3 Menu Select"));
530                         commit_reversible_selection_op ();
531                 }
532                 break;
533
534         case AutomationTrackItem:
535                 set_selected_track_as_side_effect (op);
536                 break;
537
538         default:
539                 break;
540         }
541
542         if ((!press) && _mouse_changed_selection) {
543                 begin_reversible_selection_op (X_("Button Selection"));
544                 commit_reversible_selection_op ();
545                 _mouse_changed_selection = false;
546         }
547 }
548
549 bool
550 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
551 {
552         /* single mouse clicks on any of these item types operate
553            independent of mouse mode, mostly because they are
554            not on the main track canvas or because we want
555            them to be modeless.
556         */
557
558         NoteBase* note = NULL;
559
560         switch (item_type) {
561         case PlayheadCursorItem:
562                 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
563                 return true;
564
565         case MarkerItem:
566                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
567                         hide_marker (item, event);
568                 } else {
569                         _drags->set (new MarkerDrag (this, item), event);
570                 }
571                 return true;
572
573         case TempoMarkerItem:
574         {
575                 _drags->set (
576                         new TempoMarkerDrag (
577                                 this,
578                                 item,
579                                 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
580                                 ),
581                         event
582                         );
583                 return true;
584         }
585
586         case MeterMarkerItem:
587         {
588                 _drags->set (
589                         new MeterMarkerDrag (
590                                 this,
591                                 item,
592                                 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
593                                 ),
594                         event
595                         );
596                 return true;
597         }
598
599         case VideoBarItem:
600                 _drags->set (new VideoTimeLineDrag (this, item), event);
601                 return true;
602                 break;
603
604         case MarkerBarItem:
605         case TempoBarItem:
606         case MeterBarItem:
607         case TimecodeRulerItem:
608         case SamplesRulerItem:
609         case MinsecRulerItem:
610         case BBTRulerItem:
611                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
612                         _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
613                 }
614                 return true;
615                 break;
616
617
618         case RangeMarkerBarItem:
619                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
620                         _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateSkipMarker), event);
621                 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
622                         _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
623                 } else {
624                         _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
625                 }
626                 return true;
627                 break;
628
629         case CdMarkerBarItem:
630                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
631                         _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
632                 } else {
633                         _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
634                 }
635                 return true;
636                 break;
637
638         case TransportMarkerBarItem:
639                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
640                         _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
641                 } else {
642                         _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
643                 }
644                 return true;
645                 break;
646
647         default:
648                 break;
649         }
650
651         if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
652                 /* special case: allow trim of range selections in joined object mode;
653                    in theory eff should equal MouseRange in this case, but it doesn't
654                    because entering the range selection canvas item results in entered_regionview
655                    being set to 0, so update_join_object_range_location acts as if we aren't
656                    over a region.
657                 */
658                 if (item_type == StartSelectionTrimItem) {
659                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
660                 } else if (item_type == EndSelectionTrimItem) {
661                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
662                 }
663         }
664
665         Editing::MouseMode eff = effective_mouse_mode ();
666
667         /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
668         if (get_smart_mode()) {
669                 switch (item_type) {
670                   case FadeInHandleItem:
671                   case FadeInTrimHandleItem:
672                   case FadeOutHandleItem:
673                   case FadeOutTrimHandleItem:
674                         eff = MouseObject;
675                         break;
676                 default:
677                         break;
678                 }
679         }
680
681         switch (eff) {
682         case MouseRange:
683                 switch (item_type) {
684                 case StartSelectionTrimItem:
685                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
686                         break;
687
688                 case EndSelectionTrimItem:
689                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
690                         break;
691
692                 case SelectionItem:
693                         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
694                                 start_selection_grab (item, event);
695                                 return true;
696                         } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
697                                 /* grab selection for moving */
698                                 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
699                         } else {
700                                 /* this was debated, but decided the more common action was to
701                                    make a new selection */
702                                 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
703                         }
704                         break;
705
706                 case StreamItem:
707                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
708                                 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
709                         } else {
710                                 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
711                         }
712                         return true;
713                         break;
714
715                 case RegionViewNameHighlight:
716                         if (!clicked_regionview->region()->locked()) {
717                                 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
718                                 return true;
719                         }
720                         break;
721
722                 default:
723                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
724                                 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
725                         } else {
726                                 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
727                         }
728                 }
729                 return true;
730                 break;
731
732         case MouseCut:
733                 switch (item_type) {
734                 case RegionItem:
735                 case FadeInHandleItem:
736                 case FadeOutHandleItem:
737                 case LeftFrameHandle:
738                 case RightFrameHandle:
739                 case FeatureLineItem:
740                 case RegionViewNameHighlight:
741                 case RegionViewName:
742                 case StreamItem:
743                 case AutomationTrackItem:
744                         _drags->set (new RegionCutDrag (this, item, canvas_event_sample (event)), event, get_canvas_cursor());
745                         return true;
746                         break;
747                 default:
748                         break;
749                 }
750                 break;
751
752         case MouseContent:
753                 switch (item_type) {
754                 case NoteItem:
755                         /* Existing note: allow trimming/motion */
756                         if ((note = reinterpret_cast<NoteBase*> (item->get_data ("notebase")))) {
757                                 if (note->big_enough_to_trim() && note->mouse_near_ends()) {
758                                         _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
759                                 } else {
760                                         _drags->set (new NoteDrag (this, item), event);
761                                 }
762                         }
763                         return true;
764
765                 case GainLineItem:
766                         _drags->set (new LineDrag (this, item), event);
767                         return true;
768                         break;
769
770                 case ControlPointItem:
771                         _drags->set (new ControlPointDrag (this, item), event);
772                         return true;
773                         break;
774
775                 case AutomationLineItem:
776                         _drags->set (new LineDrag (this, item), event);
777                         return true;
778                         break;
779
780                 case StreamItem:
781                         //in the past, we created a new midi region here, but perhaps that is best left to the Draw mode
782                         break;
783
784                 case AutomationTrackItem:
785                         /* rubberband drag to select automation points */
786                         _drags->set (new EditorRubberbandSelectDrag (this, item), event);
787                         return true;
788                         break;
789
790                 case RegionItem:
791                         if (dynamic_cast<AutomationRegionView*>(clicked_regionview)) {
792                                 /* rubberband drag to select automation points */
793                                 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
794                                 return true;
795                         }
796                         break;
797
798                 default:
799                         break;
800                 }
801                 break;
802
803         case MouseObject:
804                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
805                     event->type == GDK_BUTTON_PRESS) {
806
807                         _drags->set (new EditorRubberbandSelectDrag (this, item), event);
808
809                 } else if (event->type == GDK_BUTTON_PRESS) {
810
811                         switch (item_type) {
812                         case FadeInHandleItem:
813                         {
814                                 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
815                                 return true;
816                         }
817
818                         case FadeOutHandleItem:
819                         {
820                                 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
821                                 return true;
822                         }
823
824                         case StartCrossFadeItem:
825                         case EndCrossFadeItem:
826                                 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor.  for not this is not fully implemented */
827 //                              if (!clicked_regionview->region()->locked()) {
828 //                                      _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
829 //                                      return true;
830 //                              }
831                                 break;
832
833                         case FeatureLineItem:
834                         {
835                                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
836                                         remove_transient(item);
837                                         return true;
838                                 }
839
840                                 _drags->set (new FeatureLineDrag (this, item), event);
841                                 return true;
842                                 break;
843                         }
844
845                         case RegionItem:
846                                 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
847                                         /* click on an automation region view; do nothing here and let the ARV's signal handler
848                                            sort it out.
849                                         */
850                                         break;
851                                 }
852
853                                 /* click on a normal region view */
854                                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
855                                         add_region_copy_drag (item, event, clicked_regionview);
856                                 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
857                                         add_region_brush_drag (item, event, clicked_regionview);
858                                 } else {
859                                         add_region_drag (item, event, clicked_regionview);
860                                 }
861
862
863                                 _drags->start_grab (event);
864                                 return true;
865                                 break;
866
867                         case RegionViewNameHighlight:
868                         case LeftFrameHandle:
869                         case RightFrameHandle:
870                                 if (!clicked_regionview->region()->locked()) {
871                                         _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
872                                         return true;
873                                 }
874                                 break;
875
876                         case FadeInTrimHandleItem:
877                         case FadeOutTrimHandleItem:
878                                 if (!clicked_regionview->region()->locked()) {
879                                         _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
880                                         return true;
881                                 }
882                                 break;
883
884                         case RegionViewName:
885                         {
886                                 /* rename happens on edit clicks */
887                                 if (clicked_regionview->get_name_highlight()) {
888                                         _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
889                                         return true;
890                                 }
891                                 break;
892                         }
893
894                         case ControlPointItem:
895                                 _drags->set (new ControlPointDrag (this, item), event);
896                                 return true;
897                                 break;
898
899                         case AutomationLineItem:
900                                 _drags->set (new LineDrag (this, item), event);
901                                 return true;
902                                 break;
903
904                         case StreamItem:
905                                 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
906                                 return true;
907
908                         case AutomationTrackItem:
909                         {
910                                 TimeAxisView* parent = clicked_axisview->get_parent ();
911                                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
912                                 assert (atv);
913                                 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
914
915                                         RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
916                                         assert (p);
917                                         boost::shared_ptr<Playlist> pl = p->track()->playlist ();
918                                         if (pl->n_regions() == 0) {
919                                                 /* Parent has no regions; create one so that we have somewhere to put automation */
920                                                 _drags->set (new RegionCreateDrag (this, item, parent), event);
921                                         } else {
922                                                 /* See if there's a region before the click that we can extend, and extend it if so */
923                                                 framepos_t const t = canvas_event_sample (event);
924                                                 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
925                                                 if (!prev) {
926                                                         _drags->set (new RegionCreateDrag (this, item, parent), event);
927                                                 } else {
928                                                         prev->set_length (t - prev->position ());
929                                                 }
930                                         }
931                                 } else {
932                                         /* rubberband drag to select automation points */
933                                         _drags->set (new EditorRubberbandSelectDrag (this, item), event);
934                                 }
935                                 break;
936                         }
937
938                         case SelectionItem:
939                         {
940                                 break;
941                         }
942
943                         case MarkerBarItem:
944
945                                 break;
946
947                         default:
948                                 break;
949                         }
950                 }
951                 return true;
952                 break;
953
954         case MouseDraw:
955                 switch (item_type) {
956                 case GainLineItem:
957                         _drags->set (new LineDrag (this, item), event);
958                         return true;
959
960                 case ControlPointItem:
961                         _drags->set (new ControlPointDrag (this, item), event);
962                         return true;
963                         break;
964
965                 case SelectionItem:
966                 {
967                         if (dynamic_cast<AudioRegionView*>(clicked_regionview) ||
968                             dynamic_cast<AutomationRegionView*>(clicked_regionview)) {
969                                 _drags->set (new AutomationRangeDrag (this, clicked_regionview, selection->time),
970                                              event, _cursors->up_down);
971                         } else {
972                                 double const y = event->button.y;
973                                 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y, false);
974                                 if (tvp.first) {
975                                         AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
976                                         if ( atv) {
977                                                 /* smart "join" mode: drag automation */
978                                                 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
979                                         }
980                                 }
981                         }
982                         return true;
983                         break;
984                 }
985
986                 case AutomationLineItem:
987                         _drags->set (new LineDrag (this, item), event);
988                         break;
989
990                 case NoteItem:
991                         if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
992                                 if (note->big_enough_to_trim() && note->mouse_near_ends()) {
993                                         /* Note is big and pointer is near the end, trim */
994                                         _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
995                                 } else {
996                                         /* Drag note */
997                                         _drags->set (new NoteDrag (this, item), event);
998                                 }
999                                 return true;
1000                         }
1001                         return true;
1002
1003                 case StreamItem:
1004                         if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1005                                 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1006                         }
1007                         return true;
1008
1009                 default:
1010                         break;
1011                 }
1012                 return true;
1013                 break;
1014
1015         case MouseTimeFX:
1016                 if (item_type == NoteItem) {
1017                         /* resize-drag notes */
1018                         if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
1019                                 if (note->big_enough_to_trim()) {
1020                                         _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
1021                                 }
1022                         }
1023                         return true;
1024                 } else if (clicked_regionview) {
1025                         /* do time-FX  */
1026                         _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1027                         return true;
1028                 }
1029                 break;
1030
1031         case MouseAudition:
1032                 _drags->set (new ScrubDrag (this, item), event, _cursors->transparent);
1033                 scrub_reversals = 0;
1034                 scrub_reverse_distance = 0;
1035                 last_scrub_x = event->button.x;
1036                 scrubbing_direction = 0;
1037                 return true;
1038                 break;
1039
1040         default:
1041                 break;
1042         }
1043
1044         return false;
1045 }
1046
1047 bool
1048 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1049 {
1050         Editing::MouseMode const eff = effective_mouse_mode ();
1051         switch (eff) {
1052         case MouseObject:
1053                 switch (item_type) {
1054                 case RegionItem:
1055                         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1056                                 add_region_copy_drag (item, event, clicked_regionview);
1057                         } else {
1058                                 add_region_drag (item, event, clicked_regionview);
1059                         }
1060                         _drags->start_grab (event);
1061                         return true;
1062                         break;
1063                 case ControlPointItem:
1064                         _drags->set (new ControlPointDrag (this, item), event);
1065                         return true;
1066                         break;
1067
1068                 default:
1069                         break;
1070                 }
1071
1072                 switch (item_type) {
1073                 case RegionViewNameHighlight:
1074                         _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1075                         return true;
1076                         break;
1077
1078                 case LeftFrameHandle:
1079                 case RightFrameHandle:
1080                         _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1081                         return true;
1082                         break;
1083
1084                 case RegionViewName:
1085                         _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1086                         return true;
1087                         break;
1088
1089                 default:
1090                         break;
1091                 }
1092
1093                 break;
1094
1095         case MouseDraw:
1096                 return false;
1097
1098         case MouseRange:
1099                 /* relax till release */
1100                 return true;
1101                 break;
1102
1103         default:
1104                 break;
1105         }
1106
1107         return false;
1108 }
1109
1110 bool
1111 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1112 {
1113         if (event->type == GDK_2BUTTON_PRESS) {
1114                 _drags->mark_double_click ();
1115                 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1116                 return true;
1117         }
1118
1119         if (event->type != GDK_BUTTON_PRESS) {
1120                 return false;
1121         }
1122
1123         _track_canvas->grab_focus();
1124
1125         if (_session && _session->actively_recording()) {
1126                 return true;
1127         }
1128
1129         button_selection (item, event, item_type);
1130
1131         if (!_drags->active () &&
1132             (Keyboard::is_delete_event (&event->button) ||
1133              Keyboard::is_context_menu_event (&event->button) ||
1134              Keyboard::is_edit_event (&event->button))) {
1135
1136                 /* handled by button release */
1137                 return true;
1138         }
1139
1140         //not rolling, range mode click + join_play_range :  locate the PH here
1141         if ( !_drags->active () && _session && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && UIConfiguration::instance().get_follow_edits() && !_session->config.get_external_sync() ) {
1142                 framepos_t where = canvas_event_sample (event);
1143                 snap_to(where);
1144                 _session->request_locate (where, false);
1145         }
1146
1147         switch (event->button.button) {
1148         case 1:
1149                 return button_press_handler_1 (item, event, item_type);
1150                 break;
1151
1152         case 2:
1153                 return button_press_handler_2 (item, event, item_type);
1154                 break;
1155
1156         case 3:
1157                 break;
1158
1159         default:
1160                 return button_press_dispatch (&event->button);
1161                 break;
1162
1163         }
1164
1165         return false;
1166 }
1167
1168 bool
1169 Editor::button_press_dispatch (GdkEventButton* ev)
1170 {
1171         /* this function is intended only for buttons 4 and above.
1172          */
1173
1174         Gtkmm2ext::MouseButton b (ev->state, ev->button);
1175         return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1176 }
1177
1178 bool
1179 Editor::button_release_dispatch (GdkEventButton* ev)
1180 {
1181         /* this function is intended only for buttons 4 and above.
1182          */
1183
1184         Gtkmm2ext::MouseButton b (ev->state, ev->button);
1185         return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1186 }
1187
1188 bool
1189 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1190 {
1191         framepos_t where = canvas_event_sample (event);
1192         AutomationTimeAxisView* atv = 0;
1193
1194         _press_cursor_ctx.reset();
1195
1196         /* no action if we're recording */
1197
1198         if (_session && _session->actively_recording()) {
1199                 return true;
1200         }
1201
1202         bool were_dragging = false;
1203
1204         if (!Keyboard::is_context_menu_event (&event->button)) {
1205
1206                 /* see if we're finishing a drag */
1207
1208                 if (_drags->active ()) {
1209                         bool const r = _drags->end_grab (event);
1210                         if (r) {
1211                                 /* grab dragged, so do nothing else */
1212                                 return true;
1213                         }
1214
1215                         were_dragging = true;
1216                 }
1217
1218                 update_region_layering_order_editor ();
1219         }
1220
1221         /* edit events get handled here */
1222
1223         if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1224                 switch (item_type) {
1225                 case RegionItem:
1226                         show_region_properties ();
1227                         break;
1228
1229                 case TempoMarkerItem: {
1230                         ArdourMarker* marker;
1231                         TempoMarker* tempo_marker;
1232
1233                         if ((marker = reinterpret_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
1234                                 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1235                                 abort(); /*NOTREACHED*/
1236                         }
1237
1238                         if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1239                                 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1240                                 abort(); /*NOTREACHED*/
1241                         }
1242
1243                         edit_tempo_marker (*tempo_marker);
1244                         break;
1245                 }
1246
1247                 case MeterMarkerItem: {
1248                         ArdourMarker* marker;
1249                         MeterMarker* meter_marker;
1250
1251                         if ((marker = reinterpret_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
1252                                 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1253                                 abort(); /*NOTREACHED*/
1254                         }
1255
1256                         if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1257                                 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1258                                 abort(); /*NOTREACHED*/
1259                         }
1260                         edit_meter_marker (*meter_marker);
1261                         break;
1262                 }
1263
1264                 case RegionViewName:
1265                         if (clicked_regionview->name_active()) {
1266                                 return mouse_rename_region (item, event);
1267                         }
1268                         break;
1269
1270                 case ControlPointItem:
1271                         edit_control_point (item);
1272                         break;
1273
1274                 default:
1275                         break;
1276                 }
1277                 return true;
1278         }
1279
1280         /* context menu events get handled here */
1281         if (Keyboard::is_context_menu_event (&event->button)) {
1282
1283                 context_click_event = *event;
1284
1285                 if (!_drags->active ()) {
1286
1287                         /* no matter which button pops up the context menu, tell the menu
1288                            widget to use button 1 to drive menu selection.
1289                         */
1290
1291                         switch (item_type) {
1292                         case FadeInItem:
1293                         case FadeInHandleItem:
1294                         case FadeInTrimHandleItem:
1295                         case StartCrossFadeItem:
1296                                 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1297                                 break;
1298
1299                         case FadeOutItem:
1300                         case FadeOutHandleItem:
1301                         case FadeOutTrimHandleItem:
1302                         case EndCrossFadeItem:
1303                                 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1304                                 break;
1305
1306                         case LeftFrameHandle:
1307                         case RightFrameHandle:
1308                                 break;
1309
1310                         case StreamItem:
1311                                 popup_track_context_menu (1, event->button.time, item_type, false);
1312                                 break;
1313
1314                         case RegionItem:
1315                         case RegionViewNameHighlight:
1316                         case RegionViewName:
1317                                 popup_track_context_menu (1, event->button.time, item_type, false);
1318                                 break;
1319
1320                         case SelectionItem:
1321                                 popup_track_context_menu (1, event->button.time, item_type, true);
1322                                 break;
1323
1324                         case AutomationTrackItem:
1325                                 popup_track_context_menu (1, event->button.time, item_type, false);
1326                                 break;
1327
1328                         case MarkerBarItem:
1329                         case RangeMarkerBarItem:
1330                         case TransportMarkerBarItem:
1331                         case CdMarkerBarItem:
1332                         case TempoBarItem:
1333                         case MeterBarItem:
1334                         case VideoBarItem:
1335                         case TimecodeRulerItem:
1336                         case SamplesRulerItem:
1337                         case MinsecRulerItem:
1338                         case BBTRulerItem:
1339                                 popup_ruler_menu (where, item_type);
1340                                 break;
1341
1342                         case MarkerItem:
1343                                 marker_context_menu (&event->button, item);
1344                                 break;
1345
1346                         case TempoMarkerItem:
1347                                 tempo_or_meter_marker_context_menu (&event->button, item);
1348                                 break;
1349
1350                         case MeterMarkerItem:
1351                                 tempo_or_meter_marker_context_menu (&event->button, item);
1352                                 break;
1353
1354                         case CrossfadeViewItem:
1355                                 popup_track_context_menu (1, event->button.time, item_type, false);
1356                                 break;
1357
1358                         case ControlPointItem:
1359                                 popup_control_point_context_menu (item, event);
1360                                 break;
1361
1362                         case NoteItem:
1363                                 if (internal_editing()) {
1364                                         popup_note_context_menu (item, event);
1365                                 }
1366                                 break;
1367
1368                         default:
1369                                 break;
1370                         }
1371
1372                         return true;
1373                 }
1374         }
1375
1376         /* delete events get handled here */
1377
1378         Editing::MouseMode const eff = effective_mouse_mode ();
1379
1380         if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1381
1382                 switch (item_type) {
1383                 case TempoMarkerItem:
1384                         remove_tempo_marker (item);
1385                         break;
1386
1387                 case MeterMarkerItem:
1388                         remove_meter_marker (item);
1389                         break;
1390
1391                 case MarkerItem:
1392                         remove_marker (*item, event);
1393                         break;
1394
1395                 case RegionItem:
1396                         if (eff == MouseObject) {
1397                                 remove_clicked_region ();
1398                         }
1399                         break;
1400
1401                 case ControlPointItem:
1402                         remove_control_point (item);
1403                         break;
1404
1405                 case NoteItem:
1406                         remove_midi_note (item, event);
1407                         break;
1408
1409                 default:
1410                         break;
1411                 }
1412                 return true;
1413         }
1414
1415         switch (event->button.button) {
1416         case 1:
1417
1418                 switch (item_type) {
1419                 /* see comments in button_press_handler */
1420                 case PlayheadCursorItem:
1421                 case MarkerItem:
1422                 case GainLineItem:
1423                 case AutomationLineItem:
1424                 case StartSelectionTrimItem:
1425                 case EndSelectionTrimItem:
1426                         return true;
1427
1428                 case MarkerBarItem:
1429                         if (!_dragging_playhead) {
1430                                 snap_to_with_modifier (where, event, RoundNearest, true);
1431                                 mouse_add_new_marker (where);
1432                         }
1433                         return true;
1434
1435                 case CdMarkerBarItem:
1436                         if (!_dragging_playhead) {
1437                                 // if we get here then a dragged range wasn't done
1438                                 snap_to_with_modifier (where, event, RoundNearest, true);
1439                                 mouse_add_new_marker (where, true);
1440                         }
1441                         return true;
1442
1443                 case TempoBarItem:
1444                         if (!_dragging_playhead) {
1445                                 snap_to_with_modifier (where, event);
1446                                 mouse_add_new_tempo_event (where);
1447                         }
1448                         return true;
1449
1450                 case MeterBarItem:
1451                         if (!_dragging_playhead) {
1452                                 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1453                         }
1454                         return true;
1455                         break;
1456
1457                 case TimecodeRulerItem:
1458                 case SamplesRulerItem:
1459                 case MinsecRulerItem:
1460                 case BBTRulerItem:
1461                         return true;
1462                         break;
1463
1464                 default:
1465                         break;
1466                 }
1467
1468                 switch (eff) {
1469                 case MouseDraw:
1470                         switch (item_type) {
1471                         case RegionItem:
1472                         {
1473                                 /* check that we didn't drag before releasing, since
1474                                    its really annoying to create new control
1475                                    points when doing this.
1476                                 */
1477                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1478                                 if (!were_dragging && arv) {
1479                                         bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1480                                         arv->add_gain_point_event (item, event, with_guard_points);
1481                                 }
1482                                 return true;
1483                                 break;
1484                         }
1485
1486                         case AutomationTrackItem: {
1487                                 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1488                                 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1489                                 if (atv) {
1490                                         atv->add_automation_event (event, where, event->button.y, with_guard_points);
1491                                 }
1492                                 return true;
1493                                 break;
1494                         }
1495                         default:
1496                                 break;
1497                         }
1498                         break;
1499
1500                 case MouseAudition:
1501                         if (scrubbing_direction == 0) {
1502                                 /* no drag, just a click */
1503                                 switch (item_type) {
1504                                 case RegionItem:
1505                                         play_selected_region ();
1506                                         break;
1507                                 default:
1508                                         break;
1509                                 }
1510                         } else if (_session) {
1511                                 /* make sure we stop */
1512                                 _session->request_transport_speed (0.0);
1513                         }
1514                         break;
1515
1516                 default:
1517                         break;
1518
1519                 }
1520
1521                 /* do any (de)selection operations that should occur on button release */
1522                 button_selection (item, event, item_type);
1523
1524                 return true;
1525                 break;
1526
1527
1528         case 2:
1529                 switch (eff) {
1530
1531                 case MouseObject:
1532                         switch (item_type) {
1533                         case RegionItem:
1534                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1535                                         raise_region ();
1536                                 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1537                                         lower_region ();
1538                                 } else {
1539                                         // Button2 click is unused
1540                                 }
1541                                 return true;
1542
1543                                 break;
1544
1545                         default:
1546                                 break;
1547                         }
1548                         break;
1549
1550                 case MouseDraw:
1551                         return true;
1552
1553                 case MouseRange:
1554                         // x_style_paste (where, 1.0);
1555                         return true;
1556                         break;
1557
1558                 default:
1559                         break;
1560                 }
1561
1562                 break;
1563
1564         case 3:
1565                 break;
1566
1567         default:
1568                 break;
1569         }
1570
1571         return false;
1572 }
1573
1574 bool
1575 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1576 {
1577         ControlPoint* cp;
1578         ArdourMarker * marker;
1579         double fraction;
1580         bool ret = true;
1581
1582         /* by the time we reach here, entered_regionview and entered trackview
1583          * will have already been set as appropriate. Things are done this
1584          * way because this method isn't passed a pointer to a variable type of
1585          * thing that is entered (which may or may not be canvas item).
1586          * (e.g. the actual entered regionview)
1587          */
1588
1589         choose_canvas_cursor_on_entry (item_type);
1590
1591         switch (item_type) {
1592         case ControlPointItem:
1593                 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1594                         cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1595                         cp->show ();
1596
1597                         fraction = 1.0 - (cp->get_y() / cp->line().height());
1598
1599                         _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1600                         _verbose_cursor->show ();
1601                 }
1602                 break;
1603
1604         case GainLineItem:
1605                 if (mouse_mode == MouseDraw) {
1606                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1607                         if (line) {
1608                                 line->set_outline_color (UIConfiguration::instance().color ("entered gain line"));
1609                         }
1610                 }
1611                 break;
1612
1613         case AutomationLineItem:
1614                 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1615                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1616                         if (line) {
1617                                 line->set_outline_color (UIConfiguration::instance().color ("entered automation line"));
1618                         }
1619                 }
1620                 break;
1621
1622         case AutomationTrackItem:
1623                 AutomationTimeAxisView* atv;
1624                 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1625                         clear_entered_track = false;
1626                         set_entered_track (atv);
1627                 }
1628                 break;
1629
1630         case MarkerItem:
1631                 if ((marker = static_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
1632                         break;
1633                 }
1634                 entered_marker = marker;
1635                 marker->set_color_rgba (UIConfiguration::instance().color ("entered marker"));
1636                 // fall through
1637         case MeterMarkerItem:
1638         case TempoMarkerItem:
1639                 break;
1640
1641         case FadeInHandleItem:
1642         case FadeInTrimHandleItem:
1643                 if (mouse_mode == MouseObject) {
1644                         ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1645                         if (rect) {
1646                                 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1647                                 rect->set_fill_color (rv->get_fill_color());
1648                         }
1649                 }
1650                 break;
1651
1652         case FadeOutHandleItem:
1653         case FadeOutTrimHandleItem:
1654                 if (mouse_mode == MouseObject) {
1655                         ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1656                         if (rect) {
1657                                 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1658                                 rect->set_fill_color (rv->get_fill_color ());
1659                         }
1660                 }
1661                 break;
1662
1663         case FeatureLineItem:
1664         {
1665                 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1666                 line->set_outline_color (0xFF0000FF);
1667         }
1668         break;
1669
1670         case SelectionItem:
1671                 break;
1672
1673         case WaveItem:
1674         {
1675                 if (entered_regionview) {
1676                         entered_regionview->entered();
1677                 }
1678         }
1679         break;
1680
1681         default:
1682                 break;
1683         }
1684
1685         /* third pass to handle entered track status in a comprehensible way.
1686          */
1687
1688         switch (item_type) {
1689         case GainLineItem:
1690         case AutomationLineItem:
1691         case ControlPointItem:
1692                 /* these do not affect the current entered track state */
1693                 clear_entered_track = false;
1694                 break;
1695
1696         case AutomationTrackItem:
1697                 /* handled above already */
1698                 break;
1699
1700         default:
1701
1702                 break;
1703         }
1704
1705         return ret;
1706 }
1707
1708 bool
1709 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1710 {
1711         AutomationLine* al;
1712         ArdourMarker *marker;
1713         Location *loc;
1714         bool is_start;
1715         bool ret = true;
1716
1717         if (!_enter_stack.empty()) {
1718                 _enter_stack.pop_back();
1719         }
1720
1721         switch (item_type) {
1722         case ControlPointItem:
1723                 _verbose_cursor->hide ();
1724                 break;
1725
1726         case GainLineItem:
1727         case AutomationLineItem:
1728                 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1729                 {
1730                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1731                         if (line) {
1732                                 line->set_outline_color (al->get_line_color());
1733                         }
1734                 }
1735                 break;
1736
1737         case MarkerItem:
1738                 if ((marker = static_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
1739                         break;
1740                 }
1741                 entered_marker = 0;
1742                 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1743                         location_flags_changed (loc);
1744                 }
1745                 // fall through
1746         case MeterMarkerItem:
1747         case TempoMarkerItem:
1748                 break;
1749
1750         case FadeInTrimHandleItem:
1751         case FadeOutTrimHandleItem:
1752         case FadeInHandleItem:
1753         case FadeOutHandleItem:
1754         {
1755                 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1756                 if (rect) {
1757                         rect->set_fill_color (UIConfiguration::instance().color ("inactive fade handle"));
1758                 }
1759         }
1760         break;
1761
1762         case AutomationTrackItem:
1763                 break;
1764
1765         case FeatureLineItem:
1766         {
1767                 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1768                 line->set_outline_color (UIConfiguration::instance().color ("zero line"));
1769         }
1770         break;
1771
1772         default:
1773                 break;
1774         }
1775
1776         return ret;
1777 }
1778
1779 void
1780 Editor::scrub (framepos_t frame, double current_x)
1781 {
1782         double delta;
1783
1784         if (scrubbing_direction == 0) {
1785                 /* first move */
1786                 _session->request_locate (frame, false);
1787                 _session->request_transport_speed (0.1);
1788                 scrubbing_direction = 1;
1789
1790         } else {
1791
1792                 if (last_scrub_x > current_x) {
1793
1794                         /* pointer moved to the left */
1795
1796                         if (scrubbing_direction > 0) {
1797
1798                                 /* we reversed direction to go backwards */
1799
1800                                 scrub_reversals++;
1801                                 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1802
1803                         } else {
1804
1805                                 /* still moving to the left (backwards) */
1806
1807                                 scrub_reversals = 0;
1808                                 scrub_reverse_distance = 0;
1809
1810                                 delta = 0.01 * (last_scrub_x - current_x);
1811                                 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1812                         }
1813
1814                 } else {
1815                         /* pointer moved to the right */
1816
1817                         if (scrubbing_direction < 0) {
1818                                 /* we reversed direction to go forward */
1819
1820                                 scrub_reversals++;
1821                                 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1822
1823                         } else {
1824                                 /* still moving to the right */
1825
1826                                 scrub_reversals = 0;
1827                                 scrub_reverse_distance = 0;
1828
1829                                 delta = 0.01 * (current_x - last_scrub_x);
1830                                 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
1831                         }
1832                 }
1833
1834                 /* if there have been more than 2 opposite motion moves detected, or one that moves
1835                    back more than 10 pixels, reverse direction
1836                 */
1837
1838                 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1839
1840                         if (scrubbing_direction > 0) {
1841                                 /* was forwards, go backwards */
1842                                 _session->request_transport_speed (-0.1);
1843                                 scrubbing_direction = -1;
1844                         } else {
1845                                 /* was backwards, go forwards */
1846                                 _session->request_transport_speed (0.1);
1847                                 scrubbing_direction = 1;
1848                         }
1849
1850                         scrub_reverse_distance = 0;
1851                         scrub_reversals = 0;
1852                 }
1853         }
1854
1855         last_scrub_x = current_x;
1856 }
1857
1858 bool
1859 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1860 {
1861         _last_motion_y = event->motion.y;
1862
1863         if (event->motion.is_hint) {
1864                 gint x, y;
1865
1866                 /* We call this so that MOTION_NOTIFY events continue to be
1867                    delivered to the canvas. We need to do this because we set
1868                    Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1869                    the density of the events, at the expense of a round-trip
1870                    to the server. Given that this will mostly occur on cases
1871                    where DISPLAY = :0.0, and given the cost of what the motion
1872                    event might do, its a good tradeoff.
1873                 */
1874
1875                 _track_canvas->get_pointer (x, y);
1876         }
1877
1878         if (current_stepping_trackview) {
1879                 /* don't keep the persistent stepped trackview if the mouse moves */
1880                 current_stepping_trackview = 0;
1881                 step_timeout.disconnect ();
1882         }
1883
1884         if (_session && _session->actively_recording()) {
1885                 /* Sorry. no dragging stuff around while we record */
1886                 return true;
1887         }
1888
1889         update_join_object_range_location (event->motion.y);
1890
1891         if (_drags->active ()) {
1892                 return _drags->motion_handler (event, from_autoscroll);
1893         }
1894
1895         return false;
1896 }
1897
1898 bool
1899 Editor::can_remove_control_point (ArdourCanvas::Item* item)
1900 {
1901         ControlPoint* control_point;
1902
1903         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1904                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1905                 abort(); /*NOTREACHED*/
1906         }
1907
1908         AutomationLine& line = control_point->line ();
1909         if (dynamic_cast<AudioRegionGainLine*> (&line)) {
1910                 /* we shouldn't remove the first or last gain point in region gain lines */
1911                 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
1912                         return false;
1913                 }
1914         }
1915
1916         return true;
1917 }
1918
1919 void
1920 Editor::remove_control_point (ArdourCanvas::Item* item)
1921 {
1922         if (!can_remove_control_point (item)) {
1923                 return;
1924         }
1925
1926         ControlPoint* control_point;
1927
1928         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1929                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1930                 abort(); /*NOTREACHED*/
1931         }
1932
1933         control_point->line().remove_point (*control_point);
1934 }
1935
1936 void
1937 Editor::edit_control_point (ArdourCanvas::Item* item)
1938 {
1939         ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1940
1941         if (p == 0) {
1942                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1943                 abort(); /*NOTREACHED*/
1944         }
1945
1946         ControlPointDialog d (p);
1947
1948         if (d.run () != RESPONSE_ACCEPT) {
1949                 return;
1950         }
1951
1952         p->line().modify_point_y (*p, d.get_y_fraction ());
1953 }
1954
1955 void
1956 Editor::edit_notes (MidiRegionView* mrv)
1957 {
1958         MidiRegionView::Selection const & s = mrv->selection();
1959
1960         if (s.empty ()) {
1961                 return;
1962         }
1963
1964         EditNoteDialog* d = new EditNoteDialog (mrv, s);
1965         d->show_all ();
1966
1967         d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
1968 }
1969
1970 void
1971 Editor::note_edit_done (int r, EditNoteDialog* d)
1972 {
1973         d->done (r);
1974         delete d;
1975 }
1976
1977 void
1978 Editor::visible_order_range (int* low, int* high) const
1979 {
1980         *low = TimeAxisView::max_order ();
1981         *high = 0;
1982
1983         for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1984
1985                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1986
1987                 if (!rtv->hidden()) {
1988
1989                         if (*high < rtv->order()) {
1990                                 *high = rtv->order ();
1991                         }
1992
1993                         if (*low > rtv->order()) {
1994                                 *low = rtv->order ();
1995                         }
1996                 }
1997         }
1998 }
1999
2000 void
2001 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2002 {
2003         /* Either add to or set the set the region selection, unless
2004            this is an alignment click (control used)
2005         */
2006
2007         if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2008                 TimeAxisView* tv = &rv.get_time_axis_view();
2009                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2010                 double speed = 1.0;
2011                 if (rtv && rtv->is_track()) {
2012                         speed = rtv->track()->speed();
2013                 }
2014
2015                 framepos_t where = get_preferred_edit_position();
2016
2017                 if (where >= 0) {
2018
2019                         if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2020
2021                                 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2022
2023                         } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2024
2025                                 align_region (rv.region(), End, (framepos_t) (where * speed));
2026
2027                         } else {
2028
2029                                 align_region (rv.region(), Start, (framepos_t) (where * speed));
2030                         }
2031                 }
2032         }
2033 }
2034
2035 void
2036 Editor::collect_new_region_view (RegionView* rv)
2037 {
2038         latest_regionviews.push_back (rv);
2039 }
2040
2041 void
2042 Editor::collect_and_select_new_region_view (RegionView* rv)
2043 {
2044         selection->add(rv);
2045         latest_regionviews.push_back (rv);
2046 }
2047
2048 void
2049 Editor::cancel_selection ()
2050 {
2051         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2052                 (*i)->hide_selection ();
2053         }
2054
2055         selection->clear ();
2056         clicked_selection = 0;
2057 }
2058
2059 void
2060 Editor::cancel_time_selection ()
2061 {
2062         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2063                 (*i)->hide_selection ();
2064         }
2065         selection->time.clear ();
2066         clicked_selection = 0;
2067 }
2068
2069 void
2070 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2071 {
2072         RegionView* rv = clicked_regionview;
2073
2074         /* Choose action dependant on which button was pressed */
2075         switch (event->button.button) {
2076         case 1:
2077                 begin_reversible_command (_("start point trim"));
2078
2079                 if (selection->selected (rv)) {
2080                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2081                              i != selection->regions.by_layer().end(); ++i)
2082                         {
2083                                 if (!(*i)->region()->locked()) {
2084                                         (*i)->region()->clear_changes ();
2085                                         (*i)->region()->trim_front (new_bound);
2086                                         _session->add_command(new StatefulDiffCommand ((*i)->region()));
2087                                 }
2088                         }
2089
2090                 } else {
2091                         if (!rv->region()->locked()) {
2092                                 rv->region()->clear_changes ();
2093                                 rv->region()->trim_front (new_bound);
2094                                 _session->add_command(new StatefulDiffCommand (rv->region()));
2095                         }
2096                 }
2097
2098                 commit_reversible_command();
2099
2100                 break;
2101         case 2:
2102                 begin_reversible_command (_("End point trim"));
2103
2104                 if (selection->selected (rv)) {
2105
2106                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2107                         {
2108                                 if (!(*i)->region()->locked()) {
2109                                         (*i)->region()->clear_changes();
2110                                         (*i)->region()->trim_end (new_bound);
2111                                         _session->add_command(new StatefulDiffCommand ((*i)->region()));
2112                                 }
2113                         }
2114
2115                 } else {
2116
2117                         if (!rv->region()->locked()) {
2118                                 rv->region()->clear_changes ();
2119                                 rv->region()->trim_end (new_bound);
2120                                 _session->add_command (new StatefulDiffCommand (rv->region()));
2121                         }
2122                 }
2123
2124                 commit_reversible_command();
2125
2126                 break;
2127         default:
2128                 break;
2129         }
2130 }
2131
2132 void
2133 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2134 {
2135         ArdourMarker* marker;
2136         bool is_start;
2137
2138         if ((marker = static_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
2139                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2140                 abort(); /*NOTREACHED*/
2141         }
2142
2143         Location* location = find_location_from_marker (marker, is_start);
2144         location->set_hidden (true, this);
2145 }
2146
2147 gint
2148 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2149 {
2150         using namespace Gtkmm2ext;
2151
2152         ArdourPrompter prompter (false);
2153
2154         prompter.set_prompt (_("Name for region:"));
2155         prompter.set_initial_text (clicked_regionview->region()->name());
2156         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2157         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2158         prompter.show_all ();
2159         switch (prompter.run ()) {
2160         case Gtk::RESPONSE_ACCEPT:
2161                 string str;
2162                 prompter.get_result(str);
2163                 if (str.length()) {
2164                         clicked_regionview->region()->set_name (str);
2165                 }
2166                 break;
2167         }
2168         return true;
2169 }
2170
2171
2172 void
2173 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2174 {
2175         /* no brushing without a useful snap setting */
2176
2177         switch (_snap_mode) {
2178         case SnapMagnetic:
2179                 return; /* can't work because it allows region to be placed anywhere */
2180         default:
2181                 break; /* OK */
2182         }
2183
2184         switch (_snap_type) {
2185         case SnapToMark:
2186                 return;
2187
2188         default:
2189                 break;
2190         }
2191
2192         /* don't brush a copy over the original */
2193
2194         if (pos == rv->region()->position()) {
2195                 return;
2196         }
2197
2198         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2199
2200         if (rtv == 0 || !rtv->is_track()) {
2201                 return;
2202         }
2203
2204         boost::shared_ptr<Playlist> playlist = rtv->playlist();
2205         double speed = rtv->track()->speed();
2206
2207         playlist->clear_changes ();
2208         boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2209         playlist->add_region (new_region, (framepos_t) (pos * speed));
2210         _session->add_command (new StatefulDiffCommand (playlist));
2211
2212         // playlist is frozen, so we have to update manually XXX this is disgusting
2213
2214         //playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2215 }
2216
2217 gint
2218 Editor::track_height_step_timeout ()
2219 {
2220         if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2221                 current_stepping_trackview = 0;
2222                 return false;
2223         }
2224         return true;
2225 }
2226
2227 void
2228 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2229 {
2230         assert (region_view);
2231
2232         if (!region_view->region()->playlist()) {
2233                 return;
2234         }
2235
2236         switch (Config->get_edit_mode()) {
2237                 case Splice:
2238                         _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2239                         break;
2240                 case Ripple:
2241                         _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2242                         break;
2243                 default:
2244                         _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2245                         break;
2246
2247         }
2248 }
2249
2250 void
2251 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2252 {
2253         assert (region_view);
2254
2255         if (!region_view->region()->playlist()) {
2256                 return;
2257         }
2258
2259         _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2260 }
2261
2262 void
2263 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2264 {
2265         assert (region_view);
2266
2267         if (!region_view->region()->playlist()) {
2268                 return;
2269         }
2270
2271         if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2272                 return;
2273         }
2274
2275         _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2276 }
2277
2278 /** Start a grab where a time range is selected, track(s) are selected, and the
2279  *  user clicks and drags a region with a modifier in order to create a new region containing
2280  *  the section of the clicked region that lies within the time range.
2281  */
2282 void
2283 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2284 {
2285         if (clicked_regionview == 0) {
2286                 return;
2287         }
2288
2289         /* lets try to create new Region for the selection */
2290
2291         vector<boost::shared_ptr<Region> > new_regions;
2292         create_region_from_selection (new_regions);
2293
2294         if (new_regions.empty()) {
2295                 return;
2296         }
2297
2298         /* XXX fix me one day to use all new regions */
2299
2300         boost::shared_ptr<Region> region (new_regions.front());
2301
2302         /* add it to the current stream/playlist.
2303
2304            tricky: the streamview for the track will add a new regionview. we will
2305            catch the signal it sends when it creates the regionview to
2306            set the regionview we want to then drag.
2307         */
2308
2309         latest_regionviews.clear();
2310         sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2311
2312         /* A selection grab currently creates two undo/redo operations, one for
2313            creating the new region and another for moving it.
2314         */
2315         begin_reversible_command (Operations::selection_grab);
2316
2317         boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2318
2319         playlist->clear_changes ();
2320         clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2321         _session->add_command(new StatefulDiffCommand (playlist));
2322
2323         c.disconnect ();
2324
2325         if (latest_regionviews.empty()) {
2326                 /* something went wrong */
2327                 abort_reversible_command ();
2328                 return;
2329         }
2330
2331         /* we need to deselect all other regionviews, and select this one
2332            i'm ignoring undo stuff, because the region creation will take care of it
2333         */
2334
2335         selection->set (latest_regionviews);
2336
2337         commit_reversible_command ();
2338
2339         _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2340 }
2341
2342 void
2343 Editor::escape ()
2344 {
2345         if (_drags->active ()) {
2346                 _drags->abort ();
2347         } else {
2348                 selection->clear ();
2349         }
2350
2351         reset_focus ();
2352 }
2353
2354 /** Update _join_object_range_state which indicate whether we are over the top
2355  *  or bottom half of a route view, used by the `join object/range' tool
2356  *  mode. Coordinates in canvas space.
2357  */
2358 void
2359 Editor::update_join_object_range_location (double y)
2360 {
2361         if (!get_smart_mode()) {
2362                 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2363                 return;
2364         }
2365
2366         JoinObjectRangeState const old = _join_object_range_state;
2367
2368         if (mouse_mode == MouseObject) {
2369                 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2370         } else if (mouse_mode == MouseRange) {
2371                 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2372         }
2373
2374         if (entered_regionview) {
2375
2376                 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2377                 double const c = item_space.y / entered_regionview->height();
2378
2379                 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2380
2381                 Editor::EnterContext* ctx = get_enter_context(RegionItem);
2382                 if (_join_object_range_state != old && ctx) {
2383                         ctx->cursor_ctx->change(which_track_cursor());
2384                 }
2385
2386         } else if (entered_track) {
2387
2388                 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2389
2390                 if (entered_route_view) {
2391
2392                         double cx = 0;
2393                         double cy = y;
2394
2395                         entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2396
2397                         double track_height = entered_route_view->view()->child_height();
2398                         if (UIConfiguration::instance().get_show_name_highlight()) {
2399                                 track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2400                         }
2401                         double const c = cy / track_height;
2402
2403
2404                         if (c <= 0.5) {
2405                                 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2406                         } else {
2407                                 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2408                         }
2409
2410                 } else {
2411                         /* Other kinds of tracks use object mode */
2412                         _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2413                 }
2414
2415                 Editor::EnterContext* ctx = get_enter_context(StreamItem);
2416                 if (_join_object_range_state != old && ctx) {
2417                         ctx->cursor_ctx->change(which_track_cursor());
2418                 }
2419         }
2420 }
2421
2422 Editing::MouseMode
2423 Editor::effective_mouse_mode () const
2424 {
2425         if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2426                 return MouseObject;
2427         } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2428                 return MouseRange;
2429         }
2430
2431         return mouse_mode;
2432 }
2433
2434 void
2435 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2436 {
2437         NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2438         assert (e);
2439
2440         e->region_view().delete_note (e->note ());
2441 }
2442
2443 /** Obtain the pointer position in canvas coordinates */
2444 void
2445 Editor::get_pointer_position (double& x, double& y) const
2446 {
2447         int px, py;
2448         _track_canvas->get_pointer (px, py);
2449         _track_canvas->window_to_canvas (px, py, x, y);
2450 }