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