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