Fix some ignored region selection changes (probably others).
[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         /* see if we're finishing a drag */
1195
1196         bool were_dragging = false;
1197         if (_drags->active ()) {
1198                 bool const r = _drags->end_grab (event);
1199                 if (r) {
1200                         /* grab dragged, so do nothing else */
1201                         return true;
1202                 }
1203
1204                 were_dragging = true;
1205         }
1206
1207         update_region_layering_order_editor ();
1208
1209         /* edit events get handled here */
1210
1211         if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1212                 switch (item_type) {
1213                 case RegionItem:
1214                         show_region_properties ();
1215                         break;
1216
1217                 case TempoMarkerItem: {
1218                         Marker* marker;
1219                         TempoMarker* tempo_marker;
1220                         
1221                         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1222                                 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1223                                 abort(); /*NOTREACHED*/
1224                         }
1225                         
1226                         if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1227                                 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1228                                 abort(); /*NOTREACHED*/
1229                         }
1230                         
1231                         edit_tempo_marker (*tempo_marker);
1232                         break;
1233                 }
1234
1235                 case MeterMarkerItem: {
1236                         Marker* marker;
1237                         MeterMarker* meter_marker;
1238                         
1239                         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1240                                 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1241                                 abort(); /*NOTREACHED*/
1242                         }
1243                         
1244                         if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1245                                 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1246                                 abort(); /*NOTREACHED*/
1247                         }
1248                         edit_meter_marker (*meter_marker);
1249                         break;
1250                 }
1251
1252                 case RegionViewName:
1253                         if (clicked_regionview->name_active()) {
1254                                 return mouse_rename_region (item, event);
1255                         }
1256                         break;
1257
1258                 case ControlPointItem:
1259                         edit_control_point (item);
1260                         break;
1261
1262                 default:
1263                         break;
1264                 }
1265                 return true;
1266         }
1267
1268         /* context menu events get handled here */
1269         if (Keyboard::is_context_menu_event (&event->button)) {
1270
1271                 context_click_event = *event;
1272
1273                 if (!_drags->active ()) {
1274
1275                         /* no matter which button pops up the context menu, tell the menu
1276                            widget to use button 1 to drive menu selection.
1277                         */
1278
1279                         switch (item_type) {
1280                         case FadeInItem:
1281                         case FadeInHandleItem:
1282                         case FadeInTrimHandleItem:
1283                         case StartCrossFadeItem:
1284                                 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1285                                 break;
1286
1287                         case FadeOutItem:
1288                         case FadeOutHandleItem:
1289                         case FadeOutTrimHandleItem:
1290                         case EndCrossFadeItem:
1291                                 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1292                                 break;
1293
1294                         case LeftFrameHandle:
1295                         case RightFrameHandle:
1296                                 break;
1297
1298                         case StreamItem:
1299                                 popup_track_context_menu (1, event->button.time, item_type, false);
1300                                 break;
1301
1302                         case RegionItem:
1303                         case RegionViewNameHighlight:
1304                         case RegionViewName:
1305                                 popup_track_context_menu (1, event->button.time, item_type, false);
1306                                 break;
1307
1308                         case SelectionItem:
1309                                 popup_track_context_menu (1, event->button.time, item_type, true);
1310                                 break;
1311                                 
1312                         case AutomationTrackItem:
1313                                 popup_track_context_menu (1, event->button.time, item_type, false);
1314                                 break;
1315
1316                         case MarkerBarItem:
1317                         case RangeMarkerBarItem:
1318                         case TransportMarkerBarItem:
1319                         case CdMarkerBarItem:
1320                         case TempoBarItem:
1321                         case MeterBarItem:
1322                         case VideoBarItem:
1323                         case TimecodeRulerItem:
1324                         case SamplesRulerItem:
1325                         case MinsecRulerItem:
1326                         case BBTRulerItem:
1327                                 popup_ruler_menu (where, item_type);
1328                                 break;
1329
1330                         case MarkerItem:
1331                                 marker_context_menu (&event->button, item);
1332                                 break;
1333
1334                         case TempoMarkerItem:
1335                                 tempo_or_meter_marker_context_menu (&event->button, item);
1336                                 break;
1337
1338                         case MeterMarkerItem:
1339                                 tempo_or_meter_marker_context_menu (&event->button, item);
1340                                 break;
1341
1342                         case CrossfadeViewItem:
1343                                 popup_track_context_menu (1, event->button.time, item_type, false);
1344                                 break;
1345
1346                         case ControlPointItem:
1347                                 popup_control_point_context_menu (item, event);
1348                                 break;
1349
1350                         case NoteItem:
1351                                 if (internal_editing()) {
1352                                         popup_note_context_menu (item, event);
1353                                 }
1354                                 break;
1355
1356                         default:
1357                                 break;
1358                         }
1359
1360                         return true;
1361                 }
1362         }
1363
1364         /* delete events get handled here */
1365
1366         Editing::MouseMode const eff = effective_mouse_mode ();
1367
1368         if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1369
1370                 switch (item_type) {
1371                 case TempoMarkerItem:
1372                         remove_tempo_marker (item);
1373                         break;
1374
1375                 case MeterMarkerItem:
1376                         remove_meter_marker (item);
1377                         break;
1378
1379                 case MarkerItem:
1380                         remove_marker (*item, event);
1381                         break;
1382
1383                 case RegionItem:
1384                         if (eff == MouseObject) {
1385                                 remove_clicked_region ();
1386                         }
1387                         break;
1388
1389                 case ControlPointItem:
1390                         remove_control_point (item);
1391                         break;
1392
1393                 case NoteItem:
1394                         remove_midi_note (item, event);
1395                         break;
1396
1397                 default:
1398                         break;
1399                 }
1400                 return true;
1401         }
1402
1403         switch (event->button.button) {
1404         case 1:
1405
1406                 switch (item_type) {
1407                 /* see comments in button_press_handler */
1408                 case PlayheadCursorItem:
1409                 case MarkerItem:
1410                 case GainLineItem:
1411                 case AutomationLineItem:
1412                 case StartSelectionTrimItem:
1413                 case EndSelectionTrimItem:
1414                         return true;
1415
1416                 case MarkerBarItem:
1417                         if (!_dragging_playhead) {
1418                                 snap_to_with_modifier (where, event, RoundNearest, true);
1419                                 mouse_add_new_marker (where);
1420                         }
1421                         return true;
1422
1423                 case CdMarkerBarItem:
1424                         if (!_dragging_playhead) {
1425                                 // if we get here then a dragged range wasn't done
1426                                 snap_to_with_modifier (where, event, RoundNearest, true);
1427                                 mouse_add_new_marker (where, true);
1428                         }
1429                         return true;
1430
1431                 case TempoBarItem:
1432                         if (!_dragging_playhead) {
1433                                 snap_to_with_modifier (where, event);
1434                                 mouse_add_new_tempo_event (where);
1435                         }
1436                         return true;
1437
1438                 case MeterBarItem:
1439                         if (!_dragging_playhead) {
1440                                 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1441                         }
1442                         return true;
1443                         break;
1444
1445                 case TimecodeRulerItem:
1446                 case SamplesRulerItem:
1447                 case MinsecRulerItem:
1448                 case BBTRulerItem:
1449                         return true;
1450                         break;
1451
1452                 default:
1453                         break;
1454                 }
1455
1456                 switch (eff) {
1457                 case MouseDraw:
1458                         switch (item_type) {
1459                         case RegionItem:
1460                         {
1461                                 /* check that we didn't drag before releasing, since
1462                                    its really annoying to create new control
1463                                    points when doing this.
1464                                 */
1465                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1466                                 if (!were_dragging && arv) {
1467                                         bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1468                                         arv->add_gain_point_event (item, event, with_guard_points);
1469                                 }
1470                                 return true;
1471                                 break;
1472                         }
1473
1474                         case AutomationTrackItem: {
1475                                 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1476                                 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1477                                 if (atv) {
1478                                         atv->add_automation_event (event, where, event->button.y, with_guard_points);
1479                                 }
1480                                 return true;
1481                                 break;
1482                         }
1483                         default:
1484                                 break;
1485                         }
1486                         break;
1487
1488                 case MouseAudition:
1489                         if (scrubbing_direction == 0) {
1490                                 /* no drag, just a click */
1491                                 switch (item_type) {
1492                                 case RegionItem:
1493                                         play_selected_region ();
1494                                         break;
1495                                 default:
1496                                         break;
1497                                 }
1498                         } else {
1499                                 /* make sure we stop */
1500                                 _session->request_transport_speed (0.0);
1501                         }
1502                         break;
1503
1504                 default:
1505                         break;
1506
1507                 }
1508
1509                 /* do any (de)selection operations that should occur on button release */
1510                 button_selection (item, event, item_type);
1511
1512                 return true;
1513                 break;
1514
1515
1516         case 2:
1517                 switch (eff) {
1518
1519                 case MouseObject:
1520                         switch (item_type) {
1521                         case RegionItem:
1522                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1523                                         raise_region ();
1524                                 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1525                                         lower_region ();
1526                                 } else {
1527                                         // Button2 click is unused
1528                                 }
1529                                 return true;
1530
1531                                 break;
1532
1533                         default:
1534                                 break;
1535                         }
1536                         break;
1537
1538                 case MouseDraw:
1539                         return true;
1540                         
1541                 case MouseRange:
1542                         // x_style_paste (where, 1.0);
1543                         return true;
1544                         break;
1545
1546                 default:
1547                         break;
1548                 }
1549
1550                 break;
1551
1552         case 3:
1553                 break;
1554
1555         default:
1556                 break;
1557         }
1558
1559         return false;
1560 }
1561
1562 bool
1563 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1564 {
1565         ControlPoint* cp;
1566         Marker * marker;
1567         double fraction;
1568         bool ret = true;
1569
1570         /* by the time we reach here, entered_regionview and entered trackview
1571          * will have already been set as appropriate. Things are done this 
1572          * way because this method isn't passed a pointer to a variable type of
1573          * thing that is entered (which may or may not be canvas item).
1574          * (e.g. the actual entered regionview)
1575          */
1576
1577         choose_canvas_cursor_on_entry (item_type);
1578
1579         switch (item_type) {
1580         case ControlPointItem:
1581                 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1582                         cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1583                         cp->show ();
1584
1585                         fraction = 1.0 - (cp->get_y() / cp->line().height());
1586
1587                         _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1588                         _verbose_cursor->show ();
1589                 }
1590                 break;
1591
1592         case GainLineItem:
1593                 if (mouse_mode == MouseDraw) {
1594                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1595                         if (line) {
1596                                 line->set_outline_color (ARDOUR_UI::config()->color ("entered gain line"));
1597                         }
1598                 }
1599                 break;
1600
1601         case AutomationLineItem:
1602                 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1603                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1604                         if (line) {
1605                                 line->set_outline_color (ARDOUR_UI::config()->color ("entered automation line"));
1606                         }
1607                 }
1608                 break;
1609
1610         case AutomationTrackItem:
1611                 AutomationTimeAxisView* atv;
1612                 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1613                         clear_entered_track = false;
1614                         set_entered_track (atv);
1615                 }
1616                 break;
1617
1618         case MarkerItem:
1619                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1620                         break;
1621                 }
1622                 entered_marker = marker;
1623                 marker->set_color_rgba (ARDOUR_UI::config()->color ("entered marker"));
1624                 // fall through
1625         case MeterMarkerItem:
1626         case TempoMarkerItem:
1627                 break;
1628
1629         case FadeInHandleItem:
1630         case FadeInTrimHandleItem:
1631                 if (mouse_mode == MouseObject) {
1632                         ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1633                         if (rect) {
1634                                 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1635                                 rect->set_fill_color (rv->get_fill_color());
1636                         }
1637                 }
1638                 break;
1639
1640         case FadeOutHandleItem:
1641         case FadeOutTrimHandleItem:
1642                 if (mouse_mode == MouseObject) {
1643                         ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1644                         if (rect) {
1645                                 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1646                                 rect->set_fill_color (rv->get_fill_color ());
1647                         }
1648                 }
1649                 break;
1650
1651         case FeatureLineItem:
1652         {
1653                 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1654                 line->set_outline_color (0xFF0000FF);
1655         }
1656         break;
1657
1658         case SelectionItem:
1659                 break;
1660
1661         default:
1662                 break;
1663         }
1664
1665         /* third pass to handle entered track status in a comprehensible way.
1666          */
1667
1668         switch (item_type) {
1669         case GainLineItem:
1670         case AutomationLineItem:
1671         case ControlPointItem:
1672                 /* these do not affect the current entered track state */
1673                 clear_entered_track = false;
1674                 break;
1675
1676         case AutomationTrackItem:
1677                 /* handled above already */
1678                 break;
1679
1680         default:
1681
1682                 break;
1683         }
1684
1685         return ret;
1686 }
1687
1688 bool
1689 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1690 {
1691         AutomationLine* al;
1692         Marker *marker;
1693         Location *loc;
1694         bool is_start;
1695         bool ret = true;
1696
1697         if (!_enter_stack.empty()) {
1698                 _enter_stack.pop_back();
1699         }
1700
1701         switch (item_type) {
1702         case ControlPointItem:
1703                 _verbose_cursor->hide (); 
1704                 break;
1705
1706         case GainLineItem:
1707         case AutomationLineItem:
1708                 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1709                 {
1710                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1711                         if (line) {
1712                                 line->set_outline_color (al->get_line_color());
1713                         }
1714                 }
1715                 break;
1716
1717         case MarkerItem:
1718                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1719                         break;
1720                 }
1721                 entered_marker = 0;
1722                 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1723                         location_flags_changed (loc);
1724                 }
1725                 // fall through
1726         case MeterMarkerItem:
1727         case TempoMarkerItem:
1728                 break;
1729
1730         case FadeInTrimHandleItem:
1731         case FadeOutTrimHandleItem:
1732         case FadeInHandleItem:
1733         case FadeOutHandleItem:
1734         {
1735                 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1736                 if (rect) {
1737                         rect->set_fill_color (ARDOUR_UI::config()->color ("inactive fade handle"));
1738                 }
1739         }
1740         break;
1741
1742         case AutomationTrackItem:
1743                 break;
1744
1745         case FeatureLineItem:
1746         {
1747                 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1748                 line->set_outline_color (ARDOUR_UI::config()->color ("zero line"));
1749         }
1750         break;
1751
1752         default:
1753                 break;
1754         }
1755
1756         return ret;
1757 }
1758
1759 void
1760 Editor::scrub (framepos_t frame, double current_x)
1761 {
1762         double delta;
1763
1764         if (scrubbing_direction == 0) {
1765                 /* first move */
1766                 _session->request_locate (frame, false);
1767                 _session->request_transport_speed (0.1);
1768                 scrubbing_direction = 1;
1769
1770         } else {
1771
1772                 if (last_scrub_x > current_x) {
1773
1774                         /* pointer moved to the left */
1775
1776                         if (scrubbing_direction > 0) {
1777
1778                                 /* we reversed direction to go backwards */
1779
1780                                 scrub_reversals++;
1781                                 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1782
1783                         } else {
1784
1785                                 /* still moving to the left (backwards) */
1786
1787                                 scrub_reversals = 0;
1788                                 scrub_reverse_distance = 0;
1789
1790                                 delta = 0.01 * (last_scrub_x - current_x);
1791                                 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1792                         }
1793
1794                 } else {
1795                         /* pointer moved to the right */
1796
1797                         if (scrubbing_direction < 0) {
1798                                 /* we reversed direction to go forward */
1799
1800                                 scrub_reversals++;
1801                                 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1802
1803                         } else {
1804                                 /* still moving to the right */
1805
1806                                 scrub_reversals = 0;
1807                                 scrub_reverse_distance = 0;
1808
1809                                 delta = 0.01 * (current_x - last_scrub_x);
1810                                 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
1811                         }
1812                 }
1813
1814                 /* if there have been more than 2 opposite motion moves detected, or one that moves
1815                    back more than 10 pixels, reverse direction
1816                 */
1817
1818                 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1819
1820                         if (scrubbing_direction > 0) {
1821                                 /* was forwards, go backwards */
1822                                 _session->request_transport_speed (-0.1);
1823                                 scrubbing_direction = -1;
1824                         } else {
1825                                 /* was backwards, go forwards */
1826                                 _session->request_transport_speed (0.1);
1827                                 scrubbing_direction = 1;
1828                         }
1829
1830                         scrub_reverse_distance = 0;
1831                         scrub_reversals = 0;
1832                 }
1833         }
1834
1835         last_scrub_x = current_x;
1836 }
1837
1838 bool
1839 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1840 {
1841         _last_motion_y = event->motion.y;
1842
1843         if (event->motion.is_hint) {
1844                 gint x, y;
1845
1846                 /* We call this so that MOTION_NOTIFY events continue to be
1847                    delivered to the canvas. We need to do this because we set
1848                    Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1849                    the density of the events, at the expense of a round-trip
1850                    to the server. Given that this will mostly occur on cases
1851                    where DISPLAY = :0.0, and given the cost of what the motion
1852                    event might do, its a good tradeoff.
1853                 */
1854
1855                 _track_canvas->get_pointer (x, y);
1856         }
1857
1858         if (current_stepping_trackview) {
1859                 /* don't keep the persistent stepped trackview if the mouse moves */
1860                 current_stepping_trackview = 0;
1861                 step_timeout.disconnect ();
1862         }
1863         
1864         if (_session && _session->actively_recording()) {
1865                 /* Sorry. no dragging stuff around while we record */
1866                 return true;
1867         }
1868         
1869         update_join_object_range_location (event->motion.y);
1870         
1871         if (_drags->active ()) {
1872                 return _drags->motion_handler (event, from_autoscroll);
1873         }
1874
1875         return false;
1876 }
1877
1878 bool
1879 Editor::can_remove_control_point (ArdourCanvas::Item* item)
1880 {
1881         ControlPoint* control_point;
1882
1883         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1884                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1885                 abort(); /*NOTREACHED*/
1886         }
1887
1888         AutomationLine& line = control_point->line ();
1889         if (dynamic_cast<AudioRegionGainLine*> (&line)) {
1890                 /* we shouldn't remove the first or last gain point in region gain lines */
1891                 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
1892                         return false;
1893                 }
1894         }
1895
1896         return true;
1897 }
1898
1899 void
1900 Editor::remove_control_point (ArdourCanvas::Item* item)
1901 {
1902         if (!can_remove_control_point (item)) {
1903                 return;
1904         }
1905
1906         ControlPoint* control_point;
1907
1908         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1909                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1910                 abort(); /*NOTREACHED*/
1911         }
1912
1913         control_point->line().remove_point (*control_point);
1914 }
1915
1916 void
1917 Editor::edit_control_point (ArdourCanvas::Item* item)
1918 {
1919         ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1920
1921         if (p == 0) {
1922                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1923                 abort(); /*NOTREACHED*/
1924         }
1925
1926         ControlPointDialog d (p);
1927         ensure_float (d);
1928
1929         if (d.run () != RESPONSE_ACCEPT) {
1930                 return;
1931         }
1932
1933         p->line().modify_point_y (*p, d.get_y_fraction ());
1934 }
1935
1936 void
1937 Editor::edit_notes (MidiRegionView* mrv)
1938 {
1939         MidiRegionView::Selection const & s = mrv->selection();
1940
1941         if (s.empty ()) {
1942                 return;
1943         }
1944
1945         EditNoteDialog* d = new EditNoteDialog (mrv, s);
1946         d->show_all ();
1947         ensure_float (*d);
1948
1949         d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
1950 }
1951
1952 void
1953 Editor::note_edit_done (int r, EditNoteDialog* d)
1954 {
1955         d->done (r);
1956         delete d;
1957 }
1958
1959 void
1960 Editor::visible_order_range (int* low, int* high) const
1961 {
1962         *low = TimeAxisView::max_order ();
1963         *high = 0;
1964
1965         for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1966
1967                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1968
1969                 if (!rtv->hidden()) {
1970
1971                         if (*high < rtv->order()) {
1972                                 *high = rtv->order ();
1973                         }
1974
1975                         if (*low > rtv->order()) {
1976                                 *low = rtv->order ();
1977                         }
1978                 }
1979         }
1980 }
1981
1982 void
1983 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1984 {
1985         /* Either add to or set the set the region selection, unless
1986            this is an alignment click (control used)
1987         */
1988
1989         if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1990                 TimeAxisView* tv = &rv.get_time_axis_view();
1991                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1992                 double speed = 1.0;
1993                 if (rtv && rtv->is_track()) {
1994                         speed = rtv->track()->speed();
1995                 }
1996
1997                 framepos_t where = get_preferred_edit_position();
1998
1999                 if (where >= 0) {
2000
2001                         if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2002
2003                                 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2004
2005                         } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2006
2007                                 align_region (rv.region(), End, (framepos_t) (where * speed));
2008
2009                         } else {
2010
2011                                 align_region (rv.region(), Start, (framepos_t) (where * speed));
2012                         }
2013                 }
2014         }
2015 }
2016
2017 void
2018 Editor::collect_new_region_view (RegionView* rv)
2019 {
2020         latest_regionviews.push_back (rv);
2021 }
2022
2023 void
2024 Editor::collect_and_select_new_region_view (RegionView* rv)
2025 {
2026         selection->add(rv);
2027         latest_regionviews.push_back (rv);
2028 }
2029
2030 void
2031 Editor::cancel_selection ()
2032 {
2033         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2034                 (*i)->hide_selection ();
2035         }
2036
2037         selection->clear ();
2038         clicked_selection = 0;
2039 }
2040
2041 void
2042 Editor::cancel_time_selection ()
2043 {
2044         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2045                 (*i)->hide_selection ();
2046         }
2047         selection->time.clear ();
2048         clicked_selection = 0;
2049 }       
2050
2051 void
2052 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2053 {
2054         RegionView* rv = clicked_regionview;
2055
2056         /* Choose action dependant on which button was pressed */
2057         switch (event->button.button) {
2058         case 1:
2059                 begin_reversible_command (_("start point trim"));
2060
2061                 if (selection->selected (rv)) {
2062                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2063                              i != selection->regions.by_layer().end(); ++i)
2064                         {
2065                                 if (!(*i)->region()->locked()) {
2066                                         (*i)->region()->clear_changes ();
2067                                         (*i)->region()->trim_front (new_bound);
2068                                         _session->add_command(new StatefulDiffCommand ((*i)->region()));
2069                                 }
2070                         }
2071
2072                 } else {
2073                         if (!rv->region()->locked()) {
2074                                 rv->region()->clear_changes ();
2075                                 rv->region()->trim_front (new_bound);
2076                                 _session->add_command(new StatefulDiffCommand (rv->region()));
2077                         }
2078                 }
2079
2080                 commit_reversible_command();
2081
2082                 break;
2083         case 2:
2084                 begin_reversible_command (_("End point trim"));
2085
2086                 if (selection->selected (rv)) {
2087
2088                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2089                         {
2090                                 if (!(*i)->region()->locked()) {
2091                                         (*i)->region()->clear_changes();
2092                                         (*i)->region()->trim_end (new_bound);
2093                                         _session->add_command(new StatefulDiffCommand ((*i)->region()));
2094                                 }
2095                         }
2096
2097                 } else {
2098
2099                         if (!rv->region()->locked()) {
2100                                 rv->region()->clear_changes ();
2101                                 rv->region()->trim_end (new_bound);
2102                                 _session->add_command (new StatefulDiffCommand (rv->region()));
2103                         }
2104                 }
2105
2106                 commit_reversible_command();
2107
2108                 break;
2109         default:
2110                 break;
2111         }
2112 }
2113
2114 void
2115 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2116 {
2117         Marker* marker;
2118         bool is_start;
2119
2120         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2121                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2122                 abort(); /*NOTREACHED*/
2123         }
2124
2125         Location* location = find_location_from_marker (marker, is_start);
2126         location->set_hidden (true, this);
2127 }
2128
2129 gint
2130 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2131 {
2132         using namespace Gtkmm2ext;
2133
2134         ArdourPrompter prompter (false);
2135
2136         prompter.set_prompt (_("Name for region:"));
2137         prompter.set_initial_text (clicked_regionview->region()->name());
2138         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2139         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2140         prompter.show_all ();
2141         switch (prompter.run ()) {
2142         case Gtk::RESPONSE_ACCEPT:
2143                 string str;
2144                 prompter.get_result(str);
2145                 if (str.length()) {
2146                         clicked_regionview->region()->set_name (str);
2147                 }
2148                 break;
2149         }
2150         return true;
2151 }
2152
2153
2154 void
2155 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2156 {
2157         /* no brushing without a useful snap setting */
2158
2159         switch (_snap_mode) {
2160         case SnapMagnetic:
2161                 return; /* can't work because it allows region to be placed anywhere */
2162         default:
2163                 break; /* OK */
2164         }
2165
2166         switch (_snap_type) {
2167         case SnapToMark:
2168                 return;
2169
2170         default:
2171                 break;
2172         }
2173
2174         /* don't brush a copy over the original */
2175
2176         if (pos == rv->region()->position()) {
2177                 return;
2178         }
2179
2180         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2181
2182         if (rtv == 0 || !rtv->is_track()) {
2183                 return;
2184         }
2185
2186         boost::shared_ptr<Playlist> playlist = rtv->playlist();
2187         double speed = rtv->track()->speed();
2188
2189         playlist->clear_changes ();
2190         boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2191         playlist->add_region (new_region, (framepos_t) (pos * speed));
2192         _session->add_command (new StatefulDiffCommand (playlist));
2193
2194         // playlist is frozen, so we have to update manually XXX this is disgusting
2195
2196         playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2197 }
2198
2199 gint
2200 Editor::track_height_step_timeout ()
2201 {
2202         if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2203                 current_stepping_trackview = 0;
2204                 return false;
2205         }
2206         return true;
2207 }
2208
2209 void
2210 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2211 {
2212         assert (region_view);
2213
2214         if (!region_view->region()->playlist()) {
2215                 return;
2216         }
2217
2218         switch (Config->get_edit_mode()) {
2219                 case Splice:
2220                         _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2221                         break;
2222                 case Ripple:
2223                         _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2224                         break;
2225                 default:
2226                         _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2227                         break;
2228
2229         }
2230 }
2231
2232 void
2233 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2234 {
2235         assert (region_view);
2236
2237         if (!region_view->region()->playlist()) {
2238                 return;
2239         }
2240
2241         _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2242 }
2243
2244 void
2245 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2246 {
2247         assert (region_view);
2248
2249         if (!region_view->region()->playlist()) {
2250                 return;
2251         }
2252
2253         if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2254                 return;
2255         }
2256
2257         _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2258
2259         begin_reversible_command (Operations::drag_region_brush);
2260 }
2261
2262 /** Start a grab where a time range is selected, track(s) are selected, and the
2263  *  user clicks and drags a region with a modifier in order to create a new region containing
2264  *  the section of the clicked region that lies within the time range.
2265  */
2266 void
2267 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2268 {
2269         if (clicked_regionview == 0) {
2270                 return;
2271         }
2272
2273         /* lets try to create new Region for the selection */
2274
2275         vector<boost::shared_ptr<Region> > new_regions;
2276         create_region_from_selection (new_regions);
2277
2278         if (new_regions.empty()) {
2279                 return;
2280         }
2281
2282         /* XXX fix me one day to use all new regions */
2283
2284         boost::shared_ptr<Region> region (new_regions.front());
2285
2286         /* add it to the current stream/playlist.
2287
2288            tricky: the streamview for the track will add a new regionview. we will
2289            catch the signal it sends when it creates the regionview to
2290            set the regionview we want to then drag.
2291         */
2292
2293         latest_regionviews.clear();
2294         sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2295
2296         /* A selection grab currently creates two undo/redo operations, one for
2297            creating the new region and another for moving it.
2298         */
2299
2300         begin_reversible_command (Operations::selection_grab);
2301
2302         boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2303
2304         playlist->clear_changes ();
2305         clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2306         _session->add_command(new StatefulDiffCommand (playlist));
2307
2308         c.disconnect ();
2309
2310         if (latest_regionviews.empty()) {
2311                 /* something went wrong */
2312                 return;
2313         }
2314
2315         /* we need to deselect all other regionviews, and select this one
2316            i'm ignoring undo stuff, because the region creation will take care of it
2317         */
2318
2319         selection->set (latest_regionviews);
2320
2321         commit_reversible_command ();
2322
2323         _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2324 }
2325
2326 void
2327 Editor::escape ()
2328 {
2329         if (_drags->active ()) {
2330                 _drags->abort ();
2331         } else {
2332                 selection->clear ();
2333         }
2334
2335         reset_focus ();
2336 }
2337
2338 /** Update _join_object_range_state which indicate whether we are over the top
2339  *  or bottom half of a route view, used by the `join object/range' tool
2340  *  mode. Coordinates in canvas space.
2341  */
2342 void
2343 Editor::update_join_object_range_location (double y)
2344 {
2345         if (!get_smart_mode()) {
2346                 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2347                 return;
2348         }
2349
2350         JoinObjectRangeState const old = _join_object_range_state;
2351
2352         if (mouse_mode == MouseObject) {
2353                 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2354         } else if (mouse_mode == MouseRange) {
2355                 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2356         }
2357
2358         if (entered_regionview) {
2359
2360                 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2361                 double const c = item_space.y / entered_regionview->height();
2362
2363                 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2364
2365                 Editor::EnterContext* ctx = get_enter_context(RegionItem);
2366                 if (_join_object_range_state != old && ctx) {
2367                         ctx->cursor_ctx->change(which_track_cursor());
2368                 }
2369
2370         } else if (entered_track) {
2371
2372                 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2373                 
2374                 if (entered_route_view) {
2375
2376                         double cx = 0;
2377                         double cy = y;
2378
2379                         entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2380
2381                         double track_height = entered_route_view->view()->child_height();
2382                         if (ARDOUR_UI::config()->get_show_name_highlight()) {
2383                                 track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2384                         }
2385                         double const c = cy / track_height;
2386
2387
2388                         if (c <= 0.5) {
2389                                 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2390                         } else {
2391                                 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2392                         }
2393
2394                 } else {
2395                         /* Other kinds of tracks use object mode */
2396                         _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2397                 }
2398
2399                 Editor::EnterContext* ctx = get_enter_context(StreamItem);
2400                 if (_join_object_range_state != old && ctx) {
2401                         ctx->cursor_ctx->change(which_track_cursor());
2402                 }
2403         }
2404 }
2405
2406 Editing::MouseMode
2407 Editor::effective_mouse_mode () const
2408 {
2409         if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2410                 return MouseObject;
2411         } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2412                 return MouseRange;
2413         }
2414
2415         return mouse_mode;
2416 }
2417
2418 void
2419 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2420 {
2421         NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2422         assert (e);
2423
2424         e->region_view().delete_note (e->note ());
2425 }
2426
2427 /** Obtain the pointer position in canvas coordinates */
2428 void
2429 Editor::get_pointer_position (double& x, double& y) const
2430 {
2431         int px, py;
2432         _track_canvas->get_pointer (px, py);
2433         _track_canvas->window_to_canvas (px, py, x, y);
2434 }