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