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