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 (where, 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);
2056 /** @param item Canvas item
2057 * @param event GDK event, or 0.
2060 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
2062 bool did_drag = false;
2064 stop_canvas_autoscroll ();
2066 if (drag_info.item == 0) {
2070 drag_info.item->ungrab (event ? event->button.time : 0);
2072 if (drag_info.finished_callback && event) {
2073 drag_info.last_pointer_x = drag_info.current_pointer_x;
2074 drag_info.last_pointer_y = drag_info.current_pointer_y;
2075 (this->*(drag_info.finished_callback)) (item, event);
2078 did_drag = !drag_info.first_move;
2080 hide_verbose_canvas_cursor();
2088 Editor::region_gain_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2090 if (drag_info.first_move && drag_info.move_threshold_passed) {
2091 drag_info.first_move = false;
2096 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
2098 drag_info.item = item;
2099 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
2100 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
2104 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
2105 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
2109 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2112 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes64_t) arv->audio_region()->fade_in()->back()->when + arv->region()->position());
2116 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2118 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2120 nframes64_t fade_length;
2122 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2123 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2129 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2133 if (pos < (arv->region()->position() + 64)) {
2134 fade_length = 64; // this should be a minimum defined somewhere
2135 } else if (pos > arv->region()->last_frame()) {
2136 fade_length = arv->region()->length();
2138 fade_length = pos - arv->region()->position();
2140 /* mapover the region selection */
2142 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2144 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2150 tmp->reset_fade_in_shape_width (fade_length);
2153 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
2155 drag_info.first_move = false;
2159 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2161 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2163 nframes64_t fade_length;
2165 if (drag_info.first_move) return;
2167 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2168 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2173 if (pos < (arv->region()->position() + 64)) {
2174 fade_length = 64; // this should be a minimum defined somewhere
2175 } else if (pos > arv->region()->last_frame()) {
2176 fade_length = arv->region()->length();
2178 fade_length = pos - arv->region()->position();
2181 begin_reversible_command (_("change fade in length"));
2183 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2185 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2191 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2192 XMLNode &before = alist->get_state();
2194 tmp->audio_region()->set_fade_in_length (fade_length);
2195 tmp->audio_region()->set_fade_in_active (true);
2197 XMLNode &after = alist->get_state();
2198 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2201 commit_reversible_command ();
2205 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
2207 drag_info.item = item;
2208 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
2209 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
2213 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
2214 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
2218 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2220 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes64_t) arv->audio_region()->fade_out()->back()->when + arv->region()->position());
2224 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2226 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2228 nframes64_t fade_length;
2230 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2231 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2236 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2240 if (pos > (arv->region()->last_frame() - 64)) {
2241 fade_length = 64; // this should really be a minimum fade defined somewhere
2243 else if (pos < arv->region()->position()) {
2244 fade_length = arv->region()->length();
2247 fade_length = arv->region()->last_frame() - pos;
2250 /* mapover the region selection */
2252 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2254 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2260 tmp->reset_fade_out_shape_width (fade_length);
2263 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
2265 drag_info.first_move = false;
2269 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2271 if (drag_info.first_move) return;
2273 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2275 nframes64_t fade_length;
2277 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2278 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2284 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2288 if (pos > (arv->region()->last_frame() - 64)) {
2289 fade_length = 64; // this should really be a minimum fade defined somewhere
2291 else if (pos < arv->region()->position()) {
2292 fade_length = arv->region()->length();
2295 fade_length = arv->region()->last_frame() - pos;
2298 begin_reversible_command (_("change fade out length"));
2300 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2302 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2308 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2309 XMLNode &before = alist->get_state();
2311 tmp->audio_region()->set_fade_out_length (fade_length);
2312 tmp->audio_region()->set_fade_out_active (true);
2314 XMLNode &after = alist->get_state();
2315 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2318 commit_reversible_command ();
2322 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2324 drag_info.item = item;
2325 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2326 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2330 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2331 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2335 Cursor* cursor = (Cursor *) drag_info.data;
2337 if (cursor == playhead_cursor) {
2338 _dragging_playhead = true;
2340 if (session && drag_info.was_rolling) {
2341 session->request_stop ();
2344 if (session && session->is_auditioning()) {
2345 session->cancel_audition ();
2349 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2351 show_verbose_time_cursor (cursor->current_frame, 10);
2355 Editor::start_cursor_grab_no_stop (ArdourCanvas::Item* item, GdkEvent* event)
2357 drag_info.item = item;
2358 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2359 drag_info.finished_callback = &Editor::cursor_drag_finished_ensure_locate_callback;
2363 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2364 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2368 Cursor* cursor = (Cursor *) drag_info.data;
2369 nframes64_t where = event_frame (event, 0, 0);
2372 playhead_cursor->set_position (where);
2374 if (cursor == playhead_cursor) {
2375 _dragging_playhead = true;
2377 if (session && session->is_auditioning()) {
2378 session->cancel_audition ();
2382 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2384 show_verbose_time_cursor (cursor->current_frame, 10);
2388 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2390 Cursor* cursor = (Cursor *) drag_info.data;
2391 nframes64_t adjusted_frame;
2393 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2394 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2400 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2401 if (cursor == playhead_cursor) {
2402 snap_to (adjusted_frame);
2406 if (adjusted_frame == drag_info.last_pointer_frame) return;
2408 cursor->set_position (adjusted_frame);
2410 show_verbose_time_cursor (cursor->current_frame, 10);
2413 track_canvas->update_now ();
2415 UpdateAllTransportClocks (cursor->current_frame);
2417 drag_info.last_pointer_frame = adjusted_frame;
2418 drag_info.first_move = false;
2422 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2424 _dragging_playhead = false;
2426 if (drag_info.first_move) {
2430 cursor_drag_motion_callback (item, event);
2432 if (item == &playhead_cursor->canvas_item) {
2434 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2435 _pending_locate_request = true;
2441 Editor::cursor_drag_finished_ensure_locate_callback (ArdourCanvas::Item* item, GdkEvent* event)
2443 _dragging_playhead = false;
2445 cursor_drag_motion_callback (item, event);
2447 if (item == &playhead_cursor->canvas_item) {
2449 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2450 _pending_locate_request = true;
2456 Editor::update_marker_drag_item (Location *location)
2458 double x1 = frame_to_pixel (location->start());
2459 double x2 = frame_to_pixel (location->end());
2461 if (location->is_mark()) {
2462 marker_drag_line_points.front().set_x(x1);
2463 marker_drag_line_points.back().set_x(x1);
2464 marker_drag_line->property_points() = marker_drag_line_points;
2466 range_marker_drag_rect->property_x1() = x1;
2467 range_marker_drag_rect->property_x2() = x2;
2473 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2477 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2478 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2484 Location *location = find_location_from_marker (marker, is_start);
2486 drag_info.item = item;
2487 drag_info.data = marker;
2488 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2489 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2493 _dragging_edit_point = true;
2495 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2497 update_marker_drag_item (location);
2499 if (location->is_mark()) {
2500 // marker_drag_line->show();
2501 // marker_drag_line->raise_to_top();
2503 range_marker_drag_rect->show();
2504 //range_marker_drag_rect->raise_to_top();
2508 show_verbose_time_cursor (location->start(), 10);
2510 show_verbose_time_cursor (location->end(), 10);
2513 Selection::Operation op = Keyboard::selection_type (event->button.state);
2516 case Selection::Toggle:
2517 selection->toggle (marker);
2519 case Selection::Set:
2520 if (!selection->selected (marker)) {
2521 selection->set (marker);
2524 case Selection::Extend:
2526 Locations::LocationList ll;
2527 list<Marker*> to_add;
2529 selection->markers.range (s, e);
2530 s = min (marker->position(), s);
2531 e = max (marker->position(), e);
2534 if (e < max_frames) {
2537 session->locations()->find_all_between (s, e, ll, Location::Flags (0));
2538 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2539 LocationMarkers* lm = find_location_markers (*i);
2542 to_add.push_back (lm->start);
2545 to_add.push_back (lm->end);
2549 if (!to_add.empty()) {
2550 selection->add (to_add);
2554 case Selection::Add:
2555 selection->add (marker);
2559 /* set up copies for us to manipulate during the drag */
2561 drag_info.clear_copied_locations ();
2563 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
2564 Location *l = find_location_from_marker (*i, is_start);
2565 drag_info.copied_locations.push_back (new Location (*l));
2570 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2572 nframes64_t f_delta = 0;
2573 nframes64_t newframe;
2575 bool move_both = false;
2576 Marker* dragged_marker = (Marker*) drag_info.data;
2578 Location *real_location;
2579 Location *copy_location = 0;
2581 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2582 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2587 nframes64_t next = newframe;
2589 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2590 snap_to (newframe, 0, true);
2593 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2597 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2601 MarkerSelection::iterator i;
2602 list<Location*>::iterator x;
2604 /* find the marker we're dragging, and compute the delta */
2606 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2607 x != drag_info.copied_locations.end() && i != selection->markers.end();
2613 if (marker == dragged_marker) {
2615 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2620 if (real_location->is_mark()) {
2621 f_delta = newframe - copy_location->start();
2625 switch (marker->type()) {
2627 case Marker::LoopStart:
2628 case Marker::PunchIn:
2629 f_delta = newframe - copy_location->start();
2633 case Marker::LoopEnd:
2634 case Marker::PunchOut:
2635 f_delta = newframe - copy_location->end();
2638 /* what kind of marker is this ? */
2646 if (i == selection->markers.end()) {
2647 /* hmm, impossible - we didn't find the dragged marker */
2651 /* now move them all */
2653 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2654 x != drag_info.copied_locations.end() && i != selection->markers.end();
2660 /* call this to find out if its the start or end */
2662 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2666 if (real_location->locked()) {
2670 if (copy_location->is_mark()) {
2674 copy_location->set_start (copy_location->start() + f_delta);
2678 nframes64_t new_start = copy_location->start() + f_delta;
2679 nframes64_t new_end = copy_location->end() + f_delta;
2681 if (is_start) { // start-of-range marker
2684 copy_location->set_start (new_start);
2685 copy_location->set_end (new_end);
2686 } else if (new_start < copy_location->end()) {
2687 copy_location->set_start (new_start);
2689 snap_to (next, 1, true);
2690 copy_location->set_end (next);
2691 copy_location->set_start (newframe);
2694 } else { // end marker
2697 copy_location->set_end (new_end);
2698 copy_location->set_start (new_start);
2699 } else if (new_end > copy_location->start()) {
2700 copy_location->set_end (new_end);
2701 } else if (newframe > 0) {
2702 snap_to (next, -1, true);
2703 copy_location->set_start (next);
2704 copy_location->set_end (newframe);
2708 update_marker_drag_item (copy_location);
2710 LocationMarkers* lm = find_location_markers (real_location);
2713 lm->set_position (copy_location->start(), copy_location->end());
2717 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2718 drag_info.first_move = false;
2720 if (drag_info.copied_locations.empty()) {
2724 edit_point_clock.set (drag_info.copied_locations.front()->start());
2725 show_verbose_time_cursor (newframe, 10);
2728 track_canvas->update_now ();
2730 edit_point_clock.set (copy_location->start());
2734 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2736 if (drag_info.first_move) {
2738 /* just a click, do nothing but finish
2739 off the selection process
2742 Selection::Operation op = Keyboard::selection_type (event->button.state);
2743 Marker* marker = (Marker *) drag_info.data;
2746 case Selection::Set:
2747 if (selection->selected (marker) && selection->markers.size() > 1) {
2748 selection->set (marker);
2752 case Selection::Toggle:
2753 case Selection::Extend:
2754 case Selection::Add:
2761 _dragging_edit_point = false;
2764 begin_reversible_command ( _("move marker") );
2765 XMLNode &before = session->locations()->get_state();
2767 MarkerSelection::iterator i;
2768 list<Location*>::iterator x;
2771 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2772 x != drag_info.copied_locations.end() && i != selection->markers.end();
2775 Location * location = find_location_from_marker ((*i), is_start);
2779 if (location->locked()) {
2783 if (location->is_mark()) {
2784 location->set_start ((*x)->start());
2786 location->set ((*x)->start(), (*x)->end());
2791 XMLNode &after = session->locations()->get_state();
2792 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2793 commit_reversible_command ();
2795 marker_drag_line->hide();
2796 range_marker_drag_rect->hide();
2800 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2803 MeterMarker* meter_marker;
2805 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2806 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2810 meter_marker = dynamic_cast<MeterMarker*> (marker);
2812 MetricSection& section (meter_marker->meter());
2814 if (!section.movable()) {
2818 drag_info.item = item;
2819 drag_info.copy = false;
2820 drag_info.data = marker;
2821 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2822 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2826 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2828 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2832 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2835 MeterMarker* meter_marker;
2837 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2838 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2842 meter_marker = dynamic_cast<MeterMarker*> (marker);
2844 // create a dummy marker for visual representation of moving the copy.
2845 // The actual copying is not done before we reach the finish callback.
2847 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2848 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2849 *new MeterSection(meter_marker->meter()));
2851 drag_info.item = &new_marker->the_item();
2852 drag_info.copy = true;
2853 drag_info.data = new_marker;
2854 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2855 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2859 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2861 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2865 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2867 MeterMarker* marker = (MeterMarker *) drag_info.data;
2868 nframes64_t adjusted_frame;
2870 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2871 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2877 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2878 snap_to (adjusted_frame);
2881 if (adjusted_frame == drag_info.last_pointer_frame) return;
2883 marker->set_position (adjusted_frame);
2886 drag_info.last_pointer_frame = adjusted_frame;
2887 drag_info.first_move = false;
2889 show_verbose_time_cursor (adjusted_frame, 10);
2893 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2895 if (drag_info.first_move) return;
2897 meter_marker_drag_motion_callback (drag_info.item, event);
2899 MeterMarker* marker = (MeterMarker *) drag_info.data;
2902 TempoMap& map (session->tempo_map());
2903 map.bbt_time (drag_info.last_pointer_frame, when);
2905 if (drag_info.copy == true) {
2906 begin_reversible_command (_("copy meter mark"));
2907 XMLNode &before = map.get_state();
2908 map.add_meter (marker->meter(), when);
2909 XMLNode &after = map.get_state();
2910 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2911 commit_reversible_command ();
2913 // delete the dummy marker we used for visual representation of copying.
2914 // a new visual marker will show up automatically.
2917 begin_reversible_command (_("move meter mark"));
2918 XMLNode &before = map.get_state();
2919 map.move_meter (marker->meter(), when);
2920 XMLNode &after = map.get_state();
2921 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2922 commit_reversible_command ();
2927 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2930 TempoMarker* tempo_marker;
2932 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2933 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2937 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2938 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2942 MetricSection& section (tempo_marker->tempo());
2944 if (!section.movable()) {
2948 drag_info.item = item;
2949 drag_info.copy = false;
2950 drag_info.data = marker;
2951 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2952 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2956 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2957 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2961 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2964 TempoMarker* tempo_marker;
2966 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2967 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2971 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2972 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2976 // create a dummy marker for visual representation of moving the copy.
2977 // The actual copying is not done before we reach the finish callback.
2979 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2980 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2981 *new TempoSection(tempo_marker->tempo()));
2983 drag_info.item = &new_marker->the_item();
2984 drag_info.copy = true;
2985 drag_info.data = new_marker;
2986 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2987 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2991 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2993 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2997 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2999 TempoMarker* marker = (TempoMarker *) drag_info.data;
3000 nframes64_t adjusted_frame;
3002 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3003 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3009 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3010 snap_to (adjusted_frame);
3013 if (adjusted_frame == drag_info.last_pointer_frame) return;
3015 /* OK, we've moved far enough to make it worth actually move the thing. */
3017 marker->set_position (adjusted_frame);
3019 show_verbose_time_cursor (adjusted_frame, 10);
3021 drag_info.last_pointer_frame = adjusted_frame;
3022 drag_info.first_move = false;
3026 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3028 if (drag_info.first_move) return;
3030 tempo_marker_drag_motion_callback (drag_info.item, event);
3032 TempoMarker* marker = (TempoMarker *) drag_info.data;
3035 TempoMap& map (session->tempo_map());
3036 map.bbt_time (drag_info.last_pointer_frame, when);
3038 if (drag_info.copy == true) {
3039 begin_reversible_command (_("copy tempo mark"));
3040 XMLNode &before = map.get_state();
3041 map.add_tempo (marker->tempo(), when);
3042 XMLNode &after = map.get_state();
3043 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3044 commit_reversible_command ();
3046 // delete the dummy marker we used for visual representation of copying.
3047 // a new visual marker will show up automatically.
3050 begin_reversible_command (_("move tempo mark"));
3051 XMLNode &before = map.get_state();
3052 map.move_tempo (marker->tempo(), when);
3053 XMLNode &after = map.get_state();
3054 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3055 commit_reversible_command ();
3060 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
3062 ControlPoint* control_point;
3064 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
3065 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
3069 // We shouldn't remove the first or last gain point
3070 if (control_point->line().is_last_point(*control_point) ||
3071 control_point->line().is_first_point(*control_point)) {
3075 control_point->line().remove_point (*control_point);
3079 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event)
3081 ControlPoint* control_point;
3083 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
3084 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
3088 control_point->line().remove_point (*control_point);
3092 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
3094 ControlPoint* control_point;
3096 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
3097 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
3101 drag_info.item = item;
3102 drag_info.data = control_point;
3103 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
3104 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
3106 start_grab (event, fader_cursor);
3108 // start the grab at the center of the control point so
3109 // the point doesn't 'jump' to the mouse after the first drag
3110 drag_info.grab_x = control_point->get_x();
3111 drag_info.grab_y = control_point->get_y();
3113 control_point->line().parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
3114 track_canvas->w2c(drag_info.grab_x, drag_info.grab_y, drag_info.grab_x, drag_info.grab_y);
3116 drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
3118 control_point->line().start_drag (control_point, drag_info.grab_frame, 0);
3120 float fraction = 1.0 - (control_point->get_y() / control_point->line().height());
3121 set_verbose_canvas_cursor (control_point->line().get_verbose_cursor_string (fraction),
3122 drag_info.current_pointer_x + 10, drag_info.current_pointer_y + 10);
3124 show_verbose_canvas_cursor ();
3128 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3130 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
3132 double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
3133 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
3135 if (event->button.state & Keyboard::SecondaryModifier) {
3140 double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
3141 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
3143 // calculate zero crossing point. back off by .01 to stay on the
3144 // positive side of zero
3146 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line().height() - .01;
3147 cp->line().parent_group().i2w(_unused, zero_gain_y);
3149 // make sure we hit zero when passing through
3150 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
3151 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
3155 if (drag_info.x_constrained) {
3156 cx = drag_info.grab_x;
3158 if (drag_info.y_constrained) {
3159 cy = drag_info.grab_y;
3162 drag_info.cumulative_x_drag = cx - drag_info.grab_x;
3163 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
3165 cp->line().parent_group().w2i (cx, cy);
3169 cy = min ((double) cp->line().height(), cy);
3171 //translate cx to frames
3172 nframes64_t cx_frames = unit_to_frame (cx);
3174 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
3175 snap_to (cx_frames);
3178 float fraction = 1.0 - (cy / cp->line().height());
3182 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3188 cp->line().point_drag (*cp, cx_frames , fraction, push);
3190 set_verbose_canvas_cursor_text (cp->line().get_verbose_cursor_string (fraction));
3192 drag_info.first_move = false;
3196 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3198 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
3200 if (drag_info.first_move) {
3204 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3205 reset_point_selection ();
3209 control_point_drag_motion_callback (item, event);
3211 cp->line().end_drag (cp);
3215 Editor::edit_control_point (ArdourCanvas::Item* item)
3217 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
3220 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
3224 ControlPointDialog d (p);
3225 d.set_position (Gtk::WIN_POS_MOUSE);
3228 if (d.run () != RESPONSE_ACCEPT) {
3232 p->line().modify_point_y (*p, d.get_y_fraction ());
3237 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
3239 switch (mouse_mode) {
3241 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
3242 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
3250 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
3254 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
3255 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
3259 start_line_grab (al, event);
3263 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
3267 nframes64_t frame_within_region;
3269 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3270 origin, and ditto for y.
3273 cx = event->button.x;
3274 cy = event->button.y;
3276 line->parent_group().w2i (cx, cy);
3278 frame_within_region = (nframes64_t) floor (cx * frames_per_unit);
3280 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
3281 current_line_drag_info.after)) {
3282 /* no adjacent points */
3286 drag_info.item = &line->grab_item();
3287 drag_info.data = line;
3288 drag_info.motion_callback = &Editor::line_drag_motion_callback;
3289 drag_info.finished_callback = &Editor::line_drag_finished_callback;
3291 start_grab (event, fader_cursor);
3293 /* store grab start in parent frame */
3295 drag_info.grab_x = cx;
3296 drag_info.grab_y = cy;
3298 double fraction = 1.0 - (cy / line->height());
3300 line->start_drag (0, drag_info.grab_frame, fraction);
3302 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
3303 drag_info.current_pointer_x + 10, drag_info.current_pointer_y + 10);
3304 show_verbose_canvas_cursor ();
3308 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3310 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3312 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
3314 if (event->button.state & Keyboard::SecondaryModifier) {
3318 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
3320 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
3323 cy = min ((double) line->height(), cy);
3326 double fraction = 1.0 - (cy / line->height());
3330 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3336 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
3338 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
3342 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3344 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3345 line_drag_motion_callback (item, event);
3350 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3352 if (selection->regions.empty() || clicked_regionview == 0) {
3355 _region_motion_group->raise_to_top ();
3356 drag_info.copy = false;
3357 drag_info.item = item;
3358 drag_info.data = clicked_regionview;
3360 if (Config->get_edit_mode() == Splice) {
3361 drag_info.motion_callback = &Editor::region_drag_splice_motion_callback;
3362 drag_info.finished_callback = &Editor::region_drag_splice_finished_callback;
3364 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3365 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3371 TimeAxisView* tvp = clicked_axisview;
3372 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3374 if (tv && tv->is_track()) {
3375 speed = tv->get_diskstream()->speed();
3378 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3379 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3380 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3381 drag_info.source_layer = clicked_regionview->region()->layer();
3382 drag_info.dest_trackview = drag_info.source_trackview;
3383 drag_info.dest_layer = drag_info.source_layer;
3384 // we want a move threshold
3385 drag_info.want_move_threshold = true;
3386 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3388 begin_reversible_command (_("move region(s)"));
3390 /* sync the canvas to what we think is its current state */
3391 track_canvas->update_now();
3395 Editor::start_create_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3397 drag_info.copy = false;
3398 drag_info.item = item;
3399 drag_info.data = clicked_axisview;
3400 drag_info.source_trackview = clicked_axisview;
3401 drag_info.dest_trackview = drag_info.source_trackview;
3402 drag_info.dest_layer = drag_info.source_layer;
3403 drag_info.motion_callback = &Editor::create_region_drag_motion_callback;
3404 drag_info.finished_callback = &Editor::create_region_drag_finished_callback;
3410 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
3412 if (selection->regions.empty() || clicked_regionview == 0) {
3415 _region_motion_group->raise_to_top ();
3416 drag_info.copy = true;
3417 drag_info.item = item;
3418 drag_info.data = clicked_regionview;
3422 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
3423 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3426 if (rtv && rtv->is_track()) {
3427 speed = rtv->get_diskstream()->speed();
3430 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3431 drag_info.dest_trackview = drag_info.source_trackview;
3432 drag_info.dest_layer = drag_info.source_layer;
3433 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3434 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3435 // we want a move threshold
3436 drag_info.want_move_threshold = true;
3437 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3438 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3439 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3443 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
3445 if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) {
3449 drag_info.copy = false;
3450 drag_info.item = item;
3451 drag_info.data = clicked_regionview;
3452 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3453 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3458 TimeAxisView* tvp = clicked_axisview;
3459 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3461 if (tv && tv->is_track()) {
3462 speed = tv->get_diskstream()->speed();
3465 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3466 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3467 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3468 drag_info.dest_trackview = drag_info.source_trackview;
3469 drag_info.dest_layer = drag_info.source_layer;
3470 // we want a move threshold
3471 drag_info.want_move_threshold = true;
3472 drag_info.brushing = true;
3474 begin_reversible_command (_("Drag region brush"));
3478 Editor::possibly_copy_regions_during_grab (GdkEvent* event)
3480 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
3482 drag_info.want_move_threshold = false; // don't copy again
3484 /* duplicate the regionview(s) and region(s) */
3486 vector<RegionView*> new_regionviews;
3488 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3493 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
3494 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
3496 const boost::shared_ptr<const Region> original = rv->region();
3497 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
3500 boost::shared_ptr<AudioRegion> audioregion_copy
3501 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
3502 nrv = new AudioRegionView (*arv, audioregion_copy);
3504 boost::shared_ptr<MidiRegion> midiregion_copy
3505 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
3506 nrv = new MidiRegionView (*mrv, midiregion_copy);
3511 nrv->get_canvas_group()->show ();
3512 new_regionviews.push_back (nrv);
3515 if (new_regionviews.empty()) {
3519 /* reset selection to new regionviews. This will not set selection visual status for
3520 these regionviews since they don't belong to a track, so do that by hand too.
3523 selection->set (new_regionviews);
3525 for (vector<RegionView*>::iterator i = new_regionviews.begin(); i != new_regionviews.end(); ++i) {
3526 (*i)->set_selected (true);
3529 /* reset drag_info data to reflect the fact that we are dragging the copies */
3531 drag_info.data = new_regionviews.front();
3533 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3535 sync the canvas to what we think is its current state
3536 without it, the canvas seems to
3537 "forget" to update properly after the upcoming reparent()
3538 ..only if the mouse is in rapid motion at the time of the grab.
3539 something to do with regionview creation raking so long?
3541 track_canvas->update_now();
3546 Editor::check_region_drag_possible (RouteTimeAxisView** tv, layer_t* layer)
3548 /* Which trackview is this ? */
3550 std::pair<TimeAxisView*, int> const tvp = trackview_by_y_position (drag_info.current_pointer_y);
3551 (*tv) = dynamic_cast<RouteTimeAxisView*> (tvp.first);
3552 (*layer) = tvp.second;
3554 /* The region motion is only processed if the pointer is over
3558 if (!(*tv) || !(*tv)->is_track()) {
3559 /* To make sure we hide the verbose canvas cursor when the mouse is
3560 not held over and audiotrack.
3562 hide_verbose_canvas_cursor ();
3569 struct RegionSelectionByPosition {
3570 bool operator() (RegionView*a, RegionView* b) {
3571 return a->region()->position () < b->region()->position();
3576 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3578 RouteTimeAxisView* tv;
3581 if (!check_region_drag_possible (&tv, &layer)) {
3585 if (!drag_info.move_threshold_passed) {
3591 if (drag_info.current_pointer_x - drag_info.grab_x > 0) {
3597 RegionSelection copy (selection->regions);
3599 RegionSelectionByPosition cmp;
3602 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
3604 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
3610 boost::shared_ptr<Playlist> playlist;
3612 if ((playlist = atv->playlist()) == 0) {
3616 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
3621 if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) {
3625 if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) {
3631 playlist->shuffle ((*i)->region(), dir);
3633 drag_info.grab_x = drag_info.current_pointer_x;
3638 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3643 Editor::visible_order_range (int* low, int* high) const
3645 *low = TimeAxisView::max_order ();
3648 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
3650 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3652 if (!rtv->hidden()) {
3654 if (*high < rtv->order()) {
3655 *high = rtv->order ();
3658 if (*low > rtv->order()) {
3659 *low = rtv->order ();
3665 /** @param new_order New track order.
3666 * @param old_order Old track order.
3667 * @param visible_y_low Lowest visible order.
3668 * @param visible_y_high Highest visible order.
3669 * @param tracks Bitset of tracks indexed by order; 0 means a audio/MIDI track, 1 means something else.
3670 * @param heigh_list Heights of tracks indexed by order.
3671 * @return true if y movement should not happen, otherwise false.
3674 Editor::y_movement_disallowed (
3675 int new_order, int old_order, int y_span, int visible_y_low, int visible_y_high,
3676 bitset<512> const & tracks, vector<int32_t> const & height_list
3679 if (new_order != old_order) {
3681 /* this isn't the pointer track */
3685 /* moving up the canvas */
3686 if ( (new_order - y_span) >= visible_y_low) {
3690 /* work out where we'll end up with this y span, taking hidden TimeAxisViews into account */
3691 int32_t visible_tracks = 0;
3692 while (visible_tracks < y_span ) {
3694 while (height_list[new_order - (visible_tracks - n)] == 0) {
3695 /* passing through a hidden track */
3700 if (tracks[new_order - (y_span - n)] != 0x00) {
3701 /* moving to a non-track; disallow */
3707 /* moving beyond the lowest visible track; disallow */
3711 } else if (y_span < 0) {
3713 /* moving down the canvas */
3714 if ((new_order - y_span) <= visible_y_high) {
3716 int32_t visible_tracks = 0;
3718 while (visible_tracks > y_span ) {
3721 while (height_list[new_order - (visible_tracks - n)] == 0) {
3722 /* passing through a hidden track */
3727 if (tracks[new_order - (y_span - n)] != 0x00) {
3728 /* moving to a non-track; disallow */
3735 /* moving beyond the highest visible track; disallow */
3742 /* this is the pointer's track */
3744 if ((new_order - y_span) > visible_y_high) {
3745 /* we will overflow */
3747 } else if ((new_order - y_span) < visible_y_low) {
3748 /* we will overflow */
3757 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3761 nframes64_t pending_region_position = 0;
3762 int32_t pointer_order_span = 0, canvas_pointer_order_span = 0;
3763 int32_t pointer_layer_span = 0;
3765 bool clamp_y_axis = false;
3766 vector<int32_t>::iterator j;
3768 possibly_copy_regions_during_grab (event);
3770 /* *pointer* variables reflect things about the pointer; as we may be moving
3771 multiple regions, much detail must be computed per-region */
3773 /* current_pointer_view will become the TimeAxisView that we're currently pointing at, and
3774 current_pointer_layer the current layer on that TimeAxisView */
3775 RouteTimeAxisView* current_pointer_view;
3776 layer_t current_pointer_layer;
3777 if (!check_region_drag_possible (¤t_pointer_view, ¤t_pointer_layer)) {
3781 /* TimeAxisView that we were pointing at last time we entered this method */
3782 TimeAxisView const * const last_pointer_view = drag_info.dest_trackview;
3783 /* the order of the track that we were pointing at last time we entered this method */
3784 int32_t const last_pointer_order = last_pointer_view->order ();
3785 /* the layer that we were pointing at last time we entered this method */
3786 layer_t const last_pointer_layer = drag_info.dest_layer;
3788 /************************************************************
3790 ************************************************************/
3792 /* Height of TimeAxisViews, indexed by order */
3793 /* XXX: hard-coded limit of TimeAxisViews */
3794 vector<int32_t> height_list (512);
3796 if (drag_info.brushing) {
3797 clamp_y_axis = true;
3798 pointer_order_span = 0;
3802 /* the change in track order between this callback and the last */
3803 pointer_order_span = last_pointer_view->order() - current_pointer_view->order();
3804 /* the change in layer between this callback and the last;
3805 only meaningful if pointer_order_span == 0 (ie we've not moved tracks) */
3806 pointer_layer_span = last_pointer_layer - current_pointer_layer;
3808 if (pointer_order_span != 0) {
3810 int32_t children = 0;
3811 /* XXX: hard-coded limit of tracks */
3812 bitset <512> tracks (0x00);
3816 visible_order_range (&visible_y_low, &visible_y_high);
3818 /* get a bitmask representing the visible tracks */
3820 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3821 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3822 TimeAxisView::Children children_list;
3824 /* zeroes are audio/MIDI tracks. ones are other types. */
3826 if (!rtv->hidden()) {
3828 if (!rtv->is_track()) {
3829 /* not an audio nor MIDI track */
3830 tracks = tracks |= (0x01 << rtv->order());
3833 height_list[rtv->order()] = (*i)->current_height();
3836 if ((children_list = rtv->get_child_list()).size() > 0) {
3837 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
3838 tracks = tracks |= (0x01 << (rtv->order() + children));
3839 height_list[rtv->order() + children] = (*j)->current_height();
3846 /* find the actual pointer span, in terms of the number of visible tracks;
3847 to do this, we reduce |pointer_order_span| by the number of hidden tracks
3850 canvas_pointer_order_span = pointer_order_span;
3851 if (last_pointer_view->order() >= current_pointer_view->order()) {
3852 for (int32_t y = current_pointer_view->order(); y < last_pointer_view->order(); y++) {
3853 if (height_list[y] == 0) {
3854 canvas_pointer_order_span--;
3858 for (int32_t y = last_pointer_view->order(); y <= current_pointer_view->order(); y++) {
3859 if (height_list[y] == 0) {
3860 canvas_pointer_order_span++;
3865 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3867 RegionView* rv = (*i);
3869 if (rv->region()->locked()) {
3873 double ix1, ix2, iy1, iy2;
3874 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3875 rv->get_canvas_frame()->i2w (ix1, iy1);
3876 iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
3878 /* get the new trackview for this particular region */
3879 std::pair<TimeAxisView*, int> const tvp = trackview_by_y_position (iy1);
3881 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
3883 /* I know this method has a slightly excessive argument list, but I think
3884 it's nice to separate the code out all the same, since it has such a
3885 simple result, and it makes it clear that there are no other
3889 /* XXX: not sure that we should be passing canvas_pointer_order_span in here,
3890 as surely this is a per-region thing... */
3892 clamp_y_axis = y_movement_disallowed (
3893 rtv->order(), last_pointer_order, canvas_pointer_order_span, visible_y_low, visible_y_high,
3902 } else if (drag_info.dest_trackview == current_pointer_view) {
3904 if (current_pointer_layer == last_pointer_layer) {
3905 /* No movement; clamp */
3906 clamp_y_axis = true;
3911 if (!clamp_y_axis) {
3912 drag_info.dest_trackview = current_pointer_view;
3913 drag_info.dest_layer = current_pointer_layer;
3916 /************************************************************
3918 ************************************************************/
3920 /* compute the amount of pointer motion in frames, and where
3921 the region would be if we moved it by that much.
3923 if ( drag_info.move_threshold_passed ) {
3925 if (drag_info.current_pointer_frame >= drag_info.pointer_frame_offset) {
3927 nframes64_t sync_frame;
3928 nframes64_t sync_offset;
3931 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3933 sync_offset = clicked_regionview->region()->sync_offset (sync_dir);
3935 /* we don't handle a sync point that lies before zero.
3937 if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) {
3938 sync_frame = pending_region_position + (sync_dir*sync_offset);
3940 /* we snap if the snap modifier is not enabled.
3943 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3944 snap_to (sync_frame);
3947 pending_region_position = clicked_regionview->region()->adjust_to_sync (sync_frame);
3950 pending_region_position = drag_info.last_frame_position;
3954 pending_region_position = 0;
3957 if (pending_region_position > max_frames - clicked_regionview->region()->length()) {
3958 pending_region_position = drag_info.last_frame_position;
3961 bool x_move_allowed;
3963 if (Config->get_edit_mode() == Lock) {
3964 if (drag_info.copy) {
3965 x_move_allowed = !drag_info.x_constrained;
3967 /* in locked edit mode, reverse the usual meaning of x_constrained */
3968 x_move_allowed = drag_info.x_constrained;
3971 x_move_allowed = !drag_info.x_constrained;
3974 if (( pending_region_position != drag_info.last_frame_position) && x_move_allowed ) {
3976 /* now compute the canvas unit distance we need to move the regionview
3977 to make it appear at the new location.
3980 if (pending_region_position > drag_info.last_frame_position) {
3981 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3983 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3984 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3986 RegionView* rv = (*i);
3988 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3990 double ix1, ix2, iy1, iy2;
3991 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3992 rv->get_canvas_frame()->i2w (ix1, iy1);
3994 if (-x_delta > ix1 + horizontal_adjustment.get_value()) {
3996 pending_region_position = drag_info.last_frame_position;
4003 drag_info.last_frame_position = pending_region_position;
4010 /* threshold not passed */
4015 /*************************************************************
4017 ************************************************************/
4019 if (x_delta == 0 && pointer_order_span == 0 && pointer_layer_span == 0) {
4020 /* haven't reached next snap point, and we're not switching
4021 trackviews nor layers. nothing to do.
4026 /*************************************************************
4028 ************************************************************/
4029 bool do_move = true;
4030 if (drag_info.first_move) {
4031 if (!drag_info.move_threshold_passed) {
4038 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4039 const list<RegionView*>& layered_regions = selection->regions.by_layer();
4041 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
4043 RegionView* rv = (*i);
4045 if (rv->region()->locked()) {
4049 /* here we are calculating the y distance from the
4050 top of the first track view to the top of the region
4051 area of the track view that we're working on */
4053 /* this x value is just a dummy value so that we have something
4054 to pass to i2w () */
4058 /* distance from the top of this track view to the region area
4059 of our track view is always 1 */
4063 /* convert to world coordinates, ie distance from the top of
4064 the ruler section */
4066 rv->get_canvas_frame()->i2w (ix1, iy1);
4068 /* compensate for the ruler section and the vertical scrollbar position */
4069 iy1 += get_trackview_group_vertical_offset ();
4071 if (drag_info.first_move) {
4073 // hide any dependent views
4075 rv->get_time_axis_view().hide_dependent_views (*rv);
4078 reparent to a non scrolling group so that we can keep the
4079 region selection above all time axis views.
4080 reparenting means we have to move the rv as the two
4081 parent groups have different coordinates.
4084 rv->get_canvas_group()->property_y() = iy1 - 1;
4085 rv->get_canvas_group()->reparent(*_region_motion_group);
4087 rv->fake_set_opaque (true);
4090 /* current view for this particular region */
4091 std::pair<TimeAxisView*, int> pos = trackview_by_y_position (iy1);
4092 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
4094 if (pointer_order_span != 0 && !clamp_y_axis) {
4096 /* INTER-TRACK MOVEMENT */
4098 /* move through the height list to the track that the region is currently on */
4099 vector<int32_t>::iterator j = height_list.begin ();
4101 while (j != height_list.end () && x != rtv->order ()) {
4107 int32_t temp_pointer_order_span = canvas_pointer_order_span;
4109 if (j != height_list.end ()) {
4111 /* Account for layers in the original and
4112 destination tracks. If we're moving around in layers we assume
4113 that only one track is involved, so it's ok to use *pointer*
4116 StreamView* lv = last_pointer_view->view ();
4119 /* move to the top of the last trackview */
4120 if (lv->layer_display () == Stacked) {
4121 y_delta -= (lv->layers() - last_pointer_layer - 1) * lv->child_height ();
4124 StreamView* cv = current_pointer_view->view ();
4127 /* move to the right layer on the current trackview */
4128 if (cv->layer_display () == Stacked) {
4129 y_delta += (cv->layers() - current_pointer_layer - 1) * cv->child_height ();
4132 /* And for being on a non-topmost layer on the new
4135 while (temp_pointer_order_span > 0) {
4136 /* we're moving up canvas-wise,
4137 so we need to find the next track height
4139 if (j != height_list.begin()) {
4143 if (x != last_pointer_order) {
4145 ++temp_pointer_order_span;
4150 temp_pointer_order_span--;
4153 while (temp_pointer_order_span < 0) {
4157 if (x != last_pointer_order) {
4159 --temp_pointer_order_span;
4163 if (j != height_list.end()) {
4167 temp_pointer_order_span++;
4171 /* find out where we'll be when we move and set height accordingly */
4173 std::pair<TimeAxisView*, int> const pos = trackview_by_y_position (iy1 + y_delta);
4174 RouteTimeAxisView const * temp_rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
4175 rv->set_height (temp_rtv->view()->child_height());
4177 /* if you un-comment the following, the region colours will follow
4178 the track colours whilst dragging; personally
4179 i think this can confuse things, but never mind.
4182 //const GdkColor& col (temp_rtv->view->get_region_color());
4183 //rv->set_color (const_cast<GdkColor&>(col));
4187 if (pointer_order_span == 0 && pointer_layer_span != 0 && !clamp_y_axis) {
4189 /* INTER-LAYER MOVEMENT in the same track */
4190 y_delta = rtv->view()->child_height () * pointer_layer_span;
4194 if (drag_info.brushing) {
4195 mouse_brush_insert_region (rv, pending_region_position);
4197 rv->move (x_delta, y_delta);
4200 } /* foreach region */
4204 if (drag_info.first_move && drag_info.move_threshold_passed) {
4205 cursor_group->raise_to_top();
4206 drag_info.first_move = false;
4209 if (x_delta != 0 && !drag_info.brushing) {
4210 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4215 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4217 bool nocommit = true;
4218 vector<RegionView*> copies;
4219 RouteTimeAxisView* source_tv;
4220 boost::shared_ptr<Diskstream> ds;
4221 boost::shared_ptr<Playlist> from_playlist;
4222 vector<RegionView*> new_selection;
4223 typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
4224 PlaylistSet modified_playlists;
4225 PlaylistSet frozen_playlists;
4226 list <sigc::connection> modified_playlist_connections;
4227 pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
4228 nframes64_t drag_delta;
4229 bool changed_tracks, changed_position;
4230 std::pair<TimeAxisView*, int> tvp;
4231 std::map<RegionView*, RouteTimeAxisView*> final;
4233 /* first_move is set to false if the regionview has been moved in the
4237 if (drag_info.first_move) {
4244 if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
4245 selection->set (pre_drag_region_selection);
4246 pre_drag_region_selection.clear ();
4249 if (drag_info.brushing) {
4250 /* all changes were made during motion event handlers */
4252 if (drag_info.copy) {
4253 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4254 copies.push_back (*i);
4263 /* reverse this here so that we have the correct logic to finalize
4267 if (Config->get_edit_mode() == Lock && !drag_info.copy) {
4268 drag_info.x_constrained = !drag_info.x_constrained;
4271 if (drag_info.copy) {
4272 if (drag_info.x_constrained) {
4273 op_string = _("fixed time region copy");
4275 op_string = _("region copy");
4278 if (drag_info.x_constrained) {
4279 op_string = _("fixed time region drag");
4281 op_string = _("region drag");
4285 begin_reversible_command (op_string);
4286 changed_position = (drag_info.last_frame_position != (nframes64_t) (clicked_regionview->region()->position()));
4287 tvp = trackview_by_y_position (drag_info.current_pointer_y);
4288 changed_tracks = (tvp.first != &clicked_regionview->get_time_axis_view());
4290 drag_delta = clicked_regionview->region()->position() - drag_info.last_frame_position;
4292 track_canvas->update_now ();
4294 /* make a list of where each region ended up */
4295 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4297 double ix1, ix2, iy1, iy2;
4298 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
4299 (*i)->get_canvas_frame()->i2w (ix1, iy1);
4300 iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
4302 std::pair<TimeAxisView*, int> tv = trackview_by_y_position (iy1);
4303 final[*i] = dynamic_cast<RouteTimeAxisView*> (tv.first);
4306 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
4308 RegionView* rv = (*i);
4309 RouteTimeAxisView* dest_rtv = final[*i];
4313 if (rv->region()->locked()) {
4318 if (changed_position && !drag_info.x_constrained) {
4319 where = rv->region()->position() - drag_delta;
4321 where = rv->region()->position();
4324 boost::shared_ptr<Region> new_region;
4326 if (drag_info.copy) {
4327 /* we already made a copy */
4328 new_region = rv->region();
4330 /* undo the previous hide_dependent_views so that xfades don't
4331 disappear on copying regions
4334 //rv->get_time_axis_view().reveal_dependent_views (*rv);
4336 } else if (changed_tracks && dest_rtv->playlist()) {
4337 new_region = RegionFactory::create (rv->region());
4340 if (changed_tracks || drag_info.copy) {
4342 boost::shared_ptr<Playlist> to_playlist = dest_rtv->playlist();
4348 latest_regionviews.clear ();
4350 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4352 insert_result = modified_playlists.insert (to_playlist);
4353 if (insert_result.second) {
4354 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
4357 to_playlist->add_region (new_region, where);
4361 if (!latest_regionviews.empty()) {
4362 // XXX why just the first one ? we only expect one
4363 // commented out in nick_m's canvas reworking. is that intended?
4364 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
4365 new_selection.push_back (latest_regionviews.front());
4370 motion on the same track. plonk the previously reparented region
4371 back to its original canvas group (its streamview).
4372 No need to do anything for copies as they are fake regions which will be deleted.
4375 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
4376 rv->get_canvas_group()->property_y() = 0;
4378 /* just change the model */
4380 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
4382 insert_result = modified_playlists.insert (playlist);
4383 if (insert_result.second) {
4384 session->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
4386 /* freeze to avoid lots of relayering in the case of a multi-region drag */
4387 frozen_insert_result = frozen_playlists.insert(playlist);
4388 if (frozen_insert_result.second) {
4392 rv->region()->set_position (where, (void*) this);
4395 if (changed_tracks && !drag_info.copy) {
4397 /* get the playlist where this drag started. we can't use rv->region()->playlist()
4398 because we may have copied the region and it has not been attached to a playlist.
4401 assert ((source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view())));
4402 assert ((ds = source_tv->get_diskstream()));
4403 assert ((from_playlist = ds->playlist()));
4405 /* moved to a different audio track, without copying */
4407 /* the region that used to be in the old playlist is not
4408 moved to the new one - we use a copy of it. as a result,
4409 any existing editor for the region should no longer be
4413 rv->hide_region_editor();
4414 rv->fake_set_opaque (false);
4416 /* remove the region from the old playlist */
4418 insert_result = modified_playlists.insert (from_playlist);
4419 if (insert_result.second) {
4420 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
4423 from_playlist->remove_region ((rv->region()));
4425 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
4426 was selected in all of them, then removing it from a playlist will have removed all
4427 trace of it from the selection (i.e. there were N regions selected, we removed 1,
4428 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
4429 corresponding regionview, and the selection is now empty).
4431 this could have invalidated any and all iterators into the region selection.
4433 the heuristic we use here is: if the region selection is empty, break out of the loop
4434 here. if the region selection is not empty, then restart the loop because we know that
4435 we must have removed at least the region(view) we've just been working on as well as any
4436 that we processed on previous iterations.
4438 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
4439 we can just iterate.
4442 if (selection->regions.empty()) {
4445 i = selection->regions.by_layer().begin();
4452 if (drag_info.copy) {
4453 copies.push_back (rv);
4457 if (new_selection.empty()) {
4458 if (drag_info.copy) {
4459 /* the region(view)s that are selected and being dragged around
4460 are copies and do not belong to any track. remove them
4461 from the selection right here.
4463 selection->clear_regions();
4466 /* this will clear any existing selection that would have been
4467 cleared in the other clause above
4469 selection->set (new_selection);
4472 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
4478 for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
4479 session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));
4481 commit_reversible_command ();
4484 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
4491 Editor::create_region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4493 if (drag_info.move_threshold_passed) {
4494 if (drag_info.first_move) {
4495 // TODO: create region-create-drag region view here
4496 drag_info.first_move = false;
4499 // TODO: resize region-create-drag region view here
4504 Editor::create_region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4506 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (drag_info.dest_trackview);
4510 const boost::shared_ptr<MidiDiskstream> diskstream =
4511 boost::dynamic_pointer_cast<MidiDiskstream>(mtv->view()->trackview().track()->diskstream());
4514 warning << "Cannot create non-MIDI region" << endl;
4518 if (drag_info.first_move) {
4519 begin_reversible_command (_("create region"));
4520 XMLNode &before = mtv->playlist()->get_state();
4522 nframes64_t start = drag_info.grab_frame;
4523 snap_to (start, -1);
4524 const Meter& m = session->tempo_map().meter_at(start);
4525 const Tempo& t = session->tempo_map().tempo_at(start);
4526 double length = floor (m.frames_per_bar(t, session->frame_rate()));
4528 boost::shared_ptr<Source> src = session->create_midi_source_for_session(*diskstream.get());
4530 mtv->playlist()->add_region (boost::dynamic_pointer_cast<MidiRegion>
4531 (RegionFactory::create(src, 0, (nframes_t) length,
4532 PBD::basename_nosuffix(src->name()))), start);
4533 XMLNode &after = mtv->playlist()->get_state();
4534 session->add_command(new MementoCommand<Playlist>(*mtv->playlist().get(), &before, &after));
4535 commit_reversible_command();
4538 create_region_drag_motion_callback (item, event);
4539 // TODO: create region-create-drag region here
4544 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
4546 /* Either add to or set the set the region selection, unless
4547 this is an alignment click (control used)
4550 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
4551 TimeAxisView* tv = &rv.get_time_axis_view();
4552 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
4554 if (rtv && rtv->is_track()) {
4555 speed = rtv->get_diskstream()->speed();
4558 nframes64_t where = get_preferred_edit_position();
4562 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
4564 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
4566 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
4568 align_region (rv.region(), End, (nframes64_t) (where * speed));
4572 align_region (rv.region(), Start, (nframes64_t) (where * speed));
4579 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
4585 nframes64_t frame_rate;
4594 if (Profile->get_sae() || Profile->get_small_screen()) {
4595 m = ARDOUR_UI::instance()->primary_clock.mode();
4597 m = ARDOUR_UI::instance()->secondary_clock.mode();
4601 case AudioClock::BBT:
4602 session->bbt_time (frame, bbt);
4603 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
4606 case AudioClock::SMPTE:
4607 session->smpte_time (frame, smpte);
4608 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4611 case AudioClock::MinSec:
4612 /* XXX this is copied from show_verbose_duration_cursor() */
4613 frame_rate = session->frame_rate();
4614 hours = frame / (frame_rate * 3600);
4615 frame = frame % (frame_rate * 3600);
4616 mins = frame / (frame_rate * 60);
4617 frame = frame % (frame_rate * 60);
4618 secs = (float) frame / (float) frame_rate;
4619 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4623 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
4627 if (xpos >= 0 && ypos >=0) {
4628 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4631 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);
4633 show_verbose_canvas_cursor ();
4637 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
4644 nframes64_t distance, frame_rate;
4646 Meter meter_at_start(session->tempo_map().meter_at(start));
4654 if (Profile->get_sae() || Profile->get_small_screen()) {
4655 m = ARDOUR_UI::instance()->primary_clock.mode ();
4657 m = ARDOUR_UI::instance()->secondary_clock.mode ();
4661 case AudioClock::BBT:
4662 session->bbt_time (start, sbbt);
4663 session->bbt_time (end, ebbt);
4666 /* XXX this computation won't work well if the
4667 user makes a selection that spans any meter changes.
4670 ebbt.bars -= sbbt.bars;
4671 if (ebbt.beats >= sbbt.beats) {
4672 ebbt.beats -= sbbt.beats;
4675 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
4677 if (ebbt.ticks >= sbbt.ticks) {
4678 ebbt.ticks -= sbbt.ticks;
4681 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
4684 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
4687 case AudioClock::SMPTE:
4688 session->smpte_duration (end - start, smpte);
4689 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4692 case AudioClock::MinSec:
4693 /* XXX this stuff should be elsewhere.. */
4694 distance = end - start;
4695 frame_rate = session->frame_rate();
4696 hours = distance / (frame_rate * 3600);
4697 distance = distance % (frame_rate * 3600);
4698 mins = distance / (frame_rate * 60);
4699 distance = distance % (frame_rate * 60);
4700 secs = (float) distance / (float) frame_rate;
4701 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4705 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
4709 if (xpos >= 0 && ypos >=0) {
4710 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4713 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4716 show_verbose_canvas_cursor ();
4720 Editor::collect_new_region_view (RegionView* rv)
4722 latest_regionviews.push_back (rv);
4726 Editor::collect_and_select_new_region_view (RegionView* rv)
4729 latest_regionviews.push_back (rv);
4733 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
4735 if (clicked_regionview == 0) {
4739 /* lets try to create new Region for the selection */
4741 vector<boost::shared_ptr<Region> > new_regions;
4742 create_region_from_selection (new_regions);
4744 if (new_regions.empty()) {
4748 /* XXX fix me one day to use all new regions */
4750 boost::shared_ptr<Region> region (new_regions.front());
4752 /* add it to the current stream/playlist.
4754 tricky: the streamview for the track will add a new regionview. we will
4755 catch the signal it sends when it creates the regionview to
4756 set the regionview we want to then drag.
4759 latest_regionviews.clear();
4760 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4762 /* A selection grab currently creates two undo/redo operations, one for
4763 creating the new region and another for moving it.
4766 begin_reversible_command (_("selection grab"));
4768 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
4770 XMLNode *before = &(playlist->get_state());
4771 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
4772 XMLNode *after = &(playlist->get_state());
4773 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4775 commit_reversible_command ();
4779 if (latest_regionviews.empty()) {
4780 /* something went wrong */
4784 /* we need to deselect all other regionviews, and select this one
4785 i'm ignoring undo stuff, because the region creation will take care of it
4787 selection->set (latest_regionviews);
4789 drag_info.item = latest_regionviews.front()->get_canvas_group();
4790 drag_info.data = latest_regionviews.front();
4791 drag_info.motion_callback = &Editor::region_drag_motion_callback;
4792 drag_info.finished_callback = &Editor::region_drag_finished_callback;
4796 drag_info.source_trackview = clicked_routeview;
4797 drag_info.dest_trackview = drag_info.source_trackview;
4798 drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4799 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4801 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4805 Editor::cancel_selection ()
4807 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4808 (*i)->hide_selection ();
4810 selection->clear ();
4811 clicked_selection = 0;
4815 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4817 nframes64_t start = 0;
4818 nframes64_t end = 0;
4824 drag_info.item = item;
4825 drag_info.motion_callback = &Editor::drag_selection;
4826 drag_info.finished_callback = &Editor::end_selection_op;
4831 case CreateSelection:
4832 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4833 drag_info.copy = true;
4835 drag_info.copy = false;
4837 start_grab (event, selector_cursor);
4840 case SelectionStartTrim:
4841 if (clicked_axisview) {
4842 clicked_axisview->order_selection_trims (item, true);
4844 start_grab (event, trimmer_cursor);
4845 start = selection->time[clicked_selection].start;
4846 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4849 case SelectionEndTrim:
4850 if (clicked_axisview) {
4851 clicked_axisview->order_selection_trims (item, false);
4853 start_grab (event, trimmer_cursor);
4854 end = selection->time[clicked_selection].end;
4855 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
4859 start = selection->time[clicked_selection].start;
4861 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4865 if (selection_op == SelectionMove) {
4866 show_verbose_time_cursor(start, 10);
4868 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4873 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4875 nframes64_t start = 0;
4876 nframes64_t end = 0;
4878 nframes64_t pending_position;
4880 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4881 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4883 pending_position = 0;
4886 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4887 snap_to (pending_position);
4890 /* only alter selection if the current frame is
4891 different from the last frame position (adjusted)
4894 if (pending_position == drag_info.last_pointer_frame) return;
4896 switch (selection_op) {
4897 case CreateSelection:
4899 if (drag_info.first_move) {
4900 snap_to (drag_info.grab_frame);
4903 if (pending_position < drag_info.grab_frame) {
4904 start = pending_position;
4905 end = drag_info.grab_frame;
4907 end = pending_position;
4908 start = drag_info.grab_frame;
4911 /* first drag: Either add to the selection
4912 or create a new selection->
4915 if (drag_info.first_move) {
4917 begin_reversible_command (_("range selection"));
4919 if (drag_info.copy) {
4920 /* adding to the selection */
4921 clicked_selection = selection->add (start, end);
4922 drag_info.copy = false;
4924 /* new selection-> */
4925 clicked_selection = selection->set (clicked_axisview, start, end);
4930 case SelectionStartTrim:
4932 if (drag_info.first_move) {
4933 begin_reversible_command (_("trim selection start"));
4936 start = selection->time[clicked_selection].start;
4937 end = selection->time[clicked_selection].end;
4939 if (pending_position > end) {
4942 start = pending_position;
4946 case SelectionEndTrim:
4948 if (drag_info.first_move) {
4949 begin_reversible_command (_("trim selection end"));
4952 start = selection->time[clicked_selection].start;
4953 end = selection->time[clicked_selection].end;
4955 if (pending_position < start) {
4958 end = pending_position;
4965 if (drag_info.first_move) {
4966 begin_reversible_command (_("move selection"));
4969 start = selection->time[clicked_selection].start;
4970 end = selection->time[clicked_selection].end;
4972 length = end - start;
4974 start = pending_position;
4977 end = start + length;
4982 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4983 start_canvas_autoscroll (1, 0);
4987 selection->replace (clicked_selection, start, end);
4990 drag_info.last_pointer_frame = pending_position;
4991 drag_info.first_move = false;
4993 if (selection_op == SelectionMove) {
4994 show_verbose_time_cursor(start, 10);
4996 show_verbose_time_cursor(pending_position, 10);
5001 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
5003 if (!drag_info.first_move) {
5004 drag_selection (item, event);
5005 /* XXX this is not object-oriented programming at all. ick */
5006 if (selection->time.consolidate()) {
5007 selection->TimeChanged ();
5009 commit_reversible_command ();
5011 /* just a click, no pointer movement.*/
5013 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
5015 selection->clear_time();
5020 /* XXX what happens if its a music selection? */
5021 session->set_audio_range (selection->time);
5022 stop_canvas_autoscroll ();
5026 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
5029 TimeAxisView* tvp = clicked_axisview;
5030 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
5032 if (tv && tv->is_track()) {
5033 speed = tv->get_diskstream()->speed();
5036 nframes64_t region_start = (nframes64_t) (clicked_regionview->region()->position() / speed);
5037 nframes64_t region_end = (nframes64_t) (clicked_regionview->region()->last_frame() / speed);
5038 nframes64_t region_length = (nframes64_t) (clicked_regionview->region()->length() / speed);
5040 //drag_info.item = clicked_regionview->get_name_highlight();
5041 drag_info.item = item;
5042 drag_info.motion_callback = &Editor::trim_motion_callback;
5043 drag_info.finished_callback = &Editor::trim_finished_callback;
5045 start_grab (event, trimmer_cursor);
5047 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
5048 trim_op = ContentsTrim;
5050 /* These will get overridden for a point trim.*/
5051 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
5052 /* closer to start */
5053 trim_op = StartTrim;
5054 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
5062 show_verbose_time_cursor(region_start, 10);
5065 show_verbose_time_cursor(region_end, 10);
5068 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5074 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
5076 RegionView* rv = clicked_regionview;
5077 nframes64_t frame_delta = 0;
5078 bool left_direction;
5079 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
5081 /* snap modifier works differently here..
5082 its' current state has to be passed to the
5083 various trim functions in order to work properly
5087 TimeAxisView* tvp = clicked_axisview;
5088 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
5089 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
5091 if (tv && tv->is_track()) {
5092 speed = tv->get_diskstream()->speed();
5095 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
5096 left_direction = true;
5098 left_direction = false;
5102 snap_to (drag_info.current_pointer_frame);
5105 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5109 if (drag_info.first_move) {
5115 trim_type = "Region start trim";
5118 trim_type = "Region end trim";
5121 trim_type = "Region content trim";
5125 begin_reversible_command (trim_type);
5127 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
5128 (*i)->fake_set_opaque(false);
5129 (*i)->region()->freeze ();
5131 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5133 arv->temporarily_hide_envelope ();
5135 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5136 insert_result = motion_frozen_playlists.insert (pl);
5137 if (insert_result.second) {
5138 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
5144 if (left_direction) {
5145 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
5147 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
5152 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
5155 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
5156 single_start_trim (**i, frame_delta, left_direction, obey_snap);
5162 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes64_t) (rv->region()->last_frame()/speed))) {
5165 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
5166 single_end_trim (**i, frame_delta, left_direction, obey_snap);
5173 bool swap_direction = false;
5175 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
5176 swap_direction = true;
5179 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5180 i != selection->regions.by_layer().end(); ++i)
5182 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
5190 show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
5193 show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
5196 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5200 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5201 drag_info.first_move = false;
5205 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
5207 boost::shared_ptr<Region> region (rv.region());
5209 if (region->locked()) {
5213 nframes64_t new_bound;
5216 TimeAxisView* tvp = clicked_axisview;
5217 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
5219 if (tv && tv->is_track()) {
5220 speed = tv->get_diskstream()->speed();
5223 if (left_direction) {
5224 if (swap_direction) {
5225 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
5227 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
5230 if (swap_direction) {
5231 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
5233 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
5238 snap_to (new_bound);
5240 region->trim_start ((nframes64_t) (new_bound * speed), this);
5241 rv.region_changed (StartChanged);
5245 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
5247 boost::shared_ptr<Region> region (rv.region());
5249 if (region->locked()) {
5253 nframes64_t new_bound;
5256 TimeAxisView* tvp = clicked_axisview;
5257 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
5259 if (tv && tv->is_track()) {
5260 speed = tv->get_diskstream()->speed();
5263 if (left_direction) {
5264 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
5266 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
5270 snap_to (new_bound, (left_direction ? 0 : 1));
5273 region->trim_front ((nframes64_t) (new_bound * speed), this);
5275 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
5279 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
5281 boost::shared_ptr<Region> region (rv.region());
5283 if (region->locked()) {
5287 nframes64_t new_bound;
5290 TimeAxisView* tvp = clicked_axisview;
5291 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
5293 if (tv && tv->is_track()) {
5294 speed = tv->get_diskstream()->speed();
5297 if (left_direction) {
5298 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
5300 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
5304 snap_to (new_bound);
5306 region->trim_end ((nframes64_t) (new_bound * speed), this);
5307 rv.region_changed (LengthChanged);
5311 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
5313 if (!drag_info.first_move) {
5314 trim_motion_callback (item, event);
5316 if (!selection->selected (clicked_regionview)) {
5317 thaw_region_after_trim (*clicked_regionview);
5320 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5321 i != selection->regions.by_layer().end(); ++i)
5323 thaw_region_after_trim (**i);
5324 (*i)->fake_set_opaque (true);
5328 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
5330 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
5333 motion_frozen_playlists.clear ();
5335 commit_reversible_command();
5337 /* no mouse movement */
5343 Editor::point_trim (GdkEvent* event)
5345 RegionView* rv = clicked_regionview;
5346 nframes64_t new_bound = drag_info.current_pointer_frame;
5348 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5349 snap_to (new_bound);
5352 /* Choose action dependant on which button was pressed */
5353 switch (event->button.button) {
5355 trim_op = StartTrim;
5356 begin_reversible_command (_("Start point trim"));
5358 if (selection->selected (rv)) {
5360 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5361 i != selection->regions.by_layer().end(); ++i)
5363 if (!(*i)->region()->locked()) {
5364 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5365 XMLNode &before = pl->get_state();
5366 (*i)->region()->trim_front (new_bound, this);
5367 XMLNode &after = pl->get_state();
5368 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5374 if (!rv->region()->locked()) {
5375 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5376 XMLNode &before = pl->get_state();
5377 rv->region()->trim_front (new_bound, this);
5378 XMLNode &after = pl->get_state();
5379 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5383 commit_reversible_command();
5388 begin_reversible_command (_("End point trim"));
5390 if (selection->selected (rv)) {
5392 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
5394 if (!(*i)->region()->locked()) {
5395 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5396 XMLNode &before = pl->get_state();
5397 (*i)->region()->trim_end (new_bound, this);
5398 XMLNode &after = pl->get_state();
5399 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5405 if (!rv->region()->locked()) {
5406 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5407 XMLNode &before = pl->get_state();
5408 rv->region()->trim_end (new_bound, this);
5409 XMLNode &after = pl->get_state();
5410 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
5414 commit_reversible_command();
5423 Editor::thaw_region_after_trim (RegionView& rv)
5425 boost::shared_ptr<Region> region (rv.region());
5427 if (region->locked()) {
5431 region->thaw (_("trimmed region"));
5432 XMLNode &after = region->playlist()->get_state();
5433 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
5435 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
5437 arv->unhide_envelope ();
5441 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
5446 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
5447 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
5451 Location* location = find_location_from_marker (marker, is_start);
5452 location->set_hidden (true, this);
5457 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
5463 drag_info.item = item;
5464 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
5465 drag_info.finished_callback = &Editor::end_range_markerbar_op;
5467 range_marker_op = op;
5469 if (!temp_location) {
5470 temp_location = new Location;
5474 case CreateRangeMarker:
5475 case CreateTransportMarker:
5476 case CreateCDMarker:
5478 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
5479 drag_info.copy = true;
5481 drag_info.copy = false;
5483 start_grab (event, selector_cursor);
5487 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5492 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5494 nframes64_t start = 0;
5495 nframes64_t end = 0;
5496 ArdourCanvas::SimpleRect *crect;
5498 switch (range_marker_op) {
5499 case CreateRangeMarker:
5500 crect = range_bar_drag_rect;
5502 case CreateTransportMarker:
5503 crect = transport_bar_drag_rect;
5505 case CreateCDMarker:
5506 crect = cd_marker_bar_drag_rect;
5509 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
5514 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5515 snap_to (drag_info.current_pointer_frame);
5518 /* only alter selection if the current frame is
5519 different from the last frame position.
5522 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5524 switch (range_marker_op) {
5525 case CreateRangeMarker:
5526 case CreateTransportMarker:
5527 case CreateCDMarker:
5528 if (drag_info.first_move) {
5529 snap_to (drag_info.grab_frame);
5532 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5533 start = drag_info.current_pointer_frame;
5534 end = drag_info.grab_frame;
5536 end = drag_info.current_pointer_frame;
5537 start = drag_info.grab_frame;
5540 /* first drag: Either add to the selection
5541 or create a new selection.
5544 if (drag_info.first_move) {
5546 temp_location->set (start, end);
5550 update_marker_drag_item (temp_location);
5551 range_marker_drag_rect->show();
5552 //range_marker_drag_rect->raise_to_top();
5558 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
5559 start_canvas_autoscroll (1, 0);
5563 temp_location->set (start, end);
5565 double x1 = frame_to_pixel (start);
5566 double x2 = frame_to_pixel (end);
5567 crect->property_x1() = x1;
5568 crect->property_x2() = x2;
5570 update_marker_drag_item (temp_location);
5573 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5574 drag_info.first_move = false;
5576 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5581 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5583 Location * newloc = 0;
5587 if (!drag_info.first_move) {
5588 drag_range_markerbar_op (item, event);
5590 switch (range_marker_op) {
5591 case CreateRangeMarker:
5592 case CreateCDMarker:
5594 begin_reversible_command (_("new range marker"));
5595 XMLNode &before = session->locations()->get_state();
5596 session->locations()->next_available_name(rangename,"unnamed");
5597 if (range_marker_op == CreateCDMarker) {
5598 flags = Location::IsRangeMarker|Location::IsCDMarker;
5599 cd_marker_bar_drag_rect->hide();
5602 flags = Location::IsRangeMarker;
5603 range_bar_drag_rect->hide();
5605 newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
5606 session->locations()->add (newloc, true);
5607 XMLNode &after = session->locations()->get_state();
5608 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
5609 commit_reversible_command ();
5611 range_marker_drag_rect->hide();
5615 case CreateTransportMarker:
5616 // popup menu to pick loop or punch
5617 new_transport_marker_context_menu (&event->button, item);
5622 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5624 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
5629 start = session->locations()->first_mark_before (drag_info.grab_frame);
5630 end = session->locations()->first_mark_after (drag_info.grab_frame);
5632 if (end == max_frames) {
5633 end = session->current_end_frame ();
5637 start = session->current_start_frame ();
5640 switch (mouse_mode) {
5642 /* find the two markers on either side and then make the selection from it */
5643 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
5647 /* find the two markers on either side of the click and make the range out of it */
5648 selection->set (0, start, end);
5657 stop_canvas_autoscroll ();
5663 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5665 drag_info.item = item;
5666 drag_info.motion_callback = &Editor::drag_mouse_zoom;
5667 drag_info.finished_callback = &Editor::end_mouse_zoom;
5669 start_grab (event, zoom_cursor);
5671 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5675 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5680 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5681 snap_to (drag_info.current_pointer_frame);
5683 if (drag_info.first_move) {
5684 snap_to (drag_info.grab_frame);
5688 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5690 /* base start and end on initial click position */
5691 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5692 start = drag_info.current_pointer_frame;
5693 end = drag_info.grab_frame;
5695 end = drag_info.current_pointer_frame;
5696 start = drag_info.grab_frame;
5701 if (drag_info.first_move) {
5703 zoom_rect->raise_to_top();
5706 reposition_zoom_rect(start, end);
5708 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5709 drag_info.first_move = false;
5711 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5716 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5718 if (!drag_info.first_move) {
5719 drag_mouse_zoom (item, event);
5721 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5722 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
5724 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
5727 temporal_zoom_to_frame (false, drag_info.grab_frame);
5729 temporal_zoom_step (false);
5730 center_screen (drag_info.grab_frame);
5738 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
5740 double x1 = frame_to_pixel (start);
5741 double x2 = frame_to_pixel (end);
5742 double y2 = full_canvas_height - 1.0;
5744 zoom_rect->property_x1() = x1;
5745 zoom_rect->property_y1() = 1.0;
5746 zoom_rect->property_x2() = x2;
5747 zoom_rect->property_y2() = y2;
5751 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5753 drag_info.item = item;
5754 drag_info.motion_callback = &Editor::drag_rubberband_select;
5755 drag_info.finished_callback = &Editor::end_rubberband_select;
5757 start_grab (event, cross_hair_cursor);
5759 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5763 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5770 /* use a bigger drag threshold than the default */
5772 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
5776 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) {
5777 if (drag_info.first_move) {
5778 snap_to (drag_info.grab_frame);
5780 snap_to (drag_info.current_pointer_frame);
5783 /* base start and end on initial click position */
5785 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5786 start = drag_info.current_pointer_frame;
5787 end = drag_info.grab_frame;
5789 end = drag_info.current_pointer_frame;
5790 start = drag_info.grab_frame;
5793 if (drag_info.current_pointer_y < drag_info.grab_y) {
5794 y1 = drag_info.current_pointer_y;
5795 y2 = drag_info.grab_y;
5797 y2 = drag_info.current_pointer_y;
5798 y1 = drag_info.grab_y;
5802 if (start != end || y1 != y2) {
5804 double x1 = frame_to_pixel (start);
5805 double x2 = frame_to_pixel (end);
5807 rubberband_rect->property_x1() = x1;
5808 rubberband_rect->property_y1() = y1;
5809 rubberband_rect->property_x2() = x2;
5810 rubberband_rect->property_y2() = y2;
5812 rubberband_rect->show();
5813 rubberband_rect->raise_to_top();
5815 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5816 drag_info.first_move = false;
5818 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5823 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5825 if (!drag_info.first_move) {
5827 drag_rubberband_select (item, event);
5830 if (drag_info.current_pointer_y < drag_info.grab_y) {
5831 y1 = drag_info.current_pointer_y;
5832 y2 = drag_info.grab_y;
5834 y2 = drag_info.current_pointer_y;
5835 y1 = drag_info.grab_y;
5839 Selection::Operation op = Keyboard::selection_type (event->button.state);
5842 begin_reversible_command (_("rubberband selection"));
5844 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5845 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5847 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5851 commit_reversible_command ();
5855 if (!getenv("ARDOUR_SAE")) {
5856 selection->clear_tracks();
5858 selection->clear_regions();
5859 selection->clear_points ();
5860 selection->clear_lines ();
5863 rubberband_rect->hide();
5868 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5870 using namespace Gtkmm2ext;
5872 ArdourPrompter prompter (false);
5874 prompter.set_prompt (_("Name for region:"));
5875 prompter.set_initial_text (clicked_regionview->region()->name());
5876 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5877 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5878 prompter.show_all ();
5879 switch (prompter.run ()) {
5880 case Gtk::RESPONSE_ACCEPT:
5882 prompter.get_result(str);
5884 clicked_regionview->region()->set_name (str);
5892 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5894 drag_info.item = item;
5895 drag_info.motion_callback = &Editor::time_fx_motion;
5896 drag_info.finished_callback = &Editor::end_time_fx;
5900 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5904 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5906 RegionView* rv = clicked_regionview;
5908 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5909 snap_to (drag_info.current_pointer_frame);
5912 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5916 if (drag_info.current_pointer_frame > rv->region()->position()) {
5917 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5920 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5921 drag_info.first_move = false;
5923 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5927 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5929 clicked_regionview->get_time_axis_view().hide_timestretch ();
5931 if (drag_info.first_move) {
5935 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5936 /* backwards drag of the left edge - not usable */
5940 nframes64_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5942 float percentage = (double) newlen / (double) clicked_regionview->region()->length();
5944 #ifndef USE_RUBBERBAND
5945 // Soundtouch uses percentage / 100 instead of normal (/ 1)
5946 if (clicked_regionview->region()->data_type() == DataType::AUDIO) {
5947 percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5951 begin_reversible_command (_("timestretch"));
5953 // XXX how do timeFX on multiple regions ?
5956 rs.add (clicked_regionview);
5958 if (time_stretch (rs, percentage) == 0) {
5959 session->commit_reversible_command ();
5964 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
5966 /* no brushing without a useful snap setting */
5968 switch (snap_mode) {
5970 return; /* can't work because it allows region to be placed anywhere */
5975 switch (snap_type) {
5983 /* don't brush a copy over the original */
5985 if (pos == rv->region()->position()) {
5989 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
5991 if (rtv == 0 || !rtv->is_track()) {
5995 boost::shared_ptr<Playlist> playlist = rtv->playlist();
5996 double speed = rtv->get_diskstream()->speed();
5998 XMLNode &before = playlist->get_state();
5999 playlist->add_region (RegionFactory::create (rv->region()), (nframes64_t) (pos * speed));
6000 XMLNode &after = playlist->get_state();
6001 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
6003 // playlist is frozen, so we have to update manually
6005 playlist->Modified(); /* EMIT SIGNAL */
6009 Editor::track_height_step_timeout ()
6011 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
6012 current_stepping_trackview = 0;