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