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