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 == MouseAudition) m = MouseRange;
280 Glib::RefPtr<Action> act = get_mouse_mode_action(m);
281 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
283 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
284 tact->set_active (false);
285 tact->set_active (true);
287 /* NOTE: this will result in a call to mouse_mode_toggled which does the heavy lifting */
291 Editor::mouse_mode_toggled (MouseMode m)
293 if (ARDOUR::Profile->get_mixbus()) {
294 if (m == MouseAudition) m = MouseRange;
297 Glib::RefPtr<Action> act = get_mouse_mode_action(m);
298 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
300 if (!tact->get_active()) {
301 /* this was just the notification that the old mode has been
302 * left. we'll get called again with the new mode active in a
308 if (_session && mouse_mode == MouseAudition) {
309 /* stop transport and reset default speed to avoid oddness with
311 _session->request_transport_speed (0.0, true);
314 const bool was_internal = internal_editing();
318 /* Switch snap type/mode if we're moving to/from an internal tool. Note
319 this must toggle the actions and not call set_snap_*() directly,
320 otherwise things get out of sync and the combo box stops working. */
321 if (!UIConfiguration::instance().get_grid_follows_internal()) {
322 grid_type_action(pre_internal_grid_type)->set_active(true);
323 snap_mode_action(pre_internal_snap_mode)->set_active(true);
324 } else if (!was_internal && internal_editing()) {
325 grid_type_action(internal_grid_type)->set_active(true);
326 snap_mode_action(internal_snap_mode)->set_active(true);
327 } else if (was_internal && !internal_editing()) {
328 grid_type_action(pre_internal_grid_type)->set_active(true);
329 snap_mode_action(pre_internal_snap_mode)->set_active(true);
334 /* this should generate a new enter event which will
335 trigger the appropiate cursor.
339 _track_canvas->re_enter ();
342 set_gain_envelope_visibility ();
344 update_time_selection_display ();
346 update_all_enter_cursors ();
348 MouseModeChanged (); /* EMIT SIGNAL */
352 Editor::internal_editing() const
354 return mouse_mode == Editing::MouseContent || mouse_mode == Editing::MouseDraw;
358 Editor::update_time_selection_display ()
360 switch (mouse_mode) {
362 selection->clear_objects ();
363 selection->clear_midi_notes ();
366 selection->clear_time ();
367 selection->clear_midi_notes ();
370 /* Clear regions, but not time or tracks, since that
371 would destroy the range selection rectangle, which we need to stick
372 around for AutomationRangeDrag. */
373 selection->clear_regions ();
374 selection->clear_playlists ();
377 /* This handles internal edit.
378 Clear everything except points and notes.
380 selection->clear_regions();
381 selection->clear_lines();
382 selection->clear_playlists ();
384 selection->clear_time ();
385 selection->clear_tracks ();
389 /* We probably want to keep region selection */
390 selection->clear_points ();
391 selection->clear_lines();
392 selection->clear_playlists ();
394 selection->clear_time ();
395 selection->clear_tracks ();
399 /*Don't lose lines or points if no action in this mode */
400 selection->clear_regions ();
401 selection->clear_playlists ();
402 selection->clear_time ();
403 selection->clear_tracks ();
407 /*Clear everything */
408 selection->clear_objects();
409 selection->clear_time ();
410 selection->clear_tracks ();
416 Editor::step_mouse_mode (bool next)
418 const int n_mouse_modes = (int)MouseContent + 1;
419 int current = (int)current_mouse_mode();
421 set_mouse_mode((MouseMode)((current + 1) % n_mouse_modes));
423 set_mouse_mode((MouseMode)((current + n_mouse_modes - 1) % n_mouse_modes));
428 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
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
436 * note: not dbl-click or triple-click
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.
443 MouseMode eff_mouse_mode = effective_mouse_mode ();
445 if (eff_mouse_mode == MouseCut) {
446 /* never change selection in cut mode */
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.
454 eff_mouse_mode = MouseObject;
457 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
458 if (get_smart_mode()) {
460 case FadeInHandleItem:
461 case FadeInTrimHandleItem:
462 case FadeOutHandleItem:
463 case FadeOutTrimHandleItem:
464 eff_mouse_mode = MouseObject;
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)) {
480 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
482 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
484 /* almost no selection action on modified button-2 or button-3 events */
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)) {
494 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
495 bool press = (event->type == GDK_BUTTON_PRESS);
498 _mouse_changed_selection = false;
503 if (eff_mouse_mode == MouseDraw) {
507 if (eff_mouse_mode != MouseRange) {
508 _mouse_changed_selection = set_selected_regionview_from_click (press, op);
510 /* don't change the selection unless the
511 * clicked track is not currently selected. if
512 * so, "collapse" the selection to just this track
514 if (!selection->selected (clicked_axisview)) {
515 set_selected_track_as_side_effect (Selection::Set);
519 if (eff_mouse_mode != MouseRange) {
520 _mouse_changed_selection |= set_selected_regionview_from_click (press, op);
525 case RegionViewNameHighlight:
527 case LeftFrameHandle:
528 case RightFrameHandle:
529 case FadeInHandleItem:
530 case FadeInTrimHandleItem:
532 case FadeOutHandleItem:
533 case FadeOutTrimHandleItem:
535 case StartCrossFadeItem:
536 case EndCrossFadeItem:
537 if (get_smart_mode() || eff_mouse_mode != MouseRange) {
538 _mouse_changed_selection |= set_selected_regionview_from_click (press, op);
539 } else if (event->type == GDK_BUTTON_PRESS) {
540 set_selected_track_as_side_effect (op);
544 case ControlPointItem:
545 /* for object/track exclusivity, we don't call set_selected_track_as_side_effect (op); */
547 if (eff_mouse_mode != MouseRange) {
548 if (event->button.button != 3) {
549 _mouse_changed_selection |= set_selected_control_point_from_click (press, op);
551 _mouse_changed_selection |= set_selected_control_point_from_click (press, Selection::Set);
557 if (eff_mouse_mode != MouseRange) {
558 AutomationLine* argl = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
560 std::list<Selectable*> selectables;
561 uint32_t before, after;
562 samplecnt_t const where = (samplecnt_t) floor (event->button.x * samples_per_pixel) - clicked_regionview->region ()->position ();
564 if (!argl || !argl->control_points_adjacent (where, before, after)) {
568 selectables.push_back (argl->nth (before));
569 selectables.push_back (argl->nth (after));
574 selection->set (selectables);
575 _mouse_changed_selection = true;
580 selection->add (selectables);
581 _mouse_changed_selection = true;
584 case Selection::Toggle:
586 selection->toggle (selectables);
587 _mouse_changed_selection = true;
591 case Selection::Extend:
598 case AutomationLineItem:
599 if (eff_mouse_mode != MouseRange) {
600 AutomationLine* al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
601 std::list<Selectable*> selectables;
602 double mx = event->button.x;
603 double my = event->button.y;
605 al->grab_item().canvas_to_item (mx, my);
607 uint32_t before, after;
608 samplecnt_t const where = (samplecnt_t) floor (mx * samples_per_pixel);
610 if (!al || !al->control_points_adjacent (where, before, after)) {
614 selectables.push_back (al->nth (before));
615 selectables.push_back (al->nth (after));
620 selection->set (selectables);
621 _mouse_changed_selection = true;
626 selection->add (selectables);
627 _mouse_changed_selection = true;
630 case Selection::Toggle:
632 selection->toggle (selectables);
633 _mouse_changed_selection = true;
637 case Selection::Extend:
645 /* for context click, select track */
646 if (event->button.button == 3) {
647 selection->clear_tracks ();
648 set_selected_track_as_side_effect (op);
650 /* We won't get a release.*/
651 begin_reversible_selection_op (X_("Button 3 Menu Select"));
652 commit_reversible_selection_op ();
656 case AutomationTrackItem:
657 if (eff_mouse_mode != MouseDraw && op == Selection::Set) {
658 set_selected_track_as_side_effect (op);
663 if (press && event->button.button == 3) {
664 NoteBase* cnote = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
666 if (cnote->region_view().selection_size() == 0 || !cnote->selected()) {
667 selection->clear_points();
668 cnote->region_view().unique_select (cnote);
669 /* we won't get the release, so store the selection change now */
670 begin_reversible_selection_op (X_("Button 3 Note Selection"));
671 commit_reversible_selection_op ();
680 if ((!press) && _mouse_changed_selection) {
681 begin_reversible_selection_op (X_("Button Selection"));
682 commit_reversible_selection_op ();
683 _mouse_changed_selection = false;
688 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
690 /* single mouse clicks on any of these item types operate
691 * independent of mouse mode, mostly because they are
692 * not on the main track canvas or because we want
693 * them to be modeless.
696 NoteBase* note = NULL;
699 case PlayheadCursorItem:
700 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
704 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
705 hide_marker (item, event);
707 _drags->set (new MarkerDrag (this, item), event);
711 case TempoMarkerItem:
713 if (ArdourKeyboard::indicates_constraint (event->button.state)) {
723 new TempoMarkerDrag (
726 ArdourKeyboard::indicates_copy (event->button.state)
735 case MeterMarkerItem:
738 new MeterMarkerDrag (
741 ArdourKeyboard::indicates_copy (event->button.state)
749 _drags->set (new VideoTimeLineDrag (this, item), event);
757 case TimecodeRulerItem:
758 case SamplesRulerItem:
759 case MinsecRulerItem:
761 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)
762 && !ArdourKeyboard::indicates_constraint (event->button.state)) {
763 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
764 } else if (ArdourKeyboard::indicates_constraint (event->button.state)
765 && Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
766 _drags->set (new TempoTwistDrag (this, item), event);
767 } else if (ArdourKeyboard::indicates_constraint (event->button.state)) {
768 _drags->set (new BBTRulerDrag (this, item), event);
774 case RangeMarkerBarItem:
775 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
776 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateSkipMarker), event);
777 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
778 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
780 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
785 case CdMarkerBarItem:
786 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
787 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
789 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
794 case TransportMarkerBarItem:
795 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
796 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
798 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
807 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
808 /* special case: allow trim of range selections in joined object mode;
809 * in theory eff should equal MouseRange in this case, but it doesn't
810 * because entering the range selection canvas item results in entered_regionview
811 * being set to 0, so update_join_object_range_location acts as if we aren't
814 if (item_type == StartSelectionTrimItem) {
815 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
816 } else if (item_type == EndSelectionTrimItem) {
817 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
821 Editing::MouseMode eff = effective_mouse_mode ();
823 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
824 if (get_smart_mode()) {
826 case FadeInHandleItem:
827 case FadeInTrimHandleItem:
828 case FadeOutHandleItem:
829 case FadeOutTrimHandleItem:
840 case StartSelectionTrimItem:
841 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
844 case EndSelectionTrimItem:
845 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
849 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
850 start_selection_grab (item, event);
852 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
853 /* grab selection for moving */
854 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
856 /* this was debated, but decided the more common action was to make a new selection */
857 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
862 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
863 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
865 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
870 case RegionViewNameHighlight:
871 if (!clicked_regionview->region()->locked()) {
872 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
878 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
879 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
881 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
890 case FadeInHandleItem:
891 case FadeOutHandleItem:
892 case LeftFrameHandle:
893 case RightFrameHandle:
894 case FeatureLineItem:
895 case RegionViewNameHighlight:
898 case AutomationTrackItem:
899 _drags->set (new RegionCutDrag (this, item, canvas_event_sample (event)), event, get_canvas_cursor());
910 /* Existing note: allow trimming/motion */
911 if ((note = reinterpret_cast<NoteBase*> (item->get_data ("notebase")))) {
912 if (note->big_enough_to_trim() && note->mouse_near_ends()) {
913 _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
915 _drags->set (new NoteDrag (this, item), event);
921 _drags->set (new LineDrag (this, item), event);
925 case ControlPointItem:
926 _drags->set (new ControlPointDrag (this, item), event);
930 case AutomationLineItem:
931 _drags->set (new LineDrag (this, item), event);
936 /* in the past, we created a new midi region here, but perhaps that is best left to the Draw mode */
939 case AutomationTrackItem:
940 /* rubberband drag to select automation points */
941 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
946 if (dynamic_cast<AutomationRegionView*>(clicked_regionview)) {
947 /* rubberband drag to select automation points */
948 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
959 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
960 event->type == GDK_BUTTON_PRESS) {
962 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
964 } else if (event->type == GDK_BUTTON_PRESS) {
967 case FadeInHandleItem:
969 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
973 case FadeOutHandleItem:
975 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
979 case StartCrossFadeItem:
980 case EndCrossFadeItem:
981 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor.
982 * For not this is not fully implemented */
984 if (!clicked_regionview->region()->locked()) {
985 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
991 case FeatureLineItem:
993 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
994 remove_transient(item);
998 _drags->set (new FeatureLineDrag (this, item), event);
1004 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1005 /* click on an automation region view; do nothing here and let the ARV's signal handler
1011 /* click on a normal region view */
1012 if (ArdourKeyboard::indicates_copy (event->button.state)) {
1013 add_region_copy_drag (item, event, clicked_regionview);
1014 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
1015 add_region_brush_drag (item, event, clicked_regionview);
1017 add_region_drag (item, event, clicked_regionview);
1021 _drags->start_grab (event);
1025 case RegionViewNameHighlight:
1026 case LeftFrameHandle:
1027 case RightFrameHandle:
1028 if (!clicked_regionview->region()->locked()) {
1029 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1034 case FadeInTrimHandleItem:
1035 case FadeOutTrimHandleItem:
1036 if (!clicked_regionview->region()->locked()) {
1037 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
1042 case RegionViewName:
1044 /* rename happens on edit clicks */
1045 if (clicked_regionview->get_name_highlight()) {
1046 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1052 case ControlPointItem:
1053 _drags->set (new ControlPointDrag (this, item), event);
1057 case AutomationLineItem:
1058 _drags->set (new LineDrag (this, item), event);
1063 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1066 case AutomationTrackItem:
1068 TimeAxisView* parent = clicked_axisview->get_parent ();
1069 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1071 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1073 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1075 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1076 if (pl->n_regions() == 0) {
1077 /* Parent has no regions; create one so that we have somewhere to put automation */
1078 _drags->set (new RegionCreateDrag (this, item, parent), event);
1080 /* See if there's a region before the click that we can extend, and extend it if so */
1081 samplepos_t const t = canvas_event_sample (event);
1082 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1084 _drags->set (new RegionCreateDrag (this, item, parent), event);
1086 prev->set_length (t - prev->position (), get_grid_music_divisions (event->button.state));
1090 /* rubberband drag to select automation points */
1091 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1113 switch (item_type) {
1115 _drags->set (new LineDrag (this, item), event);
1118 case ControlPointItem:
1119 _drags->set (new ControlPointDrag (this, item), event);
1125 if (selection->time.empty ()) {
1129 pair<TimeAxisView*, int> tvp = trackview_by_y_position (event->button.y, false);
1131 /* clicked outside of a track */
1134 /* handle automation lanes first */
1135 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1137 /* smart "join" mode: drag automation */
1138 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1141 if (dynamic_cast<AutomationRegionView*>(clicked_regionview)) {
1142 /* MIDI CC or similar -- TODO handle multiple? */
1143 list<RegionView*> rvl;
1144 rvl.push_back (clicked_regionview);
1145 _drags->set (new AutomationRangeDrag (this, rvl, selection->time,
1146 clicked_regionview->get_time_axis_view().y_position(),
1147 clicked_regionview->get_time_axis_view().current_height()),
1148 event, _cursors->up_down);
1152 /* shift+drag: only apply to clicked_regionview (if any) */
1153 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
1154 if (dynamic_cast<AudioRegionView*>(clicked_regionview) == 0) {
1157 list<RegionView*> rvl;
1158 rvl.push_back (clicked_regionview);
1159 // TODO: handle layer_display() == Stacked
1160 _drags->set (new AutomationRangeDrag (this, rvl, selection->time,
1161 clicked_regionview->get_time_axis_view().y_position(),
1162 clicked_regionview->get_time_axis_view().current_height()),
1163 event, _cursors->up_down);
1167 /* collect all audio regions-views in the given range selection */
1168 list<RegionView*> rvl;
1169 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
1170 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
1171 RouteTimeAxisView* tatv;
1172 boost::shared_ptr<Playlist> playlist;
1173 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) == 0) {
1176 if ((playlist = (*i)->playlist()) == 0) {
1179 if (boost::dynamic_pointer_cast<AudioPlaylist> (playlist) == 0) {
1182 for (list<AudioRange>::const_iterator j = selection->time.begin(); j != selection->time.end(); ++j) {
1183 boost::shared_ptr<RegionList> rl = playlist->regions_touched (j->start, j->end);
1184 for (RegionList::iterator ir = rl->begin(); ir != rl->end(); ++ir) {
1186 if ((rv = tatv->view()->find_view (*ir)) != 0) {
1192 /* region-gain drag */
1193 if (!rvl.empty ()) {
1194 double y_pos = tvp.first->y_position();
1195 double height = tvp.first->current_height();
1196 StreamView* cv = tvp.first->view ();
1197 if (cv->layer_display() == Stacked && cv->layers() > 1) {
1198 height /= cv->layers();
1199 double yy = event->button.y - _trackview_group->canvas_origin().y;
1200 y_pos += floor ((yy - y_pos) / height) * height;
1202 _drags->set (new AutomationRangeDrag (this, rvl, selection->time, y_pos, height),
1203 event, _cursors->up_down);
1209 case AutomationLineItem:
1210 _drags->set (new LineDrag (this, item), event);
1214 if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
1215 if (note->big_enough_to_trim() && note->mouse_near_ends()) {
1216 /* Note is big and pointer is near the end, trim */
1217 _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
1220 _drags->set (new NoteDrag (this, item), event);
1227 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1228 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1239 if (item_type == NoteItem) {
1240 /* resize-drag notes */
1241 if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
1242 if (note->big_enough_to_trim()) {
1243 _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
1247 } else if (clicked_regionview) {
1249 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1255 _drags->set (new ScrubDrag (this, item), event, _cursors->transparent);
1256 scrub_reversals = 0;
1257 scrub_reverse_distance = 0;
1258 last_scrub_x = event->button.x;
1259 scrubbing_direction = 0;
1271 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1273 Editing::MouseMode const eff = effective_mouse_mode ();
1276 switch (item_type) {
1278 if (ArdourKeyboard::indicates_copy (event->button.state)) {
1279 add_region_copy_drag (item, event, clicked_regionview);
1281 add_region_drag (item, event, clicked_regionview);
1283 _drags->start_grab (event);
1286 case ControlPointItem:
1287 _drags->set (new ControlPointDrag (this, item), event);
1295 switch (item_type) {
1296 case RegionViewNameHighlight:
1297 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1301 case LeftFrameHandle:
1302 case RightFrameHandle:
1303 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1307 case RegionViewName:
1308 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1322 /* relax till release */
1334 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1336 if (event->type == GDK_2BUTTON_PRESS) {
1337 _drags->mark_double_click ();
1338 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1342 if (event->type != GDK_BUTTON_PRESS) {
1346 _track_canvas->grab_focus();
1348 if (_session && _session->actively_recording()) {
1352 button_selection (item, event, item_type);
1354 if (!_drags->active () &&
1355 (Keyboard::is_delete_event (&event->button) ||
1356 Keyboard::is_context_menu_event (&event->button) ||
1357 Keyboard::is_edit_event (&event->button))) {
1359 /* handled by button release */
1363 /* not rolling, effectively in range mode, follow edits enabled (likely
1364 * to start range drag), not in a fade handle (since that means we are
1365 * not starting a range drag): locate the PH here
1368 if ((item_type != FadeInHandleItem) &&
1369 (item_type != FadeOutHandleItem) &&
1370 !_drags->active () &&
1372 !_session->transport_rolling() &&
1373 (effective_mouse_mode() == MouseRange) &&
1374 UIConfiguration::instance().get_follow_edits() &&
1375 !_session->config.get_external_sync()) {
1377 MusicSample where (canvas_event_sample (event), 0);
1379 _session->request_locate (where.sample, false);
1382 switch (event->button.button) {
1384 return button_press_handler_1 (item, event, item_type);
1388 return button_press_handler_2 (item, event, item_type);
1395 return button_press_dispatch (&event->button);
1404 Editor::button_press_dispatch (GdkEventButton* ev)
1406 /* this function is intended only for buttons 4 and above. */
1408 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1409 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1413 Editor::button_release_dispatch (GdkEventButton* ev)
1415 /* this function is intended only for buttons 4 and above. */
1417 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1418 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1422 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1424 MusicSample where (canvas_event_sample (event), 0);
1425 AutomationTimeAxisView* atv = 0;
1427 _press_cursor_ctx.reset();
1429 /* no action if we're recording */
1431 if (_session && _session->actively_recording()) {
1435 bool were_dragging = false;
1437 if (!Keyboard::is_context_menu_event (&event->button)) {
1439 /* see if we're finishing a drag */
1441 if (_drags->active ()) {
1442 bool const r = _drags->end_grab (event);
1444 /* grab dragged, so do nothing else */
1448 were_dragging = true;
1451 update_region_layering_order_editor ();
1454 /* edit events get handled here */
1456 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1457 switch (item_type) {
1459 show_region_properties ();
1461 case TempoMarkerItem: {
1462 ArdourMarker* marker;
1463 TempoMarker* tempo_marker;
1465 if ((marker = reinterpret_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
1466 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1467 abort(); /*NOTREACHED*/
1470 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1471 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1472 abort(); /*NOTREACHED*/
1475 edit_tempo_marker (*tempo_marker);
1479 case MeterMarkerItem: {
1480 ArdourMarker* marker;
1481 MeterMarker* meter_marker;
1483 if ((marker = reinterpret_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
1484 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1485 abort(); /*NOTREACHED*/
1488 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1489 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1490 abort(); /*NOTREACHED*/
1492 edit_meter_marker (*meter_marker);
1496 case RegionViewName:
1497 if (clicked_regionview->name_active()) {
1498 return mouse_rename_region (item, event);
1502 case ControlPointItem:
1503 edit_control_point (item);
1512 /* context menu events get handled here */
1513 if (Keyboard::is_context_menu_event (&event->button)) {
1515 context_click_event = *event;
1517 if (!_drags->active ()) {
1519 /* no matter which button pops up the context menu, tell the menu
1520 widget to use button 1 to drive menu selection.
1523 switch (item_type) {
1525 case FadeInHandleItem:
1526 case FadeInTrimHandleItem:
1527 case StartCrossFadeItem:
1528 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1532 case FadeOutHandleItem:
1533 case FadeOutTrimHandleItem:
1534 case EndCrossFadeItem:
1535 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1538 case LeftFrameHandle:
1539 case RightFrameHandle:
1543 popup_track_context_menu (1, event->button.time, item_type, false);
1547 case RegionViewNameHighlight:
1548 case RegionViewName:
1549 popup_track_context_menu (1, event->button.time, item_type, false);
1553 popup_track_context_menu (1, event->button.time, item_type, true);
1556 case AutomationTrackItem:
1557 popup_track_context_menu (1, event->button.time, item_type, false);
1561 case RangeMarkerBarItem:
1562 case TransportMarkerBarItem:
1563 case CdMarkerBarItem:
1565 case TempoCurveItem:
1568 case TimecodeRulerItem:
1569 case SamplesRulerItem:
1570 case MinsecRulerItem:
1572 popup_ruler_menu (where.sample, item_type);
1576 marker_context_menu (&event->button, item);
1579 case TempoMarkerItem:
1580 tempo_or_meter_marker_context_menu (&event->button, item);
1583 case MeterMarkerItem:
1584 tempo_or_meter_marker_context_menu (&event->button, item);
1587 case CrossfadeViewItem:
1588 popup_track_context_menu (1, event->button.time, item_type, false);
1591 case ControlPointItem:
1592 popup_control_point_context_menu (item, event);
1596 if (internal_editing()) {
1597 popup_note_context_menu (item, event);
1609 /* delete events get handled here */
1611 Editing::MouseMode const eff = effective_mouse_mode ();
1613 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1615 switch (item_type) {
1616 case TempoMarkerItem:
1617 remove_tempo_marker (item);
1620 case MeterMarkerItem:
1621 remove_meter_marker (item);
1625 remove_marker (*item, event);
1629 if (eff == MouseObject) {
1630 remove_clicked_region ();
1634 case ControlPointItem:
1635 remove_control_point (item);
1639 remove_midi_note (item, event);
1648 switch (event->button.button) {
1651 switch (item_type) {
1652 /* see comments in button_press_handler */
1653 case PlayheadCursorItem:
1656 case AutomationLineItem:
1657 case StartSelectionTrimItem:
1658 case EndSelectionTrimItem:
1662 if (!_dragging_playhead) {
1663 snap_to_with_modifier (where, event, RoundNearest, SnapToGrid_Scaled);
1664 mouse_add_new_marker (where.sample);
1668 case CdMarkerBarItem:
1669 if (!_dragging_playhead) {
1670 /* if we get here then a dragged range wasn't done */
1671 snap_to_with_modifier (where, event, RoundNearest, SnapToGrid_Scaled);
1672 mouse_add_new_marker (where.sample, true);
1676 case TempoCurveItem:
1677 if (!_dragging_playhead && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1678 snap_to_with_modifier (where, event);
1679 mouse_add_new_tempo_event (where.sample);
1684 if (!_dragging_playhead && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1685 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1690 case TimecodeRulerItem:
1691 case SamplesRulerItem:
1692 case MinsecRulerItem:
1703 switch (item_type) {
1706 /* check that we didn't drag before releasing, since
1707 its really annoying to create new control
1708 points when doing this.
1710 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1711 if (!were_dragging && arv) {
1712 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1713 arv->add_gain_point_event (item, event, with_guard_points);
1719 case AutomationTrackItem: {
1720 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1721 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1723 atv->add_automation_event (event, where.sample, event->button.y, with_guard_points);
1734 if (scrubbing_direction == 0) {
1735 /* no drag, just a click */
1736 switch (item_type) {
1738 play_selected_region ();
1743 } else if (_session) {
1744 /* make sure we stop */
1745 _session->request_transport_speed (0.0);
1754 /* do any (de)selection operations that should occur on button release */
1755 button_selection (item, event, item_type);
1765 switch (item_type) {
1767 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1769 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1772 /* Button2 click is unused */
1787 // x_style_paste (where, 1.0);
1808 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1811 ArdourMarker * marker;
1812 MeterMarker* m_marker = 0;
1813 TempoMarker* t_marker = 0;
1817 /* by the time we reach here, entered_regionview and entered trackview
1818 * will have already been set as appropriate. Things are done this
1819 * way because this method isn't passed a pointer to a variable type of
1820 * thing that is entered (which may or may not be canvas item).
1821 * (e.g. the actual entered regionview)
1824 choose_canvas_cursor_on_entry (item_type);
1826 switch (item_type) {
1827 case ControlPointItem:
1828 if (mouse_mode == MouseDraw || mouse_mode == MouseObject || mouse_mode == MouseContent) {
1829 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1832 fraction = 1.0 - (cp->get_y() / cp->line().height());
1834 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1835 _verbose_cursor->show ();
1840 if (mouse_mode == MouseDraw) {
1841 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1843 line->set_outline_color (UIConfiguration::instance().color ("entered gain line"));
1848 case AutomationLineItem:
1849 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1850 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1852 line->set_outline_color (UIConfiguration::instance().color ("entered automation line"));
1857 case AutomationTrackItem:
1858 AutomationTimeAxisView* atv;
1859 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1860 clear_entered_track = false;
1861 set_entered_track (atv);
1866 if ((marker = static_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
1869 entered_marker = marker;
1870 marker->set_color_rgba (UIConfiguration::instance().color ("entered marker"));
1873 case MeterMarkerItem:
1874 if ((m_marker = static_cast<MeterMarker *> (item->get_data ("marker"))) == 0) {
1877 entered_marker = m_marker;
1878 if (m_marker->meter().position_lock_style() == MusicTime) {
1879 m_marker->set_color_rgba (UIConfiguration::instance().color ("meter marker"));
1881 m_marker->set_color_rgba (UIConfiguration::instance().color ("meter marker music"));
1885 case TempoMarkerItem:
1886 if ((t_marker = static_cast<TempoMarker *> (item->get_data ("marker"))) == 0) {
1889 entered_marker = t_marker;
1890 if (t_marker->tempo().position_lock_style() == MusicTime) {
1891 t_marker->set_color_rgba (UIConfiguration::instance().color ("tempo marker"));
1893 t_marker->set_color_rgba (UIConfiguration::instance().color ("tempo marker music"));
1897 case FadeInHandleItem:
1898 case FadeInTrimHandleItem:
1899 if (mouse_mode == MouseObject) {
1900 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1902 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1903 rect->set_fill_color (rv->get_fill_color());
1908 case FadeOutHandleItem:
1909 case FadeOutTrimHandleItem:
1910 if (mouse_mode == MouseObject) {
1911 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1913 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1914 rect->set_fill_color (rv->get_fill_color ());
1919 case FeatureLineItem:
1921 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1922 line->set_outline_color (0xFF0000FF);
1931 if (entered_regionview) {
1932 entered_regionview->entered();
1941 /* third pass to handle entered track status in a comprehensible way.
1944 switch (item_type) {
1946 case AutomationLineItem:
1947 case ControlPointItem:
1948 /* these do not affect the current entered track state */
1949 clear_entered_track = false;
1952 case AutomationTrackItem:
1953 /* handled above already */
1965 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1968 ArdourMarker *marker;
1969 TempoMarker *t_marker;
1970 MeterMarker *m_marker;
1975 if (!_enter_stack.empty()) {
1976 _enter_stack.pop_back();
1979 switch (item_type) {
1980 case ControlPointItem:
1981 _verbose_cursor->hide ();
1985 case AutomationLineItem:
1986 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1988 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1990 line->set_outline_color (al->get_line_color());
1996 if ((marker = static_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
2000 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2001 location_flags_changed (loc);
2005 case MeterMarkerItem:
2006 if ((m_marker = static_cast<MeterMarker *> (item->get_data ("marker"))) == 0) {
2010 if (m_marker->meter().position_lock_style() == MusicTime) {
2011 m_marker->set_color_rgba (UIConfiguration::instance().color ("meter marker music"));
2013 m_marker->set_color_rgba (UIConfiguration::instance().color ("meter marker"));
2017 case TempoMarkerItem:
2018 if ((t_marker = static_cast<TempoMarker *> (item->get_data ("marker"))) == 0) {
2022 if (t_marker->tempo().position_lock_style() == MusicTime) {
2023 t_marker->set_color_rgba (UIConfiguration::instance().color ("tempo marker music"));
2025 t_marker->set_color_rgba (UIConfiguration::instance().color ("tempo marker"));
2029 case FadeInTrimHandleItem:
2030 case FadeOutTrimHandleItem:
2031 case FadeInHandleItem:
2032 case FadeOutHandleItem:
2034 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2036 rect->set_fill_color (UIConfiguration::instance().color ("inactive fade handle"));
2041 case AutomationTrackItem:
2044 case FeatureLineItem:
2046 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2047 line->set_outline_color (UIConfiguration::instance().color ("zero line"));
2059 Editor::scrub (samplepos_t sample, double current_x)
2063 if (scrubbing_direction == 0) {
2065 _session->request_locate (sample, false);
2066 _session->request_transport_speed (0.1);
2067 scrubbing_direction = 1;
2071 if (last_scrub_x > current_x) {
2073 /* pointer moved to the left */
2075 if (scrubbing_direction > 0) {
2077 /* we reversed direction to go backwards */
2080 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2084 /* still moving to the left (backwards) */
2086 scrub_reversals = 0;
2087 scrub_reverse_distance = 0;
2089 delta = 0.01 * (last_scrub_x - current_x);
2090 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2094 /* pointer moved to the right */
2096 if (scrubbing_direction < 0) {
2097 /* we reversed direction to go forward */
2100 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2103 /* still moving to the right */
2105 scrub_reversals = 0;
2106 scrub_reverse_distance = 0;
2108 delta = 0.01 * (current_x - last_scrub_x);
2109 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2113 /* if there have been more than 2 opposite motion moves detected, or one that moves
2114 back more than 10 pixels, reverse direction
2117 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2119 if (scrubbing_direction > 0) {
2120 /* was forwards, go backwards */
2121 _session->request_transport_speed (-0.1);
2122 scrubbing_direction = -1;
2124 /* was backwards, go forwards */
2125 _session->request_transport_speed (0.1);
2126 scrubbing_direction = 1;
2129 scrub_reverse_distance = 0;
2130 scrub_reversals = 0;
2134 last_scrub_x = current_x;
2138 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2140 _last_motion_y = event->motion.y;
2142 if (event->motion.is_hint) {
2145 /* We call this so that MOTION_NOTIFY events continue to be
2146 * delivered to the canvas. We need to do this because we set
2147 * Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2148 * the density of the events, at the expense of a round-trip
2149 * to the server. Given that this will mostly occur on cases
2150 * where DISPLAY = :0.0, and given the cost of what the motion
2151 * event might do, its a good tradeoff.
2154 _track_canvas->get_pointer (x, y);
2157 if (current_stepping_trackview) {
2158 /* don't keep the persistent stepped trackview if the mouse moves */
2159 current_stepping_trackview = 0;
2160 step_timeout.disconnect ();
2163 if (_session && _session->actively_recording()) {
2164 /* Sorry. no dragging stuff around while we record */
2168 update_join_object_range_location (event->motion.y);
2170 if (_drags->active ()) {
2171 //drags change the snapped_cursor location, because we are snapping the thing being dragged, not the actual mouse cursor
2172 return _drags->motion_handler (event, from_autoscroll);
2174 //the snapped_cursor shows where an operation (like Split) is going to occur
2176 MusicSample where (0, 0);
2177 if (mouse_sample (where.sample, ignored)) {
2178 snap_to_with_modifier (where, event);
2179 set_snapped_cursor_position (where.sample);
2187 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2189 ControlPoint* control_point;
2191 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2192 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2193 abort(); /*NOTREACHED*/
2196 AutomationLine& line = control_point->line ();
2197 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2198 /* we shouldn't remove the first or last gain point in region gain lines */
2199 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2208 Editor::remove_control_point (ArdourCanvas::Item* item)
2210 if (!can_remove_control_point (item)) {
2214 ControlPoint* control_point;
2216 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2217 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2218 abort(); /*NOTREACHED*/
2221 control_point->line().remove_point (*control_point);
2225 Editor::edit_control_point (ArdourCanvas::Item* item)
2227 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2230 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2231 abort(); /*NOTREACHED*/
2234 ControlPointDialog d (p);
2236 if (d.run () != RESPONSE_ACCEPT) {
2240 p->line().modify_point_y (*p, d.get_y_fraction ());
2244 Editor::edit_notes (MidiRegionView* mrv)
2246 MidiRegionView::Selection const & s = mrv->selection();
2252 EditNoteDialog* d = new EditNoteDialog (mrv, s);
2255 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2259 Editor::note_edit_done (int r, EditNoteDialog* d)
2266 Editor::edit_region (RegionView* rv)
2268 if (UIConfiguration::instance().get_use_double_click_to_zoom_to_selection()) {
2269 temporal_zoom_selection (Both);
2271 rv->show_region_editor ();
2276 Editor::visible_order_range (int* low, int* high) const
2278 *low = TimeAxisView::max_order ();
2281 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2283 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2285 if (rtv && !rtv->hidden()) {
2287 if (*high < rtv->order()) {
2288 *high = rtv->order ();
2291 if (*low > rtv->order()) {
2292 *low = rtv->order ();
2299 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2301 /* Either add to or set the set the region selection, unless
2302 * this is an alignment click (control used)
2305 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2307 samplepos_t where = get_preferred_edit_position();
2311 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2313 align_region (rv.region(), SyncPoint, where);
2315 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2317 align_region (rv.region(), End, where);
2321 align_region (rv.region(), Start, where);
2328 Editor::collect_new_region_view (RegionView* rv)
2330 latest_regionviews.push_back (rv);
2334 Editor::collect_and_select_new_region_view (RegionView* rv)
2337 latest_regionviews.push_back (rv);
2341 Editor::cancel_selection ()
2343 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2344 (*i)->hide_selection ();
2347 selection->clear ();
2348 clicked_selection = 0;
2352 Editor::cancel_time_selection ()
2354 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2355 (*i)->hide_selection ();
2357 selection->time.clear ();
2358 clicked_selection = 0;
2362 Editor::point_trim (GdkEvent* event, samplepos_t new_bound)
2364 RegionView* rv = clicked_regionview;
2366 /* Choose action dependant on which button was pressed */
2367 switch (event->button.button) {
2369 begin_reversible_command (_("start point trim"));
2371 if (selection->selected (rv)) {
2372 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2373 i != selection->regions.by_layer().end(); ++i)
2375 if (!(*i)->region()->locked()) {
2376 (*i)->region()->clear_changes ();
2377 (*i)->region()->trim_front (new_bound);
2378 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2383 if (!rv->region()->locked()) {
2384 rv->region()->clear_changes ();
2385 rv->region()->trim_front (new_bound);
2386 _session->add_command(new StatefulDiffCommand (rv->region()));
2390 commit_reversible_command();
2394 begin_reversible_command (_("end point trim"));
2396 if (selection->selected (rv)) {
2398 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2400 if (!(*i)->region()->locked()) {
2401 (*i)->region()->clear_changes();
2402 (*i)->region()->trim_end (new_bound);
2403 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2409 if (!rv->region()->locked()) {
2410 rv->region()->clear_changes ();
2411 rv->region()->trim_end (new_bound);
2412 _session->add_command (new StatefulDiffCommand (rv->region()));
2416 commit_reversible_command();
2425 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2427 ArdourMarker* marker;
2430 if ((marker = static_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
2431 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2432 abort(); /*NOTREACHED*/
2435 Location* location = find_location_from_marker (marker, is_start);
2436 location->set_hidden (true, this);
2440 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2442 using namespace Gtkmm2ext;
2444 ArdourWidgets::Prompter prompter (false);
2446 prompter.set_prompt (_("Name for region:"));
2447 prompter.set_initial_text (clicked_regionview->region()->name());
2448 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2449 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2450 prompter.show_all ();
2451 switch (prompter.run ()) {
2452 case Gtk::RESPONSE_ACCEPT:
2454 prompter.get_result(str);
2456 clicked_regionview->region()->set_name (str);
2465 Editor::mouse_brush_insert_region (RegionView* rv, samplepos_t pos)
2467 /* no brushing without a useful quantize setting */
2468 if (_grid_type == GridTypeNone)
2471 /* don't brush a copy over the original */
2473 if (pos == rv->region()->position()) {
2477 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2479 if (!rtv || !rtv->is_track()) {
2483 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2485 playlist->clear_changes ();
2486 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2487 playlist->add_region (new_region, pos);
2488 _session->add_command (new StatefulDiffCommand (playlist));
2490 /* playlist is frozen, so we have to update manually XXX this is disgusting */
2492 //playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2496 Editor::track_height_step_timeout ()
2498 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2499 current_stepping_trackview = 0;
2506 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2508 assert (region_view);
2510 if (!region_view->region()->playlist()) {
2514 switch (Config->get_edit_mode()) {
2516 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2519 _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2522 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2529 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2531 assert (region_view);
2533 if (!region_view->region()->playlist()) {
2537 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2541 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2543 assert (region_view);
2545 if (!region_view->region()->playlist()) {
2549 if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2553 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2556 /** Start a grab where a time range is selected, track(s) are selected, and the
2557 * user clicks and drags a region with a modifier in order to create a new region containing
2558 * the section of the clicked region that lies within the time range.
2561 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2563 if (clicked_regionview == 0) {
2567 /* lets try to create new Region for the selection */
2569 vector<boost::shared_ptr<Region> > new_regions;
2570 create_region_from_selection (new_regions);
2572 if (new_regions.empty()) {
2576 /* XXX fix me one day to use all new regions */
2578 boost::shared_ptr<Region> region (new_regions.front());
2580 /* add it to the current stream/playlist.
2582 * tricky: the streamview for the track will add a new regionview. we will
2583 * catch the signal it sends when it creates the regionview to
2584 * set the regionview we want to then drag.
2587 latest_regionviews.clear();
2588 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2590 /* A selection grab currently creates two undo/redo operations, one for
2591 * creating the new region and another for moving it.
2593 begin_reversible_command (Operations::selection_grab);
2595 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2597 playlist->clear_changes ();
2598 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2599 _session->add_command(new StatefulDiffCommand (playlist));
2603 if (latest_regionviews.empty()) {
2604 /* something went wrong */
2605 abort_reversible_command ();
2609 /* we need to deselect all other regionviews, and select this one
2610 * i'm ignoring undo stuff, because the region creation will take care of it
2613 selection->set (latest_regionviews);
2615 commit_reversible_command ();
2617 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2623 if (_drags->active ()) {
2625 } else if (_session) {
2626 selection->clear ();
2628 /* if session is playing a range, cancel that */
2629 if (_session->get_play_range()) {
2630 _session->request_cancel_play_range();
2633 if (_session->solo_selection_active()) {
2635 _session->solo_selection (sl, false);
2639 ARDOUR_UI::instance()->reset_focus (&contents());
2642 /** Update _join_object_range_state which indicate whether we are over the top
2643 * or bottom half of a route view, used by the `join object/range' tool
2644 * mode. Coordinates in canvas space.
2647 Editor::update_join_object_range_location (double y)
2649 if (!get_smart_mode()) {
2650 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2654 JoinObjectRangeState const old = _join_object_range_state;
2656 if (mouse_mode == MouseObject) {
2657 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2658 } else if (mouse_mode == MouseRange) {
2659 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2662 if (entered_regionview) {
2664 /* TODO: there is currently a bug here(?)
2665 * 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
2666 * can it be fixed here?
2669 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2670 double const c = item_space.y / entered_regionview->height();
2672 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2674 Editor::EnterContext* ctx = get_enter_context(RegionItem);
2675 if (_join_object_range_state != old && ctx) {
2676 ctx->cursor_ctx->change(which_track_cursor());
2679 } else if (entered_track) {
2681 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2683 if (entered_route_view) {
2688 entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2690 double track_height = entered_route_view->view()->child_height();
2691 if (UIConfiguration::instance().get_show_name_highlight()) {
2692 track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2694 double const c = cy / track_height;
2698 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2700 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2704 /* Other kinds of tracks use object mode */
2705 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2708 Editor::EnterContext* ctx = get_enter_context(StreamItem);
2709 if (_join_object_range_state != old && ctx) {
2710 ctx->cursor_ctx->change(which_track_cursor());
2716 Editor::effective_mouse_mode () const
2718 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2720 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2728 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2730 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2733 e->region_view().delete_note (e->note ());
2736 /** Obtain the pointer position in canvas coordinates */
2738 Editor::get_pointer_position (double& x, double& y) const
2741 _track_canvas->get_pointer (px, py);
2742 _track_canvas->window_to_canvas (px, py, x, y);