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