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