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