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