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