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