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