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