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