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