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