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