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 (op
);
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 (op
);
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 (op
);
464 case GainAutomationControlPointItem
:
465 case PanAutomationControlPointItem
:
466 case RedirectAutomationControlPointItem
:
467 set_selected_track_as_side_effect (op
);
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 (op
);
477 } else if (event
->type
== GDK_BUTTON_PRESS
&& mouse_mode
== MouseRange
) {
478 set_selected_track_as_side_effect (op
);
482 case AutomationTrackItem
:
483 set_selected_track_as_side_effect (op
, 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::PrimaryModifier
))) {
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::SecondaryModifier
)) {
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);
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);
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 */
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?
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
);
3749 //test to make sure that we aren't dragging near 0
3750 if (selection
->regions
.by_layer().size() == 1) { // If a single regionview is being dragged to zero, make sure we go all the way to zero.
3751 RegionView
* rv2
= *(selection
->regions
.by_layer().begin());
3752 double ix1
, ix2
, iy1
, iy2
;
3753 rv2
->get_canvas_frame()->get_bounds (ix1
, iy1
, ix2
, iy2
);
3754 rv2
->get_canvas_group()->i2w (ix1
, iy1
);
3755 double pos
= ix1
+ horizontal_adjustment
.get_value();
3756 if (-x_delta
> pos
) {
3757 pending_region_position
= 0;
3759 } else { // If any regionview is at zero, we need to know so we can stop further leftward motion.
3761 //first find the earliest region in the selection
3762 RegionView
*earliest_rv
= selection
->regions
.by_layer().front();
3763 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin(); i
!= selection
->regions
.by_layer().end(); ++i
) {
3764 RegionView
* rv
= (*i
);
3765 if (rv
->region()->position() < earliest_rv
->region()->position())
3769 //if the earliest region is near 0, then limit the drag so it doesn't go any farther left
3770 double ix1
, ix2
, iy1
, iy2
;
3771 earliest_rv
->get_canvas_frame()->get_bounds (ix1
, iy1
, ix2
, iy2
);
3772 earliest_rv
->get_canvas_group()->i2w (ix1
, iy1
);
3773 double pos
= ix1
+ horizontal_adjustment
.get_value();
3774 if (x_delta
< -pos
) {
3776 pending_region_position
= clicked_regionview
->region()->position() - earliest_rv
->region()->position();
3782 drag_info
.last_frame_position
= pending_region_position
;
3789 /* threshold not passed */
3794 /*************************************************************
3796 ************************************************************/
3798 if (x_delta
== 0 && (pointer_y_span
== 0)) {
3799 /* haven't reached next snap point, and we're not switching
3800 trackviews. nothing to do.
3805 /*************************************************************
3807 ************************************************************/
3808 bool do_move
= true;
3809 if (drag_info
.first_move
) {
3810 if (!drag_info
.move_threshold_passed
) {
3817 pair
<set
<boost::shared_ptr
<Playlist
> >::iterator
,bool> insert_result
;
3818 const list
<RegionView
*>& layered_regions
= selection
->regions
.by_layer();
3820 for (list
<RegionView
*>::const_iterator i
= layered_regions
.begin(); i
!= layered_regions
.end(); ++i
) {
3822 RegionView
* rv
= (*i
);
3823 double ix1
, ix2
, iy1
, iy2
;
3824 int32_t temp_pointer_y_span
= pointer_y_span
;
3826 if (rv
->region()->locked()) {
3830 /* get item BBox, which will be relative to parent. so we have
3831 to query on a child, then convert to world coordinates using
3835 rv
->get_canvas_frame()->get_bounds (ix1
, iy1
, ix2
, iy2
);
3836 rv
->get_canvas_group()->i2w (ix1
, iy1
);
3838 /* for evaluation of the track position of iy1, we have to adjust
3839 to allow for the vertical scrolling adjustment and the height of the timebars.
3842 iy1
+= get_trackview_group_vertical_offset ();
3843 if (drag_info
.first_move
) {
3845 // hide any dependent views
3847 rv
->get_time_axis_view().hide_dependent_views (*rv
);
3850 reparent to a non scrolling group so that we can keep the
3851 region selection above all time axis views.
3852 reparenting means we have to move the rv as the two
3853 parent groups have different coordinates.
3856 rv
->get_canvas_group()->property_y() = iy1
- 1;
3857 rv
->get_canvas_group()->reparent(*_region_motion_group
);
3859 rv
->fake_set_opaque (true);
3862 TimeAxisView
* tvp2
= trackview_by_y_position (iy1
);
3863 AudioTimeAxisView
* canvas_atv
= dynamic_cast<AudioTimeAxisView
*>(tvp2
);
3864 AudioTimeAxisView
* temp_atv
;
3866 if ((pointer_y_span
!= 0) && !clamp_y_axis
) {
3869 for (j
= height_list
.begin(); j
!= height_list
.end(); j
++) {
3870 if (x
== canvas_atv
->order
) {
3871 /* we found the track the region is on */
3872 if (x
!= original_pointer_order
) {
3873 /*this isn't from the same track we're dragging from */
3874 temp_pointer_y_span
= canvas_pointer_y_span
;
3876 while (temp_pointer_y_span
> 0) {
3877 /* we're moving up canvas-wise,
3878 so we need to find the next track height
3880 if (j
!= height_list
.begin()) {
3883 if (x
!= original_pointer_order
) {
3884 /* we're not from the dragged track, so ignore hidden tracks. */
3886 temp_pointer_y_span
++;
3890 temp_pointer_y_span
--;
3893 while (temp_pointer_y_span
< 0) {
3895 if (x
!= original_pointer_order
) {
3897 temp_pointer_y_span
--;
3901 if (j
!= height_list
.end()) {
3904 temp_pointer_y_span
++;
3906 /* find out where we'll be when we move and set height accordingly */
3908 tvp2
= trackview_by_y_position (iy1
+ y_delta
);
3909 temp_atv
= dynamic_cast<AudioTimeAxisView
*>(tvp2
);
3910 rv
->set_height (temp_atv
->current_height());
3912 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3913 personally, i think this can confuse things, but never mind.
3916 //const GdkColor& col (temp_atv->view->get_region_color());
3917 //rv->set_color (const_cast<GdkColor&>(col));
3924 if (drag_info
.brushing
) {
3925 mouse_brush_insert_region (rv
, pending_region_position
);
3927 rv
->move (x_delta
, y_delta
);
3930 } /* foreach region */
3934 if (drag_info
.first_move
&& drag_info
.move_threshold_passed
) {
3935 cursor_group
->raise_to_top();
3936 drag_info
.first_move
= false;
3939 if (x_delta
!= 0 && !drag_info
.brushing
) {
3940 show_verbose_time_cursor (drag_info
.last_frame_position
, 10);
3945 Editor::region_drag_finished_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
3947 bool nocommit
= true;
3948 vector
<RegionView
*> copies
;
3949 RouteTimeAxisView
* source_tv
;
3950 boost::shared_ptr
<Diskstream
> ds
;
3951 boost::shared_ptr
<Playlist
> from_playlist
;
3952 vector
<RegionView
*> new_selection
;
3953 typedef set
<boost::shared_ptr
<Playlist
> > PlaylistSet
;
3954 PlaylistSet modified_playlists
;
3955 PlaylistSet frozen_playlists
;
3956 list
<sigc::connection
> modified_playlist_connections
;
3957 pair
<PlaylistSet::iterator
,bool> insert_result
, frozen_insert_result
;
3958 nframes64_t drag_delta
;
3959 bool changed_tracks
, changed_position
;
3961 /* first_move is set to false if the regionview has been moved in the
3965 if (drag_info
.first_move
) {
3972 if (Config
->get_edit_mode() == Splice
&& !pre_drag_region_selection
.empty()) {
3973 selection
->set (pre_drag_region_selection
);
3974 pre_drag_region_selection
.clear ();
3977 if (drag_info
.brushing
) {
3978 /* all changes were made during motion event handlers */
3980 if (drag_info
.copy
) {
3981 for (list
<RegionView
*>::iterator i
= selection
->regions
.begin(); i
!= selection
->regions
.end(); ++i
) {
3982 copies
.push_back (*i
);
3991 /* reverse this here so that we have the correct logic to finalize
3995 if (Config
->get_edit_mode() == Lock
) {
3996 drag_info
.x_constrained
= !drag_info
.x_constrained
;
3999 cerr
<< "drag done, copy ? " << drag_info
.copy
<< " x-const ? " << drag_info
.x_constrained
<< endl
;
4001 if (drag_info
.copy
) {
4002 if (drag_info
.x_constrained
) {
4003 op_string
= _("fixed time region copy");
4005 op_string
= _("region copy");
4008 if (drag_info
.x_constrained
) {
4009 op_string
= _("fixed time region drag");
4011 op_string
= _("region drag");
4015 begin_reversible_command (op_string
);
4016 changed_position
= (drag_info
.last_frame_position
!= (nframes64_t
) (clicked_regionview
->region()->position()));
4017 changed_tracks
= (trackview_by_y_position (drag_info
.current_pointer_y
) != &clicked_regionview
->get_time_axis_view());
4019 drag_delta
= clicked_regionview
->region()->position() - drag_info
.last_frame_position
;
4023 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin(); i
!= selection
->regions
.by_layer().end(); ) {
4025 RegionView
* rv
= (*i
);
4026 double ix1
, ix2
, iy1
, iy2
;
4027 rv
->get_canvas_frame()->get_bounds (ix1
, iy1
, ix2
, iy2
);
4028 rv
->get_canvas_group()->i2w (ix1
, iy1
);
4029 iy1
+= vertical_adjustment
.get_value() - canvas_timebars_vsize
;
4031 TimeAxisView
* dest_tv
= trackview_by_y_position (iy1
);
4032 AudioTimeAxisView
* dest_atv
= dynamic_cast<AudioTimeAxisView
*>(dest_tv
);
4035 if (rv
->region()->locked()) {
4040 cerr
<< "drag delta = " << drag_delta
<< " rpos was " << rv
->region()->position() << endl
;
4042 if (changed_position
&& !drag_info
.x_constrained
&& (mouse_mode
!= MouseRange
)) {
4043 where
= rv
->region()->position() - drag_delta
;
4045 where
= rv
->region()->position();
4048 boost::shared_ptr
<Region
> new_region
;
4050 if (drag_info
.copy
) {
4051 /* we already made a copy */
4052 new_region
= rv
->region();
4054 /* undo the previous hide_dependent_views so that xfades don't
4055 disappear on copying regions
4058 //rv->get_time_axis_view().reveal_dependent_views (*rv);
4060 } else if (changed_tracks
) {
4061 new_region
= RegionFactory::create (rv
->region());
4064 if (changed_tracks
|| drag_info
.copy
) {
4066 boost::shared_ptr
<Playlist
> to_playlist
= dest_atv
->playlist();
4068 latest_regionviews
.clear ();
4070 sigc::connection c
= dest_atv
->view()->RegionViewAdded
.connect (mem_fun(*this, &Editor::collect_new_region_view
));
4072 insert_result
= modified_playlists
.insert (to_playlist
);
4073 if (insert_result
.second
) {
4074 session
->add_command (new MementoCommand
<Playlist
>(*to_playlist
, &to_playlist
->get_state(), 0));
4077 cerr
<< "Adding region @ " << new_region
->position() << " at " << where
<< endl
;
4079 to_playlist
->add_region (new_region
, where
);
4083 if (!latest_regionviews
.empty()) {
4084 // XXX why just the first one ? we only expect one
4085 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
4086 new_selection
.push_back (latest_regionviews
.front());
4091 motion on the same track. plonk the previously reparented region
4092 back to its original canvas group (its streamview).
4093 No need to do anything for copies as they are fake regions which will be deleted.
4096 RouteTimeAxisView
* dest_rtv
= dynamic_cast<RouteTimeAxisView
*> (dest_atv
);
4097 rv
->get_canvas_group()->reparent (*dest_rtv
->view()->canvas_item());
4098 rv
->get_canvas_group()->property_y() = 0;
4100 /* just change the model */
4102 boost::shared_ptr
<Playlist
> playlist
= dest_atv
->playlist();
4104 insert_result
= modified_playlists
.insert (playlist
);
4105 if (insert_result
.second
) {
4106 session
->add_command (new MementoCommand
<Playlist
>(*playlist
, &playlist
->get_state(), 0));
4108 /* freeze to avoid lots of relayering in the case of a multi-region drag */
4109 frozen_insert_result
= frozen_playlists
.insert(playlist
);
4110 if (frozen_insert_result
.second
) {
4114 rv
->region()->set_position (where
, (void*) this);
4117 if (changed_tracks
&& !drag_info
.copy
) {
4119 /* get the playlist where this drag started. we can't use rv->region()->playlist()
4120 because we may have copied the region and it has not been attached to a playlist.
4123 assert ((source_tv
= dynamic_cast<RouteTimeAxisView
*> (&rv
->get_time_axis_view())));
4124 assert ((ds
= source_tv
->get_diskstream()));
4125 assert ((from_playlist
= ds
->playlist()));
4127 /* moved to a different audio track, without copying */
4129 /* the region that used to be in the old playlist is not
4130 moved to the new one - we use a copy of it. as a result,
4131 any existing editor for the region should no longer be
4135 rv
->hide_region_editor();
4136 rv
->fake_set_opaque (false);
4138 /* remove the region from the old playlist */
4140 insert_result
= modified_playlists
.insert (from_playlist
);
4141 if (insert_result
.second
) {
4142 if (mouse_mode
!= MouseRange
) {
4143 session
->add_command (new MementoCommand
<Playlist
>(*from_playlist
, &from_playlist
->get_state(), 0));
4147 from_playlist
->remove_region ((rv
->region()));
4149 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
4150 was selected in all of them, then removing it from a playlist will have removed all
4151 trace of it from the selection (i.e. there were N regions selected, we removed 1,
4152 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
4153 corresponding regionview, and the selection is now empty).
4155 this could have invalidated any and all iterators into the region selection.
4157 the heuristic we use here is: if the region selection is empty, break out of the loop
4158 here. if the region selection is not empty, then restart the loop because we know that
4159 we must have removed at least the region(view) we've just been working on as well as any
4160 that we processed on previous iterations.
4162 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
4163 we can just iterate.
4166 if (selection
->regions
.empty()) {
4169 i
= selection
->regions
.by_layer().begin();
4176 if (drag_info
.copy
) {
4177 copies
.push_back (rv
);
4181 if (new_selection
.empty()) {
4182 if (drag_info
.copy
) {
4183 /* the region(view)s that are selected and being dragged around
4184 are copies and do not belong to any track. remove them
4185 from the selection right here.
4187 selection
->clear_regions();
4190 /* this will clear any existing selection that would have been
4191 cleared in the other clause above
4193 selection
->set (new_selection
);
4196 for (set
<boost::shared_ptr
<Playlist
> >::iterator p
= frozen_playlists
.begin(); p
!= frozen_playlists
.end(); ++p
) {
4202 for (set
<boost::shared_ptr
<Playlist
> >::iterator p
= modified_playlists
.begin(); p
!= modified_playlists
.end(); ++p
) {
4203 session
->add_command (new MementoCommand
<Playlist
>(*(*p
), 0, &(*p
)->get_state()));
4205 commit_reversible_command ();
4208 for (vector
<RegionView
*>::iterator x
= copies
.begin(); x
!= copies
.end(); ++x
) {
4215 Editor::region_view_item_click (AudioRegionView
& rv
, GdkEventButton
* event
)
4217 /* Either add to or set the set the region selection, unless
4218 this is an alignment click (control used)
4221 if (Keyboard::modifier_state_contains (event
->state
, Keyboard::PrimaryModifier
)) {
4222 TimeAxisView
* tv
= &rv
.get_time_axis_view();
4223 AudioTimeAxisView
* atv
= dynamic_cast<AudioTimeAxisView
*>(tv
);
4225 if (atv
&& atv
->is_audio_track()) {
4226 speed
= atv
->get_diskstream()->speed();
4229 nframes64_t where
= get_preferred_edit_position();
4233 if (Keyboard::modifier_state_equals (event
->state
, Keyboard::ModifierMask (Keyboard::PrimaryModifier
|Keyboard::SecondaryModifier
))) {
4235 align_region (rv
.region(), SyncPoint
, (nframes64_t
) (where
* speed
));
4237 } else if (Keyboard::modifier_state_equals (event
->state
, Keyboard::ModifierMask (Keyboard::PrimaryModifier
|Keyboard::TertiaryModifier
))) {
4239 align_region (rv
.region(), End
, (nframes64_t
) (where
* speed
));
4243 align_region (rv
.region(), Start
, (nframes64_t
) (where
* speed
));
4250 Editor::show_verbose_time_cursor (nframes64_t frame
, double offset
, double xpos
, double ypos
)
4256 nframes64_t frame_rate
;
4265 if (Profile
->get_sae() || Profile
->get_small_screen()) {
4266 m
= ARDOUR_UI::instance()->primary_clock
.mode();
4268 m
= ARDOUR_UI::instance()->secondary_clock
.mode();
4272 case AudioClock::BBT
:
4273 session
->bbt_time (frame
, bbt
);
4274 snprintf (buf
, sizeof (buf
), "%02" PRIu32
"|%02" PRIu32
"|%02" PRIu32
, bbt
.bars
, bbt
.beats
, bbt
.ticks
);
4277 case AudioClock::SMPTE
:
4278 session
->smpte_time (frame
, smpte
);
4279 snprintf (buf
, sizeof (buf
), "%02" PRId32
":%02" PRId32
":%02" PRId32
":%02" PRId32
, smpte
.hours
, smpte
.minutes
, smpte
.seconds
, smpte
.frames
);
4282 case AudioClock::MinSec
:
4283 /* XXX this is copied from show_verbose_duration_cursor() */
4284 frame_rate
= session
->frame_rate();
4285 hours
= frame
/ (frame_rate
* 3600);
4286 frame
= frame
% (frame_rate
* 3600);
4287 mins
= frame
/ (frame_rate
* 60);
4288 frame
= frame
% (frame_rate
* 60);
4289 secs
= (float) frame
/ (float) frame_rate
;
4290 snprintf (buf
, sizeof (buf
), "%02" PRId32
":%02" PRId32
":%.4f", hours
, mins
, secs
);
4294 snprintf (buf
, sizeof(buf
), "%" PRIi64
, frame
);
4298 if (xpos
>= 0 && ypos
>=0) {
4299 set_verbose_canvas_cursor (buf
, xpos
+ offset
, ypos
+ offset
);
4302 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
);
4304 show_verbose_canvas_cursor ();
4308 Editor::show_verbose_duration_cursor (nframes64_t start
, nframes64_t end
, double offset
, double xpos
, double ypos
)
4315 nframes64_t distance
, frame_rate
;
4317 Meter
meter_at_start(session
->tempo_map().meter_at(start
));
4325 if (Profile
->get_sae() || Profile
->get_small_screen()) {
4326 m
= ARDOUR_UI::instance()->primary_clock
.mode ();
4328 m
= ARDOUR_UI::instance()->secondary_clock
.mode ();
4332 case AudioClock::BBT
:
4333 session
->bbt_time (start
, sbbt
);
4334 session
->bbt_time (end
, ebbt
);
4337 /* XXX this computation won't work well if the
4338 user makes a selection that spans any meter changes.
4341 ebbt
.bars
-= sbbt
.bars
;
4342 if (ebbt
.beats
>= sbbt
.beats
) {
4343 ebbt
.beats
-= sbbt
.beats
;
4346 ebbt
.beats
= int(meter_at_start
.beats_per_bar()) + ebbt
.beats
- sbbt
.beats
;
4348 if (ebbt
.ticks
>= sbbt
.ticks
) {
4349 ebbt
.ticks
-= sbbt
.ticks
;
4352 ebbt
.ticks
= int(Meter::ticks_per_beat
) + ebbt
.ticks
- sbbt
.ticks
;
4355 snprintf (buf
, sizeof (buf
), "%02" PRIu32
"|%02" PRIu32
"|%02" PRIu32
, ebbt
.bars
, ebbt
.beats
, ebbt
.ticks
);
4358 case AudioClock::SMPTE
:
4359 session
->smpte_duration (end
- start
, smpte
);
4360 snprintf (buf
, sizeof (buf
), "%02" PRId32
":%02" PRId32
":%02" PRId32
":%02" PRId32
, smpte
.hours
, smpte
.minutes
, smpte
.seconds
, smpte
.frames
);
4363 case AudioClock::MinSec
:
4364 /* XXX this stuff should be elsewhere.. */
4365 distance
= end
- start
;
4366 frame_rate
= session
->frame_rate();
4367 hours
= distance
/ (frame_rate
* 3600);
4368 distance
= distance
% (frame_rate
* 3600);
4369 mins
= distance
/ (frame_rate
* 60);
4370 distance
= distance
% (frame_rate
* 60);
4371 secs
= (float) distance
/ (float) frame_rate
;
4372 snprintf (buf
, sizeof (buf
), "%02" PRId32
":%02" PRId32
":%.4f", hours
, mins
, secs
);
4376 snprintf (buf
, sizeof(buf
), "%" PRIi64
, end
- start
);
4380 if (xpos
>= 0 && ypos
>=0) {
4381 set_verbose_canvas_cursor (buf
, xpos
+ offset
, ypos
+ offset
);
4384 set_verbose_canvas_cursor (buf
, drag_info
.current_pointer_x
+ offset
, drag_info
.current_pointer_y
+ offset
);
4387 show_verbose_canvas_cursor ();
4391 Editor::collect_new_region_view (RegionView
* rv
)
4393 latest_regionviews
.push_back (rv
);
4397 Editor::start_selection_grab (ArdourCanvas::Item
* item
, GdkEvent
* event
)
4399 if (clicked_regionview
== 0) {
4403 /* lets try to create new Region for the selection */
4405 vector
<boost::shared_ptr
<AudioRegion
> > new_regions
;
4406 create_region_from_selection (new_regions
);
4408 if (new_regions
.empty()) {
4412 cerr
<< "got " << new_regions
.size() << " regions in selection grab\n";
4414 /* XXX fix me one day to use all new regions */
4416 boost::shared_ptr
<Region
> region (new_regions
.front());
4418 /* add it to the current stream/playlist.
4420 tricky: the streamview for the track will add a new regionview. we will
4421 catch the signal it sends when it creates the regionview to
4422 set the regionview we want to then drag.
4425 latest_regionviews
.clear();
4426 sigc::connection c
= clicked_audio_trackview
->view()->RegionViewAdded
.connect (mem_fun(*this, &Editor::collect_new_region_view
));
4428 /* A selection grab currently creates two undo/redo operations, one for
4429 creating the new region and another for moving it.
4432 begin_reversible_command (_("selection grab"));
4434 boost::shared_ptr
<Playlist
> playlist
= clicked_trackview
->playlist();
4436 XMLNode
*before
= &(playlist
->get_state());
4437 clicked_trackview
->playlist()->add_region (region
, selection
->time
[clicked_selection
].start
);
4438 XMLNode
*after
= &(playlist
->get_state());
4439 session
->add_command(new MementoCommand
<Playlist
>(*playlist
, before
, after
));
4441 commit_reversible_command ();
4445 if (latest_regionviews
.empty()) {
4446 /* something went wrong */
4450 /* we need to deselect all other regionviews, and select this one
4451 i'm ignoring undo stuff, because the region creation will take care of it
4453 selection
->set (latest_regionviews
);
4455 drag_info
.item
= latest_regionviews
.front()->get_canvas_group();
4456 drag_info
.data
= latest_regionviews
.front();
4457 drag_info
.motion_callback
= &Editor::region_drag_motion_callback
;
4458 drag_info
.finished_callback
= &Editor::region_drag_finished_callback
;
4462 drag_info
.source_trackview
= clicked_trackview
;
4463 drag_info
.dest_trackview
= drag_info
.source_trackview
;
4464 drag_info
.last_frame_position
= latest_regionviews
.front()->region()->position();
4465 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- drag_info
.last_frame_position
;
4467 show_verbose_time_cursor (drag_info
.last_frame_position
, 10);
4471 Editor::cancel_selection ()
4473 for (TrackViewList::iterator i
= track_views
.begin(); i
!= track_views
.end(); ++i
) {
4474 (*i
)->hide_selection ();
4476 selection
->clear ();
4477 clicked_selection
= 0;
4481 Editor::start_selection_op (ArdourCanvas::Item
* item
, GdkEvent
* event
, SelectionOp op
)
4483 nframes64_t start
= 0;
4484 nframes64_t end
= 0;
4490 drag_info
.item
= item
;
4491 drag_info
.motion_callback
= &Editor::drag_selection
;
4492 drag_info
.finished_callback
= &Editor::end_selection_op
;
4497 case CreateSelection
:
4498 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::TertiaryModifier
)) {
4499 drag_info
.copy
= true;
4501 drag_info
.copy
= false;
4503 start_grab (event
, selector_cursor
);
4506 case SelectionStartTrim
:
4507 if (clicked_trackview
) {
4508 clicked_trackview
->order_selection_trims (item
, true);
4510 start_grab (event
, trimmer_cursor
);
4511 start
= selection
->time
[clicked_selection
].start
;
4512 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- start
;
4515 case SelectionEndTrim
:
4516 if (clicked_trackview
) {
4517 clicked_trackview
->order_selection_trims (item
, false);
4519 start_grab (event
, trimmer_cursor
);
4520 end
= selection
->time
[clicked_selection
].end
;
4521 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- end
;
4525 start
= selection
->time
[clicked_selection
].start
;
4527 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- start
;
4531 if (selection_op
== SelectionMove
) {
4532 show_verbose_time_cursor(start
, 10);
4534 show_verbose_time_cursor(drag_info
.current_pointer_frame
, 10);
4539 Editor::drag_selection (ArdourCanvas::Item
* item
, GdkEvent
* event
)
4541 nframes64_t start
= 0;
4542 nframes64_t end
= 0;
4544 nframes64_t pending_position
;
4546 if (drag_info
.current_pointer_frame
> drag_info
.pointer_frame_offset
) {
4547 pending_position
= drag_info
.current_pointer_frame
- drag_info
.pointer_frame_offset
;
4549 pending_position
= 0;
4552 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
4553 snap_to (pending_position
);
4556 /* only alter selection if the current frame is
4557 different from the last frame position (adjusted)
4560 if (pending_position
== drag_info
.last_pointer_frame
) return;
4562 switch (selection_op
) {
4563 case CreateSelection
:
4565 if (drag_info
.first_move
) {
4566 snap_to (drag_info
.grab_frame
);
4569 if (pending_position
< drag_info
.grab_frame
) {
4570 start
= pending_position
;
4571 end
= drag_info
.grab_frame
;
4573 end
= pending_position
;
4574 start
= drag_info
.grab_frame
;
4577 /* first drag: Either add to the selection
4578 or create a new selection->
4581 if (drag_info
.first_move
) {
4583 begin_reversible_command (_("range selection"));
4585 if (drag_info
.copy
) {
4586 /* adding to the selection */
4587 clicked_selection
= selection
->add (start
, end
);
4588 drag_info
.copy
= false;
4590 /* new selection-> */
4591 clicked_selection
= selection
->set (clicked_trackview
, start
, end
);
4596 case SelectionStartTrim
:
4598 if (drag_info
.first_move
) {
4599 begin_reversible_command (_("trim selection start"));
4602 start
= selection
->time
[clicked_selection
].start
;
4603 end
= selection
->time
[clicked_selection
].end
;
4605 if (pending_position
> end
) {
4608 start
= pending_position
;
4612 case SelectionEndTrim
:
4614 if (drag_info
.first_move
) {
4615 begin_reversible_command (_("trim selection end"));
4618 start
= selection
->time
[clicked_selection
].start
;
4619 end
= selection
->time
[clicked_selection
].end
;
4621 if (pending_position
< start
) {
4624 end
= pending_position
;
4631 if (drag_info
.first_move
) {
4632 begin_reversible_command (_("move selection"));
4635 start
= selection
->time
[clicked_selection
].start
;
4636 end
= selection
->time
[clicked_selection
].end
;
4638 length
= end
- start
;
4640 start
= pending_position
;
4643 end
= start
+ length
;
4648 if (event
->button
.x
>= horizontal_adjustment
.get_value() + canvas_width
) {
4649 start_canvas_autoscroll (1, 0);
4653 selection
->replace (clicked_selection
, start
, end
);
4656 drag_info
.last_pointer_frame
= pending_position
;
4657 drag_info
.first_move
= false;
4659 if (selection_op
== SelectionMove
) {
4660 show_verbose_time_cursor(start
, 10);
4662 show_verbose_time_cursor(pending_position
, 10);
4667 Editor::end_selection_op (ArdourCanvas::Item
* item
, GdkEvent
* event
)
4669 if (!drag_info
.first_move
) {
4670 drag_selection (item
, event
);
4671 /* XXX this is not object-oriented programming at all. ick */
4672 if (selection
->time
.consolidate()) {
4673 selection
->TimeChanged ();
4675 commit_reversible_command ();
4678 /* XXX what if its a music time selection? */
4679 if (Config
->get_auto_play() || (session
->get_play_range() && session
->transport_rolling())) {
4680 session
->request_play_range (&selection
->time
, true);
4684 /* just a click, no pointer movement.*/
4686 if (Keyboard::no_modifier_keys_pressed (&event
->button
)) {
4688 selection
->clear_time();
4692 if (session
->get_play_range () && session
->transport_rolling()) {
4693 session
->request_stop (false, false);
4697 stop_canvas_autoscroll ();
4701 Editor::start_trim (ArdourCanvas::Item
* item
, GdkEvent
* event
)
4704 TimeAxisView
* tvp
= clicked_trackview
;
4705 AudioTimeAxisView
* tv
= dynamic_cast<AudioTimeAxisView
*>(tvp
);
4707 if (tv
&& tv
->is_audio_track()) {
4708 speed
= tv
->get_diskstream()->speed();
4711 nframes64_t region_start
= (nframes64_t
) (clicked_regionview
->region()->position() / speed
);
4712 nframes64_t region_end
= (nframes64_t
) (clicked_regionview
->region()->last_frame() / speed
);
4713 nframes64_t region_length
= (nframes64_t
) (clicked_regionview
->region()->length() / speed
);
4715 //drag_info.item = clicked_regionview->get_name_highlight();
4716 drag_info
.item
= item
;
4717 drag_info
.motion_callback
= &Editor::trim_motion_callback
;
4718 drag_info
.finished_callback
= &Editor::trim_finished_callback
;
4720 start_grab (event
, trimmer_cursor
);
4722 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
4723 trim_op
= ContentsTrim
;
4725 /* These will get overridden for a point trim.*/
4726 if (drag_info
.current_pointer_frame
< (region_start
+ region_length
/2)) {
4727 /* closer to start */
4728 trim_op
= StartTrim
;
4729 } else if (drag_info
.current_pointer_frame
> (region_end
- region_length
/2)) {
4737 show_verbose_time_cursor(region_start
, 10);
4740 show_verbose_time_cursor(region_end
, 10);
4743 show_verbose_time_cursor(drag_info
.current_pointer_frame
, 10);
4749 Editor::trim_motion_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
4751 RegionView
* rv
= clicked_regionview
;
4752 nframes64_t frame_delta
= 0;
4753 bool left_direction
;
4754 bool obey_snap
= !Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier());
4756 /* snap modifier works differently here..
4757 its' current state has to be passed to the
4758 various trim functions in order to work properly
4762 TimeAxisView
* tvp
= clicked_trackview
;
4763 RouteTimeAxisView
* tv
= dynamic_cast<RouteTimeAxisView
*>(tvp
);
4764 pair
<set
<boost::shared_ptr
<Playlist
> >::iterator
,bool> insert_result
;
4766 if (tv
&& tv
->is_audio_track()) {
4767 speed
= tv
->get_diskstream()->speed();
4770 if (drag_info
.last_pointer_frame
> drag_info
.current_pointer_frame
) {
4771 left_direction
= true;
4773 left_direction
= false;
4777 snap_to (drag_info
.current_pointer_frame
);
4780 if (drag_info
.current_pointer_frame
== drag_info
.last_pointer_frame
) {
4784 if (drag_info
.first_move
) {
4790 trim_type
= "Region start trim";
4793 trim_type
= "Region end trim";
4796 trim_type
= "Region content trim";
4800 begin_reversible_command (trim_type
);
4802 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin(); i
!= selection
->regions
.by_layer().end(); ++i
) {
4803 (*i
)->fake_set_opaque(false);
4804 (*i
)->region()->freeze ();
4806 AudioRegionView
* const arv
= dynamic_cast<AudioRegionView
*>(*i
);
4808 arv
->temporarily_hide_envelope ();
4810 boost::shared_ptr
<Playlist
> pl
= (*i
)->region()->playlist();
4811 insert_result
= motion_frozen_playlists
.insert (pl
);
4812 if (insert_result
.second
) {
4813 session
->add_command(new MementoCommand
<Playlist
>(*pl
, &pl
->get_state(), 0));
4818 if (left_direction
) {
4819 frame_delta
= (drag_info
.last_pointer_frame
- drag_info
.current_pointer_frame
);
4821 frame_delta
= (drag_info
.current_pointer_frame
- drag_info
.last_pointer_frame
);
4826 if ((left_direction
== false) && (drag_info
.current_pointer_frame
<= rv
->region()->first_frame()/speed
)) {
4829 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin(); i
!= selection
->regions
.by_layer().end(); ++i
) {
4830 single_start_trim (**i
, frame_delta
, left_direction
, obey_snap
);
4836 if ((left_direction
== true) && (drag_info
.current_pointer_frame
> (nframes64_t
) (rv
->region()->last_frame()/speed
))) {
4839 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin(); i
!= selection
->regions
.by_layer().end(); ++i
) {
4840 single_end_trim (**i
, frame_delta
, left_direction
, obey_snap
);
4847 bool swap_direction
= false;
4849 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
4850 swap_direction
= true;
4853 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin();
4854 i
!= selection
->regions
.by_layer().end(); ++i
)
4856 single_contents_trim (**i
, frame_delta
, left_direction
, swap_direction
, obey_snap
);
4864 show_verbose_time_cursor((nframes64_t
) (rv
->region()->position()/speed
), 10);
4867 show_verbose_time_cursor((nframes64_t
) (rv
->region()->last_frame()/speed
), 10);
4870 show_verbose_time_cursor(drag_info
.current_pointer_frame
, 10);
4874 drag_info
.last_pointer_frame
= drag_info
.current_pointer_frame
;
4875 drag_info
.first_move
= false;
4879 Editor::single_contents_trim (RegionView
& rv
, nframes64_t frame_delta
, bool left_direction
, bool swap_direction
, bool obey_snap
)
4881 boost::shared_ptr
<Region
> region (rv
.region());
4883 if (region
->locked()) {
4887 nframes64_t new_bound
;
4890 TimeAxisView
* tvp
= clicked_trackview
;
4891 RouteTimeAxisView
* tv
= dynamic_cast<RouteTimeAxisView
*>(tvp
);
4893 if (tv
&& tv
->is_audio_track()) {
4894 speed
= tv
->get_diskstream()->speed();
4897 if (left_direction
) {
4898 if (swap_direction
) {
4899 new_bound
= (nframes64_t
) (region
->position()/speed
) + frame_delta
;
4901 new_bound
= (nframes64_t
) (region
->position()/speed
) - frame_delta
;
4904 if (swap_direction
) {
4905 new_bound
= (nframes64_t
) (region
->position()/speed
) - frame_delta
;
4907 new_bound
= (nframes64_t
) (region
->position()/speed
) + frame_delta
;
4912 snap_to (new_bound
);
4914 region
->trim_start ((nframes64_t
) (new_bound
* speed
), this);
4915 rv
.region_changed (StartChanged
);
4919 Editor::single_start_trim (RegionView
& rv
, nframes64_t frame_delta
, bool left_direction
, bool obey_snap
)
4921 boost::shared_ptr
<Region
> region (rv
.region());
4923 if (region
->locked()) {
4927 nframes64_t new_bound
;
4930 TimeAxisView
* tvp
= clicked_trackview
;
4931 AudioTimeAxisView
* tv
= dynamic_cast<AudioTimeAxisView
*>(tvp
);
4933 if (tv
&& tv
->is_audio_track()) {
4934 speed
= tv
->get_diskstream()->speed();
4937 if (left_direction
) {
4938 new_bound
= (nframes64_t
) (region
->position()/speed
) - frame_delta
;
4940 new_bound
= (nframes64_t
) (region
->position()/speed
) + frame_delta
;
4944 snap_to (new_bound
, (left_direction
? 0 : 1));
4947 region
->trim_front ((nframes64_t
) (new_bound
* speed
), this);
4949 rv
.region_changed (Change (LengthChanged
|PositionChanged
|StartChanged
));
4953 Editor::single_end_trim (RegionView
& rv
, nframes64_t frame_delta
, bool left_direction
, bool obey_snap
)
4955 boost::shared_ptr
<Region
> region (rv
.region());
4957 if (region
->locked()) {
4961 nframes64_t new_bound
;
4964 TimeAxisView
* tvp
= clicked_trackview
;
4965 AudioTimeAxisView
* tv
= dynamic_cast<AudioTimeAxisView
*>(tvp
);
4967 if (tv
&& tv
->is_audio_track()) {
4968 speed
= tv
->get_diskstream()->speed();
4971 if (left_direction
) {
4972 new_bound
= (nframes64_t
) ((region
->last_frame() + 1)/speed
) - frame_delta
;
4974 new_bound
= (nframes64_t
) ((region
->last_frame() + 1)/speed
) + frame_delta
;
4978 snap_to (new_bound
);
4980 region
->trim_end ((nframes64_t
) (new_bound
* speed
), this);
4981 rv
.region_changed (LengthChanged
);
4985 Editor::trim_finished_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
4987 if (!drag_info
.first_move
) {
4988 trim_motion_callback (item
, event
);
4990 if (!selection
->selected (clicked_regionview
)) {
4991 thaw_region_after_trim (*clicked_regionview
);
4994 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin();
4995 i
!= selection
->regions
.by_layer().end(); ++i
)
4997 thaw_region_after_trim (**i
);
4998 (*i
)->fake_set_opaque (true);
5002 for (set
<boost::shared_ptr
<Playlist
> >::iterator p
= motion_frozen_playlists
.begin(); p
!= motion_frozen_playlists
.end(); ++p
) {
5004 session
->add_command (new MementoCommand
<Playlist
>(*(*p
).get(), 0, &(*p
)->get_state()));
5007 motion_frozen_playlists
.clear ();
5009 commit_reversible_command();
5011 /* no mouse movement */
5017 Editor::point_trim (GdkEvent
* event
)
5019 RegionView
* rv
= clicked_regionview
;
5020 nframes64_t new_bound
= drag_info
.current_pointer_frame
;
5022 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
5023 snap_to (new_bound
);
5026 /* Choose action dependant on which button was pressed */
5027 switch (event
->button
.button
) {
5029 trim_op
= StartTrim
;
5030 begin_reversible_command (_("Start point trim"));
5032 if (selection
->selected (rv
)) {
5034 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin();
5035 i
!= selection
->regions
.by_layer().end(); ++i
)
5037 if (!(*i
)->region()->locked()) {
5038 boost::shared_ptr
<Playlist
> pl
= (*i
)->region()->playlist();
5039 XMLNode
&before
= pl
->get_state();
5040 (*i
)->region()->trim_front (new_bound
, this);
5041 XMLNode
&after
= pl
->get_state();
5042 session
->add_command(new MementoCommand
<Playlist
>(*pl
.get(), &before
, &after
));
5048 if (!rv
->region()->locked()) {
5049 boost::shared_ptr
<Playlist
> pl
= rv
->region()->playlist();
5050 XMLNode
&before
= pl
->get_state();
5051 rv
->region()->trim_front (new_bound
, this);
5052 XMLNode
&after
= pl
->get_state();
5053 session
->add_command(new MementoCommand
<Playlist
>(*pl
.get(), &before
, &after
));
5057 commit_reversible_command();
5062 begin_reversible_command (_("End point trim"));
5064 if (selection
->selected (rv
)) {
5066 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin(); i
!= selection
->regions
.by_layer().end(); ++i
)
5068 if (!(*i
)->region()->locked()) {
5069 boost::shared_ptr
<Playlist
> pl
= (*i
)->region()->playlist();
5070 XMLNode
&before
= pl
->get_state();
5071 (*i
)->region()->trim_end (new_bound
, this);
5072 XMLNode
&after
= pl
->get_state();
5073 session
->add_command(new MementoCommand
<Playlist
>(*pl
.get(), &before
, &after
));
5079 if (!rv
->region()->locked()) {
5080 boost::shared_ptr
<Playlist
> pl
= rv
->region()->playlist();
5081 XMLNode
&before
= pl
->get_state();
5082 rv
->region()->trim_end (new_bound
, this);
5083 XMLNode
&after
= pl
->get_state();
5084 session
->add_command (new MementoCommand
<Playlist
>(*pl
.get(), &before
, &after
));
5088 commit_reversible_command();
5097 Editor::thaw_region_after_trim (RegionView
& rv
)
5099 boost::shared_ptr
<Region
> region (rv
.region());
5101 if (region
->locked()) {
5105 region
->thaw (_("trimmed region"));
5106 XMLNode
&after
= region
->playlist()->get_state();
5107 session
->add_command (new MementoCommand
<Playlist
>(*(region
->playlist()), 0, &after
));
5109 AudioRegionView
* arv
= dynamic_cast<AudioRegionView
*>(&rv
);
5111 arv
->unhide_envelope ();
5115 Editor::hide_marker (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5120 if ((marker
= static_cast<Marker
*> (item
->get_data ("marker"))) == 0) {
5121 fatal
<< _("programming error: marker canvas item has no marker object pointer!") << endmsg
;
5125 Location
* location
= find_location_from_marker (marker
, is_start
);
5126 location
->set_hidden (true, this);
5131 Editor::start_range_markerbar_op (ArdourCanvas::Item
* item
, GdkEvent
* event
, RangeMarkerOp op
)
5137 drag_info
.item
= item
;
5138 drag_info
.motion_callback
= &Editor::drag_range_markerbar_op
;
5139 drag_info
.finished_callback
= &Editor::end_range_markerbar_op
;
5141 range_marker_op
= op
;
5143 if (!temp_location
) {
5144 temp_location
= new Location
;
5148 case CreateRangeMarker
:
5149 case CreateTransportMarker
:
5150 case CreateCDMarker
:
5152 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::TertiaryModifier
)) {
5153 drag_info
.copy
= true;
5155 drag_info
.copy
= false;
5157 start_grab (event
, selector_cursor
);
5161 show_verbose_time_cursor(drag_info
.current_pointer_frame
, 10);
5166 Editor::drag_range_markerbar_op (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5168 nframes64_t start
= 0;
5169 nframes64_t end
= 0;
5170 ArdourCanvas::SimpleRect
*crect
;
5172 switch (range_marker_op
) {
5173 case CreateRangeMarker
:
5174 crect
= range_bar_drag_rect
;
5176 case CreateTransportMarker
:
5177 crect
= transport_bar_drag_rect
;
5179 case CreateCDMarker
:
5180 crect
= cd_marker_bar_drag_rect
;
5183 cerr
<< "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl
;
5188 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
5189 snap_to (drag_info
.current_pointer_frame
);
5192 /* only alter selection if the current frame is
5193 different from the last frame position.
5196 if (drag_info
.current_pointer_frame
== drag_info
.last_pointer_frame
) return;
5198 switch (range_marker_op
) {
5199 case CreateRangeMarker
:
5200 case CreateTransportMarker
:
5201 case CreateCDMarker
:
5202 if (drag_info
.first_move
) {
5203 snap_to (drag_info
.grab_frame
);
5206 if (drag_info
.current_pointer_frame
< drag_info
.grab_frame
) {
5207 start
= drag_info
.current_pointer_frame
;
5208 end
= drag_info
.grab_frame
;
5210 end
= drag_info
.current_pointer_frame
;
5211 start
= drag_info
.grab_frame
;
5214 /* first drag: Either add to the selection
5215 or create a new selection.
5218 if (drag_info
.first_move
) {
5220 temp_location
->set (start
, end
);
5224 update_marker_drag_item (temp_location
);
5225 range_marker_drag_rect
->show();
5226 //range_marker_drag_rect->raise_to_top();
5232 if (event
->button
.x
>= horizontal_adjustment
.get_value() + canvas_width
) {
5233 start_canvas_autoscroll (1, 0);
5237 temp_location
->set (start
, end
);
5239 double x1
= frame_to_pixel (start
);
5240 double x2
= frame_to_pixel (end
);
5241 crect
->property_x1() = x1
;
5242 crect
->property_x2() = x2
;
5244 update_marker_drag_item (temp_location
);
5247 drag_info
.last_pointer_frame
= drag_info
.current_pointer_frame
;
5248 drag_info
.first_move
= false;
5250 show_verbose_time_cursor(drag_info
.current_pointer_frame
, 10);
5255 Editor::end_range_markerbar_op (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5257 Location
* newloc
= 0;
5261 if (!drag_info
.first_move
) {
5262 drag_range_markerbar_op (item
, event
);
5264 switch (range_marker_op
) {
5265 case CreateRangeMarker
:
5266 case CreateCDMarker
:
5268 begin_reversible_command (_("new range marker"));
5269 XMLNode
&before
= session
->locations()->get_state();
5270 session
->locations()->next_available_name(rangename
,"unnamed");
5271 if (range_marker_op
== CreateCDMarker
) {
5272 flags
= Location::IsRangeMarker
|Location::IsCDMarker
;
5273 cd_marker_bar_drag_rect
->hide();
5276 flags
= Location::IsRangeMarker
;
5277 range_bar_drag_rect
->hide();
5279 newloc
= new Location(temp_location
->start(), temp_location
->end(), rangename
, (Location::Flags
) flags
);
5280 session
->locations()->add (newloc
, true);
5281 XMLNode
&after
= session
->locations()->get_state();
5282 session
->add_command(new MementoCommand
<Locations
>(*(session
->locations()), &before
, &after
));
5283 commit_reversible_command ();
5285 range_marker_drag_rect
->hide();
5289 case CreateTransportMarker
:
5290 // popup menu to pick loop or punch
5291 new_transport_marker_context_menu (&event
->button
, item
);
5296 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5298 if (Keyboard::no_modifier_keys_pressed (&event
->button
) && range_marker_op
!= CreateCDMarker
) {
5303 start
= session
->locations()->first_mark_before (drag_info
.grab_frame
);
5304 end
= session
->locations()->first_mark_after (drag_info
.grab_frame
);
5306 if (end
== max_frames
) {
5307 end
= session
->current_end_frame ();
5311 start
= session
->current_start_frame ();
5314 switch (mouse_mode
) {
5316 /* find the two markers on either side and then make the selection from it */
5317 select_all_within (start
, end
, 0.0f
, FLT_MAX
, track_views
, Selection::Set
);
5321 /* find the two markers on either side of the click and make the range out of it */
5322 selection
->set (0, start
, end
);
5331 stop_canvas_autoscroll ();
5337 Editor::start_mouse_zoom (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5339 drag_info
.item
= item
;
5340 drag_info
.motion_callback
= &Editor::drag_mouse_zoom
;
5341 drag_info
.finished_callback
= &Editor::end_mouse_zoom
;
5343 start_grab (event
, zoom_cursor
);
5345 show_verbose_time_cursor (drag_info
.current_pointer_frame
, 10);
5349 Editor::drag_mouse_zoom (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5354 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
5355 snap_to (drag_info
.current_pointer_frame
);
5357 if (drag_info
.first_move
) {
5358 snap_to (drag_info
.grab_frame
);
5362 if (drag_info
.current_pointer_frame
== drag_info
.last_pointer_frame
) return;
5364 /* base start and end on initial click position */
5365 if (drag_info
.current_pointer_frame
< drag_info
.grab_frame
) {
5366 start
= drag_info
.current_pointer_frame
;
5367 end
= drag_info
.grab_frame
;
5369 end
= drag_info
.current_pointer_frame
;
5370 start
= drag_info
.grab_frame
;
5375 if (drag_info
.first_move
) {
5377 zoom_rect
->raise_to_top();
5380 reposition_zoom_rect(start
, end
);
5382 drag_info
.last_pointer_frame
= drag_info
.current_pointer_frame
;
5383 drag_info
.first_move
= false;
5385 show_verbose_time_cursor (drag_info
.current_pointer_frame
, 10);
5390 Editor::end_mouse_zoom (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5392 if (!drag_info
.first_move
) {
5393 drag_mouse_zoom (item
, event
);
5395 if (drag_info
.grab_frame
< drag_info
.last_pointer_frame
) {
5396 temporal_zoom_by_frame (drag_info
.grab_frame
, drag_info
.last_pointer_frame
, "mouse zoom");
5398 temporal_zoom_by_frame (drag_info
.last_pointer_frame
, drag_info
.grab_frame
, "mouse zoom");
5401 temporal_zoom_to_frame (false, drag_info
.grab_frame
);
5403 temporal_zoom_step (false);
5404 center_screen (drag_info.grab_frame);
5412 Editor::reposition_zoom_rect (nframes64_t start
, nframes64_t end
)
5414 double x1
= frame_to_pixel (start
);
5415 double x2
= frame_to_pixel (end
);
5416 double y2
= full_canvas_height
- 1.0;
5418 zoom_rect
->property_x1() = x1
;
5419 zoom_rect
->property_y1() = 1.0;
5420 zoom_rect
->property_x2() = x2
;
5421 zoom_rect
->property_y2() = y2
;
5425 Editor::start_rubberband_select (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5427 drag_info
.item
= item
;
5428 drag_info
.motion_callback
= &Editor::drag_rubberband_select
;
5429 drag_info
.finished_callback
= &Editor::end_rubberband_select
;
5431 start_grab (event
, cross_hair_cursor
);
5433 show_verbose_time_cursor (drag_info
.current_pointer_frame
, 10);
5437 Editor::drag_rubberband_select (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5444 /* use a bigger drag threshold than the default */
5446 if (abs ((int) (drag_info
.current_pointer_frame
- drag_info
.grab_frame
)) < 8) {
5450 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier()) && Config
->get_rubberbanding_snaps_to_grid()) {
5451 if (drag_info
.first_move
) {
5452 snap_to (drag_info
.grab_frame
);
5454 snap_to (drag_info
.current_pointer_frame
);
5457 /* base start and end on initial click position */
5459 if (drag_info
.current_pointer_frame
< drag_info
.grab_frame
) {
5460 start
= drag_info
.current_pointer_frame
;
5461 end
= drag_info
.grab_frame
;
5463 end
= drag_info
.current_pointer_frame
;
5464 start
= drag_info
.grab_frame
;
5467 if (drag_info
.current_pointer_y
< drag_info
.grab_y
) {
5468 y1
= drag_info
.current_pointer_y
;
5469 y2
= drag_info
.grab_y
;
5471 y2
= drag_info
.current_pointer_y
;
5472 y1
= drag_info
.grab_y
;
5476 if (start
!= end
|| y1
!= y2
) {
5478 double x1
= frame_to_pixel (start
);
5479 double x2
= frame_to_pixel (end
);
5481 rubberband_rect
->property_x1() = x1
;
5482 rubberband_rect
->property_y1() = y1
;
5483 rubberband_rect
->property_x2() = x2
;
5484 rubberband_rect
->property_y2() = y2
;
5486 rubberband_rect
->show();
5487 rubberband_rect
->raise_to_top();
5489 drag_info
.last_pointer_frame
= drag_info
.current_pointer_frame
;
5490 drag_info
.first_move
= false;
5492 show_verbose_time_cursor (drag_info
.current_pointer_frame
, 10);
5497 Editor::end_rubberband_select (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5499 if (!drag_info
.first_move
) {
5501 drag_rubberband_select (item
, event
);
5504 if (drag_info
.current_pointer_y
< drag_info
.grab_y
) {
5505 y1
= drag_info
.current_pointer_y
;
5506 y2
= drag_info
.grab_y
;
5508 y2
= drag_info
.current_pointer_y
;
5509 y1
= drag_info
.grab_y
;
5513 Selection::Operation op
= Keyboard::selection_type (event
->button
.state
);
5516 begin_reversible_command (_("rubberband selection"));
5518 if (drag_info
.grab_frame
< drag_info
.last_pointer_frame
) {
5519 commit
= select_all_within (drag_info
.grab_frame
, drag_info
.last_pointer_frame
, y1
, y2
, track_views
, op
);
5521 commit
= select_all_within (drag_info
.last_pointer_frame
, drag_info
.grab_frame
, y1
, y2
, track_views
, op
);
5525 commit_reversible_command ();
5529 if (!getenv("ARDOUR_SAE")) {
5530 selection
->clear_tracks();
5532 selection
->clear_regions();
5533 selection
->clear_points ();
5534 selection
->clear_lines ();
5537 rubberband_rect
->hide();
5542 Editor::mouse_rename_region (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5544 using namespace Gtkmm2ext
;
5546 ArdourPrompter
prompter (false);
5548 prompter
.set_prompt (_("Name for region:"));
5549 prompter
.set_initial_text (clicked_regionview
->region()->name());
5550 prompter
.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT
);
5551 prompter
.set_response_sensitive (Gtk::RESPONSE_ACCEPT
, false);
5552 prompter
.show_all ();
5553 switch (prompter
.run ()) {
5554 case Gtk::RESPONSE_ACCEPT
:
5556 prompter
.get_result(str
);
5558 clicked_regionview
->region()->set_name (str
);
5566 Editor::start_time_fx (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5568 drag_info
.item
= item
;
5569 drag_info
.motion_callback
= &Editor::time_fx_motion
;
5570 drag_info
.finished_callback
= &Editor::end_time_fx
;
5574 show_verbose_time_cursor (drag_info
.current_pointer_frame
, 10);
5578 Editor::time_fx_motion (ArdourCanvas::Item
*item
, GdkEvent
* event
)
5580 RegionView
* rv
= clicked_regionview
;
5582 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
5583 snap_to (drag_info
.current_pointer_frame
);
5586 if (drag_info
.current_pointer_frame
== drag_info
.last_pointer_frame
) {
5590 if (drag_info
.current_pointer_frame
> rv
->region()->position()) {
5591 rv
->get_time_axis_view().show_timestretch (rv
->region()->position(), drag_info
.current_pointer_frame
);
5594 drag_info
.last_pointer_frame
= drag_info
.current_pointer_frame
;
5595 drag_info
.first_move
= false;
5597 show_verbose_time_cursor (drag_info
.current_pointer_frame
, 10);
5601 Editor::end_time_fx (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5603 clicked_regionview
->get_time_axis_view().hide_timestretch ();
5605 if (drag_info
.first_move
) {
5609 if (drag_info
.last_pointer_frame
< clicked_regionview
->region()->position()) {
5610 /* backwards drag of the left edge - not usable */
5614 nframes64_t newlen
= drag_info
.last_pointer_frame
- clicked_regionview
->region()->position();
5615 #ifdef USE_RUBBERBAND
5616 float percentage
= (float) ((double) newlen
/ (double) clicked_regionview
->region()->length());
5618 float percentage
= (float) ((double) newlen
- (double) clicked_regionview
->region()->length()) / ((double) newlen
) * 100.0f
;
5621 begin_reversible_command (_("timestretch"));
5623 // XXX how do timeFX on multiple regions ?
5626 rs
.add (clicked_regionview
);
5628 if (time_stretch (rs
, percentage
) == 0) {
5629 session
->commit_reversible_command ();
5634 Editor::mouse_brush_insert_region (RegionView
* rv
, nframes64_t pos
)
5636 /* no brushing without a useful snap setting */
5639 AudioRegionView
* arv
= dynamic_cast<AudioRegionView
*>(rv
);
5642 switch (snap_mode
) {
5644 return; /* can't work because it allows region to be placed anywhere */
5649 switch (snap_type
) {
5657 /* don't brush a copy over the original */
5659 if (pos
== rv
->region()->position()) {
5663 RouteTimeAxisView
* atv
= dynamic_cast<RouteTimeAxisView
*>(&arv
->get_time_axis_view());
5665 if (atv
== 0 || !atv
->is_audio_track()) {
5669 boost::shared_ptr
<Playlist
> playlist
= atv
->playlist();
5670 double speed
= atv
->get_diskstream()->speed();
5672 XMLNode
&before
= playlist
->get_state();
5673 playlist
->add_region (boost::dynamic_pointer_cast
<AudioRegion
> (RegionFactory::create (arv
->audio_region())), (nframes64_t
) (pos
* speed
));
5674 XMLNode
&after
= playlist
->get_state();
5675 session
->add_command(new MementoCommand
<Playlist
>(*playlist
.get(), &before
, &after
));
5677 // playlist is frozen, so we have to update manually
5679 playlist
->Modified(); /* EMIT SIGNAL */
5683 Editor::track_height_step_timeout ()
5685 if (get_microseconds() - last_track_height_step_timestamp
< 250000) {
5686 current_stepping_trackview
= 0;