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