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