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