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