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