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