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