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