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