Fix yet another oversight for the windows icon file update
[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)) {
730                         _drags->set (new BBTRulerDrag (this, item), event);
731                 }
732                 return true;
733                 break;
734
735
736         case RangeMarkerBarItem:
737                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
738                         _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateSkipMarker), event);
739                 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
740                         _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
741                 } else {
742                         _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
743                 }
744                 return true;
745                 break;
746
747         case CdMarkerBarItem:
748                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
749                         _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
750                 } else {
751                         _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
752                 }
753                 return true;
754                 break;
755
756         case TransportMarkerBarItem:
757                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
758                         _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
759                 } else {
760                         _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
761                 }
762                 return true;
763                 break;
764
765         default:
766                 break;
767         }
768
769         if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
770                 /* special case: allow trim of range selections in joined object mode;
771                    in theory eff should equal MouseRange in this case, but it doesn't
772                    because entering the range selection canvas item results in entered_regionview
773                    being set to 0, so update_join_object_range_location acts as if we aren't
774                    over a region.
775                 */
776                 if (item_type == StartSelectionTrimItem) {
777                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
778                 } else if (item_type == EndSelectionTrimItem) {
779                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
780                 }
781         }
782
783         Editing::MouseMode eff = effective_mouse_mode ();
784
785         /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
786         if (get_smart_mode()) {
787                 switch (item_type) {
788                   case FadeInHandleItem:
789                   case FadeInTrimHandleItem:
790                   case FadeOutHandleItem:
791                   case FadeOutTrimHandleItem:
792                         eff = MouseObject;
793                         break;
794                 default:
795                         break;
796                 }
797         }
798
799         switch (eff) {
800         case MouseRange:
801                 switch (item_type) {
802                 case StartSelectionTrimItem:
803                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
804                         break;
805
806                 case EndSelectionTrimItem:
807                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
808                         break;
809
810                 case SelectionItem:
811                         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
812                                 start_selection_grab (item, event);
813                                 return true;
814                         } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
815                                 /* grab selection for moving */
816                                 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
817                         } else {
818                                 /* this was debated, but decided the more common action was to
819                                    make a new selection */
820                                 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
821                         }
822                         break;
823
824                 case StreamItem:
825                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
826                                 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
827                         } else {
828                                 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
829                         }
830                         return true;
831                         break;
832
833                 case RegionViewNameHighlight:
834                         if (!clicked_regionview->region()->locked()) {
835                                 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
836                                 return true;
837                         }
838                         break;
839
840                 default:
841                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
842                                 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
843                         } else {
844                                 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
845                         }
846                 }
847                 return true;
848                 break;
849
850         case MouseCut:
851                 switch (item_type) {
852                 case RegionItem:
853                 case FadeInHandleItem:
854                 case FadeOutHandleItem:
855                 case LeftFrameHandle:
856                 case RightFrameHandle:
857                 case FeatureLineItem:
858                 case RegionViewNameHighlight:
859                 case RegionViewName:
860                 case StreamItem:
861                 case AutomationTrackItem:
862                         _drags->set (new RegionCutDrag (this, item, canvas_event_sample (event)), event, get_canvas_cursor());
863                         return true;
864                         break;
865                 default:
866                         break;
867                 }
868                 break;
869
870         case MouseContent:
871                 switch (item_type) {
872                 case NoteItem:
873                         /* Existing note: allow trimming/motion */
874                         if ((note = reinterpret_cast<NoteBase*> (item->get_data ("notebase")))) {
875                                 if (note->big_enough_to_trim() && note->mouse_near_ends()) {
876                                         _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
877                                 } else {
878                                         _drags->set (new NoteDrag (this, item), event);
879                                 }
880                         }
881                         return true;
882
883                 case GainLineItem:
884                         _drags->set (new LineDrag (this, item), event);
885                         return true;
886                         break;
887
888                 case ControlPointItem:
889                         _drags->set (new ControlPointDrag (this, item), event);
890                         return true;
891                         break;
892
893                 case AutomationLineItem:
894                         _drags->set (new LineDrag (this, item), event);
895                         return true;
896                         break;
897
898                 case StreamItem:
899                         //in the past, we created a new midi region here, but perhaps that is best left to the Draw mode
900                         break;
901
902                 case AutomationTrackItem:
903                         /* rubberband drag to select automation points */
904                         _drags->set (new EditorRubberbandSelectDrag (this, item), event);
905                         return true;
906                         break;
907
908                 case RegionItem:
909                         if (dynamic_cast<AutomationRegionView*>(clicked_regionview)) {
910                                 /* rubberband drag to select automation points */
911                                 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
912                                 return true;
913                         }
914                         break;
915
916                 default:
917                         break;
918                 }
919                 break;
920
921         case MouseObject:
922                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
923                     event->type == GDK_BUTTON_PRESS) {
924
925                         _drags->set (new EditorRubberbandSelectDrag (this, item), event);
926
927                 } else if (event->type == GDK_BUTTON_PRESS) {
928
929                         switch (item_type) {
930                         case FadeInHandleItem:
931                         {
932                                 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
933                                 return true;
934                         }
935
936                         case FadeOutHandleItem:
937                         {
938                                 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
939                                 return true;
940                         }
941
942                         case StartCrossFadeItem:
943                         case EndCrossFadeItem:
944                                 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor.  for not this is not fully implemented */
945 //                              if (!clicked_regionview->region()->locked()) {
946 //                                      _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
947 //                                      return true;
948 //                              }
949                                 break;
950
951                         case FeatureLineItem:
952                         {
953                                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
954                                         remove_transient(item);
955                                         return true;
956                                 }
957
958                                 _drags->set (new FeatureLineDrag (this, item), event);
959                                 return true;
960                                 break;
961                         }
962
963                         case RegionItem:
964                                 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
965                                         /* click on an automation region view; do nothing here and let the ARV's signal handler
966                                            sort it out.
967                                         */
968                                         break;
969                                 }
970
971                                 /* click on a normal region view */
972                                 if (ArdourKeyboard::indicates_copy (event->button.state)) {
973                                         add_region_copy_drag (item, event, clicked_regionview);
974                                 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
975                                         add_region_brush_drag (item, event, clicked_regionview);
976                                 } else {
977                                         add_region_drag (item, event, clicked_regionview);
978                                 }
979
980
981                                 _drags->start_grab (event);
982                                 return true;
983                                 break;
984
985                         case RegionViewNameHighlight:
986                         case LeftFrameHandle:
987                         case RightFrameHandle:
988                                 if (!clicked_regionview->region()->locked()) {
989                                         _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
990                                         return true;
991                                 }
992                                 break;
993
994                         case FadeInTrimHandleItem:
995                         case FadeOutTrimHandleItem:
996                                 if (!clicked_regionview->region()->locked()) {
997                                         _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
998                                         return true;
999                                 }
1000                                 break;
1001
1002                         case RegionViewName:
1003                         {
1004                                 /* rename happens on edit clicks */
1005                                 if (clicked_regionview->get_name_highlight()) {
1006                                         _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1007                                         return true;
1008                                 }
1009                                 break;
1010                         }
1011
1012                         case ControlPointItem:
1013                                 _drags->set (new ControlPointDrag (this, item), event);
1014                                 return true;
1015                                 break;
1016
1017                         case AutomationLineItem:
1018                                 _drags->set (new LineDrag (this, item), event);
1019                                 return true;
1020                                 break;
1021
1022                         case StreamItem:
1023                                 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1024                                 return true;
1025
1026                         case AutomationTrackItem:
1027                         {
1028                                 TimeAxisView* parent = clicked_axisview->get_parent ();
1029                                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1030                                 assert (atv);
1031                                 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1032
1033                                         RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1034                                         assert (p);
1035                                         boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1036                                         if (pl->n_regions() == 0) {
1037                                                 /* Parent has no regions; create one so that we have somewhere to put automation */
1038                                                 _drags->set (new RegionCreateDrag (this, item, parent), event);
1039                                         } else {
1040                                                 /* See if there's a region before the click that we can extend, and extend it if so */
1041                                                 framepos_t const t = canvas_event_sample (event);
1042                                                 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1043                                                 if (!prev) {
1044                                                         _drags->set (new RegionCreateDrag (this, item, parent), event);
1045                                                 } else {
1046                                                         prev->set_length (t - prev->position (), get_grid_music_divisions (event->button.state));
1047                                                 }
1048                                         }
1049                                 } else {
1050                                         /* rubberband drag to select automation points */
1051                                         _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1052                                 }
1053                                 break;
1054                         }
1055
1056                         case SelectionItem:
1057                         {
1058                                 break;
1059                         }
1060
1061                         case MarkerBarItem:
1062
1063                                 break;
1064
1065                         default:
1066                                 break;
1067                         }
1068                 }
1069                 return true;
1070                 break;
1071
1072         case MouseDraw:
1073                 switch (item_type) {
1074                 case GainLineItem:
1075                         _drags->set (new LineDrag (this, item), event);
1076                         return true;
1077
1078                 case ControlPointItem:
1079                         _drags->set (new ControlPointDrag (this, item), event);
1080                         return true;
1081                         break;
1082
1083                 case SelectionItem:
1084                 {
1085                         if (dynamic_cast<AudioRegionView*>(clicked_regionview) ||
1086                             dynamic_cast<AutomationRegionView*>(clicked_regionview)) {
1087                                 _drags->set (new AutomationRangeDrag (this, clicked_regionview, selection->time),
1088                                              event, _cursors->up_down);
1089                         } else {
1090                                 double const y = event->button.y;
1091                                 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y, false);
1092                                 if (tvp.first) {
1093                                         AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1094                                         if ( atv) {
1095                                                 /* smart "join" mode: drag automation */
1096                                                 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1097                                         }
1098                                 }
1099                         }
1100                         return true;
1101                         break;
1102                 }
1103
1104                 case AutomationLineItem:
1105                         _drags->set (new LineDrag (this, item), event);
1106                         break;
1107
1108                 case NoteItem:
1109                         if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
1110                                 if (note->big_enough_to_trim() && note->mouse_near_ends()) {
1111                                         /* Note is big and pointer is near the end, trim */
1112                                         _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
1113                                 } else {
1114                                         /* Drag note */
1115                                         _drags->set (new NoteDrag (this, item), event);
1116                                 }
1117                                 return true;
1118                         }
1119                         return true;
1120
1121                 case StreamItem:
1122                         if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1123                                 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1124                         }
1125                         return true;
1126
1127                 default:
1128                         break;
1129                 }
1130                 return true;
1131                 break;
1132
1133         case MouseTimeFX:
1134                 if (item_type == NoteItem) {
1135                         /* resize-drag notes */
1136                         if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
1137                                 if (note->big_enough_to_trim()) {
1138                                         _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
1139                                 }
1140                         }
1141                         return true;
1142                 } else if (clicked_regionview) {
1143                         /* do time-FX  */
1144                         _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1145                         return true;
1146                 }
1147                 break;
1148
1149         case MouseAudition:
1150                 _drags->set (new ScrubDrag (this, item), event, _cursors->transparent);
1151                 scrub_reversals = 0;
1152                 scrub_reverse_distance = 0;
1153                 last_scrub_x = event->button.x;
1154                 scrubbing_direction = 0;
1155                 return true;
1156                 break;
1157
1158         default:
1159                 break;
1160         }
1161
1162         return false;
1163 }
1164
1165 bool
1166 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1167 {
1168         Editing::MouseMode const eff = effective_mouse_mode ();
1169         switch (eff) {
1170         case MouseObject:
1171                 switch (item_type) {
1172                 case RegionItem:
1173                         if (ArdourKeyboard::indicates_copy (event->button.state)) {
1174                                 add_region_copy_drag (item, event, clicked_regionview);
1175                         } else {
1176                                 add_region_drag (item, event, clicked_regionview);
1177                         }
1178                         _drags->start_grab (event);
1179                         return true;
1180                         break;
1181                 case ControlPointItem:
1182                         _drags->set (new ControlPointDrag (this, item), event);
1183                         return true;
1184                         break;
1185
1186                 default:
1187                         break;
1188                 }
1189
1190                 switch (item_type) {
1191                 case RegionViewNameHighlight:
1192                         _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1193                         return true;
1194                         break;
1195
1196                 case LeftFrameHandle:
1197                 case RightFrameHandle:
1198                         _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1199                         return true;
1200                         break;
1201
1202                 case RegionViewName:
1203                         _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1204                         return true;
1205                         break;
1206
1207                 default:
1208                         break;
1209                 }
1210
1211                 break;
1212
1213         case MouseDraw:
1214                 return false;
1215
1216         case MouseRange:
1217                 /* relax till release */
1218                 return true;
1219                 break;
1220
1221         default:
1222                 break;
1223         }
1224
1225         return false;
1226 }
1227
1228 bool
1229 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1230 {
1231         if (event->type == GDK_2BUTTON_PRESS) {
1232                 _drags->mark_double_click ();
1233                 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1234                 return true;
1235         }
1236
1237         if (event->type != GDK_BUTTON_PRESS) {
1238                 return false;
1239         }
1240
1241         _track_canvas->grab_focus();
1242
1243         if (_session && _session->actively_recording()) {
1244                 return true;
1245         }
1246
1247         button_selection (item, event, item_type);
1248
1249         if (!_drags->active () &&
1250             (Keyboard::is_delete_event (&event->button) ||
1251              Keyboard::is_context_menu_event (&event->button) ||
1252              Keyboard::is_edit_event (&event->button))) {
1253
1254                 /* handled by button release */
1255                 return true;
1256         }
1257
1258         /* not rolling, effectively in range mode, follow edits enabled (likely
1259          * to start range drag), not in a fade handle (since that means we are
1260          * not starting a range drag): locate the PH here
1261          */
1262
1263         if ((item_type != FadeInHandleItem) &&
1264             (item_type != FadeOutHandleItem) &&
1265             !_drags->active () && 
1266             _session && 
1267             !_session->transport_rolling() && 
1268             (effective_mouse_mode() == MouseRange) && 
1269             UIConfiguration::instance().get_follow_edits() && 
1270             !_session->config.get_external_sync()) {
1271
1272                 MusicFrame where (canvas_event_sample (event), 0);
1273                 snap_to (where);
1274                 _session->request_locate (where.frame, false);
1275         }
1276
1277         switch (event->button.button) {
1278         case 1:
1279                 return button_press_handler_1 (item, event, item_type);
1280                 break;
1281
1282         case 2:
1283                 return button_press_handler_2 (item, event, item_type);
1284                 break;
1285
1286         case 3:
1287                 break;
1288
1289         default:
1290                 return button_press_dispatch (&event->button);
1291                 break;
1292
1293         }
1294
1295         return false;
1296 }
1297
1298 bool
1299 Editor::button_press_dispatch (GdkEventButton* ev)
1300 {
1301         /* this function is intended only for buttons 4 and above.
1302          */
1303
1304         Gtkmm2ext::MouseButton b (ev->state, ev->button);
1305         return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1306 }
1307
1308 bool
1309 Editor::button_release_dispatch (GdkEventButton* ev)
1310 {
1311         /* this function is intended only for buttons 4 and above.
1312          */
1313
1314         Gtkmm2ext::MouseButton b (ev->state, ev->button);
1315         return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1316 }
1317
1318 bool
1319 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1320 {
1321         MusicFrame where (canvas_event_sample (event), 0);
1322         AutomationTimeAxisView* atv = 0;
1323
1324         _press_cursor_ctx.reset();
1325
1326         /* no action if we're recording */
1327
1328         if (_session && _session->actively_recording()) {
1329                 return true;
1330         }
1331
1332         bool were_dragging = false;
1333
1334         if (!Keyboard::is_context_menu_event (&event->button)) {
1335
1336                 /* see if we're finishing a drag */
1337
1338                 if (_drags->active ()) {
1339                         bool const r = _drags->end_grab (event);
1340                         if (r) {
1341                                 /* grab dragged, so do nothing else */
1342                                 return true;
1343                         }
1344
1345                         were_dragging = true;
1346                 }
1347
1348                 update_region_layering_order_editor ();
1349         }
1350
1351         /* edit events get handled here */
1352
1353         if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1354                 switch (item_type) {
1355                 case RegionItem:
1356                         show_region_properties ();
1357                         break;
1358                 case TempoMarkerItem: {
1359                         ArdourMarker* marker;
1360                         TempoMarker* tempo_marker;
1361
1362                         if ((marker = reinterpret_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
1363                                 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1364                                 abort(); /*NOTREACHED*/
1365                         }
1366
1367                         if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1368                                 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1369                                 abort(); /*NOTREACHED*/
1370                         }
1371
1372                         edit_tempo_marker (*tempo_marker);
1373                         break;
1374                 }
1375
1376                 case MeterMarkerItem: {
1377                         ArdourMarker* marker;
1378                         MeterMarker* meter_marker;
1379
1380                         if ((marker = reinterpret_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
1381                                 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1382                                 abort(); /*NOTREACHED*/
1383                         }
1384
1385                         if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1386                                 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1387                                 abort(); /*NOTREACHED*/
1388                         }
1389                         edit_meter_marker (*meter_marker);
1390                         break;
1391                 }
1392
1393                 case RegionViewName:
1394                         if (clicked_regionview->name_active()) {
1395                                 return mouse_rename_region (item, event);
1396                         }
1397                         break;
1398
1399                 case ControlPointItem:
1400                         edit_control_point (item);
1401                         break;
1402
1403                 default:
1404                         break;
1405                 }
1406                 return true;
1407         }
1408
1409         /* context menu events get handled here */
1410         if (Keyboard::is_context_menu_event (&event->button)) {
1411
1412                 context_click_event = *event;
1413
1414                 if (!_drags->active ()) {
1415
1416                         /* no matter which button pops up the context menu, tell the menu
1417                            widget to use button 1 to drive menu selection.
1418                         */
1419
1420                         switch (item_type) {
1421                         case FadeInItem:
1422                         case FadeInHandleItem:
1423                         case FadeInTrimHandleItem:
1424                         case StartCrossFadeItem:
1425                                 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1426                                 break;
1427
1428                         case FadeOutItem:
1429                         case FadeOutHandleItem:
1430                         case FadeOutTrimHandleItem:
1431                         case EndCrossFadeItem:
1432                                 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1433                                 break;
1434
1435                         case LeftFrameHandle:
1436                         case RightFrameHandle:
1437                                 break;
1438
1439                         case StreamItem:
1440                                 popup_track_context_menu (1, event->button.time, item_type, false);
1441                                 break;
1442
1443                         case RegionItem:
1444                         case RegionViewNameHighlight:
1445                         case RegionViewName:
1446                                 popup_track_context_menu (1, event->button.time, item_type, false);
1447                                 break;
1448
1449                         case SelectionItem:
1450                                 popup_track_context_menu (1, event->button.time, item_type, true);
1451                                 break;
1452
1453                         case AutomationTrackItem:
1454                                 popup_track_context_menu (1, event->button.time, item_type, false);
1455                                 break;
1456
1457                         case MarkerBarItem:
1458                         case RangeMarkerBarItem:
1459                         case TransportMarkerBarItem:
1460                         case CdMarkerBarItem:
1461                         case TempoBarItem:
1462                         case TempoCurveItem:
1463                         case MeterBarItem:
1464                         case VideoBarItem:
1465                         case TimecodeRulerItem:
1466                         case SamplesRulerItem:
1467                         case MinsecRulerItem:
1468                         case BBTRulerItem:
1469                                 popup_ruler_menu (where.frame, item_type);
1470                                 break;
1471
1472                         case MarkerItem:
1473                                 marker_context_menu (&event->button, item);
1474                                 break;
1475
1476                         case TempoMarkerItem:
1477                                 tempo_or_meter_marker_context_menu (&event->button, item);
1478                                 break;
1479
1480                         case MeterMarkerItem:
1481                                 tempo_or_meter_marker_context_menu (&event->button, item);
1482                                 break;
1483
1484                         case CrossfadeViewItem:
1485                                 popup_track_context_menu (1, event->button.time, item_type, false);
1486                                 break;
1487
1488                         case ControlPointItem:
1489                                 popup_control_point_context_menu (item, event);
1490                                 break;
1491
1492                         case NoteItem:
1493                                 if (internal_editing()) {
1494                                         popup_note_context_menu (item, event);
1495                                 }
1496                                 break;
1497
1498                         default:
1499                                 break;
1500                         }
1501
1502                         return true;
1503                 }
1504         }
1505
1506         /* delete events get handled here */
1507
1508         Editing::MouseMode const eff = effective_mouse_mode ();
1509
1510         if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1511
1512                 switch (item_type) {
1513                 case TempoMarkerItem:
1514                         remove_tempo_marker (item);
1515                         break;
1516
1517                 case MeterMarkerItem:
1518                         remove_meter_marker (item);
1519                         break;
1520
1521                 case MarkerItem:
1522                         remove_marker (*item, event);
1523                         break;
1524
1525                 case RegionItem:
1526                         if (eff == MouseObject) {
1527                                 remove_clicked_region ();
1528                         }
1529                         break;
1530
1531                 case ControlPointItem:
1532                         remove_control_point (item);
1533                         break;
1534
1535                 case NoteItem:
1536                         remove_midi_note (item, event);
1537                         break;
1538
1539                 default:
1540                         break;
1541                 }
1542                 return true;
1543         }
1544
1545         switch (event->button.button) {
1546         case 1:
1547
1548                 switch (item_type) {
1549                 /* see comments in button_press_handler */
1550                 case PlayheadCursorItem:
1551                 case MarkerItem:
1552                 case GainLineItem:
1553                 case AutomationLineItem:
1554                 case StartSelectionTrimItem:
1555                 case EndSelectionTrimItem:
1556                         return true;
1557
1558                 case MarkerBarItem:
1559                         if (!_dragging_playhead) {
1560                                 snap_to_with_modifier (where, event, RoundNearest, true);
1561                                 mouse_add_new_marker (where.frame);
1562                         }
1563                         return true;
1564
1565                 case CdMarkerBarItem:
1566                         if (!_dragging_playhead) {
1567                                 // if we get here then a dragged range wasn't done
1568                                 snap_to_with_modifier (where, event, RoundNearest, true);
1569                                 mouse_add_new_marker (where.frame, true);
1570                         }
1571                         return true;
1572                 case TempoBarItem:
1573                 case TempoCurveItem:
1574                         if (!_dragging_playhead) {
1575                                 snap_to_with_modifier (where, event);
1576                                 mouse_add_new_tempo_event (where.frame);
1577                         }
1578                         return true;
1579
1580                 case MeterBarItem:
1581                         if (!_dragging_playhead) {
1582                                 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1583                         }
1584                         return true;
1585                         break;
1586
1587                 case TimecodeRulerItem:
1588                 case SamplesRulerItem:
1589                 case MinsecRulerItem:
1590                 case BBTRulerItem:
1591                         return true;
1592                         break;
1593
1594                 default:
1595                         break;
1596                 }
1597
1598                 switch (eff) {
1599                 case MouseDraw:
1600                         switch (item_type) {
1601                         case RegionItem:
1602                         {
1603                                 /* check that we didn't drag before releasing, since
1604                                    its really annoying to create new control
1605                                    points when doing this.
1606                                 */
1607                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1608                                 if (!were_dragging && arv) {
1609                                         bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1610                                         arv->add_gain_point_event (item, event, with_guard_points);
1611                                 }
1612                                 return true;
1613                                 break;
1614                         }
1615
1616                         case AutomationTrackItem: {
1617                                 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1618                                 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1619                                 if (atv) {
1620                                         atv->add_automation_event (event, where.frame, event->button.y, with_guard_points);
1621                                 }
1622                                 return true;
1623                                 break;
1624                         }
1625                         default:
1626                                 break;
1627                         }
1628                         break;
1629
1630                 case MouseAudition:
1631                         if (scrubbing_direction == 0) {
1632                                 /* no drag, just a click */
1633                                 switch (item_type) {
1634                                 case RegionItem:
1635                                         play_selected_region ();
1636                                         break;
1637                                 default:
1638                                         break;
1639                                 }
1640                         } else if (_session) {
1641                                 /* make sure we stop */
1642                                 _session->request_transport_speed (0.0);
1643                         }
1644                         break;
1645
1646                 default:
1647                         break;
1648
1649                 }
1650
1651                 /* do any (de)selection operations that should occur on button release */
1652                 button_selection (item, event, item_type);
1653
1654                 return true;
1655                 break;
1656
1657
1658         case 2:
1659                 switch (eff) {
1660
1661                 case MouseObject:
1662                         switch (item_type) {
1663                         case RegionItem:
1664                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1665                                         raise_region ();
1666                                 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1667                                         lower_region ();
1668                                 } else {
1669                                         // Button2 click is unused
1670                                 }
1671                                 return true;
1672
1673                                 break;
1674
1675                         default:
1676                                 break;
1677                         }
1678                         break;
1679
1680                 case MouseDraw:
1681                         return true;
1682
1683                 case MouseRange:
1684                         // x_style_paste (where, 1.0);
1685                         return true;
1686                         break;
1687
1688                 default:
1689                         break;
1690                 }
1691
1692                 break;
1693
1694         case 3:
1695                 break;
1696
1697         default:
1698                 break;
1699         }
1700
1701         return false;
1702 }
1703
1704 bool
1705 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1706 {
1707         ControlPoint* cp;
1708         ArdourMarker * marker;
1709         MeterMarker* m_marker = 0;
1710         TempoMarker* t_marker = 0;
1711         double fraction;
1712         bool ret = true;
1713
1714         /* by the time we reach here, entered_regionview and entered trackview
1715          * will have already been set as appropriate. Things are done this
1716          * way because this method isn't passed a pointer to a variable type of
1717          * thing that is entered (which may or may not be canvas item).
1718          * (e.g. the actual entered regionview)
1719          */
1720
1721         choose_canvas_cursor_on_entry (item_type);
1722
1723         switch (item_type) {
1724         case ControlPointItem:
1725                 if (mouse_mode == MouseDraw || mouse_mode == MouseObject || mouse_mode == MouseContent) {
1726                         cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1727                         cp->show ();
1728
1729                         fraction = 1.0 - (cp->get_y() / cp->line().height());
1730
1731                         _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1732                         _verbose_cursor->show ();
1733                 }
1734                 break;
1735
1736         case GainLineItem:
1737                 if (mouse_mode == MouseDraw) {
1738                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1739                         if (line) {
1740                                 line->set_outline_color (UIConfiguration::instance().color ("entered gain line"));
1741                         }
1742                 }
1743                 break;
1744
1745         case AutomationLineItem:
1746                 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1747                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1748                         if (line) {
1749                                 line->set_outline_color (UIConfiguration::instance().color ("entered automation line"));
1750                         }
1751                 }
1752                 break;
1753
1754         case AutomationTrackItem:
1755                 AutomationTimeAxisView* atv;
1756                 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1757                         clear_entered_track = false;
1758                         set_entered_track (atv);
1759                 }
1760                 break;
1761
1762         case MarkerItem:
1763                 if ((marker = static_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
1764                         break;
1765                 }
1766                 entered_marker = marker;
1767                 marker->set_color_rgba (UIConfiguration::instance().color ("entered marker"));
1768                 break;
1769
1770         case MeterMarkerItem:
1771                 if ((m_marker = static_cast<MeterMarker *> (item->get_data ("marker"))) == 0) {
1772                         break;
1773                 }
1774                 entered_marker = m_marker;
1775                 if (m_marker->meter().position_lock_style() == MusicTime) {
1776                         m_marker->set_color_rgba (UIConfiguration::instance().color ("meter marker"));
1777                 } else {
1778                         m_marker->set_color_rgba (UIConfiguration::instance().color ("meter marker music"));
1779                 }
1780                 break;
1781
1782         case TempoMarkerItem:
1783                 if ((t_marker = static_cast<TempoMarker *> (item->get_data ("marker"))) == 0) {
1784                         break;
1785                 }
1786                 entered_marker = t_marker;
1787                 if (t_marker->tempo().position_lock_style() == MusicTime) {
1788                         t_marker->set_color_rgba (UIConfiguration::instance().color ("tempo marker"));
1789                 } else {
1790                         t_marker->set_color_rgba (UIConfiguration::instance().color ("tempo marker music"));
1791                 }
1792                 break;
1793
1794         case FadeInHandleItem:
1795         case FadeInTrimHandleItem:
1796                 if (mouse_mode == MouseObject) {
1797                         ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1798                         if (rect) {
1799                                 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1800                                 rect->set_fill_color (rv->get_fill_color());
1801                         }
1802                 }
1803                 break;
1804
1805         case FadeOutHandleItem:
1806         case FadeOutTrimHandleItem:
1807                 if (mouse_mode == MouseObject) {
1808                         ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1809                         if (rect) {
1810                                 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1811                                 rect->set_fill_color (rv->get_fill_color ());
1812                         }
1813                 }
1814                 break;
1815
1816         case FeatureLineItem:
1817         {
1818                 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1819                 line->set_outline_color (0xFF0000FF);
1820         }
1821         break;
1822
1823         case SelectionItem:
1824                 break;
1825
1826         case WaveItem:
1827         {
1828                 if (entered_regionview) {
1829                         entered_regionview->entered();
1830                 }
1831         }
1832         break;
1833
1834         default:
1835                 break;
1836         }
1837
1838         /* third pass to handle entered track status in a comprehensible way.
1839          */
1840
1841         switch (item_type) {
1842         case GainLineItem:
1843         case AutomationLineItem:
1844         case ControlPointItem:
1845                 /* these do not affect the current entered track state */
1846                 clear_entered_track = false;
1847                 break;
1848
1849         case AutomationTrackItem:
1850                 /* handled above already */
1851                 break;
1852
1853         default:
1854
1855                 break;
1856         }
1857
1858         return ret;
1859 }
1860
1861 bool
1862 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1863 {
1864         AutomationLine* al;
1865         ArdourMarker *marker;
1866         TempoMarker *t_marker;
1867         MeterMarker *m_marker;
1868         Location *loc;
1869         bool is_start;
1870         bool ret = true;
1871
1872         if (!_enter_stack.empty()) {
1873                 _enter_stack.pop_back();
1874         }
1875
1876         switch (item_type) {
1877         case ControlPointItem:
1878                 _verbose_cursor->hide ();
1879                 break;
1880
1881         case GainLineItem:
1882         case AutomationLineItem:
1883                 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1884                 {
1885                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1886                         if (line) {
1887                                 line->set_outline_color (al->get_line_color());
1888                         }
1889                 }
1890                 break;
1891
1892         case MarkerItem:
1893                 if ((marker = static_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
1894                         break;
1895                 }
1896                 entered_marker = 0;
1897                 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1898                         location_flags_changed (loc);
1899                 }
1900                 break;
1901
1902         case MeterMarkerItem:
1903                 if ((m_marker = static_cast<MeterMarker *> (item->get_data ("marker"))) == 0) {
1904                         break;
1905                 }
1906                 entered_marker = 0;
1907                 if (m_marker->meter().position_lock_style() == MusicTime) {
1908                         m_marker->set_color_rgba (UIConfiguration::instance().color ("meter marker music"));
1909                 } else {
1910                         m_marker->set_color_rgba (UIConfiguration::instance().color ("meter marker"));
1911                 }
1912                 break;
1913
1914         case TempoMarkerItem:
1915                 if ((t_marker = static_cast<TempoMarker *> (item->get_data ("marker"))) == 0) {
1916                         break;
1917                 }
1918                 entered_marker = 0;
1919                 if (t_marker->tempo().position_lock_style() == MusicTime) {
1920                         t_marker->set_color_rgba (UIConfiguration::instance().color ("tempo marker music"));
1921                 } else {
1922                         t_marker->set_color_rgba (UIConfiguration::instance().color ("tempo marker"));
1923                 }
1924                 break;
1925
1926         case FadeInTrimHandleItem:
1927         case FadeOutTrimHandleItem:
1928         case FadeInHandleItem:
1929         case FadeOutHandleItem:
1930         {
1931                 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1932                 if (rect) {
1933                         rect->set_fill_color (UIConfiguration::instance().color ("inactive fade handle"));
1934                 }
1935         }
1936         break;
1937
1938         case AutomationTrackItem:
1939                 break;
1940
1941         case FeatureLineItem:
1942         {
1943                 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1944                 line->set_outline_color (UIConfiguration::instance().color ("zero line"));
1945         }
1946         break;
1947
1948         default:
1949                 break;
1950         }
1951
1952         return ret;
1953 }
1954
1955 void
1956 Editor::scrub (framepos_t frame, double current_x)
1957 {
1958         double delta;
1959
1960         if (scrubbing_direction == 0) {
1961                 /* first move */
1962                 _session->request_locate (frame, false);
1963                 _session->request_transport_speed (0.1);
1964                 scrubbing_direction = 1;
1965
1966         } else {
1967
1968                 if (last_scrub_x > current_x) {
1969
1970                         /* pointer moved to the left */
1971
1972                         if (scrubbing_direction > 0) {
1973
1974                                 /* we reversed direction to go backwards */
1975
1976                                 scrub_reversals++;
1977                                 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1978
1979                         } else {
1980
1981                                 /* still moving to the left (backwards) */
1982
1983                                 scrub_reversals = 0;
1984                                 scrub_reverse_distance = 0;
1985
1986                                 delta = 0.01 * (last_scrub_x - current_x);
1987                                 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1988                         }
1989
1990                 } else {
1991                         /* pointer moved to the right */
1992
1993                         if (scrubbing_direction < 0) {
1994                                 /* we reversed direction to go forward */
1995
1996                                 scrub_reversals++;
1997                                 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1998
1999                         } else {
2000                                 /* still moving to the right */
2001
2002                                 scrub_reversals = 0;
2003                                 scrub_reverse_distance = 0;
2004
2005                                 delta = 0.01 * (current_x - last_scrub_x);
2006                                 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2007                         }
2008                 }
2009
2010                 /* if there have been more than 2 opposite motion moves detected, or one that moves
2011                    back more than 10 pixels, reverse direction
2012                 */
2013
2014                 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2015
2016                         if (scrubbing_direction > 0) {
2017                                 /* was forwards, go backwards */
2018                                 _session->request_transport_speed (-0.1);
2019                                 scrubbing_direction = -1;
2020                         } else {
2021                                 /* was backwards, go forwards */
2022                                 _session->request_transport_speed (0.1);
2023                                 scrubbing_direction = 1;
2024                         }
2025
2026                         scrub_reverse_distance = 0;
2027                         scrub_reversals = 0;
2028                 }
2029         }
2030
2031         last_scrub_x = current_x;
2032 }
2033
2034 bool
2035 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2036 {
2037         _last_motion_y = event->motion.y;
2038
2039         if (event->motion.is_hint) {
2040                 gint x, y;
2041
2042                 /* We call this so that MOTION_NOTIFY events continue to be
2043                    delivered to the canvas. We need to do this because we set
2044                    Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2045                    the density of the events, at the expense of a round-trip
2046                    to the server. Given that this will mostly occur on cases
2047                    where DISPLAY = :0.0, and given the cost of what the motion
2048                    event might do, its a good tradeoff.
2049                 */
2050
2051                 _track_canvas->get_pointer (x, y);
2052         }
2053
2054         if (current_stepping_trackview) {
2055                 /* don't keep the persistent stepped trackview if the mouse moves */
2056                 current_stepping_trackview = 0;
2057                 step_timeout.disconnect ();
2058         }
2059
2060         if (_session && _session->actively_recording()) {
2061                 /* Sorry. no dragging stuff around while we record */
2062                 return true;
2063         }
2064
2065         update_join_object_range_location (event->motion.y);
2066
2067         if (_drags->active ()) {
2068                 return _drags->motion_handler (event, from_autoscroll);
2069         }
2070
2071         return false;
2072 }
2073
2074 bool
2075 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2076 {
2077         ControlPoint* control_point;
2078
2079         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2080                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2081                 abort(); /*NOTREACHED*/
2082         }
2083
2084         AutomationLine& line = control_point->line ();
2085         if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2086                 /* we shouldn't remove the first or last gain point in region gain lines */
2087                 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2088                         return false;
2089                 }
2090         }
2091
2092         return true;
2093 }
2094
2095 void
2096 Editor::remove_control_point (ArdourCanvas::Item* item)
2097 {
2098         if (!can_remove_control_point (item)) {
2099                 return;
2100         }
2101
2102         ControlPoint* control_point;
2103
2104         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2105                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2106                 abort(); /*NOTREACHED*/
2107         }
2108
2109         control_point->line().remove_point (*control_point);
2110 }
2111
2112 void
2113 Editor::edit_control_point (ArdourCanvas::Item* item)
2114 {
2115         ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2116
2117         if (p == 0) {
2118                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2119                 abort(); /*NOTREACHED*/
2120         }
2121
2122         ControlPointDialog d (p);
2123
2124         if (d.run () != RESPONSE_ACCEPT) {
2125                 return;
2126         }
2127
2128         p->line().modify_point_y (*p, d.get_y_fraction ());
2129 }
2130
2131 void
2132 Editor::edit_notes (MidiRegionView* mrv)
2133 {
2134         MidiRegionView::Selection const & s = mrv->selection();
2135
2136         if (s.empty ()) {
2137                 return;
2138         }
2139
2140         EditNoteDialog* d = new EditNoteDialog (mrv, s);
2141         d->show_all ();
2142
2143         d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2144 }
2145
2146 void
2147 Editor::note_edit_done (int r, EditNoteDialog* d)
2148 {
2149         begin_reversible_command (_("edit note(s)"));
2150
2151         d->done (r);
2152         delete d;
2153
2154         commit_reversible_command();
2155 }
2156
2157 void
2158 Editor::edit_region (RegionView* rv)
2159 {
2160         if (UIConfiguration::instance().get_use_double_click_to_zoom_to_selection()) {
2161                 temporal_zoom_selection (Both);
2162         } else {
2163                 rv->show_region_editor ();
2164         }
2165 }
2166
2167 void
2168 Editor::visible_order_range (int* low, int* high) const
2169 {
2170         *low = TimeAxisView::max_order ();
2171         *high = 0;
2172
2173         for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2174
2175                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2176
2177                 if (rtv && !rtv->hidden()) {
2178
2179                         if (*high < rtv->order()) {
2180                                 *high = rtv->order ();
2181                         }
2182
2183                         if (*low > rtv->order()) {
2184                                 *low = rtv->order ();
2185                         }
2186                 }
2187         }
2188 }
2189
2190 void
2191 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2192 {
2193         /* Either add to or set the set the region selection, unless
2194            this is an alignment click (control used)
2195         */
2196
2197         if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2198                 TimeAxisView* tv = &rv.get_time_axis_view();
2199                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2200                 double speed = 1.0;
2201                 if (rtv && rtv->is_track()) {
2202                         speed = rtv->track()->speed();
2203                 }
2204
2205                 framepos_t where = get_preferred_edit_position();
2206
2207                 if (where >= 0) {
2208
2209                         if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2210
2211                                 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2212
2213                         } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2214
2215                                 align_region (rv.region(), End, (framepos_t) (where * speed));
2216
2217                         } else {
2218
2219                                 align_region (rv.region(), Start, (framepos_t) (where * speed));
2220                         }
2221                 }
2222         }
2223 }
2224
2225 void
2226 Editor::collect_new_region_view (RegionView* rv)
2227 {
2228         latest_regionviews.push_back (rv);
2229 }
2230
2231 void
2232 Editor::collect_and_select_new_region_view (RegionView* rv)
2233 {
2234         selection->add(rv);
2235         latest_regionviews.push_back (rv);
2236 }
2237
2238 void
2239 Editor::cancel_selection ()
2240 {
2241         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2242                 (*i)->hide_selection ();
2243         }
2244
2245         selection->clear ();
2246         clicked_selection = 0;
2247 }
2248
2249 void
2250 Editor::cancel_time_selection ()
2251 {
2252         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2253                 (*i)->hide_selection ();
2254         }
2255         selection->time.clear ();
2256         clicked_selection = 0;
2257 }
2258
2259 void
2260 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2261 {
2262         RegionView* rv = clicked_regionview;
2263
2264         /* Choose action dependant on which button was pressed */
2265         switch (event->button.button) {
2266         case 1:
2267                 begin_reversible_command (_("start point trim"));
2268
2269                 if (selection->selected (rv)) {
2270                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2271                              i != selection->regions.by_layer().end(); ++i)
2272                         {
2273                                 if (!(*i)->region()->locked()) {
2274                                         (*i)->region()->clear_changes ();
2275                                         (*i)->region()->trim_front (new_bound);
2276                                         _session->add_command(new StatefulDiffCommand ((*i)->region()));
2277                                 }
2278                         }
2279
2280                 } else {
2281                         if (!rv->region()->locked()) {
2282                                 rv->region()->clear_changes ();
2283                                 rv->region()->trim_front (new_bound);
2284                                 _session->add_command(new StatefulDiffCommand (rv->region()));
2285                         }
2286                 }
2287
2288                 commit_reversible_command();
2289
2290                 break;
2291         case 2:
2292                 begin_reversible_command (_("end point trim"));
2293
2294                 if (selection->selected (rv)) {
2295
2296                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2297                         {
2298                                 if (!(*i)->region()->locked()) {
2299                                         (*i)->region()->clear_changes();
2300                                         (*i)->region()->trim_end (new_bound);
2301                                         _session->add_command(new StatefulDiffCommand ((*i)->region()));
2302                                 }
2303                         }
2304
2305                 } else {
2306
2307                         if (!rv->region()->locked()) {
2308                                 rv->region()->clear_changes ();
2309                                 rv->region()->trim_end (new_bound);
2310                                 _session->add_command (new StatefulDiffCommand (rv->region()));
2311                         }
2312                 }
2313
2314                 commit_reversible_command();
2315
2316                 break;
2317         default:
2318                 break;
2319         }
2320 }
2321
2322 void
2323 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2324 {
2325         ArdourMarker* marker;
2326         bool is_start;
2327
2328         if ((marker = static_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
2329                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2330                 abort(); /*NOTREACHED*/
2331         }
2332
2333         Location* location = find_location_from_marker (marker, is_start);
2334         location->set_hidden (true, this);
2335 }
2336
2337 gint
2338 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2339 {
2340         using namespace Gtkmm2ext;
2341
2342         ArdourPrompter prompter (false);
2343
2344         prompter.set_prompt (_("Name for region:"));
2345         prompter.set_initial_text (clicked_regionview->region()->name());
2346         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2347         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2348         prompter.show_all ();
2349         switch (prompter.run ()) {
2350         case Gtk::RESPONSE_ACCEPT:
2351                 string str;
2352                 prompter.get_result(str);
2353                 if (str.length()) {
2354                         clicked_regionview->region()->set_name (str);
2355                 }
2356                 break;
2357         }
2358         return true;
2359 }
2360
2361
2362 void
2363 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2364 {
2365         /* no brushing without a useful snap setting */
2366
2367         switch (_snap_mode) {
2368         case SnapMagnetic:
2369                 return; /* can't work because it allows region to be placed anywhere */
2370         default:
2371                 break; /* OK */
2372         }
2373
2374         switch (_snap_type) {
2375         case SnapToMark:
2376                 return;
2377
2378         default:
2379                 break;
2380         }
2381
2382         /* don't brush a copy over the original */
2383
2384         if (pos == rv->region()->position()) {
2385                 return;
2386         }
2387
2388         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2389
2390         if (!rtv || !rtv->is_track()) {
2391                 return;
2392         }
2393
2394         boost::shared_ptr<Playlist> playlist = rtv->playlist();
2395         double speed = rtv->track()->speed();
2396
2397         playlist->clear_changes ();
2398         boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2399         playlist->add_region (new_region, (framepos_t) (pos * speed));
2400         _session->add_command (new StatefulDiffCommand (playlist));
2401
2402         // playlist is frozen, so we have to update manually XXX this is disgusting
2403
2404         //playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2405 }
2406
2407 gint
2408 Editor::track_height_step_timeout ()
2409 {
2410         if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2411                 current_stepping_trackview = 0;
2412                 return false;
2413         }
2414         return true;
2415 }
2416
2417 void
2418 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2419 {
2420         assert (region_view);
2421
2422         if (!region_view->region()->playlist()) {
2423                 return;
2424         }
2425
2426         switch (Config->get_edit_mode()) {
2427                 case Splice:
2428                         _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2429                         break;
2430                 case Ripple:
2431                         _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2432                         break;
2433                 default:
2434                         _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2435                         break;
2436
2437         }
2438 }
2439
2440 void
2441 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2442 {
2443         assert (region_view);
2444
2445         if (!region_view->region()->playlist()) {
2446                 return;
2447         }
2448
2449         _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2450 }
2451
2452 void
2453 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2454 {
2455         assert (region_view);
2456
2457         if (!region_view->region()->playlist()) {
2458                 return;
2459         }
2460
2461         if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2462                 return;
2463         }
2464
2465         _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2466 }
2467
2468 /** Start a grab where a time range is selected, track(s) are selected, and the
2469  *  user clicks and drags a region with a modifier in order to create a new region containing
2470  *  the section of the clicked region that lies within the time range.
2471  */
2472 void
2473 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2474 {
2475         if (clicked_regionview == 0) {
2476                 return;
2477         }
2478
2479         /* lets try to create new Region for the selection */
2480
2481         vector<boost::shared_ptr<Region> > new_regions;
2482         create_region_from_selection (new_regions);
2483
2484         if (new_regions.empty()) {
2485                 return;
2486         }
2487
2488         /* XXX fix me one day to use all new regions */
2489
2490         boost::shared_ptr<Region> region (new_regions.front());
2491
2492         /* add it to the current stream/playlist.
2493
2494            tricky: the streamview for the track will add a new regionview. we will
2495            catch the signal it sends when it creates the regionview to
2496            set the regionview we want to then drag.
2497         */
2498
2499         latest_regionviews.clear();
2500         sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2501
2502         /* A selection grab currently creates two undo/redo operations, one for
2503            creating the new region and another for moving it.
2504         */
2505         begin_reversible_command (Operations::selection_grab);
2506
2507         boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2508
2509         playlist->clear_changes ();
2510         clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2511         _session->add_command(new StatefulDiffCommand (playlist));
2512
2513         c.disconnect ();
2514
2515         if (latest_regionviews.empty()) {
2516                 /* something went wrong */
2517                 abort_reversible_command ();
2518                 return;
2519         }
2520
2521         /* we need to deselect all other regionviews, and select this one
2522            i'm ignoring undo stuff, because the region creation will take care of it
2523         */
2524
2525         selection->set (latest_regionviews);
2526
2527         commit_reversible_command ();
2528
2529         _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2530 }
2531
2532 void
2533 Editor::escape ()
2534 {
2535         if (_drags->active ()) {
2536                 _drags->abort ();
2537         } else {
2538                 selection->clear ();
2539         }
2540
2541         ARDOUR_UI::instance()->reset_focus (&contents());
2542 }
2543
2544 /** Update _join_object_range_state which indicate whether we are over the top
2545  *  or bottom half of a route view, used by the `join object/range' tool
2546  *  mode. Coordinates in canvas space.
2547  */
2548 void
2549 Editor::update_join_object_range_location (double y)
2550 {
2551         if (!get_smart_mode()) {
2552                 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2553                 return;
2554         }
2555
2556         JoinObjectRangeState const old = _join_object_range_state;
2557
2558         if (mouse_mode == MouseObject) {
2559                 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2560         } else if (mouse_mode == MouseRange) {
2561                 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2562         }
2563
2564         if (entered_regionview) {
2565
2566                 //ToDo:  there is currently a bug here(?)
2567                 //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
2568                 //can it be fixed here?
2569
2570                 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2571                 double const c = item_space.y / entered_regionview->height();
2572
2573                 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2574
2575                 Editor::EnterContext* ctx = get_enter_context(RegionItem);
2576                 if (_join_object_range_state != old && ctx) {
2577                         ctx->cursor_ctx->change(which_track_cursor());
2578                 }
2579
2580         } else if (entered_track) {
2581
2582                 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2583
2584                 if (entered_route_view) {
2585
2586                         double cx = 0;
2587                         double cy = y;
2588
2589                         entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2590
2591                         double track_height = entered_route_view->view()->child_height();
2592                         if (UIConfiguration::instance().get_show_name_highlight()) {
2593                                 track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2594                         }
2595                         double const c = cy / track_height;
2596
2597
2598                         if (c <= 0.5) {
2599                                 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2600                         } else {
2601                                 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2602                         }
2603
2604                 } else {
2605                         /* Other kinds of tracks use object mode */
2606                         _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2607                 }
2608
2609                 Editor::EnterContext* ctx = get_enter_context(StreamItem);
2610                 if (_join_object_range_state != old && ctx) {
2611                         ctx->cursor_ctx->change(which_track_cursor());
2612                 }
2613         }
2614 }
2615
2616 Editing::MouseMode
2617 Editor::effective_mouse_mode () const
2618 {
2619         if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2620                 return MouseObject;
2621         } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2622                 return MouseRange;
2623         }
2624
2625         return mouse_mode;
2626 }
2627
2628 void
2629 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2630 {
2631         NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2632         assert (e);
2633
2634         e->region_view().delete_note (e->note ());
2635 }
2636
2637 /** Obtain the pointer position in canvas coordinates */
2638 void
2639 Editor::get_pointer_position (double& x, double& y) const
2640 {
2641         int px, py;
2642         _track_canvas->get_pointer (px, py);
2643         _track_canvas->window_to_canvas (px, py, x, y);
2644 }