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