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