3 Copyright (C) 2000-2001 Paul Davis
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
30 #include <pbd/error.h>
31 #include <gtkmm2ext/utils.h>
32 #include <pbd/memento_command.h>
34 #include "ardour_ui.h"
36 #include "time_axis_view.h"
37 #include "audio_time_axis.h"
38 #include "audio_region_view.h"
40 #include "streamview.h"
41 #include "region_gain_line.h"
42 #include "automation_time_axis.h"
45 #include "selection.h"
48 #include "rgb_macros.h"
50 #include <ardour/types.h>
51 #include <ardour/profile.h>
52 #include <ardour/route.h>
53 #include <ardour/audio_track.h>
54 #include <ardour/audio_diskstream.h>
55 #include <ardour/playlist.h>
56 #include <ardour/audioplaylist.h>
57 #include <ardour/audioregion.h>
58 #include <ardour/dB.h>
59 #include <ardour/utils.h>
60 #include <ardour/region_factory.h>
67 using namespace ARDOUR
;
71 using namespace Editing
;
74 Editor::mouse_frame (nframes64_t
& where
, bool& in_track_canvas
) const
78 Gdk::ModifierType mask
;
79 Glib::RefPtr
<Gdk::Window
> canvas_window
= const_cast<Editor
*>(this)->track_canvas
->get_window();
80 Glib::RefPtr
<const Gdk::Window
> pointer_window
;
86 pointer_window
= canvas_window
->get_pointer (x
, y
, mask
);
88 if (pointer_window
== track_canvas
->get_bin_window()) {
91 in_track_canvas
= true;
94 in_track_canvas
= false;
99 event
.type
= GDK_BUTTON_RELEASE
;
103 where
= event_frame (&event
, 0, 0);
108 Editor::event_frame (GdkEvent
* event
, double* pcx
, double* pcy
) const
122 switch (event
->type
) {
123 case GDK_BUTTON_RELEASE
:
124 case GDK_BUTTON_PRESS
:
125 case GDK_2BUTTON_PRESS
:
126 case GDK_3BUTTON_PRESS
:
128 *pcx
= event
->button
.x
;
129 *pcy
= event
->button
.y
;
130 _trackview_group
->w2i(*pcx
, *pcy
);
132 case GDK_MOTION_NOTIFY
:
134 *pcx
= event
->motion
.x
;
135 *pcy
= event
->motion
.y
;
136 _trackview_group
->w2i(*pcx
, *pcy
);
138 case GDK_ENTER_NOTIFY
:
139 case GDK_LEAVE_NOTIFY
:
140 track_canvas
->w2c(event
->crossing
.x
, event
->crossing
.y
, *pcx
, *pcy
);
143 case GDK_KEY_RELEASE
:
144 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
147 warning
<< string_compose (_("Editor::event_frame() used on unhandled event type %1"), event
->type
) << endmsg
;
151 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
152 position is negative (as can be the case with motion events in particular),
153 the frame location is always positive.
156 return pixel_to_frame (*pcx
);
160 Editor::mouse_mode_toggled (MouseMode m
)
162 if (ignore_mouse_mode_toggle
) {
168 if (mouse_select_button
.get_active()) {
174 if (mouse_move_button
.get_active()) {
180 if (mouse_gain_button
.get_active()) {
186 if (mouse_zoom_button
.get_active()) {
192 if (mouse_timefx_button
.get_active()) {
198 if (mouse_audition_button
.get_active()) {
209 Editor::which_grabber_cursor ()
211 switch (_edit_point
) {
213 return grabber_edit_point_cursor
;
218 return grabber_cursor
;
222 Editor::set_canvas_cursor ()
224 switch (mouse_mode
) {
226 current_canvas_cursor
= selector_cursor
;
230 current_canvas_cursor
= which_grabber_cursor();
234 current_canvas_cursor
= cross_hair_cursor
;
238 current_canvas_cursor
= zoom_cursor
;
242 current_canvas_cursor
= time_fx_cursor
; // just use playhead
246 current_canvas_cursor
= speaker_cursor
;
251 track_canvas
->get_window()->set_cursor(*current_canvas_cursor
);
256 Editor::set_mouse_mode (MouseMode m
, bool force
)
258 if (drag_info
.item
) {
262 if (!force
&& m
== mouse_mode
) {
270 if (mouse_mode
!= MouseRange
) {
272 /* in all modes except range, hide the range selection,
273 show the object (region) selection.
276 for (RegionSelection::iterator i
= selection
->regions
.begin(); i
!= selection
->regions
.end(); ++i
) {
277 (*i
)->set_should_show_selection (true);
279 for (TrackViewList::iterator i
= track_views
.begin(); i
!= track_views
.end(); ++i
) {
280 (*i
)->hide_selection ();
286 in range mode,show the range selection.
289 for (TrackSelection::iterator i
= selection
->tracks
.begin(); i
!= selection
->tracks
.end(); ++i
) {
290 if ((*i
)->get_selected()) {
291 (*i
)->show_selection (selection
->time
);
296 /* XXX the hack of unsetting all other buttons should go
297 away once GTK2 allows us to use regular radio buttons drawn like
298 normal buttons, rather than my silly GroupedButton hack.
301 ignore_mouse_mode_toggle
= true;
303 switch (mouse_mode
) {
305 mouse_select_button
.set_active (true);
309 mouse_move_button
.set_active (true);
313 mouse_gain_button
.set_active (true);
317 mouse_zoom_button
.set_active (true);
321 mouse_timefx_button
.set_active (true);
325 mouse_audition_button
.set_active (true);
329 ignore_mouse_mode_toggle
= false;
331 set_canvas_cursor ();
335 Editor::step_mouse_mode (bool next
)
337 switch (current_mouse_mode()) {
340 if (Profile
->get_sae()) {
341 set_mouse_mode (MouseZoom
);
343 set_mouse_mode (MouseRange
);
346 set_mouse_mode (MouseTimeFX
);
351 if (next
) set_mouse_mode (MouseZoom
);
352 else set_mouse_mode (MouseObject
);
357 if (Profile
->get_sae()) {
358 set_mouse_mode (MouseTimeFX
);
360 set_mouse_mode (MouseGain
);
363 if (Profile
->get_sae()) {
364 set_mouse_mode (MouseObject
);
366 set_mouse_mode (MouseRange
);
372 if (next
) set_mouse_mode (MouseTimeFX
);
373 else set_mouse_mode (MouseZoom
);
378 set_mouse_mode (MouseAudition
);
380 if (Profile
->get_sae()) {
381 set_mouse_mode (MouseZoom
);
383 set_mouse_mode (MouseGain
);
389 if (next
) set_mouse_mode (MouseObject
);
390 else set_mouse_mode (MouseTimeFX
);
396 Editor::button_selection (ArdourCanvas::Item
* item
, GdkEvent
* event
, ItemType item_type
)
398 /* in object/audition/timefx/gain-automation mode,
399 any button press sets the selection if the object
400 can be selected. this is a bit of hack, because
401 we want to avoid this if the mouse operation is a
404 note: not dbl-click or triple-click
407 if (((mouse_mode
!= MouseObject
) &&
408 (mouse_mode
!= MouseAudition
|| item_type
!= RegionItem
) &&
409 (mouse_mode
!= MouseTimeFX
|| item_type
!= RegionItem
) &&
410 (mouse_mode
!= MouseGain
) &&
411 (mouse_mode
!= MouseRange
)) ||
413 ((event
->type
!= GDK_BUTTON_PRESS
&& event
->type
!= GDK_BUTTON_RELEASE
) || event
->button
.button
> 3)) {
418 if (event
->type
== GDK_BUTTON_PRESS
|| event
->type
== GDK_BUTTON_RELEASE
) {
420 if ((event
->button
.state
& Keyboard::RelevantModifierKeyMask
) && event
->button
.button
!= 1) {
422 /* almost no selection action on modified button-2 or button-3 events */
424 if (item_type
!= RegionItem
&& event
->button
.button
!= 2) {
430 Selection::Operation op
= Keyboard::selection_type (event
->button
.state
);
431 bool press
= (event
->type
== GDK_BUTTON_PRESS
);
433 // begin_reversible_command (_("select on click"));
437 if (mouse_mode
!= MouseRange
) {
438 set_selected_regionview_from_click (press
, op
, true);
439 } else if (event
->type
== GDK_BUTTON_PRESS
) {
440 set_selected_track_as_side_effect ();
444 case RegionViewNameHighlight
:
446 if (mouse_mode
!= MouseRange
) {
447 set_selected_regionview_from_click (press
, op
, true);
448 } else if (event
->type
== GDK_BUTTON_PRESS
) {
449 set_selected_track_as_side_effect ();
453 case FadeInHandleItem
:
455 case FadeOutHandleItem
:
457 if (mouse_mode
!= MouseRange
) {
458 set_selected_regionview_from_click (press
, op
, true);
459 } else if (event
->type
== GDK_BUTTON_PRESS
) {
460 set_selected_track_as_side_effect ();
464 case GainAutomationControlPointItem
:
465 case PanAutomationControlPointItem
:
466 case RedirectAutomationControlPointItem
:
467 set_selected_track_as_side_effect ();
468 if (mouse_mode
!= MouseRange
) {
469 set_selected_control_point_from_click (op
, false);
474 /* for context click or range selection, select track */
475 if (event
->button
.button
== 3) {
476 set_selected_track_as_side_effect ();
477 } else if (event
->type
== GDK_BUTTON_PRESS
&& mouse_mode
== MouseRange
) {
478 set_selected_track_as_side_effect ();
482 case AutomationTrackItem
:
483 set_selected_track_as_side_effect (true);
491 const static double ZERO_GAIN_FRACTION
= gain_to_slider_position(dB_to_coefficient(0.0));
494 Editor::button_press_handler (ArdourCanvas::Item
* item
, GdkEvent
* event
, ItemType item_type
)
496 Glib::RefPtr
<Gdk::Window
> canvas_window
= const_cast<Editor
*>(this)->track_canvas
->get_window();
499 Glib::RefPtr
<const Gdk::Window
> pointer_window
;
502 Gdk::ModifierType mask
;
504 pointer_window
= canvas_window
->get_pointer (x
, y
, mask
);
506 if (pointer_window
== track_canvas
->get_bin_window()) {
507 track_canvas
->window_to_world (x
, y
, wx
, wy
);
508 allow_vertical_scroll
= true;
510 allow_vertical_scroll
= false;
514 track_canvas
->grab_focus();
516 if (session
&& session
->actively_recording()) {
520 button_selection (item
, event
, item_type
);
522 //ctrl-drag or right-click-drag on a "range" ruler should start a range drag
523 if (event
->type
== GDK_BUTTON_PRESS
) {
524 if (event
->button
.button
== 3 || ( (event
->button
.button
== 1) && (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
) ))) {
525 if (item_type
== TransportMarkerBarItem
) {
526 start_range_markerbar_op (item
, event
, CreateTransportMarker
);
529 if (item_type
== RangeMarkerBarItem
) {
530 start_range_markerbar_op (item
, event
, CreateRangeMarker
);
533 if (item_type
== CdMarkerBarItem
) {
534 start_range_markerbar_op (item
, event
, CreateCDMarker
);
540 if (drag_info
.item
== 0 &&
541 (Keyboard::is_delete_event (&event
->button
) ||
542 Keyboard::is_context_menu_event (&event
->button
) ||
543 Keyboard::is_edit_event (&event
->button
))) {
545 /* handled by button release */
549 switch (event
->button
.button
) {
552 if (event
->type
== GDK_BUTTON_PRESS
) {
554 if (drag_info
.item
) {
555 drag_info
.item
->ungrab (event
->button
.time
);
558 /* single mouse clicks on any of these item types operate
559 independent of mouse mode, mostly because they are
560 not on the main track canvas or because we want
565 case PlayheadCursorItem
:
566 start_cursor_grab (item
, event
);
570 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::ModifierMask(Keyboard::PrimaryModifier
|Keyboard::TertiaryModifier
))) {
571 hide_marker (item
, event
);
573 start_marker_grab (item
, event
);
577 case TempoMarkerItem
:
578 if (Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::CopyModifier
)) {
579 start_tempo_marker_copy_grab (item
, event
);
581 start_tempo_marker_grab (item
, event
);
585 case MeterMarkerItem
:
586 if (Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::CopyModifier
)) {
587 start_meter_marker_copy_grab (item
, event
);
589 start_meter_marker_grab (item
, event
);
596 case TransportMarkerBarItem
:
597 case RangeMarkerBarItem
:
598 case CdMarkerBarItem
:
599 if (!Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
600 start_cursor_grab_no_stop(&playhead_cursor
->canvas_item
, event
);
610 switch (mouse_mode
) {
613 case StartSelectionTrimItem
:
614 start_selection_op (item
, event
, SelectionStartTrim
);
617 case EndSelectionTrimItem
:
618 start_selection_op (item
, event
, SelectionEndTrim
);
622 if (Keyboard::modifier_state_contains
623 (event
->button
.state
, Keyboard::ModifierMask(Keyboard::SecondaryModifier
))) {
624 // contains and not equals because I can't use alt as a modifier alone.
625 start_selection_grab (item
, event
);
626 } else if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
627 /* grab selection for moving */
628 start_selection_op (item
, event
, SelectionMove
);
630 /* this was debated, but decided the more common action was to
631 make a new selection */
632 start_selection_op (item
, event
, CreateSelection
);
637 start_selection_op (item
, event
, CreateSelection
);
643 if (Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::ModifierMask(Keyboard::PrimaryModifier
|Keyboard::SecondaryModifier
)) &&
644 event
->type
== GDK_BUTTON_PRESS
) {
646 start_rubberband_select (item
, event
);
648 } else if (event
->type
== GDK_BUTTON_PRESS
) {
651 case FadeInHandleItem
:
652 start_fade_in_grab (item
, event
);
655 case FadeOutHandleItem
:
656 start_fade_out_grab (item
, event
);
660 if (Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::CopyModifier
)) {
661 start_region_copy_grab (item
, event
);
662 } else if (Keyboard::the_keyboard().key_is_down (GDK_b
)) {
663 start_region_brush_grab (item
, event
);
665 start_region_grab (item
, event
);
669 case RegionViewNameHighlight
:
670 start_trim (item
, event
);
675 /* rename happens on edit clicks */
676 start_trim (clicked_regionview
->get_name_highlight(), event
);
680 case GainAutomationControlPointItem
:
681 case PanAutomationControlPointItem
:
682 case RedirectAutomationControlPointItem
:
683 start_control_point_grab (item
, event
);
687 case GainAutomationLineItem
:
688 case PanAutomationLineItem
:
689 case RedirectAutomationLineItem
:
690 start_line_grab_from_line (item
, event
);
695 case AutomationTrackItem
:
696 start_rubberband_select (item
, event
);
699 /* <CMT Additions> */
700 case ImageFrameHandleStartItem
:
701 imageframe_start_handle_op(item
, event
) ;
704 case ImageFrameHandleEndItem
:
705 imageframe_end_handle_op(item
, event
) ;
708 case MarkerViewHandleStartItem
:
709 markerview_item_start_handle_op(item
, event
) ;
712 case MarkerViewHandleEndItem
:
713 markerview_item_end_handle_op(item
, event
) ;
716 /* </CMT Additions> */
718 /* <CMT Additions> */
720 start_markerview_grab(item
, event
) ;
723 start_imageframe_grab(item
, event
) ;
725 /* </CMT Additions> */
741 /* start a grab so that if we finish after moving
742 we can tell what happened.
744 drag_info
.item
= item
;
745 drag_info
.motion_callback
= &Editor::region_gain_motion_callback
;
746 drag_info
.finished_callback
= 0;
747 start_grab (event
, current_canvas_cursor
);
750 case GainControlPointItem
:
751 start_control_point_grab (item
, event
);
755 start_line_grab_from_line (item
, event
);
758 case GainAutomationControlPointItem
:
759 case PanAutomationControlPointItem
:
760 case RedirectAutomationControlPointItem
:
761 start_control_point_grab (item
, event
);
772 case GainAutomationControlPointItem
:
773 case PanAutomationControlPointItem
:
774 case RedirectAutomationControlPointItem
:
775 start_control_point_grab (item
, event
);
778 case GainAutomationLineItem
:
779 case PanAutomationLineItem
:
780 case RedirectAutomationLineItem
:
781 start_line_grab_from_line (item
, event
);
785 // XXX need automation mode to identify which
787 // start_line_grab_from_regionview (item, event);
797 if (event
->type
== GDK_BUTTON_PRESS
) {
798 start_mouse_zoom (item
, event
);
805 if (item_type
== RegionItem
) {
806 start_time_fx (item
, event
);
813 scrub_reverse_distance
= 0;
814 last_scrub_x
= event
->button
.x
;
815 scrubbing_direction
= 0;
816 track_canvas
->get_window()->set_cursor (*transparent_cursor
);
817 /* rest handled in motion & release */
826 switch (mouse_mode
) {
828 if (event
->type
== GDK_BUTTON_PRESS
) {
831 if (Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::CopyModifier
)) {
832 start_region_copy_grab (item
, event
);
834 start_region_grab (item
, event
);
839 case GainAutomationControlPointItem
:
840 case PanAutomationControlPointItem
:
841 case RedirectAutomationControlPointItem
:
842 start_control_point_grab (item
, event
);
853 case RegionViewNameHighlight
:
854 start_trim (item
, event
);
859 start_trim (clicked_regionview
->get_name_highlight(), event
);
870 if (event
->type
== GDK_BUTTON_PRESS
) {
871 /* relax till release */
878 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
879 temporal_zoom_session();
881 temporal_zoom_to_frame (true, event_frame(event
));
904 Editor::button_release_handler (ArdourCanvas::Item
* item
, GdkEvent
* event
, ItemType item_type
)
906 nframes64_t where
= event_frame (event
, 0, 0);
907 AutomationTimeAxisView
* atv
= 0;
909 /* no action if we're recording */
911 if (session
&& session
->actively_recording()) {
915 /* first, see if we're finishing a drag ... */
917 if (drag_info
.item
) {
918 if (end_grab (item
, event
)) {
919 /* grab dragged, so do nothing else */
924 button_selection (item
, event
, item_type
);
926 /* edit events get handled here */
928 if (drag_info
.item
== 0 && Keyboard::is_edit_event (&event
->button
)) {
934 case TempoMarkerItem
:
935 edit_tempo_marker (item
);
938 case MeterMarkerItem
:
939 edit_meter_marker (item
);
943 if (clicked_regionview
->name_active()) {
944 return mouse_rename_region (item
, event
);
954 /* context menu events get handled here */
956 if (Keyboard::is_context_menu_event (&event
->button
)) {
958 if (drag_info
.item
== 0) {
960 /* no matter which button pops up the context menu, tell the menu
961 widget to use button 1 to drive menu selection.
966 case FadeInHandleItem
:
968 case FadeOutHandleItem
:
969 popup_fade_context_menu (1, event
->button
.time
, item
, item_type
);
973 popup_track_context_menu (1, event
->button
.time
, item_type
, false, where
);
977 case RegionViewNameHighlight
:
979 popup_track_context_menu (1, event
->button
.time
, item_type
, false, where
);
983 popup_track_context_menu (1, event
->button
.time
, item_type
, true, where
);
986 case AutomationTrackItem
:
987 popup_track_context_menu (1, event
->button
.time
, item_type
, false, where
);
991 case RangeMarkerBarItem
:
992 case TransportMarkerBarItem
:
993 case CdMarkerBarItem
:
996 popup_ruler_menu (where
, item_type
);
1000 marker_context_menu (&event
->button
, item
);
1003 case TempoMarkerItem
:
1004 tm_marker_context_menu (&event
->button
, item
);
1007 case MeterMarkerItem
:
1008 tm_marker_context_menu (&event
->button
, item
);
1011 case CrossfadeViewItem
:
1012 popup_track_context_menu (1, event
->button
.time
, item_type
, false, where
);
1015 /* <CMT Additions> */
1016 case ImageFrameItem
:
1017 popup_imageframe_edit_menu(1, event
->button
.time
, item
, true) ;
1019 case ImageFrameTimeAxisItem
:
1020 popup_imageframe_edit_menu(1, event
->button
.time
, item
, false) ;
1022 case MarkerViewItem
:
1023 popup_marker_time_axis_edit_menu(1, event
->button
.time
, item
, true) ;
1025 case MarkerTimeAxisItem
:
1026 popup_marker_time_axis_edit_menu(1, event
->button
.time
, item
, false) ;
1028 /* <CMT Additions> */
1039 /* delete events get handled here */
1041 if (drag_info
.item
== 0 && Keyboard::is_delete_event (&event
->button
)) {
1043 switch (item_type
) {
1044 case TempoMarkerItem
:
1045 remove_tempo_marker (item
);
1048 case MeterMarkerItem
:
1049 remove_meter_marker (item
);
1053 remove_marker (*item
, event
);
1057 if (mouse_mode
== MouseObject
) {
1058 remove_clicked_region ();
1062 case GainControlPointItem
:
1063 if (mouse_mode
== MouseGain
) {
1064 remove_gain_control_point (item
, event
);
1068 case GainAutomationControlPointItem
:
1069 case PanAutomationControlPointItem
:
1070 case RedirectAutomationControlPointItem
:
1071 remove_control_point (item
, event
);
1080 switch (event
->button
.button
) {
1083 switch (item_type
) {
1084 /* see comments in button_press_handler */
1085 case PlayheadCursorItem
:
1088 case GainAutomationLineItem
:
1089 case PanAutomationLineItem
:
1090 case RedirectAutomationLineItem
:
1091 case StartSelectionTrimItem
:
1092 case EndSelectionTrimItem
:
1096 if (!_dragging_playhead
) {
1097 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
1098 snap_to (where
, 0, true);
1100 mouse_add_new_marker (where
);
1104 case CdMarkerBarItem
:
1105 if (!_dragging_playhead
) {
1106 // if we get here then a dragged range wasn't done
1107 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
1108 snap_to (where
, 0, true);
1110 mouse_add_new_marker (where
, true);
1115 if (!_dragging_playhead
) {
1116 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
1119 mouse_add_new_tempo_event (where
);
1124 if (!_dragging_playhead
) {
1125 mouse_add_new_meter_event (pixel_to_frame (event
->button
.x
));
1134 switch (mouse_mode
) {
1136 switch (item_type
) {
1137 case AutomationTrackItem
:
1138 atv
= dynamic_cast<AutomationTimeAxisView
*>(clicked_trackview
);
1140 atv
->add_automation_event (item
, event
, where
, event
->button
.y
);
1152 // Gain only makes sense for audio regions
1154 if (!dynamic_cast<AudioRegionView
*>(clicked_regionview
)) {
1158 switch (item_type
) {
1160 /* check that we didn't drag before releasing, since
1161 its really annoying to create new control
1162 points when doing this.
1164 if (drag_info
.first_move
) {
1165 dynamic_cast<AudioRegionView
*>(clicked_regionview
)->add_gain_point_event (item
, event
);
1170 case AutomationTrackItem
:
1171 dynamic_cast<AutomationTimeAxisView
*>(clicked_trackview
)->
1172 add_automation_event (item
, event
, where
, event
->button
.y
);
1182 track_canvas
->get_window()->set_cursor (*current_canvas_cursor
);
1183 if (scrubbing_direction
== 0) {
1184 /* no drag, just a click */
1185 switch (item_type
) {
1187 play_selected_region ();
1193 /* make sure we stop */
1194 session
->request_transport_speed (0.0);
1208 switch (mouse_mode
) {
1211 switch (item_type
) {
1213 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::TertiaryModifier
)) {
1215 } else if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::ModifierMask (Keyboard::TertiaryModifier
|Keyboard::SecondaryModifier
))) {
1218 // Button2 click is unused
1231 // x_style_paste (where, 1.0);
1251 Editor::enter_handler (ArdourCanvas::Item
* item
, GdkEvent
* event
, ItemType item_type
)
1257 if (last_item_entered
!= item
) {
1258 last_item_entered
= item
;
1259 last_item_entered_n
= 0;
1262 switch (item_type
) {
1263 case GainControlPointItem
:
1264 if (mouse_mode
== MouseGain
) {
1265 cp
= static_cast<ControlPoint
*>(item
->get_data ("control_point"));
1266 cp
->set_visible (true);
1270 at_y
= cp
->get_y ();
1271 cp
->item
->i2w (at_x
, at_y
);
1275 fraction
= 1.0 - (cp
->get_y() / cp
->line
.height());
1277 if (is_drawable() && !_scrubbing
) {
1278 track_canvas
->get_window()->set_cursor (*fader_cursor
);
1281 last_item_entered_n
++;
1282 set_verbose_canvas_cursor (cp
->line
.get_verbose_cursor_string (fraction
), at_x
, at_y
);
1283 if (last_item_entered_n
< 10) {
1284 show_verbose_canvas_cursor ();
1289 case GainAutomationControlPointItem
:
1290 case PanAutomationControlPointItem
:
1291 case RedirectAutomationControlPointItem
:
1292 if (mouse_mode
== MouseGain
|| mouse_mode
== MouseObject
) {
1293 cp
= static_cast<ControlPoint
*>(item
->get_data ("control_point"));
1294 cp
->set_visible (true);
1298 at_y
= cp
->get_y ();
1299 cp
->item
->i2w (at_x
, at_y
);
1303 fraction
= 1.0 - (cp
->get_y() / cp
->line
.height());
1305 set_verbose_canvas_cursor (cp
->line
.get_verbose_cursor_string (fraction
), at_x
, at_y
);
1306 show_verbose_canvas_cursor ();
1308 if (is_drawable()) {
1309 track_canvas
->get_window()->set_cursor (*fader_cursor
);
1315 if (mouse_mode
== MouseGain
) {
1316 ArdourCanvas::Line
*line
= dynamic_cast<ArdourCanvas::Line
*> (item
);
1318 line
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine
.get();
1319 if (is_drawable()) {
1320 track_canvas
->get_window()->set_cursor (*fader_cursor
);
1325 case GainAutomationLineItem
:
1326 case RedirectAutomationLineItem
:
1327 case PanAutomationLineItem
:
1328 if (mouse_mode
== MouseGain
|| mouse_mode
== MouseObject
) {
1330 ArdourCanvas::Line
*line
= dynamic_cast<ArdourCanvas::Line
*> (item
);
1332 line
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine
.get();
1334 if (is_drawable()) {
1335 track_canvas
->get_window()->set_cursor (*fader_cursor
);
1340 case RegionViewNameHighlight
:
1341 if (is_drawable() && mouse_mode
== MouseObject
) {
1342 track_canvas
->get_window()->set_cursor (*trimmer_cursor
);
1346 case StartSelectionTrimItem
:
1347 case EndSelectionTrimItem
:
1348 /* <CMT Additions> */
1349 case ImageFrameHandleStartItem
:
1350 case ImageFrameHandleEndItem
:
1351 case MarkerViewHandleStartItem
:
1352 case MarkerViewHandleEndItem
:
1353 /* </CMT Additions> */
1355 if (is_drawable()) {
1356 track_canvas
->get_window()->set_cursor (*trimmer_cursor
);
1360 case PlayheadCursorItem
:
1361 if (is_drawable()) {
1362 switch (_edit_point
) {
1364 track_canvas
->get_window()->set_cursor (*grabber_edit_point_cursor
);
1367 track_canvas
->get_window()->set_cursor (*grabber_cursor
);
1373 case RegionViewName
:
1375 /* when the name is not an active item, the entire name highlight is for trimming */
1377 if (!reinterpret_cast<RegionView
*> (item
->get_data ("regionview"))->name_active()) {
1378 if (mouse_mode
== MouseObject
&& is_drawable()) {
1379 track_canvas
->get_window()->set_cursor (*trimmer_cursor
);
1385 case AutomationTrackItem
:
1386 if (is_drawable()) {
1387 Gdk::Cursor
*cursor
;
1388 switch (mouse_mode
) {
1390 cursor
= selector_cursor
;
1393 cursor
= zoom_cursor
;
1396 cursor
= cross_hair_cursor
;
1400 track_canvas
->get_window()->set_cursor (*cursor
);
1402 AutomationTimeAxisView
* atv
;
1403 if ((atv
= static_cast<AutomationTimeAxisView
*>(item
->get_data ("trackview"))) != 0) {
1404 clear_entered_track
= false;
1405 set_entered_track (atv
);
1411 case RangeMarkerBarItem
:
1412 case TransportMarkerBarItem
:
1413 case CdMarkerBarItem
:
1416 if (is_drawable()) {
1417 track_canvas
->get_window()->set_cursor (*timebar_cursor
);
1422 if ((marker
= static_cast<Marker
*> (item
->get_data ("marker"))) == 0) {
1425 entered_marker
= marker
;
1426 marker
->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker
.get());
1428 case MeterMarkerItem
:
1429 case TempoMarkerItem
:
1430 if (is_drawable()) {
1431 track_canvas
->get_window()->set_cursor (*timebar_cursor
);
1434 case FadeInHandleItem
:
1435 case FadeOutHandleItem
:
1436 if (mouse_mode
== MouseObject
) {
1437 ArdourCanvas::SimpleRect
*rect
= dynamic_cast<ArdourCanvas::SimpleRect
*> (item
);
1439 rect
->property_fill_color_rgba() = 0;
1440 rect
->property_outline_pixels() = 1;
1449 /* second pass to handle entered track status in a comprehensible way.
1452 switch (item_type
) {
1454 case GainAutomationLineItem
:
1455 case RedirectAutomationLineItem
:
1456 case PanAutomationLineItem
:
1457 case GainControlPointItem
:
1458 case GainAutomationControlPointItem
:
1459 case PanAutomationControlPointItem
:
1460 case RedirectAutomationControlPointItem
:
1461 /* these do not affect the current entered track state */
1462 clear_entered_track
= false;
1465 case AutomationTrackItem
:
1466 /* handled above already */
1470 set_entered_track (0);
1478 Editor::leave_handler (ArdourCanvas::Item
* item
, GdkEvent
* event
, ItemType item_type
)
1487 switch (item_type
) {
1488 case GainControlPointItem
:
1489 case GainAutomationControlPointItem
:
1490 case PanAutomationControlPointItem
:
1491 case RedirectAutomationControlPointItem
:
1492 cp
= reinterpret_cast<ControlPoint
*>(item
->get_data ("control_point"));
1493 if (cp
->line
.npoints() > 1) {
1494 if (!cp
->selected
) {
1495 cp
->set_visible (false);
1499 if (is_drawable()) {
1500 track_canvas
->get_window()->set_cursor (*current_canvas_cursor
);
1503 hide_verbose_canvas_cursor ();
1506 case RegionViewNameHighlight
:
1507 case StartSelectionTrimItem
:
1508 case EndSelectionTrimItem
:
1509 case PlayheadCursorItem
:
1510 /* <CMT Additions> */
1511 case ImageFrameHandleStartItem
:
1512 case ImageFrameHandleEndItem
:
1513 case MarkerViewHandleStartItem
:
1514 case MarkerViewHandleEndItem
:
1515 /* </CMT Additions> */
1516 if (is_drawable()) {
1517 track_canvas
->get_window()->set_cursor (*current_canvas_cursor
);
1522 case GainAutomationLineItem
:
1523 case RedirectAutomationLineItem
:
1524 case PanAutomationLineItem
:
1525 al
= reinterpret_cast<AutomationLine
*> (item
->get_data ("line"));
1527 ArdourCanvas::Line
*line
= dynamic_cast<ArdourCanvas::Line
*> (item
);
1529 line
->property_fill_color_rgba() = al
->get_line_color();
1531 if (is_drawable()) {
1532 track_canvas
->get_window()->set_cursor (*current_canvas_cursor
);
1536 case RegionViewName
:
1537 /* see enter_handler() for notes */
1538 if (!reinterpret_cast<RegionView
*> (item
->get_data ("regionview"))->name_active()) {
1539 if (is_drawable() && mouse_mode
== MouseObject
) {
1540 track_canvas
->get_window()->set_cursor (*current_canvas_cursor
);
1545 case RangeMarkerBarItem
:
1546 case TransportMarkerBarItem
:
1547 case CdMarkerBarItem
:
1551 if (is_drawable()) {
1552 track_canvas
->get_window()->set_cursor (*current_canvas_cursor
);
1557 if ((marker
= static_cast<Marker
*> (item
->get_data ("marker"))) == 0) {
1561 if ((loc
= find_location_from_marker (marker
, is_start
)) != 0) {
1562 location_flags_changed (loc
, this);
1565 case MeterMarkerItem
:
1566 case TempoMarkerItem
:
1568 if (is_drawable()) {
1569 track_canvas
->get_window()->set_cursor (*timebar_cursor
);
1574 case FadeInHandleItem
:
1575 case FadeOutHandleItem
:
1576 rv
= static_cast<RegionView
*>(item
->get_data ("regionview"));
1578 ArdourCanvas::SimpleRect
*rect
= dynamic_cast<ArdourCanvas::SimpleRect
*> (item
);
1580 rect
->property_fill_color_rgba() = rv
->get_fill_color();
1581 rect
->property_outline_pixels() = 0;
1586 case AutomationTrackItem
:
1587 if (is_drawable()) {
1588 track_canvas
->get_window()->set_cursor (*current_canvas_cursor
);
1589 clear_entered_track
= true;
1590 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track
));
1602 Editor::left_automation_track ()
1604 if (clear_entered_track
) {
1605 set_entered_track (0);
1606 clear_entered_track
= false;
1616 if (scrubbing_direction
== 0) {
1618 session
->request_locate (drag_info
.current_pointer_frame
, false);
1619 session
->request_transport_speed (0.1);
1620 scrubbing_direction
= 1;
1624 if (last_scrub_x
> drag_info
.current_pointer_x
) {
1626 /* pointer moved to the left */
1628 if (scrubbing_direction
> 0) {
1630 /* we reversed direction to go backwards */
1633 scrub_reverse_distance
+= (int) (last_scrub_x
- drag_info
.current_pointer_x
);
1637 /* still moving to the left (backwards) */
1639 scrub_reversals
= 0;
1640 scrub_reverse_distance
= 0;
1642 delta
= 0.01 * (last_scrub_x
- drag_info
.current_pointer_x
);
1643 session
->request_transport_speed (session
->transport_speed() - delta
);
1647 /* pointer moved to the right */
1649 if (scrubbing_direction
< 0) {
1650 /* we reversed direction to go forward */
1653 scrub_reverse_distance
+= (int) (drag_info
.current_pointer_x
- last_scrub_x
);
1656 /* still moving to the right */
1658 scrub_reversals
= 0;
1659 scrub_reverse_distance
= 0;
1661 delta
= 0.01 * (drag_info
.current_pointer_x
- last_scrub_x
);
1662 session
->request_transport_speed (session
->transport_speed() + delta
);
1666 /* if there have been more than 2 opposite motion moves detected, or one that moves
1667 back more than 10 pixels, reverse direction
1670 if (scrub_reversals
>= 2 || scrub_reverse_distance
> 10) {
1672 if (scrubbing_direction
> 0) {
1673 /* was forwards, go backwards */
1674 session
->request_transport_speed (-0.1);
1675 scrubbing_direction
= -1;
1677 /* was backwards, go forwards */
1678 session
->request_transport_speed (0.1);
1679 scrubbing_direction
= 1;
1682 scrub_reverse_distance
= 0;
1683 scrub_reversals
= 0;
1687 last_scrub_x
= drag_info
.current_pointer_x
;
1691 Editor::motion_handler (ArdourCanvas::Item
* item
, GdkEvent
* event
, ItemType item_type
, bool from_autoscroll
)
1693 if (event
->motion
.is_hint
) {
1696 /* We call this so that MOTION_NOTIFY events continue to be
1697 delivered to the canvas. We need to do this because we set
1698 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1699 the density of the events, at the expense of a round-trip
1700 to the server. Given that this will mostly occur on cases
1701 where DISPLAY = :0.0, and given the cost of what the motion
1702 event might do, its a good tradeoff.
1705 track_canvas
->get_pointer (x
, y
);
1708 if (current_stepping_trackview
) {
1709 /* don't keep the persistent stepped trackview if the mouse moves */
1710 current_stepping_trackview
= 0;
1711 step_timeout
.disconnect ();
1714 if (session
&& session
->actively_recording()) {
1715 /* Sorry. no dragging stuff around while we record */
1719 drag_info
.item_type
= item_type
;
1720 drag_info
.last_pointer_x
= drag_info
.current_pointer_x
;
1721 drag_info
.last_pointer_y
= drag_info
.current_pointer_y
;
1722 drag_info
.current_pointer_frame
= event_frame (event
, &drag_info
.current_pointer_x
,
1723 &drag_info
.current_pointer_y
);
1726 switch (mouse_mode
) {
1737 if (!from_autoscroll
&& drag_info
.item
) {
1738 /* item != 0 is the best test i can think of for dragging.
1740 if (!drag_info
.move_threshold_passed
) {
1742 bool x_threshold_passed
= (::llabs ((nframes64_t
) (drag_info
.current_pointer_x
- drag_info
.grab_x
)) > 4LL);
1743 bool y_threshold_passed
= (::llabs ((nframes64_t
) (drag_info
.current_pointer_y
- drag_info
.grab_y
)) > 4LL);
1745 drag_info
.move_threshold_passed
= (x_threshold_passed
|| y_threshold_passed
);
1747 // and change the initial grab loc/frame if this drag info wants us to
1749 if (drag_info
.want_move_threshold
&& drag_info
.move_threshold_passed
) {
1750 drag_info
.grab_frame
= drag_info
.current_pointer_frame
;
1751 drag_info
.grab_x
= drag_info
.current_pointer_x
;
1752 drag_info
.grab_y
= drag_info
.current_pointer_y
;
1753 drag_info
.last_pointer_frame
= drag_info
.grab_frame
;
1754 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- drag_info
.last_frame_position
;
1759 switch (item_type
) {
1760 case PlayheadCursorItem
:
1765 case RangeMarkerBarItem
:
1766 case TransportMarkerBarItem
:
1767 case CdMarkerBarItem
:
1768 case GainControlPointItem
:
1769 case RedirectAutomationControlPointItem
:
1770 case GainAutomationControlPointItem
:
1771 case PanAutomationControlPointItem
:
1772 case TempoMarkerItem
:
1773 case MeterMarkerItem
:
1774 case RegionViewNameHighlight
:
1775 case StartSelectionTrimItem
:
1776 case EndSelectionTrimItem
:
1779 case RedirectAutomationLineItem
:
1780 case GainAutomationLineItem
:
1781 case PanAutomationLineItem
:
1782 case FadeInHandleItem
:
1783 case FadeOutHandleItem
:
1784 /* <CMT Additions> */
1785 case ImageFrameHandleStartItem
:
1786 case ImageFrameHandleEndItem
:
1787 case MarkerViewHandleStartItem
:
1788 case MarkerViewHandleEndItem
:
1789 /* </CMT Additions> */
1790 if (drag_info
.item
&& (event
->motion
.state
& Gdk::BUTTON1_MASK
||
1791 (event
->motion
.state
& Gdk::BUTTON3_MASK
) ||
1792 (event
->motion
.state
& Gdk::BUTTON2_MASK
))) {
1793 if (!from_autoscroll
) {
1794 maybe_autoscroll_horizontally (&event
->motion
);
1796 if (drag_info
.motion_callback
) {
1797 (this->*(drag_info
.motion_callback
)) (item
, event
);
1807 switch (mouse_mode
) {
1809 if (item_type
== RegionItem
) {
1810 if (drag_info
.item
&& drag_info
.motion_callback
) {
1811 (this->*(drag_info
.motion_callback
)) (item
, event
);
1821 if (drag_info
.item
&& (event
->motion
.state
& GDK_BUTTON1_MASK
||
1822 (event
->motion
.state
& GDK_BUTTON2_MASK
))) {
1823 if (!from_autoscroll
) {
1824 maybe_autoscroll (&event
->motion
);
1826 if (drag_info
.motion_callback
) {
1827 (this->*(drag_info
.motion_callback
)) (item
, event
);
1839 track_canvas_motion (event
);
1840 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1848 Editor::break_drag ()
1850 stop_canvas_autoscroll ();
1851 hide_verbose_canvas_cursor ();
1853 if (drag_info
.item
) {
1854 drag_info
.item
->ungrab (0);
1856 /* put it back where it came from */
1861 drag_info
.item
->i2w (cxw
, cyw
);
1862 drag_info
.item
->move (drag_info
.original_x
- cxw
, drag_info
.original_y
- cyw
);
1869 Editor::finalize_drag ()
1872 drag_info
.copy
= false;
1873 drag_info
.motion_callback
= 0;
1874 drag_info
.finished_callback
= 0;
1875 drag_info
.dest_trackview
= 0;
1876 drag_info
.source_trackview
= 0;
1877 drag_info
.last_frame_position
= 0;
1878 drag_info
.grab_frame
= 0;
1879 drag_info
.last_pointer_frame
= 0;
1880 drag_info
.current_pointer_frame
= 0;
1881 drag_info
.brushing
= false;
1882 range_marker_drag_rect
->hide();
1883 drag_info
.clear_copied_locations ();
1887 Editor::start_grab (GdkEvent
* event
, Gdk::Cursor
*cursor
)
1889 if (drag_info
.item
== 0) {
1890 fatal
<< _("programming error: start_grab called without drag item") << endmsg
;
1896 cursor
= which_grabber_cursor ();
1899 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1901 if (Keyboard::is_button2_event (&event
->button
)) {
1902 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::SecondaryModifier
)) {
1903 drag_info
.y_constrained
= true;
1904 drag_info
.x_constrained
= false;
1906 drag_info
.y_constrained
= false;
1907 drag_info
.x_constrained
= true;
1910 drag_info
.x_constrained
= false;
1911 drag_info
.y_constrained
= false;
1914 drag_info
.grab_frame
= event_frame (event
, &drag_info
.grab_x
, &drag_info
.grab_y
);
1915 drag_info
.last_pointer_frame
= drag_info
.grab_frame
;
1916 drag_info
.current_pointer_frame
= drag_info
.grab_frame
;
1917 drag_info
.current_pointer_x
= drag_info
.grab_x
;
1918 drag_info
.current_pointer_y
= drag_info
.grab_y
;
1919 drag_info
.last_pointer_x
= drag_info
.current_pointer_x
;
1920 drag_info
.last_pointer_y
= drag_info
.current_pointer_y
;
1921 drag_info
.cumulative_x_drag
= 0;
1922 drag_info
.cumulative_y_drag
= 0;
1923 drag_info
.first_move
= true;
1924 drag_info
.move_threshold_passed
= false;
1925 drag_info
.want_move_threshold
= false;
1926 drag_info
.pointer_frame_offset
= 0;
1927 drag_info
.brushing
= false;
1928 drag_info
.clear_copied_locations ();
1930 drag_info
.original_x
= 0;
1931 drag_info
.original_y
= 0;
1932 drag_info
.item
->i2w (drag_info
.original_x
, drag_info
.original_y
);
1934 drag_info
.item
->grab (Gdk::POINTER_MOTION_MASK
|Gdk::BUTTON_PRESS_MASK
|Gdk::BUTTON_RELEASE_MASK
,
1936 event
->button
.time
);
1938 if (session
&& session
->transport_rolling()) {
1939 drag_info
.was_rolling
= true;
1941 drag_info
.was_rolling
= false;
1944 switch (snap_type
) {
1945 case SnapToRegionStart
:
1946 case SnapToRegionEnd
:
1947 case SnapToRegionSync
:
1948 case SnapToRegionBoundary
:
1949 build_region_boundary_cache ();
1957 Editor::swap_grab (ArdourCanvas::Item
* new_item
, Gdk::Cursor
* cursor
, uint32_t time
)
1959 drag_info
.item
->ungrab (0);
1960 drag_info
.item
= new_item
;
1963 cursor
= which_grabber_cursor ();
1966 drag_info
.item
->grab (Gdk::POINTER_MOTION_MASK
|Gdk::BUTTON_PRESS_MASK
|Gdk::BUTTON_RELEASE_MASK
, *cursor
, time
);
1970 Editor::end_grab (ArdourCanvas::Item
* item
, GdkEvent
* event
)
1972 bool did_drag
= false;
1974 stop_canvas_autoscroll ();
1976 if (drag_info
.item
== 0) {
1980 drag_info
.item
->ungrab (event
? event
->button
.time
: 0);
1982 if (drag_info
.finished_callback
&& event
) {
1983 drag_info
.last_pointer_x
= drag_info
.current_pointer_x
;
1984 drag_info
.last_pointer_y
= drag_info
.current_pointer_y
;
1985 (this->*(drag_info
.finished_callback
)) (item
, event
);
1988 did_drag
= !drag_info
.first_move
;
1990 hide_verbose_canvas_cursor();
1998 Editor::region_gain_motion_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2000 if (drag_info
.first_move
&& drag_info
.move_threshold_passed
) {
2001 drag_info
.first_move
= false;
2006 Editor::start_fade_in_grab (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2008 drag_info
.item
= item
;
2009 drag_info
.motion_callback
= &Editor::fade_in_drag_motion_callback
;
2010 drag_info
.finished_callback
= &Editor::fade_in_drag_finished_callback
;
2014 if ((drag_info
.data
= (item
->get_data ("regionview"))) == 0) {
2015 fatal
<< _("programming error: fade in canvas item has no regionview data pointer!") << endmsg
;
2019 AudioRegionView
* arv
= static_cast<AudioRegionView
*>(drag_info
.data
);
2021 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- ((nframes64_t
) arv
->audio_region()->fade_in().back()->when
+ arv
->region()->position());
2025 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2027 AudioRegionView
* arv
= static_cast<AudioRegionView
*>(drag_info
.data
);
2029 nframes64_t fade_length
;
2031 if (drag_info
.current_pointer_frame
> drag_info
.pointer_frame_offset
) {
2032 pos
= drag_info
.current_pointer_frame
- drag_info
.pointer_frame_offset
;
2038 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
2042 if (pos
< (arv
->region()->position() + 64)) {
2043 fade_length
= 64; // this should be a minimum defined somewhere
2044 } else if (pos
> arv
->region()->last_frame()) {
2045 fade_length
= arv
->region()->length();
2047 fade_length
= pos
- arv
->region()->position();
2049 /* mapover the region selection */
2051 for (RegionSelection::iterator i
= selection
->regions
.begin(); i
!= selection
->regions
.end(); ++i
) {
2053 AudioRegionView
* tmp
= dynamic_cast<AudioRegionView
*> (*i
);
2059 tmp
->reset_fade_in_shape_width (fade_length
);
2062 show_verbose_duration_cursor (arv
->region()->position(), arv
->region()->position() + fade_length
, 10);
2064 drag_info
.first_move
= false;
2068 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2070 AudioRegionView
* arv
= static_cast<AudioRegionView
*>(drag_info
.data
);
2072 nframes64_t fade_length
;
2074 if (drag_info
.first_move
) return;
2076 if (drag_info
.current_pointer_frame
> drag_info
.pointer_frame_offset
) {
2077 pos
= drag_info
.current_pointer_frame
- drag_info
.pointer_frame_offset
;
2082 if (pos
< (arv
->region()->position() + 64)) {
2083 fade_length
= 64; // this should be a minimum defined somewhere
2084 } else if (pos
> arv
->region()->last_frame()) {
2085 fade_length
= arv
->region()->length();
2087 fade_length
= pos
- arv
->region()->position();
2090 begin_reversible_command (_("change fade in length"));
2092 for (RegionSelection::iterator i
= selection
->regions
.begin(); i
!= selection
->regions
.end(); ++i
) {
2094 AudioRegionView
* tmp
= dynamic_cast<AudioRegionView
*> (*i
);
2100 AutomationList
& alist
= tmp
->audio_region()->fade_in();
2101 XMLNode
&before
= alist
.get_state();
2103 tmp
->audio_region()->set_fade_in_length (fade_length
);
2104 tmp
->audio_region()->set_fade_in_active (true);
2106 XMLNode
&after
= alist
.get_state();
2107 session
->add_command(new MementoCommand
<AutomationList
>(alist
, &before
, &after
));
2110 commit_reversible_command ();
2114 Editor::start_fade_out_grab (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2116 drag_info
.item
= item
;
2117 drag_info
.motion_callback
= &Editor::fade_out_drag_motion_callback
;
2118 drag_info
.finished_callback
= &Editor::fade_out_drag_finished_callback
;
2122 if ((drag_info
.data
= (item
->get_data ("regionview"))) == 0) {
2123 fatal
<< _("programming error: fade out canvas item has no regionview data pointer!") << endmsg
;
2127 AudioRegionView
* arv
= static_cast<AudioRegionView
*>(drag_info
.data
);
2129 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- (arv
->region()->length() - (nframes64_t
) arv
->audio_region()->fade_out().back()->when
+ arv
->region()->position());
2133 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2135 AudioRegionView
* arv
= static_cast<AudioRegionView
*>(drag_info
.data
);
2137 nframes64_t fade_length
;
2139 if (drag_info
.current_pointer_frame
> drag_info
.pointer_frame_offset
) {
2140 pos
= drag_info
.current_pointer_frame
- drag_info
.pointer_frame_offset
;
2145 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
2149 if (pos
> (arv
->region()->last_frame() - 64)) {
2150 fade_length
= 64; // this should really be a minimum fade defined somewhere
2152 else if (pos
< arv
->region()->position()) {
2153 fade_length
= arv
->region()->length();
2156 fade_length
= arv
->region()->last_frame() - pos
;
2159 /* mapover the region selection */
2161 for (RegionSelection::iterator i
= selection
->regions
.begin(); i
!= selection
->regions
.end(); ++i
) {
2163 AudioRegionView
* tmp
= dynamic_cast<AudioRegionView
*> (*i
);
2169 tmp
->reset_fade_out_shape_width (fade_length
);
2172 show_verbose_duration_cursor (arv
->region()->last_frame() - fade_length
, arv
->region()->last_frame(), 10);
2174 drag_info
.first_move
= false;
2178 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2180 if (drag_info
.first_move
) return;
2182 AudioRegionView
* arv
= static_cast<AudioRegionView
*>(drag_info
.data
);
2184 nframes64_t fade_length
;
2186 if (drag_info
.current_pointer_frame
> drag_info
.pointer_frame_offset
) {
2187 pos
= drag_info
.current_pointer_frame
- drag_info
.pointer_frame_offset
;
2193 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
2197 if (pos
> (arv
->region()->last_frame() - 64)) {
2198 fade_length
= 64; // this should really be a minimum fade defined somewhere
2200 else if (pos
< arv
->region()->position()) {
2201 fade_length
= arv
->region()->length();
2204 fade_length
= arv
->region()->last_frame() - pos
;
2207 begin_reversible_command (_("change fade out length"));
2209 for (RegionSelection::iterator i
= selection
->regions
.begin(); i
!= selection
->regions
.end(); ++i
) {
2211 AudioRegionView
* tmp
= dynamic_cast<AudioRegionView
*> (*i
);
2217 AutomationList
& alist
= tmp
->audio_region()->fade_out();
2218 XMLNode
&before
= alist
.get_state();
2220 tmp
->audio_region()->set_fade_out_length (fade_length
);
2221 tmp
->audio_region()->set_fade_out_active (true);
2223 XMLNode
&after
= alist
.get_state();
2224 session
->add_command(new MementoCommand
<AutomationList
>(alist
, &before
, &after
));
2227 commit_reversible_command ();
2231 Editor::start_cursor_grab (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2233 drag_info
.item
= item
;
2234 drag_info
.motion_callback
= &Editor::cursor_drag_motion_callback
;
2235 drag_info
.finished_callback
= &Editor::cursor_drag_finished_callback
;
2239 if ((drag_info
.data
= (item
->get_data ("cursor"))) == 0) {
2240 fatal
<< _("programming error: cursor canvas item has no cursor data pointer!") << endmsg
;
2244 Cursor
* cursor
= (Cursor
*) drag_info
.data
;
2246 if (cursor
== playhead_cursor
) {
2247 _dragging_playhead
= true;
2249 if (session
&& drag_info
.was_rolling
) {
2250 session
->request_stop ();
2253 if (session
&& session
->is_auditioning()) {
2254 session
->cancel_audition ();
2258 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- cursor
->current_frame
;
2260 show_verbose_time_cursor (cursor
->current_frame
, 10);
2264 Editor::start_cursor_grab_no_stop (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2266 drag_info
.item
= item
;
2267 drag_info
.motion_callback
= &Editor::cursor_drag_motion_callback
;
2268 drag_info
.finished_callback
= &Editor::cursor_drag_finished_ensure_locate_callback
;
2272 if ((drag_info
.data
= (item
->get_data ("cursor"))) == 0) {
2273 fatal
<< _("programming error: cursor canvas item has no cursor data pointer!") << endmsg
;
2277 Cursor
* cursor
= (Cursor
*) drag_info
.data
;
2278 nframes64_t where
= event_frame (event
, 0, 0);
2281 playhead_cursor
->set_position (where
);
2283 if (cursor
== playhead_cursor
) {
2284 _dragging_playhead
= true;
2286 if (session
&& session
->is_auditioning()) {
2287 session
->cancel_audition ();
2291 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- cursor
->current_frame
;
2293 show_verbose_time_cursor (cursor
->current_frame
, 10);
2297 Editor::cursor_drag_motion_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2299 Cursor
* cursor
= (Cursor
*) drag_info
.data
;
2300 nframes64_t adjusted_frame
;
2302 if (drag_info
.current_pointer_frame
> drag_info
.pointer_frame_offset
) {
2303 adjusted_frame
= drag_info
.current_pointer_frame
- drag_info
.pointer_frame_offset
;
2309 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
2310 if (cursor
== playhead_cursor
) {
2311 snap_to (adjusted_frame
);
2315 if (adjusted_frame
== drag_info
.last_pointer_frame
) return;
2317 cursor
->set_position (adjusted_frame
);
2319 show_verbose_time_cursor (cursor
->current_frame
, 10);
2322 track_canvas
->update_now ();
2324 UpdateAllTransportClocks (cursor
->current_frame
);
2326 drag_info
.last_pointer_frame
= adjusted_frame
;
2327 drag_info
.first_move
= false;
2331 Editor::cursor_drag_finished_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2333 _dragging_playhead
= false;
2335 if (drag_info
.first_move
) {
2339 cursor_drag_motion_callback (item
, event
);
2341 if (item
== &playhead_cursor
->canvas_item
) {
2343 session
->request_locate (playhead_cursor
->current_frame
, drag_info
.was_rolling
);
2349 Editor::cursor_drag_finished_ensure_locate_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2351 _dragging_playhead
= false;
2353 cursor_drag_motion_callback (item
, event
);
2355 if (item
== &playhead_cursor
->canvas_item
) {
2357 session
->request_locate (playhead_cursor
->current_frame
, drag_info
.was_rolling
);
2363 Editor::update_marker_drag_item (Location
*location
)
2365 double x1
= frame_to_pixel (location
->start());
2366 double x2
= frame_to_pixel (location
->end());
2368 if (location
->is_mark()) {
2369 marker_drag_line_points
.front().set_x(x1
);
2370 marker_drag_line_points
.back().set_x(x1
);
2371 marker_drag_line
->property_points() = marker_drag_line_points
;
2373 range_marker_drag_rect
->property_x1() = x1
;
2374 range_marker_drag_rect
->property_x2() = x2
;
2379 Editor::start_marker_grab (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2383 if ((marker
= static_cast<Marker
*> (item
->get_data ("marker"))) == 0) {
2384 fatal
<< _("programming error: marker canvas item has no marker object pointer!") << endmsg
;
2390 Location
*location
= find_location_from_marker (marker
, is_start
);
2392 drag_info
.item
= item
;
2393 drag_info
.data
= marker
;
2394 drag_info
.motion_callback
= &Editor::marker_drag_motion_callback
;
2395 drag_info
.finished_callback
= &Editor::marker_drag_finished_callback
;
2399 _dragging_edit_point
= true;
2401 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- (is_start
? location
->start() : location
->end());
2403 update_marker_drag_item (location
);
2405 if (location
->is_mark()) {
2406 // marker_drag_line->show();
2407 // marker_drag_line->raise_to_top();
2409 range_marker_drag_rect
->show();
2410 //range_marker_drag_rect->raise_to_top();
2414 show_verbose_time_cursor (location
->start(), 10);
2416 show_verbose_time_cursor (location
->end(), 10);
2419 Selection::Operation op
= Keyboard::selection_type (event
->button
.state
);
2422 case Selection::Toggle
:
2423 selection
->toggle (marker
);
2425 case Selection::Set
:
2426 if (!selection
->selected (marker
)) {
2427 selection
->set (marker
);
2430 case Selection::Extend
:
2432 Locations::LocationList ll
;
2433 list
<Marker
*> to_add
;
2435 selection
->markers
.range (s
, e
);
2436 s
= min (marker
->position(), s
);
2437 e
= max (marker
->position(), e
);
2440 if (e
< max_frames
) {
2443 session
->locations()->find_all_between (s
, e
, ll
, Location::Flags (0));
2444 for (Locations::LocationList::iterator i
= ll
.begin(); i
!= ll
.end(); ++i
) {
2445 LocationMarkers
* lm
= find_location_markers (*i
);
2448 to_add
.push_back (lm
->start
);
2451 to_add
.push_back (lm
->end
);
2455 if (!to_add
.empty()) {
2456 selection
->add (to_add
);
2460 case Selection::Add
:
2461 selection
->add (marker
);
2465 /* set up copies for us to manipulate during the drag */
2467 drag_info
.clear_copied_locations ();
2469 for (MarkerSelection::iterator i
= selection
->markers
.begin(); i
!= selection
->markers
.end(); ++i
) {
2470 Location
*l
= find_location_from_marker (*i
, is_start
);
2471 drag_info
.copied_locations
.push_back (new Location (*l
));
2476 Editor::marker_drag_motion_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2478 nframes64_t f_delta
= 0;
2479 nframes64_t newframe
;
2481 bool move_both
= false;
2482 Marker
* dragged_marker
= (Marker
*) drag_info
.data
;
2484 Location
*real_location
;
2485 Location
*copy_location
;
2487 if (drag_info
.pointer_frame_offset
<= drag_info
.current_pointer_frame
) {
2488 newframe
= drag_info
.current_pointer_frame
- drag_info
.pointer_frame_offset
;
2493 nframes64_t next
= newframe
;
2495 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
2496 snap_to (newframe
, 0, true);
2499 if (drag_info
.current_pointer_frame
== drag_info
.last_pointer_frame
) {
2503 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
2507 MarkerSelection::iterator i
;
2508 list
<Location
*>::iterator x
;
2510 /* find the marker we're dragging, and compute the delta */
2512 for (i
= selection
->markers
.begin(), x
= drag_info
.copied_locations
.begin();
2513 x
!= drag_info
.copied_locations
.end() && i
!= selection
->markers
.end();
2519 if (marker
== dragged_marker
) {
2521 if ((real_location
= find_location_from_marker (marker
, is_start
)) == 0) {
2526 if (real_location
->is_mark()) {
2527 f_delta
= newframe
- copy_location
->start();
2531 switch (marker
->type()) {
2533 case Marker::LoopStart
:
2534 case Marker::PunchIn
:
2535 f_delta
= newframe
- copy_location
->start();
2539 case Marker::LoopEnd
:
2540 case Marker::PunchOut
:
2541 f_delta
= newframe
- copy_location
->end();
2544 /* what kind of marker is this ? */
2552 if (i
== selection
->markers
.end()) {
2553 /* hmm, impossible - we didn't find the dragged marker */
2557 /* now move them all */
2559 for (i
= selection
->markers
.begin(), x
= drag_info
.copied_locations
.begin();
2560 x
!= drag_info
.copied_locations
.end() && i
!= selection
->markers
.end();
2566 /* call this to find out if its the start or end */
2568 if ((real_location
= find_location_from_marker (marker
, is_start
)) == 0) {
2572 if (real_location
->locked()) {
2576 if (copy_location
->is_mark()) {
2580 copy_location
->set_start (copy_location
->start() + f_delta
);
2584 nframes64_t new_start
= copy_location
->start() + f_delta
;
2585 nframes64_t new_end
= copy_location
->end() + f_delta
;
2587 if (is_start
) { // start-of-range marker
2590 copy_location
->set_start (new_start
);
2591 copy_location
->set_end (new_end
);
2592 } else if (new_start
< copy_location
->end()) {
2593 copy_location
->set_start (new_start
);
2595 snap_to (next
, 1, true);
2596 copy_location
->set_end (next
);
2597 copy_location
->set_start (newframe
);
2600 } else { // end marker
2603 copy_location
->set_end (new_end
);
2604 copy_location
->set_start (new_start
);
2605 } else if (new_end
> copy_location
->start()) {
2606 copy_location
->set_end (new_end
);
2607 } else if (newframe
> 0) {
2608 snap_to (next
, -1, true);
2609 copy_location
->set_start (next
);
2610 copy_location
->set_end (newframe
);
2614 update_marker_drag_item (copy_location
);
2616 LocationMarkers
* lm
= find_location_markers (real_location
);
2619 lm
->set_position (copy_location
->start(), copy_location
->end());
2623 drag_info
.last_pointer_frame
= drag_info
.current_pointer_frame
;
2624 drag_info
.first_move
= false;
2626 if (drag_info
.copied_locations
.empty()) {
2630 edit_point_clock
.set (drag_info
.copied_locations
.front()->start());
2631 show_verbose_time_cursor (newframe
, 10);
2634 track_canvas
->update_now ();
2639 Editor::marker_drag_finished_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2641 if (drag_info
.first_move
) {
2643 /* just a click, do nothing but finish
2644 off the selection process
2647 Selection::Operation op
= Keyboard::selection_type (event
->button
.state
);
2648 Marker
* marker
= (Marker
*) drag_info
.data
;
2651 case Selection::Set
:
2652 if (selection
->selected (marker
) && selection
->markers
.size() > 1) {
2653 selection
->set (marker
);
2657 case Selection::Toggle
:
2658 case Selection::Extend
:
2659 case Selection::Add
:
2666 _dragging_edit_point
= false;
2669 begin_reversible_command ( _("move marker") );
2670 XMLNode
&before
= session
->locations()->get_state();
2672 MarkerSelection::iterator i
;
2673 list
<Location
*>::iterator x
;
2676 for (i
= selection
->markers
.begin(), x
= drag_info
.copied_locations
.begin();
2677 x
!= drag_info
.copied_locations
.end() && i
!= selection
->markers
.end();
2680 Location
* location
= find_location_from_marker ((*i
), is_start
);
2684 if (location
->locked()) {
2688 if (location
->is_mark()) {
2689 location
->set_start ((*x
)->start());
2691 location
->set ((*x
)->start(), (*x
)->end());
2696 XMLNode
&after
= session
->locations()->get_state();
2697 session
->add_command(new MementoCommand
<Locations
>(*(session
->locations()), &before
, &after
));
2698 commit_reversible_command ();
2700 marker_drag_line
->hide();
2701 range_marker_drag_rect
->hide();
2705 Editor::start_meter_marker_grab (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2708 MeterMarker
* meter_marker
;
2710 if ((marker
= reinterpret_cast<Marker
*> (item
->get_data ("marker"))) == 0) {
2711 fatal
<< _("programming error: meter marker canvas item has no marker object pointer!") << endmsg
;
2715 meter_marker
= dynamic_cast<MeterMarker
*> (marker
);
2717 MetricSection
& section (meter_marker
->meter());
2719 if (!section
.movable()) {
2723 drag_info
.item
= item
;
2724 drag_info
.copy
= false;
2725 drag_info
.data
= marker
;
2726 drag_info
.motion_callback
= &Editor::meter_marker_drag_motion_callback
;
2727 drag_info
.finished_callback
= &Editor::meter_marker_drag_finished_callback
;
2731 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- meter_marker
->meter().frame();
2733 show_verbose_time_cursor (drag_info
.current_pointer_frame
, 10);
2737 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2740 MeterMarker
* meter_marker
;
2742 if ((marker
= reinterpret_cast<Marker
*> (item
->get_data ("marker"))) == 0) {
2743 fatal
<< _("programming error: meter marker canvas item has no marker object pointer!") << endmsg
;
2747 meter_marker
= dynamic_cast<MeterMarker
*> (marker
);
2749 // create a dummy marker for visual representation of moving the copy.
2750 // The actual copying is not done before we reach the finish callback.
2752 snprintf (name
, sizeof(name
), "%g/%g", meter_marker
->meter().beats_per_bar(), meter_marker
->meter().note_divisor ());
2753 MeterMarker
* new_marker
= new MeterMarker(*this, *meter_group
, ARDOUR_UI::config()->canvasvar_MeterMarker
.get(), name
,
2754 *new MeterSection(meter_marker
->meter()));
2756 drag_info
.item
= &new_marker
->the_item();
2757 drag_info
.copy
= true;
2758 drag_info
.data
= new_marker
;
2759 drag_info
.motion_callback
= &Editor::meter_marker_drag_motion_callback
;
2760 drag_info
.finished_callback
= &Editor::meter_marker_drag_finished_callback
;
2764 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- meter_marker
->meter().frame();
2766 show_verbose_time_cursor (drag_info
.current_pointer_frame
, 10);
2770 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2772 MeterMarker
* marker
= (MeterMarker
*) drag_info
.data
;
2773 nframes64_t adjusted_frame
;
2775 if (drag_info
.current_pointer_frame
> drag_info
.pointer_frame_offset
) {
2776 adjusted_frame
= drag_info
.current_pointer_frame
- drag_info
.pointer_frame_offset
;
2782 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
2783 snap_to (adjusted_frame
);
2786 if (adjusted_frame
== drag_info
.last_pointer_frame
) return;
2788 marker
->set_position (adjusted_frame
);
2791 drag_info
.last_pointer_frame
= adjusted_frame
;
2792 drag_info
.first_move
= false;
2794 show_verbose_time_cursor (adjusted_frame
, 10);
2798 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2800 if (drag_info
.first_move
) return;
2802 meter_marker_drag_motion_callback (drag_info
.item
, event
);
2804 MeterMarker
* marker
= (MeterMarker
*) drag_info
.data
;
2807 TempoMap
& map (session
->tempo_map());
2808 map
.bbt_time (drag_info
.last_pointer_frame
, when
);
2810 if (drag_info
.copy
== true) {
2811 begin_reversible_command (_("copy meter mark"));
2812 XMLNode
&before
= map
.get_state();
2813 map
.add_meter (marker
->meter(), when
);
2814 XMLNode
&after
= map
.get_state();
2815 session
->add_command(new MementoCommand
<TempoMap
>(map
, &before
, &after
));
2816 commit_reversible_command ();
2818 // delete the dummy marker we used for visual representation of copying.
2819 // a new visual marker will show up automatically.
2822 begin_reversible_command (_("move meter mark"));
2823 XMLNode
&before
= map
.get_state();
2824 map
.move_meter (marker
->meter(), when
);
2825 XMLNode
&after
= map
.get_state();
2826 session
->add_command(new MementoCommand
<TempoMap
>(map
, &before
, &after
));
2827 commit_reversible_command ();
2832 Editor::start_tempo_marker_grab (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2835 TempoMarker
* tempo_marker
;
2837 if ((marker
= reinterpret_cast<Marker
*> (item
->get_data ("marker"))) == 0) {
2838 fatal
<< _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg
;
2842 if ((tempo_marker
= dynamic_cast<TempoMarker
*> (marker
)) == 0) {
2843 fatal
<< _("programming error: marker for tempo is not a tempo marker!") << endmsg
;
2847 MetricSection
& section (tempo_marker
->tempo());
2849 if (!section
.movable()) {
2853 drag_info
.item
= item
;
2854 drag_info
.copy
= false;
2855 drag_info
.data
= marker
;
2856 drag_info
.motion_callback
= &Editor::tempo_marker_drag_motion_callback
;
2857 drag_info
.finished_callback
= &Editor::tempo_marker_drag_finished_callback
;
2861 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- tempo_marker
->tempo().frame();
2862 show_verbose_time_cursor (drag_info
.current_pointer_frame
, 10);
2866 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2869 TempoMarker
* tempo_marker
;
2871 if ((marker
= reinterpret_cast<Marker
*> (item
->get_data ("marker"))) == 0) {
2872 fatal
<< _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg
;
2876 if ((tempo_marker
= dynamic_cast<TempoMarker
*> (marker
)) == 0) {
2877 fatal
<< _("programming error: marker for tempo is not a tempo marker!") << endmsg
;
2881 // create a dummy marker for visual representation of moving the copy.
2882 // The actual copying is not done before we reach the finish callback.
2884 snprintf (name
, sizeof (name
), "%.2f", tempo_marker
->tempo().beats_per_minute());
2885 TempoMarker
* new_marker
= new TempoMarker(*this, *tempo_group
, ARDOUR_UI::config()->canvasvar_TempoMarker
.get(), name
,
2886 *new TempoSection(tempo_marker
->tempo()));
2888 drag_info
.item
= &new_marker
->the_item();
2889 drag_info
.copy
= true;
2890 drag_info
.data
= new_marker
;
2891 drag_info
.motion_callback
= &Editor::tempo_marker_drag_motion_callback
;
2892 drag_info
.finished_callback
= &Editor::tempo_marker_drag_finished_callback
;
2896 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- tempo_marker
->tempo().frame();
2898 show_verbose_time_cursor (drag_info
.current_pointer_frame
, 10);
2902 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2904 TempoMarker
* marker
= (TempoMarker
*) drag_info
.data
;
2905 nframes64_t adjusted_frame
;
2907 if (drag_info
.current_pointer_frame
> drag_info
.pointer_frame_offset
) {
2908 adjusted_frame
= drag_info
.current_pointer_frame
- drag_info
.pointer_frame_offset
;
2914 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
2915 snap_to (adjusted_frame
);
2918 if (adjusted_frame
== drag_info
.last_pointer_frame
) return;
2920 /* OK, we've moved far enough to make it worth actually move the thing. */
2922 marker
->set_position (adjusted_frame
);
2924 show_verbose_time_cursor (adjusted_frame
, 10);
2926 drag_info
.last_pointer_frame
= adjusted_frame
;
2927 drag_info
.first_move
= false;
2931 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2933 if (drag_info
.first_move
) return;
2935 tempo_marker_drag_motion_callback (drag_info
.item
, event
);
2937 TempoMarker
* marker
= (TempoMarker
*) drag_info
.data
;
2940 TempoMap
& map (session
->tempo_map());
2941 map
.bbt_time (drag_info
.last_pointer_frame
, when
);
2943 if (drag_info
.copy
== true) {
2944 begin_reversible_command (_("copy tempo mark"));
2945 XMLNode
&before
= map
.get_state();
2946 map
.add_tempo (marker
->tempo(), when
);
2947 XMLNode
&after
= map
.get_state();
2948 session
->add_command (new MementoCommand
<TempoMap
>(map
, &before
, &after
));
2949 commit_reversible_command ();
2951 // delete the dummy marker we used for visual representation of copying.
2952 // a new visual marker will show up automatically.
2955 begin_reversible_command (_("move tempo mark"));
2956 XMLNode
&before
= map
.get_state();
2957 map
.move_tempo (marker
->tempo(), when
);
2958 XMLNode
&after
= map
.get_state();
2959 session
->add_command (new MementoCommand
<TempoMap
>(map
, &before
, &after
));
2960 commit_reversible_command ();
2965 Editor::remove_gain_control_point (ArdourCanvas::Item
*item
, GdkEvent
* event
)
2967 ControlPoint
* control_point
;
2969 if ((control_point
= reinterpret_cast<ControlPoint
*> (item
->get_data ("control_point"))) == 0) {
2970 fatal
<< _("programming error: control point canvas item has no control point object pointer!") << endmsg
;
2974 // We shouldn't remove the first or last gain point
2975 if (control_point
->line
.is_last_point(*control_point
) ||
2976 control_point
->line
.is_first_point(*control_point
)) {
2980 control_point
->line
.remove_point (*control_point
);
2984 Editor::remove_control_point (ArdourCanvas::Item
*item
, GdkEvent
* event
)
2986 ControlPoint
* control_point
;
2988 if ((control_point
= reinterpret_cast<ControlPoint
*> (item
->get_data ("control_point"))) == 0) {
2989 fatal
<< _("programming error: control point canvas item has no control point object pointer!") << endmsg
;
2993 control_point
->line
.remove_point (*control_point
);
2997 Editor::start_control_point_grab (ArdourCanvas::Item
* item
, GdkEvent
* event
)
2999 ControlPoint
* control_point
;
3001 if ((control_point
= reinterpret_cast<ControlPoint
*> (item
->get_data ("control_point"))) == 0) {
3002 fatal
<< _("programming error: control point canvas item has no control point object pointer!") << endmsg
;
3006 drag_info
.item
= item
;
3007 drag_info
.data
= control_point
;
3008 drag_info
.motion_callback
= &Editor::control_point_drag_motion_callback
;
3009 drag_info
.finished_callback
= &Editor::control_point_drag_finished_callback
;
3011 start_grab (event
, fader_cursor
);
3013 // start the grab at the center of the control point so
3014 // the point doesn't 'jump' to the mouse after the first drag
3015 drag_info
.grab_x
= control_point
->get_x();
3016 drag_info
.grab_y
= control_point
->get_y();
3017 control_point
->line
.parent_group().i2w(drag_info
.grab_x
, drag_info
.grab_y
);
3018 track_canvas
->w2c(drag_info
.grab_x
, drag_info
.grab_y
, drag_info
.grab_x
, drag_info
.grab_y
);
3020 drag_info
.grab_frame
= pixel_to_frame(drag_info
.grab_x
);
3022 control_point
->line
.start_drag (control_point
, drag_info
.grab_frame
, 0);
3024 float fraction
= 1.0 - (control_point
->get_y() / control_point
->line
.height());
3025 set_verbose_canvas_cursor (control_point
->line
.get_verbose_cursor_string (fraction
),
3026 drag_info
.current_pointer_x
+ 10, drag_info
.current_pointer_y
+ 10);
3028 show_verbose_canvas_cursor ();
3032 Editor::control_point_drag_motion_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
3034 ControlPoint
* cp
= reinterpret_cast<ControlPoint
*> (drag_info
.data
);
3036 double dx
= drag_info
.current_pointer_x
- drag_info
.last_pointer_x
;
3037 double dy
= drag_info
.current_pointer_y
- drag_info
.last_pointer_y
;
3039 if (event
->button
.state
& Keyboard::SecondaryModifier
) {
3044 double cx
= drag_info
.grab_x
+ drag_info
.cumulative_x_drag
+ dx
;
3045 double cy
= drag_info
.grab_y
+ drag_info
.cumulative_y_drag
+ dy
;
3047 // calculate zero crossing point. back off by .01 to stay on the
3048 // positive side of zero
3050 double zero_gain_y
= (1.0 - ZERO_GAIN_FRACTION
) * cp
->line
.height() - .01;
3051 cp
->line
.parent_group().i2w(_unused
, zero_gain_y
);
3053 // make sure we hit zero when passing through
3054 if ((cy
< zero_gain_y
and (cy
- dy
) > zero_gain_y
)
3055 or (cy
> zero_gain_y
and (cy
- dy
) < zero_gain_y
)) {
3059 if (drag_info
.x_constrained
) {
3060 cx
= drag_info
.grab_x
;
3062 if (drag_info
.y_constrained
) {
3063 cy
= drag_info
.grab_y
;
3066 drag_info
.cumulative_x_drag
= cx
- drag_info
.grab_x
;
3067 drag_info
.cumulative_y_drag
= cy
- drag_info
.grab_y
;
3069 cp
->line
.parent_group().w2i (cx
, cy
);
3073 cy
= min ((double) cp
->line
.height(), cy
);
3075 //translate cx to frames
3076 nframes64_t cx_frames
= unit_to_frame (cx
);
3078 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier()) && !drag_info
.x_constrained
) {
3079 snap_to (cx_frames
);
3082 float fraction
= 1.0 - (cy
/ cp
->line
.height());
3086 if (Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::PrimaryModifier
)) {
3092 cp
->line
.point_drag (*cp
, cx_frames
, fraction
, push
);
3094 set_verbose_canvas_cursor_text (cp
->line
.get_verbose_cursor_string (fraction
));
3096 drag_info
.first_move
= false;
3100 Editor::control_point_drag_finished_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
3102 ControlPoint
* cp
= reinterpret_cast<ControlPoint
*> (drag_info
.data
);
3104 if (drag_info
.first_move
) {
3108 if ((event
->type
== GDK_BUTTON_RELEASE
) && (event
->button
.button
== 1) && Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::TertiaryModifier
)) {
3109 reset_point_selection ();
3113 control_point_drag_motion_callback (item
, event
);
3115 cp
->line
.end_drag (cp
);
3119 Editor::start_line_grab_from_regionview (ArdourCanvas::Item
* item
, GdkEvent
* event
)
3121 switch (mouse_mode
) {
3123 assert(dynamic_cast<AudioRegionView
*>(clicked_regionview
));
3124 start_line_grab (dynamic_cast<AudioRegionView
*>(clicked_regionview
)->get_gain_line(), event
);
3132 Editor::start_line_grab_from_line (ArdourCanvas::Item
* item
, GdkEvent
* event
)
3136 if ((al
= reinterpret_cast<AutomationLine
*> (item
->get_data ("line"))) == 0) {
3137 fatal
<< _("programming error: line canvas item has no line pointer!") << endmsg
;
3141 start_line_grab (al
, event
);
3145 Editor::start_line_grab (AutomationLine
* line
, GdkEvent
* event
)
3149 nframes64_t frame_within_region
;
3151 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3152 origin, and ditto for y.
3155 cx
= event
->button
.x
;
3156 cy
= event
->button
.y
;
3158 line
->parent_group().w2i (cx
, cy
);
3160 frame_within_region
= (nframes64_t
) floor (cx
* frames_per_unit
);
3162 if (!line
->control_points_adjacent (frame_within_region
, current_line_drag_info
.before
,
3163 current_line_drag_info
.after
)) {
3164 /* no adjacent points */
3168 drag_info
.item
= &line
->grab_item();
3169 drag_info
.data
= line
;
3170 drag_info
.motion_callback
= &Editor::line_drag_motion_callback
;
3171 drag_info
.finished_callback
= &Editor::line_drag_finished_callback
;
3173 start_grab (event
, fader_cursor
);
3175 /* store grab start in parent frame */
3177 drag_info
.grab_x
= cx
;
3178 drag_info
.grab_y
= cy
;
3180 double fraction
= 1.0 - (cy
/ line
->height());
3182 line
->start_drag (0, drag_info
.grab_frame
, fraction
);
3184 set_verbose_canvas_cursor (line
->get_verbose_cursor_string (fraction
),
3185 drag_info
.current_pointer_x
+ 10, drag_info
.current_pointer_y
+ 10);
3186 show_verbose_canvas_cursor ();
3190 Editor::line_drag_motion_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
3192 AutomationLine
* line
= reinterpret_cast<AutomationLine
*> (drag_info
.data
);
3194 double dy
= drag_info
.current_pointer_y
- drag_info
.last_pointer_y
;
3196 if (event
->button
.state
& Keyboard::SecondaryModifier
) {
3200 double cy
= drag_info
.grab_y
+ drag_info
.cumulative_y_drag
+ dy
;
3202 drag_info
.cumulative_y_drag
= cy
- drag_info
.grab_y
;
3205 cy
= min ((double) line
->height(), cy
);
3207 double fraction
= 1.0 - (cy
/ line
->height());
3211 if (Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::PrimaryModifier
)) {
3217 line
->line_drag (current_line_drag_info
.before
, current_line_drag_info
.after
, fraction
, push
);
3219 set_verbose_canvas_cursor_text (line
->get_verbose_cursor_string (fraction
));
3223 Editor::line_drag_finished_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
3225 AutomationLine
* line
= reinterpret_cast<AutomationLine
*> (drag_info
.data
);
3226 line_drag_motion_callback (item
, event
);
3231 Editor::start_region_grab (ArdourCanvas::Item
* item
, GdkEvent
* event
)
3233 if (selection
->regions
.empty() || clicked_regionview
== 0) {
3237 drag_info
.copy
= false;
3238 drag_info
.item
= item
;
3239 drag_info
.data
= clicked_regionview
;
3241 if (Config
->get_edit_mode() == Splice
) {
3242 drag_info
.motion_callback
= &Editor::region_drag_splice_motion_callback
;
3243 drag_info
.finished_callback
= &Editor::region_drag_splice_finished_callback
;
3245 drag_info
.motion_callback
= &Editor::region_drag_motion_callback
;
3246 drag_info
.finished_callback
= &Editor::region_drag_finished_callback
;
3252 TimeAxisView
* tvp
= clicked_trackview
;
3253 RouteTimeAxisView
* tv
= dynamic_cast<RouteTimeAxisView
*>(tvp
);
3255 if (tv
&& tv
->is_audio_track()) {
3256 speed
= tv
->get_diskstream()->speed();
3259 drag_info
.last_frame_position
= (nframes64_t
) (clicked_regionview
->region()->position() / speed
);
3260 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- drag_info
.last_frame_position
;
3261 drag_info
.source_trackview
= &clicked_regionview
->get_time_axis_view();
3262 drag_info
.dest_trackview
= drag_info
.source_trackview
;
3263 // we want a move threshold
3264 drag_info
.want_move_threshold
= true;
3266 show_verbose_time_cursor (drag_info
.last_frame_position
, 10);
3268 begin_reversible_command (_("move region(s)"));
3270 _region_motion_group
->raise_to_top ();
3272 /* sync the canvas to what we think is its current state */
3273 track_canvas
->update_now();
3277 Editor::start_region_copy_grab (ArdourCanvas::Item
* item
, GdkEvent
* event
)
3279 if (selection
->regions
.empty() || clicked_regionview
== 0) {
3283 drag_info
.copy
= true;
3284 drag_info
.item
= item
;
3285 drag_info
.data
= clicked_regionview
;
3289 TimeAxisView
* tv
= &clicked_regionview
->get_time_axis_view();
3290 RouteTimeAxisView
* atv
= dynamic_cast<RouteTimeAxisView
*>(tv
);
3293 if (atv
&& atv
->is_audio_track()) {
3294 speed
= atv
->get_diskstream()->speed();
3297 drag_info
.source_trackview
= &clicked_regionview
->get_time_axis_view();
3298 drag_info
.dest_trackview
= drag_info
.source_trackview
;
3299 drag_info
.last_frame_position
= (nframes64_t
) (clicked_regionview
->region()->position() / speed
);
3300 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- drag_info
.last_frame_position
;
3301 // we want a move threshold
3302 drag_info
.want_move_threshold
= true;
3303 drag_info
.motion_callback
= &Editor::region_drag_motion_callback
;
3304 drag_info
.finished_callback
= &Editor::region_drag_finished_callback
;
3305 show_verbose_time_cursor (drag_info
.last_frame_position
, 10);
3306 _region_motion_group
->raise_to_top ();
3310 Editor::start_region_brush_grab (ArdourCanvas::Item
* item
, GdkEvent
* event
)
3312 if (selection
->regions
.empty() || clicked_regionview
== 0 || Config
->get_edit_mode() == Splice
) {
3316 drag_info
.copy
= false;
3317 drag_info
.item
= item
;
3318 drag_info
.data
= clicked_regionview
;
3319 drag_info
.motion_callback
= &Editor::region_drag_motion_callback
;
3320 drag_info
.finished_callback
= &Editor::region_drag_finished_callback
;
3325 TimeAxisView
* tvp
= clicked_trackview
;
3326 RouteTimeAxisView
* tv
= dynamic_cast<RouteTimeAxisView
*>(tvp
);
3328 if (tv
&& tv
->is_audio_track()) {
3329 speed
= tv
->get_diskstream()->speed();
3332 drag_info
.last_frame_position
= (nframes64_t
) (clicked_regionview
->region()->position() / speed
);
3333 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- drag_info
.last_frame_position
;
3334 drag_info
.source_trackview
= &clicked_regionview
->get_time_axis_view();
3335 drag_info
.dest_trackview
= drag_info
.source_trackview
;
3336 // we want a move threshold
3337 drag_info
.want_move_threshold
= true;
3338 drag_info
.brushing
= true;
3340 begin_reversible_command (_("Drag region brush"));
3344 Editor::possibly_copy_regions_during_grab (GdkEvent
* event
)
3346 if (drag_info
.copy
&& drag_info
.move_threshold_passed
&& drag_info
.want_move_threshold
) {
3348 drag_info
.want_move_threshold
= false; // don't copy again
3350 /* duplicate the regionview(s) and region(s) */
3352 vector
<RegionView
*> new_regionviews
;
3354 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin(); i
!= selection
->regions
.by_layer().end(); ++i
) {
3356 AudioRegionView
* arv
;
3358 if ((arv
= dynamic_cast<AudioRegionView
*>(*i
)) == 0) {
3359 /* XXX handle MIDI here */
3363 const boost::shared_ptr
<const Region
> original
= arv
->region();
3364 boost::shared_ptr
<Region
> region_copy
= RegionFactory::create (original
);
3365 boost::shared_ptr
<AudioRegion
> ar
= boost::dynamic_pointer_cast
<AudioRegion
> (region_copy
);
3367 nrv
= new AudioRegionView (*arv
, ar
);
3368 nrv
->get_canvas_group()->show ();
3370 new_regionviews
.push_back (nrv
);
3373 if (new_regionviews
.empty()) {
3377 /* reset selection to new regionviews. This will not set selection visual status for
3378 these regionviews since they don't belong to a track, so do that by hand too.
3381 selection
->set (new_regionviews
);
3383 for (vector
<RegionView
*>::iterator i
= new_regionviews
.begin(); i
!= new_regionviews
.end(); ++i
) {
3384 (*i
)->set_selected (true);
3387 /* reset drag_info data to reflect the fact that we are dragging the copies */
3389 drag_info
.data
= new_regionviews
.front();
3391 swap_grab (new_regionviews
.front()->get_canvas_group (), 0, event
->motion
.time
);
3393 sync the canvas to what we think is its current state
3394 without it, the canvas seems to
3395 "forget" to update properly after the upcoming reparent()
3396 ..only if the mouse is in rapid motion at the time of the grab.
3397 something to do with regionview creation raking so long?
3399 track_canvas
->update_now();
3404 Editor::check_region_drag_possible (AudioTimeAxisView
** tv
)
3406 /* Which trackview is this ? */
3408 TimeAxisView
* tvp
= trackview_by_y_position (drag_info
.current_pointer_y
);
3409 (*tv
) = dynamic_cast<AudioTimeAxisView
*>(tvp
);
3411 /* The region motion is only processed if the pointer is over
3415 if (!(*tv
) || !(*tv
)->is_audio_track()) {
3416 /* To make sure we hide the verbose canvas cursor when the mouse is
3417 not held over and audiotrack.
3419 hide_verbose_canvas_cursor ();
3426 struct RegionSelectionByPosition
{
3427 bool operator() (RegionView
*a
, RegionView
* b
) {
3428 return a
->region()->position () < b
->region()->position();
3433 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
3435 AudioTimeAxisView
* tv
;
3437 if (!check_region_drag_possible (&tv
)) {
3441 if (!drag_info
.move_threshold_passed
) {
3447 if (drag_info
.current_pointer_x
- drag_info
.grab_x
> 0) {
3453 RegionSelection
copy (selection
->regions
);
3455 RegionSelectionByPosition cmp
;
3458 for (RegionSelection::iterator i
= copy
.begin(); i
!= copy
.end(); ++i
) {
3460 AudioTimeAxisView
* atv
= dynamic_cast<AudioTimeAxisView
*> (&(*i
)->get_time_axis_view());
3466 boost::shared_ptr
<Playlist
> playlist
;
3468 if ((playlist
= atv
->playlist()) == 0) {
3472 if (!playlist
->region_is_shuffle_constrained ((*i
)->region())) {
3477 if (drag_info
.current_pointer_frame
< (*i
)->region()->last_frame() + 1) {
3481 if (drag_info
.current_pointer_frame
> (*i
)->region()->first_frame()) {
3487 playlist
->shuffle ((*i
)->region(), dir
);
3489 drag_info
.grab_x
= drag_info
.current_pointer_x
;
3494 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
3499 Editor::region_drag_motion_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
3503 nframes64_t pending_region_position
= 0;
3504 int32_t pointer_y_span
= 0, canvas_pointer_y_span
= 0, original_pointer_order
;
3505 int32_t visible_y_high
= 0, visible_y_low
= 512; //high meaning higher numbered.. not the height on the screen
3506 bool clamp_y_axis
= false;
3507 vector
<int32_t> height_list(512) ;
3508 vector
<int32_t>::iterator j
;
3509 AudioTimeAxisView
* tv
;
3511 possibly_copy_regions_during_grab (event
);
3513 if (!check_region_drag_possible (&tv
)) {
3517 original_pointer_order
= drag_info
.dest_trackview
->order
;
3519 /************************************************************
3521 ************************************************************/
3523 if (drag_info
.brushing
) {
3524 clamp_y_axis
= true;
3529 if ((pointer_y_span
= (drag_info
.dest_trackview
->order
- tv
->order
)) != 0) {
3531 int32_t children
= 0, numtracks
= 0;
3532 // XXX hard coding track limit, oh my, so very very bad
3533 bitset
<1024> tracks (0x00);
3534 /* get a bitmask representing the visible tracks */
3536 for (TrackViewList::iterator i
= track_views
.begin(); i
!= track_views
.end(); ++i
) {
3537 TimeAxisView
*tracklist_timeview
;
3538 tracklist_timeview
= (*i
);
3539 AudioTimeAxisView
* atv2
= dynamic_cast<AudioTimeAxisView
*>(tracklist_timeview
);
3540 list
<TimeAxisView
*> children_list
;
3542 /* zeroes are audio tracks. ones are other types. */
3544 if (!atv2
->hidden()) {
3546 if (visible_y_high
< atv2
->order
) {
3547 visible_y_high
= atv2
->order
;
3549 if (visible_y_low
> atv2
->order
) {
3550 visible_y_low
= atv2
->order
;
3553 if (!atv2
->is_audio_track()) {
3554 tracks
= tracks
|= (0x01 << atv2
->order
);
3557 height_list
[atv2
->order
] = (*i
)->current_height();
3559 if ((children_list
= atv2
->get_child_list()).size() > 0) {
3560 for (list
<TimeAxisView
*>::iterator j
= children_list
.begin(); j
!= children_list
.end(); ++j
) {
3561 tracks
= tracks
|= (0x01 << (atv2
->order
+ children
));
3562 height_list
[atv2
->order
+ children
] = (*j
)->current_height();
3570 /* find the actual span according to the canvas */
3572 canvas_pointer_y_span
= pointer_y_span
;
3573 if (drag_info
.dest_trackview
->order
>= tv
->order
) {
3575 for (y
= tv
->order
; y
< drag_info
.dest_trackview
->order
; y
++) {
3576 if (height_list
[y
] == 0 ) {
3577 canvas_pointer_y_span
--;
3582 for (y
= drag_info
.dest_trackview
->order
;y
<= tv
->order
; y
++) {
3583 if ( height_list
[y
] == 0 ) {
3584 canvas_pointer_y_span
++;
3589 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin(); i
!= selection
->regions
.by_layer().end(); ++i
) {
3590 RegionView
* rv2
= (*i
);
3591 double ix1
, ix2
, iy1
, iy2
;
3594 if (rv2
->region()->locked()) {
3598 rv2
->get_canvas_frame()->get_bounds (ix1
, iy1
, ix2
, iy2
);
3599 rv2
->get_canvas_group()->i2w (ix1
, iy1
);
3600 iy1
+= vertical_adjustment
.get_value() - canvas_timebars_vsize
;
3602 TimeAxisView
* tvp2
= trackview_by_y_position (iy1
);
3603 RouteTimeAxisView
* atv2
= dynamic_cast<RouteTimeAxisView
*>(tvp2
);
3605 if (atv2
->order
!= original_pointer_order
) {
3606 /* this isn't the pointer track */
3608 if (canvas_pointer_y_span
> 0) {
3610 /* moving up the canvas */
3611 if ((atv2
->order
- canvas_pointer_y_span
) >= visible_y_low
) {
3613 int32_t visible_tracks
= 0;
3614 while (visible_tracks
< canvas_pointer_y_span
) {
3617 while (height_list
[atv2
->order
- (visible_tracks
- n
)] == 0) {
3618 /* we're passing through a hidden track */
3623 if (tracks
[atv2
->order
- (canvas_pointer_y_span
- n
)] != 0x00) {
3624 clamp_y_axis
= true;
3628 clamp_y_axis
= true;
3631 } else if (canvas_pointer_y_span
< 0) {
3633 /*moving down the canvas*/
3635 if ((atv2
->order
- (canvas_pointer_y_span
- n
)) <= visible_y_high
) { // we will overflow
3638 int32_t visible_tracks
= 0;
3640 while (visible_tracks
> canvas_pointer_y_span
) {
3643 while (height_list
[atv2
->order
- (visible_tracks
- n
)] == 0) {
3647 if ( tracks
[atv2
->order
- ( canvas_pointer_y_span
- n
)] != 0x00) {
3648 clamp_y_axis
= true;
3653 clamp_y_axis
= true;
3659 /* this is the pointer's track */
3660 if ((atv2
->order
- pointer_y_span
) > visible_y_high
) { // we will overflow
3661 clamp_y_axis
= true;
3662 } else if ((atv2
->order
- pointer_y_span
) < visible_y_low
) { // we will underflow
3663 clamp_y_axis
= true;
3671 } else if (drag_info
.dest_trackview
== tv
) {
3672 clamp_y_axis
= true;
3676 if (!clamp_y_axis
) {
3677 drag_info
.dest_trackview
= tv
;
3680 /************************************************************
3682 ************************************************************/
3684 /* compute the amount of pointer motion in frames, and where
3685 the region would be if we moved it by that much.
3687 if ( drag_info
.move_threshold_passed
) {
3689 if (drag_info
.current_pointer_frame
>= drag_info
.pointer_frame_offset
) {
3691 nframes64_t sync_frame
;
3692 nframes64_t sync_offset
;
3695 pending_region_position
= drag_info
.current_pointer_frame
- drag_info
.pointer_frame_offset
;
3697 sync_offset
= clicked_regionview
->region()->sync_offset (sync_dir
);
3699 /* we don't handle a sync point that lies before zero.
3701 if (sync_dir
>= 0 || (sync_dir
< 0 && pending_region_position
>= sync_offset
)) {
3702 sync_frame
= pending_region_position
+ (sync_dir
*sync_offset
);
3704 /* we snap if the snap modifier is not enabled.
3707 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
3708 snap_to (sync_frame
);
3711 pending_region_position
= clicked_regionview
->region()->adjust_to_sync (sync_frame
);
3714 pending_region_position
= drag_info
.last_frame_position
;
3718 pending_region_position
= 0;
3721 if (pending_region_position
> max_frames
- clicked_regionview
->region()->length()) {
3722 pending_region_position
= drag_info
.last_frame_position
;
3725 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3727 bool x_move_allowed
;
3729 if (Config
->get_edit_mode() == Lock
) {
3730 if (drag_info
.copy
) {
3731 x_move_allowed
= !drag_info
.x_constrained
;
3733 /* in locked edit mode, reverse the usual meaning of x_constrained */
3734 x_move_allowed
= drag_info
.x_constrained
;
3737 x_move_allowed
= !drag_info
.x_constrained
;
3740 if (( pending_region_position
!= drag_info
.last_frame_position
) && x_move_allowed
) {
3742 /* now compute the canvas unit distance we need to move the regionview
3743 to make it appear at the new location.
3746 if (pending_region_position
> drag_info
.last_frame_position
) {
3747 x_delta
= ((double) (pending_region_position
- drag_info
.last_frame_position
) / frames_per_unit
);
3749 x_delta
= -((double) (drag_info
.last_frame_position
- pending_region_position
) / frames_per_unit
);
3750 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin(); i
!= selection
->regions
.by_layer().end(); ++i
) {
3752 RegionView
* rv2
= (*i
);
3754 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3756 double ix1
, ix2
, iy1
, iy2
;
3757 rv2
->get_canvas_frame()->get_bounds (ix1
, iy1
, ix2
, iy2
);
3758 rv2
->get_canvas_group()->i2w (ix1
, iy1
);
3760 if (-x_delta
> ix1
+ horizontal_adjustment
.get_value()) {
3762 pending_region_position
= drag_info
.last_frame_position
;
3769 drag_info
.last_frame_position
= pending_region_position
;
3776 /* threshold not passed */
3781 /*************************************************************
3783 ************************************************************/
3785 if (x_delta
== 0 && (pointer_y_span
== 0)) {
3786 /* haven't reached next snap point, and we're not switching
3787 trackviews. nothing to do.
3792 /*************************************************************
3794 ************************************************************/
3795 bool do_move
= true;
3796 if (drag_info
.first_move
) {
3797 if (!drag_info
.move_threshold_passed
) {
3804 pair
<set
<boost::shared_ptr
<Playlist
> >::iterator
,bool> insert_result
;
3805 const list
<RegionView
*>& layered_regions
= selection
->regions
.by_layer();
3807 for (list
<RegionView
*>::const_iterator i
= layered_regions
.begin(); i
!= layered_regions
.end(); ++i
) {
3809 RegionView
* rv
= (*i
);
3810 double ix1
, ix2
, iy1
, iy2
;
3811 int32_t temp_pointer_y_span
= pointer_y_span
;
3813 if (rv
->region()->locked()) {
3817 /* get item BBox, which will be relative to parent. so we have
3818 to query on a child, then convert to world coordinates using
3822 rv
->get_canvas_frame()->get_bounds (ix1
, iy1
, ix2
, iy2
);
3823 rv
->get_canvas_group()->i2w (ix1
, iy1
);
3825 /* for evaluation of the track position of iy1, we have to adjust
3826 to allow for the vertical scrolling adjustment and the height of the timebars.
3829 iy1
+= get_trackview_group_vertical_offset ();
3830 if (drag_info
.first_move
) {
3832 // hide any dependent views
3834 rv
->get_time_axis_view().hide_dependent_views (*rv
);
3837 reparent to a non scrolling group so that we can keep the
3838 region selection above all time axis views.
3839 reparenting means we have to move the rv as the two
3840 parent groups have different coordinates.
3843 rv
->get_canvas_group()->property_y() = iy1
- 1;
3844 rv
->get_canvas_group()->reparent(*_region_motion_group
);
3846 rv
->fake_set_opaque (true);
3849 TimeAxisView
* tvp2
= trackview_by_y_position (iy1
);
3850 AudioTimeAxisView
* canvas_atv
= dynamic_cast<AudioTimeAxisView
*>(tvp2
);
3851 AudioTimeAxisView
* temp_atv
;
3853 if ((pointer_y_span
!= 0) && !clamp_y_axis
) {
3856 for (j
= height_list
.begin(); j
!= height_list
.end(); j
++) {
3857 if (x
== canvas_atv
->order
) {
3858 /* we found the track the region is on */
3859 if (x
!= original_pointer_order
) {
3860 /*this isn't from the same track we're dragging from */
3861 temp_pointer_y_span
= canvas_pointer_y_span
;
3863 while (temp_pointer_y_span
> 0) {
3864 /* we're moving up canvas-wise,
3865 so we need to find the next track height
3867 if (j
!= height_list
.begin()) {
3870 if (x
!= original_pointer_order
) {
3871 /* we're not from the dragged track, so ignore hidden tracks. */
3873 temp_pointer_y_span
++;
3877 temp_pointer_y_span
--;
3880 while (temp_pointer_y_span
< 0) {
3882 if (x
!= original_pointer_order
) {
3884 temp_pointer_y_span
--;
3888 if (j
!= height_list
.end()) {
3891 temp_pointer_y_span
++;
3893 /* find out where we'll be when we move and set height accordingly */
3895 tvp2
= trackview_by_y_position (iy1
+ y_delta
);
3896 temp_atv
= dynamic_cast<AudioTimeAxisView
*>(tvp2
);
3897 rv
->set_height (temp_atv
->current_height());
3899 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3900 personally, i think this can confuse things, but never mind.
3903 //const GdkColor& col (temp_atv->view->get_region_color());
3904 //rv->set_color (const_cast<GdkColor&>(col));
3911 if (drag_info
.brushing
) {
3912 mouse_brush_insert_region (rv
, pending_region_position
);
3914 rv
->move (x_delta
, y_delta
);
3917 } /* foreach region */
3921 if (drag_info
.first_move
&& drag_info
.move_threshold_passed
) {
3922 cursor_group
->raise_to_top();
3923 drag_info
.first_move
= false;
3926 if (x_delta
!= 0 && !drag_info
.brushing
) {
3927 show_verbose_time_cursor (drag_info
.last_frame_position
, 10);
3932 Editor::region_drag_finished_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
3934 bool nocommit
= true;
3935 vector
<RegionView
*> copies
;
3936 RouteTimeAxisView
* source_tv
;
3937 boost::shared_ptr
<Diskstream
> ds
;
3938 boost::shared_ptr
<Playlist
> from_playlist
;
3939 vector
<RegionView
*> new_selection
;
3940 typedef set
<boost::shared_ptr
<Playlist
> > PlaylistSet
;
3941 PlaylistSet modified_playlists
;
3942 PlaylistSet frozen_playlists
;
3943 list
<sigc::connection
> modified_playlist_connections
;
3944 pair
<PlaylistSet::iterator
,bool> insert_result
, frozen_insert_result
;
3945 nframes64_t drag_delta
;
3946 bool changed_tracks
, changed_position
;
3948 /* first_move is set to false if the regionview has been moved in the
3952 if (drag_info
.first_move
) {
3959 if (Config
->get_edit_mode() == Splice
&& !pre_drag_region_selection
.empty()) {
3960 selection
->set (pre_drag_region_selection
);
3961 pre_drag_region_selection
.clear ();
3964 if (drag_info
.brushing
) {
3965 /* all changes were made during motion event handlers */
3967 if (drag_info
.copy
) {
3968 for (list
<RegionView
*>::iterator i
= selection
->regions
.begin(); i
!= selection
->regions
.end(); ++i
) {
3969 copies
.push_back (*i
);
3978 /* reverse this here so that we have the correct logic to finalize
3982 if (Config
->get_edit_mode() == Lock
&& !drag_info
.copy
) {
3983 drag_info
.x_constrained
= !drag_info
.x_constrained
;
3986 if (drag_info
.copy
) {
3987 if (drag_info
.x_constrained
) {
3988 op_string
= _("fixed time region copy");
3990 op_string
= _("region copy");
3993 if (drag_info
.x_constrained
) {
3994 op_string
= _("fixed time region drag");
3996 op_string
= _("region drag");
4000 begin_reversible_command (op_string
);
4001 changed_position
= (drag_info
.last_frame_position
!= (nframes64_t
) (clicked_regionview
->region()->position()));
4002 changed_tracks
= (trackview_by_y_position (drag_info
.current_pointer_y
) != &clicked_regionview
->get_time_axis_view());
4004 drag_delta
= clicked_regionview
->region()->position() - drag_info
.last_frame_position
;
4006 track_canvas
->update_now ();
4008 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin(); i
!= selection
->regions
.by_layer().end(); ) {
4010 RegionView
* rv
= (*i
);
4011 double ix1
, ix2
, iy1
, iy2
;
4012 rv
->get_canvas_frame()->get_bounds (ix1
, iy1
, ix2
, iy2
);
4013 rv
->get_canvas_group()->i2w (ix1
, iy1
);
4014 iy1
+= vertical_adjustment
.get_value() - canvas_timebars_vsize
;
4016 TimeAxisView
* dest_tv
= trackview_by_y_position (iy1
);
4017 AudioTimeAxisView
* dest_atv
= dynamic_cast<AudioTimeAxisView
*>(dest_tv
);
4020 if (rv
->region()->locked()) {
4025 if (changed_position
&& !drag_info
.x_constrained
) {
4026 where
= rv
->region()->position() - drag_delta
;
4028 where
= rv
->region()->position();
4031 boost::shared_ptr
<Region
> new_region
;
4033 if (drag_info
.copy
) {
4034 /* we already made a copy */
4035 new_region
= rv
->region();
4037 /* undo the previous hide_dependent_views so that xfades don't
4038 disappear on copying regions
4041 //rv->get_time_axis_view().reveal_dependent_views (*rv);
4043 } else if (changed_tracks
) {
4044 new_region
= RegionFactory::create (rv
->region());
4047 if (changed_tracks
|| drag_info
.copy
) {
4049 boost::shared_ptr
<Playlist
> to_playlist
= dest_atv
->playlist();
4051 latest_regionviews
.clear ();
4053 sigc::connection c
= dest_atv
->view()->RegionViewAdded
.connect (mem_fun(*this, &Editor::collect_new_region_view
));
4055 insert_result
= modified_playlists
.insert (to_playlist
);
4056 if (insert_result
.second
) {
4057 session
->add_command (new MementoCommand
<Playlist
>(*to_playlist
, &to_playlist
->get_state(), 0));
4060 to_playlist
->add_region (new_region
, where
);
4064 if (!latest_regionviews
.empty()) {
4065 // XXX why just the first one ? we only expect one
4066 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
4067 new_selection
.push_back (latest_regionviews
.front());
4072 motion on the same track. plonk the previously reparented region
4073 back to its original canvas group (its streamview).
4074 No need to do anything for copies as they are fake regions which will be deleted.
4077 RouteTimeAxisView
* dest_rtv
= dynamic_cast<RouteTimeAxisView
*> (dest_atv
);
4078 rv
->get_canvas_group()->reparent (*dest_rtv
->view()->canvas_item());
4079 rv
->get_canvas_group()->property_y() = 0;
4081 /* just change the model */
4083 boost::shared_ptr
<Playlist
> playlist
= dest_atv
->playlist();
4085 insert_result
= modified_playlists
.insert (playlist
);
4086 if (insert_result
.second
) {
4087 session
->add_command (new MementoCommand
<Playlist
>(*playlist
, &playlist
->get_state(), 0));
4089 /* freeze to avoid lots of relayering in the case of a multi-region drag */
4090 frozen_insert_result
= frozen_playlists
.insert(playlist
);
4091 if (frozen_insert_result
.second
) {
4095 rv
->region()->set_position (where
, (void*) this);
4098 if (changed_tracks
&& !drag_info
.copy
) {
4100 /* get the playlist where this drag started. we can't use rv->region()->playlist()
4101 because we may have copied the region and it has not been attached to a playlist.
4104 assert ((source_tv
= dynamic_cast<RouteTimeAxisView
*> (&rv
->get_time_axis_view())));
4105 assert ((ds
= source_tv
->get_diskstream()));
4106 assert ((from_playlist
= ds
->playlist()));
4108 /* moved to a different audio track, without copying */
4110 /* the region that used to be in the old playlist is not
4111 moved to the new one - we use a copy of it. as a result,
4112 any existing editor for the region should no longer be
4116 rv
->hide_region_editor();
4117 rv
->fake_set_opaque (false);
4119 /* remove the region from the old playlist */
4121 insert_result
= modified_playlists
.insert (from_playlist
);
4122 if (insert_result
.second
) {
4123 session
->add_command (new MementoCommand
<Playlist
>(*from_playlist
, &from_playlist
->get_state(), 0));
4126 from_playlist
->remove_region ((rv
->region()));
4128 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
4129 was selected in all of them, then removing it from a playlist will have removed all
4130 trace of it from the selection (i.e. there were N regions selected, we removed 1,
4131 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
4132 corresponding regionview, and the selection is now empty).
4134 this could have invalidated any and all iterators into the region selection.
4136 the heuristic we use here is: if the region selection is empty, break out of the loop
4137 here. if the region selection is not empty, then restart the loop because we know that
4138 we must have removed at least the region(view) we've just been working on as well as any
4139 that we processed on previous iterations.
4141 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
4142 we can just iterate.
4145 if (selection
->regions
.empty()) {
4148 i
= selection
->regions
.by_layer().begin();
4155 if (drag_info
.copy
) {
4156 copies
.push_back (rv
);
4160 if (new_selection
.empty()) {
4161 if (drag_info
.copy
) {
4162 /* the region(view)s that are selected and being dragged around
4163 are copies and do not belong to any track. remove them
4164 from the selection right here.
4166 selection
->clear_regions();
4169 /* this will clear any existing selection that would have been
4170 cleared in the other clause above
4172 selection
->set (new_selection
);
4175 for (set
<boost::shared_ptr
<Playlist
> >::iterator p
= frozen_playlists
.begin(); p
!= frozen_playlists
.end(); ++p
) {
4181 for (set
<boost::shared_ptr
<Playlist
> >::iterator p
= modified_playlists
.begin(); p
!= modified_playlists
.end(); ++p
) {
4182 session
->add_command (new MementoCommand
<Playlist
>(*(*p
), 0, &(*p
)->get_state()));
4184 commit_reversible_command ();
4187 for (vector
<RegionView
*>::iterator x
= copies
.begin(); x
!= copies
.end(); ++x
) {
4194 Editor::region_view_item_click (AudioRegionView
& rv
, GdkEventButton
* event
)
4196 /* Either add to or set the set the region selection, unless
4197 this is an alignment click (control used)
4200 if (Keyboard::modifier_state_contains (event
->state
, Keyboard::PrimaryModifier
)) {
4201 TimeAxisView
* tv
= &rv
.get_time_axis_view();
4202 AudioTimeAxisView
* atv
= dynamic_cast<AudioTimeAxisView
*>(tv
);
4204 if (atv
&& atv
->is_audio_track()) {
4205 speed
= atv
->get_diskstream()->speed();
4208 nframes64_t where
= get_preferred_edit_position();
4212 if (Keyboard::modifier_state_equals (event
->state
, Keyboard::ModifierMask (Keyboard::PrimaryModifier
|Keyboard::SecondaryModifier
))) {
4214 align_region (rv
.region(), SyncPoint
, (nframes64_t
) (where
* speed
));
4216 } else if (Keyboard::modifier_state_equals (event
->state
, Keyboard::ModifierMask (Keyboard::PrimaryModifier
|Keyboard::TertiaryModifier
))) {
4218 align_region (rv
.region(), End
, (nframes64_t
) (where
* speed
));
4222 align_region (rv
.region(), Start
, (nframes64_t
) (where
* speed
));
4229 Editor::show_verbose_time_cursor (nframes64_t frame
, double offset
, double xpos
, double ypos
)
4235 nframes64_t frame_rate
;
4244 if (Profile
->get_sae() || Profile
->get_small_screen()) {
4245 m
= ARDOUR_UI::instance()->primary_clock
.mode();
4247 m
= ARDOUR_UI::instance()->secondary_clock
.mode();
4251 case AudioClock::BBT
:
4252 session
->bbt_time (frame
, bbt
);
4253 snprintf (buf
, sizeof (buf
), "%02" PRIu32
"|%02" PRIu32
"|%02" PRIu32
, bbt
.bars
, bbt
.beats
, bbt
.ticks
);
4256 case AudioClock::SMPTE
:
4257 session
->smpte_time (frame
, smpte
);
4258 snprintf (buf
, sizeof (buf
), "%02" PRId32
":%02" PRId32
":%02" PRId32
":%02" PRId32
, smpte
.hours
, smpte
.minutes
, smpte
.seconds
, smpte
.frames
);
4261 case AudioClock::MinSec
:
4262 /* XXX this is copied from show_verbose_duration_cursor() */
4263 frame_rate
= session
->frame_rate();
4264 hours
= frame
/ (frame_rate
* 3600);
4265 frame
= frame
% (frame_rate
* 3600);
4266 mins
= frame
/ (frame_rate
* 60);
4267 frame
= frame
% (frame_rate
* 60);
4268 secs
= (float) frame
/ (float) frame_rate
;
4269 snprintf (buf
, sizeof (buf
), "%02" PRId32
":%02" PRId32
":%.4f", hours
, mins
, secs
);
4273 snprintf (buf
, sizeof(buf
), "%" PRIi64
, frame
);
4277 if (xpos
>= 0 && ypos
>=0) {
4278 set_verbose_canvas_cursor (buf
, xpos
+ offset
, ypos
+ offset
);
4281 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
);
4283 show_verbose_canvas_cursor ();
4287 Editor::show_verbose_duration_cursor (nframes64_t start
, nframes64_t end
, double offset
, double xpos
, double ypos
)
4294 nframes64_t distance
, frame_rate
;
4296 Meter
meter_at_start(session
->tempo_map().meter_at(start
));
4304 if (Profile
->get_sae() || Profile
->get_small_screen()) {
4305 m
= ARDOUR_UI::instance()->primary_clock
.mode ();
4307 m
= ARDOUR_UI::instance()->secondary_clock
.mode ();
4311 case AudioClock::BBT
:
4312 session
->bbt_time (start
, sbbt
);
4313 session
->bbt_time (end
, ebbt
);
4316 /* XXX this computation won't work well if the
4317 user makes a selection that spans any meter changes.
4320 ebbt
.bars
-= sbbt
.bars
;
4321 if (ebbt
.beats
>= sbbt
.beats
) {
4322 ebbt
.beats
-= sbbt
.beats
;
4325 ebbt
.beats
= int(meter_at_start
.beats_per_bar()) + ebbt
.beats
- sbbt
.beats
;
4327 if (ebbt
.ticks
>= sbbt
.ticks
) {
4328 ebbt
.ticks
-= sbbt
.ticks
;
4331 ebbt
.ticks
= int(Meter::ticks_per_beat
) + ebbt
.ticks
- sbbt
.ticks
;
4334 snprintf (buf
, sizeof (buf
), "%02" PRIu32
"|%02" PRIu32
"|%02" PRIu32
, ebbt
.bars
, ebbt
.beats
, ebbt
.ticks
);
4337 case AudioClock::SMPTE
:
4338 session
->smpte_duration (end
- start
, smpte
);
4339 snprintf (buf
, sizeof (buf
), "%02" PRId32
":%02" PRId32
":%02" PRId32
":%02" PRId32
, smpte
.hours
, smpte
.minutes
, smpte
.seconds
, smpte
.frames
);
4342 case AudioClock::MinSec
:
4343 /* XXX this stuff should be elsewhere.. */
4344 distance
= end
- start
;
4345 frame_rate
= session
->frame_rate();
4346 hours
= distance
/ (frame_rate
* 3600);
4347 distance
= distance
% (frame_rate
* 3600);
4348 mins
= distance
/ (frame_rate
* 60);
4349 distance
= distance
% (frame_rate
* 60);
4350 secs
= (float) distance
/ (float) frame_rate
;
4351 snprintf (buf
, sizeof (buf
), "%02" PRId32
":%02" PRId32
":%.4f", hours
, mins
, secs
);
4355 snprintf (buf
, sizeof(buf
), "%" PRIi64
, end
- start
);
4359 if (xpos
>= 0 && ypos
>=0) {
4360 set_verbose_canvas_cursor (buf
, xpos
+ offset
, ypos
+ offset
);
4363 set_verbose_canvas_cursor (buf
, drag_info
.current_pointer_x
+ offset
, drag_info
.current_pointer_y
+ offset
);
4366 show_verbose_canvas_cursor ();
4370 Editor::collect_new_region_view (RegionView
* rv
)
4372 latest_regionviews
.push_back (rv
);
4376 Editor::start_selection_grab (ArdourCanvas::Item
* item
, GdkEvent
* event
)
4378 if (clicked_regionview
== 0) {
4382 /* lets try to create new Region for the selection */
4384 vector
<boost::shared_ptr
<AudioRegion
> > new_regions
;
4385 create_region_from_selection (new_regions
);
4387 if (new_regions
.empty()) {
4391 /* XXX fix me one day to use all new regions */
4393 boost::shared_ptr
<Region
> region (new_regions
.front());
4395 /* add it to the current stream/playlist.
4397 tricky: the streamview for the track will add a new regionview. we will
4398 catch the signal it sends when it creates the regionview to
4399 set the regionview we want to then drag.
4402 latest_regionviews
.clear();
4403 sigc::connection c
= clicked_audio_trackview
->view()->RegionViewAdded
.connect (mem_fun(*this, &Editor::collect_new_region_view
));
4405 /* A selection grab currently creates two undo/redo operations, one for
4406 creating the new region and another for moving it.
4409 begin_reversible_command (_("selection grab"));
4411 boost::shared_ptr
<Playlist
> playlist
= clicked_trackview
->playlist();
4413 XMLNode
*before
= &(playlist
->get_state());
4414 clicked_trackview
->playlist()->add_region (region
, selection
->time
[clicked_selection
].start
);
4415 XMLNode
*after
= &(playlist
->get_state());
4416 session
->add_command(new MementoCommand
<Playlist
>(*playlist
, before
, after
));
4418 commit_reversible_command ();
4422 if (latest_regionviews
.empty()) {
4423 /* something went wrong */
4427 /* we need to deselect all other regionviews, and select this one
4428 i'm ignoring undo stuff, because the region creation will take care of it
4430 selection
->set (latest_regionviews
);
4432 drag_info
.item
= latest_regionviews
.front()->get_canvas_group();
4433 drag_info
.data
= latest_regionviews
.front();
4434 drag_info
.motion_callback
= &Editor::region_drag_motion_callback
;
4435 drag_info
.finished_callback
= &Editor::region_drag_finished_callback
;
4439 drag_info
.source_trackview
= clicked_trackview
;
4440 drag_info
.dest_trackview
= drag_info
.source_trackview
;
4441 drag_info
.last_frame_position
= latest_regionviews
.front()->region()->position();
4442 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- drag_info
.last_frame_position
;
4444 show_verbose_time_cursor (drag_info
.last_frame_position
, 10);
4448 Editor::cancel_selection ()
4450 for (TrackViewList::iterator i
= track_views
.begin(); i
!= track_views
.end(); ++i
) {
4451 (*i
)->hide_selection ();
4453 selection
->clear ();
4454 clicked_selection
= 0;
4458 Editor::start_selection_op (ArdourCanvas::Item
* item
, GdkEvent
* event
, SelectionOp op
)
4460 nframes64_t start
= 0;
4461 nframes64_t end
= 0;
4467 drag_info
.item
= item
;
4468 drag_info
.motion_callback
= &Editor::drag_selection
;
4469 drag_info
.finished_callback
= &Editor::end_selection_op
;
4474 case CreateSelection
:
4475 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::TertiaryModifier
)) {
4476 drag_info
.copy
= true;
4478 drag_info
.copy
= false;
4480 start_grab (event
, selector_cursor
);
4483 case SelectionStartTrim
:
4484 if (clicked_trackview
) {
4485 clicked_trackview
->order_selection_trims (item
, true);
4487 start_grab (event
, trimmer_cursor
);
4488 start
= selection
->time
[clicked_selection
].start
;
4489 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- start
;
4492 case SelectionEndTrim
:
4493 if (clicked_trackview
) {
4494 clicked_trackview
->order_selection_trims (item
, false);
4496 start_grab (event
, trimmer_cursor
);
4497 end
= selection
->time
[clicked_selection
].end
;
4498 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- end
;
4502 start
= selection
->time
[clicked_selection
].start
;
4504 drag_info
.pointer_frame_offset
= drag_info
.grab_frame
- start
;
4508 if (selection_op
== SelectionMove
) {
4509 show_verbose_time_cursor(start
, 10);
4511 show_verbose_time_cursor(drag_info
.current_pointer_frame
, 10);
4516 Editor::drag_selection (ArdourCanvas::Item
* item
, GdkEvent
* event
)
4518 nframes64_t start
= 0;
4519 nframes64_t end
= 0;
4521 nframes64_t pending_position
;
4523 if (drag_info
.current_pointer_frame
> drag_info
.pointer_frame_offset
) {
4524 pending_position
= drag_info
.current_pointer_frame
- drag_info
.pointer_frame_offset
;
4526 pending_position
= 0;
4529 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
4530 snap_to (pending_position
);
4533 /* only alter selection if the current frame is
4534 different from the last frame position (adjusted)
4537 if (pending_position
== drag_info
.last_pointer_frame
) return;
4539 switch (selection_op
) {
4540 case CreateSelection
:
4542 if (drag_info
.first_move
) {
4543 snap_to (drag_info
.grab_frame
);
4546 if (pending_position
< drag_info
.grab_frame
) {
4547 start
= pending_position
;
4548 end
= drag_info
.grab_frame
;
4550 end
= pending_position
;
4551 start
= drag_info
.grab_frame
;
4554 /* first drag: Either add to the selection
4555 or create a new selection->
4558 if (drag_info
.first_move
) {
4560 begin_reversible_command (_("range selection"));
4562 if (drag_info
.copy
) {
4563 /* adding to the selection */
4564 clicked_selection
= selection
->add (start
, end
);
4565 drag_info
.copy
= false;
4567 /* new selection-> */
4568 clicked_selection
= selection
->set (clicked_trackview
, start
, end
);
4573 case SelectionStartTrim
:
4575 if (drag_info
.first_move
) {
4576 begin_reversible_command (_("trim selection start"));
4579 start
= selection
->time
[clicked_selection
].start
;
4580 end
= selection
->time
[clicked_selection
].end
;
4582 if (pending_position
> end
) {
4585 start
= pending_position
;
4589 case SelectionEndTrim
:
4591 if (drag_info
.first_move
) {
4592 begin_reversible_command (_("trim selection end"));
4595 start
= selection
->time
[clicked_selection
].start
;
4596 end
= selection
->time
[clicked_selection
].end
;
4598 if (pending_position
< start
) {
4601 end
= pending_position
;
4608 if (drag_info
.first_move
) {
4609 begin_reversible_command (_("move selection"));
4612 start
= selection
->time
[clicked_selection
].start
;
4613 end
= selection
->time
[clicked_selection
].end
;
4615 length
= end
- start
;
4617 start
= pending_position
;
4620 end
= start
+ length
;
4625 if (event
->button
.x
>= horizontal_adjustment
.get_value() + canvas_width
) {
4626 start_canvas_autoscroll (1, 0);
4630 selection
->replace (clicked_selection
, start
, end
);
4633 drag_info
.last_pointer_frame
= pending_position
;
4634 drag_info
.first_move
= false;
4636 if (selection_op
== SelectionMove
) {
4637 show_verbose_time_cursor(start
, 10);
4639 show_verbose_time_cursor(pending_position
, 10);
4644 Editor::end_selection_op (ArdourCanvas::Item
* item
, GdkEvent
* event
)
4646 if (!drag_info
.first_move
) {
4647 drag_selection (item
, event
);
4648 /* XXX this is not object-oriented programming at all. ick */
4649 if (selection
->time
.consolidate()) {
4650 selection
->TimeChanged ();
4652 commit_reversible_command ();
4654 /* just a click, no pointer movement.*/
4656 if (Keyboard::no_modifier_keys_pressed (&event
->button
)) {
4658 selection
->clear_time();
4663 /* XXX what happens if its a music selection? */
4664 session
->set_audio_range (selection
->time
);
4665 stop_canvas_autoscroll ();
4669 Editor::start_trim (ArdourCanvas::Item
* item
, GdkEvent
* event
)
4672 TimeAxisView
* tvp
= clicked_trackview
;
4673 AudioTimeAxisView
* tv
= dynamic_cast<AudioTimeAxisView
*>(tvp
);
4675 if (tv
&& tv
->is_audio_track()) {
4676 speed
= tv
->get_diskstream()->speed();
4679 nframes64_t region_start
= (nframes64_t
) (clicked_regionview
->region()->position() / speed
);
4680 nframes64_t region_end
= (nframes64_t
) (clicked_regionview
->region()->last_frame() / speed
);
4681 nframes64_t region_length
= (nframes64_t
) (clicked_regionview
->region()->length() / speed
);
4683 //drag_info.item = clicked_regionview->get_name_highlight();
4684 drag_info
.item
= item
;
4685 drag_info
.motion_callback
= &Editor::trim_motion_callback
;
4686 drag_info
.finished_callback
= &Editor::trim_finished_callback
;
4688 start_grab (event
, trimmer_cursor
);
4690 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
4691 trim_op
= ContentsTrim
;
4693 /* These will get overridden for a point trim.*/
4694 if (drag_info
.current_pointer_frame
< (region_start
+ region_length
/2)) {
4695 /* closer to start */
4696 trim_op
= StartTrim
;
4697 } else if (drag_info
.current_pointer_frame
> (region_end
- region_length
/2)) {
4705 show_verbose_time_cursor(region_start
, 10);
4708 show_verbose_time_cursor(region_end
, 10);
4711 show_verbose_time_cursor(drag_info
.current_pointer_frame
, 10);
4717 Editor::trim_motion_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
4719 RegionView
* rv
= clicked_regionview
;
4720 nframes64_t frame_delta
= 0;
4721 bool left_direction
;
4722 bool obey_snap
= !Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier());
4724 /* snap modifier works differently here..
4725 its' current state has to be passed to the
4726 various trim functions in order to work properly
4730 TimeAxisView
* tvp
= clicked_trackview
;
4731 RouteTimeAxisView
* tv
= dynamic_cast<RouteTimeAxisView
*>(tvp
);
4732 pair
<set
<boost::shared_ptr
<Playlist
> >::iterator
,bool> insert_result
;
4734 if (tv
&& tv
->is_audio_track()) {
4735 speed
= tv
->get_diskstream()->speed();
4738 if (drag_info
.last_pointer_frame
> drag_info
.current_pointer_frame
) {
4739 left_direction
= true;
4741 left_direction
= false;
4745 snap_to (drag_info
.current_pointer_frame
);
4748 if (drag_info
.current_pointer_frame
== drag_info
.last_pointer_frame
) {
4752 if (drag_info
.first_move
) {
4758 trim_type
= "Region start trim";
4761 trim_type
= "Region end trim";
4764 trim_type
= "Region content trim";
4768 begin_reversible_command (trim_type
);
4770 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin(); i
!= selection
->regions
.by_layer().end(); ++i
) {
4771 (*i
)->fake_set_opaque(false);
4772 (*i
)->region()->freeze ();
4774 AudioRegionView
* const arv
= dynamic_cast<AudioRegionView
*>(*i
);
4776 arv
->temporarily_hide_envelope ();
4778 boost::shared_ptr
<Playlist
> pl
= (*i
)->region()->playlist();
4779 insert_result
= motion_frozen_playlists
.insert (pl
);
4780 if (insert_result
.second
) {
4781 session
->add_command(new MementoCommand
<Playlist
>(*pl
, &pl
->get_state(), 0));
4786 if (left_direction
) {
4787 frame_delta
= (drag_info
.last_pointer_frame
- drag_info
.current_pointer_frame
);
4789 frame_delta
= (drag_info
.current_pointer_frame
- drag_info
.last_pointer_frame
);
4794 if ((left_direction
== false) && (drag_info
.current_pointer_frame
<= rv
->region()->first_frame()/speed
)) {
4797 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin(); i
!= selection
->regions
.by_layer().end(); ++i
) {
4798 single_start_trim (**i
, frame_delta
, left_direction
, obey_snap
);
4804 if ((left_direction
== true) && (drag_info
.current_pointer_frame
> (nframes64_t
) (rv
->region()->last_frame()/speed
))) {
4807 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin(); i
!= selection
->regions
.by_layer().end(); ++i
) {
4808 single_end_trim (**i
, frame_delta
, left_direction
, obey_snap
);
4815 bool swap_direction
= false;
4817 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
4818 swap_direction
= true;
4821 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin();
4822 i
!= selection
->regions
.by_layer().end(); ++i
)
4824 single_contents_trim (**i
, frame_delta
, left_direction
, swap_direction
, obey_snap
);
4832 show_verbose_time_cursor((nframes64_t
) (rv
->region()->position()/speed
), 10);
4835 show_verbose_time_cursor((nframes64_t
) (rv
->region()->last_frame()/speed
), 10);
4838 show_verbose_time_cursor(drag_info
.current_pointer_frame
, 10);
4842 drag_info
.last_pointer_frame
= drag_info
.current_pointer_frame
;
4843 drag_info
.first_move
= false;
4847 Editor::single_contents_trim (RegionView
& rv
, nframes64_t frame_delta
, bool left_direction
, bool swap_direction
, bool obey_snap
)
4849 boost::shared_ptr
<Region
> region (rv
.region());
4851 if (region
->locked()) {
4855 nframes64_t new_bound
;
4858 TimeAxisView
* tvp
= clicked_trackview
;
4859 RouteTimeAxisView
* tv
= dynamic_cast<RouteTimeAxisView
*>(tvp
);
4861 if (tv
&& tv
->is_audio_track()) {
4862 speed
= tv
->get_diskstream()->speed();
4865 if (left_direction
) {
4866 if (swap_direction
) {
4867 new_bound
= (nframes64_t
) (region
->position()/speed
) + frame_delta
;
4869 new_bound
= (nframes64_t
) (region
->position()/speed
) - frame_delta
;
4872 if (swap_direction
) {
4873 new_bound
= (nframes64_t
) (region
->position()/speed
) - frame_delta
;
4875 new_bound
= (nframes64_t
) (region
->position()/speed
) + frame_delta
;
4880 snap_to (new_bound
);
4882 region
->trim_start ((nframes64_t
) (new_bound
* speed
), this);
4883 rv
.region_changed (StartChanged
);
4887 Editor::single_start_trim (RegionView
& rv
, nframes64_t frame_delta
, bool left_direction
, bool obey_snap
)
4889 boost::shared_ptr
<Region
> region (rv
.region());
4891 if (region
->locked()) {
4895 nframes64_t new_bound
;
4898 TimeAxisView
* tvp
= clicked_trackview
;
4899 AudioTimeAxisView
* tv
= dynamic_cast<AudioTimeAxisView
*>(tvp
);
4901 if (tv
&& tv
->is_audio_track()) {
4902 speed
= tv
->get_diskstream()->speed();
4905 if (left_direction
) {
4906 new_bound
= (nframes64_t
) (region
->position()/speed
) - frame_delta
;
4908 new_bound
= (nframes64_t
) (region
->position()/speed
) + frame_delta
;
4912 snap_to (new_bound
, (left_direction
? 0 : 1));
4915 region
->trim_front ((nframes64_t
) (new_bound
* speed
), this);
4917 rv
.region_changed (Change (LengthChanged
|PositionChanged
|StartChanged
));
4921 Editor::single_end_trim (RegionView
& rv
, nframes64_t frame_delta
, bool left_direction
, bool obey_snap
)
4923 boost::shared_ptr
<Region
> region (rv
.region());
4925 if (region
->locked()) {
4929 nframes64_t new_bound
;
4932 TimeAxisView
* tvp
= clicked_trackview
;
4933 AudioTimeAxisView
* tv
= dynamic_cast<AudioTimeAxisView
*>(tvp
);
4935 if (tv
&& tv
->is_audio_track()) {
4936 speed
= tv
->get_diskstream()->speed();
4939 if (left_direction
) {
4940 new_bound
= (nframes64_t
) ((region
->last_frame() + 1)/speed
) - frame_delta
;
4942 new_bound
= (nframes64_t
) ((region
->last_frame() + 1)/speed
) + frame_delta
;
4946 snap_to (new_bound
);
4948 region
->trim_end ((nframes64_t
) (new_bound
* speed
), this);
4949 rv
.region_changed (LengthChanged
);
4953 Editor::trim_finished_callback (ArdourCanvas::Item
* item
, GdkEvent
* event
)
4955 if (!drag_info
.first_move
) {
4956 trim_motion_callback (item
, event
);
4958 if (!selection
->selected (clicked_regionview
)) {
4959 thaw_region_after_trim (*clicked_regionview
);
4962 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin();
4963 i
!= selection
->regions
.by_layer().end(); ++i
)
4965 thaw_region_after_trim (**i
);
4966 (*i
)->fake_set_opaque (true);
4970 for (set
<boost::shared_ptr
<Playlist
> >::iterator p
= motion_frozen_playlists
.begin(); p
!= motion_frozen_playlists
.end(); ++p
) {
4972 session
->add_command (new MementoCommand
<Playlist
>(*(*p
).get(), 0, &(*p
)->get_state()));
4975 motion_frozen_playlists
.clear ();
4977 commit_reversible_command();
4979 /* no mouse movement */
4985 Editor::point_trim (GdkEvent
* event
)
4987 RegionView
* rv
= clicked_regionview
;
4988 nframes64_t new_bound
= drag_info
.current_pointer_frame
;
4990 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
4991 snap_to (new_bound
);
4994 /* Choose action dependant on which button was pressed */
4995 switch (event
->button
.button
) {
4997 trim_op
= StartTrim
;
4998 begin_reversible_command (_("Start point trim"));
5000 if (selection
->selected (rv
)) {
5002 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin();
5003 i
!= selection
->regions
.by_layer().end(); ++i
)
5005 if (!(*i
)->region()->locked()) {
5006 boost::shared_ptr
<Playlist
> pl
= (*i
)->region()->playlist();
5007 XMLNode
&before
= pl
->get_state();
5008 (*i
)->region()->trim_front (new_bound
, this);
5009 XMLNode
&after
= pl
->get_state();
5010 session
->add_command(new MementoCommand
<Playlist
>(*pl
.get(), &before
, &after
));
5016 if (!rv
->region()->locked()) {
5017 boost::shared_ptr
<Playlist
> pl
= rv
->region()->playlist();
5018 XMLNode
&before
= pl
->get_state();
5019 rv
->region()->trim_front (new_bound
, this);
5020 XMLNode
&after
= pl
->get_state();
5021 session
->add_command(new MementoCommand
<Playlist
>(*pl
.get(), &before
, &after
));
5025 commit_reversible_command();
5030 begin_reversible_command (_("End point trim"));
5032 if (selection
->selected (rv
)) {
5034 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin(); i
!= selection
->regions
.by_layer().end(); ++i
)
5036 if (!(*i
)->region()->locked()) {
5037 boost::shared_ptr
<Playlist
> pl
= (*i
)->region()->playlist();
5038 XMLNode
&before
= pl
->get_state();
5039 (*i
)->region()->trim_end (new_bound
, this);
5040 XMLNode
&after
= pl
->get_state();
5041 session
->add_command(new MementoCommand
<Playlist
>(*pl
.get(), &before
, &after
));
5047 if (!rv
->region()->locked()) {
5048 boost::shared_ptr
<Playlist
> pl
= rv
->region()->playlist();
5049 XMLNode
&before
= pl
->get_state();
5050 rv
->region()->trim_end (new_bound
, this);
5051 XMLNode
&after
= pl
->get_state();
5052 session
->add_command (new MementoCommand
<Playlist
>(*pl
.get(), &before
, &after
));
5056 commit_reversible_command();
5065 Editor::thaw_region_after_trim (RegionView
& rv
)
5067 boost::shared_ptr
<Region
> region (rv
.region());
5069 if (region
->locked()) {
5073 region
->thaw (_("trimmed region"));
5074 XMLNode
&after
= region
->playlist()->get_state();
5075 session
->add_command (new MementoCommand
<Playlist
>(*(region
->playlist()), 0, &after
));
5077 AudioRegionView
* arv
= dynamic_cast<AudioRegionView
*>(&rv
);
5079 arv
->unhide_envelope ();
5083 Editor::hide_marker (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5088 if ((marker
= static_cast<Marker
*> (item
->get_data ("marker"))) == 0) {
5089 fatal
<< _("programming error: marker canvas item has no marker object pointer!") << endmsg
;
5093 Location
* location
= find_location_from_marker (marker
, is_start
);
5094 location
->set_hidden (true, this);
5099 Editor::start_range_markerbar_op (ArdourCanvas::Item
* item
, GdkEvent
* event
, RangeMarkerOp op
)
5105 drag_info
.item
= item
;
5106 drag_info
.motion_callback
= &Editor::drag_range_markerbar_op
;
5107 drag_info
.finished_callback
= &Editor::end_range_markerbar_op
;
5109 range_marker_op
= op
;
5111 if (!temp_location
) {
5112 temp_location
= new Location
;
5116 case CreateRangeMarker
:
5117 case CreateTransportMarker
:
5118 case CreateCDMarker
:
5120 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::TertiaryModifier
)) {
5121 drag_info
.copy
= true;
5123 drag_info
.copy
= false;
5125 start_grab (event
, selector_cursor
);
5129 show_verbose_time_cursor(drag_info
.current_pointer_frame
, 10);
5134 Editor::drag_range_markerbar_op (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5136 nframes64_t start
= 0;
5137 nframes64_t end
= 0;
5138 ArdourCanvas::SimpleRect
*crect
;
5140 switch (range_marker_op
) {
5141 case CreateRangeMarker
:
5142 crect
= range_bar_drag_rect
;
5144 case CreateTransportMarker
:
5145 crect
= transport_bar_drag_rect
;
5147 case CreateCDMarker
:
5148 crect
= cd_marker_bar_drag_rect
;
5151 cerr
<< "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl
;
5156 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
5157 snap_to (drag_info
.current_pointer_frame
);
5160 /* only alter selection if the current frame is
5161 different from the last frame position.
5164 if (drag_info
.current_pointer_frame
== drag_info
.last_pointer_frame
) return;
5166 switch (range_marker_op
) {
5167 case CreateRangeMarker
:
5168 case CreateTransportMarker
:
5169 case CreateCDMarker
:
5170 if (drag_info
.first_move
) {
5171 snap_to (drag_info
.grab_frame
);
5174 if (drag_info
.current_pointer_frame
< drag_info
.grab_frame
) {
5175 start
= drag_info
.current_pointer_frame
;
5176 end
= drag_info
.grab_frame
;
5178 end
= drag_info
.current_pointer_frame
;
5179 start
= drag_info
.grab_frame
;
5182 /* first drag: Either add to the selection
5183 or create a new selection.
5186 if (drag_info
.first_move
) {
5188 temp_location
->set (start
, end
);
5192 update_marker_drag_item (temp_location
);
5193 range_marker_drag_rect
->show();
5194 //range_marker_drag_rect->raise_to_top();
5200 if (event
->button
.x
>= horizontal_adjustment
.get_value() + canvas_width
) {
5201 start_canvas_autoscroll (1, 0);
5205 temp_location
->set (start
, end
);
5207 double x1
= frame_to_pixel (start
);
5208 double x2
= frame_to_pixel (end
);
5209 crect
->property_x1() = x1
;
5210 crect
->property_x2() = x2
;
5212 update_marker_drag_item (temp_location
);
5215 drag_info
.last_pointer_frame
= drag_info
.current_pointer_frame
;
5216 drag_info
.first_move
= false;
5218 show_verbose_time_cursor(drag_info
.current_pointer_frame
, 10);
5223 Editor::end_range_markerbar_op (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5225 Location
* newloc
= 0;
5229 if (!drag_info
.first_move
) {
5230 drag_range_markerbar_op (item
, event
);
5232 switch (range_marker_op
) {
5233 case CreateRangeMarker
:
5234 case CreateCDMarker
:
5236 begin_reversible_command (_("new range marker"));
5237 XMLNode
&before
= session
->locations()->get_state();
5238 session
->locations()->next_available_name(rangename
,"unnamed");
5239 if (range_marker_op
== CreateCDMarker
) {
5240 flags
= Location::IsRangeMarker
|Location::IsCDMarker
;
5241 cd_marker_bar_drag_rect
->hide();
5244 flags
= Location::IsRangeMarker
;
5245 range_bar_drag_rect
->hide();
5247 newloc
= new Location(temp_location
->start(), temp_location
->end(), rangename
, (Location::Flags
) flags
);
5248 session
->locations()->add (newloc
, true);
5249 XMLNode
&after
= session
->locations()->get_state();
5250 session
->add_command(new MementoCommand
<Locations
>(*(session
->locations()), &before
, &after
));
5251 commit_reversible_command ();
5253 range_marker_drag_rect
->hide();
5257 case CreateTransportMarker
:
5258 // popup menu to pick loop or punch
5259 new_transport_marker_context_menu (&event
->button
, item
);
5264 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5266 if (Keyboard::no_modifier_keys_pressed (&event
->button
) && range_marker_op
!= CreateCDMarker
) {
5271 start
= session
->locations()->first_mark_before (drag_info
.grab_frame
);
5272 end
= session
->locations()->first_mark_after (drag_info
.grab_frame
);
5274 if (end
== max_frames
) {
5275 end
= session
->current_end_frame ();
5279 start
= session
->current_start_frame ();
5282 switch (mouse_mode
) {
5284 /* find the two markers on either side and then make the selection from it */
5285 select_all_within (start
, end
, 0.0f
, FLT_MAX
, track_views
, Selection::Set
);
5289 /* find the two markers on either side of the click and make the range out of it */
5290 selection
->set (0, start
, end
);
5299 stop_canvas_autoscroll ();
5305 Editor::start_mouse_zoom (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5307 drag_info
.item
= item
;
5308 drag_info
.motion_callback
= &Editor::drag_mouse_zoom
;
5309 drag_info
.finished_callback
= &Editor::end_mouse_zoom
;
5311 start_grab (event
, zoom_cursor
);
5313 show_verbose_time_cursor (drag_info
.current_pointer_frame
, 10);
5317 Editor::drag_mouse_zoom (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5322 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
5323 snap_to (drag_info
.current_pointer_frame
);
5325 if (drag_info
.first_move
) {
5326 snap_to (drag_info
.grab_frame
);
5330 if (drag_info
.current_pointer_frame
== drag_info
.last_pointer_frame
) return;
5332 /* base start and end on initial click position */
5333 if (drag_info
.current_pointer_frame
< drag_info
.grab_frame
) {
5334 start
= drag_info
.current_pointer_frame
;
5335 end
= drag_info
.grab_frame
;
5337 end
= drag_info
.current_pointer_frame
;
5338 start
= drag_info
.grab_frame
;
5343 if (drag_info
.first_move
) {
5345 zoom_rect
->raise_to_top();
5348 reposition_zoom_rect(start
, end
);
5350 drag_info
.last_pointer_frame
= drag_info
.current_pointer_frame
;
5351 drag_info
.first_move
= false;
5353 show_verbose_time_cursor (drag_info
.current_pointer_frame
, 10);
5358 Editor::end_mouse_zoom (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5360 if (!drag_info
.first_move
) {
5361 drag_mouse_zoom (item
, event
);
5363 if (drag_info
.grab_frame
< drag_info
.last_pointer_frame
) {
5364 temporal_zoom_by_frame (drag_info
.grab_frame
, drag_info
.last_pointer_frame
, "mouse zoom");
5366 temporal_zoom_by_frame (drag_info
.last_pointer_frame
, drag_info
.grab_frame
, "mouse zoom");
5369 temporal_zoom_to_frame (false, drag_info
.grab_frame
);
5371 temporal_zoom_step (false);
5372 center_screen (drag_info.grab_frame);
5380 Editor::reposition_zoom_rect (nframes64_t start
, nframes64_t end
)
5382 double x1
= frame_to_pixel (start
);
5383 double x2
= frame_to_pixel (end
);
5384 double y2
= full_canvas_height
- 1.0;
5386 zoom_rect
->property_x1() = x1
;
5387 zoom_rect
->property_y1() = 1.0;
5388 zoom_rect
->property_x2() = x2
;
5389 zoom_rect
->property_y2() = y2
;
5393 Editor::start_rubberband_select (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5395 drag_info
.item
= item
;
5396 drag_info
.motion_callback
= &Editor::drag_rubberband_select
;
5397 drag_info
.finished_callback
= &Editor::end_rubberband_select
;
5399 start_grab (event
, cross_hair_cursor
);
5401 show_verbose_time_cursor (drag_info
.current_pointer_frame
, 10);
5405 Editor::drag_rubberband_select (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5412 /* use a bigger drag threshold than the default */
5414 if (abs ((int) (drag_info
.current_pointer_frame
- drag_info
.grab_frame
)) < 8) {
5418 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier()) && Config
->get_rubberbanding_snaps_to_grid()) {
5419 if (drag_info
.first_move
) {
5420 snap_to (drag_info
.grab_frame
);
5422 snap_to (drag_info
.current_pointer_frame
);
5425 /* base start and end on initial click position */
5427 if (drag_info
.current_pointer_frame
< drag_info
.grab_frame
) {
5428 start
= drag_info
.current_pointer_frame
;
5429 end
= drag_info
.grab_frame
;
5431 end
= drag_info
.current_pointer_frame
;
5432 start
= drag_info
.grab_frame
;
5435 if (drag_info
.current_pointer_y
< drag_info
.grab_y
) {
5436 y1
= drag_info
.current_pointer_y
;
5437 y2
= drag_info
.grab_y
;
5439 y2
= drag_info
.current_pointer_y
;
5440 y1
= drag_info
.grab_y
;
5444 if (start
!= end
|| y1
!= y2
) {
5446 double x1
= frame_to_pixel (start
);
5447 double x2
= frame_to_pixel (end
);
5449 rubberband_rect
->property_x1() = x1
;
5450 rubberband_rect
->property_y1() = y1
;
5451 rubberband_rect
->property_x2() = x2
;
5452 rubberband_rect
->property_y2() = y2
;
5454 rubberband_rect
->show();
5455 rubberband_rect
->raise_to_top();
5457 drag_info
.last_pointer_frame
= drag_info
.current_pointer_frame
;
5458 drag_info
.first_move
= false;
5460 show_verbose_time_cursor (drag_info
.current_pointer_frame
, 10);
5465 Editor::end_rubberband_select (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5467 if (!drag_info
.first_move
) {
5469 drag_rubberband_select (item
, event
);
5472 if (drag_info
.current_pointer_y
< drag_info
.grab_y
) {
5473 y1
= drag_info
.current_pointer_y
;
5474 y2
= drag_info
.grab_y
;
5476 y2
= drag_info
.current_pointer_y
;
5477 y1
= drag_info
.grab_y
;
5481 Selection::Operation op
= Keyboard::selection_type (event
->button
.state
);
5484 begin_reversible_command (_("rubberband selection"));
5486 if (drag_info
.grab_frame
< drag_info
.last_pointer_frame
) {
5487 commit
= select_all_within (drag_info
.grab_frame
, drag_info
.last_pointer_frame
, y1
, y2
, track_views
, op
);
5489 commit
= select_all_within (drag_info
.last_pointer_frame
, drag_info
.grab_frame
, y1
, y2
, track_views
, op
);
5493 commit_reversible_command ();
5497 if (!getenv("ARDOUR_SAE")) {
5498 selection
->clear_tracks();
5500 selection
->clear_regions();
5501 selection
->clear_points ();
5502 selection
->clear_lines ();
5505 rubberband_rect
->hide();
5510 Editor::mouse_rename_region (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5512 using namespace Gtkmm2ext
;
5514 ArdourPrompter
prompter (false);
5516 prompter
.set_prompt (_("Name for region:"));
5517 prompter
.set_initial_text (clicked_regionview
->region()->name());
5518 prompter
.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT
);
5519 prompter
.set_response_sensitive (Gtk::RESPONSE_ACCEPT
, false);
5520 prompter
.show_all ();
5521 switch (prompter
.run ()) {
5522 case Gtk::RESPONSE_ACCEPT
:
5524 prompter
.get_result(str
);
5526 clicked_regionview
->region()->set_name (str
);
5534 Editor::start_time_fx (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5536 drag_info
.item
= item
;
5537 drag_info
.motion_callback
= &Editor::time_fx_motion
;
5538 drag_info
.finished_callback
= &Editor::end_time_fx
;
5542 show_verbose_time_cursor (drag_info
.current_pointer_frame
, 10);
5546 Editor::time_fx_motion (ArdourCanvas::Item
*item
, GdkEvent
* event
)
5548 RegionView
* rv
= clicked_regionview
;
5550 if (!Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::snap_modifier())) {
5551 snap_to (drag_info
.current_pointer_frame
);
5554 if (drag_info
.current_pointer_frame
== drag_info
.last_pointer_frame
) {
5558 if (drag_info
.current_pointer_frame
> rv
->region()->position()) {
5559 rv
->get_time_axis_view().show_timestretch (rv
->region()->position(), drag_info
.current_pointer_frame
);
5562 drag_info
.last_pointer_frame
= drag_info
.current_pointer_frame
;
5563 drag_info
.first_move
= false;
5565 show_verbose_time_cursor (drag_info
.current_pointer_frame
, 10);
5569 Editor::end_time_fx (ArdourCanvas::Item
* item
, GdkEvent
* event
)
5571 clicked_regionview
->get_time_axis_view().hide_timestretch ();
5573 if (drag_info
.first_move
) {
5577 if (drag_info
.last_pointer_frame
< clicked_regionview
->region()->position()) {
5578 /* backwards drag of the left edge - not usable */
5582 nframes64_t newlen
= drag_info
.last_pointer_frame
- clicked_regionview
->region()->position();
5583 #ifdef USE_RUBBERBAND
5584 float percentage
= (float) ((double) newlen
/ (double) clicked_regionview
->region()->length());
5586 float percentage
= (float) ((double) newlen
- (double) clicked_regionview
->region()->length()) / ((double) newlen
) * 100.0f
;
5589 begin_reversible_command (_("timestretch"));
5591 // XXX how do timeFX on multiple regions ?
5594 rs
.add (clicked_regionview
);
5596 if (time_stretch (rs
, percentage
) == 0) {
5597 session
->commit_reversible_command ();
5602 Editor::mouse_brush_insert_region (RegionView
* rv
, nframes64_t pos
)
5604 /* no brushing without a useful snap setting */
5607 AudioRegionView
* arv
= dynamic_cast<AudioRegionView
*>(rv
);
5610 switch (snap_mode
) {
5612 return; /* can't work because it allows region to be placed anywhere */
5617 switch (snap_type
) {
5625 /* don't brush a copy over the original */
5627 if (pos
== rv
->region()->position()) {
5631 RouteTimeAxisView
* atv
= dynamic_cast<RouteTimeAxisView
*>(&arv
->get_time_axis_view());
5633 if (atv
== 0 || !atv
->is_audio_track()) {
5637 boost::shared_ptr
<Playlist
> playlist
= atv
->playlist();
5638 double speed
= atv
->get_diskstream()->speed();
5640 XMLNode
&before
= playlist
->get_state();
5641 playlist
->add_region (boost::dynamic_pointer_cast
<AudioRegion
> (RegionFactory::create (arv
->audio_region())), (nframes64_t
) (pos
* speed
));
5642 XMLNode
&after
= playlist
->get_state();
5643 session
->add_command(new MementoCommand
<Playlist
>(*playlist
.get(), &before
, &after
));
5645 // playlist is frozen, so we have to update manually
5647 playlist
->Modified(); /* EMIT SIGNAL */
5651 Editor::track_height_step_timeout ()
5653 if (get_microseconds() - last_track_height_step_timestamp
< 250000) {
5654 current_stepping_trackview
= 0;