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