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