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