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