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