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