2 * Copyright (C) 2005-2006 Taybin Rutkin <taybin@taybin.com>
3 * Copyright (C) 2005-2018 Paul Davis <paul@linuxaudiosystems.com>
4 * Copyright (C) 2006-2015 David Robillard <d@drobilla.net>
5 * Copyright (C) 2006-2016 Tim Mayberry <mojofunk@gmail.com>
6 * Copyright (C) 2006 Sampo Savolainen <v2@iki.fi>
7 * Copyright (C) 2007-2012 Carl Hetherington <carl@carlh.net>
8 * Copyright (C) 2007 Doug McLain <doug@nostar.net>
9 * Copyright (C) 2013-2014 Colin Fletcher <colin.m.fletcher@googlemail.com>
10 * Copyright (C) 2013-2019 Robin Gareus <robin@gareus.org>
11 * Copyright (C) 2014-2017 Nick Mainsbridge <mainsbridge@gmail.com>
12 * Copyright (C) 2014-2018 Ben Loftis <ben@harrisonconsoles.com>
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License along
25 * with this program; if not, write to the Free Software Foundation, Inc.,
26 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
38 #include "pbd/error.h"
39 #include "pbd/enumwriter.h"
40 #include "pbd/memento_command.h"
41 #include "pbd/basename.h"
42 #include "pbd/stateful_diff_command.h"
44 #include "gtkmm2ext/bindings.h"
45 #include "gtkmm2ext/utils.h"
47 #include "canvas/canvas.h"
49 #include "ardour/audioplaylist.h"
50 #include "ardour/audioregion.h"
51 #include "ardour/operations.h"
52 #include "ardour/playlist.h"
53 #include "ardour/profile.h"
54 #include "ardour/region_factory.h"
55 #include "ardour/route.h"
56 #include "ardour/session.h"
57 #include "ardour/types.h"
59 #include "widgets/prompter.h"
62 #include "ardour_ui.h"
64 #include "time_axis_view.h"
65 #include "audio_time_axis.h"
66 #include "audio_region_view.h"
67 #include "midi_region_view.h"
69 #include "streamview.h"
70 #include "region_gain_line.h"
71 #include "rc_option_editor.h"
72 #include "automation_time_axis.h"
73 #include "control_point.h"
74 #include "selection.h"
77 #include "rgb_macros.h"
78 #include "control_point_dialog.h"
79 #include "editor_drag.h"
80 #include "automation_region_view.h"
81 #include "edit_note_dialog.h"
82 #include "mouse_cursors.h"
83 #include "editor_cursors.h"
84 #include "verbose_cursor.h"
90 using namespace ARDOUR;
93 using namespace Editing;
94 using Gtkmm2ext::Keyboard;
97 Editor::mouse_sample (samplepos_t& where, bool& in_track_canvas) const
99 /* gdk_window_get_pointer() has X11's XQueryPointer semantics in that it only
100 * pays attentions to subwindows. this means that menu windows are ignored, and
101 * if the pointer is in a menu, the return window from the call will be the
102 * the regular subwindow *under* the menu.
104 * this matters quite a lot if the pointer is moving around in a menu that overlaps
105 * the track canvas because we will believe that we are within the track canvas
106 * when we are not. therefore, we track enter/leave events for the track canvas
107 * and allow that to override the result of gdk_window_get_pointer().
110 if (!within_track_canvas) {
115 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas->get_window();
117 if (!canvas_window) {
121 Glib::RefPtr<const Gdk::Window> pointer_window = Gdk::Display::get_default()->get_window_at_pointer (x, y);
123 if (!pointer_window) {
127 if (pointer_window != canvas_window) {
128 in_track_canvas = false;
132 in_track_canvas = true;
135 event.type = GDK_BUTTON_RELEASE;
139 where = window_event_sample (&event, 0, 0);
145 Editor::window_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
147 ArdourCanvas::Duple d;
149 if (!gdk_event_get_coords (event, &d.x, &d.y)) {
153 /* event coordinates are in window units, so convert to canvas
156 d = _track_canvas->window_to_canvas (d);
166 return pixel_to_sample (d.x);
170 Editor::canvas_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
175 /* event coordinates are already in canvas units */
177 if (!gdk_event_get_coords (event, &x, &y)) {
178 cerr << "!NO c COORDS for event type " << event->type << endl;
190 /* note that pixel_to_sample_from_event() never returns less than zero, so even if the pixel
191 position is negative (as can be the case with motion events in particular),
192 the sample location is always positive.
195 return pixel_to_sample_from_event (x);
199 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
201 boost::shared_ptr<Trimmable> st = _trimmable.lock();
203 if (!st || st == t) {
209 Editor::set_current_movable (boost::shared_ptr<Movable> m)
211 boost::shared_ptr<Movable> sm = _movable.lock();
213 if (!sm || sm != m) {
219 Editor::mouse_mode_object_range_toggled()
221 set_mouse_mode (mouse_mode, true); /* updates set-mouse-mode-range */
225 Editor::snap_mode_button_clicked (GdkEventButton* ev)
227 if (ev->button != 3) {
232 RCOptionEditor* rc_option_editor = ARDOUR_UI::instance()->get_rc_option_editor();
233 if (rc_option_editor) {
234 ARDOUR_UI::instance()->show_tabbable (rc_option_editor);
235 rc_option_editor->set_current_page (_("Editor/Snap"));
244 Editor::get_mouse_mode_action(MouseMode m) const
248 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
250 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
252 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-cut"));
254 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
256 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
258 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-content"));
260 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
262 return Glib::RefPtr<Action>();
266 Editor::set_mouse_mode (MouseMode m, bool force)
268 if (_drags->active ()) {
272 if (!force && m == mouse_mode) {
276 if (ARDOUR::Profile->get_mixbus()) {
277 if (m == MouseCut) m = MouseObject;
278 if (m == MouseAudition) m = MouseRange;
281 Glib::RefPtr<Action> act = get_mouse_mode_action(m);
282 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
284 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
285 tact->set_active (false);
286 tact->set_active (true);
288 /* NOTE: this will result in a call to mouse_mode_toggled which does the heavy lifting */
292 Editor::mouse_mode_toggled (MouseMode m)
294 if (ARDOUR::Profile->get_mixbus()) {
295 if (m == MouseCut) m = MouseObject;
296 if (m == MouseAudition) m = MouseRange;
299 Glib::RefPtr<Action> act = get_mouse_mode_action(m);
300 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
302 if (!tact->get_active()) {
303 /* this was just the notification that the old mode has been
304 * left. we'll get called again with the new mode active in a
310 if (_session && mouse_mode == MouseAudition) {
311 /* stop transport and reset default speed to avoid oddness with
313 _session->request_transport_speed (0.0, true);
316 const bool was_internal = internal_editing();
320 /* Switch snap type/mode if we're moving to/from an internal tool. Note
321 this must toggle the actions and not call set_snap_*() directly,
322 otherwise things get out of sync and the combo box stops working. */
323 if (!UIConfiguration::instance().get_grid_follows_internal()) {
324 grid_type_action(pre_internal_grid_type)->set_active(true);
325 snap_mode_action(pre_internal_snap_mode)->set_active(true);
326 } else if (!was_internal && internal_editing()) {
327 grid_type_action(internal_grid_type)->set_active(true);
328 snap_mode_action(internal_snap_mode)->set_active(true);
329 } else if (was_internal && !internal_editing()) {
330 grid_type_action(pre_internal_grid_type)->set_active(true);
331 snap_mode_action(pre_internal_snap_mode)->set_active(true);
336 /* this should generate a new enter event which will
337 trigger the appropiate cursor.
341 _track_canvas->re_enter ();
344 set_gain_envelope_visibility ();
346 update_time_selection_display ();
348 update_all_enter_cursors ();
350 MouseModeChanged (); /* EMIT SIGNAL */
354 Editor::internal_editing() const
356 return mouse_mode == Editing::MouseContent || mouse_mode == Editing::MouseDraw;
360 Editor::update_time_selection_display ()
362 switch (mouse_mode) {
364 selection->clear_objects ();
365 selection->clear_midi_notes ();
368 selection->clear_time ();
369 selection->clear_midi_notes ();
372 /* Clear regions, but not time or tracks, since that
373 would destroy the range selection rectangle, which we need to stick
374 around for AutomationRangeDrag. */
375 selection->clear_regions ();
376 selection->clear_playlists ();
379 /* This handles internal edit.
380 Clear everything except points and notes.
382 selection->clear_regions();
383 selection->clear_lines();
384 selection->clear_playlists ();
386 selection->clear_time ();
387 selection->clear_tracks ();
391 /* We probably want to keep region selection */
392 selection->clear_points ();
393 selection->clear_lines();
394 selection->clear_playlists ();
396 selection->clear_time ();
397 selection->clear_tracks ();
401 /*Don't lose lines or points if no action in this mode */
402 selection->clear_regions ();
403 selection->clear_playlists ();
404 selection->clear_time ();
405 selection->clear_tracks ();
409 /*Clear everything */
410 selection->clear_objects();
411 selection->clear_time ();
412 selection->clear_tracks ();
418 Editor::step_mouse_mode (bool next)
420 const int n_mouse_modes = (int)MouseContent + 1;
421 int current = (int)current_mouse_mode();
423 set_mouse_mode((MouseMode)((current + 1) % n_mouse_modes));
425 set_mouse_mode((MouseMode)((current + n_mouse_modes - 1) % n_mouse_modes));
430 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
432 /* in object/audition/timefx/gain-automation mode,
433 * any button press sets the selection if the object
434 * can be selected. this is a bit of hack, because
435 * we want to avoid this if the mouse operation is a
438 * note: not dbl-click or triple-click
440 * Also note that there is no region selection in internal edit mode, otherwise
441 * for operations operating on the selection (e.g. cut) it is not obvious whether
442 * to cut notes or regions.
445 MouseMode eff_mouse_mode = effective_mouse_mode ();
447 if (eff_mouse_mode == MouseCut) {
448 /* never change selection in cut mode */
452 if (get_smart_mode() && eff_mouse_mode == MouseRange && event->button.button == 3 && item_type == RegionItem) {
453 /* context clicks are always about object properties, even if
454 * we're in range mode within smart mode.
456 eff_mouse_mode = MouseObject;
459 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
460 if (get_smart_mode()) {
462 case FadeInHandleItem:
463 case FadeInTrimHandleItem:
464 case FadeOutHandleItem:
465 case FadeOutTrimHandleItem:
466 eff_mouse_mode = MouseObject;
473 if (((mouse_mode != MouseObject) &&
474 (mouse_mode != MouseAudition || item_type != RegionItem) &&
475 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
476 (mouse_mode != MouseDraw) &&
477 (mouse_mode != MouseContent || item_type == RegionItem)) ||
478 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
482 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
484 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
486 /* almost no selection action on modified button-2 or button-3 events */
488 if ((item_type != RegionItem && event->button.button != 2)
489 /* for selection of control points prior to delete (shift-right click) */
490 && !(item_type == ControlPointItem && event->button.button == 3 && event->type == GDK_BUTTON_PRESS)) {
496 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
497 bool press = (event->type == GDK_BUTTON_PRESS);
500 _mouse_changed_selection = false;
505 if (eff_mouse_mode == MouseDraw) {
509 if (eff_mouse_mode != MouseRange) {
510 _mouse_changed_selection = set_selected_regionview_from_click (press, op);
512 /* don't change the selection unless the
513 * clicked track is not currently selected. if
514 * so, "collapse" the selection to just this track
516 if (!selection->selected (clicked_axisview)) {
517 set_selected_track_as_side_effect (Selection::Set);
521 if (eff_mouse_mode != MouseRange) {
522 _mouse_changed_selection |= set_selected_regionview_from_click (press, op);
527 case RegionViewNameHighlight:
529 case LeftFrameHandle:
530 case RightFrameHandle:
531 case FadeInHandleItem:
532 case FadeInTrimHandleItem:
534 case FadeOutHandleItem:
535 case FadeOutTrimHandleItem:
537 case StartCrossFadeItem:
538 case EndCrossFadeItem:
539 if (get_smart_mode() || eff_mouse_mode != MouseRange) {
540 _mouse_changed_selection |= set_selected_regionview_from_click (press, op);
541 } else if (event->type == GDK_BUTTON_PRESS) {
542 set_selected_track_as_side_effect (op);
546 case ControlPointItem:
547 /* for object/track exclusivity, we don't call set_selected_track_as_side_effect (op); */
549 if (eff_mouse_mode != MouseRange) {
550 if (event->button.button != 3) {
551 _mouse_changed_selection |= set_selected_control_point_from_click (press, op);
553 _mouse_changed_selection |= set_selected_control_point_from_click (press, Selection::Set);
559 if (eff_mouse_mode != MouseRange) {
560 AutomationLine* argl = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
562 std::list<Selectable*> selectables;
563 uint32_t before, after;
564 samplecnt_t const where = (samplecnt_t) floor (event->button.x * samples_per_pixel) - clicked_regionview->region ()->position ();
566 if (!argl || !argl->control_points_adjacent (where, before, after)) {
570 selectables.push_back (argl->nth (before));
571 selectables.push_back (argl->nth (after));
576 selection->set (selectables);
577 _mouse_changed_selection = true;
582 selection->add (selectables);
583 _mouse_changed_selection = true;
586 case Selection::Toggle:
588 selection->toggle (selectables);
589 _mouse_changed_selection = true;
593 case Selection::Extend:
600 case AutomationLineItem:
601 if (eff_mouse_mode != MouseRange) {
602 AutomationLine* al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
603 std::list<Selectable*> selectables;
604 double mx = event->button.x;
605 double my = event->button.y;
607 al->grab_item().canvas_to_item (mx, my);
609 uint32_t before, after;
610 samplecnt_t const where = (samplecnt_t) floor (mx * samples_per_pixel);
612 if (!al || !al->control_points_adjacent (where, before, after)) {
616 selectables.push_back (al->nth (before));
617 selectables.push_back (al->nth (after));
622 selection->set (selectables);
623 _mouse_changed_selection = true;
628 selection->add (selectables);
629 _mouse_changed_selection = true;
632 case Selection::Toggle:
634 selection->toggle (selectables);
635 _mouse_changed_selection = true;
639 case Selection::Extend:
647 /* for context click, select track */
648 if (event->button.button == 3) {
649 selection->clear_tracks ();
650 set_selected_track_as_side_effect (op);
652 /* We won't get a release.*/
653 begin_reversible_selection_op (X_("Button 3 Menu Select"));
654 commit_reversible_selection_op ();
658 case AutomationTrackItem:
659 if (eff_mouse_mode != MouseDraw && op == Selection::Set) {
660 set_selected_track_as_side_effect (op);
665 if (press && event->button.button == 3) {
666 NoteBase* cnote = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
668 if (cnote->region_view().selection_size() == 0 || !cnote->selected()) {
669 selection->clear_points();
670 cnote->region_view().unique_select (cnote);
671 /* we won't get the release, so store the selection change now */
672 begin_reversible_selection_op (X_("Button 3 Note Selection"));
673 commit_reversible_selection_op ();
682 if ((!press) && _mouse_changed_selection) {
683 begin_reversible_selection_op (X_("Button Selection"));
684 commit_reversible_selection_op ();
685 _mouse_changed_selection = false;
690 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
692 /* single mouse clicks on any of these item types operate
693 * independent of mouse mode, mostly because they are
694 * not on the main track canvas or because we want
695 * them to be modeless.
698 NoteBase* note = NULL;
701 case PlayheadCursorItem:
702 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
706 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
707 hide_marker (item, event);
709 _drags->set (new MarkerDrag (this, item), event);
713 case TempoMarkerItem:
715 if (ArdourKeyboard::indicates_constraint (event->button.state)) {
725 new TempoMarkerDrag (
728 ArdourKeyboard::indicates_copy (event->button.state)
737 case MeterMarkerItem:
740 new MeterMarkerDrag (
743 ArdourKeyboard::indicates_copy (event->button.state)
751 _drags->set (new VideoTimeLineDrag (this, item), event);
759 case TimecodeRulerItem:
760 case SamplesRulerItem:
761 case MinsecRulerItem:
763 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)
764 && !ArdourKeyboard::indicates_constraint (event->button.state)) {
765 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
766 } else if (ArdourKeyboard::indicates_constraint (event->button.state)
767 && Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
768 _drags->set (new TempoTwistDrag (this, item), event);
769 } else if (ArdourKeyboard::indicates_constraint (event->button.state)) {
770 _drags->set (new BBTRulerDrag (this, item), event);
776 case RangeMarkerBarItem:
777 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
778 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateSkipMarker), event);
779 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
780 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
782 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
787 case CdMarkerBarItem:
788 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
789 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
791 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
796 case TransportMarkerBarItem:
797 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
798 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
800 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
809 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
810 /* special case: allow trim of range selections in joined object mode;
811 * in theory eff should equal MouseRange in this case, but it doesn't
812 * because entering the range selection canvas item results in entered_regionview
813 * being set to 0, so update_join_object_range_location acts as if we aren't
816 if (item_type == StartSelectionTrimItem) {
817 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
818 } else if (item_type == EndSelectionTrimItem) {
819 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
823 Editing::MouseMode eff = effective_mouse_mode ();
825 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
826 if (get_smart_mode()) {
828 case FadeInHandleItem:
829 case FadeInTrimHandleItem:
830 case FadeOutHandleItem:
831 case FadeOutTrimHandleItem:
842 case StartSelectionTrimItem:
843 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
846 case EndSelectionTrimItem:
847 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
851 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
852 start_selection_grab (item, event);
854 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
855 /* grab selection for moving */
856 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
858 /* this was debated, but decided the more common action was to make a new selection */
859 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
864 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
865 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
867 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
872 case RegionViewNameHighlight:
873 if (!clicked_regionview->region()->locked()) {
874 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
880 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
881 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
883 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
892 case FadeInHandleItem:
893 case FadeOutHandleItem:
894 case LeftFrameHandle:
895 case RightFrameHandle:
896 case FeatureLineItem:
897 case RegionViewNameHighlight:
900 case AutomationTrackItem:
901 _drags->set (new RegionCutDrag (this, item, canvas_event_sample (event)), event, get_canvas_cursor());
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());
917 _drags->set (new NoteDrag (this, item), event);
923 _drags->set (new LineDrag (this, item), event);
927 case ControlPointItem:
928 _drags->set (new ControlPointDrag (this, item), event);
932 case AutomationLineItem:
933 _drags->set (new LineDrag (this, item), event);
938 /* in the past, we created a new midi region here, but perhaps that is best left to the Draw mode */
941 case AutomationTrackItem:
942 /* rubberband drag to select automation points */
943 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
948 if (dynamic_cast<AutomationRegionView*>(clicked_regionview)) {
949 /* rubberband drag to select automation points */
950 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
961 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
962 event->type == GDK_BUTTON_PRESS) {
964 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
966 } else if (event->type == GDK_BUTTON_PRESS) {
969 case FadeInHandleItem:
971 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
975 case FadeOutHandleItem:
977 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
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 */
986 if (!clicked_regionview->region()->locked()) {
987 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
993 case FeatureLineItem:
995 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
996 remove_transient(item);
1000 _drags->set (new FeatureLineDrag (this, item), event);
1006 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1007 /* click on an automation region view; do nothing here and let the ARV's signal handler
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);
1019 add_region_drag (item, event, clicked_regionview);
1023 _drags->start_grab (event);
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);
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);
1044 case RegionViewName:
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);
1054 case ControlPointItem:
1055 _drags->set (new ControlPointDrag (this, item), event);
1059 case AutomationLineItem:
1060 _drags->set (new LineDrag (this, item), event);
1065 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1068 case AutomationTrackItem:
1070 TimeAxisView* parent = clicked_axisview->get_parent ();
1071 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1073 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1075 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
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);
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);
1086 _drags->set (new RegionCreateDrag (this, item, parent), event);
1088 prev->set_length (t - prev->position (), get_grid_music_divisions (event->button.state));
1092 /* rubberband drag to select automation points */
1093 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1115 switch (item_type) {
1117 _drags->set (new LineDrag (this, item), event);
1120 case ControlPointItem:
1121 _drags->set (new ControlPointDrag (this, item), event);
1127 if (selection->time.empty ()) {
1131 pair<TimeAxisView*, int> tvp = trackview_by_y_position (event->button.y, false);
1133 /* clicked outside of a track */
1136 /* handle automation lanes first */
1137 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1139 /* smart "join" mode: drag automation */
1140 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1143 if (dynamic_cast<AutomationRegionView*>(clicked_regionview)) {
1144 /* MIDI CC or similar -- TODO handle multiple? */
1145 list<RegionView*> rvl;
1146 rvl.push_back (clicked_regionview);
1147 _drags->set (new AutomationRangeDrag (this, rvl, selection->time,
1148 clicked_regionview->get_time_axis_view().y_position(),
1149 clicked_regionview->get_time_axis_view().current_height()),
1150 event, _cursors->up_down);
1154 /* shift+drag: only apply to clicked_regionview (if any) */
1155 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
1156 if (dynamic_cast<AudioRegionView*>(clicked_regionview) == 0) {
1159 list<RegionView*> rvl;
1160 rvl.push_back (clicked_regionview);
1161 // TODO: handle layer_display() == Stacked
1162 _drags->set (new AutomationRangeDrag (this, rvl, selection->time,
1163 clicked_regionview->get_time_axis_view().y_position(),
1164 clicked_regionview->get_time_axis_view().current_height()),
1165 event, _cursors->up_down);
1169 /* collect all audio regions-views in the given range selection */
1170 list<RegionView*> rvl;
1171 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
1172 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
1173 RouteTimeAxisView* tatv;
1174 boost::shared_ptr<Playlist> playlist;
1175 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) == 0) {
1178 if ((playlist = (*i)->playlist()) == 0) {
1181 if (boost::dynamic_pointer_cast<AudioPlaylist> (playlist) == 0) {
1184 for (list<AudioRange>::const_iterator j = selection->time.begin(); j != selection->time.end(); ++j) {
1185 boost::shared_ptr<RegionList> rl = playlist->regions_touched (j->start, j->end);
1186 for (RegionList::iterator ir = rl->begin(); ir != rl->end(); ++ir) {
1188 if ((rv = tatv->view()->find_view (*ir)) != 0) {
1194 /* region-gain drag */
1195 if (!rvl.empty ()) {
1196 double y_pos = tvp.first->y_position();
1197 double height = tvp.first->current_height();
1198 StreamView* cv = tvp.first->view ();
1199 if (cv->layer_display() == Stacked && cv->layers() > 1) {
1200 height /= cv->layers();
1201 double yy = event->button.y - _trackview_group->canvas_origin().y;
1202 y_pos += floor ((yy - y_pos) / height) * height;
1204 _drags->set (new AutomationRangeDrag (this, rvl, selection->time, y_pos, height),
1205 event, _cursors->up_down);
1211 case AutomationLineItem:
1212 _drags->set (new LineDrag (this, item), event);
1216 if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
1217 if (note->big_enough_to_trim() && note->mouse_near_ends()) {
1218 /* Note is big and pointer is near the end, trim */
1219 _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
1222 _drags->set (new NoteDrag (this, item), event);
1229 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1230 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1241 if (item_type == NoteItem) {
1242 /* resize-drag notes */
1243 if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
1244 if (note->big_enough_to_trim()) {
1245 _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
1249 } else if (clicked_regionview) {
1251 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1257 _drags->set (new ScrubDrag (this, item), event, _cursors->transparent);
1258 scrub_reversals = 0;
1259 scrub_reverse_distance = 0;
1260 last_scrub_x = event->button.x;
1261 scrubbing_direction = 0;
1273 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1275 Editing::MouseMode const eff = effective_mouse_mode ();
1278 switch (item_type) {
1280 if (ArdourKeyboard::indicates_copy (event->button.state)) {
1281 add_region_copy_drag (item, event, clicked_regionview);
1283 add_region_drag (item, event, clicked_regionview);
1285 _drags->start_grab (event);
1288 case ControlPointItem:
1289 _drags->set (new ControlPointDrag (this, item), event);
1297 switch (item_type) {
1298 case RegionViewNameHighlight:
1299 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1303 case LeftFrameHandle:
1304 case RightFrameHandle:
1305 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1309 case RegionViewName:
1310 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1324 /* relax till release */
1336 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1338 if (event->type == GDK_2BUTTON_PRESS) {
1339 _drags->mark_double_click ();
1340 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1344 if (event->type != GDK_BUTTON_PRESS) {
1348 _track_canvas->grab_focus();
1350 if (_session && _session->actively_recording()) {
1354 button_selection (item, event, item_type);
1356 if (!_drags->active () &&
1357 (Keyboard::is_delete_event (&event->button) ||
1358 Keyboard::is_context_menu_event (&event->button) ||
1359 Keyboard::is_edit_event (&event->button))) {
1361 /* handled by button release */
1365 /* not rolling, effectively in range mode, follow edits enabled (likely
1366 * to start range drag), not in a fade handle (since that means we are
1367 * not starting a range drag): locate the PH here
1370 if ((item_type != FadeInHandleItem) &&
1371 (item_type != FadeOutHandleItem) &&
1372 !_drags->active () &&
1374 !_session->transport_rolling() &&
1375 (effective_mouse_mode() == MouseRange) &&
1376 UIConfiguration::instance().get_follow_edits() &&
1377 !_session->config.get_external_sync()) {
1379 MusicSample where (canvas_event_sample (event), 0);
1381 _session->request_locate (where.sample, false);
1384 switch (event->button.button) {
1386 return button_press_handler_1 (item, event, item_type);
1390 return button_press_handler_2 (item, event, item_type);
1397 return button_press_dispatch (&event->button);
1406 Editor::button_press_dispatch (GdkEventButton* ev)
1408 /* this function is intended only for buttons 4 and above. */
1410 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1411 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1415 Editor::button_release_dispatch (GdkEventButton* ev)
1417 /* this function is intended only for buttons 4 and above. */
1419 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1420 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1424 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1426 MusicSample where (canvas_event_sample (event), 0);
1427 AutomationTimeAxisView* atv = 0;
1429 _press_cursor_ctx.reset();
1431 /* no action if we're recording */
1433 if (_session && _session->actively_recording()) {
1437 bool were_dragging = false;
1439 if (!Keyboard::is_context_menu_event (&event->button)) {
1441 /* see if we're finishing a drag */
1443 if (_drags->active ()) {
1444 bool const r = _drags->end_grab (event);
1446 /* grab dragged, so do nothing else */
1450 were_dragging = true;
1453 update_region_layering_order_editor ();
1456 /* edit events get handled here */
1458 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1459 switch (item_type) {
1461 show_region_properties ();
1463 case TempoMarkerItem: {
1464 ArdourMarker* marker;
1465 TempoMarker* tempo_marker;
1467 if ((marker = reinterpret_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
1468 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1469 abort(); /*NOTREACHED*/
1472 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1473 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1474 abort(); /*NOTREACHED*/
1477 edit_tempo_marker (*tempo_marker);
1481 case MeterMarkerItem: {
1482 ArdourMarker* marker;
1483 MeterMarker* meter_marker;
1485 if ((marker = reinterpret_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
1486 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1487 abort(); /*NOTREACHED*/
1490 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1491 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1492 abort(); /*NOTREACHED*/
1494 edit_meter_marker (*meter_marker);
1498 case RegionViewName:
1499 if (clicked_regionview->name_active()) {
1500 return mouse_rename_region (item, event);
1504 case ControlPointItem:
1505 edit_control_point (item);
1514 /* context menu events get handled here */
1515 if (Keyboard::is_context_menu_event (&event->button)) {
1517 context_click_event = *event;
1519 if (!_drags->active ()) {
1521 /* no matter which button pops up the context menu, tell the menu
1522 widget to use button 1 to drive menu selection.
1525 switch (item_type) {
1527 case FadeInHandleItem:
1528 case FadeInTrimHandleItem:
1529 case StartCrossFadeItem:
1530 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1534 case FadeOutHandleItem:
1535 case FadeOutTrimHandleItem:
1536 case EndCrossFadeItem:
1537 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1540 case LeftFrameHandle:
1541 case RightFrameHandle:
1545 popup_track_context_menu (1, event->button.time, item_type, false);
1549 case RegionViewNameHighlight:
1550 case RegionViewName:
1551 popup_track_context_menu (1, event->button.time, item_type, false);
1555 popup_track_context_menu (1, event->button.time, item_type, true);
1558 case AutomationTrackItem:
1559 popup_track_context_menu (1, event->button.time, item_type, false);
1563 case RangeMarkerBarItem:
1564 case TransportMarkerBarItem:
1565 case CdMarkerBarItem:
1567 case TempoCurveItem:
1570 case TimecodeRulerItem:
1571 case SamplesRulerItem:
1572 case MinsecRulerItem:
1574 popup_ruler_menu (where.sample, item_type);
1578 marker_context_menu (&event->button, item);
1581 case TempoMarkerItem:
1582 tempo_or_meter_marker_context_menu (&event->button, item);
1585 case MeterMarkerItem:
1586 tempo_or_meter_marker_context_menu (&event->button, item);
1589 case CrossfadeViewItem:
1590 popup_track_context_menu (1, event->button.time, item_type, false);
1593 case ControlPointItem:
1594 popup_control_point_context_menu (item, event);
1598 if (internal_editing()) {
1599 popup_note_context_menu (item, event);
1611 /* delete events get handled here */
1613 Editing::MouseMode const eff = effective_mouse_mode ();
1615 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1617 switch (item_type) {
1618 case TempoMarkerItem:
1619 remove_tempo_marker (item);
1622 case MeterMarkerItem:
1623 remove_meter_marker (item);
1627 remove_marker (*item, event);
1631 if (eff == MouseObject) {
1632 remove_clicked_region ();
1636 case ControlPointItem:
1637 remove_control_point (item);
1641 remove_midi_note (item, event);
1650 switch (event->button.button) {
1653 switch (item_type) {
1654 /* see comments in button_press_handler */
1655 case PlayheadCursorItem:
1658 case AutomationLineItem:
1659 case StartSelectionTrimItem:
1660 case EndSelectionTrimItem:
1664 if (!_dragging_playhead) {
1665 snap_to_with_modifier (where, event, RoundNearest, SnapToGrid_Scaled);
1666 mouse_add_new_marker (where.sample);
1670 case CdMarkerBarItem:
1671 if (!_dragging_playhead) {
1672 /* if we get here then a dragged range wasn't done */
1673 snap_to_with_modifier (where, event, RoundNearest, SnapToGrid_Scaled);
1674 mouse_add_new_marker (where.sample, true);
1678 case TempoCurveItem:
1679 if (!_dragging_playhead && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1680 snap_to_with_modifier (where, event);
1681 mouse_add_new_tempo_event (where.sample);
1686 if (!_dragging_playhead && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1687 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1692 case TimecodeRulerItem:
1693 case SamplesRulerItem:
1694 case MinsecRulerItem:
1705 switch (item_type) {
1708 /* check that we didn't drag before releasing, since
1709 its really annoying to create new control
1710 points when doing this.
1712 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1713 if (!were_dragging && arv) {
1714 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1715 arv->add_gain_point_event (item, event, with_guard_points);
1721 case AutomationTrackItem: {
1722 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1723 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1725 atv->add_automation_event (event, where.sample, event->button.y, with_guard_points);
1736 if (scrubbing_direction == 0) {
1737 /* no drag, just a click */
1738 switch (item_type) {
1740 play_selected_region ();
1745 } else if (_session) {
1746 /* make sure we stop */
1747 _session->request_transport_speed (0.0);
1756 /* do any (de)selection operations that should occur on button release */
1757 button_selection (item, event, item_type);
1767 switch (item_type) {
1769 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1771 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1774 /* Button2 click is unused */
1789 // x_style_paste (where, 1.0);
1810 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1813 ArdourMarker * marker;
1814 MeterMarker* m_marker = 0;
1815 TempoMarker* t_marker = 0;
1819 /* by the time we reach here, entered_regionview and entered trackview
1820 * will have already been set as appropriate. Things are done this
1821 * way because this method isn't passed a pointer to a variable type of
1822 * thing that is entered (which may or may not be canvas item).
1823 * (e.g. the actual entered regionview)
1826 choose_canvas_cursor_on_entry (item_type);
1828 switch (item_type) {
1829 case ControlPointItem:
1830 if (mouse_mode == MouseDraw || mouse_mode == MouseObject || mouse_mode == MouseContent) {
1831 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1834 fraction = 1.0 - (cp->get_y() / cp->line().height());
1836 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1837 _verbose_cursor->show ();
1842 if (mouse_mode == MouseDraw) {
1843 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1845 line->set_outline_color (UIConfiguration::instance().color ("entered gain line"));
1850 case AutomationLineItem:
1851 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1852 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1854 line->set_outline_color (UIConfiguration::instance().color ("entered automation line"));
1859 case AutomationTrackItem:
1860 AutomationTimeAxisView* atv;
1861 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1862 clear_entered_track = false;
1863 set_entered_track (atv);
1868 if ((marker = static_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
1871 entered_marker = marker;
1872 marker->set_color_rgba (UIConfiguration::instance().color ("entered marker"));
1875 case MeterMarkerItem:
1876 if ((m_marker = static_cast<MeterMarker *> (item->get_data ("marker"))) == 0) {
1879 entered_marker = m_marker;
1880 if (m_marker->meter().position_lock_style() == MusicTime) {
1881 m_marker->set_color_rgba (UIConfiguration::instance().color ("meter marker"));
1883 m_marker->set_color_rgba (UIConfiguration::instance().color ("meter marker music"));
1887 case TempoMarkerItem:
1888 if ((t_marker = static_cast<TempoMarker *> (item->get_data ("marker"))) == 0) {
1891 entered_marker = t_marker;
1892 if (t_marker->tempo().position_lock_style() == MusicTime) {
1893 t_marker->set_color_rgba (UIConfiguration::instance().color ("tempo marker"));
1895 t_marker->set_color_rgba (UIConfiguration::instance().color ("tempo marker music"));
1899 case FadeInHandleItem:
1900 case FadeInTrimHandleItem:
1901 if (mouse_mode == MouseObject) {
1902 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1904 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1905 rect->set_fill_color (rv->get_fill_color());
1910 case FadeOutHandleItem:
1911 case FadeOutTrimHandleItem:
1912 if (mouse_mode == MouseObject) {
1913 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1915 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1916 rect->set_fill_color (rv->get_fill_color ());
1921 case FeatureLineItem:
1923 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1924 line->set_outline_color (0xFF0000FF);
1933 if (entered_regionview) {
1934 entered_regionview->entered();
1943 /* third pass to handle entered track status in a comprehensible way.
1946 switch (item_type) {
1948 case AutomationLineItem:
1949 case ControlPointItem:
1950 /* these do not affect the current entered track state */
1951 clear_entered_track = false;
1954 case AutomationTrackItem:
1955 /* handled above already */
1967 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1970 ArdourMarker *marker;
1971 TempoMarker *t_marker;
1972 MeterMarker *m_marker;
1977 if (!_enter_stack.empty()) {
1978 _enter_stack.pop_back();
1981 switch (item_type) {
1982 case ControlPointItem:
1983 _verbose_cursor->hide ();
1987 case AutomationLineItem:
1988 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1990 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1992 line->set_outline_color (al->get_line_color());
1998 if ((marker = static_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
2002 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2003 location_flags_changed (loc);
2007 case MeterMarkerItem:
2008 if ((m_marker = static_cast<MeterMarker *> (item->get_data ("marker"))) == 0) {
2012 if (m_marker->meter().position_lock_style() == MusicTime) {
2013 m_marker->set_color_rgba (UIConfiguration::instance().color ("meter marker music"));
2015 m_marker->set_color_rgba (UIConfiguration::instance().color ("meter marker"));
2019 case TempoMarkerItem:
2020 if ((t_marker = static_cast<TempoMarker *> (item->get_data ("marker"))) == 0) {
2024 if (t_marker->tempo().position_lock_style() == MusicTime) {
2025 t_marker->set_color_rgba (UIConfiguration::instance().color ("tempo marker music"));
2027 t_marker->set_color_rgba (UIConfiguration::instance().color ("tempo marker"));
2031 case FadeInTrimHandleItem:
2032 case FadeOutTrimHandleItem:
2033 case FadeInHandleItem:
2034 case FadeOutHandleItem:
2036 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2038 rect->set_fill_color (UIConfiguration::instance().color ("inactive fade handle"));
2043 case AutomationTrackItem:
2046 case FeatureLineItem:
2048 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2049 line->set_outline_color (UIConfiguration::instance().color ("zero line"));
2061 Editor::scrub (samplepos_t sample, double current_x)
2065 if (scrubbing_direction == 0) {
2067 _session->request_locate (sample, false);
2068 _session->request_transport_speed (0.1);
2069 scrubbing_direction = 1;
2073 if (last_scrub_x > current_x) {
2075 /* pointer moved to the left */
2077 if (scrubbing_direction > 0) {
2079 /* we reversed direction to go backwards */
2082 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2086 /* still moving to the left (backwards) */
2088 scrub_reversals = 0;
2089 scrub_reverse_distance = 0;
2091 delta = 0.01 * (last_scrub_x - current_x);
2092 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2096 /* pointer moved to the right */
2098 if (scrubbing_direction < 0) {
2099 /* we reversed direction to go forward */
2102 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2105 /* still moving to the right */
2107 scrub_reversals = 0;
2108 scrub_reverse_distance = 0;
2110 delta = 0.01 * (current_x - last_scrub_x);
2111 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2115 /* if there have been more than 2 opposite motion moves detected, or one that moves
2116 back more than 10 pixels, reverse direction
2119 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2121 if (scrubbing_direction > 0) {
2122 /* was forwards, go backwards */
2123 _session->request_transport_speed (-0.1);
2124 scrubbing_direction = -1;
2126 /* was backwards, go forwards */
2127 _session->request_transport_speed (0.1);
2128 scrubbing_direction = 1;
2131 scrub_reverse_distance = 0;
2132 scrub_reversals = 0;
2136 last_scrub_x = current_x;
2140 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2142 _last_motion_y = event->motion.y;
2144 if (event->motion.is_hint) {
2147 /* We call this so that MOTION_NOTIFY events continue to be
2148 * delivered to the canvas. We need to do this because we set
2149 * Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2150 * the density of the events, at the expense of a round-trip
2151 * to the server. Given that this will mostly occur on cases
2152 * where DISPLAY = :0.0, and given the cost of what the motion
2153 * event might do, its a good tradeoff.
2156 _track_canvas->get_pointer (x, y);
2159 if (current_stepping_trackview) {
2160 /* don't keep the persistent stepped trackview if the mouse moves */
2161 current_stepping_trackview = 0;
2162 step_timeout.disconnect ();
2165 if (_session && _session->actively_recording()) {
2166 /* Sorry. no dragging stuff around while we record */
2170 update_join_object_range_location (event->motion.y);
2172 if (_drags->active ()) {
2173 //drags change the snapped_cursor location, because we are snapping the thing being dragged, not the actual mouse cursor
2174 return _drags->motion_handler (event, from_autoscroll);
2176 //the snapped_cursor shows where an operation (like Split) is going to occur
2178 MusicSample where (0, 0);
2179 if (mouse_sample (where.sample, ignored)) {
2180 snap_to_with_modifier (where, event);
2181 set_snapped_cursor_position (where.sample);
2189 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2191 ControlPoint* control_point;
2193 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2194 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2195 abort(); /*NOTREACHED*/
2198 AutomationLine& line = control_point->line ();
2199 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2200 /* we shouldn't remove the first or last gain point in region gain lines */
2201 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2210 Editor::remove_control_point (ArdourCanvas::Item* item)
2212 if (!can_remove_control_point (item)) {
2216 ControlPoint* control_point;
2218 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2219 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2220 abort(); /*NOTREACHED*/
2223 control_point->line().remove_point (*control_point);
2227 Editor::edit_control_point (ArdourCanvas::Item* item)
2229 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2232 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2233 abort(); /*NOTREACHED*/
2236 ControlPointDialog d (p);
2238 if (d.run () != RESPONSE_ACCEPT) {
2242 p->line().modify_point_y (*p, d.get_y_fraction ());
2246 Editor::edit_notes (MidiRegionView* mrv)
2248 MidiRegionView::Selection const & s = mrv->selection();
2254 EditNoteDialog* d = new EditNoteDialog (mrv, s);
2257 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2261 Editor::note_edit_done (int r, EditNoteDialog* d)
2268 Editor::edit_region (RegionView* rv)
2270 if (UIConfiguration::instance().get_use_double_click_to_zoom_to_selection()) {
2271 temporal_zoom_selection (Both);
2273 rv->show_region_editor ();
2278 Editor::visible_order_range (int* low, int* high) const
2280 *low = TimeAxisView::max_order ();
2283 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2285 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2287 if (rtv && !rtv->hidden()) {
2289 if (*high < rtv->order()) {
2290 *high = rtv->order ();
2293 if (*low > rtv->order()) {
2294 *low = rtv->order ();
2301 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2303 /* Either add to or set the set the region selection, unless
2304 * this is an alignment click (control used)
2307 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2309 samplepos_t where = get_preferred_edit_position();
2313 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2315 align_region (rv.region(), SyncPoint, where);
2317 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2319 align_region (rv.region(), End, where);
2323 align_region (rv.region(), Start, where);
2330 Editor::collect_new_region_view (RegionView* rv)
2332 latest_regionviews.push_back (rv);
2336 Editor::collect_and_select_new_region_view (RegionView* rv)
2339 latest_regionviews.push_back (rv);
2343 Editor::cancel_selection ()
2345 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2346 (*i)->hide_selection ();
2349 selection->clear ();
2350 clicked_selection = 0;
2354 Editor::cancel_time_selection ()
2356 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2357 (*i)->hide_selection ();
2359 selection->time.clear ();
2360 clicked_selection = 0;
2364 Editor::point_trim (GdkEvent* event, samplepos_t new_bound)
2366 RegionView* rv = clicked_regionview;
2368 /* Choose action dependant on which button was pressed */
2369 switch (event->button.button) {
2371 begin_reversible_command (_("start point trim"));
2373 if (selection->selected (rv)) {
2374 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2375 i != selection->regions.by_layer().end(); ++i)
2377 if (!(*i)->region()->locked()) {
2378 (*i)->region()->clear_changes ();
2379 (*i)->region()->trim_front (new_bound);
2380 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2385 if (!rv->region()->locked()) {
2386 rv->region()->clear_changes ();
2387 rv->region()->trim_front (new_bound);
2388 _session->add_command(new StatefulDiffCommand (rv->region()));
2392 commit_reversible_command();
2396 begin_reversible_command (_("end point trim"));
2398 if (selection->selected (rv)) {
2400 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2402 if (!(*i)->region()->locked()) {
2403 (*i)->region()->clear_changes();
2404 (*i)->region()->trim_end (new_bound);
2405 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2411 if (!rv->region()->locked()) {
2412 rv->region()->clear_changes ();
2413 rv->region()->trim_end (new_bound);
2414 _session->add_command (new StatefulDiffCommand (rv->region()));
2418 commit_reversible_command();
2427 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2429 ArdourMarker* marker;
2432 if ((marker = static_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
2433 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2434 abort(); /*NOTREACHED*/
2437 Location* location = find_location_from_marker (marker, is_start);
2438 location->set_hidden (true, this);
2442 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2444 using namespace Gtkmm2ext;
2446 ArdourWidgets::Prompter prompter (false);
2448 prompter.set_prompt (_("Name for region:"));
2449 prompter.set_initial_text (clicked_regionview->region()->name());
2450 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2451 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2452 prompter.show_all ();
2453 switch (prompter.run ()) {
2454 case Gtk::RESPONSE_ACCEPT:
2456 prompter.get_result(str);
2458 clicked_regionview->region()->set_name (str);
2467 Editor::mouse_brush_insert_region (RegionView* rv, samplepos_t pos)
2469 /* no brushing without a useful quantize setting */
2470 if (_grid_type == GridTypeNone)
2473 /* don't brush a copy over the original */
2475 if (pos == rv->region()->position()) {
2479 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2481 if (!rtv || !rtv->is_track()) {
2485 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2487 playlist->clear_changes ();
2488 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2489 playlist->add_region (new_region, pos);
2490 _session->add_command (new StatefulDiffCommand (playlist));
2492 /* playlist is frozen, so we have to update manually XXX this is disgusting */
2494 //playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2498 Editor::track_height_step_timeout ()
2500 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2501 current_stepping_trackview = 0;
2508 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2510 assert (region_view);
2512 if (!region_view->region()->playlist()) {
2516 switch (Config->get_edit_mode()) {
2518 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2521 _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2524 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2531 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2533 assert (region_view);
2535 if (!region_view->region()->playlist()) {
2539 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2543 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2545 assert (region_view);
2547 if (!region_view->region()->playlist()) {
2551 if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2555 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2558 /** Start a grab where a time range is selected, track(s) are selected, and the
2559 * user clicks and drags a region with a modifier in order to create a new region containing
2560 * the section of the clicked region that lies within the time range.
2563 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2565 if (clicked_regionview == 0) {
2569 /* lets try to create new Region for the selection */
2571 vector<boost::shared_ptr<Region> > new_regions;
2572 create_region_from_selection (new_regions);
2574 if (new_regions.empty()) {
2578 /* XXX fix me one day to use all new regions */
2580 boost::shared_ptr<Region> region (new_regions.front());
2582 /* add it to the current stream/playlist.
2584 * tricky: the streamview for the track will add a new regionview. we will
2585 * catch the signal it sends when it creates the regionview to
2586 * set the regionview we want to then drag.
2589 latest_regionviews.clear();
2590 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2592 /* A selection grab currently creates two undo/redo operations, one for
2593 * creating the new region and another for moving it.
2595 begin_reversible_command (Operations::selection_grab);
2597 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2599 playlist->clear_changes ();
2600 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2601 _session->add_command(new StatefulDiffCommand (playlist));
2605 if (latest_regionviews.empty()) {
2606 /* something went wrong */
2607 abort_reversible_command ();
2611 /* we need to deselect all other regionviews, and select this one
2612 * i'm ignoring undo stuff, because the region creation will take care of it
2615 selection->set (latest_regionviews);
2617 commit_reversible_command ();
2619 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2625 if (_drags->active ()) {
2627 } else if (_session) {
2628 selection->clear ();
2630 /* if session is playing a range, cancel that */
2631 if (_session->get_play_range()) {
2632 _session->request_cancel_play_range();
2635 if (_session->solo_selection_active()) {
2637 _session->solo_selection (sl, false);
2641 ARDOUR_UI::instance()->reset_focus (&contents());
2644 /** Update _join_object_range_state which indicate whether we are over the top
2645 * or bottom half of a route view, used by the `join object/range' tool
2646 * mode. Coordinates in canvas space.
2649 Editor::update_join_object_range_location (double y)
2651 if (!get_smart_mode()) {
2652 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2656 JoinObjectRangeState const old = _join_object_range_state;
2658 if (mouse_mode == MouseObject) {
2659 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2660 } else if (mouse_mode == MouseRange) {
2661 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2664 if (entered_regionview) {
2666 /* TODO: there is currently a bug here(?)
2667 * 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
2668 * can it be fixed here?
2671 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2672 double const c = item_space.y / entered_regionview->height();
2674 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2676 Editor::EnterContext* ctx = get_enter_context(RegionItem);
2677 if (_join_object_range_state != old && ctx) {
2678 ctx->cursor_ctx->change(which_track_cursor());
2681 } else if (entered_track) {
2683 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2685 if (entered_route_view) {
2690 entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2692 double track_height = entered_route_view->view()->child_height();
2693 if (UIConfiguration::instance().get_show_name_highlight()) {
2694 track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2696 double const c = cy / track_height;
2700 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2702 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2706 /* Other kinds of tracks use object mode */
2707 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2710 Editor::EnterContext* ctx = get_enter_context(StreamItem);
2711 if (_join_object_range_state != old && ctx) {
2712 ctx->cursor_ctx->change(which_track_cursor());
2718 Editor::effective_mouse_mode () const
2720 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2722 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2730 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2732 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2735 e->region_view().delete_note (e->note ());
2738 /** Obtain the pointer position in canvas coordinates */
2740 Editor::get_pointer_position (double& x, double& y) const
2743 _track_canvas->get_pointer (px, py);
2744 _track_canvas->window_to_canvas (px, py, x, y);