2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #include "pbd/error.h"
29 #include <gtkmm2ext/utils.h>
30 #include <gtkmm2ext/tearoff.h>
31 #include "pbd/memento_command.h"
32 #include "pbd/basename.h"
34 #include "ardour_ui.h"
36 #include "time_axis_view.h"
37 #include "audio_time_axis.h"
38 #include "audio_region_view.h"
39 #include "midi_region_view.h"
41 #include "streamview.h"
42 #include "region_gain_line.h"
43 #include "automation_time_axis.h"
44 #include "control_point.h"
47 #include "selection.h"
50 #include "rgb_macros.h"
51 #include "control_point_dialog.h"
53 #include "ardour/types.h"
54 #include "ardour/profile.h"
55 #include "ardour/route.h"
56 #include "ardour/audio_track.h"
57 #include "ardour/audio_diskstream.h"
58 #include "ardour/midi_diskstream.h"
59 #include "ardour/playlist.h"
60 #include "ardour/audioplaylist.h"
61 #include "ardour/audioregion.h"
62 #include "ardour/midi_region.h"
63 #include "ardour/dB.h"
64 #include "ardour/utils.h"
65 #include "ardour/region_factory.h"
66 #include "ardour/source_factory.h"
73 using namespace ARDOUR;
77 using namespace Editing;
79 const static double ZERO_GAIN_FRACTION = gain_to_slider_position(dB_to_coefficient(0.0));
82 Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
86 Gdk::ModifierType mask;
87 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
88 Glib::RefPtr<const Gdk::Window> pointer_window;
94 pointer_window = canvas_window->get_pointer (x, y, mask);
96 if (pointer_window == track_canvas->get_bin_window()) {
99 in_track_canvas = true;
102 in_track_canvas = false;
107 event.type = GDK_BUTTON_RELEASE;
111 where = event_frame (&event, 0, 0);
116 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy) const
130 switch (event->type) {
131 case GDK_BUTTON_RELEASE:
132 case GDK_BUTTON_PRESS:
133 case GDK_2BUTTON_PRESS:
134 case GDK_3BUTTON_PRESS:
136 *pcx = event->button.x;
137 *pcy = event->button.y;
138 _trackview_group->w2i(*pcx, *pcy);
140 case GDK_MOTION_NOTIFY:
142 *pcx = event->motion.x;
143 *pcy = event->motion.y;
144 _trackview_group->w2i(*pcx, *pcy);
146 case GDK_ENTER_NOTIFY:
147 case GDK_LEAVE_NOTIFY:
148 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
151 case GDK_KEY_RELEASE:
152 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
155 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
159 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
160 position is negative (as can be the case with motion events in particular),
161 the frame location is always positive.
164 return pixel_to_frame (*pcx);
168 Editor::mouse_mode_toggled (MouseMode m)
170 if (ignore_mouse_mode_toggle) {
176 if (mouse_select_button.get_active()) {
182 if (mouse_move_button.get_active()) {
188 if (mouse_gain_button.get_active()) {
194 if (mouse_zoom_button.get_active()) {
200 if (mouse_timefx_button.get_active()) {
206 if (mouse_audition_button.get_active()) {
212 if (mouse_note_button.get_active()) {
223 Editor::which_grabber_cursor ()
225 switch (_edit_point) {
227 return grabber_edit_point_cursor;
232 return grabber_cursor;
236 Editor::set_canvas_cursor ()
238 switch (mouse_mode) {
240 current_canvas_cursor = selector_cursor;
244 current_canvas_cursor = which_grabber_cursor();
248 current_canvas_cursor = cross_hair_cursor;
252 current_canvas_cursor = zoom_cursor;
256 current_canvas_cursor = time_fx_cursor; // just use playhead
260 current_canvas_cursor = speaker_cursor;
264 set_midi_edit_cursor (current_midi_edit_mode());
269 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
274 Editor::set_mouse_mode (MouseMode m, bool force)
276 if (drag_info.item) {
280 if (!force && m == mouse_mode) {
288 if (mouse_mode != MouseRange) {
290 /* in all modes except range, hide the range selection,
291 show the object (region) selection.
294 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
295 (*i)->set_should_show_selection (true);
297 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
298 (*i)->hide_selection ();
304 in range mode,show the range selection.
307 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
308 if ((*i)->get_selected()) {
309 (*i)->show_selection (selection->time);
314 /* XXX the hack of unsetting all other buttons should go
315 away once GTK2 allows us to use regular radio buttons drawn like
316 normal buttons, rather than my silly GroupedButton hack.
319 ignore_mouse_mode_toggle = true;
321 switch (mouse_mode) {
323 mouse_select_button.set_active (true);
327 mouse_move_button.set_active (true);
331 mouse_gain_button.set_active (true);
335 mouse_zoom_button.set_active (true);
339 mouse_timefx_button.set_active (true);
343 mouse_audition_button.set_active (true);
347 mouse_note_button.set_active (true);
348 set_midi_edit_cursor (current_midi_edit_mode());
352 if (midi_tools_tearoff) {
353 if (mouse_mode == MouseNote) {
354 midi_tools_tearoff->show();
356 midi_tools_tearoff->hide();
360 ignore_mouse_mode_toggle = false;
362 set_canvas_cursor ();
366 Editor::step_mouse_mode (bool next)
368 switch (current_mouse_mode()) {
371 if (Profile->get_sae()) {
372 set_mouse_mode (MouseZoom);
374 set_mouse_mode (MouseRange);
377 set_mouse_mode (MouseTimeFX);
382 if (next) set_mouse_mode (MouseZoom);
383 else set_mouse_mode (MouseObject);
388 if (Profile->get_sae()) {
389 set_mouse_mode (MouseTimeFX);
391 set_mouse_mode (MouseGain);
394 if (Profile->get_sae()) {
395 set_mouse_mode (MouseObject);
397 set_mouse_mode (MouseRange);
403 if (next) set_mouse_mode (MouseTimeFX);
404 else set_mouse_mode (MouseZoom);
409 set_mouse_mode (MouseAudition);
411 if (Profile->get_sae()) {
412 set_mouse_mode (MouseZoom);
414 set_mouse_mode (MouseGain);
420 if (next) set_mouse_mode (MouseObject);
421 else set_mouse_mode (MouseTimeFX);
425 if (next) set_mouse_mode (MouseObject);
426 else set_mouse_mode (MouseAudition);
432 Editor::midi_edit_mode_toggled (MidiEditMode m)
434 if (ignore_midi_edit_mode_toggle) {
440 if (midi_tool_pencil_button.get_active())
441 set_midi_edit_mode (m);
445 if (midi_tool_select_button.get_active())
446 set_midi_edit_mode (m);
450 if (midi_tool_resize_button.get_active())
451 set_midi_edit_mode (m);
455 if (midi_tool_erase_button.get_active())
456 set_midi_edit_mode (m);
463 set_midi_edit_cursor(m);
468 Editor::set_midi_edit_mode (MidiEditMode m, bool force)
470 if (drag_info.item) {
474 if (!force && m == midi_edit_mode) {
482 ignore_midi_edit_mode_toggle = true;
484 switch (midi_edit_mode) {
486 midi_tool_pencil_button.set_active (true);
490 midi_tool_select_button.set_active (true);
494 midi_tool_resize_button.set_active (true);
498 midi_tool_erase_button.set_active (true);
502 ignore_midi_edit_mode_toggle = false;
504 set_midi_edit_cursor (current_midi_edit_mode());
507 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
512 Editor::set_midi_edit_cursor (MidiEditMode m)
514 switch (midi_edit_mode) {
516 current_canvas_cursor = midi_pencil_cursor;
520 current_canvas_cursor = midi_select_cursor;
524 current_canvas_cursor = midi_resize_cursor;
528 current_canvas_cursor = midi_erase_cursor;
534 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
536 /* in object/audition/timefx/gain-automation mode,
537 any button press sets the selection if the object
538 can be selected. this is a bit of hack, because
539 we want to avoid this if the mouse operation is a
542 note: not dbl-click or triple-click
545 if (((mouse_mode != MouseObject) &&
546 (mouse_mode != MouseAudition || item_type != RegionItem) &&
547 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
548 (mouse_mode != MouseGain) &&
549 (mouse_mode != MouseRange)) ||
551 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
556 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
558 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
560 /* almost no selection action on modified button-2 or button-3 events */
562 if (item_type != RegionItem && event->button.button != 2) {
568 Selection::Operation op = Keyboard::selection_type (event->button.state);
569 bool press = (event->type == GDK_BUTTON_PRESS);
571 // begin_reversible_command (_("select on click"));
575 if (mouse_mode != MouseRange) {
576 set_selected_regionview_from_click (press, op, true);
577 } else if (event->type == GDK_BUTTON_PRESS) {
578 set_selected_track_as_side_effect ();
582 case RegionViewNameHighlight:
584 if (mouse_mode != MouseRange) {
585 set_selected_regionview_from_click (press, op, true);
586 } else if (event->type == GDK_BUTTON_PRESS) {
587 set_selected_track_as_side_effect ();
592 case FadeInHandleItem:
594 case FadeOutHandleItem:
596 if (mouse_mode != MouseRange) {
597 set_selected_regionview_from_click (press, op, true);
598 } else if (event->type == GDK_BUTTON_PRESS) {
599 set_selected_track_as_side_effect ();
603 case ControlPointItem:
604 set_selected_track_as_side_effect ();
605 if (mouse_mode != MouseRange) {
606 set_selected_control_point_from_click (op, false);
611 /* for context click or range selection, select track */
612 if (event->button.button == 3) {
613 set_selected_track_as_side_effect ();
614 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
615 set_selected_track_as_side_effect ();
619 case AutomationTrackItem:
620 set_selected_track_as_side_effect (true);
629 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
631 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
634 Glib::RefPtr<const Gdk::Window> pointer_window;
637 Gdk::ModifierType mask;
639 pointer_window = canvas_window->get_pointer (x, y, mask);
641 if (pointer_window == track_canvas->get_bin_window()) {
642 track_canvas->window_to_world (x, y, wx, wy);
643 allow_vertical_scroll = true;
645 allow_vertical_scroll = false;
649 track_canvas->grab_focus();
651 if (session && session->actively_recording()) {
655 button_selection (item, event, item_type);
657 if (drag_info.item == 0 &&
658 (Keyboard::is_delete_event (&event->button) ||
659 Keyboard::is_context_menu_event (&event->button) ||
660 Keyboard::is_edit_event (&event->button))) {
662 /* handled by button release */
666 switch (event->button.button) {
669 if (event->type == GDK_BUTTON_PRESS) {
671 if (drag_info.item) {
672 drag_info.item->ungrab (event->button.time);
675 /* single mouse clicks on any of these item types operate
676 independent of mouse mode, mostly because they are
677 not on the main track canvas or because we want
682 case PlayheadCursorItem:
683 start_cursor_grab (item, event);
687 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
688 hide_marker (item, event);
690 start_marker_grab (item, event);
694 case TempoMarkerItem:
695 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
696 start_tempo_marker_copy_grab (item, event);
698 start_tempo_marker_grab (item, event);
702 case MeterMarkerItem:
703 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
704 start_meter_marker_copy_grab (item, event);
706 start_meter_marker_grab (item, event);
713 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
714 start_cursor_grab_no_stop(&playhead_cursor->canvas_item, event);
720 case RangeMarkerBarItem:
721 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
722 start_cursor_grab_no_stop(&playhead_cursor->canvas_item, event);
724 start_range_markerbar_op (item, event, CreateRangeMarker);
729 case CdMarkerBarItem:
730 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
731 start_cursor_grab_no_stop(&playhead_cursor->canvas_item, event);
733 start_range_markerbar_op (item, event, CreateCDMarker);
738 case TransportMarkerBarItem:
739 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
740 start_cursor_grab_no_stop(&playhead_cursor->canvas_item, event);
742 start_range_markerbar_op (item, event, CreateTransportMarker);
752 switch (mouse_mode) {
755 case StartSelectionTrimItem:
756 start_selection_op (item, event, SelectionStartTrim);
759 case EndSelectionTrimItem:
760 start_selection_op (item, event, SelectionEndTrim);
764 if (Keyboard::modifier_state_contains
765 (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) {
766 // contains and not equals because I can't use alt as a modifier alone.
767 start_selection_grab (item, event);
768 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
769 /* grab selection for moving */
770 start_selection_op (item, event, SelectionMove);
772 /* this was debated, but decided the more common action was to
773 make a new selection */
774 start_selection_op (item, event, CreateSelection);
779 start_selection_op (item, event, CreateSelection);
785 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
786 event->type == GDK_BUTTON_PRESS) {
788 start_rubberband_select (item, event);
790 } else if (event->type == GDK_BUTTON_PRESS) {
793 case FadeInHandleItem:
794 start_fade_in_grab (item, event);
797 case FadeOutHandleItem:
798 start_fade_out_grab (item, event);
802 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
803 start_region_copy_grab (item, event);
804 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
805 start_region_brush_grab (item, event);
807 start_region_grab (item, event);
811 case RegionViewNameHighlight:
812 start_trim (item, event);
817 /* rename happens on edit clicks */
818 start_trim (clicked_regionview->get_name_highlight(), event);
822 case ControlPointItem:
823 start_control_point_grab (item, event);
827 case AutomationLineItem:
828 start_line_grab_from_line (item, event);
833 case AutomationTrackItem:
834 start_rubberband_select (item, event);
838 case ImageFrameHandleStartItem:
839 imageframe_start_handle_op(item, event) ;
842 case ImageFrameHandleEndItem:
843 imageframe_end_handle_op(item, event) ;
846 case MarkerViewHandleStartItem:
847 markerview_item_start_handle_op(item, event) ;
850 case MarkerViewHandleEndItem:
851 markerview_item_end_handle_op(item, event) ;
855 start_markerview_grab(item, event) ;
858 start_imageframe_grab(item, event) ;
876 /* start a grab so that if we finish after moving
877 we can tell what happened.
879 drag_info.item = item;
880 drag_info.motion_callback = &Editor::region_gain_motion_callback;
881 drag_info.finished_callback = 0;
882 start_grab (event, current_canvas_cursor);
886 start_line_grab_from_line (item, event);
889 case ControlPointItem:
890 start_control_point_grab (item, event);
901 case ControlPointItem:
902 start_control_point_grab (item, event);
905 case AutomationLineItem:
906 start_line_grab_from_line (item, event);
910 // XXX need automation mode to identify which
912 // start_line_grab_from_regionview (item, event);
922 if (event->type == GDK_BUTTON_PRESS) {
923 start_mouse_zoom (item, event);
930 if (item_type == RegionItem) {
931 start_time_fx (item, event);
938 scrub_reverse_distance = 0;
939 last_scrub_x = event->button.x;
940 scrubbing_direction = 0;
941 track_canvas->get_window()->set_cursor (*transparent_cursor);
942 /* rest handled in motion & release */
946 start_create_region_grab (item, event);
955 switch (mouse_mode) {
957 if (event->type == GDK_BUTTON_PRESS) {
960 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
961 start_region_copy_grab (item, event);
963 start_region_grab (item, event);
967 case ControlPointItem:
968 start_control_point_grab (item, event);
979 case RegionViewNameHighlight:
980 start_trim (item, event);
985 start_trim (clicked_regionview->get_name_highlight(), event);
996 if (event->type == GDK_BUTTON_PRESS) {
997 /* relax till release */
1004 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1005 temporal_zoom_session();
1007 temporal_zoom_to_frame (true, event_frame(event));
1030 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1032 nframes64_t where = event_frame (event, 0, 0);
1033 AutomationTimeAxisView* atv = 0;
1035 /* no action if we're recording */
1037 if (session && session->actively_recording()) {
1041 /* first, see if we're finishing a drag ... */
1043 if (drag_info.item) {
1044 if (end_grab (item, event)) {
1045 /* grab dragged, so do nothing else */
1050 button_selection (item, event, item_type);
1052 /* edit events get handled here */
1054 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
1055 switch (item_type) {
1060 case TempoMarkerItem:
1061 edit_tempo_marker (item);
1064 case MeterMarkerItem:
1065 edit_meter_marker (item);
1068 case RegionViewName:
1069 if (clicked_regionview->name_active()) {
1070 return mouse_rename_region (item, event);
1074 case ControlPointItem:
1075 edit_control_point (item);
1084 /* context menu events get handled here */
1086 if (Keyboard::is_context_menu_event (&event->button)) {
1088 if (drag_info.item == 0) {
1090 /* no matter which button pops up the context menu, tell the menu
1091 widget to use button 1 to drive menu selection.
1094 switch (item_type) {
1096 case FadeInHandleItem:
1098 case FadeOutHandleItem:
1099 popup_fade_context_menu (1, event->button.time, item, item_type);
1103 popup_track_context_menu (1, event->button.time, item_type, false, where);
1107 case RegionViewNameHighlight:
1108 case RegionViewName:
1109 popup_track_context_menu (1, event->button.time, item_type, false, where);
1113 popup_track_context_menu (1, event->button.time, item_type, true, where);
1116 case AutomationTrackItem:
1117 popup_track_context_menu (1, event->button.time, item_type, false, where);
1121 case RangeMarkerBarItem:
1122 case TransportMarkerBarItem:
1123 case CdMarkerBarItem:
1126 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
1130 marker_context_menu (&event->button, item);
1133 case TempoMarkerItem:
1134 tm_marker_context_menu (&event->button, item);
1137 case MeterMarkerItem:
1138 tm_marker_context_menu (&event->button, item);
1141 case CrossfadeViewItem:
1142 popup_track_context_menu (1, event->button.time, item_type, false, where);
1146 case ImageFrameItem:
1147 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1149 case ImageFrameTimeAxisItem:
1150 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1152 case MarkerViewItem:
1153 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1155 case MarkerTimeAxisItem:
1156 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1168 /* delete events get handled here */
1170 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
1172 switch (item_type) {
1173 case TempoMarkerItem:
1174 remove_tempo_marker (item);
1177 case MeterMarkerItem:
1178 remove_meter_marker (item);
1182 remove_marker (*item, event);
1186 if (mouse_mode == MouseObject) {
1187 remove_clicked_region ();
1191 case ControlPointItem:
1192 if (mouse_mode == MouseGain) {
1193 remove_gain_control_point (item, event);
1195 remove_control_point (item, event);
1205 switch (event->button.button) {
1208 switch (item_type) {
1209 /* see comments in button_press_handler */
1210 case PlayheadCursorItem:
1213 case AutomationLineItem:
1214 case StartSelectionTrimItem:
1215 case EndSelectionTrimItem:
1219 if (!_dragging_playhead) {
1220 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1221 snap_to (where, 0, true);
1223 mouse_add_new_marker (where);
1227 case CdMarkerBarItem:
1228 if (!_dragging_playhead) {
1229 // if we get here then a dragged range wasn't done
1230 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1231 snap_to (where, 0, true);
1233 mouse_add_new_marker (where, true);
1238 if (!_dragging_playhead) {
1239 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1242 mouse_add_new_tempo_event (where);
1247 if (!_dragging_playhead) {
1248 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1257 switch (mouse_mode) {
1259 switch (item_type) {
1260 case AutomationTrackItem:
1261 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_routeview);
1263 atv->add_automation_event (item, event, where, event->button.y);
1275 // Gain only makes sense for audio regions
1277 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1281 switch (item_type) {
1283 /* check that we didn't drag before releasing, since
1284 its really annoying to create new control
1285 points when doing this.
1287 if (drag_info.first_move) {
1288 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1293 case AutomationTrackItem:
1294 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1295 add_automation_event (item, event, where, event->button.y);
1305 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1306 if (scrubbing_direction == 0) {
1307 /* no drag, just a click */
1308 switch (item_type) {
1310 play_selected_region ();
1316 /* make sure we stop */
1317 session->request_transport_speed (0.0);
1331 switch (mouse_mode) {
1334 switch (item_type) {
1336 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1338 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1341 // Button2 click is unused
1354 // x_style_paste (where, 1.0);
1374 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1380 if (last_item_entered != item) {
1381 last_item_entered = item;
1382 last_item_entered_n = 0;
1385 switch (item_type) {
1386 case ControlPointItem:
1387 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1388 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1389 cp->set_visible (true);
1393 at_y = cp->get_y ();
1394 cp->item()->i2w (at_x, at_y);
1398 fraction = 1.0 - (cp->get_y() / cp->line().height());
1400 if (is_drawable() && !_scrubbing) {
1401 track_canvas->get_window()->set_cursor (*fader_cursor);
1404 last_item_entered_n++;
1405 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1406 if (last_item_entered_n < 10) {
1407 show_verbose_canvas_cursor ();
1413 if (mouse_mode == MouseGain) {
1414 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1416 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1417 if (is_drawable()) {
1418 track_canvas->get_window()->set_cursor (*fader_cursor);
1423 case AutomationLineItem:
1424 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1426 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1428 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1430 if (is_drawable()) {
1431 track_canvas->get_window()->set_cursor (*fader_cursor);
1436 case RegionViewNameHighlight:
1437 if (is_drawable() && mouse_mode == MouseObject) {
1438 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1442 case StartSelectionTrimItem:
1443 case EndSelectionTrimItem:
1446 case ImageFrameHandleStartItem:
1447 case ImageFrameHandleEndItem:
1448 case MarkerViewHandleStartItem:
1449 case MarkerViewHandleEndItem:
1452 if (is_drawable()) {
1453 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1457 case PlayheadCursorItem:
1458 if (is_drawable()) {
1459 switch (_edit_point) {
1461 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1464 track_canvas->get_window()->set_cursor (*grabber_cursor);
1470 case RegionViewName:
1472 /* when the name is not an active item, the entire name highlight is for trimming */
1474 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1475 if (mouse_mode == MouseObject && is_drawable()) {
1476 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1482 case AutomationTrackItem:
1483 if (is_drawable()) {
1484 Gdk::Cursor *cursor;
1485 switch (mouse_mode) {
1487 cursor = selector_cursor;
1490 cursor = zoom_cursor;
1493 cursor = cross_hair_cursor;
1497 track_canvas->get_window()->set_cursor (*cursor);
1499 AutomationTimeAxisView* atv;
1500 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1501 clear_entered_track = false;
1502 set_entered_track (atv);
1508 case RangeMarkerBarItem:
1509 case TransportMarkerBarItem:
1510 case CdMarkerBarItem:
1513 if (is_drawable()) {
1514 track_canvas->get_window()->set_cursor (*timebar_cursor);
1519 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1522 entered_marker = marker;
1523 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1525 case MeterMarkerItem:
1526 case TempoMarkerItem:
1527 if (is_drawable()) {
1528 track_canvas->get_window()->set_cursor (*timebar_cursor);
1531 case FadeInHandleItem:
1532 case FadeOutHandleItem:
1533 if (mouse_mode == MouseObject) {
1534 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1536 rect->property_fill_color_rgba() = 0;
1537 rect->property_outline_pixels() = 1;
1546 /* second pass to handle entered track status in a comprehensible way.
1549 switch (item_type) {
1551 case AutomationLineItem:
1552 case ControlPointItem:
1553 /* these do not affect the current entered track state */
1554 clear_entered_track = false;
1557 case AutomationTrackItem:
1558 /* handled above already */
1562 set_entered_track (0);
1570 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1579 switch (item_type) {
1580 case ControlPointItem:
1581 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1582 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1583 if (cp->line().npoints() > 1 && !cp->selected()) {
1584 cp->set_visible (false);
1588 if (is_drawable()) {
1589 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1592 hide_verbose_canvas_cursor ();
1595 case RegionViewNameHighlight:
1596 case StartSelectionTrimItem:
1597 case EndSelectionTrimItem:
1598 case PlayheadCursorItem:
1601 case ImageFrameHandleStartItem:
1602 case ImageFrameHandleEndItem:
1603 case MarkerViewHandleStartItem:
1604 case MarkerViewHandleEndItem:
1607 if (is_drawable()) {
1608 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1613 case AutomationLineItem:
1614 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1616 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1618 line->property_fill_color_rgba() = al->get_line_color();
1620 if (is_drawable()) {
1621 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1625 case RegionViewName:
1626 /* see enter_handler() for notes */
1627 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1628 if (is_drawable() && mouse_mode == MouseObject) {
1629 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1634 case RangeMarkerBarItem:
1635 case TransportMarkerBarItem:
1636 case CdMarkerBarItem:
1640 if (is_drawable()) {
1641 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1646 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1650 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1651 location_flags_changed (loc, this);
1654 case MeterMarkerItem:
1655 case TempoMarkerItem:
1657 if (is_drawable()) {
1658 track_canvas->get_window()->set_cursor (*timebar_cursor);
1663 case FadeInHandleItem:
1664 case FadeOutHandleItem:
1665 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1667 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1669 rect->property_fill_color_rgba() = rv->get_fill_color();
1670 rect->property_outline_pixels() = 0;
1675 case AutomationTrackItem:
1676 if (is_drawable()) {
1677 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1678 clear_entered_track = true;
1679 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1691 Editor::left_automation_track ()
1693 if (clear_entered_track) {
1694 set_entered_track (0);
1695 clear_entered_track = false;
1705 if (scrubbing_direction == 0) {
1707 session->request_locate (drag_info.current_pointer_frame, false);
1708 session->request_transport_speed (0.1);
1709 scrubbing_direction = 1;
1713 if (last_scrub_x > drag_info.current_pointer_x) {
1715 /* pointer moved to the left */
1717 if (scrubbing_direction > 0) {
1719 /* we reversed direction to go backwards */
1722 scrub_reverse_distance += (int) (last_scrub_x - drag_info.current_pointer_x);
1726 /* still moving to the left (backwards) */
1728 scrub_reversals = 0;
1729 scrub_reverse_distance = 0;
1731 delta = 0.01 * (last_scrub_x - drag_info.current_pointer_x);
1732 session->request_transport_speed (session->transport_speed() - delta);
1736 /* pointer moved to the right */
1738 if (scrubbing_direction < 0) {
1739 /* we reversed direction to go forward */
1742 scrub_reverse_distance += (int) (drag_info.current_pointer_x - last_scrub_x);
1745 /* still moving to the right */
1747 scrub_reversals = 0;
1748 scrub_reverse_distance = 0;
1750 delta = 0.01 * (drag_info.current_pointer_x - last_scrub_x);
1751 session->request_transport_speed (session->transport_speed() + delta);
1755 /* if there have been more than 2 opposite motion moves detected, or one that moves
1756 back more than 10 pixels, reverse direction
1759 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1761 if (scrubbing_direction > 0) {
1762 /* was forwards, go backwards */
1763 session->request_transport_speed (-0.1);
1764 scrubbing_direction = -1;
1766 /* was backwards, go forwards */
1767 session->request_transport_speed (0.1);
1768 scrubbing_direction = 1;
1771 scrub_reverse_distance = 0;
1772 scrub_reversals = 0;
1776 last_scrub_x = drag_info.current_pointer_x;
1780 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1782 if (event->motion.is_hint) {
1785 /* We call this so that MOTION_NOTIFY events continue to be
1786 delivered to the canvas. We need to do this because we set
1787 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1788 the density of the events, at the expense of a round-trip
1789 to the server. Given that this will mostly occur on cases
1790 where DISPLAY = :0.0, and given the cost of what the motion
1791 event might do, its a good tradeoff.
1794 track_canvas->get_pointer (x, y);
1797 if (current_stepping_trackview) {
1798 /* don't keep the persistent stepped trackview if the mouse moves */
1799 current_stepping_trackview = 0;
1800 step_timeout.disconnect ();
1803 if (session && session->actively_recording()) {
1804 /* Sorry. no dragging stuff around while we record */
1808 drag_info.item_type = item_type;
1809 drag_info.last_pointer_x = drag_info.current_pointer_x;
1810 drag_info.last_pointer_y = drag_info.current_pointer_y;
1811 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1812 &drag_info.current_pointer_y);
1815 switch (mouse_mode) {
1827 if (!from_autoscroll && drag_info.item) {
1828 /* item != 0 is the best test i can think of for dragging.
1830 if (!drag_info.move_threshold_passed) {
1832 bool x_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1833 bool y_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1835 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1837 // and change the initial grab loc/frame if this drag info wants us to
1839 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1840 drag_info.grab_frame = drag_info.current_pointer_frame;
1841 drag_info.grab_x = drag_info.current_pointer_x;
1842 drag_info.grab_y = drag_info.current_pointer_y;
1843 drag_info.last_pointer_frame = drag_info.grab_frame;
1844 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1849 switch (item_type) {
1850 case PlayheadCursorItem:
1852 case ControlPointItem:
1856 case RangeMarkerBarItem:
1857 case TransportMarkerBarItem:
1858 case CdMarkerBarItem:
1859 case TempoMarkerItem:
1860 case MeterMarkerItem:
1861 case RegionViewNameHighlight:
1862 case StartSelectionTrimItem:
1863 case EndSelectionTrimItem:
1866 case AutomationLineItem:
1867 case FadeInHandleItem:
1868 case FadeOutHandleItem:
1871 case ImageFrameHandleStartItem:
1872 case ImageFrameHandleEndItem:
1873 case MarkerViewHandleStartItem:
1874 case MarkerViewHandleEndItem:
1877 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1878 (event->motion.state & Gdk::BUTTON2_MASK))) {
1879 if (!from_autoscroll) {
1880 maybe_autoscroll_horizontally (&event->motion);
1882 if (drag_info.motion_callback) {
1883 (this->*(drag_info.motion_callback)) (item, event);
1893 switch (mouse_mode) {
1895 if (item_type == RegionItem) {
1896 if (drag_info.item && drag_info.motion_callback) {
1897 (this->*(drag_info.motion_callback)) (item, event);
1908 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1909 (event->motion.state & GDK_BUTTON2_MASK))) {
1910 if (!from_autoscroll) {
1911 maybe_autoscroll (&event->motion);
1913 if (drag_info.motion_callback) {
1914 (this->*(drag_info.motion_callback)) (item, event);
1926 track_canvas_motion (event);
1927 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1935 Editor::break_drag ()
1937 stop_canvas_autoscroll ();
1938 hide_verbose_canvas_cursor ();
1940 if (drag_info.item) {
1941 drag_info.item->ungrab (0);
1943 /* put it back where it came from */
1948 drag_info.item->i2w (cxw, cyw);
1949 drag_info.item->move (drag_info.original_x - cxw, drag_info.original_y - cyw);
1956 Editor::finalize_drag ()
1959 drag_info.copy = false;
1960 drag_info.motion_callback = 0;
1961 drag_info.finished_callback = 0;
1962 drag_info.dest_trackview = 0;
1963 drag_info.source_trackview = 0;
1964 drag_info.last_frame_position = 0;
1965 drag_info.grab_frame = 0;
1966 drag_info.last_pointer_frame = 0;
1967 drag_info.current_pointer_frame = 0;
1968 drag_info.brushing = false;
1969 range_marker_drag_rect->hide();
1970 drag_info.clear_copied_locations ();
1974 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1976 if (drag_info.item == 0) {
1977 fatal << _("programming error: start_grab called without drag item") << endmsg;
1983 cursor = which_grabber_cursor ();
1986 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1988 if (Keyboard::is_button2_event (&event->button)) {
1989 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
1990 drag_info.y_constrained = true;
1991 drag_info.x_constrained = false;
1993 drag_info.y_constrained = false;
1994 drag_info.x_constrained = true;
1997 drag_info.x_constrained = false;
1998 drag_info.y_constrained = false;
2001 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
2002 drag_info.last_pointer_frame = drag_info.grab_frame;
2003 drag_info.current_pointer_frame = drag_info.grab_frame;
2004 drag_info.current_pointer_x = drag_info.grab_x;
2005 drag_info.current_pointer_y = drag_info.grab_y;
2006 drag_info.last_pointer_x = drag_info.current_pointer_x;
2007 drag_info.last_pointer_y = drag_info.current_pointer_y;
2008 drag_info.cumulative_x_drag = 0;
2009 drag_info.cumulative_y_drag = 0;
2010 drag_info.first_move = true;
2011 drag_info.move_threshold_passed = false;
2012 drag_info.want_move_threshold = false;
2013 drag_info.pointer_frame_offset = 0;
2014 drag_info.brushing = false;
2015 drag_info.clear_copied_locations ();
2017 drag_info.original_x = 0;
2018 drag_info.original_y = 0;
2019 drag_info.item->i2w (drag_info.original_x, drag_info.original_y);
2021 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
2023 event->button.time);
2025 if (session && session->transport_rolling()) {
2026 drag_info.was_rolling = true;
2028 drag_info.was_rolling = false;
2031 switch (snap_type) {
2032 case SnapToRegionStart:
2033 case SnapToRegionEnd:
2034 case SnapToRegionSync:
2035 case SnapToRegionBoundary:
2036 build_region_boundary_cache ();
2044 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
2046 drag_info.item->ungrab (0);
2047 drag_info.item = new_item;
2050 cursor = which_grabber_cursor ();
2053 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
2057 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
2059 bool did_drag = false;
2061 stop_canvas_autoscroll ();
2063 if (drag_info.item == 0) {
2067 drag_info.item->ungrab (event->button.time);
2069 if (drag_info.finished_callback) {
2070 drag_info.last_pointer_x = drag_info.current_pointer_x;
2071 drag_info.last_pointer_y = drag_info.current_pointer_y;
2072 (this->*(drag_info.finished_callback)) (item, event);
2075 did_drag = !drag_info.first_move;
2077 hide_verbose_canvas_cursor();
2085 Editor::region_gain_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2087 if (drag_info.first_move && drag_info.move_threshold_passed) {
2088 drag_info.first_move = false;
2093 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
2095 drag_info.item = item;
2096 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
2097 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
2101 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
2102 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
2106 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2109 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes64_t) arv->audio_region()->fade_in()->back()->when + arv->region()->position());
2113 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2115 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2117 nframes64_t fade_length;
2119 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2120 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2126 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2130 if (pos < (arv->region()->position() + 64)) {
2131 fade_length = 64; // this should be a minimum defined somewhere
2132 } else if (pos > arv->region()->last_frame()) {
2133 fade_length = arv->region()->length();
2135 fade_length = pos - arv->region()->position();
2137 /* mapover the region selection */
2139 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2141 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2147 tmp->reset_fade_in_shape_width (fade_length);
2150 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
2152 drag_info.first_move = false;
2156 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2158 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2160 nframes64_t fade_length;
2162 if (drag_info.first_move) return;
2164 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2165 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2170 if (pos < (arv->region()->position() + 64)) {
2171 fade_length = 64; // this should be a minimum defined somewhere
2172 } else if (pos > arv->region()->last_frame()) {
2173 fade_length = arv->region()->length();
2175 fade_length = pos - arv->region()->position();
2178 begin_reversible_command (_("change fade in length"));
2180 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2182 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2188 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2189 XMLNode &before = alist->get_state();
2191 tmp->audio_region()->set_fade_in_length (fade_length);
2192 tmp->audio_region()->set_fade_in_active (true);
2194 XMLNode &after = alist->get_state();
2195 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2198 commit_reversible_command ();
2202 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
2204 drag_info.item = item;
2205 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
2206 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
2210 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
2211 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
2215 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2217 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes64_t) arv->audio_region()->fade_out()->back()->when + arv->region()->position());
2221 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2223 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2225 nframes64_t fade_length;
2227 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2228 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2233 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2237 if (pos > (arv->region()->last_frame() - 64)) {
2238 fade_length = 64; // this should really be a minimum fade defined somewhere
2240 else if (pos < arv->region()->position()) {
2241 fade_length = arv->region()->length();
2244 fade_length = arv->region()->last_frame() - pos;
2247 /* mapover the region selection */
2249 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2251 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2257 tmp->reset_fade_out_shape_width (fade_length);
2260 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
2262 drag_info.first_move = false;
2266 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2268 if (drag_info.first_move) return;
2270 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2272 nframes64_t fade_length;
2274 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2275 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2281 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2285 if (pos > (arv->region()->last_frame() - 64)) {
2286 fade_length = 64; // this should really be a minimum fade defined somewhere
2288 else if (pos < arv->region()->position()) {
2289 fade_length = arv->region()->length();
2292 fade_length = arv->region()->last_frame() - pos;
2295 begin_reversible_command (_("change fade out length"));
2297 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2299 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2305 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2306 XMLNode &before = alist->get_state();
2308 tmp->audio_region()->set_fade_out_length (fade_length);
2309 tmp->audio_region()->set_fade_out_active (true);
2311 XMLNode &after = alist->get_state();
2312 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2315 commit_reversible_command ();
2319 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2321 drag_info.item = item;
2322 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2323 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2327 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2328 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2332 Cursor* cursor = (Cursor *) drag_info.data;
2334 if (cursor == playhead_cursor) {
2335 _dragging_playhead = true;
2337 if (session && drag_info.was_rolling) {
2338 session->request_stop ();
2341 if (session && session->is_auditioning()) {
2342 session->cancel_audition ();
2346 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2348 show_verbose_time_cursor (cursor->current_frame, 10);
2352 Editor::start_cursor_grab_no_stop (ArdourCanvas::Item* item, GdkEvent* event)
2354 drag_info.item = item;
2355 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2356 drag_info.finished_callback = &Editor::cursor_drag_finished_ensure_locate_callback;
2360 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2361 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2365 Cursor* cursor = (Cursor *) drag_info.data;
2366 nframes64_t where = event_frame (event, 0, 0);
2369 playhead_cursor->set_position (where);
2371 if (cursor == playhead_cursor) {
2372 _dragging_playhead = true;
2374 if (session && session->is_auditioning()) {
2375 session->cancel_audition ();
2379 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2381 show_verbose_time_cursor (cursor->current_frame, 10);
2385 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2387 Cursor* cursor = (Cursor *) drag_info.data;
2388 nframes64_t adjusted_frame;
2390 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2391 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2397 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2398 if (cursor == playhead_cursor) {
2399 snap_to (adjusted_frame);
2403 if (adjusted_frame == drag_info.last_pointer_frame) return;
2405 cursor->set_position (adjusted_frame);
2407 show_verbose_time_cursor (cursor->current_frame, 10);
2410 track_canvas->update_now ();
2412 UpdateAllTransportClocks (cursor->current_frame);
2414 drag_info.last_pointer_frame = adjusted_frame;
2415 drag_info.first_move = false;
2419 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2421 _dragging_playhead = false;
2423 if (drag_info.first_move) {
2427 cursor_drag_motion_callback (item, event);
2429 if (item == &playhead_cursor->canvas_item) {
2431 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2432 _pending_locate_request = true;
2438 Editor::cursor_drag_finished_ensure_locate_callback (ArdourCanvas::Item* item, GdkEvent* event)
2440 _dragging_playhead = false;
2442 cursor_drag_motion_callback (item, event);
2444 if (item == &playhead_cursor->canvas_item) {
2446 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2447 _pending_locate_request = true;
2453 Editor::update_marker_drag_item (Location *location)
2455 double x1 = frame_to_pixel (location->start());
2456 double x2 = frame_to_pixel (location->end());
2458 if (location->is_mark()) {
2459 marker_drag_line_points.front().set_x(x1);
2460 marker_drag_line_points.back().set_x(x1);
2461 marker_drag_line->property_points() = marker_drag_line_points;
2463 range_marker_drag_rect->property_x1() = x1;
2464 range_marker_drag_rect->property_x2() = x2;
2470 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2474 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2475 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2481 Location *location = find_location_from_marker (marker, is_start);
2483 drag_info.item = item;
2484 drag_info.data = marker;
2485 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2486 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2490 _dragging_edit_point = true;
2492 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2494 update_marker_drag_item (location);
2496 if (location->is_mark()) {
2497 // marker_drag_line->show();
2498 // marker_drag_line->raise_to_top();
2500 range_marker_drag_rect->show();
2501 //range_marker_drag_rect->raise_to_top();
2505 show_verbose_time_cursor (location->start(), 10);
2507 show_verbose_time_cursor (location->end(), 10);
2510 Selection::Operation op = Keyboard::selection_type (event->button.state);
2513 case Selection::Toggle:
2514 selection->toggle (marker);
2516 case Selection::Set:
2517 if (!selection->selected (marker)) {
2518 selection->set (marker);
2521 case Selection::Extend:
2523 Locations::LocationList ll;
2524 list<Marker*> to_add;
2526 selection->markers.range (s, e);
2527 s = min (marker->position(), s);
2528 e = max (marker->position(), e);
2531 if (e < max_frames) {
2534 session->locations()->find_all_between (s, e, ll, Location::Flags (0));
2535 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2536 LocationMarkers* lm = find_location_markers (*i);
2539 to_add.push_back (lm->start);
2542 to_add.push_back (lm->end);
2546 if (!to_add.empty()) {
2547 selection->add (to_add);
2551 case Selection::Add:
2552 selection->add (marker);
2556 /* set up copies for us to manipulate during the drag */
2558 drag_info.clear_copied_locations ();
2560 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
2561 Location *l = find_location_from_marker (*i, is_start);
2562 drag_info.copied_locations.push_back (new Location (*l));
2567 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2569 nframes64_t f_delta = 0;
2570 nframes64_t newframe;
2572 bool move_both = false;
2573 Marker* dragged_marker = (Marker*) drag_info.data;
2575 Location *real_location;
2576 Location *copy_location = 0;
2578 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2579 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2584 nframes64_t next = newframe;
2586 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2587 snap_to (newframe, 0, true);
2590 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2594 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2598 MarkerSelection::iterator i;
2599 list<Location*>::iterator x;
2601 /* find the marker we're dragging, and compute the delta */
2603 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2604 x != drag_info.copied_locations.end() && i != selection->markers.end();
2610 if (marker == dragged_marker) {
2612 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2617 if (real_location->is_mark()) {
2618 f_delta = newframe - copy_location->start();
2622 switch (marker->type()) {
2624 case Marker::LoopStart:
2625 case Marker::PunchIn:
2626 f_delta = newframe - copy_location->start();
2630 case Marker::LoopEnd:
2631 case Marker::PunchOut:
2632 f_delta = newframe - copy_location->end();
2635 /* what kind of marker is this ? */
2643 if (i == selection->markers.end()) {
2644 /* hmm, impossible - we didn't find the dragged marker */
2648 /* now move them all */
2650 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2651 x != drag_info.copied_locations.end() && i != selection->markers.end();
2657 /* call this to find out if its the start or end */
2659 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2663 if (real_location->locked()) {
2667 if (copy_location->is_mark()) {
2671 copy_location->set_start (copy_location->start() + f_delta);
2675 nframes64_t new_start = copy_location->start() + f_delta;
2676 nframes64_t new_end = copy_location->end() + f_delta;
2678 if (is_start) { // start-of-range marker
2681 copy_location->set_start (new_start);
2682 copy_location->set_end (new_end);
2683 } else if (new_start < copy_location->end()) {
2684 copy_location->set_start (new_start);
2686 snap_to (next, 1, true);
2687 copy_location->set_end (next);
2688 copy_location->set_start (newframe);
2691 } else { // end marker
2694 copy_location->set_end (new_end);
2695 copy_location->set_start (new_start);
2696 } else if (new_end > copy_location->start()) {
2697 copy_location->set_end (new_end);
2698 } else if (newframe > 0) {
2699 snap_to (next, -1, true);
2700 copy_location->set_start (next);
2701 copy_location->set_end (newframe);
2705 update_marker_drag_item (copy_location);
2707 LocationMarkers* lm = find_location_markers (real_location);
2710 lm->set_position (copy_location->start(), copy_location->end());
2714 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2715 drag_info.first_move = false;
2717 if (drag_info.copied_locations.empty()) {
2721 edit_point_clock.set (drag_info.copied_locations.front()->start());
2722 show_verbose_time_cursor (newframe, 10);
2725 track_canvas->update_now ();
2727 edit_point_clock.set (copy_location->start());
2731 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2733 if (drag_info.first_move) {
2735 /* just a click, do nothing but finish
2736 off the selection process
2739 Selection::Operation op = Keyboard::selection_type (event->button.state);
2740 Marker* marker = (Marker *) drag_info.data;
2743 case Selection::Set:
2744 if (selection->selected (marker) && selection->markers.size() > 1) {
2745 selection->set (marker);
2749 case Selection::Toggle:
2750 case Selection::Extend:
2751 case Selection::Add:
2758 _dragging_edit_point = false;
2761 begin_reversible_command ( _("move marker") );
2762 XMLNode &before = session->locations()->get_state();
2764 MarkerSelection::iterator i;
2765 list<Location*>::iterator x;
2768 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2769 x != drag_info.copied_locations.end() && i != selection->markers.end();
2772 Location * location = find_location_from_marker ((*i), is_start);
2776 if (location->locked()) {
2780 if (location->is_mark()) {
2781 location->set_start ((*x)->start());
2783 location->set ((*x)->start(), (*x)->end());
2788 XMLNode &after = session->locations()->get_state();
2789 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2790 commit_reversible_command ();
2792 marker_drag_line->hide();
2793 range_marker_drag_rect->hide();
2797 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2800 MeterMarker* meter_marker;
2802 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2803 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2807 meter_marker = dynamic_cast<MeterMarker*> (marker);
2809 MetricSection& section (meter_marker->meter());
2811 if (!section.movable()) {
2815 drag_info.item = item;
2816 drag_info.copy = false;
2817 drag_info.data = marker;
2818 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2819 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2823 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2825 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2829 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2832 MeterMarker* meter_marker;
2834 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2835 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2839 meter_marker = dynamic_cast<MeterMarker*> (marker);
2841 // create a dummy marker for visual representation of moving the copy.
2842 // The actual copying is not done before we reach the finish callback.
2844 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2845 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2846 *new MeterSection(meter_marker->meter()));
2848 drag_info.item = &new_marker->the_item();
2849 drag_info.copy = true;
2850 drag_info.data = new_marker;
2851 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2852 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2856 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2858 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2862 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2864 MeterMarker* marker = (MeterMarker *) drag_info.data;
2865 nframes64_t adjusted_frame;
2867 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2868 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2874 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2875 snap_to (adjusted_frame);
2878 if (adjusted_frame == drag_info.last_pointer_frame) return;
2880 marker->set_position (adjusted_frame);
2883 drag_info.last_pointer_frame = adjusted_frame;
2884 drag_info.first_move = false;
2886 show_verbose_time_cursor (adjusted_frame, 10);
2890 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2892 if (drag_info.first_move) return;
2894 meter_marker_drag_motion_callback (drag_info.item, event);
2896 MeterMarker* marker = (MeterMarker *) drag_info.data;
2899 TempoMap& map (session->tempo_map());
2900 map.bbt_time (drag_info.last_pointer_frame, when);
2902 if (drag_info.copy == true) {
2903 begin_reversible_command (_("copy meter mark"));
2904 XMLNode &before = map.get_state();
2905 map.add_meter (marker->meter(), when);
2906 XMLNode &after = map.get_state();
2907 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2908 commit_reversible_command ();
2910 // delete the dummy marker we used for visual representation of copying.
2911 // a new visual marker will show up automatically.
2914 begin_reversible_command (_("move meter mark"));
2915 XMLNode &before = map.get_state();
2916 map.move_meter (marker->meter(), when);
2917 XMLNode &after = map.get_state();
2918 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2919 commit_reversible_command ();
2924 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2927 TempoMarker* tempo_marker;
2929 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2930 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2934 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2935 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2939 MetricSection& section (tempo_marker->tempo());
2941 if (!section.movable()) {
2945 drag_info.item = item;
2946 drag_info.copy = false;
2947 drag_info.data = marker;
2948 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2949 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2953 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2954 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2958 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2961 TempoMarker* tempo_marker;
2963 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2964 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2968 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2969 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2973 // create a dummy marker for visual representation of moving the copy.
2974 // The actual copying is not done before we reach the finish callback.
2976 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2977 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2978 *new TempoSection(tempo_marker->tempo()));
2980 drag_info.item = &new_marker->the_item();
2981 drag_info.copy = true;
2982 drag_info.data = new_marker;
2983 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2984 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2988 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2990 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2994 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2996 TempoMarker* marker = (TempoMarker *) drag_info.data;
2997 nframes64_t adjusted_frame;
2999 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3000 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3006 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3007 snap_to (adjusted_frame);
3010 if (adjusted_frame == drag_info.last_pointer_frame) return;
3012 /* OK, we've moved far enough to make it worth actually move the thing. */
3014 marker->set_position (adjusted_frame);
3016 show_verbose_time_cursor (adjusted_frame, 10);
3018 drag_info.last_pointer_frame = adjusted_frame;
3019 drag_info.first_move = false;
3023 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3025 if (drag_info.first_move) return;
3027 tempo_marker_drag_motion_callback (drag_info.item, event);
3029 TempoMarker* marker = (TempoMarker *) drag_info.data;
3032 TempoMap& map (session->tempo_map());
3033 map.bbt_time (drag_info.last_pointer_frame, when);
3035 if (drag_info.copy == true) {
3036 begin_reversible_command (_("copy tempo mark"));
3037 XMLNode &before = map.get_state();
3038 map.add_tempo (marker->tempo(), when);
3039 XMLNode &after = map.get_state();
3040 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3041 commit_reversible_command ();
3043 // delete the dummy marker we used for visual representation of copying.
3044 // a new visual marker will show up automatically.
3047 begin_reversible_command (_("move tempo mark"));
3048 XMLNode &before = map.get_state();
3049 map.move_tempo (marker->tempo(), when);
3050 XMLNode &after = map.get_state();
3051 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3052 commit_reversible_command ();
3057 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
3059 ControlPoint* control_point;
3061 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
3062 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
3066 // We shouldn't remove the first or last gain point
3067 if (control_point->line().is_last_point(*control_point) ||
3068 control_point->line().is_first_point(*control_point)) {
3072 control_point->line().remove_point (*control_point);
3076 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event)
3078 ControlPoint* control_point;
3080 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
3081 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
3085 control_point->line().remove_point (*control_point);
3089 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
3091 ControlPoint* control_point;
3093 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
3094 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
3098 drag_info.item = item;
3099 drag_info.data = control_point;
3100 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
3101 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
3103 start_grab (event, fader_cursor);
3105 // start the grab at the center of the control point so
3106 // the point doesn't 'jump' to the mouse after the first drag
3107 drag_info.grab_x = control_point->get_x();
3108 drag_info.grab_y = control_point->get_y();
3110 control_point->line().parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
3111 track_canvas->w2c(drag_info.grab_x, drag_info.grab_y, drag_info.grab_x, drag_info.grab_y);
3113 drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
3115 control_point->line().start_drag (control_point, drag_info.grab_frame, 0);
3117 float fraction = 1.0 - (control_point->get_y() / control_point->line().height());
3118 set_verbose_canvas_cursor (control_point->line().get_verbose_cursor_string (fraction),
3119 drag_info.current_pointer_x + 10, drag_info.current_pointer_y + 10);
3121 show_verbose_canvas_cursor ();
3125 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3127 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
3129 double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
3130 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
3132 if (event->button.state & Keyboard::SecondaryModifier) {
3137 double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
3138 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
3140 // calculate zero crossing point. back off by .01 to stay on the
3141 // positive side of zero
3143 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line().height() - .01;
3144 cp->line().parent_group().i2w(_unused, zero_gain_y);
3146 // make sure we hit zero when passing through
3147 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
3148 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
3152 if (drag_info.x_constrained) {
3153 cx = drag_info.grab_x;
3155 if (drag_info.y_constrained) {
3156 cy = drag_info.grab_y;
3159 drag_info.cumulative_x_drag = cx - drag_info.grab_x;
3160 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
3162 cp->line().parent_group().w2i (cx, cy);
3166 cy = min ((double) cp->line().height(), cy);
3168 //translate cx to frames
3169 nframes64_t cx_frames = unit_to_frame (cx);
3171 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
3172 snap_to (cx_frames);
3175 float fraction = 1.0 - (cy / cp->line().height());
3179 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3185 cp->line().point_drag (*cp, cx_frames , fraction, push);
3187 set_verbose_canvas_cursor_text (cp->line().get_verbose_cursor_string (fraction));
3189 drag_info.first_move = false;
3193 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3195 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
3197 if (drag_info.first_move) {
3201 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3202 reset_point_selection ();
3206 control_point_drag_motion_callback (item, event);
3208 cp->line().end_drag (cp);
3212 Editor::edit_control_point (ArdourCanvas::Item* item)
3214 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
3217 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
3221 ControlPointDialog d (p);
3222 d.set_position (Gtk::WIN_POS_MOUSE);
3225 if (d.run () != RESPONSE_ACCEPT) {
3229 p->line().modify_point_y (*p, d.get_y_fraction ());
3234 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
3236 switch (mouse_mode) {
3238 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
3239 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
3247 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
3251 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
3252 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
3256 start_line_grab (al, event);
3260 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
3264 nframes64_t frame_within_region;
3266 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3267 origin, and ditto for y.
3270 cx = event->button.x;
3271 cy = event->button.y;
3273 line->parent_group().w2i (cx, cy);
3275 frame_within_region = (nframes64_t) floor (cx * frames_per_unit);
3277 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
3278 current_line_drag_info.after)) {
3279 /* no adjacent points */
3283 drag_info.item = &line->grab_item();
3284 drag_info.data = line;
3285 drag_info.motion_callback = &Editor::line_drag_motion_callback;
3286 drag_info.finished_callback = &Editor::line_drag_finished_callback;
3288 start_grab (event, fader_cursor);
3290 /* store grab start in parent frame */
3292 drag_info.grab_x = cx;
3293 drag_info.grab_y = cy;
3295 double fraction = 1.0 - (cy / line->height());
3297 line->start_drag (0, drag_info.grab_frame, fraction);
3299 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
3300 drag_info.current_pointer_x + 10, drag_info.current_pointer_y + 10);
3301 show_verbose_canvas_cursor ();
3305 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3307 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3309 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
3311 if (event->button.state & Keyboard::SecondaryModifier) {
3315 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
3317 // calculate zero crossing point. back off by .01 to stay on the
3318 // positive side of zero
3319 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
3321 // line->parent_group().i2w(_unused, zero_gain_y);
3323 // make sure we hit zero when passing through
3324 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
3325 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
3329 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
3332 cy = min ((double) line->height(), cy);
3335 double fraction = 1.0 - (cy / line->height());
3339 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3345 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
3347 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
3351 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3353 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3354 line_drag_motion_callback (item, event);
3359 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3361 if (selection->regions.empty() || clicked_regionview == 0) {
3364 _region_motion_group->raise_to_top ();
3365 drag_info.copy = false;
3366 drag_info.item = item;
3367 drag_info.data = clicked_regionview;
3369 if (Config->get_edit_mode() == Splice) {
3370 drag_info.motion_callback = &Editor::region_drag_splice_motion_callback;
3371 drag_info.finished_callback = &Editor::region_drag_splice_finished_callback;
3373 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3374 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3380 TimeAxisView* tvp = clicked_axisview;
3381 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3383 if (tv && tv->is_track()) {
3384 speed = tv->get_diskstream()->speed();
3387 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3388 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3389 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3390 drag_info.source_layer = clicked_regionview->region()->layer();
3391 drag_info.dest_trackview = drag_info.source_trackview;
3392 drag_info.dest_layer = drag_info.source_layer;
3393 // we want a move threshold
3394 drag_info.want_move_threshold = true;
3395 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3397 begin_reversible_command (_("move region(s)"));
3399 /* sync the canvas to what we think is its current state */
3400 track_canvas->update_now();
3404 Editor::start_create_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3406 drag_info.copy = false;
3407 drag_info.item = item;
3408 drag_info.data = clicked_axisview;
3409 drag_info.source_trackview = clicked_axisview;
3410 drag_info.dest_trackview = drag_info.source_trackview;
3411 drag_info.dest_layer = drag_info.source_layer;
3412 drag_info.motion_callback = &Editor::create_region_drag_motion_callback;
3413 drag_info.finished_callback = &Editor::create_region_drag_finished_callback;
3419 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
3421 if (selection->regions.empty() || clicked_regionview == 0) {
3424 _region_motion_group->raise_to_top ();
3425 drag_info.copy = true;
3426 drag_info.item = item;
3427 drag_info.data = clicked_regionview;
3431 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
3432 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3435 if (rtv && rtv->is_track()) {
3436 speed = rtv->get_diskstream()->speed();
3439 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3440 drag_info.dest_trackview = drag_info.source_trackview;
3441 drag_info.dest_layer = drag_info.source_layer;
3442 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3443 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3444 // we want a move threshold
3445 drag_info.want_move_threshold = true;
3446 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3447 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3448 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3452 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
3454 if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) {
3458 drag_info.copy = false;
3459 drag_info.item = item;
3460 drag_info.data = clicked_regionview;
3461 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3462 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3467 TimeAxisView* tvp = clicked_axisview;
3468 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3470 if (tv && tv->is_track()) {
3471 speed = tv->get_diskstream()->speed();
3474 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3475 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3476 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3477 drag_info.dest_trackview = drag_info.source_trackview;
3478 drag_info.dest_layer = drag_info.source_layer;
3479 // we want a move threshold
3480 drag_info.want_move_threshold = true;
3481 drag_info.brushing = true;
3483 begin_reversible_command (_("Drag region brush"));
3487 Editor::possibly_copy_regions_during_grab (GdkEvent* event)
3489 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
3491 drag_info.want_move_threshold = false; // don't copy again
3493 /* duplicate the regionview(s) and region(s) */
3495 vector<RegionView*> new_regionviews;
3497 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3502 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
3503 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
3505 const boost::shared_ptr<const Region> original = rv->region();
3506 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
3509 boost::shared_ptr<AudioRegion> audioregion_copy
3510 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
3511 nrv = new AudioRegionView (*arv, audioregion_copy);
3513 boost::shared_ptr<MidiRegion> midiregion_copy
3514 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
3515 nrv = new MidiRegionView (*mrv, midiregion_copy);
3520 nrv->get_canvas_group()->show ();
3521 new_regionviews.push_back (nrv);
3524 if (new_regionviews.empty()) {
3528 /* reset selection to new regionviews. This will not set selection visual status for
3529 these regionviews since they don't belong to a track, so do that by hand too.
3532 selection->set (new_regionviews);
3534 for (vector<RegionView*>::iterator i = new_regionviews.begin(); i != new_regionviews.end(); ++i) {
3535 (*i)->set_selected (true);
3538 /* reset drag_info data to reflect the fact that we are dragging the copies */
3540 drag_info.data = new_regionviews.front();
3542 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3544 sync the canvas to what we think is its current state
3545 without it, the canvas seems to
3546 "forget" to update properly after the upcoming reparent()
3547 ..only if the mouse is in rapid motion at the time of the grab.
3548 something to do with regionview creation raking so long?
3550 track_canvas->update_now();
3555 Editor::check_region_drag_possible (RouteTimeAxisView** tv, layer_t* layer)
3557 /* Which trackview is this ? */
3559 std::pair<TimeAxisView*, int> const tvp = trackview_by_y_position (drag_info.current_pointer_y);
3560 (*tv) = dynamic_cast<RouteTimeAxisView*> (tvp.first);
3561 (*layer) = tvp.second;
3563 /* The region motion is only processed if the pointer is over
3567 if (!(*tv) || !(*tv)->is_track()) {
3568 /* To make sure we hide the verbose canvas cursor when the mouse is
3569 not held over and audiotrack.
3571 hide_verbose_canvas_cursor ();
3578 struct RegionSelectionByPosition {
3579 bool operator() (RegionView*a, RegionView* b) {
3580 return a->region()->position () < b->region()->position();
3585 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3587 RouteTimeAxisView* tv;
3590 if (!check_region_drag_possible (&tv, &layer)) {
3594 if (!drag_info.move_threshold_passed) {
3600 if (drag_info.current_pointer_x - drag_info.grab_x > 0) {
3606 RegionSelection copy (selection->regions);
3608 RegionSelectionByPosition cmp;
3611 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
3613 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
3619 boost::shared_ptr<Playlist> playlist;
3621 if ((playlist = atv->playlist()) == 0) {
3625 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
3630 if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) {
3634 if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) {
3640 playlist->shuffle ((*i)->region(), dir);
3642 drag_info.grab_x = drag_info.current_pointer_x;
3647 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3652 Editor::visible_order_range (int* low, int* high) const
3654 *low = TimeAxisView::max_order ();
3657 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
3659 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3661 if (!rtv->hidden()) {
3663 if (*high < rtv->order()) {
3664 *high = rtv->order ();
3667 if (*low > rtv->order()) {
3668 *low = rtv->order ();
3674 /** @param new_order New track order.
3675 * @param old_order Old track order.
3676 * @param visible_y_low Lowest visible order.
3677 * @param visible_y_high Highest visible order.
3678 * @param tracks Bitset of tracks indexed by order; 0 means a audio/MIDI track, 1 means something else.
3679 * @param heigh_list Heights of tracks indexed by order.
3680 * @return true if y movement should not happen, otherwise false.
3683 Editor::y_movement_disallowed (
3684 int new_order, int old_order, int y_span, int visible_y_low, int visible_y_high,
3685 bitset<512> const & tracks, vector<int32_t> const & height_list
3688 if (new_order != old_order) {
3690 /* this isn't the pointer track */
3694 /* moving up the canvas */
3695 if ( (new_order - y_span) >= visible_y_low) {
3699 /* work out where we'll end up with this y span, taking hidden TimeAxisViews into account */
3700 int32_t visible_tracks = 0;
3701 while (visible_tracks < y_span ) {
3703 while (height_list[new_order - (visible_tracks - n)] == 0) {
3704 /* passing through a hidden track */
3709 if (tracks[new_order - (y_span - n)] != 0x00) {
3710 /* moving to a non-track; disallow */
3716 /* moving beyond the lowest visible track; disallow */
3720 } else if (y_span < 0) {
3722 /* moving down the canvas */
3723 if ((new_order - y_span) <= visible_y_high) {
3725 int32_t visible_tracks = 0;
3727 while (visible_tracks > y_span ) {
3730 while (height_list[new_order - (visible_tracks - n)] == 0) {
3731 /* passing through a hidden track */
3736 if (tracks[new_order - (y_span - n)] != 0x00) {
3737 /* moving to a non-track; disallow */
3744 /* moving beyond the highest visible track; disallow */
3751 /* this is the pointer's track */
3753 if ((new_order - y_span) > visible_y_high) {
3754 /* we will overflow */
3756 } else if ((new_order - y_span) < visible_y_low) {
3757 /* we will overflow */
3766 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3770 nframes64_t pending_region_position = 0;
3771 int32_t pointer_order_span = 0, canvas_pointer_order_span = 0;
3772 int32_t pointer_layer_span = 0;
3774 bool clamp_y_axis = false;
3775 vector<int32_t>::iterator j;
3777 possibly_copy_regions_during_grab (event);
3779 /* *pointer* variables reflect things about the pointer; as we may be moving
3780 multiple regions, much detail must be computed per-region */
3782 /* current_pointer_view will become the TimeAxisView that we're currently pointing at, and
3783 current_pointer_layer the current layer on that TimeAxisView */
3784 RouteTimeAxisView* current_pointer_view;
3785 layer_t current_pointer_layer;
3786 if (!check_region_drag_possible (¤t_pointer_view, ¤t_pointer_layer)) {
3790 /* TimeAxisView that we were pointing at last time we entered this method */
3791 TimeAxisView const * const last_pointer_view = drag_info.dest_trackview;
3792 /* the order of the track that we were pointing at last time we entered this method */
3793 int32_t const last_pointer_order = last_pointer_view->order ();
3794 /* the layer that we were pointing at last time we entered this method */
3795 layer_t const last_pointer_layer = drag_info.dest_layer;
3797 /************************************************************
3799 ************************************************************/
3801 /* Height of TimeAxisViews, indexed by order */
3802 /* XXX: hard-coded limit of TimeAxisViews */
3803 vector<int32_t> height_list (512);
3805 if (drag_info.brushing) {
3806 clamp_y_axis = true;
3807 pointer_order_span = 0;
3811 /* the change in track order between this callback and the last */
3812 pointer_order_span = last_pointer_view->order() - current_pointer_view->order();
3813 /* the change in layer between this callback and the last;
3814 only meaningful if pointer_order_span == 0 (ie we've not moved tracks) */
3815 pointer_layer_span = last_pointer_layer - current_pointer_layer;
3817 if (pointer_order_span != 0) {
3819 int32_t children = 0;
3820 /* XXX: hard-coded limit of tracks */
3821 bitset <512> tracks (0x00);
3825 visible_order_range (&visible_y_low, &visible_y_high);
3827 /* get a bitmask representing the visible tracks */
3829 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3830 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3831 TimeAxisView::Children children_list;
3833 /* zeroes are audio/MIDI tracks. ones are other types. */
3835 if (!rtv->hidden()) {
3837 if (!rtv->is_track()) {
3838 /* not an audio nor MIDI track */
3839 tracks = tracks |= (0x01 << rtv->order());
3842 height_list[rtv->order()] = (*i)->current_height();
3845 if ((children_list = rtv->get_child_list()).size() > 0) {
3846 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
3847 tracks = tracks |= (0x01 << (rtv->order() + children));
3848 height_list[rtv->order() + children] = (*j)->current_height();
3855 /* find the actual pointer span, in terms of the number of visible tracks;
3856 to do this, we reduce |pointer_order_span| by the number of hidden tracks
3859 canvas_pointer_order_span = pointer_order_span;
3860 if (last_pointer_view->order() >= current_pointer_view->order()) {
3861 for (int32_t y = current_pointer_view->order(); y < last_pointer_view->order(); y++) {
3862 if (height_list[y] == 0) {
3863 canvas_pointer_order_span--;
3867 for (int32_t y = last_pointer_view->order(); y <= current_pointer_view->order(); y++) {
3868 if (height_list[y] == 0) {
3869 canvas_pointer_order_span++;
3874 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3876 RegionView* rv = (*i);
3878 if (rv->region()->locked()) {
3882 double ix1, ix2, iy1, iy2;
3883 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3884 rv->get_canvas_frame()->i2w (ix1, iy1);
3885 iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
3887 /* get the new trackview for this particular region */
3888 std::pair<TimeAxisView*, int> const tvp = trackview_by_y_position (iy1);
3890 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
3892 /* I know this method has a slightly excessive argument list, but I think
3893 it's nice to separate the code out all the same, since it has such a
3894 simple result, and it makes it clear that there are no other
3898 /* XXX: not sure that we should be passing canvas_pointer_order_span in here,
3899 as surely this is a per-region thing... */
3901 clamp_y_axis = y_movement_disallowed (
3902 rtv->order(), last_pointer_order, canvas_pointer_order_span, visible_y_low, visible_y_high,
3911 } else if (drag_info.dest_trackview == current_pointer_view) {
3913 if (current_pointer_layer == last_pointer_layer) {
3914 /* No movement; clamp */
3915 clamp_y_axis = true;
3920 if (!clamp_y_axis) {
3921 drag_info.dest_trackview = current_pointer_view;
3922 drag_info.dest_layer = current_pointer_layer;
3925 /************************************************************
3927 ************************************************************/
3929 /* compute the amount of pointer motion in frames, and where
3930 the region would be if we moved it by that much.
3932 if ( drag_info.move_threshold_passed ) {
3934 if (drag_info.current_pointer_frame >= drag_info.pointer_frame_offset) {
3936 nframes64_t sync_frame;
3937 nframes64_t sync_offset;
3940 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3942 sync_offset = clicked_regionview->region()->sync_offset (sync_dir);
3944 /* we don't handle a sync point that lies before zero.
3946 if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) {
3947 sync_frame = pending_region_position + (sync_dir*sync_offset);
3949 /* we snap if the snap modifier is not enabled.
3952 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3953 snap_to (sync_frame);
3956 pending_region_position = clicked_regionview->region()->adjust_to_sync (sync_frame);
3959 pending_region_position = drag_info.last_frame_position;
3963 pending_region_position = 0;
3966 if (pending_region_position > max_frames - clicked_regionview->region()->length()) {
3967 pending_region_position = drag_info.last_frame_position;
3970 bool x_move_allowed;
3972 if (Config->get_edit_mode() == Lock) {
3973 if (drag_info.copy) {
3974 x_move_allowed = !drag_info.x_constrained;
3976 /* in locked edit mode, reverse the usual meaning of x_constrained */
3977 x_move_allowed = drag_info.x_constrained;
3980 x_move_allowed = !drag_info.x_constrained;
3983 if (( pending_region_position != drag_info.last_frame_position) && x_move_allowed ) {
3985 /* now compute the canvas unit distance we need to move the regionview
3986 to make it appear at the new location.
3989 if (pending_region_position > drag_info.last_frame_position) {
3990 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3992 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3993 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3995 RegionView* rv = (*i);
3997 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3999 double ix1, ix2, iy1, iy2;
4000 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
4001 rv->get_canvas_frame()->i2w (ix1, iy1);
4003 if (-x_delta > ix1 + horizontal_adjustment.get_value()) {
4005 pending_region_position = drag_info.last_frame_position;
4012 drag_info.last_frame_position = pending_region_position;
4019 /* threshold not passed */
4024 /*************************************************************
4026 ************************************************************/
4028 if (x_delta == 0 && pointer_order_span == 0 && pointer_layer_span == 0) {
4029 /* haven't reached next snap point, and we're not switching
4030 trackviews nor layers. nothing to do.
4035 /*************************************************************
4037 ************************************************************/
4038 bool do_move = true;
4039 if (drag_info.first_move) {
4040 if (!drag_info.move_threshold_passed) {
4047 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4048 const list<RegionView*>& layered_regions = selection->regions.by_layer();
4050 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
4052 RegionView* rv = (*i);
4054 if (rv->region()->locked()) {
4058 /* here we are calculating the y distance from the
4059 top of the first track view to the top of the region
4060 area of the track view that we're working on */
4062 /* this x value is just a dummy value so that we have something
4063 to pass to i2w () */
4067 /* distance from the top of this track view to the region area
4068 of our track view is always 1 */
4072 /* convert to world coordinates, ie distance from the top of
4073 the ruler section */
4075 rv->get_canvas_frame()->i2w (ix1, iy1);
4077 /* compensate for the ruler section and the vertical scrollbar position */
4078 iy1 += get_trackview_group_vertical_offset ();
4080 if (drag_info.first_move) {
4082 // hide any dependent views
4084 rv->get_time_axis_view().hide_dependent_views (*rv);
4087 reparent to a non scrolling group so that we can keep the
4088 region selection above all time axis views.
4089 reparenting means we have to move the rv as the two
4090 parent groups have different coordinates.
4093 rv->get_canvas_group()->property_y() = iy1 - 1;
4094 rv->get_canvas_group()->reparent(*_region_motion_group);
4096 rv->fake_set_opaque (true);
4099 /* current view for this particular region */
4100 std::pair<TimeAxisView*, int> pos = trackview_by_y_position (iy1);
4101 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
4103 if (pointer_order_span != 0 && !clamp_y_axis) {
4105 /* INTER-TRACK MOVEMENT */
4107 /* move through the height list to the track that the region is currently on */
4108 vector<int32_t>::iterator j = height_list.begin ();
4110 while (j != height_list.end () && x != rtv->order ()) {
4116 int32_t temp_pointer_order_span = canvas_pointer_order_span;
4118 if (j != height_list.end ()) {
4120 /* Account for layers in the original and
4121 destination tracks. If we're moving around in layers we assume
4122 that only one track is involved, so it's ok to use *pointer*
4125 StreamView* lv = last_pointer_view->view ();
4128 /* move to the top of the last trackview */
4129 if (lv->layer_display () == Stacked) {
4130 y_delta -= (lv->layers() - last_pointer_layer - 1) * lv->child_height ();
4133 StreamView* cv = current_pointer_view->view ();
4136 /* move to the right layer on the current trackview */
4137 if (cv->layer_display () == Stacked) {
4138 y_delta += (cv->layers() - current_pointer_layer - 1) * cv->child_height ();
4141 /* And for being on a non-topmost layer on the new
4144 while (temp_pointer_order_span > 0) {
4145 /* we're moving up canvas-wise,
4146 so we need to find the next track height
4148 if (j != height_list.begin()) {
4152 if (x != last_pointer_order) {
4154 ++temp_pointer_order_span;
4159 temp_pointer_order_span--;
4162 while (temp_pointer_order_span < 0) {
4166 if (x != last_pointer_order) {
4168 --temp_pointer_order_span;
4172 if (j != height_list.end()) {
4176 temp_pointer_order_span++;
4180 /* find out where we'll be when we move and set height accordingly */
4182 std::pair<TimeAxisView*, int> const pos = trackview_by_y_position (iy1 + y_delta);
4183 RouteTimeAxisView const * temp_rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
4184 rv->set_height (temp_rtv->view()->child_height());
4186 /* if you un-comment the following, the region colours will follow
4187 the track colours whilst dragging; personally
4188 i think this can confuse things, but never mind.
4191 //const GdkColor& col (temp_rtv->view->get_region_color());
4192 //rv->set_color (const_cast<GdkColor&>(col));
4196 if (pointer_order_span == 0 && pointer_layer_span != 0 && !clamp_y_axis) {
4198 /* INTER-LAYER MOVEMENT in the same track */
4199 y_delta = rtv->view()->child_height () * pointer_layer_span;
4203 if (drag_info.brushing) {
4204 mouse_brush_insert_region (rv, pending_region_position);
4206 rv->move (x_delta, y_delta);
4209 } /* foreach region */
4213 if (drag_info.first_move && drag_info.move_threshold_passed) {
4214 cursor_group->raise_to_top();
4215 drag_info.first_move = false;
4218 if (x_delta != 0 && !drag_info.brushing) {
4219 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4224 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4226 bool nocommit = true;
4227 vector<RegionView*> copies;
4228 RouteTimeAxisView* source_tv;
4229 boost::shared_ptr<Diskstream> ds;
4230 boost::shared_ptr<Playlist> from_playlist;
4231 vector<RegionView*> new_selection;
4232 typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
4233 PlaylistSet modified_playlists;
4234 PlaylistSet frozen_playlists;
4235 list <sigc::connection> modified_playlist_connections;
4236 pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
4237 nframes64_t drag_delta;
4238 bool changed_tracks, changed_position;
4239 std::pair<TimeAxisView*, int> tvp;
4240 std::map<RegionView*, RouteTimeAxisView*> final;
4242 /* first_move is set to false if the regionview has been moved in the
4246 if (drag_info.first_move) {
4253 if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
4254 selection->set (pre_drag_region_selection);
4255 pre_drag_region_selection.clear ();
4258 if (drag_info.brushing) {
4259 /* all changes were made during motion event handlers */
4261 if (drag_info.copy) {
4262 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4263 copies.push_back (*i);
4272 /* reverse this here so that we have the correct logic to finalize
4276 if (Config->get_edit_mode() == Lock && !drag_info.copy) {
4277 drag_info.x_constrained = !drag_info.x_constrained;
4280 if (drag_info.copy) {
4281 if (drag_info.x_constrained) {
4282 op_string = _("fixed time region copy");
4284 op_string = _("region copy");
4287 if (drag_info.x_constrained) {
4288 op_string = _("fixed time region drag");
4290 op_string = _("region drag");
4294 begin_reversible_command (op_string);
4295 changed_position = (drag_info.last_frame_position != (nframes64_t) (clicked_regionview->region()->position()));
4296 tvp = trackview_by_y_position (drag_info.current_pointer_y);
4297 changed_tracks = (tvp.first != &clicked_regionview->get_time_axis_view());
4299 drag_delta = clicked_regionview->region()->position() - drag_info.last_frame_position;
4301 track_canvas->update_now ();
4303 /* make a list of where each region ended up */
4304 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4306 double ix1, ix2, iy1, iy2;
4307 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
4308 (*i)->get_canvas_frame()->i2w (ix1, iy1);
4309 iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
4311 std::pair<TimeAxisView*, int> tv = trackview_by_y_position (iy1);
4312 final[*i] = dynamic_cast<RouteTimeAxisView*> (tv.first);
4315 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
4317 RegionView* rv = (*i);
4318 RouteTimeAxisView* dest_rtv = final[*i];
4322 if (rv->region()->locked()) {
4327 if (changed_position && !drag_info.x_constrained) {
4328 where = rv->region()->position() - drag_delta;
4330 where = rv->region()->position();
4333 boost::shared_ptr<Region> new_region;
4335 if (drag_info.copy) {
4336 /* we already made a copy */
4337 new_region = rv->region();
4339 /* undo the previous hide_dependent_views so that xfades don't
4340 disappear on copying regions
4343 //rv->get_time_axis_view().reveal_dependent_views (*rv);
4345 } else if (changed_tracks && dest_rtv->playlist()) {
4346 new_region = RegionFactory::create (rv->region());
4349 if (changed_tracks || drag_info.copy) {
4351 boost::shared_ptr<Playlist> to_playlist = dest_rtv->playlist();
4357 latest_regionviews.clear ();
4359 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4361 insert_result = modified_playlists.insert (to_playlist);
4362 if (insert_result.second) {
4363 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
4366 to_playlist->add_region (new_region, where);
4370 if (!latest_regionviews.empty()) {
4371 // XXX why just the first one ? we only expect one
4372 // commented out in nick_m's canvas reworking. is that intended?
4373 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
4374 new_selection.push_back (latest_regionviews.front());
4379 motion on the same track. plonk the previously reparented region
4380 back to its original canvas group (its streamview).
4381 No need to do anything for copies as they are fake regions which will be deleted.
4384 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
4385 rv->get_canvas_group()->property_y() = 0;
4387 /* just change the model */
4389 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
4391 insert_result = modified_playlists.insert (playlist);
4392 if (insert_result.second) {
4393 session->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
4395 /* freeze to avoid lots of relayering in the case of a multi-region drag */
4396 frozen_insert_result = frozen_playlists.insert(playlist);
4397 if (frozen_insert_result.second) {
4401 rv->region()->set_position (where, (void*) this);
4404 if (changed_tracks && !drag_info.copy) {
4406 /* get the playlist where this drag started. we can't use rv->region()->playlist()
4407 because we may have copied the region and it has not been attached to a playlist.
4410 assert ((source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view())));
4411 assert ((ds = source_tv->get_diskstream()));
4412 assert ((from_playlist = ds->playlist()));
4414 /* moved to a different audio track, without copying */
4416 /* the region that used to be in the old playlist is not
4417 moved to the new one - we use a copy of it. as a result,
4418 any existing editor for the region should no longer be
4422 rv->hide_region_editor();
4423 rv->fake_set_opaque (false);
4425 /* remove the region from the old playlist */
4427 insert_result = modified_playlists.insert (from_playlist);
4428 if (insert_result.second) {
4429 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
4432 from_playlist->remove_region ((rv->region()));
4434 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
4435 was selected in all of them, then removing it from a playlist will have removed all
4436 trace of it from the selection (i.e. there were N regions selected, we removed 1,
4437 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
4438 corresponding regionview, and the selection is now empty).
4440 this could have invalidated any and all iterators into the region selection.
4442 the heuristic we use here is: if the region selection is empty, break out of the loop
4443 here. if the region selection is not empty, then restart the loop because we know that
4444 we must have removed at least the region(view) we've just been working on as well as any
4445 that we processed on previous iterations.
4447 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
4448 we can just iterate.
4451 if (selection->regions.empty()) {
4454 i = selection->regions.by_layer().begin();
4461 if (drag_info.copy) {
4462 copies.push_back (rv);
4466 if (new_selection.empty()) {
4467 if (drag_info.copy) {
4468 /* the region(view)s that are selected and being dragged around
4469 are copies and do not belong to any track. remove them
4470 from the selection right here.
4472 selection->clear_regions();
4475 /* this will clear any existing selection that would have been
4476 cleared in the other clause above
4478 selection->set (new_selection);
4481 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
4487 for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
4488 session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));
4490 commit_reversible_command ();
4493 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
4500 Editor::create_region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4502 if (drag_info.move_threshold_passed) {
4503 if (drag_info.first_move) {
4504 // TODO: create region-create-drag region view here
4505 drag_info.first_move = false;
4508 // TODO: resize region-create-drag region view here
4513 Editor::create_region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4515 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (drag_info.dest_trackview);
4519 const boost::shared_ptr<MidiDiskstream> diskstream =
4520 boost::dynamic_pointer_cast<MidiDiskstream>(mtv->view()->trackview().track()->diskstream());
4523 warning << "Cannot create non-MIDI region" << endl;
4527 if (drag_info.first_move) {
4528 begin_reversible_command (_("create region"));
4529 XMLNode &before = mtv->playlist()->get_state();
4531 nframes64_t start = drag_info.grab_frame;
4532 snap_to (start, -1);
4533 const Meter& m = session->tempo_map().meter_at(start);
4534 const Tempo& t = session->tempo_map().tempo_at(start);
4535 double length = floor (m.frames_per_bar(t, session->frame_rate()));
4537 boost::shared_ptr<Source> src = session->create_midi_source_for_session(*diskstream.get());
4539 mtv->playlist()->add_region (boost::dynamic_pointer_cast<MidiRegion>
4540 (RegionFactory::create(src, 0, (nframes_t) length,
4541 PBD::basename_nosuffix(src->name()))), start);
4542 XMLNode &after = mtv->playlist()->get_state();
4543 session->add_command(new MementoCommand<Playlist>(*mtv->playlist().get(), &before, &after));
4544 commit_reversible_command();
4547 create_region_drag_motion_callback (item, event);
4548 // TODO: create region-create-drag region here
4553 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
4555 /* Either add to or set the set the region selection, unless
4556 this is an alignment click (control used)
4559 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
4560 TimeAxisView* tv = &rv.get_time_axis_view();
4561 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
4563 if (rtv && rtv->is_track()) {
4564 speed = rtv->get_diskstream()->speed();
4567 nframes64_t where = get_preferred_edit_position();
4571 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
4573 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
4575 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
4577 align_region (rv.region(), End, (nframes64_t) (where * speed));
4581 align_region (rv.region(), Start, (nframes64_t) (where * speed));
4588 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
4594 nframes64_t frame_rate;
4603 if (Profile->get_sae() || Profile->get_small_screen()) {
4604 m = ARDOUR_UI::instance()->primary_clock.mode();
4606 m = ARDOUR_UI::instance()->secondary_clock.mode();
4610 case AudioClock::BBT:
4611 session->bbt_time (frame, bbt);
4612 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
4615 case AudioClock::SMPTE:
4616 session->smpte_time (frame, smpte);
4617 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4620 case AudioClock::MinSec:
4621 /* XXX this is copied from show_verbose_duration_cursor() */
4622 frame_rate = session->frame_rate();
4623 hours = frame / (frame_rate * 3600);
4624 frame = frame % (frame_rate * 3600);
4625 mins = frame / (frame_rate * 60);
4626 frame = frame % (frame_rate * 60);
4627 secs = (float) frame / (float) frame_rate;
4628 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4632 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
4636 if (xpos >= 0 && ypos >=0) {
4637 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4640 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);
4642 show_verbose_canvas_cursor ();
4646 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
4653 nframes64_t distance, frame_rate;
4655 Meter meter_at_start(session->tempo_map().meter_at(start));
4663 if (Profile->get_sae() || Profile->get_small_screen()) {
4664 m = ARDOUR_UI::instance()->primary_clock.mode ();
4666 m = ARDOUR_UI::instance()->secondary_clock.mode ();
4670 case AudioClock::BBT:
4671 session->bbt_time (start, sbbt);
4672 session->bbt_time (end, ebbt);
4675 /* XXX this computation won't work well if the
4676 user makes a selection that spans any meter changes.
4679 ebbt.bars -= sbbt.bars;
4680 if (ebbt.beats >= sbbt.beats) {
4681 ebbt.beats -= sbbt.beats;
4684 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
4686 if (ebbt.ticks >= sbbt.ticks) {
4687 ebbt.ticks -= sbbt.ticks;
4690 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
4693 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
4696 case AudioClock::SMPTE:
4697 session->smpte_duration (end - start, smpte);
4698 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4701 case AudioClock::MinSec:
4702 /* XXX this stuff should be elsewhere.. */
4703 distance = end - start;
4704 frame_rate = session->frame_rate();
4705 hours = distance / (frame_rate * 3600);
4706 distance = distance % (frame_rate * 3600);
4707 mins = distance / (frame_rate * 60);
4708 distance = distance % (frame_rate * 60);
4709 secs = (float) distance / (float) frame_rate;
4710 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4714 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
4718 if (xpos >= 0 && ypos >=0) {
4719 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4722 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4725 show_verbose_canvas_cursor ();
4729 Editor::collect_new_region_view (RegionView* rv)
4731 latest_regionviews.push_back (rv);
4735 Editor::collect_and_select_new_region_view (RegionView* rv)
4738 latest_regionviews.push_back (rv);
4742 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
4744 if (clicked_regionview == 0) {
4748 /* lets try to create new Region for the selection */
4750 vector<boost::shared_ptr<Region> > new_regions;
4751 create_region_from_selection (new_regions);
4753 if (new_regions.empty()) {
4757 /* XXX fix me one day to use all new regions */
4759 boost::shared_ptr<Region> region (new_regions.front());
4761 /* add it to the current stream/playlist.
4763 tricky: the streamview for the track will add a new regionview. we will
4764 catch the signal it sends when it creates the regionview to
4765 set the regionview we want to then drag.
4768 latest_regionviews.clear();
4769 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4771 /* A selection grab currently creates two undo/redo operations, one for
4772 creating the new region and another for moving it.
4775 begin_reversible_command (_("selection grab"));
4777 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
4779 XMLNode *before = &(playlist->get_state());
4780 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
4781 XMLNode *after = &(playlist->get_state());
4782 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4784 commit_reversible_command ();
4788 if (latest_regionviews.empty()) {
4789 /* something went wrong */
4793 /* we need to deselect all other regionviews, and select this one
4794 i'm ignoring undo stuff, because the region creation will take care of it
4796 selection->set (latest_regionviews);
4798 drag_info.item = latest_regionviews.front()->get_canvas_group();
4799 drag_info.data = latest_regionviews.front();
4800 drag_info.motion_callback = &Editor::region_drag_motion_callback;
4801 drag_info.finished_callback = &Editor::region_drag_finished_callback;
4805 drag_info.source_trackview = clicked_routeview;
4806 drag_info.dest_trackview = drag_info.source_trackview;
4807 drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4808 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4810 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4814 Editor::cancel_selection ()
4816 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4817 (*i)->hide_selection ();
4819 selection->clear ();
4820 clicked_selection = 0;
4824 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4826 nframes64_t start = 0;
4827 nframes64_t end = 0;
4833 drag_info.item = item;
4834 drag_info.motion_callback = &Editor::drag_selection;
4835 drag_info.finished_callback = &Editor::end_selection_op;
4840 case CreateSelection:
4841 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4842 drag_info.copy = true;
4844 drag_info.copy = false;
4846 start_grab (event, selector_cursor);
4849 case SelectionStartTrim:
4850 if (clicked_axisview) {
4851 clicked_axisview->order_selection_trims (item, true);
4853 start_grab (event, trimmer_cursor);
4854 start = selection->time[clicked_selection].start;
4855 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4858 case SelectionEndTrim:
4859 if (clicked_axisview) {
4860 clicked_axisview->order_selection_trims (item, false);
4862 start_grab (event, trimmer_cursor);
4863 end = selection->time[clicked_selection].end;
4864 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
4868 start = selection->time[clicked_selection].start;
4870 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4874 if (selection_op == SelectionMove) {
4875 show_verbose_time_cursor(start, 10);
4877 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4882 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4884 nframes64_t start = 0;
4885 nframes64_t end = 0;
4887 nframes64_t pending_position;
4889 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4890 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4892 pending_position = 0;
4895 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4896 snap_to (pending_position);
4899 /* only alter selection if the current frame is
4900 different from the last frame position (adjusted)
4903 if (pending_position == drag_info.last_pointer_frame) return;
4905 switch (selection_op) {
4906 case CreateSelection:
4908 if (drag_info.first_move) {
4909 snap_to (drag_info.grab_frame);
4912 if (pending_position < drag_info.grab_frame) {
4913 start = pending_position;
4914 end = drag_info.grab_frame;
4916 end = pending_position;
4917 start = drag_info.grab_frame;
4920 /* first drag: Either add to the selection
4921 or create a new selection->
4924 if (drag_info.first_move) {
4926 begin_reversible_command (_("range selection"));
4928 if (drag_info.copy) {
4929 /* adding to the selection */
4930 clicked_selection = selection->add (start, end);
4931 drag_info.copy = false;
4933 /* new selection-> */
4934 clicked_selection = selection->set (clicked_axisview, start, end);
4939 case SelectionStartTrim:
4941 if (drag_info.first_move) {
4942 begin_reversible_command (_("trim selection start"));
4945 start = selection->time[clicked_selection].start;
4946 end = selection->time[clicked_selection].end;
4948 if (pending_position > end) {
4951 start = pending_position;
4955 case SelectionEndTrim:
4957 if (drag_info.first_move) {
4958 begin_reversible_command (_("trim selection end"));
4961 start = selection->time[clicked_selection].start;
4962 end = selection->time[clicked_selection].end;
4964 if (pending_position < start) {
4967 end = pending_position;
4974 if (drag_info.first_move) {
4975 begin_reversible_command (_("move selection"));
4978 start = selection->time[clicked_selection].start;
4979 end = selection->time[clicked_selection].end;
4981 length = end - start;
4983 start = pending_position;
4986 end = start + length;
4991 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4992 start_canvas_autoscroll (1, 0);
4996 selection->replace (clicked_selection, start, end);
4999 drag_info.last_pointer_frame = pending_position;
5000 drag_info.first_move = false;
5002 if (selection_op == SelectionMove) {
5003 show_verbose_time_cursor(start, 10);
5005 show_verbose_time_cursor(pending_position, 10);
5010 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
5012 if (!drag_info.first_move) {
5013 drag_selection (item, event);
5014 /* XXX this is not object-oriented programming at all. ick */
5015 if (selection->time.consolidate()) {
5016 selection->TimeChanged ();
5018 commit_reversible_command ();
5020 /* just a click, no pointer movement.*/
5022 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
5024 selection->clear_time();
5029 /* XXX what happens if its a music selection? */
5030 session->set_audio_range (selection->time);
5031 stop_canvas_autoscroll ();
5035 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
5038 TimeAxisView* tvp = clicked_axisview;
5039 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
5041 if (tv && tv->is_track()) {
5042 speed = tv->get_diskstream()->speed();
5045 nframes64_t region_start = (nframes64_t) (clicked_regionview->region()->position() / speed);
5046 nframes64_t region_end = (nframes64_t) (clicked_regionview->region()->last_frame() / speed);
5047 nframes64_t region_length = (nframes64_t) (clicked_regionview->region()->length() / speed);
5049 //drag_info.item = clicked_regionview->get_name_highlight();
5050 drag_info.item = item;
5051 drag_info.motion_callback = &Editor::trim_motion_callback;
5052 drag_info.finished_callback = &Editor::trim_finished_callback;
5054 start_grab (event, trimmer_cursor);
5056 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
5057 trim_op = ContentsTrim;
5059 /* These will get overridden for a point trim.*/
5060 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
5061 /* closer to start */
5062 trim_op = StartTrim;
5063 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
5071 show_verbose_time_cursor(region_start, 10);
5074 show_verbose_time_cursor(region_end, 10);
5077 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5083 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
5085 RegionView* rv = clicked_regionview;
5086 nframes64_t frame_delta = 0;
5087 bool left_direction;
5088 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
5090 /* snap modifier works differently here..
5091 its' current state has to be passed to the
5092 various trim functions in order to work properly
5096 TimeAxisView* tvp = clicked_axisview;
5097 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
5098 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
5100 if (tv && tv->is_track()) {
5101 speed = tv->get_diskstream()->speed();
5104 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
5105 left_direction = true;
5107 left_direction = false;
5111 snap_to (drag_info.current_pointer_frame);
5114 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5118 if (drag_info.first_move) {
5124 trim_type = "Region start trim";
5127 trim_type = "Region end trim";
5130 trim_type = "Region content trim";
5134 begin_reversible_command (trim_type);
5136 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
5137 (*i)->fake_set_opaque(false);
5138 (*i)->region()->freeze ();
5140 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5142 arv->temporarily_hide_envelope ();
5144 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5145 insert_result = motion_frozen_playlists.insert (pl);
5146 if (insert_result.second) {
5147 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
5153 if (left_direction) {
5154 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
5156 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
5161 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
5164 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
5165 single_start_trim (**i, frame_delta, left_direction, obey_snap);
5171 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes64_t) (rv->region()->last_frame()/speed))) {
5174 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
5175 single_end_trim (**i, frame_delta, left_direction, obey_snap);
5182 bool swap_direction = false;
5184 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
5185 swap_direction = true;
5188 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5189 i != selection->regions.by_layer().end(); ++i)
5191 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
5199 show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
5202 show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
5205 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5209 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5210 drag_info.first_move = false;
5214 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
5216 boost::shared_ptr<Region> region (rv.region());
5218 if (region->locked()) {
5222 nframes64_t new_bound;
5225 TimeAxisView* tvp = clicked_axisview;
5226 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
5228 if (tv && tv->is_track()) {
5229 speed = tv->get_diskstream()->speed();
5232 if (left_direction) {
5233 if (swap_direction) {
5234 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
5236 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
5239 if (swap_direction) {
5240 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
5242 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
5247 snap_to (new_bound);
5249 region->trim_start ((nframes64_t) (new_bound * speed), this);
5250 rv.region_changed (StartChanged);
5254 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
5256 boost::shared_ptr<Region> region (rv.region());
5258 if (region->locked()) {
5262 nframes64_t new_bound;
5265 TimeAxisView* tvp = clicked_axisview;
5266 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
5268 if (tv && tv->is_track()) {
5269 speed = tv->get_diskstream()->speed();
5272 if (left_direction) {
5273 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
5275 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
5279 snap_to (new_bound, (left_direction ? 0 : 1));
5282 region->trim_front ((nframes64_t) (new_bound * speed), this);
5284 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
5288 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
5290 boost::shared_ptr<Region> region (rv.region());
5292 if (region->locked()) {
5296 nframes64_t new_bound;
5299 TimeAxisView* tvp = clicked_axisview;
5300 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
5302 if (tv && tv->is_track()) {
5303 speed = tv->get_diskstream()->speed();
5306 if (left_direction) {
5307 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
5309 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
5313 snap_to (new_bound);
5315 region->trim_end ((nframes64_t) (new_bound * speed), this);
5316 rv.region_changed (LengthChanged);
5320 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
5322 if (!drag_info.first_move) {
5323 trim_motion_callback (item, event);
5325 if (!selection->selected (clicked_regionview)) {
5326 thaw_region_after_trim (*clicked_regionview);
5329 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5330 i != selection->regions.by_layer().end(); ++i)
5332 thaw_region_after_trim (**i);
5333 (*i)->fake_set_opaque (true);
5337 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
5339 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
5342 motion_frozen_playlists.clear ();
5344 commit_reversible_command();
5346 /* no mouse movement */
5352 Editor::point_trim (GdkEvent* event)
5354 RegionView* rv = clicked_regionview;
5355 nframes64_t new_bound = drag_info.current_pointer_frame;
5357 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5358 snap_to (new_bound);
5361 /* Choose action dependant on which button was pressed */
5362 switch (event->button.button) {
5364 trim_op = StartTrim;
5365 begin_reversible_command (_("Start point trim"));
5367 if (selection->selected (rv)) {
5369 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5370 i != selection->regions.by_layer().end(); ++i)
5372 if (!(*i)->region()->locked()) {
5373 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5374 XMLNode &before = pl->get_state();
5375 (*i)->region()->trim_front (new_bound, this);
5376 XMLNode &after = pl->get_state();
5377 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5383 if (!rv->region()->locked()) {
5384 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5385 XMLNode &before = pl->get_state();
5386 rv->region()->trim_front (new_bound, this);
5387 XMLNode &after = pl->get_state();
5388 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5392 commit_reversible_command();
5397 begin_reversible_command (_("End point trim"));
5399 if (selection->selected (rv)) {
5401 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
5403 if (!(*i)->region()->locked()) {
5404 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5405 XMLNode &before = pl->get_state();
5406 (*i)->region()->trim_end (new_bound, this);
5407 XMLNode &after = pl->get_state();
5408 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5414 if (!rv->region()->locked()) {
5415 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5416 XMLNode &before = pl->get_state();
5417 rv->region()->trim_end (new_bound, this);
5418 XMLNode &after = pl->get_state();
5419 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
5423 commit_reversible_command();
5432 Editor::thaw_region_after_trim (RegionView& rv)
5434 boost::shared_ptr<Region> region (rv.region());
5436 if (region->locked()) {
5440 region->thaw (_("trimmed region"));
5441 XMLNode &after = region->playlist()->get_state();
5442 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
5444 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
5446 arv->unhide_envelope ();
5450 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
5455 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
5456 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
5460 Location* location = find_location_from_marker (marker, is_start);
5461 location->set_hidden (true, this);
5466 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
5472 drag_info.item = item;
5473 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
5474 drag_info.finished_callback = &Editor::end_range_markerbar_op;
5476 range_marker_op = op;
5478 if (!temp_location) {
5479 temp_location = new Location;
5483 case CreateRangeMarker:
5484 case CreateTransportMarker:
5485 case CreateCDMarker:
5487 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
5488 drag_info.copy = true;
5490 drag_info.copy = false;
5492 start_grab (event, selector_cursor);
5496 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5501 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5503 nframes64_t start = 0;
5504 nframes64_t end = 0;
5505 ArdourCanvas::SimpleRect *crect;
5507 switch (range_marker_op) {
5508 case CreateRangeMarker:
5509 crect = range_bar_drag_rect;
5511 case CreateTransportMarker:
5512 crect = transport_bar_drag_rect;
5514 case CreateCDMarker:
5515 crect = cd_marker_bar_drag_rect;
5518 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
5523 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5524 snap_to (drag_info.current_pointer_frame);
5527 /* only alter selection if the current frame is
5528 different from the last frame position.
5531 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5533 switch (range_marker_op) {
5534 case CreateRangeMarker:
5535 case CreateTransportMarker:
5536 case CreateCDMarker:
5537 if (drag_info.first_move) {
5538 snap_to (drag_info.grab_frame);
5541 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5542 start = drag_info.current_pointer_frame;
5543 end = drag_info.grab_frame;
5545 end = drag_info.current_pointer_frame;
5546 start = drag_info.grab_frame;
5549 /* first drag: Either add to the selection
5550 or create a new selection.
5553 if (drag_info.first_move) {
5555 temp_location->set (start, end);
5559 update_marker_drag_item (temp_location);
5560 range_marker_drag_rect->show();
5561 //range_marker_drag_rect->raise_to_top();
5567 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
5568 start_canvas_autoscroll (1, 0);
5572 temp_location->set (start, end);
5574 double x1 = frame_to_pixel (start);
5575 double x2 = frame_to_pixel (end);
5576 crect->property_x1() = x1;
5577 crect->property_x2() = x2;
5579 update_marker_drag_item (temp_location);
5582 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5583 drag_info.first_move = false;
5585 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5590 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5592 Location * newloc = 0;
5596 if (!drag_info.first_move) {
5597 drag_range_markerbar_op (item, event);
5599 switch (range_marker_op) {
5600 case CreateRangeMarker:
5601 case CreateCDMarker:
5603 begin_reversible_command (_("new range marker"));
5604 XMLNode &before = session->locations()->get_state();
5605 session->locations()->next_available_name(rangename,"unnamed");
5606 if (range_marker_op == CreateCDMarker) {
5607 flags = Location::IsRangeMarker|Location::IsCDMarker;
5608 cd_marker_bar_drag_rect->hide();
5611 flags = Location::IsRangeMarker;
5612 range_bar_drag_rect->hide();
5614 newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
5615 session->locations()->add (newloc, true);
5616 XMLNode &after = session->locations()->get_state();
5617 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
5618 commit_reversible_command ();
5620 range_marker_drag_rect->hide();
5624 case CreateTransportMarker:
5625 // popup menu to pick loop or punch
5626 new_transport_marker_context_menu (&event->button, item);
5631 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5633 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
5638 start = session->locations()->first_mark_before (drag_info.grab_frame);
5639 end = session->locations()->first_mark_after (drag_info.grab_frame);
5641 if (end == max_frames) {
5642 end = session->current_end_frame ();
5646 start = session->current_start_frame ();
5649 switch (mouse_mode) {
5651 /* find the two markers on either side and then make the selection from it */
5652 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
5656 /* find the two markers on either side of the click and make the range out of it */
5657 selection->set (0, start, end);
5666 stop_canvas_autoscroll ();
5672 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5674 drag_info.item = item;
5675 drag_info.motion_callback = &Editor::drag_mouse_zoom;
5676 drag_info.finished_callback = &Editor::end_mouse_zoom;
5678 start_grab (event, zoom_cursor);
5680 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5684 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5689 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5690 snap_to (drag_info.current_pointer_frame);
5692 if (drag_info.first_move) {
5693 snap_to (drag_info.grab_frame);
5697 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5699 /* base start and end on initial click position */
5700 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5701 start = drag_info.current_pointer_frame;
5702 end = drag_info.grab_frame;
5704 end = drag_info.current_pointer_frame;
5705 start = drag_info.grab_frame;
5710 if (drag_info.first_move) {
5712 zoom_rect->raise_to_top();
5715 reposition_zoom_rect(start, end);
5717 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5718 drag_info.first_move = false;
5720 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5725 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5727 if (!drag_info.first_move) {
5728 drag_mouse_zoom (item, event);
5730 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5731 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
5733 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
5736 temporal_zoom_to_frame (false, drag_info.grab_frame);
5738 temporal_zoom_step (false);
5739 center_screen (drag_info.grab_frame);
5747 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
5749 double x1 = frame_to_pixel (start);
5750 double x2 = frame_to_pixel (end);
5751 double y2 = full_canvas_height - 1.0;
5753 zoom_rect->property_x1() = x1;
5754 zoom_rect->property_y1() = 1.0;
5755 zoom_rect->property_x2() = x2;
5756 zoom_rect->property_y2() = y2;
5760 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5762 drag_info.item = item;
5763 drag_info.motion_callback = &Editor::drag_rubberband_select;
5764 drag_info.finished_callback = &Editor::end_rubberband_select;
5766 start_grab (event, cross_hair_cursor);
5768 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5772 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5779 /* use a bigger drag threshold than the default */
5781 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
5785 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) {
5786 if (drag_info.first_move) {
5787 snap_to (drag_info.grab_frame);
5789 snap_to (drag_info.current_pointer_frame);
5792 /* base start and end on initial click position */
5794 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5795 start = drag_info.current_pointer_frame;
5796 end = drag_info.grab_frame;
5798 end = drag_info.current_pointer_frame;
5799 start = drag_info.grab_frame;
5802 if (drag_info.current_pointer_y < drag_info.grab_y) {
5803 y1 = drag_info.current_pointer_y;
5804 y2 = drag_info.grab_y;
5806 y2 = drag_info.current_pointer_y;
5807 y1 = drag_info.grab_y;
5811 if (start != end || y1 != y2) {
5813 double x1 = frame_to_pixel (start);
5814 double x2 = frame_to_pixel (end);
5816 rubberband_rect->property_x1() = x1;
5817 rubberband_rect->property_y1() = y1;
5818 rubberband_rect->property_x2() = x2;
5819 rubberband_rect->property_y2() = y2;
5821 rubberband_rect->show();
5822 rubberband_rect->raise_to_top();
5824 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5825 drag_info.first_move = false;
5827 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5832 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5834 if (!drag_info.first_move) {
5836 drag_rubberband_select (item, event);
5839 if (drag_info.current_pointer_y < drag_info.grab_y) {
5840 y1 = drag_info.current_pointer_y;
5841 y2 = drag_info.grab_y;
5843 y2 = drag_info.current_pointer_y;
5844 y1 = drag_info.grab_y;
5848 Selection::Operation op = Keyboard::selection_type (event->button.state);
5851 begin_reversible_command (_("rubberband selection"));
5853 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5854 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5856 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5860 commit_reversible_command ();
5864 if (!getenv("ARDOUR_SAE")) {
5865 selection->clear_tracks();
5867 selection->clear_regions();
5868 selection->clear_points ();
5869 selection->clear_lines ();
5872 rubberband_rect->hide();
5877 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5879 using namespace Gtkmm2ext;
5881 ArdourPrompter prompter (false);
5883 prompter.set_prompt (_("Name for region:"));
5884 prompter.set_initial_text (clicked_regionview->region()->name());
5885 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5886 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5887 prompter.show_all ();
5888 switch (prompter.run ()) {
5889 case Gtk::RESPONSE_ACCEPT:
5891 prompter.get_result(str);
5893 clicked_regionview->region()->set_name (str);
5901 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5903 drag_info.item = item;
5904 drag_info.motion_callback = &Editor::time_fx_motion;
5905 drag_info.finished_callback = &Editor::end_time_fx;
5909 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5913 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5915 RegionView* rv = clicked_regionview;
5917 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5918 snap_to (drag_info.current_pointer_frame);
5921 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5925 if (drag_info.current_pointer_frame > rv->region()->position()) {
5926 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5929 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5930 drag_info.first_move = false;
5932 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5936 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5938 clicked_regionview->get_time_axis_view().hide_timestretch ();
5940 if (drag_info.first_move) {
5944 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5945 /* backwards drag of the left edge - not usable */
5949 nframes64_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5951 float percentage = (double) newlen / (double) clicked_regionview->region()->length();
5953 #ifndef USE_RUBBERBAND
5954 // Soundtouch uses percentage / 100 instead of normal (/ 1)
5955 if (clicked_regionview->region()->data_type() == DataType::AUDIO) {
5956 percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5960 begin_reversible_command (_("timestretch"));
5962 // XXX how do timeFX on multiple regions ?
5965 rs.add (clicked_regionview);
5967 if (time_stretch (rs, percentage) == 0) {
5968 session->commit_reversible_command ();
5973 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
5975 /* no brushing without a useful snap setting */
5977 switch (snap_mode) {
5979 return; /* can't work because it allows region to be placed anywhere */
5984 switch (snap_type) {
5992 /* don't brush a copy over the original */
5994 if (pos == rv->region()->position()) {
5998 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
6000 if (rtv == 0 || !rtv->is_track()) {
6004 boost::shared_ptr<Playlist> playlist = rtv->playlist();
6005 double speed = rtv->get_diskstream()->speed();
6007 XMLNode &before = playlist->get_state();
6008 playlist->add_region (RegionFactory::create (rv->region()), (nframes64_t) (pos * speed));
6009 XMLNode &after = playlist->get_state();
6010 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
6012 // playlist is frozen, so we have to update manually
6014 playlist->Modified(); /* EMIT SIGNAL */
6018 Editor::track_height_step_timeout ()
6020 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
6021 current_stepping_trackview = 0;