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