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