don't wipe out SHIFT from event state when processing keyboard events, as done in...
[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 (_("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 (_("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 StreamItem:
770                         if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
771                                 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
772                                 return true;
773                         }
774                         break;
775
776                 case AutomationTrackItem:
777                         /* rubberband drag to select automation points */
778                         _drags->set (new EditorRubberbandSelectDrag (this, item), event);
779                         return true;
780                         break;
781
782                 case RegionItem:
783                         if (dynamic_cast<AutomationRegionView*>(clicked_regionview)) {
784                                 /* rubberband drag to select automation points */
785                                 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
786                                 return true;
787                         }
788                         break;
789
790                 default:
791                         break;
792                 }
793                 break;
794
795         case MouseObject:
796                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
797                     event->type == GDK_BUTTON_PRESS) {
798
799                         _drags->set (new EditorRubberbandSelectDrag (this, item), event);
800
801                 } else if (event->type == GDK_BUTTON_PRESS) {
802
803                         switch (item_type) {
804                         case FadeInHandleItem:
805                         {
806                                 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
807                                 return true;
808                         }
809
810                         case FadeOutHandleItem:
811                         {
812                                 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
813                                 return true;
814                         }
815
816                         case StartCrossFadeItem:
817                         case EndCrossFadeItem:
818                                 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor.  for not this is not fully implemented */ 
819 //                              if (!clicked_regionview->region()->locked()) {
820 //                                      _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
821 //                                      return true;
822 //                              }
823                                 break;
824
825                         case FeatureLineItem:
826                         {
827                                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
828                                         remove_transient(item);
829                                         return true;
830                                 }
831
832                                 _drags->set (new FeatureLineDrag (this, item), event);
833                                 return true;
834                                 break;
835                         }
836
837                         case RegionItem:
838                                 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
839                                         /* click on an automation region view; do nothing here and let the ARV's signal handler
840                                            sort it out.
841                                         */
842                                         break;
843                                 }
844
845                                 /* click on a normal region view */
846                                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
847                                         add_region_copy_drag (item, event, clicked_regionview);
848                                 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
849                                         add_region_brush_drag (item, event, clicked_regionview);
850                                 } else {
851                                         add_region_drag (item, event, clicked_regionview);
852                                 }
853
854
855                                 _drags->start_grab (event);
856                                 return true;
857                                 break;
858
859                         case RegionViewNameHighlight:
860                         case LeftFrameHandle:
861                         case RightFrameHandle:
862                                 if (!clicked_regionview->region()->locked()) {
863                                         _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
864                                         return true;
865                                 }
866                                 break;
867
868                         case FadeInTrimHandleItem:
869                         case FadeOutTrimHandleItem:
870                                 if (!clicked_regionview->region()->locked()) {
871                                         _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
872                                         return true;
873                                 }
874                                 break;
875
876                         case RegionViewName:
877                         {
878                                 /* rename happens on edit clicks */
879                                 if (clicked_regionview->get_name_highlight()) {
880                                         _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
881                                         return true;
882                                 }
883                                 break;
884                         }
885
886                         case ControlPointItem:
887                                 _drags->set (new ControlPointDrag (this, item), event);
888                                 return true;
889                                 break;
890
891                         case AutomationLineItem:
892                                 _drags->set (new LineDrag (this, item), event);
893                                 return true;
894                                 break;
895
896                         case StreamItem:
897                                 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
898                                 return true;
899
900                         case AutomationTrackItem:
901                         {
902                                 TimeAxisView* parent = clicked_axisview->get_parent ();
903                                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
904                                 assert (atv);
905                                 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
906
907                                         RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
908                                         assert (p);
909                                         boost::shared_ptr<Playlist> pl = p->track()->playlist ();
910                                         if (pl->n_regions() == 0) {
911                                                 /* Parent has no regions; create one so that we have somewhere to put automation */
912                                                 _drags->set (new RegionCreateDrag (this, item, parent), event);
913                                         } else {
914                                                 /* See if there's a region before the click that we can extend, and extend it if so */
915                                                 framepos_t const t = canvas_event_sample (event);
916                                                 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
917                                                 if (!prev) {
918                                                         _drags->set (new RegionCreateDrag (this, item, parent), event);
919                                                 } else {
920                                                         prev->set_length (t - prev->position ());
921                                                 }
922                                         }
923                                 } else {
924                                         /* rubberband drag to select automation points */
925                                         _drags->set (new EditorRubberbandSelectDrag (this, item), event);
926                                 }
927                                 break;
928                         }
929
930                         case SelectionItem:
931                         {
932                                 break;
933                         }
934
935                         case MarkerBarItem:
936
937                                 break;
938
939                         default:
940                                 break;
941                         }
942                 }
943                 return true;
944                 break;
945
946         case MouseDraw:
947                 switch (item_type) {
948                 case GainLineItem:
949                         _drags->set (new LineDrag (this, item), event);
950                         return true;
951
952                 case ControlPointItem:
953                         _drags->set (new ControlPointDrag (this, item), event);
954                         return true;
955                         break;
956
957                 case SelectionItem:
958                 {
959                         if (dynamic_cast<AudioRegionView*>(clicked_regionview) ||
960                             dynamic_cast<AutomationRegionView*>(clicked_regionview)) {
961                                 _drags->set (new AutomationRangeDrag (this, clicked_regionview, selection->time),
962                                              event, _cursors->up_down);
963                         } else {
964                                 double const y = event->button.y;
965                                 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y, false);
966                                 if (tvp.first) {
967                                         AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
968                                         if ( atv) {
969                                                 /* smart "join" mode: drag automation */
970                                                 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
971                                         }
972                                 }
973                         }
974                         return true;
975                         break;
976                 }
977
978                 case AutomationLineItem:
979                         _drags->set (new LineDrag (this, item), event);
980                         break;
981
982                 case NoteItem:
983                         if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
984                                 if (note->big_enough_to_trim() && note->mouse_near_ends()) {
985                                         /* Note is big and pointer is near the end, trim */
986                                         _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
987                                 } else {
988                                         /* Drag note */
989                                         _drags->set (new NoteDrag (this, item), event);
990                                 }
991                                 return true;
992                         }
993                         return true;
994
995                 case StreamItem:
996                         if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
997                                 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
998                         }
999                         return true;
1000
1001                 default:
1002                         break;
1003                 }
1004                 return true;
1005                 break;
1006
1007         case MouseTimeFX:
1008                 if (item_type == NoteItem) {
1009                         /* resize-drag notes */
1010                         if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
1011                                 if (note->big_enough_to_trim()) {
1012                                         _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
1013                                 }
1014                         }
1015                         return true;
1016                 } else if (clicked_regionview) {
1017                         /* do time-FX  */
1018                         _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1019                         return true;
1020                 }
1021                 break;
1022
1023         case MouseAudition:
1024                 _drags->set (new ScrubDrag (this, item), event, _cursors->transparent);
1025                 scrub_reversals = 0;
1026                 scrub_reverse_distance = 0;
1027                 last_scrub_x = event->button.x;
1028                 scrubbing_direction = 0;
1029                 return true;
1030                 break;
1031
1032         default:
1033                 break;
1034         }
1035
1036         return false;
1037 }
1038
1039 bool
1040 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1041 {
1042         Editing::MouseMode const eff = effective_mouse_mode ();
1043         switch (eff) {
1044         case MouseObject:
1045                 switch (item_type) {
1046                 case RegionItem:
1047                         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1048                                 add_region_copy_drag (item, event, clicked_regionview);
1049                         } else {
1050                                 add_region_drag (item, event, clicked_regionview);
1051                         }
1052                         _drags->start_grab (event);
1053                         return true;
1054                         break;
1055                 case ControlPointItem:
1056                         _drags->set (new ControlPointDrag (this, item), event);
1057                         return true;
1058                         break;
1059
1060                 default:
1061                         break;
1062                 }
1063
1064                 switch (item_type) {
1065                 case RegionViewNameHighlight:
1066                         _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1067                         return true;
1068                         break;
1069
1070                 case LeftFrameHandle:
1071                 case RightFrameHandle:
1072                         _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1073                         return true;
1074                         break;
1075
1076                 case RegionViewName:
1077                         _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1078                         return true;
1079                         break;
1080
1081                 default:
1082                         break;
1083                 }
1084
1085                 break;
1086
1087         case MouseDraw:
1088                 return false;
1089
1090         case MouseRange:
1091                 /* relax till release */
1092                 return true;
1093                 break;
1094
1095         default:
1096                 break;
1097         }
1098
1099         return false;
1100 }
1101    
1102 bool
1103 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1104 {
1105         if (event->type == GDK_2BUTTON_PRESS) {
1106                 _drags->mark_double_click ();
1107                 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1108                 return true;
1109         }
1110
1111         if (event->type != GDK_BUTTON_PRESS) {
1112                 return false;
1113         }
1114
1115         _track_canvas->grab_focus();
1116
1117         if (_session && _session->actively_recording()) {
1118                 return true;
1119         }
1120
1121         button_selection (item, event, item_type);
1122
1123         if (!_drags->active () &&
1124             (Keyboard::is_delete_event (&event->button) ||
1125              Keyboard::is_context_menu_event (&event->button) ||
1126              Keyboard::is_edit_event (&event->button))) {
1127
1128                 /* handled by button release */
1129                 return true;
1130         }
1131
1132         //not rolling, range mode click + join_play_range :  locate the PH here
1133         if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && ARDOUR_UI::config()->get_follow_edits() ) {
1134                 framepos_t where = canvas_event_sample (event);
1135                 snap_to(where);
1136                 _session->request_locate (where, false);
1137         }
1138
1139         switch (event->button.button) {
1140         case 1:
1141                 return button_press_handler_1 (item, event, item_type);
1142                 break;
1143
1144         case 2:
1145                 return button_press_handler_2 (item, event, item_type);
1146                 break;
1147
1148         case 3:
1149                 break;
1150
1151         default:
1152                 return button_press_dispatch (&event->button);
1153                 break;
1154
1155         }
1156
1157         return false;
1158 }
1159
1160 bool
1161 Editor::button_press_dispatch (GdkEventButton* ev)
1162 {
1163         /* this function is intended only for buttons 4 and above.
1164          */
1165
1166         Gtkmm2ext::MouseButton b (ev->state, ev->button);
1167         return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1168 }
1169
1170 bool
1171 Editor::button_release_dispatch (GdkEventButton* ev)
1172 {
1173         /* this function is intended only for buttons 4 and above.
1174          */
1175
1176         Gtkmm2ext::MouseButton b (ev->state, ev->button);
1177         return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1178 }
1179
1180 bool
1181 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1182 {
1183         framepos_t where = canvas_event_sample (event);
1184         AutomationTimeAxisView* atv = 0;
1185
1186         _press_cursor_ctx.reset();
1187
1188         /* no action if we're recording */
1189
1190         if (_session && _session->actively_recording()) {
1191                 return true;
1192         }
1193
1194         bool were_dragging = false;
1195
1196         if (!Keyboard::is_context_menu_event (&event->button)) {
1197
1198                 /* see if we're finishing a drag */
1199                 
1200                 if (_drags->active ()) {
1201                         bool const r = _drags->end_grab (event);
1202                         if (r) {
1203                                 /* grab dragged, so do nothing else */
1204                                 return true;
1205                         }
1206                         
1207                         were_dragging = true;
1208                 }
1209
1210                 update_region_layering_order_editor ();
1211         }
1212
1213         /* edit events get handled here */
1214
1215         if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1216                 switch (item_type) {
1217                 case RegionItem:
1218                         show_region_properties ();
1219                         break;
1220
1221                 case TempoMarkerItem: {
1222                         Marker* marker;
1223                         TempoMarker* tempo_marker;
1224                         
1225                         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1226                                 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1227                                 abort(); /*NOTREACHED*/
1228                         }
1229                         
1230                         if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1231                                 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1232                                 abort(); /*NOTREACHED*/
1233                         }
1234                         
1235                         edit_tempo_marker (*tempo_marker);
1236                         break;
1237                 }
1238
1239                 case MeterMarkerItem: {
1240                         Marker* marker;
1241                         MeterMarker* meter_marker;
1242                         
1243                         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1244                                 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1245                                 abort(); /*NOTREACHED*/
1246                         }
1247                         
1248                         if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1249                                 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1250                                 abort(); /*NOTREACHED*/
1251                         }
1252                         edit_meter_marker (*meter_marker);
1253                         break;
1254                 }
1255
1256                 case RegionViewName:
1257                         if (clicked_regionview->name_active()) {
1258                                 return mouse_rename_region (item, event);
1259                         }
1260                         break;
1261
1262                 case ControlPointItem:
1263                         edit_control_point (item);
1264                         break;
1265
1266                 default:
1267                         break;
1268                 }
1269                 return true;
1270         }
1271
1272         /* context menu events get handled here */
1273         if (Keyboard::is_context_menu_event (&event->button)) {
1274
1275                 context_click_event = *event;
1276
1277                 if (!_drags->active ()) {
1278
1279                         /* no matter which button pops up the context menu, tell the menu
1280                            widget to use button 1 to drive menu selection.
1281                         */
1282
1283                         switch (item_type) {
1284                         case FadeInItem:
1285                         case FadeInHandleItem:
1286                         case FadeInTrimHandleItem:
1287                         case StartCrossFadeItem:
1288                                 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1289                                 break;
1290
1291                         case FadeOutItem:
1292                         case FadeOutHandleItem:
1293                         case FadeOutTrimHandleItem:
1294                         case EndCrossFadeItem:
1295                                 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1296                                 break;
1297
1298                         case LeftFrameHandle:
1299                         case RightFrameHandle:
1300                                 break;
1301
1302                         case StreamItem:
1303                                 popup_track_context_menu (1, event->button.time, item_type, false);
1304                                 break;
1305
1306                         case RegionItem:
1307                         case RegionViewNameHighlight:
1308                         case RegionViewName:
1309                                 popup_track_context_menu (1, event->button.time, item_type, false);
1310                                 break;
1311
1312                         case SelectionItem:
1313                                 popup_track_context_menu (1, event->button.time, item_type, true);
1314                                 break;
1315                                 
1316                         case AutomationTrackItem:
1317                                 popup_track_context_menu (1, event->button.time, item_type, false);
1318                                 break;
1319
1320                         case MarkerBarItem:
1321                         case RangeMarkerBarItem:
1322                         case TransportMarkerBarItem:
1323                         case CdMarkerBarItem:
1324                         case TempoBarItem:
1325                         case MeterBarItem:
1326                         case VideoBarItem:
1327                         case TimecodeRulerItem:
1328                         case SamplesRulerItem:
1329                         case MinsecRulerItem:
1330                         case BBTRulerItem:
1331                                 popup_ruler_menu (where, item_type);
1332                                 break;
1333
1334                         case MarkerItem:
1335                                 marker_context_menu (&event->button, item);
1336                                 break;
1337
1338                         case TempoMarkerItem:
1339                                 tempo_or_meter_marker_context_menu (&event->button, item);
1340                                 break;
1341
1342                         case MeterMarkerItem:
1343                                 tempo_or_meter_marker_context_menu (&event->button, item);
1344                                 break;
1345
1346                         case CrossfadeViewItem:
1347                                 popup_track_context_menu (1, event->button.time, item_type, false);
1348                                 break;
1349
1350                         case ControlPointItem:
1351                                 popup_control_point_context_menu (item, event);
1352                                 break;
1353
1354                         case NoteItem:
1355                                 if (internal_editing()) {
1356                                         popup_note_context_menu (item, event);
1357                                 }
1358                                 break;
1359
1360                         default:
1361                                 break;
1362                         }
1363
1364                         return true;
1365                 }
1366         }
1367
1368         /* delete events get handled here */
1369
1370         Editing::MouseMode const eff = effective_mouse_mode ();
1371
1372         if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1373
1374                 switch (item_type) {
1375                 case TempoMarkerItem:
1376                         remove_tempo_marker (item);
1377                         break;
1378
1379                 case MeterMarkerItem:
1380                         remove_meter_marker (item);
1381                         break;
1382
1383                 case MarkerItem:
1384                         remove_marker (*item, event);
1385                         break;
1386
1387                 case RegionItem:
1388                         if (eff == MouseObject) {
1389                                 remove_clicked_region ();
1390                         }
1391                         break;
1392
1393                 case ControlPointItem:
1394                         remove_control_point (item);
1395                         break;
1396
1397                 case NoteItem:
1398                         remove_midi_note (item, event);
1399                         break;
1400
1401                 default:
1402                         break;
1403                 }
1404                 return true;
1405         }
1406
1407         switch (event->button.button) {
1408         case 1:
1409
1410                 switch (item_type) {
1411                 /* see comments in button_press_handler */
1412                 case PlayheadCursorItem:
1413                 case MarkerItem:
1414                 case GainLineItem:
1415                 case AutomationLineItem:
1416                 case StartSelectionTrimItem:
1417                 case EndSelectionTrimItem:
1418                         return true;
1419
1420                 case MarkerBarItem:
1421                         if (!_dragging_playhead) {
1422                                 snap_to_with_modifier (where, event, RoundNearest, true);
1423                                 mouse_add_new_marker (where);
1424                         }
1425                         return true;
1426
1427                 case CdMarkerBarItem:
1428                         if (!_dragging_playhead) {
1429                                 // if we get here then a dragged range wasn't done
1430                                 snap_to_with_modifier (where, event, RoundNearest, true);
1431                                 mouse_add_new_marker (where, true);
1432                         }
1433                         return true;
1434
1435                 case TempoBarItem:
1436                         if (!_dragging_playhead) {
1437                                 snap_to_with_modifier (where, event);
1438                                 mouse_add_new_tempo_event (where);
1439                         }
1440                         return true;
1441
1442                 case MeterBarItem:
1443                         if (!_dragging_playhead) {
1444                                 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1445                         }
1446                         return true;
1447                         break;
1448
1449                 case TimecodeRulerItem:
1450                 case SamplesRulerItem:
1451                 case MinsecRulerItem:
1452                 case BBTRulerItem:
1453                         return true;
1454                         break;
1455
1456                 default:
1457                         break;
1458                 }
1459
1460                 switch (eff) {
1461                 case MouseDraw:
1462                         switch (item_type) {
1463                         case RegionItem:
1464                         {
1465                                 /* check that we didn't drag before releasing, since
1466                                    its really annoying to create new control
1467                                    points when doing this.
1468                                 */
1469                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1470                                 if (!were_dragging && arv) {
1471                                         bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1472                                         arv->add_gain_point_event (item, event, with_guard_points);
1473                                 }
1474                                 return true;
1475                                 break;
1476                         }
1477
1478                         case AutomationTrackItem: {
1479                                 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1480                                 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1481                                 if (atv) {
1482                                         atv->add_automation_event (event, where, event->button.y, with_guard_points);
1483                                 }
1484                                 return true;
1485                                 break;
1486                         }
1487                         default:
1488                                 break;
1489                         }
1490                         break;
1491
1492                 case MouseAudition:
1493                         if (scrubbing_direction == 0) {
1494                                 /* no drag, just a click */
1495                                 switch (item_type) {
1496                                 case RegionItem:
1497                                         play_selected_region ();
1498                                         break;
1499                                 default:
1500                                         break;
1501                                 }
1502                         } else {
1503                                 /* make sure we stop */
1504                                 _session->request_transport_speed (0.0);
1505                         }
1506                         break;
1507
1508                 default:
1509                         break;
1510
1511                 }
1512
1513                 /* do any (de)selection operations that should occur on button release */
1514                 button_selection (item, event, item_type);
1515
1516                 return true;
1517                 break;
1518
1519
1520         case 2:
1521                 switch (eff) {
1522
1523                 case MouseObject:
1524                         switch (item_type) {
1525                         case RegionItem:
1526                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1527                                         raise_region ();
1528                                 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1529                                         lower_region ();
1530                                 } else {
1531                                         // Button2 click is unused
1532                                 }
1533                                 return true;
1534
1535                                 break;
1536
1537                         default:
1538                                 break;
1539                         }
1540                         break;
1541
1542                 case MouseDraw:
1543                         return true;
1544                         
1545                 case MouseRange:
1546                         // x_style_paste (where, 1.0);
1547                         return true;
1548                         break;
1549
1550                 default:
1551                         break;
1552                 }
1553
1554                 break;
1555
1556         case 3:
1557                 break;
1558
1559         default:
1560                 break;
1561         }
1562
1563         return false;
1564 }
1565
1566 bool
1567 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1568 {
1569         ControlPoint* cp;
1570         Marker * marker;
1571         double fraction;
1572         bool ret = true;
1573
1574         /* by the time we reach here, entered_regionview and entered trackview
1575          * will have already been set as appropriate. Things are done this 
1576          * way because this method isn't passed a pointer to a variable type of
1577          * thing that is entered (which may or may not be canvas item).
1578          * (e.g. the actual entered regionview)
1579          */
1580
1581         choose_canvas_cursor_on_entry (item_type);
1582
1583         switch (item_type) {
1584         case ControlPointItem:
1585                 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1586                         cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1587                         cp->show ();
1588
1589                         fraction = 1.0 - (cp->get_y() / cp->line().height());
1590
1591                         _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1592                         _verbose_cursor->show ();
1593                 }
1594                 break;
1595
1596         case GainLineItem:
1597                 if (mouse_mode == MouseDraw) {
1598                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1599                         if (line) {
1600                                 line->set_outline_color (ARDOUR_UI::config()->color ("entered gain line"));
1601                         }
1602                 }
1603                 break;
1604
1605         case AutomationLineItem:
1606                 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1607                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1608                         if (line) {
1609                                 line->set_outline_color (ARDOUR_UI::config()->color ("entered automation line"));
1610                         }
1611                 }
1612                 break;
1613
1614         case AutomationTrackItem:
1615                 AutomationTimeAxisView* atv;
1616                 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1617                         clear_entered_track = false;
1618                         set_entered_track (atv);
1619                 }
1620                 break;
1621
1622         case MarkerItem:
1623                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1624                         break;
1625                 }
1626                 entered_marker = marker;
1627                 marker->set_color_rgba (ARDOUR_UI::config()->color ("entered marker"));
1628                 // fall through
1629         case MeterMarkerItem:
1630         case TempoMarkerItem:
1631                 break;
1632
1633         case FadeInHandleItem:
1634         case FadeInTrimHandleItem:
1635                 if (mouse_mode == MouseObject) {
1636                         ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1637                         if (rect) {
1638                                 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1639                                 rect->set_fill_color (rv->get_fill_color());
1640                         }
1641                 }
1642                 break;
1643
1644         case FadeOutHandleItem:
1645         case FadeOutTrimHandleItem:
1646                 if (mouse_mode == MouseObject) {
1647                         ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1648                         if (rect) {
1649                                 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1650                                 rect->set_fill_color (rv->get_fill_color ());
1651                         }
1652                 }
1653                 break;
1654
1655         case FeatureLineItem:
1656         {
1657                 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1658                 line->set_outline_color (0xFF0000FF);
1659         }
1660         break;
1661
1662         case SelectionItem:
1663                 break;
1664
1665         default:
1666                 break;
1667         }
1668
1669         /* third pass to handle entered track status in a comprehensible way.
1670          */
1671
1672         switch (item_type) {
1673         case GainLineItem:
1674         case AutomationLineItem:
1675         case ControlPointItem:
1676                 /* these do not affect the current entered track state */
1677                 clear_entered_track = false;
1678                 break;
1679
1680         case AutomationTrackItem:
1681                 /* handled above already */
1682                 break;
1683
1684         default:
1685
1686                 break;
1687         }
1688
1689         return ret;
1690 }
1691
1692 bool
1693 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1694 {
1695         AutomationLine* al;
1696         Marker *marker;
1697         Location *loc;
1698         bool is_start;
1699         bool ret = true;
1700
1701         if (!_enter_stack.empty()) {
1702                 _enter_stack.pop_back();
1703         }
1704
1705         switch (item_type) {
1706         case ControlPointItem:
1707                 _verbose_cursor->hide (); 
1708                 break;
1709
1710         case GainLineItem:
1711         case AutomationLineItem:
1712                 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1713                 {
1714                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1715                         if (line) {
1716                                 line->set_outline_color (al->get_line_color());
1717                         }
1718                 }
1719                 break;
1720
1721         case MarkerItem:
1722                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1723                         break;
1724                 }
1725                 entered_marker = 0;
1726                 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1727                         location_flags_changed (loc);
1728                 }
1729                 // fall through
1730         case MeterMarkerItem:
1731         case TempoMarkerItem:
1732                 break;
1733
1734         case FadeInTrimHandleItem:
1735         case FadeOutTrimHandleItem:
1736         case FadeInHandleItem:
1737         case FadeOutHandleItem:
1738         {
1739                 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1740                 if (rect) {
1741                         rect->set_fill_color (ARDOUR_UI::config()->color ("inactive fade handle"));
1742                 }
1743         }
1744         break;
1745
1746         case AutomationTrackItem:
1747                 break;
1748
1749         case FeatureLineItem:
1750         {
1751                 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1752                 line->set_outline_color (ARDOUR_UI::config()->color ("zero line"));
1753         }
1754         break;
1755
1756         default:
1757                 break;
1758         }
1759
1760         return ret;
1761 }
1762
1763 void
1764 Editor::scrub (framepos_t frame, double current_x)
1765 {
1766         double delta;
1767
1768         if (scrubbing_direction == 0) {
1769                 /* first move */
1770                 _session->request_locate (frame, false);
1771                 _session->request_transport_speed (0.1);
1772                 scrubbing_direction = 1;
1773
1774         } else {
1775
1776                 if (last_scrub_x > current_x) {
1777
1778                         /* pointer moved to the left */
1779
1780                         if (scrubbing_direction > 0) {
1781
1782                                 /* we reversed direction to go backwards */
1783
1784                                 scrub_reversals++;
1785                                 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1786
1787                         } else {
1788
1789                                 /* still moving to the left (backwards) */
1790
1791                                 scrub_reversals = 0;
1792                                 scrub_reverse_distance = 0;
1793
1794                                 delta = 0.01 * (last_scrub_x - current_x);
1795                                 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1796                         }
1797
1798                 } else {
1799                         /* pointer moved to the right */
1800
1801                         if (scrubbing_direction < 0) {
1802                                 /* we reversed direction to go forward */
1803
1804                                 scrub_reversals++;
1805                                 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1806
1807                         } else {
1808                                 /* still moving to the right */
1809
1810                                 scrub_reversals = 0;
1811                                 scrub_reverse_distance = 0;
1812
1813                                 delta = 0.01 * (current_x - last_scrub_x);
1814                                 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
1815                         }
1816                 }
1817
1818                 /* if there have been more than 2 opposite motion moves detected, or one that moves
1819                    back more than 10 pixels, reverse direction
1820                 */
1821
1822                 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1823
1824                         if (scrubbing_direction > 0) {
1825                                 /* was forwards, go backwards */
1826                                 _session->request_transport_speed (-0.1);
1827                                 scrubbing_direction = -1;
1828                         } else {
1829                                 /* was backwards, go forwards */
1830                                 _session->request_transport_speed (0.1);
1831                                 scrubbing_direction = 1;
1832                         }
1833
1834                         scrub_reverse_distance = 0;
1835                         scrub_reversals = 0;
1836                 }
1837         }
1838
1839         last_scrub_x = current_x;
1840 }
1841
1842 bool
1843 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1844 {
1845         _last_motion_y = event->motion.y;
1846
1847         if (event->motion.is_hint) {
1848                 gint x, y;
1849
1850                 /* We call this so that MOTION_NOTIFY events continue to be
1851                    delivered to the canvas. We need to do this because we set
1852                    Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1853                    the density of the events, at the expense of a round-trip
1854                    to the server. Given that this will mostly occur on cases
1855                    where DISPLAY = :0.0, and given the cost of what the motion
1856                    event might do, its a good tradeoff.
1857                 */
1858
1859                 _track_canvas->get_pointer (x, y);
1860         }
1861
1862         if (current_stepping_trackview) {
1863                 /* don't keep the persistent stepped trackview if the mouse moves */
1864                 current_stepping_trackview = 0;
1865                 step_timeout.disconnect ();
1866         }
1867         
1868         if (_session && _session->actively_recording()) {
1869                 /* Sorry. no dragging stuff around while we record */
1870                 return true;
1871         }
1872         
1873         update_join_object_range_location (event->motion.y);
1874         
1875         if (_drags->active ()) {
1876                 return _drags->motion_handler (event, from_autoscroll);
1877         }
1878
1879         return false;
1880 }
1881
1882 bool
1883 Editor::can_remove_control_point (ArdourCanvas::Item* item)
1884 {
1885         ControlPoint* control_point;
1886
1887         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1888                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1889                 abort(); /*NOTREACHED*/
1890         }
1891
1892         AutomationLine& line = control_point->line ();
1893         if (dynamic_cast<AudioRegionGainLine*> (&line)) {
1894                 /* we shouldn't remove the first or last gain point in region gain lines */
1895                 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
1896                         return false;
1897                 }
1898         }
1899
1900         return true;
1901 }
1902
1903 void
1904 Editor::remove_control_point (ArdourCanvas::Item* item)
1905 {
1906         if (!can_remove_control_point (item)) {
1907                 return;
1908         }
1909
1910         ControlPoint* control_point;
1911
1912         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1913                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1914                 abort(); /*NOTREACHED*/
1915         }
1916
1917         control_point->line().remove_point (*control_point);
1918 }
1919
1920 void
1921 Editor::edit_control_point (ArdourCanvas::Item* item)
1922 {
1923         ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1924
1925         if (p == 0) {
1926                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1927                 abort(); /*NOTREACHED*/
1928         }
1929
1930         ControlPointDialog d (p);
1931         ensure_float (d);
1932
1933         if (d.run () != RESPONSE_ACCEPT) {
1934                 return;
1935         }
1936
1937         p->line().modify_point_y (*p, d.get_y_fraction ());
1938 }
1939
1940 void
1941 Editor::edit_notes (MidiRegionView* mrv)
1942 {
1943         MidiRegionView::Selection const & s = mrv->selection();
1944
1945         if (s.empty ()) {
1946                 return;
1947         }
1948
1949         EditNoteDialog* d = new EditNoteDialog (mrv, s);
1950         d->show_all ();
1951         ensure_float (*d);
1952
1953         d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
1954 }
1955
1956 void
1957 Editor::note_edit_done (int r, EditNoteDialog* d)
1958 {
1959         d->done (r);
1960         delete d;
1961 }
1962
1963 void
1964 Editor::visible_order_range (int* low, int* high) const
1965 {
1966         *low = TimeAxisView::max_order ();
1967         *high = 0;
1968
1969         for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1970
1971                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1972
1973                 if (!rtv->hidden()) {
1974
1975                         if (*high < rtv->order()) {
1976                                 *high = rtv->order ();
1977                         }
1978
1979                         if (*low > rtv->order()) {
1980                                 *low = rtv->order ();
1981                         }
1982                 }
1983         }
1984 }
1985
1986 void
1987 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1988 {
1989         /* Either add to or set the set the region selection, unless
1990            this is an alignment click (control used)
1991         */
1992
1993         if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1994                 TimeAxisView* tv = &rv.get_time_axis_view();
1995                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1996                 double speed = 1.0;
1997                 if (rtv && rtv->is_track()) {
1998                         speed = rtv->track()->speed();
1999                 }
2000
2001                 framepos_t where = get_preferred_edit_position();
2002
2003                 if (where >= 0) {
2004
2005                         if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2006
2007                                 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2008
2009                         } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2010
2011                                 align_region (rv.region(), End, (framepos_t) (where * speed));
2012
2013                         } else {
2014
2015                                 align_region (rv.region(), Start, (framepos_t) (where * speed));
2016                         }
2017                 }
2018         }
2019 }
2020
2021 void
2022 Editor::collect_new_region_view (RegionView* rv)
2023 {
2024         latest_regionviews.push_back (rv);
2025 }
2026
2027 void
2028 Editor::collect_and_select_new_region_view (RegionView* rv)
2029 {
2030         selection->add(rv);
2031         latest_regionviews.push_back (rv);
2032 }
2033
2034 void
2035 Editor::cancel_selection ()
2036 {
2037         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2038                 (*i)->hide_selection ();
2039         }
2040
2041         selection->clear ();
2042         clicked_selection = 0;
2043 }
2044
2045 void
2046 Editor::cancel_time_selection ()
2047 {
2048         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2049                 (*i)->hide_selection ();
2050         }
2051         selection->time.clear ();
2052         clicked_selection = 0;
2053 }       
2054
2055 void
2056 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2057 {
2058         RegionView* rv = clicked_regionview;
2059
2060         /* Choose action dependant on which button was pressed */
2061         switch (event->button.button) {
2062         case 1:
2063                 begin_reversible_command (_("start point trim"));
2064
2065                 if (selection->selected (rv)) {
2066                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2067                              i != selection->regions.by_layer().end(); ++i)
2068                         {
2069                                 if (!(*i)->region()->locked()) {
2070                                         (*i)->region()->clear_changes ();
2071                                         (*i)->region()->trim_front (new_bound);
2072                                         _session->add_command(new StatefulDiffCommand ((*i)->region()));
2073                                 }
2074                         }
2075
2076                 } else {
2077                         if (!rv->region()->locked()) {
2078                                 rv->region()->clear_changes ();
2079                                 rv->region()->trim_front (new_bound);
2080                                 _session->add_command(new StatefulDiffCommand (rv->region()));
2081                         }
2082                 }
2083
2084                 commit_reversible_command();
2085
2086                 break;
2087         case 2:
2088                 begin_reversible_command (_("End point trim"));
2089
2090                 if (selection->selected (rv)) {
2091
2092                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2093                         {
2094                                 if (!(*i)->region()->locked()) {
2095                                         (*i)->region()->clear_changes();
2096                                         (*i)->region()->trim_end (new_bound);
2097                                         _session->add_command(new StatefulDiffCommand ((*i)->region()));
2098                                 }
2099                         }
2100
2101                 } else {
2102
2103                         if (!rv->region()->locked()) {
2104                                 rv->region()->clear_changes ();
2105                                 rv->region()->trim_end (new_bound);
2106                                 _session->add_command (new StatefulDiffCommand (rv->region()));
2107                         }
2108                 }
2109
2110                 commit_reversible_command();
2111
2112                 break;
2113         default:
2114                 break;
2115         }
2116 }
2117
2118 void
2119 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2120 {
2121         Marker* marker;
2122         bool is_start;
2123
2124         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2125                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2126                 abort(); /*NOTREACHED*/
2127         }
2128
2129         Location* location = find_location_from_marker (marker, is_start);
2130         location->set_hidden (true, this);
2131 }
2132
2133 gint
2134 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2135 {
2136         using namespace Gtkmm2ext;
2137
2138         ArdourPrompter prompter (false);
2139
2140         prompter.set_prompt (_("Name for region:"));
2141         prompter.set_initial_text (clicked_regionview->region()->name());
2142         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2143         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2144         prompter.show_all ();
2145         switch (prompter.run ()) {
2146         case Gtk::RESPONSE_ACCEPT:
2147                 string str;
2148                 prompter.get_result(str);
2149                 if (str.length()) {
2150                         clicked_regionview->region()->set_name (str);
2151                 }
2152                 break;
2153         }
2154         return true;
2155 }
2156
2157
2158 void
2159 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2160 {
2161         /* no brushing without a useful snap setting */
2162
2163         switch (_snap_mode) {
2164         case SnapMagnetic:
2165                 return; /* can't work because it allows region to be placed anywhere */
2166         default:
2167                 break; /* OK */
2168         }
2169
2170         switch (_snap_type) {
2171         case SnapToMark:
2172                 return;
2173
2174         default:
2175                 break;
2176         }
2177
2178         /* don't brush a copy over the original */
2179
2180         if (pos == rv->region()->position()) {
2181                 return;
2182         }
2183
2184         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2185
2186         if (rtv == 0 || !rtv->is_track()) {
2187                 return;
2188         }
2189
2190         boost::shared_ptr<Playlist> playlist = rtv->playlist();
2191         double speed = rtv->track()->speed();
2192
2193         playlist->clear_changes ();
2194         boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2195         playlist->add_region (new_region, (framepos_t) (pos * speed));
2196         _session->add_command (new StatefulDiffCommand (playlist));
2197
2198         // playlist is frozen, so we have to update manually XXX this is disgusting
2199
2200         playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2201 }
2202
2203 gint
2204 Editor::track_height_step_timeout ()
2205 {
2206         if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2207                 current_stepping_trackview = 0;
2208                 return false;
2209         }
2210         return true;
2211 }
2212
2213 void
2214 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2215 {
2216         assert (region_view);
2217
2218         if (!region_view->region()->playlist()) {
2219                 return;
2220         }
2221
2222         switch (Config->get_edit_mode()) {
2223                 case Splice:
2224                         _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2225                         break;
2226                 case Ripple:
2227                         _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2228                         break;
2229                 default:
2230                         _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2231                         break;
2232
2233         }
2234 }
2235
2236 void
2237 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2238 {
2239         assert (region_view);
2240
2241         if (!region_view->region()->playlist()) {
2242                 return;
2243         }
2244
2245         _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2246 }
2247
2248 void
2249 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2250 {
2251         assert (region_view);
2252
2253         if (!region_view->region()->playlist()) {
2254                 return;
2255         }
2256
2257         if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2258                 return;
2259         }
2260
2261         _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2262
2263         begin_reversible_command (Operations::drag_region_brush);
2264 }
2265
2266 /** Start a grab where a time range is selected, track(s) are selected, and the
2267  *  user clicks and drags a region with a modifier in order to create a new region containing
2268  *  the section of the clicked region that lies within the time range.
2269  */
2270 void
2271 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2272 {
2273         if (clicked_regionview == 0) {
2274                 return;
2275         }
2276
2277         /* lets try to create new Region for the selection */
2278
2279         vector<boost::shared_ptr<Region> > new_regions;
2280         create_region_from_selection (new_regions);
2281
2282         if (new_regions.empty()) {
2283                 return;
2284         }
2285
2286         /* XXX fix me one day to use all new regions */
2287
2288         boost::shared_ptr<Region> region (new_regions.front());
2289
2290         /* add it to the current stream/playlist.
2291
2292            tricky: the streamview for the track will add a new regionview. we will
2293            catch the signal it sends when it creates the regionview to
2294            set the regionview we want to then drag.
2295         */
2296
2297         latest_regionviews.clear();
2298         sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2299
2300         /* A selection grab currently creates two undo/redo operations, one for
2301            creating the new region and another for moving it.
2302         */
2303
2304         begin_reversible_command (Operations::selection_grab);
2305
2306         boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2307
2308         playlist->clear_changes ();
2309         clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2310         _session->add_command(new StatefulDiffCommand (playlist));
2311
2312         c.disconnect ();
2313
2314         if (latest_regionviews.empty()) {
2315                 /* something went wrong */
2316                 return;
2317         }
2318
2319         /* we need to deselect all other regionviews, and select this one
2320            i'm ignoring undo stuff, because the region creation will take care of it
2321         */
2322
2323         selection->set (latest_regionviews);
2324
2325         commit_reversible_command ();
2326
2327         _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2328 }
2329
2330 void
2331 Editor::escape ()
2332 {
2333         if (_drags->active ()) {
2334                 _drags->abort ();
2335         } else {
2336                 selection->clear ();
2337         }
2338
2339         reset_focus ();
2340 }
2341
2342 /** Update _join_object_range_state which indicate whether we are over the top
2343  *  or bottom half of a route view, used by the `join object/range' tool
2344  *  mode. Coordinates in canvas space.
2345  */
2346 void
2347 Editor::update_join_object_range_location (double y)
2348 {
2349         if (!get_smart_mode()) {
2350                 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2351                 return;
2352         }
2353
2354         JoinObjectRangeState const old = _join_object_range_state;
2355
2356         if (mouse_mode == MouseObject) {
2357                 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2358         } else if (mouse_mode == MouseRange) {
2359                 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2360         }
2361
2362         if (entered_regionview) {
2363
2364                 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2365                 double const c = item_space.y / entered_regionview->height();
2366
2367                 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2368
2369                 Editor::EnterContext* ctx = get_enter_context(RegionItem);
2370                 if (_join_object_range_state != old && ctx) {
2371                         ctx->cursor_ctx->change(which_track_cursor());
2372                 }
2373
2374         } else if (entered_track) {
2375
2376                 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2377                 
2378                 if (entered_route_view) {
2379
2380                         double cx = 0;
2381                         double cy = y;
2382
2383                         entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2384
2385                         double track_height = entered_route_view->view()->child_height();
2386                         if (ARDOUR_UI::config()->get_show_name_highlight()) {
2387                                 track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2388                         }
2389                         double const c = cy / track_height;
2390
2391
2392                         if (c <= 0.5) {
2393                                 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2394                         } else {
2395                                 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2396                         }
2397
2398                 } else {
2399                         /* Other kinds of tracks use object mode */
2400                         _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2401                 }
2402
2403                 Editor::EnterContext* ctx = get_enter_context(StreamItem);
2404                 if (_join_object_range_state != old && ctx) {
2405                         ctx->cursor_ctx->change(which_track_cursor());
2406                 }
2407         }
2408 }
2409
2410 Editing::MouseMode
2411 Editor::effective_mouse_mode () const
2412 {
2413         if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2414                 return MouseObject;
2415         } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2416                 return MouseRange;
2417         }
2418
2419         return mouse_mode;
2420 }
2421
2422 void
2423 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2424 {
2425         NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2426         assert (e);
2427
2428         e->region_view().delete_note (e->note ());
2429 }
2430
2431 /** Obtain the pointer position in canvas coordinates */
2432 void
2433 Editor::get_pointer_position (double& x, double& y) const
2434 {
2435         int px, py;
2436         _track_canvas->get_pointer (px, py);
2437         _track_canvas->window_to_canvas (px, py, x, y);
2438 }