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