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