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