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