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