3 Copyright (C) 2000-2001 Paul Davis
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
30 #include <pbd/error.h>
31 #include <gtkmm2ext/utils.h>
32 #include <pbd/memento_command.h>
34 #include "ardour_ui.h"
36 #include "time_axis_view.h"
37 #include "audio_time_axis.h"
38 #include "audio_region_view.h"
40 #include "streamview.h"
41 #include "region_gain_line.h"
42 #include "automation_time_axis.h"
45 #include "selection.h"
48 #include "rgb_macros.h"
50 #include <ardour/types.h>
51 #include <ardour/profile.h>
52 #include <ardour/route.h>
53 #include <ardour/audio_track.h>
54 #include <ardour/audio_diskstream.h>
55 #include <ardour/playlist.h>
56 #include <ardour/audioplaylist.h>
57 #include <ardour/audioregion.h>
58 #include <ardour/dB.h>
59 #include <ardour/utils.h>
60 #include <ardour/region_factory.h>
67 using namespace ARDOUR
;
71 using namespace Editing
;
74 Editor::mouse_frame (nframes64_t
& where
, bool& in_track_canvas
) const
78 Gdk::ModifierType mask
;
79 Glib::RefPtr
<Gdk::Window
> canvas_window
= const_cast<Editor
*>(this)->track_canvas
->get_window();
80 Glib::RefPtr
<const Gdk::Window
> pointer_window
;
86 pointer_window
= canvas_window
->get_pointer (x
, y
, mask
);
88 if (pointer_window
== track_canvas
->get_bin_window()) {
91 in_track_canvas
= true;
94 in_track_canvas
= false;
99 event
.type
= GDK_BUTTON_RELEASE
;
103 where
= event_frame (&event
, 0, 0);
108 Editor::event_frame (GdkEvent
* event
, double* pcx
, double* pcy
) const
122 switch (event
->type
) {
123 case GDK_BUTTON_RELEASE
:
124 case GDK_BUTTON_PRESS
:
125 case GDK_2BUTTON_PRESS
:
126 case GDK_3BUTTON_PRESS
:
128 *pcx
= event
->button
.x
;
129 *pcy
= event
->button
.y
;
130 _trackview_group
->w2i(*pcx
, *pcy
);
132 case GDK_MOTION_NOTIFY
:
134 *pcx
= event
->motion
.x
;
135 *pcy
= event
->motion
.y
;
136 _trackview_group
->w2i(*pcx
, *pcy
);
138 case GDK_ENTER_NOTIFY
:
139 case GDK_LEAVE_NOTIFY
:
140 track_canvas
->w2c(event
->crossing
.x
, event
->crossing
.y
, *pcx
, *pcy
);
143 case GDK_KEY_RELEASE
:
144 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
147 warning
<< string_compose (_("Editor::event_frame() used on unhandled event type %1"), event
->type
) << endmsg
;
151 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
152 position is negative (as can be the case with motion events in particular),
153 the frame location is always positive.
156 return pixel_to_frame (*pcx
);
160 Editor::mouse_mode_toggled (MouseMode m
)
162 if (ignore_mouse_mode_toggle
) {
168 if (mouse_select_button
.get_active()) {
174 if (mouse_move_button
.get_active()) {
180 if (mouse_gain_button
.get_active()) {
186 if (mouse_zoom_button
.get_active()) {
192 if (mouse_timefx_button
.get_active()) {
198 if (mouse_audition_button
.get_active()) {
209 Editor::which_grabber_cursor ()
211 switch (_edit_point
) {
213 return grabber_edit_point_cursor
;
218 return grabber_cursor
;
222 Editor::set_canvas_cursor ()
224 switch (mouse_mode
) {
226 current_canvas_cursor
= selector_cursor
;
230 current_canvas_cursor
= which_grabber_cursor();
234 current_canvas_cursor
= cross_hair_cursor
;
238 current_canvas_cursor
= zoom_cursor
;
242 current_canvas_cursor
= time_fx_cursor
; // just use playhead
246 current_canvas_cursor
= speaker_cursor
;
251 track_canvas
->get_window()->set_cursor(*current_canvas_cursor
);
256 Editor::set_mouse_mode (MouseMode m
, bool force
)
258 if (drag_info
.item
) {
262 if (!force
&& m
== mouse_mode
) {
270 if (mouse_mode
!= MouseRange
) {
272 /* in all modes except range, hide the range selection,
273 show the object (region) selection.
276 for (RegionSelection::iterator i
= selection
->regions
.begin(); i
!= selection
->regions
.end(); ++i
) {
277 (*i
)->set_should_show_selection (true);
279 for (TrackViewList::iterator i
= track_views
.begin(); i
!= track_views
.end(); ++i
) {
280 (*i
)->hide_selection ();
286 in range mode,show the range selection.
289 for (TrackSelection::iterator i
= selection
->tracks
.begin(); i
!= selection
->tracks
.end(); ++i
) {
290 if ((*i
)->get_selected()) {
291 (*i
)->show_selection (selection
->time
);
296 /* XXX the hack of unsetting all other buttons should go
297 away once GTK2 allows us to use regular radio buttons drawn like
298 normal buttons, rather than my silly GroupedButton hack.
301 ignore_mouse_mode_toggle
= true;
303 switch (mouse_mode
) {
305 mouse_select_button
.set_active (true);
309 mouse_move_button
.set_active (true);
313 mouse_gain_button
.set_active (true);
317 mouse_zoom_button
.set_active (true);
321 mouse_timefx_button
.set_active (true);
325 mouse_audition_button
.set_active (true);
329 ignore_mouse_mode_toggle
= false;
331 set_canvas_cursor ();
335 Editor::step_mouse_mode (bool next
)
337 switch (current_mouse_mode()) {
340 if (Profile
->get_sae()) {
341 set_mouse_mode (MouseZoom
);
343 set_mouse_mode (MouseRange
);
346 set_mouse_mode (MouseTimeFX
);
351 if (next
) set_mouse_mode (MouseZoom
);
352 else set_mouse_mode (MouseObject
);
357 if (Profile
->get_sae()) {
358 set_mouse_mode (MouseTimeFX
);
360 set_mouse_mode (MouseGain
);
363 if (Profile
->get_sae()) {
364 set_mouse_mode (MouseObject
);
366 set_mouse_mode (MouseRange
);
372 if (next
) set_mouse_mode (MouseTimeFX
);
373 else set_mouse_mode (MouseZoom
);
378 set_mouse_mode (MouseAudition
);
380 if (Profile
->get_sae()) {
381 set_mouse_mode (MouseZoom
);
383 set_mouse_mode (MouseGain
);
389 if (next
) set_mouse_mode (MouseObject
);
390 else set_mouse_mode (MouseTimeFX
);
396 Editor::button_selection (ArdourCanvas::Item
* item
, GdkEvent
* event
, ItemType item_type
)
398 /* in object/audition/timefx/gain-automation mode,
399 any button press sets the selection if the object
400 can be selected. this is a bit of hack, because
401 we want to avoid this if the mouse operation is a
404 note: not dbl-click or triple-click
407 if (((mouse_mode
!= MouseObject
) &&
408 (mouse_mode
!= MouseAudition
|| item_type
!= RegionItem
) &&
409 (mouse_mode
!= MouseTimeFX
|| item_type
!= RegionItem
) &&
410 (mouse_mode
!= MouseGain
) &&
411 (mouse_mode
!= MouseRange
)) ||
413 ((event
->type
!= GDK_BUTTON_PRESS
&& event
->type
!= GDK_BUTTON_RELEASE
) || event
->button
.button
> 3)) {
418 if (event
->type
== GDK_BUTTON_PRESS
|| event
->type
== GDK_BUTTON_RELEASE
) {
420 if ((event
->button
.state
& Keyboard::RelevantModifierKeyMask
) && event
->button
.button
!= 1) {
422 /* almost no selection action on modified button-2 or button-3 events */
424 if (item_type
!= RegionItem
&& event
->button
.button
!= 2) {
430 Selection::Operation op
= Keyboard::selection_type (event
->button
.state
);
431 bool press
= (event
->type
== GDK_BUTTON_PRESS
);
433 // begin_reversible_command (_("select on click"));
437 if (mouse_mode
!= MouseRange
) {
438 set_selected_regionview_from_click (press
, op
, true);
439 } else if (event
->type
== GDK_BUTTON_PRESS
) {
440 set_selected_track_as_side_effect ();
444 case RegionViewNameHighlight
:
446 if (mouse_mode
!= MouseRange
) {
447 set_selected_regionview_from_click (press
, op
, true);
448 } else if (event
->type
== GDK_BUTTON_PRESS
) {
449 set_selected_track_as_side_effect ();
453 case FadeInHandleItem
:
455 case FadeOutHandleItem
:
457 if (mouse_mode
!= MouseRange
) {
458 set_selected_regionview_from_click (press
, op
, true);
459 } else if (event
->type
== GDK_BUTTON_PRESS
) {
460 set_selected_track_as_side_effect ();
464 case GainAutomationControlPointItem
:
465 case PanAutomationControlPointItem
:
466 case RedirectAutomationControlPointItem
:
467 set_selected_track_as_side_effect ();
468 if (mouse_mode
!= MouseRange
) {
469 set_selected_control_point_from_click (op
, false);
474 /* for context click or range selection, select track */
475 if (event
->button
.button
== 3) {
476 set_selected_track_as_side_effect ();
477 } else if (event
->type
== GDK_BUTTON_PRESS
&& mouse_mode
== MouseRange
) {
478 set_selected_track_as_side_effect ();
482 case AutomationTrackItem
:
483 set_selected_track_as_side_effect (true);
491 const static double ZERO_GAIN_FRACTION
= gain_to_slider_position(dB_to_coefficient(0.0));
494 Editor::button_press_handler (ArdourCanvas::Item
* item
, GdkEvent
* event
, ItemType item_type
)
496 Glib::RefPtr
<Gdk::Window
> canvas_window
= const_cast<Editor
*>(this)->track_canvas
->get_window();
499 Glib::RefPtr
<const Gdk::Window
> pointer_window
;
502 Gdk::ModifierType mask
;
504 pointer_window
= canvas_window
->get_pointer (x
, y
, mask
);
506 if (pointer_window
== track_canvas
->get_bin_window()) {
507 track_canvas
->window_to_world (x
, y
, wx
, wy
);
508 allow_vertical_scroll
= true;
510 allow_vertical_scroll
= false;
514 track_canvas
->grab_focus();
516 if (session
&& session
->actively_recording()) {
520 button_selection (item
, event
, item_type
);
522 //ctrl-drag or right-click-drag on a "range" ruler should start a range drag
523 if (event
->type
== GDK_BUTTON_PRESS
) {
524 if (event
->button
.button
== 3 || ( (event
->button
.button
== 1) && (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
) ))) {
525 if (item_type
== TransportMarkerBarItem
) {
526 start_range_markerbar_op (item
, event
, CreateTransportMarker
);
529 if (item_type
== RangeMarkerBarItem
) {
530 start_range_markerbar_op (item
, event
, CreateRangeMarker
);
533 if (item_type
== CdMarkerBarItem
) {
534 start_range_markerbar_op (item
, event
, CreateCDMarker
);
540 if (drag_info
.item
== 0 &&
541 (Keyboard::is_delete_event (&event
->button
) ||
542 Keyboard::is_context_menu_event (&event
->button
) ||
543 Keyboard::is_edit_event (&event
->button
))) {
545 /* handled by button release */
549 switch (event
->button
.button
) {
552 if (event
->type
== GDK_BUTTON_PRESS
) {
554 if (drag_info
.item
) {
555 drag_info
.item
->ungrab (event
->button
.time
);
558 /* single mouse clicks on any of these item types operate
559 independent of mouse mode, mostly because they are
560 not on the main track canvas or because we want
565 case PlayheadCursorItem
:
566 start_cursor_grab (item
, event
);
570 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::ModifierMask(Keyboard::PrimaryModifier
|Keyboard::TertiaryModifier
))) {
571 hide_marker (item
, event
);
573 start_marker_grab (item
, event
);
577 case TempoMarkerItem
:
578 if (Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::CopyModifier
)) {
579 start_tempo_marker_copy_grab (item
, event
);
581 start_tempo_marker_grab (item
, event
);
585 case MeterMarkerItem
:
586 if (Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::CopyModifier
)) {
587 start_meter_marker_copy_grab (item
, event
);
589 start_meter_marker_grab (item
, event
);
596 case TransportMarkerBarItem
:
597 case RangeMarkerBarItem
:
598 case CdMarkerBarItem
:
599 if (!Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
600 start_cursor_grab_no_stop(&playhead_cursor
->canvas_item
, event
);
610 switch (mouse_mode
) {
613 case StartSelectionTrimItem
:
614 start_selection_op (item
, event
, SelectionStartTrim
);
617 case EndSelectionTrimItem
:
618 start_selection_op (item
, event
, SelectionEndTrim
);
622 if (Keyboard::modifier_state_contains
623 (event
->button
.state
, Keyboard::ModifierMask(Keyboard::SecondaryModifier
))) {
624 // contains and not equals because I can't use alt as a modifier alone.
625 start_selection_grab (item
, event
);
626 } else if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
627 /* grab selection for moving */
628 start_selection_op (item
, event
, SelectionMove
);
630 /* this was debated, but decided the more common action was to
631 make a new selection */
632 start_selection_op (item
, event
, CreateSelection
);
637 start_selection_op (item
, event
, CreateSelection
);
643 if (Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::ModifierMask(Keyboard::PrimaryModifier
|Keyboard::SecondaryModifier
)) &&
644 event
->type
== GDK_BUTTON_PRESS
) {
646 start_rubberband_select (item
, event
);
648 } else if (event
->type
== GDK_BUTTON_PRESS
) {
651 case FadeInHandleItem
:
652 start_fade_in_grab (item
, event
);
655 case FadeOutHandleItem
:
656 start_fade_out_grab (item
, event
);
660 if (Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::CopyModifier
)) {
661 start_region_copy_grab (item
, event
);
662 } else if (Keyboard::the_keyboard().key_is_down (GDK_b
)) {
663 start_region_brush_grab (item
, event
);
665 start_region_grab (item
, event
);
669 case RegionViewNameHighlight
:
670 start_trim (item
, event
);
675 /* rename happens on edit clicks */
676 start_trim (clicked_regionview
->get_name_highlight(), event
);
680 case GainAutomationControlPointItem
:
681 case PanAutomationControlPointItem
:
682 case RedirectAutomationControlPointItem
:
683 start_control_point_grab (item
, event
);
687 case GainAutomationLineItem
:
688 case PanAutomationLineItem
:
689 case RedirectAutomationLineItem
:
690 start_line_grab_from_line (item
, event
);
695 case AutomationTrackItem
:
696 start_rubberband_select (item
, event
);
699 /* <CMT Additions> */
700 case ImageFrameHandleStartItem
:
701 imageframe_start_handle_op(item
, event
) ;
704 case ImageFrameHandleEndItem
:
705 imageframe_end_handle_op(item
, event
) ;
708 case MarkerViewHandleStartItem
:
709 markerview_item_start_handle_op(item
, event
) ;
712 case MarkerViewHandleEndItem
:
713 markerview_item_end_handle_op(item
, event
) ;
716 /* </CMT Additions> */
718 /* <CMT Additions> */
720 start_markerview_grab(item
, event
) ;
723 start_imageframe_grab(item
, event
) ;
725 /* </CMT Additions> */
741 /* start a grab so that if we finish after moving
742 we can tell what happened.
744 drag_info
.item
= item
;
745 drag_info
.motion_callback
= &Editor::region_gain_motion_callback
;
746 drag_info
.finished_callback
= 0;
747 start_grab (event
, current_canvas_cursor
);
750 case GainControlPointItem
:
751 start_control_point_grab (item
, event
);
755 start_line_grab_from_line (item
, event
);
758 case GainAutomationControlPointItem
:
759 case PanAutomationControlPointItem
:
760 case RedirectAutomationControlPointItem
:
761 start_control_point_grab (item
, event
);
772 case GainAutomationControlPointItem
:
773 case PanAutomationControlPointItem
:
774 case RedirectAutomationControlPointItem
:
775 start_control_point_grab (item
, event
);
778 case GainAutomationLineItem
:
779 case PanAutomationLineItem
:
780 case RedirectAutomationLineItem
:
781 start_line_grab_from_line (item
, event
);
785 // XXX need automation mode to identify which
787 // start_line_grab_from_regionview (item, event);
797 if (event
->type
== GDK_BUTTON_PRESS
) {
798 start_mouse_zoom (item
, event
);
805 if (item_type
== RegionItem
) {
806 start_time_fx (item
, event
);
813 scrub_reverse_distance
= 0;
814 last_scrub_x
= event
->button
.x
;
815 scrubbing_direction
= 0;
816 track_canvas
->get_window()->set_cursor (*transparent_cursor
);
817 /* rest handled in motion & release */
826 switch (mouse_mode
) {
828 if (event
->type
== GDK_BUTTON_PRESS
) {
831 if (Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::CopyModifier
)) {
832 start_region_copy_grab (item
, event
);
834 start_region_grab (item
, event
);
839 case GainAutomationControlPointItem
:
840 case PanAutomationControlPointItem
:
841 case RedirectAutomationControlPointItem
:
842 start_control_point_grab (item
, event
);
853 case RegionViewNameHighlight
:
854 start_trim (item
, event
);
859 start_trim (clicked_regionview
->get_name_highlight(), event
);
870 if (event
->type
== GDK_BUTTON_PRESS
) {
871 /* relax till release */
878 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
879 temporal_zoom_session();
881 temporal_zoom_to_frame (true, event_frame(event
));
904 Editor::button_release_handler (ArdourCanvas::Item
* item
, GdkEvent
* event
, ItemType item_type
)
906 nframes64_t where
= event_frame (event
, 0, 0);
907 AutomationTimeAxisView
* atv
= 0;
909 /* no action if we're recording */
911 if (session
&& session
->actively_recording()) {
915 /* first, see if we're finishing a drag ... */
917 if (drag_info
.item
) {
918 if (end_grab (item
, event
)) {
919 /* grab dragged, so do nothing else */
924 button_selection (item
, event
, item_type
);
925 update_region_layering_order_editor (where
);
927 /* edit events get handled here */
929 if (drag_info
.item
== 0 && Keyboard::is_edit_event (&event
->button
)) {
935 case TempoMarkerItem
:
936 edit_tempo_marker (item
);
939 case MeterMarkerItem
:
940 edit_meter_marker (item
);
944 if (clicked_regionview
->name_active()) {
945 return mouse_rename_region (item
, event
);
955 /* context menu events get handled here */
957 if (Keyboard::is_context_menu_event (&event
->button
)) {
959 if (drag_info
.item
== 0) {
961 /* no matter which button pops up the context menu, tell the menu
962 widget to use button 1 to drive menu selection.
967 case FadeInHandleItem
:
969 case FadeOutHandleItem
:
970 popup_fade_context_menu (1, event
->button
.time
, item
, item_type
);
974 popup_track_context_menu (1, event
->button
.time
, item_type
, false, where
);
978 case RegionViewNameHighlight
:
980 popup_track_context_menu (1, event
->button
.time
, item_type
, false, where
);
984 popup_track_context_menu (1, event
->button
.time
, item_type
, true, where
);
987 case AutomationTrackItem
:
988 popup_track_context_menu (1, event
->button
.time
, item_type
, false, where
);
992 case RangeMarkerBarItem
:
993 case TransportMarkerBarItem
:
994 case CdMarkerBarItem
:
997 popup_ruler_menu (where
, item_type
);
1001 marker_context_menu (&event
->button
, item
);
1004 case TempoMarkerItem
:
1005 tm_marker_context_menu (&event
->button
, item
);
1008 case MeterMarkerItem
:
1009 tm_marker_context_menu (&event
->button
, item
);
1012 case CrossfadeViewItem
:
1013 popup_track_context_menu (1, event
->button
.time
, item_type
, false, where
);
1016 /* <CMT Additions> */
1017 case ImageFrameItem
:
1018 popup_imageframe_edit_menu(1, event
->button
.time
, item
, true) ;
1020 case ImageFrameTimeAxisItem
:
1021 popup_imageframe_edit_menu(1, event
->button
.time
, item
, false) ;
1023 case MarkerViewItem
:
1024 popup_marker_time_axis_edit_menu(1, event
->button
.time
, item
, true) ;
1026 case MarkerTimeAxisItem
:
1027 popup_marker_time_axis_edit_menu(1, event
->button
.time
, item
, false) ;
1029 /* <CMT Additions> */
1040 /* delete events get handled here */
1042 if (drag_info
.item
== 0 && Keyboard::is_delete_event (&event
->button
)) {
1044 switch (item_type
) {
1045 case TempoMarkerItem
:
1046 remove_tempo_marker (item
);
1049 case MeterMarkerItem
:
1050 remove_meter_marker (item
);
1054 remove_marker (*item
, event
);
1058 if (mouse_mode
== MouseObject
) {
1059 remove_clicked_region ();
1063 case GainControlPointItem
:
1064 if (mouse_mode
== MouseGain
) {
1065 remove_gain_control_point (item
, event
);
1069 case GainAutomationControlPointItem
:
1070 case PanAutomationControlPointItem
:
1071 case RedirectAutomationControlPointItem
:
1072 remove_control_point (item
, event
);
1081 switch (event
->button
.button
) {
1084 switch (item_type
) {
1085 /* see comments in button_press_handler */
1086 case PlayheadCursorItem
:
1089 case GainAutomationLineItem
:
1090 case PanAutomationLineItem
:
1091 case RedirectAutomationLineItem
:
1092 case StartSelectionTrimItem
:
1093 case EndSelectionTrimItem
:
1097 if (!_dragging_playhead
) {
1098 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
1099 snap_to (where
, 0, true);
1101 mouse_add_new_marker (where
);
1105 case CdMarkerBarItem
:
1106 if (!_dragging_playhead
) {
1107 // if we get here then a dragged range wasn't done
1108 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
1109 snap_to (where
, 0, true);
1111 mouse_add_new_marker (where
, true);
1116 if (!_dragging_playhead
) {
1117 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
1120 mouse_add_new_tempo_event (where
);
1125 if (!_dragging_playhead
) {
1126 mouse_add_new_meter_event (pixel_to_frame (event
->button
.x
));
1135 switch (mouse_mode
) {
1137 switch (item_type
) {
1138 case AutomationTrackItem
:
1139 atv
= dynamic_cast<AutomationTimeAxisView
*>(clicked_trackview
);
1141 atv
->add_automation_event (item
, event
, where
, event
->button
.y
);
1153 // Gain only makes sense for audio regions
1155 if (!dynamic_cast<AudioRegionView
*>(clicked_regionview
)) {
1159 switch (item_type
) {
1161 /* check that we didn't drag before releasing, since
1162 its really annoying to create new control
1163 points when doing this.
1165 if (drag_info
.first_move
) {
1166 dynamic_cast<AudioRegionView
*>(clicked_regionview
)->add_gain_point_event (item
, event
);
1171 case AutomationTrackItem
:
1172 dynamic_cast<AutomationTimeAxisView
*>(clicked_trackview
)->
1173 add_automation_event (item
, event
, where
, event
->button
.y
);
1183 track_canvas
->get_window()->set_cursor (*current_canvas_cursor
);
1184 if (scrubbing_direction
== 0) {
1185 /* no drag, just a click */
1186 switch (item_type
) {
1188 play_selected_region ();
1194 /* make sure we stop */
1195 session
->request_stop ();
1209 switch (mouse_mode
) {
1212 switch (item_type
) {
1214 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::TertiaryModifier
)) {
1216 } else if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::ModifierMask (Keyboard::TertiaryModifier
|Keyboard::SecondaryModifier
))) {
1219 // Button2 click is unused
1232 // x_style_paste (where, 1.0);
1252 Editor::enter_handler (ArdourCanvas::Item
* item
, GdkEvent
* event
, ItemType item_type
)
1258 if (last_item_entered
!= item
) {
1259 last_item_entered
= item
;
1260 last_item_entered_n
= 0;
1263 switch (item_type
) {
1264 case GainControlPointItem
:
1265 if (mouse_mode
== MouseGain
) {
1266 cp
= static_cast<ControlPoint
*>(item
->get_data ("control_point"));
1267 cp
->set_visible (true);
1271 at_y
= cp
->get_y ();
1272 cp
->item
->i2w (at_x
, at_y
);
1276 fraction
= 1.0 - (cp
->get_y() / cp
->line
.height());
1278 if (is_drawable() && !_scrubbing
) {
1279 track_canvas
->get_window()->set_cursor (*fader_cursor
);
1282 last_item_entered_n
++;
1283 set_verbose_canvas_cursor (cp
->line
.get_verbose_cursor_string (fraction
), at_x
, at_y
);
1284 if (last_item_entered_n
< 10) {
1285 show_verbose_canvas_cursor ();
1290 case GainAutomationControlPointItem
:
1291 case PanAutomationControlPointItem
:
1292 case RedirectAutomationControlPointItem
:
1293 if (mouse_mode
== MouseGain
|| mouse_mode
== MouseObject
) {
1294 cp
= static_cast<ControlPoint
*>(item
->get_data ("control_point"));
1295 cp
->set_visible (true);
1299 at_y
= cp
->get_y ();
1300 cp
->item
->i2w (at_x
, at_y
);
1304 fraction
= 1.0 - (cp
->get_y() / cp
->line
.height());
1306 set_verbose_canvas_cursor (cp
->line
.get_verbose_cursor_string (fraction
), at_x
, at_y
);
1307 show_verbose_canvas_cursor ();
1309 if (is_drawable()) {
1310 track_canvas
->get_window()->set_cursor (*fader_cursor
);
1316 if (mouse_mode
== MouseGain
) {
1317 ArdourCanvas::Line
*line
= dynamic_cast<ArdourCanvas::Line
*> (item
);
1319 line
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine
.get();
1320 if (is_drawable()) {
1321 track_canvas
->get_window()->set_cursor (*fader_cursor
);
1326 case GainAutomationLineItem
:
1327 case RedirectAutomationLineItem
:
1328 case PanAutomationLineItem
:
1329 if (mouse_mode
== MouseGain
|| mouse_mode
== MouseObject
) {
1331 ArdourCanvas::Line
*line
= dynamic_cast<ArdourCanvas::Line
*> (item
);
1333 line
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine
.get();
1335 if (is_drawable()) {
1336 track_canvas
->get_window()->set_cursor (*fader_cursor
);
1341 case RegionViewNameHighlight
:
1342 if (is_drawable() && mouse_mode
== MouseObject
) {
1343 track_canvas
->get_window()->set_cursor (*trimmer_cursor
);
1347 case StartSelectionTrimItem
:
1348 case EndSelectionTrimItem
:
1349 /* <CMT Additions> */
1350 case ImageFrameHandleStartItem
:
1351 case ImageFrameHandleEndItem
:
1352 case MarkerViewHandleStartItem
:
1353 case MarkerViewHandleEndItem
:
1354 /* </CMT Additions> */
1356 if (is_drawable()) {
1357 track_canvas
->get_window()->set_cursor (*trimmer_cursor
);
1361 case PlayheadCursorItem
:
1362 if (is_drawable()) {
1363 switch (_edit_point
) {
1365 track_canvas
->get_window()->set_cursor (*grabber_edit_point_cursor
);
1368 track_canvas
->get_window()->set_cursor (*grabber_cursor
);
1374 case RegionViewName
:
1376 /* when the name is not an active item, the entire name highlight is for trimming */
1378 if (!reinterpret_cast<RegionView
*> (item
->get_data ("regionview"))->name_active()) {
1379 if (mouse_mode
== MouseObject
&& is_drawable()) {
1380 track_canvas
->get_window()->set_cursor (*trimmer_cursor
);
1386 case AutomationTrackItem
:
1387 if (is_drawable()) {
1388 Gdk::Cursor
*cursor
;
1389 switch (mouse_mode
) {
1391 cursor
= selector_cursor
;
1394 cursor
= zoom_cursor
;
1397 cursor
= cross_hair_cursor
;
1401 track_canvas
->get_window()->set_cursor (*cursor
);
1403 AutomationTimeAxisView
* atv
;
1404 if ((atv
= static_cast<AutomationTimeAxisView
*>(item
->get_data ("trackview"))) != 0) {
1405 clear_entered_track
= false;
1406 set_entered_track (atv
);
1412 case RangeMarkerBarItem
:
1413 case TransportMarkerBarItem
:
1414 case CdMarkerBarItem
:
1417 if (is_drawable()) {
1418 track_canvas
->get_window()->set_cursor (*timebar_cursor
);
1423 if ((marker
= static_cast<Marker
*> (item
->get_data ("marker"))) == 0) {
1426 entered_marker
= marker
;
1427 marker
->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker
.get());
1429 case MeterMarkerItem
:
1430 case TempoMarkerItem
:
1431 if (is_drawable()) {
1432 track_canvas
->get_window()->set_cursor (*timebar_cursor
);
1435 case FadeInHandleItem
:
1436 case FadeOutHandleItem
:
1437 if (mouse_mode
== MouseObject
) {
1438 ArdourCanvas::SimpleRect
*rect
= dynamic_cast<ArdourCanvas::SimpleRect
*> (item
);
1440 rect
->property_fill_color_rgba() = 0;
1441 rect
->property_outline_pixels() = 1;
1450 /* second pass to handle entered track status in a comprehensible way.
1453 switch (item_type
) {
1455 case GainAutomationLineItem
:
1456 case RedirectAutomationLineItem
:
1457 case PanAutomationLineItem
:
1458 case GainControlPointItem
:
1459 case GainAutomationControlPointItem
:
1460 case PanAutomationControlPointItem
:
1461 case RedirectAutomationControlPointItem
:
1462 /* these do not affect the current entered track state */
1463 clear_entered_track
= false;
1466 case AutomationTrackItem
:
1467 /* handled above already */
1471 set_entered_track (0);
1479 Editor::leave_handler (ArdourCanvas::Item
* item
, GdkEvent
* event
, ItemType item_type
)
1488 switch (item_type
) {
1489 case GainControlPointItem
:
1490 case GainAutomationControlPointItem
:
1491 case PanAutomationControlPointItem
:
1492 case RedirectAutomationControlPointItem
:
1493 cp
= reinterpret_cast<ControlPoint
*>(item
->get_data ("control_point"));
1494 if (cp
->line
.npoints() > 1) {
1495 if (!cp
->selected
) {
1496 cp
->set_visible (false);
1500 if (is_drawable()) {
1501 track_canvas
->get_window()->set_cursor (*current_canvas_cursor
);
1504 hide_verbose_canvas_cursor ();
1507 case RegionViewNameHighlight
:
1508 case StartSelectionTrimItem
:
1509 case EndSelectionTrimItem
:
1510 case PlayheadCursorItem
:
1511 /* <CMT Additions> */
1512 case ImageFrameHandleStartItem
:
1513 case ImageFrameHandleEndItem
:
1514 case MarkerViewHandleStartItem
:
1515 case MarkerViewHandleEndItem
:
1516 /* </CMT Additions> */
1517 if (is_drawable()) {
1518 track_canvas
->get_window()->set_cursor (*current_canvas_cursor
);
1523 case GainAutomationLineItem
:
1524 case RedirectAutomationLineItem
:
1525 case PanAutomationLineItem
:
1526 al
= reinterpret_cast<AutomationLine
*> (item
->get_data ("line"));
1528 ArdourCanvas::Line
*line
= dynamic_cast<ArdourCanvas::Line
*> (item
);
1530 line
->property_fill_color_rgba() = al
->get_line_color();
1532 if (is_drawable()) {
1533 track_canvas
->get_window()->set_cursor (*current_canvas_cursor
);
1537 case RegionViewName
:
1538 /* see enter_handler() for notes */
1539 if (!reinterpret_cast<RegionView
*> (item
->get_data ("regionview"))->name_active()) {
1540 if (is_drawable() && mouse_mode
== MouseObject
) {
1541 track_canvas
->get_window()->set_cursor (*current_canvas_cursor
);
1546 case RangeMarkerBarItem
:
1547 case TransportMarkerBarItem
:
1548 case CdMarkerBarItem
:
1552 if (is_drawable()) {
1553 track_canvas
->get_window()->set_cursor (*current_canvas_cursor
);
1558 if ((marker
= static_cast<Marker
*> (item
->get_data ("marker"))) == 0) {
1562 if ((loc
= find_location_from_marker (marker
, is_start
)) != 0) {
1563 location_flags_changed (loc
, this);
1566 case MeterMarkerItem
:
1567 case TempoMarkerItem
:
1569 if (is_drawable()) {
1570 track_canvas
->get_window()->set_cursor (*timebar_cursor
);
1575 case FadeInHandleItem
:
1576 case FadeOutHandleItem
:
1577 rv
= static_cast<RegionView
*>(item
->get_data ("regionview"));
1579 ArdourCanvas::SimpleRect
*rect
= dynamic_cast<ArdourCanvas::SimpleRect
*> (item
);
1581 rect
->property_fill_color_rgba() = rv
->get_fill_color();
1582 rect
->property_outline_pixels() = 0;
1587 case AutomationTrackItem
:
1588 if (is_drawable()) {
1589 track_canvas
->get_window()->set_cursor (*current_canvas_cursor
);
1590 clear_entered_track
= true;
1591 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track
));
1603 Editor::left_automation_track ()
1605 if (clear_entered_track
) {
1606 set_entered_track (0);
1607 clear_entered_track
= false;
1617 if (scrubbing_direction
== 0) {
1619 session
->request_locate (drag_info
.current_pointer_frame
, false);
1620 session
->request_transport_speed (0.1);
1621 scrubbing_direction
= 1;
1625 if (last_scrub_x
> drag_info
.current_pointer_x
) {
1627 /* pointer moved to the left */
1629 if (scrubbing_direction
> 0) {
1631 /* we reversed direction to go backwards */
1634 scrub_reverse_distance
+= (int) (last_scrub_x
- drag_info
.current_pointer_x
);
1638 /* still moving to the left (backwards) */
1640 scrub_reversals
= 0;
1641 scrub_reverse_distance
= 0;
1643 delta
= 0.01 * (last_scrub_x
- drag_info
.current_pointer_x
);
1644 session
->request_transport_speed (session
->transport_speed() - delta
);
1648 /* pointer moved to the right */
1650 if (scrubbing_direction
< 0) {
1651 /* we reversed direction to go forward */
1654 scrub_reverse_distance
+= (int) (drag_info
.current_pointer_x
- last_scrub_x
);
1657 /* still moving to the right */
1659 scrub_reversals
= 0;
1660 scrub_reverse_distance
= 0;
1662 delta
= 0.01 * (drag_info
.current_pointer_x
- last_scrub_x
);
1663 session
->request_transport_speed (session
->transport_speed() + delta
);
1667 /* if there have been more than 2 opposite motion moves detected, or one that moves
1668 back more than 10 pixels, reverse direction
1671 if (scrub_reversals
>= 2 || scrub_reverse_distance
> 10) {
1673 if (scrubbing_direction
> 0) {
1674 /* was forwards, go backwards */
1675 session
->request_transport_speed (-0.1);
1676 scrubbing_direction
= -1;
1678 /* was backwards, go forwards */
1679 session
->request_transport_speed (0.1);
1680 scrubbing_direction
= 1;
1683 scrub_reverse_distance
= 0;
1684 scrub_reversals
= 0;
1688 last_scrub_x
= drag_info
.current_pointer_x
;
1692 Editor::motion_handler (ArdourCanvas::Item
* item
, GdkEvent
* event
, ItemType item_type
, bool from_autoscroll
)
1694 if (event
->motion
.is_hint
) {
1697 /* We call this so that MOTION_NOTIFY events continue to be
1698 delivered to the canvas. We need to do this because we set
1699 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1700 the density of the events, at the expense of a round-trip
1701 to the server. Given that this will mostly occur on cases
1702 where DISPLAY = :0.0, and given the cost of what the motion
1703 event might do, its a good tradeoff.
1706 track_canvas
->get_pointer (x
, y
);
1709 if (current_stepping_trackview
) {
1710 /* don't keep the persistent stepped trackview if the mouse moves */
1711 current_stepping_trackview
= 0;
1712 step_timeout
.disconnect ();
1715 if (session
&& session
->actively_recording()) {
1716 /* Sorry. no dragging stuff around while we record */
1720 drag_info
.item_type
= item_type
;
1721 drag_info
.last_pointer_x
= drag_info
.current_pointer_x
;
1722 drag_info
.last_pointer_y
= drag_info
.current_pointer_y
;
1723 drag_info
.current_pointer_frame
= event_frame (event
, &drag_info
.current_pointer_x
,
1724 &drag_info
.current_pointer_y
);
1727 switch (mouse_mode
) {
1738 if (!from_autoscroll
&& drag_info
.item
) {
1739 /* item != 0 is the best test i can think of for dragging.
1741 if (!drag_info
.move_threshold_passed
) {
1743 bool x_threshold_passed
= (::llabs ((nframes64_t
) (drag_info
.current_pointer_x
- drag_info
.grab_x
)) > 4LL);
1744 bool y_threshold_passed
= (::llabs ((nframes64_t
) (drag_info
.current_pointer_y
- drag_info
.grab_y
)) > 4LL);
1746 drag_info
.move_threshold_passed
= (x_threshold_passed
|| y_threshold_passed
);
1748 // and change the initial grab loc/frame if this drag info wants us to
1750 if (drag_info
.want_move_threshold
&& drag_info
.move_threshold_passed
) {
1751 drag_info
.grab_frame
= drag_info
.current_pointer_frame
;
1752 drag_info
.grab_x
= drag_info
.current_pointer_x
;
1753 drag_info
.grab_y
= drag_info
.current_pointer_y
;
1754 drag_info
.last_pointer_frame
= drag_info
.grab_frame
;
1755 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- drag_info
.last_frame_position
;
1760 switch (item_type
) {
1761 case PlayheadCursorItem
:
1766 case RangeMarkerBarItem
:
1767 case TransportMarkerBarItem
:
1768 case CdMarkerBarItem
:
1769 case GainControlPointItem
:
1770 case RedirectAutomationControlPointItem
:
1771 case GainAutomationControlPointItem
:
1772 case PanAutomationControlPointItem
:
1773 case TempoMarkerItem
:
1774 case MeterMarkerItem
:
1775 case RegionViewNameHighlight
:
1776 case StartSelectionTrimItem
:
1777 case EndSelectionTrimItem
:
1780 case RedirectAutomationLineItem
:
1781 case GainAutomationLineItem
:
1782 case PanAutomationLineItem
:
1783 case FadeInHandleItem
:
1784 case FadeOutHandleItem
:
1785 /* <CMT Additions> */
1786 case ImageFrameHandleStartItem
:
1787 case ImageFrameHandleEndItem
:
1788 case MarkerViewHandleStartItem
:
1789 case MarkerViewHandleEndItem
:
1790 /* </CMT Additions> */
1791 if (drag_info
.item
&& (event
->motion
.state
& Gdk::BUTTON1_MASK
||
1792 (event
->motion
.state
& Gdk::BUTTON3_MASK
) ||
1793 (event
->motion
.state
& Gdk::BUTTON2_MASK
))) {
1794 if (!from_autoscroll
) {
1795 maybe_autoscroll_horizontally (&event
->motion
);
1797 if (drag_info
.motion_callback
) {
1798 (this->*(drag_info
.motion_callback
)) (item
, event
);
1808 switch (mouse_mode
) {
1810 if (item_type
== RegionItem
) {
1811 if (drag_info
.item
&& drag_info
.motion_callback
) {
1812 (this->*(drag_info
.motion_callback
)) (item
, event
);
1822 if (drag_info
.item
&& (event
->motion
.state
& GDK_BUTTON1_MASK
||
1823 (event
->motion
.state
& GDK_BUTTON2_MASK
))) {
1824 if (!from_autoscroll
) {
1825 maybe_autoscroll (&event
->motion
);
1827 if (drag_info
.motion_callback
) {
1828 (this->*(drag_info
.motion_callback
)) (item
, event
);
1840 track_canvas_motion (event
);
1841 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1849 Editor::break_drag ()
1851 stop_canvas_autoscroll ();
1852 hide_verbose_canvas_cursor ();
1854 if (drag_info
.item
) {
1855 drag_info
.item
->ungrab (0);
1857 /* put it back where it came from */
1862 drag_info
.item
->i2w (cxw
, cyw
);
1863 drag_info
.item
->move (drag_info
.original_x
- cxw
, drag_info
.original_y
- cyw
);
1870 Editor::finalize_drag ()
1873 drag_info
.copy
= false;
1874 drag_info
.motion_callback
= 0;
1875 drag_info
.finished_callback
= 0;
1876 drag_info
.dest_trackview
= 0;
1877 drag_info
.source_trackview
= 0;
1878 drag_info
.last_frame_position
= 0;
1879 drag_info
.grab_frame
= 0;
1880 drag_info
.last_pointer_frame
= 0;
1881 drag_info
.current_pointer_frame
= 0;
1882 drag_info
.brushing
= false;
1883 range_marker_drag_rect
->hide();
1884 drag_info
.clear_copied_locations ();
1888 Editor::start_grab (GdkEvent
* event
, Gdk::Cursor
*cursor
)
1890 if (drag_info
.item
== 0) {
1891 fatal
<< _("programming error: start_grab called without drag item") << endmsg
;
1897 cursor
= which_grabber_cursor ();
1900 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1902 if (Keyboard::is_button2_event (&event
->button
)) {
1903 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::SecondaryModifier
)) {
1904 drag_info
.y_constrained
= true;
1905 drag_info
.x_constrained
= false;
1907 drag_info
.y_constrained
= false;
1908 drag_info
.x_constrained
= true;
1911 drag_info
.x_constrained
= false;
1912 drag_info
.y_constrained
= false;
1915 drag_info
.grab_frame
= event_frame (event
, &drag_info
.grab_x
, &drag_info
.grab_y
);
1916 drag_info
.last_pointer_frame
= drag_info
.grab_frame
;
1917 drag_info
.current_pointer_frame
= drag_info
.grab_frame
;
1918 drag_info
.current_pointer_x
= drag_info
.grab_x
;
1919 drag_info
.current_pointer_y
= drag_info
.grab_y
;
1920 drag_info
.last_pointer_x
= drag_info
.current_pointer_x
;
1921 drag_info
.last_pointer_y
= drag_info
.current_pointer_y
;
1922 drag_info
.cumulative_x_drag
= 0;
1923 drag_info
.cumulative_y_drag
= 0;
1924 drag_info
.first_move
= true;
1925 drag_info
.move_threshold_passed
= false;
1926 drag_info
.want_move_threshold
= false;
1927 drag_info
.pointer_frame_offset
= 0;
1928 drag_info
.brushing
= false;
1929 drag_info
.clear_copied_locations ();
1931 drag_info
.original_x
= 0;
1932 drag_info
.original_y
= 0;
1933 drag_info
.item
->i2w (drag_info
.original_x
, drag_info
.original_y
);
1935 drag_info
.item
->grab (Gdk::POINTER_MOTION_MASK
|Gdk::BUTTON_PRESS_MASK
|Gdk::BUTTON_RELEASE_MASK
,
1937 event
->button
.time
);
1939 if (session
&& session
->transport_rolling()) {
1940 drag_info
.was_rolling
= true;
1942 drag_info
.was_rolling
= false;
1945 switch (snap_type
) {
1946 case SnapToRegionStart
:
1947 case SnapToRegionEnd
:
1948 case SnapToRegionSync
:
1949 case SnapToRegionBoundary
:
1950 build_region_boundary_cache ();
1958 Editor::swap_grab (ArdourCanvas::Item
* new_item
, Gdk::Cursor
* cursor
, uint32_t time
)
1960 drag_info
.item
->ungrab (0);
1961 drag_info
.item
= new_item
;
1964 cursor
= which_grabber_cursor ();
1967 drag_info
.item
->grab (Gdk::POINTER_MOTION_MASK
|Gdk::BUTTON_PRESS_MASK
|Gdk::BUTTON_RELEASE_MASK
, *cursor
, time
);
1971 Editor::end_grab (ArdourCanvas::Item
* item
, GdkEvent
* event
)
1973 bool did_drag
= false;
1975 stop_canvas_autoscroll ();
1977 if (drag_info
.item
== 0) {
1981 drag_info
.item
->ungrab (event
? event
->button
.time
: 0);
1983 if (drag_info
.finished_callback
&& event
) {
1984 drag_info
.last_pointer_x
= drag_info
.current_pointer_x
;
1985 drag_info
.last_pointer_y
= drag_info
.current_pointer_y
;
1986 (this->*(drag_info
.finished_callback
)) (item
, event
);
1989 did_drag
= !drag_info
.first_move
;
1991 hide_verbose_canvas_cursor();
1999 Editor::region_gain_motion_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2001 if (drag_info
.first_move
&& drag_info
.move_threshold_passed
) {
2002 drag_info
.first_move
= false;
2007 Editor::start_fade_in_grab (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2009 drag_info
.item
= item
;
2010 drag_info
.motion_callback
= &Editor::fade_in_drag_motion_callback
;
2011 drag_info
.finished_callback
= &Editor::fade_in_drag_finished_callback
;
2015 if ((drag_info
.data
= (item
->get_data ("regionview"))) == 0) {
2016 fatal
<< _("programming error: fade in canvas item has no regionview data pointer!") << endmsg
;
2020 AudioRegionView
* arv
= static_cast<AudioRegionView
*>(drag_info
.data
);
2022 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- ((nframes64_t
) arv
->audio_region()->fade_in().back()->when
+ arv
->region()->position());
2026 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2028 AudioRegionView
* arv
= static_cast<AudioRegionView
*>(drag_info
.data
);
2030 nframes64_t fade_length
;
2032 if (drag_info
.current_pointer_frame
> drag_info
.pointer_frame_offset
) {
2033 pos
= drag_info
.current_pointer_frame
- drag_info
.pointer_frame_offset
;
2039 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
2043 if (pos
< (arv
->region()->position() + 64)) {
2044 fade_length
= 64; // this should be a minimum defined somewhere
2045 } else if (pos
> arv
->region()->last_frame()) {
2046 fade_length
= arv
->region()->length();
2048 fade_length
= pos
- arv
->region()->position();
2050 /* mapover the region selection */
2052 for (RegionSelection::iterator i
= selection
->regions
.begin(); i
!= selection
->regions
.end(); ++i
) {
2054 AudioRegionView
* tmp
= dynamic_cast<AudioRegionView
*> (*i
);
2060 tmp
->reset_fade_in_shape_width (fade_length
);
2063 show_verbose_duration_cursor (arv
->region()->position(), arv
->region()->position() + fade_length
, 10);
2065 drag_info
.first_move
= false;
2069 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2071 AudioRegionView
* arv
= static_cast<AudioRegionView
*>(drag_info
.data
);
2073 nframes64_t fade_length
;
2075 if (drag_info
.first_move
) return;
2077 if (drag_info
.current_pointer_frame
> drag_info
.pointer_frame_offset
) {
2078 pos
= drag_info
.current_pointer_frame
- drag_info
.pointer_frame_offset
;
2083 if (pos
< (arv
->region()->position() + 64)) {
2084 fade_length
= 64; // this should be a minimum defined somewhere
2085 } else if (pos
> arv
->region()->last_frame()) {
2086 fade_length
= arv
->region()->length();
2088 fade_length
= pos
- arv
->region()->position();
2091 begin_reversible_command (_("change fade in length"));
2093 for (RegionSelection::iterator i
= selection
->regions
.begin(); i
!= selection
->regions
.end(); ++i
) {
2095 AudioRegionView
* tmp
= dynamic_cast<AudioRegionView
*> (*i
);
2101 AutomationList
& alist
= tmp
->audio_region()->fade_in();
2102 XMLNode
&before
= alist
.get_state();
2104 tmp
->audio_region()->set_fade_in_length (fade_length
);
2105 tmp
->audio_region()->set_fade_in_active (true);
2107 XMLNode
&after
= alist
.get_state();
2108 session
->add_command(new MementoCommand
<AutomationList
>(alist
, &before
, &after
));
2111 commit_reversible_command ();
2115 Editor::start_fade_out_grab (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2117 drag_info
.item
= item
;
2118 drag_info
.motion_callback
= &Editor::fade_out_drag_motion_callback
;
2119 drag_info
.finished_callback
= &Editor::fade_out_drag_finished_callback
;
2123 if ((drag_info
.data
= (item
->get_data ("regionview"))) == 0) {
2124 fatal
<< _("programming error: fade out canvas item has no regionview data pointer!") << endmsg
;
2128 AudioRegionView
* arv
= static_cast<AudioRegionView
*>(drag_info
.data
);
2130 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- (arv
->region()->length() - (nframes64_t
) arv
->audio_region()->fade_out().back()->when
+ arv
->region()->position());
2134 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2136 AudioRegionView
* arv
= static_cast<AudioRegionView
*>(drag_info
.data
);
2138 nframes64_t fade_length
;
2140 if (drag_info
.current_pointer_frame
> drag_info
.pointer_frame_offset
) {
2141 pos
= drag_info
.current_pointer_frame
- drag_info
.pointer_frame_offset
;
2146 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
2150 if (pos
> (arv
->region()->last_frame() - 64)) {
2151 fade_length
= 64; // this should really be a minimum fade defined somewhere
2153 else if (pos
< arv
->region()->position()) {
2154 fade_length
= arv
->region()->length();
2157 fade_length
= arv
->region()->last_frame() - pos
;
2160 /* mapover the region selection */
2162 for (RegionSelection::iterator i
= selection
->regions
.begin(); i
!= selection
->regions
.end(); ++i
) {
2164 AudioRegionView
* tmp
= dynamic_cast<AudioRegionView
*> (*i
);
2170 tmp
->reset_fade_out_shape_width (fade_length
);
2173 show_verbose_duration_cursor (arv
->region()->last_frame() - fade_length
, arv
->region()->last_frame(), 10);
2175 drag_info
.first_move
= false;
2179 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2181 if (drag_info
.first_move
) return;
2183 AudioRegionView
* arv
= static_cast<AudioRegionView
*>(drag_info
.data
);
2185 nframes64_t fade_length
;
2187 if (drag_info
.current_pointer_frame
> drag_info
.pointer_frame_offset
) {
2188 pos
= drag_info
.current_pointer_frame
- drag_info
.pointer_frame_offset
;
2194 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
2198 if (pos
> (arv
->region()->last_frame() - 64)) {
2199 fade_length
= 64; // this should really be a minimum fade defined somewhere
2201 else if (pos
< arv
->region()->position()) {
2202 fade_length
= arv
->region()->length();
2205 fade_length
= arv
->region()->last_frame() - pos
;
2208 begin_reversible_command (_("change fade out length"));
2210 for (RegionSelection::iterator i
= selection
->regions
.begin(); i
!= selection
->regions
.end(); ++i
) {
2212 AudioRegionView
* tmp
= dynamic_cast<AudioRegionView
*> (*i
);
2218 AutomationList
& alist
= tmp
->audio_region()->fade_out();
2219 XMLNode
&before
= alist
.get_state();
2221 tmp
->audio_region()->set_fade_out_length (fade_length
);
2222 tmp
->audio_region()->set_fade_out_active (true);
2224 XMLNode
&after
= alist
.get_state();
2225 session
->add_command(new MementoCommand
<AutomationList
>(alist
, &before
, &after
));
2228 commit_reversible_command ();
2232 Editor::start_cursor_grab (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2234 drag_info
.item
= item
;
2235 drag_info
.motion_callback
= &Editor::cursor_drag_motion_callback
;
2236 drag_info
.finished_callback
= &Editor::cursor_drag_finished_callback
;
2240 if ((drag_info
.data
= (item
->get_data ("cursor"))) == 0) {
2241 fatal
<< _("programming error: cursor canvas item has no cursor data pointer!") << endmsg
;
2245 Cursor
* cursor
= (Cursor
*) drag_info
.data
;
2247 if (cursor
== playhead_cursor
) {
2248 _dragging_playhead
= true;
2250 if (session
&& drag_info
.was_rolling
) {
2251 session
->request_stop (false, true);
2254 if (session
&& session
->is_auditioning()) {
2255 session
->cancel_audition ();
2259 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- cursor
->current_frame
;
2261 show_verbose_time_cursor (cursor
->current_frame
, 10);
2265 Editor::start_cursor_grab_no_stop (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2267 drag_info
.item
= item
;
2268 drag_info
.motion_callback
= &Editor::cursor_drag_motion_callback
;
2269 drag_info
.finished_callback
= &Editor::cursor_drag_finished_ensure_locate_callback
;
2273 if ((drag_info
.data
= (item
->get_data ("cursor"))) == 0) {
2274 fatal
<< _("programming error: cursor canvas item has no cursor data pointer!") << endmsg
;
2278 Cursor
* cursor
= (Cursor
*) drag_info
.data
;
2279 nframes64_t where
= event_frame (event
, 0, 0);
2282 playhead_cursor
->set_position (where
);
2284 if (cursor
== playhead_cursor
) {
2285 _dragging_playhead
= true;
2287 if (session
&& session
->is_auditioning()) {
2288 session
->cancel_audition ();
2292 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- cursor
->current_frame
;
2294 show_verbose_time_cursor (cursor
->current_frame
, 10);
2298 Editor::cursor_drag_motion_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2300 Cursor
* cursor
= (Cursor
*) drag_info
.data
;
2301 nframes64_t adjusted_frame
;
2303 if (drag_info
.current_pointer_frame
> drag_info
.pointer_frame_offset
) {
2304 adjusted_frame
= drag_info
.current_pointer_frame
- drag_info
.pointer_frame_offset
;
2310 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
2311 if (cursor
== playhead_cursor
) {
2312 snap_to (adjusted_frame
);
2316 if (adjusted_frame
== drag_info
.last_pointer_frame
) return;
2318 cursor
->set_position (adjusted_frame
);
2320 show_verbose_time_cursor (cursor
->current_frame
, 10);
2323 track_canvas
->update_now ();
2325 UpdateAllTransportClocks (cursor
->current_frame
);
2327 drag_info
.last_pointer_frame
= adjusted_frame
;
2328 drag_info
.first_move
= false;
2332 Editor::cursor_drag_finished_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2334 _dragging_playhead
= false;
2336 if (drag_info
.first_move
) {
2340 cursor_drag_motion_callback (item
, event
);
2342 if (item
== &playhead_cursor
->canvas_item
) {
2344 session
->request_locate (playhead_cursor
->current_frame
, drag_info
.was_rolling
);
2350 Editor::cursor_drag_finished_ensure_locate_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2352 _dragging_playhead
= false;
2354 cursor_drag_motion_callback (item
, event
);
2356 if (item
== &playhead_cursor
->canvas_item
) {
2358 session
->request_locate (playhead_cursor
->current_frame
, drag_info
.was_rolling
);
2364 Editor::update_marker_drag_item (Location
*location
)
2366 double x1
= frame_to_pixel (location
->start());
2367 double x2
= frame_to_pixel (location
->end());
2369 if (location
->is_mark()) {
2370 marker_drag_line_points
.front().set_x(x1
);
2371 marker_drag_line_points
.back().set_x(x1
);
2372 marker_drag_line
->property_points() = marker_drag_line_points
;
2374 range_marker_drag_rect
->property_x1() = x1
;
2375 range_marker_drag_rect
->property_x2() = x2
;
2380 Editor::start_marker_grab (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2384 if ((marker
= static_cast<Marker
*> (item
->get_data ("marker"))) == 0) {
2385 fatal
<< _("programming error: marker canvas item has no marker object pointer!") << endmsg
;
2391 Location
*location
= find_location_from_marker (marker
, is_start
);
2393 drag_info
.item
= item
;
2394 drag_info
.data
= marker
;
2395 drag_info
.motion_callback
= &Editor::marker_drag_motion_callback
;
2396 drag_info
.finished_callback
= &Editor::marker_drag_finished_callback
;
2400 _dragging_edit_point
= true;
2402 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- (is_start
? location
->start() : location
->end());
2404 update_marker_drag_item (location
);
2406 if (location
->is_mark()) {
2407 // marker_drag_line->show();
2408 // marker_drag_line->raise_to_top();
2410 range_marker_drag_rect
->show();
2411 //range_marker_drag_rect->raise_to_top();
2415 show_verbose_time_cursor (location
->start(), 10);
2417 show_verbose_time_cursor (location
->end(), 10);
2420 Selection::Operation op
= Keyboard::selection_type (event
->button
.state
);
2423 case Selection::Toggle
:
2424 selection
->toggle (marker
);
2426 case Selection::Set
:
2427 if (!selection
->selected (marker
)) {
2428 selection
->set (marker
);
2431 case Selection::Extend
:
2433 Locations::LocationList ll
;
2434 list
<Marker
*> to_add
;
2436 selection
->markers
.range (s
, e
);
2437 s
= min (marker
->position(), s
);
2438 e
= max (marker
->position(), e
);
2441 if (e
< max_frames
) {
2444 session
->locations()->find_all_between (s
, e
, ll
, Location::Flags (0));
2445 for (Locations::LocationList::iterator i
= ll
.begin(); i
!= ll
.end(); ++i
) {
2446 LocationMarkers
* lm
= find_location_markers (*i
);
2449 to_add
.push_back (lm
->start
);
2452 to_add
.push_back (lm
->end
);
2456 if (!to_add
.empty()) {
2457 selection
->add (to_add
);
2461 case Selection::Add
:
2462 selection
->add (marker
);
2466 /* set up copies for us to manipulate during the drag */
2468 drag_info
.clear_copied_locations ();
2470 for (MarkerSelection::iterator i
= selection
->markers
.begin(); i
!= selection
->markers
.end(); ++i
) {
2471 Location
*l
= find_location_from_marker (*i
, is_start
);
2472 drag_info
.copied_locations
.push_back (new Location (*l
));
2477 Editor::marker_drag_motion_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2479 nframes64_t f_delta
= 0;
2480 nframes64_t newframe
;
2482 bool move_both
= false;
2483 Marker
* dragged_marker
= (Marker
*) drag_info
.data
;
2485 Location
*real_location
;
2486 Location
*copy_location
;
2488 if (drag_info
.pointer_frame_offset
<= drag_info
.current_pointer_frame
) {
2489 newframe
= drag_info
.current_pointer_frame
- drag_info
.pointer_frame_offset
;
2494 nframes64_t next
= newframe
;
2496 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
2497 snap_to (newframe
, 0, true);
2500 if (drag_info
.current_pointer_frame
== drag_info
.last_pointer_frame
) {
2504 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
2508 MarkerSelection::iterator i
;
2509 list
<Location
*>::iterator x
;
2511 /* find the marker we're dragging, and compute the delta */
2513 for (i
= selection
->markers
.begin(), x
= drag_info
.copied_locations
.begin();
2514 x
!= drag_info
.copied_locations
.end() && i
!= selection
->markers
.end();
2520 if (marker
== dragged_marker
) {
2522 if ((real_location
= find_location_from_marker (marker
, is_start
)) == 0) {
2527 if (real_location
->is_mark()) {
2528 f_delta
= newframe
- copy_location
->start();
2532 switch (marker
->type()) {
2534 case Marker::LoopStart
:
2535 case Marker::PunchIn
:
2536 f_delta
= newframe
- copy_location
->start();
2540 case Marker::LoopEnd
:
2541 case Marker::PunchOut
:
2542 f_delta
= newframe
- copy_location
->end();
2545 /* what kind of marker is this ? */
2553 if (i
== selection
->markers
.end()) {
2554 /* hmm, impossible - we didn't find the dragged marker */
2558 /* now move them all */
2560 for (i
= selection
->markers
.begin(), x
= drag_info
.copied_locations
.begin();
2561 x
!= drag_info
.copied_locations
.end() && i
!= selection
->markers
.end();
2567 /* call this to find out if its the start or end */
2569 if ((real_location
= find_location_from_marker (marker
, is_start
)) == 0) {
2573 if (real_location
->locked()) {
2577 if (copy_location
->is_mark()) {
2581 copy_location
->set_start (copy_location
->start() + f_delta
);
2585 nframes64_t new_start
= copy_location
->start() + f_delta
;
2586 nframes64_t new_end
= copy_location
->end() + f_delta
;
2588 if (is_start
) { // start-of-range marker
2591 copy_location
->set_start (new_start
);
2592 copy_location
->set_end (new_end
);
2593 } else if (new_start
< copy_location
->end()) {
2594 copy_location
->set_start (new_start
);
2596 snap_to (next
, 1, true);
2597 copy_location
->set_end (next
);
2598 copy_location
->set_start (newframe
);
2601 } else { // end marker
2604 copy_location
->set_end (new_end
);
2605 copy_location
->set_start (new_start
);
2606 } else if (new_end
> copy_location
->start()) {
2607 copy_location
->set_end (new_end
);
2608 } else if (newframe
> 0) {
2609 snap_to (next
, -1, true);
2610 copy_location
->set_start (next
);
2611 copy_location
->set_end (newframe
);
2615 update_marker_drag_item (copy_location
);
2617 LocationMarkers
* lm
= find_location_markers (real_location
);
2620 lm
->set_position (copy_location
->start(), copy_location
->end());
2624 drag_info
.last_pointer_frame
= drag_info
.current_pointer_frame
;
2625 drag_info
.first_move
= false;
2627 if (drag_info
.copied_locations
.empty()) {
2631 if (Profile
->get_sae()) {
2632 edit_point_clock
.set (drag_info
.copied_locations
.front()->start());
2634 show_verbose_time_cursor (newframe
, 10);
2637 track_canvas
->update_now ();
2642 Editor::marker_drag_finished_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2644 if (drag_info
.first_move
) {
2646 /* just a click, do nothing but finish
2647 off the selection process
2650 Selection::Operation op
= Keyboard::selection_type (event
->button
.state
);
2651 Marker
* marker
= (Marker
*) drag_info
.data
;
2654 case Selection::Set
:
2655 if (selection
->selected (marker
) && selection
->markers
.size() > 1) {
2656 selection
->set (marker
);
2660 case Selection::Toggle
:
2661 case Selection::Extend
:
2662 case Selection::Add
:
2669 _dragging_edit_point
= false;
2672 begin_reversible_command ( _("move marker") );
2673 XMLNode
&before
= session
->locations()->get_state();
2675 MarkerSelection::iterator i
;
2676 list
<Location
*>::iterator x
;
2679 for (i
= selection
->markers
.begin(), x
= drag_info
.copied_locations
.begin();
2680 x
!= drag_info
.copied_locations
.end() && i
!= selection
->markers
.end();
2683 Location
* location
= find_location_from_marker ((*i
), is_start
);
2687 if (location
->locked()) {
2691 if (location
->is_mark()) {
2692 location
->set_start ((*x
)->start());
2694 location
->set ((*x
)->start(), (*x
)->end());
2699 XMLNode
&after
= session
->locations()->get_state();
2700 session
->add_command(new MementoCommand
<Locations
>(*(session
->locations()), &before
, &after
));
2701 commit_reversible_command ();
2703 marker_drag_line
->hide();
2704 range_marker_drag_rect
->hide();
2708 Editor::start_meter_marker_grab (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2711 MeterMarker
* meter_marker
;
2713 if ((marker
= reinterpret_cast<Marker
*> (item
->get_data ("marker"))) == 0) {
2714 fatal
<< _("programming error: meter marker canvas item has no marker object pointer!") << endmsg
;
2718 meter_marker
= dynamic_cast<MeterMarker
*> (marker
);
2720 MetricSection
& section (meter_marker
->meter());
2722 if (!section
.movable()) {
2726 drag_info
.item
= item
;
2727 drag_info
.copy
= false;
2728 drag_info
.data
= marker
;
2729 drag_info
.motion_callback
= &Editor::meter_marker_drag_motion_callback
;
2730 drag_info
.finished_callback
= &Editor::meter_marker_drag_finished_callback
;
2734 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- meter_marker
->meter().frame();
2736 show_verbose_time_cursor (drag_info
.current_pointer_frame
, 10);
2740 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2743 MeterMarker
* meter_marker
;
2745 if ((marker
= reinterpret_cast<Marker
*> (item
->get_data ("marker"))) == 0) {
2746 fatal
<< _("programming error: meter marker canvas item has no marker object pointer!") << endmsg
;
2750 meter_marker
= dynamic_cast<MeterMarker
*> (marker
);
2752 // create a dummy marker for visual representation of moving the copy.
2753 // The actual copying is not done before we reach the finish callback.
2755 snprintf (name
, sizeof(name
), "%g/%g", meter_marker
->meter().beats_per_bar(), meter_marker
->meter().note_divisor ());
2756 MeterMarker
* new_marker
= new MeterMarker(*this, *meter_group
, ARDOUR_UI::config()->canvasvar_MeterMarker
.get(), name
,
2757 *new MeterSection(meter_marker
->meter()));
2759 drag_info
.item
= &new_marker
->the_item();
2760 drag_info
.copy
= true;
2761 drag_info
.data
= new_marker
;
2762 drag_info
.motion_callback
= &Editor::meter_marker_drag_motion_callback
;
2763 drag_info
.finished_callback
= &Editor::meter_marker_drag_finished_callback
;
2767 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- meter_marker
->meter().frame();
2769 show_verbose_time_cursor (drag_info
.current_pointer_frame
, 10);
2773 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2775 MeterMarker
* marker
= (MeterMarker
*) drag_info
.data
;
2776 nframes64_t adjusted_frame
;
2778 if (drag_info
.current_pointer_frame
> drag_info
.pointer_frame_offset
) {
2779 adjusted_frame
= drag_info
.current_pointer_frame
- drag_info
.pointer_frame_offset
;
2785 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
2786 snap_to (adjusted_frame
);
2789 if (adjusted_frame
== drag_info
.last_pointer_frame
) return;
2791 marker
->set_position (adjusted_frame
);
2794 drag_info
.last_pointer_frame
= adjusted_frame
;
2795 drag_info
.first_move
= false;
2797 show_verbose_time_cursor (adjusted_frame
, 10);
2801 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2803 if (drag_info
.first_move
) return;
2805 meter_marker_drag_motion_callback (drag_info
.item
, event
);
2807 MeterMarker
* marker
= (MeterMarker
*) drag_info
.data
;
2810 TempoMap
& map (session
->tempo_map());
2811 map
.bbt_time (drag_info
.last_pointer_frame
, when
);
2813 if (drag_info
.copy
== true) {
2814 begin_reversible_command (_("copy meter mark"));
2815 XMLNode
&before
= map
.get_state();
2816 map
.add_meter (marker
->meter(), when
);
2817 XMLNode
&after
= map
.get_state();
2818 session
->add_command(new MementoCommand
<TempoMap
>(map
, &before
, &after
));
2819 commit_reversible_command ();
2821 // delete the dummy marker we used for visual representation of copying.
2822 // a new visual marker will show up automatically.
2825 begin_reversible_command (_("move meter mark"));
2826 XMLNode
&before
= map
.get_state();
2827 map
.move_meter (marker
->meter(), when
);
2828 XMLNode
&after
= map
.get_state();
2829 session
->add_command(new MementoCommand
<TempoMap
>(map
, &before
, &after
));
2830 commit_reversible_command ();
2835 Editor::start_tempo_marker_grab (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2838 TempoMarker
* tempo_marker
;
2840 if ((marker
= reinterpret_cast<Marker
*> (item
->get_data ("marker"))) == 0) {
2841 fatal
<< _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg
;
2845 if ((tempo_marker
= dynamic_cast<TempoMarker
*> (marker
)) == 0) {
2846 fatal
<< _("programming error: marker for tempo is not a tempo marker!") << endmsg
;
2850 MetricSection
& section (tempo_marker
->tempo());
2852 if (!section
.movable()) {
2856 drag_info
.item
= item
;
2857 drag_info
.copy
= false;
2858 drag_info
.data
= marker
;
2859 drag_info
.motion_callback
= &Editor::tempo_marker_drag_motion_callback
;
2860 drag_info
.finished_callback
= &Editor::tempo_marker_drag_finished_callback
;
2864 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- tempo_marker
->tempo().frame();
2865 show_verbose_time_cursor (drag_info
.current_pointer_frame
, 10);
2869 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2872 TempoMarker
* tempo_marker
;
2874 if ((marker
= reinterpret_cast<Marker
*> (item
->get_data ("marker"))) == 0) {
2875 fatal
<< _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg
;
2879 if ((tempo_marker
= dynamic_cast<TempoMarker
*> (marker
)) == 0) {
2880 fatal
<< _("programming error: marker for tempo is not a tempo marker!") << endmsg
;
2884 // create a dummy marker for visual representation of moving the copy.
2885 // The actual copying is not done before we reach the finish callback.
2887 snprintf (name
, sizeof (name
), "%.2f", tempo_marker
->tempo().beats_per_minute());
2888 TempoMarker
* new_marker
= new TempoMarker(*this, *tempo_group
, ARDOUR_UI::config()->canvasvar_TempoMarker
.get(), name
,
2889 *new TempoSection(tempo_marker
->tempo()));
2891 drag_info
.item
= &new_marker
->the_item();
2892 drag_info
.copy
= true;
2893 drag_info
.data
= new_marker
;
2894 drag_info
.motion_callback
= &Editor::tempo_marker_drag_motion_callback
;
2895 drag_info
.finished_callback
= &Editor::tempo_marker_drag_finished_callback
;
2899 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- tempo_marker
->tempo().frame();
2901 show_verbose_time_cursor (drag_info
.current_pointer_frame
, 10);
2905 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2907 TempoMarker
* marker
= (TempoMarker
*) drag_info
.data
;
2908 nframes64_t adjusted_frame
;
2910 if (drag_info
.current_pointer_frame
> drag_info
.pointer_frame_offset
) {
2911 adjusted_frame
= drag_info
.current_pointer_frame
- drag_info
.pointer_frame_offset
;
2917 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
2918 snap_to (adjusted_frame
);
2921 if (adjusted_frame
== drag_info
.last_pointer_frame
) return;
2923 /* OK, we've moved far enough to make it worth actually move the thing. */
2925 marker
->set_position (adjusted_frame
);
2927 show_verbose_time_cursor (adjusted_frame
, 10);
2929 drag_info
.last_pointer_frame
= adjusted_frame
;
2930 drag_info
.first_move
= false;
2934 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2936 if (drag_info
.first_move
) return;
2938 tempo_marker_drag_motion_callback (drag_info
.item
, event
);
2940 TempoMarker
* marker
= (TempoMarker
*) drag_info
.data
;
2943 TempoMap
& map (session
->tempo_map());
2944 map
.bbt_time (drag_info
.last_pointer_frame
, when
);
2946 if (drag_info
.copy
== true) {
2947 begin_reversible_command (_("copy tempo mark"));
2948 XMLNode
&before
= map
.get_state();
2949 map
.add_tempo (marker
->tempo(), when
);
2950 XMLNode
&after
= map
.get_state();
2951 session
->add_command (new MementoCommand
<TempoMap
>(map
, &before
, &after
));
2952 commit_reversible_command ();
2954 // delete the dummy marker we used for visual representation of copying.
2955 // a new visual marker will show up automatically.
2958 begin_reversible_command (_("move tempo mark"));
2959 XMLNode
&before
= map
.get_state();
2960 map
.move_tempo (marker
->tempo(), when
);
2961 XMLNode
&after
= map
.get_state();
2962 session
->add_command (new MementoCommand
<TempoMap
>(map
, &before
, &after
));
2963 commit_reversible_command ();
2968 Editor::remove_gain_control_point (ArdourCanvas::Item
*item
, GdkEvent
* event
)
2970 ControlPoint
* control_point
;
2972 if ((control_point
= reinterpret_cast<ControlPoint
*> (item
->get_data ("control_point"))) == 0) {
2973 fatal
<< _("programming error: control point canvas item has no control point object pointer!") << endmsg
;
2977 // We shouldn't remove the first or last gain point
2978 if (control_point
->line
.is_last_point(*control_point
) ||
2979 control_point
->line
.is_first_point(*control_point
)) {
2983 control_point
->line
.remove_point (*control_point
);
2987 Editor::remove_control_point (ArdourCanvas::Item
*item
, GdkEvent
* event
)
2989 ControlPoint
* control_point
;
2991 if ((control_point
= reinterpret_cast<ControlPoint
*> (item
->get_data ("control_point"))) == 0) {
2992 fatal
<< _("programming error: control point canvas item has no control point object pointer!") << endmsg
;
2996 control_point
->line
.remove_point (*control_point
);
3000 Editor::start_control_point_grab (ArdourCanvas::Item
* item
, GdkEvent
* event
)
3002 ControlPoint
* control_point
;
3004 if ((control_point
= reinterpret_cast<ControlPoint
*> (item
->get_data ("control_point"))) == 0) {
3005 fatal
<< _("programming error: control point canvas item has no control point object pointer!") << endmsg
;
3009 drag_info
.item
= item
;
3010 drag_info
.data
= control_point
;
3011 drag_info
.motion_callback
= &Editor::control_point_drag_motion_callback
;
3012 drag_info
.finished_callback
= &Editor::control_point_drag_finished_callback
;
3014 start_grab (event
, fader_cursor
);
3016 // start the grab at the center of the control point so
3017 // the point doesn't 'jump' to the mouse after the first drag
3018 drag_info
.grab_x
= control_point
->get_x();
3019 drag_info
.grab_y
= control_point
->get_y();
3020 control_point
->line
.parent_group().i2w(drag_info
.grab_x
, drag_info
.grab_y
);
3021 track_canvas
->w2c(drag_info
.grab_x
, drag_info
.grab_y
, drag_info
.grab_x
, drag_info
.grab_y
);
3023 drag_info
.grab_frame
= pixel_to_frame(drag_info
.grab_x
);
3025 control_point
->line
.start_drag (control_point
, drag_info
.grab_frame
, 0);
3027 float fraction
= 1.0 - (control_point
->get_y() / control_point
->line
.height());
3028 set_verbose_canvas_cursor (control_point
->line
.get_verbose_cursor_string (fraction
),
3029 drag_info
.current_pointer_x
+ 10, drag_info
.current_pointer_y
+ 10);
3031 show_verbose_canvas_cursor ();
3035 Editor::control_point_drag_motion_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
3037 ControlPoint
* cp
= reinterpret_cast<ControlPoint
*> (drag_info
.data
);
3039 double dx
= drag_info
.current_pointer_x
- drag_info
.last_pointer_x
;
3040 double dy
= drag_info
.current_pointer_y
- drag_info
.last_pointer_y
;
3042 if (event
->button
.state
& Keyboard::SecondaryModifier
) {
3047 double cx
= drag_info
.grab_x
+ drag_info
.cumulative_x_drag
+ dx
;
3048 double cy
= drag_info
.grab_y
+ drag_info
.cumulative_y_drag
+ dy
;
3050 // calculate zero crossing point. back off by .01 to stay on the
3051 // positive side of zero
3053 double zero_gain_y
= (1.0 - ZERO_GAIN_FRACTION
) * cp
->line
.height() - .01;
3054 cp
->line
.parent_group().i2w(_unused
, zero_gain_y
);
3056 // make sure we hit zero when passing through
3057 if ((cy
< zero_gain_y
and (cy
- dy
) > zero_gain_y
)
3058 or (cy
> zero_gain_y
and (cy
- dy
) < zero_gain_y
)) {
3062 if (drag_info
.x_constrained
) {
3063 cx
= drag_info
.grab_x
;
3065 if (drag_info
.y_constrained
) {
3066 cy
= drag_info
.grab_y
;
3069 drag_info
.cumulative_x_drag
= cx
- drag_info
.grab_x
;
3070 drag_info
.cumulative_y_drag
= cy
- drag_info
.grab_y
;
3072 cp
->line
.parent_group().w2i (cx
, cy
);
3076 cy
= min ((double) cp
->line
.height(), cy
);
3078 //translate cx to frames
3079 nframes64_t cx_frames
= unit_to_frame (cx
);
3081 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier()) && !drag_info
.x_constrained
) {
3082 snap_to (cx_frames
);
3085 float fraction
= 1.0 - (cy
/ cp
->line
.height());
3089 if (Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::PrimaryModifier
)) {
3095 cp
->line
.point_drag (*cp
, cx_frames
, fraction
, push
);
3097 set_verbose_canvas_cursor_text (cp
->line
.get_verbose_cursor_string (fraction
));
3099 drag_info
.first_move
= false;
3103 Editor::control_point_drag_finished_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
3105 ControlPoint
* cp
= reinterpret_cast<ControlPoint
*> (drag_info
.data
);
3107 if (drag_info
.first_move
) {
3111 if ((event
->type
== GDK_BUTTON_RELEASE
) && (event
->button
.button
== 1) && Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::TertiaryModifier
)) {
3112 reset_point_selection ();
3116 control_point_drag_motion_callback (item
, event
);
3118 cp
->line
.end_drag (cp
);
3122 Editor::start_line_grab_from_regionview (ArdourCanvas::Item
* item
, GdkEvent
* event
)
3124 switch (mouse_mode
) {
3126 assert(dynamic_cast<AudioRegionView
*>(clicked_regionview
));
3127 start_line_grab (dynamic_cast<AudioRegionView
*>(clicked_regionview
)->get_gain_line(), event
);
3135 Editor::start_line_grab_from_line (ArdourCanvas::Item
* item
, GdkEvent
* event
)
3139 if ((al
= reinterpret_cast<AutomationLine
*> (item
->get_data ("line"))) == 0) {
3140 fatal
<< _("programming error: line canvas item has no line pointer!") << endmsg
;
3144 start_line_grab (al
, event
);
3148 Editor::start_line_grab (AutomationLine
* line
, GdkEvent
* event
)
3152 nframes64_t frame_within_region
;
3154 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3155 origin, and ditto for y.
3158 cx
= event
->button
.x
;
3159 cy
= event
->button
.y
;
3161 line
->parent_group().w2i (cx
, cy
);
3163 frame_within_region
= (nframes64_t
) floor (cx
* frames_per_unit
);
3165 if (!line
->control_points_adjacent (frame_within_region
, current_line_drag_info
.before
,
3166 current_line_drag_info
.after
)) {
3167 /* no adjacent points */
3171 drag_info
.item
= &line
->grab_item();
3172 drag_info
.data
= line
;
3173 drag_info
.motion_callback
= &Editor::line_drag_motion_callback
;
3174 drag_info
.finished_callback
= &Editor::line_drag_finished_callback
;
3176 start_grab (event
, fader_cursor
);
3178 /* store grab start in parent frame */
3180 drag_info
.grab_x
= cx
;
3181 drag_info
.grab_y
= cy
;
3183 double fraction
= 1.0 - (cy
/ line
->height());
3185 line
->start_drag (0, drag_info
.grab_frame
, fraction
);
3187 set_verbose_canvas_cursor (line
->get_verbose_cursor_string (fraction
),
3188 drag_info
.current_pointer_x
+ 10, drag_info
.current_pointer_y
+ 10);
3189 show_verbose_canvas_cursor ();
3193 Editor::line_drag_motion_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
3195 AutomationLine
* line
= reinterpret_cast<AutomationLine
*> (drag_info
.data
);
3197 double dy
= drag_info
.current_pointer_y
- drag_info
.last_pointer_y
;
3199 if (event
->button
.state
& Keyboard::SecondaryModifier
) {
3203 double cy
= drag_info
.grab_y
+ drag_info
.cumulative_y_drag
+ dy
;
3205 drag_info
.cumulative_y_drag
= cy
- drag_info
.grab_y
;
3208 cy
= min ((double) line
->height(), cy
);
3210 double fraction
= 1.0 - (cy
/ line
->height());
3214 if (Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::PrimaryModifier
)) {
3220 line
->line_drag (current_line_drag_info
.before
, current_line_drag_info
.after
, fraction
, push
);
3222 set_verbose_canvas_cursor_text (line
->get_verbose_cursor_string (fraction
));
3226 Editor::line_drag_finished_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
3228 AutomationLine
* line
= reinterpret_cast<AutomationLine
*> (drag_info
.data
);
3229 line_drag_motion_callback (item
, event
);
3234 Editor::start_region_grab (ArdourCanvas::Item
* item
, GdkEvent
* event
)
3236 if (selection
->regions
.empty() || clicked_regionview
== 0) {
3240 drag_info
.copy
= false;
3241 drag_info
.item
= item
;
3242 drag_info
.data
= clicked_regionview
;
3244 if (Config
->get_edit_mode() == Splice
) {
3245 drag_info
.motion_callback
= &Editor::region_drag_splice_motion_callback
;
3246 drag_info
.finished_callback
= &Editor::region_drag_splice_finished_callback
;
3248 drag_info
.motion_callback
= &Editor::region_drag_motion_callback
;
3249 drag_info
.finished_callback
= &Editor::region_drag_finished_callback
;
3255 TimeAxisView
* tvp
= clicked_trackview
;
3256 RouteTimeAxisView
* tv
= dynamic_cast<RouteTimeAxisView
*>(tvp
);
3258 if (tv
&& tv
->is_audio_track()) {
3259 speed
= tv
->get_diskstream()->speed();
3262 drag_info
.last_frame_position
= (nframes64_t
) (clicked_regionview
->region()->position() / speed
);
3263 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- drag_info
.last_frame_position
;
3264 drag_info
.source_trackview
= &clicked_regionview
->get_time_axis_view();
3265 drag_info
.dest_trackview
= drag_info
.source_trackview
;
3266 // we want a move threshold
3267 drag_info
.want_move_threshold
= true;
3269 show_verbose_time_cursor (drag_info
.last_frame_position
, 10);
3271 begin_reversible_command (_("move region(s)"));
3273 _region_motion_group
->raise_to_top ();
3275 /* sync the canvas to what we think is its current state */
3276 track_canvas
->update_now();
3280 Editor::start_region_copy_grab (ArdourCanvas::Item
* item
, GdkEvent
* event
)
3282 if (selection
->regions
.empty() || clicked_regionview
== 0) {
3286 drag_info
.copy
= true;
3287 drag_info
.item
= item
;
3288 drag_info
.data
= clicked_regionview
;
3292 TimeAxisView
* tv
= &clicked_regionview
->get_time_axis_view();
3293 RouteTimeAxisView
* atv
= dynamic_cast<RouteTimeAxisView
*>(tv
);
3296 if (atv
&& atv
->is_audio_track()) {
3297 speed
= atv
->get_diskstream()->speed();
3300 drag_info
.source_trackview
= &clicked_regionview
->get_time_axis_view();
3301 drag_info
.dest_trackview
= drag_info
.source_trackview
;
3302 drag_info
.last_frame_position
= (nframes64_t
) (clicked_regionview
->region()->position() / speed
);
3303 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- drag_info
.last_frame_position
;
3304 // we want a move threshold
3305 drag_info
.want_move_threshold
= true;
3306 drag_info
.motion_callback
= &Editor::region_drag_motion_callback
;
3307 drag_info
.finished_callback
= &Editor::region_drag_finished_callback
;
3308 show_verbose_time_cursor (drag_info
.last_frame_position
, 10);
3309 _region_motion_group
->raise_to_top ();
3313 Editor::start_region_brush_grab (ArdourCanvas::Item
* item
, GdkEvent
* event
)
3315 if (selection
->regions
.empty() || clicked_regionview
== 0 || Config
->get_edit_mode() == Splice
) {
3319 drag_info
.copy
= false;
3320 drag_info
.item
= item
;
3321 drag_info
.data
= clicked_regionview
;
3322 drag_info
.motion_callback
= &Editor::region_drag_motion_callback
;
3323 drag_info
.finished_callback
= &Editor::region_drag_finished_callback
;
3328 TimeAxisView
* tvp
= clicked_trackview
;
3329 RouteTimeAxisView
* tv
= dynamic_cast<RouteTimeAxisView
*>(tvp
);
3331 if (tv
&& tv
->is_audio_track()) {
3332 speed
= tv
->get_diskstream()->speed();
3335 drag_info
.last_frame_position
= (nframes64_t
) (clicked_regionview
->region()->position() / speed
);
3336 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- drag_info
.last_frame_position
;
3337 drag_info
.source_trackview
= &clicked_regionview
->get_time_axis_view();
3338 drag_info
.dest_trackview
= drag_info
.source_trackview
;
3339 // we want a move threshold
3340 drag_info
.want_move_threshold
= true;
3341 drag_info
.brushing
= true;
3343 begin_reversible_command (_("Drag region brush"));
3347 Editor::possibly_copy_regions_during_grab (GdkEvent
* event
)
3349 if (drag_info
.copy
&& drag_info
.move_threshold_passed
&& drag_info
.want_move_threshold
) {
3351 drag_info
.want_move_threshold
= false; // don't copy again
3353 /* duplicate the regionview(s) and region(s) */
3355 vector
<RegionView
*> new_regionviews
;
3357 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin(); i
!= selection
->regions
.by_layer().end(); ++i
) {
3359 AudioRegionView
* arv
;
3361 if ((arv
= dynamic_cast<AudioRegionView
*>(*i
)) == 0) {
3362 /* XXX handle MIDI here */
3366 const boost::shared_ptr
<const Region
> original
= arv
->region();
3367 boost::shared_ptr
<Region
> region_copy
= RegionFactory::create (original
);
3368 boost::shared_ptr
<AudioRegion
> ar
= boost::dynamic_pointer_cast
<AudioRegion
> (region_copy
);
3370 nrv
= new AudioRegionView (*arv
, ar
);
3371 nrv
->get_canvas_group()->show ();
3373 new_regionviews
.push_back (nrv
);
3376 if (new_regionviews
.empty()) {
3380 /* reset selection to new regionviews. This will not set selection visual status for
3381 these regionviews since they don't belong to a track, so do that by hand too.
3384 selection
->set (new_regionviews
);
3386 for (vector
<RegionView
*>::iterator i
= new_regionviews
.begin(); i
!= new_regionviews
.end(); ++i
) {
3387 (*i
)->set_selected (true);
3390 /* reset drag_info data to reflect the fact that we are dragging the copies */
3392 drag_info
.data
= new_regionviews
.front();
3394 swap_grab (new_regionviews
.front()->get_canvas_group (), 0, event
->motion
.time
);
3396 sync the canvas to what we think is its current state
3397 without it, the canvas seems to
3398 "forget" to update properly after the upcoming reparent()
3399 ..only if the mouse is in rapid motion at the time of the grab.
3400 something to do with regionview creation raking so long?
3402 track_canvas
->update_now();
3407 Editor::check_region_drag_possible (AudioTimeAxisView
** tv
)
3409 /* Which trackview is this ? */
3411 TimeAxisView
* tvp
= trackview_by_y_position (drag_info
.current_pointer_y
);
3412 (*tv
) = dynamic_cast<AudioTimeAxisView
*>(tvp
);
3414 /* The region motion is only processed if the pointer is over
3418 if (!(*tv
) || !(*tv
)->is_audio_track()) {
3419 /* To make sure we hide the verbose canvas cursor when the mouse is
3420 not held over and audiotrack.
3422 hide_verbose_canvas_cursor ();
3429 struct RegionSelectionByPosition
{
3430 bool operator() (RegionView
*a
, RegionView
* b
) {
3431 return a
->region()->position () < b
->region()->position();
3436 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
3438 AudioTimeAxisView
* tv
;
3440 if (!check_region_drag_possible (&tv
)) {
3444 if (!drag_info
.move_threshold_passed
) {
3450 if (drag_info
.current_pointer_x
- drag_info
.grab_x
> 0) {
3456 RegionSelection
copy (selection
->regions
);
3458 RegionSelectionByPosition cmp
;
3461 for (RegionSelection::iterator i
= copy
.begin(); i
!= copy
.end(); ++i
) {
3463 AudioTimeAxisView
* atv
= dynamic_cast<AudioTimeAxisView
*> (&(*i
)->get_time_axis_view());
3469 boost::shared_ptr
<Playlist
> playlist
;
3471 if ((playlist
= atv
->playlist()) == 0) {
3475 if (!playlist
->region_is_shuffle_constrained ((*i
)->region())) {
3480 if (drag_info
.current_pointer_frame
< (*i
)->region()->last_frame() + 1) {
3484 if (drag_info
.current_pointer_frame
> (*i
)->region()->first_frame()) {
3490 playlist
->shuffle ((*i
)->region(), dir
);
3492 drag_info
.grab_x
= drag_info
.current_pointer_x
;
3497 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
3502 Editor::region_drag_motion_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
3506 nframes64_t pending_region_position
= 0;
3507 int32_t pointer_y_span
= 0, canvas_pointer_y_span
= 0, original_pointer_order
;
3508 int32_t visible_y_high
= 0, visible_y_low
= 512; //high meaning higher numbered.. not the height on the screen
3509 bool clamp_y_axis
= false;
3510 vector
<int32_t> height_list(512) ;
3511 vector
<int32_t>::iterator j
;
3512 AudioTimeAxisView
* tv
;
3514 possibly_copy_regions_during_grab (event
);
3516 if (!check_region_drag_possible (&tv
)) {
3520 original_pointer_order
= drag_info
.dest_trackview
->order
;
3522 /************************************************************
3524 ************************************************************/
3526 if (drag_info
.brushing
) {
3527 clamp_y_axis
= true;
3532 if ((pointer_y_span
= (drag_info
.dest_trackview
->order
- tv
->order
)) != 0) {
3534 int32_t children
= 0, numtracks
= 0;
3535 // XXX hard coding track limit, oh my, so very very bad
3536 bitset
<1024> tracks (0x00ul
);
3537 /* get a bitmask representing the visible tracks */
3539 for (TrackViewList::iterator i
= track_views
.begin(); i
!= track_views
.end(); ++i
) {
3540 TimeAxisView
*tracklist_timeview
;
3541 tracklist_timeview
= (*i
);
3542 AudioTimeAxisView
* atv2
= dynamic_cast<AudioTimeAxisView
*>(tracklist_timeview
);
3543 list
<TimeAxisView
*> children_list
;
3545 /* zeroes are audio tracks. ones are other types. */
3547 if (!atv2
->hidden()) {
3549 if (visible_y_high
< atv2
->order
) {
3550 visible_y_high
= atv2
->order
;
3552 if (visible_y_low
> atv2
->order
) {
3553 visible_y_low
= atv2
->order
;
3556 if (!atv2
->is_audio_track()) {
3557 tracks
= tracks
|= (0x01 << atv2
->order
);
3560 height_list
[atv2
->order
] = (*i
)->current_height();
3562 if ((children_list
= atv2
->get_child_list()).size() > 0) {
3563 for (list
<TimeAxisView
*>::iterator j
= children_list
.begin(); j
!= children_list
.end(); ++j
) {
3564 tracks
= tracks
|= (0x01 << (atv2
->order
+ children
));
3565 height_list
[atv2
->order
+ children
] = (*j
)->current_height();
3573 /* find the actual span according to the canvas */
3575 canvas_pointer_y_span
= pointer_y_span
;
3576 if (drag_info
.dest_trackview
->order
>= tv
->order
) {
3578 for (y
= tv
->order
; y
< drag_info
.dest_trackview
->order
; y
++) {
3579 if (height_list
[y
] == 0 ) {
3580 canvas_pointer_y_span
--;
3585 for (y
= drag_info
.dest_trackview
->order
;y
<= tv
->order
; y
++) {
3586 if ( height_list
[y
] == 0 ) {
3587 canvas_pointer_y_span
++;
3592 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin(); i
!= selection
->regions
.by_layer().end(); ++i
) {
3593 RegionView
* rv2
= (*i
);
3594 double ix1
, ix2
, iy1
, iy2
;
3597 if (rv2
->region()->locked()) {
3601 rv2
->get_canvas_frame()->get_bounds (ix1
, iy1
, ix2
, iy2
);
3602 rv2
->get_canvas_group()->i2w (ix1
, iy1
);
3603 iy1
+= vertical_adjustment
.get_value() - canvas_timebars_vsize
;
3605 TimeAxisView
* tvp2
= trackview_by_y_position (iy1
);
3606 RouteTimeAxisView
* atv2
= dynamic_cast<RouteTimeAxisView
*>(tvp2
);
3608 if (atv2
->order
!= original_pointer_order
) {
3609 /* this isn't the pointer track */
3611 if (canvas_pointer_y_span
> 0) {
3613 /* moving up the canvas */
3614 if ((atv2
->order
- canvas_pointer_y_span
) >= visible_y_low
) {
3616 int32_t visible_tracks
= 0;
3617 while (visible_tracks
< canvas_pointer_y_span
) {
3620 while (height_list
[atv2
->order
- (visible_tracks
- n
)] == 0) {
3621 /* we're passing through a hidden track */
3626 if (tracks
[atv2
->order
- (canvas_pointer_y_span
- n
)] != 0x00) {
3627 clamp_y_axis
= true;
3631 clamp_y_axis
= true;
3634 } else if (canvas_pointer_y_span
< 0) {
3636 /*moving down the canvas*/
3638 if ((atv2
->order
- (canvas_pointer_y_span
- n
)) <= visible_y_high
) { // we will overflow
3641 int32_t visible_tracks
= 0;
3643 while (visible_tracks
> canvas_pointer_y_span
) {
3646 while (height_list
[atv2
->order
- (visible_tracks
- n
)] == 0) {
3650 if ( tracks
[atv2
->order
- ( canvas_pointer_y_span
- n
)] != 0x00) {
3651 clamp_y_axis
= true;
3656 clamp_y_axis
= true;
3662 /* this is the pointer's track */
3663 if ((atv2
->order
- pointer_y_span
) > visible_y_high
) { // we will overflow
3664 clamp_y_axis
= true;
3665 } else if ((atv2
->order
- pointer_y_span
) < visible_y_low
) { // we will underflow
3666 clamp_y_axis
= true;
3674 } else if (drag_info
.dest_trackview
== tv
) {
3675 clamp_y_axis
= true;
3679 if (!clamp_y_axis
) {
3680 drag_info
.dest_trackview
= tv
;
3683 /************************************************************
3685 ************************************************************/
3687 /* compute the amount of pointer motion in frames, and where
3688 the region would be if we moved it by that much.
3690 if ( drag_info
.move_threshold_passed
) {
3692 if (drag_info
.current_pointer_frame
>= drag_info
.pointer_frame_offset
) {
3694 nframes64_t sync_frame
;
3695 nframes64_t sync_offset
;
3698 pending_region_position
= drag_info
.current_pointer_frame
- drag_info
.pointer_frame_offset
;
3700 sync_offset
= clicked_regionview
->region()->sync_offset (sync_dir
);
3702 /* we don't handle a sync point that lies before zero.
3704 if (sync_dir
>= 0 || (sync_dir
< 0 && pending_region_position
>= sync_offset
)) {
3705 sync_frame
= pending_region_position
+ (sync_dir
*sync_offset
);
3707 /* we snap if the snap modifier is not enabled.
3710 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
3711 snap_to (sync_frame
);
3714 pending_region_position
= clicked_regionview
->region()->adjust_to_sync (sync_frame
);
3717 pending_region_position
= drag_info
.last_frame_position
;
3721 pending_region_position
= 0;
3724 if (pending_region_position
> max_frames
- clicked_regionview
->region()->length()) {
3725 pending_region_position
= drag_info
.last_frame_position
;
3728 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3730 bool x_move_allowed
;
3732 if (Config
->get_edit_mode() == Lock
) {
3733 x_move_allowed
= drag_info
.x_constrained
;
3735 x_move_allowed
= !drag_info
.x_constrained
;
3738 if (( pending_region_position
!= drag_info
.last_frame_position
) && x_move_allowed
) {
3740 /* now compute the canvas unit distance we need to move the regionview
3741 to make it appear at the new location.
3744 if (pending_region_position
> drag_info
.last_frame_position
) {
3745 x_delta
= ((double) (pending_region_position
- drag_info
.last_frame_position
) / frames_per_unit
);
3747 x_delta
= -((double) (drag_info
.last_frame_position
- pending_region_position
) / frames_per_unit
);
3748 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin(); i
!= selection
->regions
.by_layer().end(); ++i
) {
3750 RegionView
* rv2
= (*i
);
3752 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3754 double ix1
, ix2
, iy1
, iy2
;
3755 rv2
->get_canvas_frame()->get_bounds (ix1
, iy1
, ix2
, iy2
);
3756 rv2
->get_canvas_group()->i2w (ix1
, iy1
);
3758 if (-x_delta
> ix1
+ horizontal_adjustment
.get_value()) {
3760 pending_region_position
= drag_info
.last_frame_position
;
3767 drag_info
.last_frame_position
= pending_region_position
;
3774 /* threshold not passed */
3779 /*************************************************************
3781 ************************************************************/
3783 if (x_delta
== 0 && (pointer_y_span
== 0)) {
3784 /* haven't reached next snap point, and we're not switching
3785 trackviews. nothing to do.
3790 /*************************************************************
3792 ************************************************************/
3793 bool do_move
= true;
3794 if (drag_info
.first_move
) {
3795 if (!drag_info
.move_threshold_passed
) {
3802 pair
<set
<boost::shared_ptr
<Playlist
> >::iterator
,bool> insert_result
;
3803 const list
<RegionView
*>& layered_regions
= selection
->regions
.by_layer();
3805 for (list
<RegionView
*>::const_iterator i
= layered_regions
.begin(); i
!= layered_regions
.end(); ++i
) {
3807 RegionView
* rv
= (*i
);
3808 double ix1
, ix2
, iy1
, iy2
;
3809 int32_t temp_pointer_y_span
= pointer_y_span
;
3811 if (rv
->region()->locked()) {
3815 /* get item BBox, which will be relative to parent. so we have
3816 to query on a child, then convert to world coordinates using
3820 rv
->get_canvas_frame()->get_bounds (ix1
, iy1
, ix2
, iy2
);
3821 rv
->get_canvas_group()->i2w (ix1
, iy1
);
3823 /* for evaluation of the track position of iy1, we have to adjust
3824 to allow for the vertical scrolling adjustment and the height of the timebars.
3827 iy1
+= get_trackview_group_vertical_offset ();
3828 if (drag_info
.first_move
) {
3830 // hide any dependent views
3832 rv
->get_time_axis_view().hide_dependent_views (*rv
);
3835 reparent to a non scrolling group so that we can keep the
3836 region selection above all time axis views.
3837 reparenting means we have to move the rv as the two
3838 parent groups have different coordinates.
3841 rv
->get_canvas_group()->property_y() = iy1
- 1;
3842 rv
->get_canvas_group()->reparent(*_region_motion_group
);
3844 rv
->fake_set_opaque (true);
3847 TimeAxisView
* tvp2
= trackview_by_y_position (iy1
);
3848 AudioTimeAxisView
* canvas_atv
= dynamic_cast<AudioTimeAxisView
*>(tvp2
);
3849 AudioTimeAxisView
* temp_atv
;
3851 if ((pointer_y_span
!= 0) && !clamp_y_axis
) {
3854 for (j
= height_list
.begin(); j
!= height_list
.end(); j
++) {
3855 if (x
== canvas_atv
->order
) {
3856 /* we found the track the region is on */
3857 if (x
!= original_pointer_order
) {
3858 /*this isn't from the same track we're dragging from */
3859 temp_pointer_y_span
= canvas_pointer_y_span
;
3861 while (temp_pointer_y_span
> 0) {
3862 /* we're moving up canvas-wise,
3863 so we need to find the next track height
3865 if (j
!= height_list
.begin()) {
3868 if (x
!= original_pointer_order
) {
3869 /* we're not from the dragged track, so ignore hidden tracks. */
3871 temp_pointer_y_span
++;
3875 temp_pointer_y_span
--;
3878 while (temp_pointer_y_span
< 0) {
3880 if (x
!= original_pointer_order
) {
3882 temp_pointer_y_span
--;
3886 if (j
!= height_list
.end()) {
3889 temp_pointer_y_span
++;
3891 /* find out where we'll be when we move and set height accordingly */
3893 tvp2
= trackview_by_y_position (iy1
+ y_delta
);
3894 temp_atv
= dynamic_cast<AudioTimeAxisView
*>(tvp2
);
3895 rv
->set_height (temp_atv
->current_height());
3897 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3898 personally, i think this can confuse things, but never mind.
3901 //const GdkColor& col (temp_atv->view->get_region_color());
3902 //rv->set_color (const_cast<GdkColor&>(col));
3909 if (drag_info
.brushing
) {
3910 mouse_brush_insert_region (rv
, pending_region_position
);
3912 rv
->move (x_delta
, y_delta
);
3915 } /* foreach region */
3919 if (drag_info
.first_move
&& drag_info
.move_threshold_passed
) {
3920 cursor_group
->raise_to_top();
3921 drag_info
.first_move
= false;
3924 if (x_delta
!= 0 && !drag_info
.brushing
) {
3925 show_verbose_time_cursor (drag_info
.last_frame_position
, 10);
3930 Editor::region_drag_finished_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
3932 bool nocommit
= true;
3933 vector
<RegionView
*> copies
;
3934 RouteTimeAxisView
* source_tv
;
3935 boost::shared_ptr
<Diskstream
> ds
;
3936 boost::shared_ptr
<Playlist
> from_playlist
;
3937 vector
<RegionView
*> new_selection
;
3938 typedef set
<boost::shared_ptr
<Playlist
> > PlaylistSet
;
3939 PlaylistSet modified_playlists
;
3940 PlaylistSet frozen_playlists
;
3941 list
<sigc::connection
> modified_playlist_connections
;
3942 pair
<PlaylistSet::iterator
,bool> insert_result
, frozen_insert_result
;
3943 nframes64_t drag_delta
;
3944 bool changed_tracks
, changed_position
;
3946 /* first_move is set to false if the regionview has been moved in the
3950 if (drag_info
.first_move
) {
3957 if (Config
->get_edit_mode() == Splice
&& !pre_drag_region_selection
.empty()) {
3958 selection
->set (pre_drag_region_selection
);
3959 pre_drag_region_selection
.clear ();
3962 if (drag_info
.brushing
) {
3963 /* all changes were made during motion event handlers */
3965 if (drag_info
.copy
) {
3966 for (list
<RegionView
*>::iterator i
= selection
->regions
.begin(); i
!= selection
->regions
.end(); ++i
) {
3967 copies
.push_back (*i
);
3976 /* reverse this here so that we have the correct logic to finalize
3980 if (Config
->get_edit_mode() == Lock
) {
3981 drag_info
.x_constrained
= !drag_info
.x_constrained
;
3984 if (drag_info
.copy
) {
3985 if (drag_info
.x_constrained
) {
3986 op_string
= _("fixed time region copy");
3988 op_string
= _("region copy");
3991 if (drag_info
.x_constrained
) {
3992 op_string
= _("fixed time region drag");
3994 op_string
= _("region drag");
3998 begin_reversible_command (op_string
);
3999 changed_position
= (drag_info
.last_frame_position
!= (nframes64_t
) (clicked_regionview
->region()->position()));
4000 changed_tracks
= (trackview_by_y_position (drag_info
.current_pointer_y
) != &clicked_regionview
->get_time_axis_view());
4002 drag_delta
= clicked_regionview
->region()->position() - drag_info
.last_frame_position
;
4004 track_canvas
->update_now ();
4006 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin(); i
!= selection
->regions
.by_layer().end(); ) {
4008 RegionView
* rv
= (*i
);
4009 double ix1
, ix2
, iy1
, iy2
;
4010 rv
->get_canvas_frame()->get_bounds (ix1
, iy1
, ix2
, iy2
);
4011 rv
->get_canvas_group()->i2w (ix1
, iy1
);
4012 iy1
+= vertical_adjustment
.get_value() - canvas_timebars_vsize
;
4014 TimeAxisView
* dest_tv
= trackview_by_y_position (iy1
);
4015 AudioTimeAxisView
* dest_atv
= dynamic_cast<AudioTimeAxisView
*>(dest_tv
);
4018 if (rv
->region()->locked()) {
4023 if (changed_position
&& !drag_info
.x_constrained
) {
4024 where
= rv
->region()->position() - drag_delta
;
4026 where
= rv
->region()->position();
4029 boost::shared_ptr
<Region
> new_region
;
4031 if (drag_info
.copy
) {
4032 /* we already made a copy */
4033 new_region
= rv
->region();
4035 /* undo the previous hide_dependent_views so that xfades don't
4036 disappear on copying regions
4039 //rv->get_time_axis_view().reveal_dependent_views (*rv);
4041 } else if (changed_tracks
) {
4042 new_region
= RegionFactory::create (rv
->region());
4045 if (changed_tracks
|| drag_info
.copy
) {
4047 boost::shared_ptr
<Playlist
> to_playlist
= dest_atv
->playlist();
4049 latest_regionviews
.clear ();
4051 sigc::connection c
= dest_atv
->view()->RegionViewAdded
.connect (mem_fun(*this, &Editor::collect_new_region_view
));
4053 insert_result
= modified_playlists
.insert (to_playlist
);
4054 if (insert_result
.second
) {
4055 session
->add_command (new MementoCommand
<Playlist
>(*to_playlist
, &to_playlist
->get_state(), 0));
4058 to_playlist
->add_region (new_region
, where
);
4062 if (!latest_regionviews
.empty()) {
4063 // XXX why just the first one ? we only expect one
4064 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
4065 new_selection
.push_back (latest_regionviews
.front());
4070 motion on the same track. plonk the previously reparented region
4071 back to its original canvas group (its streamview).
4072 No need to do anything for copies as they are fake regions which will be deleted.
4075 RouteTimeAxisView
* dest_rtv
= dynamic_cast<RouteTimeAxisView
*> (dest_atv
);
4076 rv
->get_canvas_group()->reparent (*dest_rtv
->view()->canvas_item());
4077 rv
->get_canvas_group()->property_y() = 0;
4079 /* just change the model */
4081 boost::shared_ptr
<Playlist
> playlist
= dest_atv
->playlist();
4083 insert_result
= modified_playlists
.insert (playlist
);
4084 if (insert_result
.second
) {
4085 session
->add_command (new MementoCommand
<Playlist
>(*playlist
, &playlist
->get_state(), 0));
4087 /* freeze to avoid lots of relayering in the case of a multi-region drag */
4088 frozen_insert_result
= frozen_playlists
.insert(playlist
);
4089 if (frozen_insert_result
.second
) {
4093 rv
->region()->set_position (where
, (void*) this);
4096 if (changed_tracks
&& !drag_info
.copy
) {
4098 /* get the playlist where this drag started. we can't use rv->region()->playlist()
4099 because we may have copied the region and it has not been attached to a playlist.
4102 assert ((source_tv
= dynamic_cast<RouteTimeAxisView
*> (&rv
->get_time_axis_view())));
4103 assert ((ds
= source_tv
->get_diskstream()));
4104 assert ((from_playlist
= ds
->playlist()));
4106 /* moved to a different audio track, without copying */
4108 /* the region that used to be in the old playlist is not
4109 moved to the new one - we use a copy of it. as a result,
4110 any existing editor for the region should no longer be
4114 rv
->hide_region_editor();
4115 rv
->fake_set_opaque (false);
4117 /* remove the region from the old playlist */
4119 insert_result
= modified_playlists
.insert (from_playlist
);
4120 if (insert_result
.second
) {
4121 session
->add_command (new MementoCommand
<Playlist
>(*from_playlist
, &from_playlist
->get_state(), 0));
4124 from_playlist
->remove_region ((rv
->region()));
4126 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
4127 was selected in all of them, then removing it from a playlist will have removed all
4128 trace of it from the selection (i.e. there were N regions selected, we removed 1,
4129 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
4130 corresponding regionview, and the selection is now empty).
4132 this could have invalidated any and all iterators into the region selection.
4134 the heuristic we use here is: if the region selection is empty, break out of the loop
4135 here. if the region selection is not empty, then restart the loop because we know that
4136 we must have removed at least the region(view) we've just been working on as well as any
4137 that we processed on previous iterations.
4139 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
4140 we can just iterate.
4143 if (selection
->regions
.empty()) {
4146 i
= selection
->regions
.by_layer().begin();
4153 if (drag_info
.copy
) {
4154 copies
.push_back (rv
);
4158 if (new_selection
.empty()) {
4159 if (drag_info
.copy
) {
4160 /* the region(view)s that are selected and being dragged around
4161 are copies and do not belong to any track. remove them
4162 from the selection right here.
4164 selection
->clear_regions();
4167 /* this will clear any existing selection that would have been
4168 cleared in the other clause above
4170 selection
->set (new_selection
);
4173 for (set
<boost::shared_ptr
<Playlist
> >::iterator p
= frozen_playlists
.begin(); p
!= frozen_playlists
.end(); ++p
) {
4179 for (set
<boost::shared_ptr
<Playlist
> >::iterator p
= modified_playlists
.begin(); p
!= modified_playlists
.end(); ++p
) {
4180 session
->add_command (new MementoCommand
<Playlist
>(*(*p
), 0, &(*p
)->get_state()));
4182 commit_reversible_command ();
4185 for (vector
<RegionView
*>::iterator x
= copies
.begin(); x
!= copies
.end(); ++x
) {
4192 Editor::region_view_item_click (AudioRegionView
& rv
, GdkEventButton
* event
)
4194 /* Either add to or set the set the region selection, unless
4195 this is an alignment click (control used)
4198 if (Keyboard::modifier_state_contains (event
->state
, Keyboard::PrimaryModifier
)) {
4199 TimeAxisView
* tv
= &rv
.get_time_axis_view();
4200 AudioTimeAxisView
* atv
= dynamic_cast<AudioTimeAxisView
*>(tv
);
4202 if (atv
&& atv
->is_audio_track()) {
4203 speed
= atv
->get_diskstream()->speed();
4206 nframes64_t where
= get_preferred_edit_position();
4210 if (Keyboard::modifier_state_equals (event
->state
, Keyboard::ModifierMask (Keyboard::PrimaryModifier
|Keyboard::SecondaryModifier
))) {
4212 align_region (rv
.region(), SyncPoint
, (nframes64_t
) (where
* speed
));
4214 } else if (Keyboard::modifier_state_equals (event
->state
, Keyboard::ModifierMask (Keyboard::PrimaryModifier
|Keyboard::TertiaryModifier
))) {
4216 align_region (rv
.region(), End
, (nframes64_t
) (where
* speed
));
4220 align_region (rv
.region(), Start
, (nframes64_t
) (where
* speed
));
4227 Editor::show_verbose_time_cursor (nframes64_t frame
, double offset
, double xpos
, double ypos
)
4233 nframes64_t frame_rate
;
4242 if (Profile
->get_sae() || Profile
->get_small_screen()) {
4243 m
= ARDOUR_UI::instance()->primary_clock
.mode();
4245 m
= ARDOUR_UI::instance()->secondary_clock
.mode();
4249 case AudioClock::BBT
:
4250 session
->bbt_time (frame
, bbt
);
4251 snprintf (buf
, sizeof (buf
), "%02" PRIu32
"|%02" PRIu32
"|%02" PRIu32
, bbt
.bars
, bbt
.beats
, bbt
.ticks
);
4254 case AudioClock::SMPTE
:
4255 session
->smpte_time (frame
, smpte
);
4256 snprintf (buf
, sizeof (buf
), "%02" PRId32
":%02" PRId32
":%02" PRId32
":%02" PRId32
, smpte
.hours
, smpte
.minutes
, smpte
.seconds
, smpte
.frames
);
4259 case AudioClock::MinSec
:
4260 /* XXX this is copied from show_verbose_duration_cursor() */
4261 frame_rate
= session
->frame_rate();
4262 hours
= frame
/ (frame_rate
* 3600);
4263 frame
= frame
% (frame_rate
* 3600);
4264 mins
= frame
/ (frame_rate
* 60);
4265 frame
= frame
% (frame_rate
* 60);
4266 secs
= (float) frame
/ (float) frame_rate
;
4267 snprintf (buf
, sizeof (buf
), "%02" PRId32
":%02" PRId32
":%.4f", hours
, mins
, secs
);
4271 snprintf (buf
, sizeof(buf
), "%" PRIi64
, frame
);
4275 if (xpos
>= 0 && ypos
>=0) {
4276 set_verbose_canvas_cursor (buf
, xpos
+ offset
, ypos
+ offset
);
4279 set_verbose_canvas_cursor (buf
, drag_info
.current_pointer_x
+ offset
- horizontal_adjustment
.get_value(), drag_info
.current_pointer_y
+ offset
- vertical_adjustment
.get_value() + canvas_timebars_vsize
);
4281 show_verbose_canvas_cursor ();
4285 Editor::show_verbose_duration_cursor (nframes64_t start
, nframes64_t end
, double offset
, double xpos
, double ypos
)
4292 nframes64_t distance
, frame_rate
;
4294 Meter
meter_at_start(session
->tempo_map().meter_at(start
));
4302 if (Profile
->get_sae() || Profile
->get_small_screen()) {
4303 m
= ARDOUR_UI::instance()->primary_clock
.mode ();
4305 m
= ARDOUR_UI::instance()->secondary_clock
.mode ();
4309 case AudioClock::BBT
:
4310 session
->bbt_time (start
, sbbt
);
4311 session
->bbt_time (end
, ebbt
);
4314 /* XXX this computation won't work well if the
4315 user makes a selection that spans any meter changes.
4318 ebbt
.bars
-= sbbt
.bars
;
4319 if (ebbt
.beats
>= sbbt
.beats
) {
4320 ebbt
.beats
-= sbbt
.beats
;
4323 ebbt
.beats
= int(meter_at_start
.beats_per_bar()) + ebbt
.beats
- sbbt
.beats
;
4325 if (ebbt
.ticks
>= sbbt
.ticks
) {
4326 ebbt
.ticks
-= sbbt
.ticks
;
4329 ebbt
.ticks
= int(Meter::ticks_per_beat
) + ebbt
.ticks
- sbbt
.ticks
;
4332 snprintf (buf
, sizeof (buf
), "%02" PRIu32
"|%02" PRIu32
"|%02" PRIu32
, ebbt
.bars
, ebbt
.beats
, ebbt
.ticks
);
4335 case AudioClock::SMPTE
:
4336 session
->smpte_duration (end
- start
, smpte
);
4337 snprintf (buf
, sizeof (buf
), "%02" PRId32
":%02" PRId32
":%02" PRId32
":%02" PRId32
, smpte
.hours
, smpte
.minutes
, smpte
.seconds
, smpte
.frames
);
4340 case AudioClock::MinSec
:
4341 /* XXX this stuff should be elsewhere.. */
4342 distance
= end
- start
;
4343 frame_rate
= session
->frame_rate();
4344 hours
= distance
/ (frame_rate
* 3600);
4345 distance
= distance
% (frame_rate
* 3600);
4346 mins
= distance
/ (frame_rate
* 60);
4347 distance
= distance
% (frame_rate
* 60);
4348 secs
= (float) distance
/ (float) frame_rate
;
4349 snprintf (buf
, sizeof (buf
), "%02" PRId32
":%02" PRId32
":%.4f", hours
, mins
, secs
);
4353 snprintf (buf
, sizeof(buf
), "%" PRIi64
, end
- start
);
4357 if (xpos
>= 0 && ypos
>=0) {
4358 set_verbose_canvas_cursor (buf
, xpos
+ offset
, ypos
+ offset
);
4361 set_verbose_canvas_cursor (buf
, drag_info
.current_pointer_x
+ offset
, drag_info
.current_pointer_y
+ offset
);
4364 show_verbose_canvas_cursor ();
4368 Editor::collect_new_region_view (RegionView
* rv
)
4370 latest_regionviews
.push_back (rv
);
4374 Editor::start_selection_grab (ArdourCanvas::Item
* item
, GdkEvent
* event
)
4376 if (clicked_regionview
== 0) {
4380 /* lets try to create new Region for the selection */
4382 vector
<boost::shared_ptr
<AudioRegion
> > new_regions
;
4383 create_region_from_selection (new_regions
);
4385 if (new_regions
.empty()) {
4389 /* XXX fix me one day to use all new regions */
4391 boost::shared_ptr
<Region
> region (new_regions
.front());
4393 /* add it to the current stream/playlist.
4395 tricky: the streamview for the track will add a new regionview. we will
4396 catch the signal it sends when it creates the regionview to
4397 set the regionview we want to then drag.
4400 latest_regionviews
.clear();
4401 sigc::connection c
= clicked_audio_trackview
->view()->RegionViewAdded
.connect (mem_fun(*this, &Editor::collect_new_region_view
));
4403 /* A selection grab currently creates two undo/redo operations, one for
4404 creating the new region and another for moving it.
4407 begin_reversible_command (_("selection grab"));
4409 boost::shared_ptr
<Playlist
> playlist
= clicked_trackview
->playlist();
4411 XMLNode
*before
= &(playlist
->get_state());
4412 clicked_trackview
->playlist()->add_region (region
, selection
->time
[clicked_selection
].start
);
4413 XMLNode
*after
= &(playlist
->get_state());
4414 session
->add_command(new MementoCommand
<Playlist
>(*playlist
, before
, after
));
4416 commit_reversible_command ();
4420 if (latest_regionviews
.empty()) {
4421 /* something went wrong */
4425 /* we need to deselect all other regionviews, and select this one
4426 i'm ignoring undo stuff, because the region creation will take care of it
4428 selection
->set (latest_regionviews
);
4430 drag_info
.item
= latest_regionviews
.front()->get_canvas_group();
4431 drag_info
.data
= latest_regionviews
.front();
4432 drag_info
.motion_callback
= &Editor::region_drag_motion_callback
;
4433 drag_info
.finished_callback
= &Editor::region_drag_finished_callback
;
4437 drag_info
.source_trackview
= clicked_trackview
;
4438 drag_info
.dest_trackview
= drag_info
.source_trackview
;
4439 drag_info
.last_frame_position
= latest_regionviews
.front()->region()->position();
4440 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- drag_info
.last_frame_position
;
4442 show_verbose_time_cursor (drag_info
.last_frame_position
, 10);
4446 Editor::cancel_selection ()
4448 for (TrackViewList::iterator i
= track_views
.begin(); i
!= track_views
.end(); ++i
) {
4449 (*i
)->hide_selection ();
4451 selection
->clear ();
4452 clicked_selection
= 0;
4456 Editor::start_selection_op (ArdourCanvas::Item
* item
, GdkEvent
* event
, SelectionOp op
)
4458 nframes64_t start
= 0;
4459 nframes64_t end
= 0;
4465 drag_info
.item
= item
;
4466 drag_info
.motion_callback
= &Editor::drag_selection
;
4467 drag_info
.finished_callback
= &Editor::end_selection_op
;
4472 case CreateSelection
:
4473 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::TertiaryModifier
)) {
4474 drag_info
.copy
= true;
4476 drag_info
.copy
= false;
4478 start_grab (event
, selector_cursor
);
4481 case SelectionStartTrim
:
4482 if (clicked_trackview
) {
4483 clicked_trackview
->order_selection_trims (item
, true);
4485 start_grab (event
, trimmer_cursor
);
4486 start
= selection
->time
[clicked_selection
].start
;
4487 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- start
;
4490 case SelectionEndTrim
:
4491 if (clicked_trackview
) {
4492 clicked_trackview
->order_selection_trims (item
, false);
4494 start_grab (event
, trimmer_cursor
);
4495 end
= selection
->time
[clicked_selection
].end
;
4496 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- end
;
4500 start
= selection
->time
[clicked_selection
].start
;
4502 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- start
;
4506 if (selection_op
== SelectionMove
) {
4507 show_verbose_time_cursor(start
, 10);
4509 show_verbose_time_cursor(drag_info
.current_pointer_frame
, 10);
4514 Editor::drag_selection (ArdourCanvas::Item
* item
, GdkEvent
* event
)
4516 nframes64_t start
= 0;
4517 nframes64_t end
= 0;
4519 nframes64_t pending_position
;
4521 if (drag_info
.current_pointer_frame
> drag_info
.pointer_frame_offset
) {
4522 pending_position
= drag_info
.current_pointer_frame
- drag_info
.pointer_frame_offset
;
4524 pending_position
= 0;
4527 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
4528 snap_to (pending_position
);
4531 /* only alter selection if the current frame is
4532 different from the last frame position (adjusted)
4535 if (pending_position
== drag_info
.last_pointer_frame
) return;
4537 switch (selection_op
) {
4538 case CreateSelection
:
4540 if (drag_info
.first_move
) {
4541 snap_to (drag_info
.grab_frame
);
4544 if (pending_position
< drag_info
.grab_frame
) {
4545 start
= pending_position
;
4546 end
= drag_info
.grab_frame
;
4548 end
= pending_position
;
4549 start
= drag_info
.grab_frame
;
4552 /* first drag: Either add to the selection
4553 or create a new selection->
4556 if (drag_info
.first_move
) {
4558 begin_reversible_command (_("range selection"));
4560 if (drag_info
.copy
) {
4561 /* adding to the selection */
4562 clicked_selection
= selection
->add (start
, end
);
4563 drag_info
.copy
= false;
4565 /* new selection-> */
4566 clicked_selection
= selection
->set (clicked_trackview
, start
, end
);
4571 case SelectionStartTrim
:
4573 if (drag_info
.first_move
) {
4574 begin_reversible_command (_("trim selection start"));
4577 start
= selection
->time
[clicked_selection
].start
;
4578 end
= selection
->time
[clicked_selection
].end
;
4580 if (pending_position
> end
) {
4583 start
= pending_position
;
4587 case SelectionEndTrim
:
4589 if (drag_info
.first_move
) {
4590 begin_reversible_command (_("trim selection end"));
4593 start
= selection
->time
[clicked_selection
].start
;
4594 end
= selection
->time
[clicked_selection
].end
;
4596 if (pending_position
< start
) {
4599 end
= pending_position
;
4606 if (drag_info
.first_move
) {
4607 begin_reversible_command (_("move selection"));
4610 start
= selection
->time
[clicked_selection
].start
;
4611 end
= selection
->time
[clicked_selection
].end
;
4613 length
= end
- start
;
4615 start
= pending_position
;
4618 end
= start
+ length
;
4623 if (event
->button
.x
>= horizontal_adjustment
.get_value() + canvas_width
) {
4624 start_canvas_autoscroll (1, 0);
4628 selection
->replace (clicked_selection
, start
, end
);
4631 drag_info
.last_pointer_frame
= pending_position
;
4632 drag_info
.first_move
= false;
4634 if (selection_op
== SelectionMove
) {
4635 show_verbose_time_cursor(start
, 10);
4637 show_verbose_time_cursor(pending_position
, 10);
4642 Editor::end_selection_op (ArdourCanvas::Item
* item
, GdkEvent
* event
)
4644 if (!drag_info
.first_move
) {
4645 drag_selection (item
, event
);
4646 /* XXX this is not object-oriented programming at all. ick */
4647 if (selection
->time
.consolidate()) {
4648 selection
->TimeChanged ();
4650 commit_reversible_command ();
4653 /* XXX what if its a music time selection? */
4654 if (Config
->get_auto_play() || (session
->get_play_range() && session
->transport_rolling())) {
4655 session
->request_play_range (&selection
->time
, true);
4659 /* just a click, no pointer movement.*/
4661 if (Keyboard::no_modifier_keys_pressed (&event
->button
)) {
4663 selection
->clear_time();
4667 if (session
->get_play_range () && session
->transport_rolling()) {
4668 session
->request_stop (false, false);
4672 stop_canvas_autoscroll ();
4676 Editor::start_trim (ArdourCanvas::Item
* item
, GdkEvent
* event
)
4679 TimeAxisView
* tvp
= clicked_trackview
;
4680 AudioTimeAxisView
* tv
= dynamic_cast<AudioTimeAxisView
*>(tvp
);
4682 if (tv
&& tv
->is_audio_track()) {
4683 speed
= tv
->get_diskstream()->speed();
4686 nframes64_t region_start
= (nframes64_t
) (clicked_regionview
->region()->position() / speed
);
4687 nframes64_t region_end
= (nframes64_t
) (clicked_regionview
->region()->last_frame() / speed
);
4688 nframes64_t region_length
= (nframes64_t
) (clicked_regionview
->region()->length() / speed
);
4690 //drag_info.item = clicked_regionview->get_name_highlight();
4691 drag_info
.item
= item
;
4692 drag_info
.motion_callback
= &Editor::trim_motion_callback
;
4693 drag_info
.finished_callback
= &Editor::trim_finished_callback
;
4695 start_grab (event
, trimmer_cursor
);
4697 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
4698 trim_op
= ContentsTrim
;
4700 /* These will get overridden for a point trim.*/
4701 if (drag_info
.current_pointer_frame
< (region_start
+ region_length
/2)) {
4702 /* closer to start */
4703 trim_op
= StartTrim
;
4704 } else if (drag_info
.current_pointer_frame
> (region_end
- region_length
/2)) {
4712 show_verbose_time_cursor(region_start
, 10);
4715 show_verbose_time_cursor(region_end
, 10);
4718 show_verbose_time_cursor(drag_info
.current_pointer_frame
, 10);
4724 Editor::trim_motion_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
4726 RegionView
* rv
= clicked_regionview
;
4727 nframes64_t frame_delta
= 0;
4728 bool left_direction
;
4729 bool obey_snap
= !Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier());
4731 /* snap modifier works differently here..
4732 its' current state has to be passed to the
4733 various trim functions in order to work properly
4737 TimeAxisView
* tvp
= clicked_trackview
;
4738 RouteTimeAxisView
* tv
= dynamic_cast<RouteTimeAxisView
*>(tvp
);
4739 pair
<set
<boost::shared_ptr
<Playlist
> >::iterator
,bool> insert_result
;
4741 if (tv
&& tv
->is_audio_track()) {
4742 speed
= tv
->get_diskstream()->speed();
4745 if (drag_info
.last_pointer_frame
> drag_info
.current_pointer_frame
) {
4746 left_direction
= true;
4748 left_direction
= false;
4752 snap_to (drag_info
.current_pointer_frame
);
4755 if (drag_info
.current_pointer_frame
== drag_info
.last_pointer_frame
) {
4759 if (drag_info
.first_move
) {
4765 trim_type
= "Region start trim";
4768 trim_type
= "Region end trim";
4771 trim_type
= "Region content trim";
4775 begin_reversible_command (trim_type
);
4777 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin(); i
!= selection
->regions
.by_layer().end(); ++i
) {
4778 (*i
)->fake_set_opaque(false);
4779 (*i
)->region()->freeze ();
4781 AudioRegionView
* const arv
= dynamic_cast<AudioRegionView
*>(*i
);
4783 arv
->temporarily_hide_envelope ();
4785 boost::shared_ptr
<Playlist
> pl
= (*i
)->region()->playlist();
4786 insert_result
= motion_frozen_playlists
.insert (pl
);
4787 if (insert_result
.second
) {
4788 session
->add_command(new MementoCommand
<Playlist
>(*pl
, &pl
->get_state(), 0));
4793 if (left_direction
) {
4794 frame_delta
= (drag_info
.last_pointer_frame
- drag_info
.current_pointer_frame
);
4796 frame_delta
= (drag_info
.current_pointer_frame
- drag_info
.last_pointer_frame
);
4801 if ((left_direction
== false) && (drag_info
.current_pointer_frame
<= rv
->region()->first_frame()/speed
)) {
4804 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin(); i
!= selection
->regions
.by_layer().end(); ++i
) {
4805 single_start_trim (**i
, frame_delta
, left_direction
, obey_snap
);
4811 if ((left_direction
== true) && (drag_info
.current_pointer_frame
> (nframes64_t
) (rv
->region()->last_frame()/speed
))) {
4814 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin(); i
!= selection
->regions
.by_layer().end(); ++i
) {
4815 single_end_trim (**i
, frame_delta
, left_direction
, obey_snap
);
4822 bool swap_direction
= false;
4824 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
4825 swap_direction
= true;
4828 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin();
4829 i
!= selection
->regions
.by_layer().end(); ++i
)
4831 single_contents_trim (**i
, frame_delta
, left_direction
, swap_direction
, obey_snap
);
4839 show_verbose_time_cursor((nframes64_t
) (rv
->region()->position()/speed
), 10);
4842 show_verbose_time_cursor((nframes64_t
) (rv
->region()->last_frame()/speed
), 10);
4845 show_verbose_time_cursor(drag_info
.current_pointer_frame
, 10);
4849 drag_info
.last_pointer_frame
= drag_info
.current_pointer_frame
;
4850 drag_info
.first_move
= false;
4854 Editor::single_contents_trim (RegionView
& rv
, nframes64_t frame_delta
, bool left_direction
, bool swap_direction
, bool obey_snap
)
4856 boost::shared_ptr
<Region
> region (rv
.region());
4858 if (region
->locked()) {
4862 nframes64_t new_bound
;
4865 TimeAxisView
* tvp
= clicked_trackview
;
4866 RouteTimeAxisView
* tv
= dynamic_cast<RouteTimeAxisView
*>(tvp
);
4868 if (tv
&& tv
->is_audio_track()) {
4869 speed
= tv
->get_diskstream()->speed();
4872 if (left_direction
) {
4873 if (swap_direction
) {
4874 new_bound
= (nframes64_t
) (region
->position()/speed
) + frame_delta
;
4876 new_bound
= (nframes64_t
) (region
->position()/speed
) - frame_delta
;
4879 if (swap_direction
) {
4880 new_bound
= (nframes64_t
) (region
->position()/speed
) - frame_delta
;
4882 new_bound
= (nframes64_t
) (region
->position()/speed
) + frame_delta
;
4887 snap_to (new_bound
);
4889 region
->trim_start ((nframes64_t
) (new_bound
* speed
), this);
4890 rv
.region_changed (StartChanged
);
4894 Editor::single_start_trim (RegionView
& rv
, nframes64_t frame_delta
, bool left_direction
, bool obey_snap
)
4896 boost::shared_ptr
<Region
> region (rv
.region());
4898 if (region
->locked()) {
4902 nframes64_t new_bound
;
4905 TimeAxisView
* tvp
= clicked_trackview
;
4906 AudioTimeAxisView
* tv
= dynamic_cast<AudioTimeAxisView
*>(tvp
);
4908 if (tv
&& tv
->is_audio_track()) {
4909 speed
= tv
->get_diskstream()->speed();
4912 if (left_direction
) {
4913 new_bound
= (nframes64_t
) (region
->position()/speed
) - frame_delta
;
4915 new_bound
= (nframes64_t
) (region
->position()/speed
) + frame_delta
;
4919 snap_to (new_bound
, (left_direction
? 0 : 1));
4922 region
->trim_front ((nframes64_t
) (new_bound
* speed
), this);
4924 rv
.region_changed (Change (LengthChanged
|PositionChanged
|StartChanged
));
4928 Editor::single_end_trim (RegionView
& rv
, nframes64_t frame_delta
, bool left_direction
, bool obey_snap
)
4930 boost::shared_ptr
<Region
> region (rv
.region());
4932 if (region
->locked()) {
4936 nframes64_t new_bound
;
4939 TimeAxisView
* tvp
= clicked_trackview
;
4940 AudioTimeAxisView
* tv
= dynamic_cast<AudioTimeAxisView
*>(tvp
);
4942 if (tv
&& tv
->is_audio_track()) {
4943 speed
= tv
->get_diskstream()->speed();
4946 if (left_direction
) {
4947 new_bound
= (nframes64_t
) ((region
->last_frame() + 1)/speed
) - frame_delta
;
4949 new_bound
= (nframes64_t
) ((region
->last_frame() + 1)/speed
) + frame_delta
;
4953 snap_to (new_bound
);
4955 region
->trim_end ((nframes64_t
) (new_bound
* speed
), this);
4956 rv
.region_changed (LengthChanged
);
4960 Editor::trim_finished_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
4962 if (!drag_info
.first_move
) {
4963 trim_motion_callback (item
, event
);
4965 if (!selection
->selected (clicked_regionview
)) {
4966 thaw_region_after_trim (*clicked_regionview
);
4969 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin();
4970 i
!= selection
->regions
.by_layer().end(); ++i
)
4972 thaw_region_after_trim (**i
);
4973 (*i
)->fake_set_opaque (true);
4977 for (set
<boost::shared_ptr
<Playlist
> >::iterator p
= motion_frozen_playlists
.begin(); p
!= motion_frozen_playlists
.end(); ++p
) {
4979 session
->add_command (new MementoCommand
<Playlist
>(*(*p
).get(), 0, &(*p
)->get_state()));
4982 motion_frozen_playlists
.clear ();
4984 commit_reversible_command();
4986 /* no mouse movement */
4992 Editor::point_trim (GdkEvent
* event
)
4994 RegionView
* rv
= clicked_regionview
;
4995 nframes64_t new_bound
= drag_info
.current_pointer_frame
;
4997 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
4998 snap_to (new_bound
);
5001 /* Choose action dependant on which button was pressed */
5002 switch (event
->button
.button
) {
5004 trim_op
= StartTrim
;
5005 begin_reversible_command (_("Start point trim"));
5007 if (selection
->selected (rv
)) {
5009 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin();
5010 i
!= selection
->regions
.by_layer().end(); ++i
)
5012 if (!(*i
)->region()->locked()) {
5013 boost::shared_ptr
<Playlist
> pl
= (*i
)->region()->playlist();
5014 XMLNode
&before
= pl
->get_state();
5015 (*i
)->region()->trim_front (new_bound
, this);
5016 XMLNode
&after
= pl
->get_state();
5017 session
->add_command(new MementoCommand
<Playlist
>(*pl
.get(), &before
, &after
));
5023 if (!rv
->region()->locked()) {
5024 boost::shared_ptr
<Playlist
> pl
= rv
->region()->playlist();
5025 XMLNode
&before
= pl
->get_state();
5026 rv
->region()->trim_front (new_bound
, this);
5027 XMLNode
&after
= pl
->get_state();
5028 session
->add_command(new MementoCommand
<Playlist
>(*pl
.get(), &before
, &after
));
5032 commit_reversible_command();
5037 begin_reversible_command (_("End point trim"));
5039 if (selection
->selected (rv
)) {
5041 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin(); i
!= selection
->regions
.by_layer().end(); ++i
)
5043 if (!(*i
)->region()->locked()) {
5044 boost::shared_ptr
<Playlist
> pl
= (*i
)->region()->playlist();
5045 XMLNode
&before
= pl
->get_state();
5046 (*i
)->region()->trim_end (new_bound
, this);
5047 XMLNode
&after
= pl
->get_state();
5048 session
->add_command(new MementoCommand
<Playlist
>(*pl
.get(), &before
, &after
));
5054 if (!rv
->region()->locked()) {
5055 boost::shared_ptr
<Playlist
> pl
= rv
->region()->playlist();
5056 XMLNode
&before
= pl
->get_state();
5057 rv
->region()->trim_end (new_bound
, this);
5058 XMLNode
&after
= pl
->get_state();
5059 session
->add_command (new MementoCommand
<Playlist
>(*pl
.get(), &before
, &after
));
5063 commit_reversible_command();
5072 Editor::thaw_region_after_trim (RegionView
& rv
)
5074 boost::shared_ptr
<Region
> region (rv
.region());
5076 if (region
->locked()) {
5080 region
->thaw (_("trimmed region"));
5081 XMLNode
&after
= region
->playlist()->get_state();
5082 session
->add_command (new MementoCommand
<Playlist
>(*(region
->playlist()), 0, &after
));
5084 AudioRegionView
* arv
= dynamic_cast<AudioRegionView
*>(&rv
);
5086 arv
->unhide_envelope ();
5090 Editor::hide_marker (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5095 if ((marker
= static_cast<Marker
*> (item
->get_data ("marker"))) == 0) {
5096 fatal
<< _("programming error: marker canvas item has no marker object pointer!") << endmsg
;
5100 Location
* location
= find_location_from_marker (marker
, is_start
);
5101 location
->set_hidden (true, this);
5106 Editor::start_range_markerbar_op (ArdourCanvas::Item
* item
, GdkEvent
* event
, RangeMarkerOp op
)
5112 drag_info
.item
= item
;
5113 drag_info
.motion_callback
= &Editor::drag_range_markerbar_op
;
5114 drag_info
.finished_callback
= &Editor::end_range_markerbar_op
;
5116 range_marker_op
= op
;
5118 if (!temp_location
) {
5119 temp_location
= new Location
;
5123 case CreateRangeMarker
:
5124 case CreateTransportMarker
:
5125 case CreateCDMarker
:
5127 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::TertiaryModifier
)) {
5128 drag_info
.copy
= true;
5130 drag_info
.copy
= false;
5132 start_grab (event
, selector_cursor
);
5136 show_verbose_time_cursor(drag_info
.current_pointer_frame
, 10);
5141 Editor::drag_range_markerbar_op (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5143 nframes64_t start
= 0;
5144 nframes64_t end
= 0;
5145 ArdourCanvas::SimpleRect
*crect
;
5147 switch (range_marker_op
) {
5148 case CreateRangeMarker
:
5149 crect
= range_bar_drag_rect
;
5151 case CreateTransportMarker
:
5152 crect
= transport_bar_drag_rect
;
5154 case CreateCDMarker
:
5155 crect
= cd_marker_bar_drag_rect
;
5158 cerr
<< "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl
;
5163 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
5164 snap_to (drag_info
.current_pointer_frame
);
5167 /* only alter selection if the current frame is
5168 different from the last frame position.
5171 if (drag_info
.current_pointer_frame
== drag_info
.last_pointer_frame
) return;
5173 switch (range_marker_op
) {
5174 case CreateRangeMarker
:
5175 case CreateTransportMarker
:
5176 case CreateCDMarker
:
5177 if (drag_info
.first_move
) {
5178 snap_to (drag_info
.grab_frame
);
5181 if (drag_info
.current_pointer_frame
< drag_info
.grab_frame
) {
5182 start
= drag_info
.current_pointer_frame
;
5183 end
= drag_info
.grab_frame
;
5185 end
= drag_info
.current_pointer_frame
;
5186 start
= drag_info
.grab_frame
;
5189 /* first drag: Either add to the selection
5190 or create a new selection.
5193 if (drag_info
.first_move
) {
5195 temp_location
->set (start
, end
);
5199 update_marker_drag_item (temp_location
);
5200 range_marker_drag_rect
->show();
5201 //range_marker_drag_rect->raise_to_top();
5207 if (event
->button
.x
>= horizontal_adjustment
.get_value() + canvas_width
) {
5208 start_canvas_autoscroll (1, 0);
5212 temp_location
->set (start
, end
);
5214 double x1
= frame_to_pixel (start
);
5215 double x2
= frame_to_pixel (end
);
5216 crect
->property_x1() = x1
;
5217 crect
->property_x2() = x2
;
5219 update_marker_drag_item (temp_location
);
5222 drag_info
.last_pointer_frame
= drag_info
.current_pointer_frame
;
5223 drag_info
.first_move
= false;
5225 show_verbose_time_cursor(drag_info
.current_pointer_frame
, 10);
5230 Editor::end_range_markerbar_op (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5232 Location
* newloc
= 0;
5236 if (!drag_info
.first_move
) {
5237 drag_range_markerbar_op (item
, event
);
5239 switch (range_marker_op
) {
5240 case CreateRangeMarker
:
5241 case CreateCDMarker
:
5243 begin_reversible_command (_("new range marker"));
5244 XMLNode
&before
= session
->locations()->get_state();
5245 session
->locations()->next_available_name(rangename
,"unnamed");
5246 if (range_marker_op
== CreateCDMarker
) {
5247 flags
= Location::IsRangeMarker
|Location::IsCDMarker
;
5248 cd_marker_bar_drag_rect
->hide();
5251 flags
= Location::IsRangeMarker
;
5252 range_bar_drag_rect
->hide();
5254 newloc
= new Location(temp_location
->start(), temp_location
->end(), rangename
, (Location::Flags
) flags
);
5255 session
->locations()->add (newloc
, true);
5256 XMLNode
&after
= session
->locations()->get_state();
5257 session
->add_command(new MementoCommand
<Locations
>(*(session
->locations()), &before
, &after
));
5258 commit_reversible_command ();
5260 range_marker_drag_rect
->hide();
5264 case CreateTransportMarker
:
5265 // popup menu to pick loop or punch
5266 new_transport_marker_context_menu (&event
->button
, item
);
5271 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5273 if (Keyboard::no_modifier_keys_pressed (&event
->button
) && range_marker_op
!= CreateCDMarker
) {
5278 start
= session
->locations()->first_mark_before (drag_info
.grab_frame
);
5279 end
= session
->locations()->first_mark_after (drag_info
.grab_frame
);
5281 if (end
== max_frames
) {
5282 end
= session
->current_end_frame ();
5286 start
= session
->current_start_frame ();
5289 switch (mouse_mode
) {
5291 /* find the two markers on either side and then make the selection from it */
5292 select_all_within (start
, end
, 0.0f
, FLT_MAX
, track_views
, Selection::Set
);
5296 /* find the two markers on either side of the click and make the range out of it */
5297 selection
->set (0, start
, end
);
5306 stop_canvas_autoscroll ();
5312 Editor::start_mouse_zoom (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5314 drag_info
.item
= item
;
5315 drag_info
.motion_callback
= &Editor::drag_mouse_zoom
;
5316 drag_info
.finished_callback
= &Editor::end_mouse_zoom
;
5318 start_grab (event
, zoom_cursor
);
5320 show_verbose_time_cursor (drag_info
.current_pointer_frame
, 10);
5324 Editor::drag_mouse_zoom (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5329 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
5330 snap_to (drag_info
.current_pointer_frame
);
5332 if (drag_info
.first_move
) {
5333 snap_to (drag_info
.grab_frame
);
5337 if (drag_info
.current_pointer_frame
== drag_info
.last_pointer_frame
) return;
5339 /* base start and end on initial click position */
5340 if (drag_info
.current_pointer_frame
< drag_info
.grab_frame
) {
5341 start
= drag_info
.current_pointer_frame
;
5342 end
= drag_info
.grab_frame
;
5344 end
= drag_info
.current_pointer_frame
;
5345 start
= drag_info
.grab_frame
;
5350 if (drag_info
.first_move
) {
5352 zoom_rect
->raise_to_top();
5355 reposition_zoom_rect(start
, end
);
5357 drag_info
.last_pointer_frame
= drag_info
.current_pointer_frame
;
5358 drag_info
.first_move
= false;
5360 show_verbose_time_cursor (drag_info
.current_pointer_frame
, 10);
5365 Editor::end_mouse_zoom (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5367 if (!drag_info
.first_move
) {
5368 drag_mouse_zoom (item
, event
);
5370 if (drag_info
.grab_frame
< drag_info
.last_pointer_frame
) {
5371 temporal_zoom_by_frame (drag_info
.grab_frame
, drag_info
.last_pointer_frame
, "mouse zoom");
5373 temporal_zoom_by_frame (drag_info
.last_pointer_frame
, drag_info
.grab_frame
, "mouse zoom");
5376 temporal_zoom_to_frame (false, drag_info
.grab_frame
);
5378 temporal_zoom_step (false);
5379 center_screen (drag_info.grab_frame);
5387 Editor::reposition_zoom_rect (nframes64_t start
, nframes64_t end
)
5389 double x1
= frame_to_pixel (start
);
5390 double x2
= frame_to_pixel (end
);
5391 double y2
= full_canvas_height
- 1.0;
5393 zoom_rect
->property_x1() = x1
;
5394 zoom_rect
->property_y1() = 1.0;
5395 zoom_rect
->property_x2() = x2
;
5396 zoom_rect
->property_y2() = y2
;
5400 Editor::start_rubberband_select (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5402 drag_info
.item
= item
;
5403 drag_info
.motion_callback
= &Editor::drag_rubberband_select
;
5404 drag_info
.finished_callback
= &Editor::end_rubberband_select
;
5406 start_grab (event
, cross_hair_cursor
);
5408 show_verbose_time_cursor (drag_info
.current_pointer_frame
, 10);
5412 Editor::drag_rubberband_select (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5419 /* use a bigger drag threshold than the default */
5421 if (abs ((int) (drag_info
.current_pointer_frame
- drag_info
.grab_frame
)) < 8) {
5425 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier()) && Config
->get_rubberbanding_snaps_to_grid()) {
5426 if (drag_info
.first_move
) {
5427 snap_to (drag_info
.grab_frame
);
5429 snap_to (drag_info
.current_pointer_frame
);
5432 /* base start and end on initial click position */
5434 if (drag_info
.current_pointer_frame
< drag_info
.grab_frame
) {
5435 start
= drag_info
.current_pointer_frame
;
5436 end
= drag_info
.grab_frame
;
5438 end
= drag_info
.current_pointer_frame
;
5439 start
= drag_info
.grab_frame
;
5442 if (drag_info
.current_pointer_y
< drag_info
.grab_y
) {
5443 y1
= drag_info
.current_pointer_y
;
5444 y2
= drag_info
.grab_y
;
5446 y2
= drag_info
.current_pointer_y
;
5447 y1
= drag_info
.grab_y
;
5451 if (start
!= end
|| y1
!= y2
) {
5453 double x1
= frame_to_pixel (start
);
5454 double x2
= frame_to_pixel (end
);
5456 rubberband_rect
->property_x1() = x1
;
5457 rubberband_rect
->property_y1() = y1
;
5458 rubberband_rect
->property_x2() = x2
;
5459 rubberband_rect
->property_y2() = y2
;
5461 rubberband_rect
->show();
5462 rubberband_rect
->raise_to_top();
5464 drag_info
.last_pointer_frame
= drag_info
.current_pointer_frame
;
5465 drag_info
.first_move
= false;
5467 show_verbose_time_cursor (drag_info
.current_pointer_frame
, 10);
5472 Editor::end_rubberband_select (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5474 if (!drag_info
.first_move
) {
5476 drag_rubberband_select (item
, event
);
5479 if (drag_info
.current_pointer_y
< drag_info
.grab_y
) {
5480 y1
= drag_info
.current_pointer_y
;
5481 y2
= drag_info
.grab_y
;
5483 y2
= drag_info
.current_pointer_y
;
5484 y1
= drag_info
.grab_y
;
5488 Selection::Operation op
= Keyboard::selection_type (event
->button
.state
);
5491 begin_reversible_command (_("rubberband selection"));
5493 if (drag_info
.grab_frame
< drag_info
.last_pointer_frame
) {
5494 commit
= select_all_within (drag_info
.grab_frame
, drag_info
.last_pointer_frame
, y1
, y2
, track_views
, op
);
5496 commit
= select_all_within (drag_info
.last_pointer_frame
, drag_info
.grab_frame
, y1
, y2
, track_views
, op
);
5500 commit_reversible_command ();
5504 if (!getenv("ARDOUR_SAE")) {
5505 selection
->clear_tracks();
5507 selection
->clear_regions();
5508 selection
->clear_points ();
5509 selection
->clear_lines ();
5512 rubberband_rect
->hide();
5517 Editor::mouse_rename_region (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5519 using namespace Gtkmm2ext
;
5521 ArdourPrompter
prompter (false);
5523 prompter
.set_prompt (_("Name for region:"));
5524 prompter
.set_initial_text (clicked_regionview
->region()->name());
5525 prompter
.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT
);
5526 prompter
.set_response_sensitive (Gtk::RESPONSE_ACCEPT
, false);
5527 prompter
.show_all ();
5528 switch (prompter
.run ()) {
5529 case Gtk::RESPONSE_ACCEPT
:
5531 prompter
.get_result(str
);
5533 clicked_regionview
->region()->set_name (str
);
5541 Editor::start_time_fx (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5543 drag_info
.item
= item
;
5544 drag_info
.motion_callback
= &Editor::time_fx_motion
;
5545 drag_info
.finished_callback
= &Editor::end_time_fx
;
5549 show_verbose_time_cursor (drag_info
.current_pointer_frame
, 10);
5553 Editor::time_fx_motion (ArdourCanvas::Item
*item
, GdkEvent
* event
)
5555 RegionView
* rv
= clicked_regionview
;
5557 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
5558 snap_to (drag_info
.current_pointer_frame
);
5561 if (drag_info
.current_pointer_frame
== drag_info
.last_pointer_frame
) {
5565 if (drag_info
.current_pointer_frame
> rv
->region()->position()) {
5566 rv
->get_time_axis_view().show_timestretch (rv
->region()->position(), drag_info
.current_pointer_frame
);
5569 drag_info
.last_pointer_frame
= drag_info
.current_pointer_frame
;
5570 drag_info
.first_move
= false;
5572 show_verbose_time_cursor (drag_info
.current_pointer_frame
, 10);
5576 Editor::end_time_fx (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5578 clicked_regionview
->get_time_axis_view().hide_timestretch ();
5580 if (drag_info
.first_move
) {
5584 if (drag_info
.last_pointer_frame
< clicked_regionview
->region()->position()) {
5585 /* backwards drag of the left edge - not usable */
5589 nframes64_t newlen
= drag_info
.last_pointer_frame
- clicked_regionview
->region()->position();
5590 #ifdef USE_RUBBERBAND
5591 float percentage
= (float) ((double) newlen
/ (double) clicked_regionview
->region()->length());
5593 float percentage
= (float) ((double) newlen
- (double) clicked_regionview
->region()->length()) / ((double) newlen
) * 100.0f
;
5596 begin_reversible_command (_("timestretch"));
5598 // XXX how do timeFX on multiple regions ?
5601 rs
.add (clicked_regionview
);
5603 if (time_stretch (rs
, percentage
) == 0) {
5604 session
->commit_reversible_command ();
5609 Editor::mouse_brush_insert_region (RegionView
* rv
, nframes64_t pos
)
5611 /* no brushing without a useful snap setting */
5614 AudioRegionView
* arv
= dynamic_cast<AudioRegionView
*>(rv
);
5617 switch (snap_mode
) {
5619 return; /* can't work because it allows region to be placed anywhere */
5624 switch (snap_type
) {
5632 /* don't brush a copy over the original */
5634 if (pos
== rv
->region()->position()) {
5638 RouteTimeAxisView
* atv
= dynamic_cast<RouteTimeAxisView
*>(&arv
->get_time_axis_view());
5640 if (atv
== 0 || !atv
->is_audio_track()) {
5644 boost::shared_ptr
<Playlist
> playlist
= atv
->playlist();
5645 double speed
= atv
->get_diskstream()->speed();
5647 XMLNode
&before
= playlist
->get_state();
5648 playlist
->add_region (boost::dynamic_pointer_cast
<AudioRegion
> (RegionFactory::create (arv
->audio_region())), (nframes64_t
) (pos
* speed
));
5649 XMLNode
&after
= playlist
->get_state();
5650 session
->add_command(new MementoCommand
<Playlist
>(*playlist
.get(), &before
, &after
));
5652 // playlist is frozen, so we have to update manually
5654 playlist
->Modified(); /* EMIT SIGNAL */
5658 Editor::track_height_step_timeout ()
5660 if (get_microseconds() - last_track_height_step_timestamp
< 250000) {
5661 current_stepping_trackview
= 0;