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