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