3 Copyright (C) 2000-2001 Paul Davis
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <pbd/error.h>
30 #include <gtkmm2ext/utils.h>
31 #include <gtkmm2ext/tearoff.h>
32 #include <pbd/memento_command.h>
33 #include <pbd/basename.h>
35 #include "ardour_ui.h"
37 #include "time_axis_view.h"
38 #include "audio_time_axis.h"
39 #include "audio_region_view.h"
40 #include "midi_region_view.h"
42 #include "streamview.h"
43 #include "region_gain_line.h"
44 #include "automation_time_axis.h"
45 #include "control_point.h"
48 #include "selection.h"
51 #include "rgb_macros.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);
1080 /* context menu events get handled here */
1082 if (Keyboard::is_context_menu_event (&event->button)) {
1084 if (drag_info.item == 0) {
1086 /* no matter which button pops up the context menu, tell the menu
1087 widget to use button 1 to drive menu selection.
1090 switch (item_type) {
1092 case FadeInHandleItem:
1094 case FadeOutHandleItem:
1095 popup_fade_context_menu (1, event->button.time, item, item_type);
1099 popup_track_context_menu (1, event->button.time, item_type, false, where);
1103 case RegionViewNameHighlight:
1104 case RegionViewName:
1105 popup_track_context_menu (1, event->button.time, item_type, false, where);
1109 popup_track_context_menu (1, event->button.time, item_type, true, where);
1112 case AutomationTrackItem:
1113 popup_track_context_menu (1, event->button.time, item_type, false, where);
1117 case RangeMarkerBarItem:
1118 case TransportMarkerBarItem:
1119 case CdMarkerBarItem:
1122 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
1126 marker_context_menu (&event->button, item);
1129 case TempoMarkerItem:
1130 tm_marker_context_menu (&event->button, item);
1133 case MeterMarkerItem:
1134 tm_marker_context_menu (&event->button, item);
1137 case CrossfadeViewItem:
1138 popup_track_context_menu (1, event->button.time, item_type, false, where);
1142 case ImageFrameItem:
1143 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1145 case ImageFrameTimeAxisItem:
1146 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1148 case MarkerViewItem:
1149 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1151 case MarkerTimeAxisItem:
1152 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1164 /* delete events get handled here */
1166 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
1168 switch (item_type) {
1169 case TempoMarkerItem:
1170 remove_tempo_marker (item);
1173 case MeterMarkerItem:
1174 remove_meter_marker (item);
1178 remove_marker (*item, event);
1182 if (mouse_mode == MouseObject) {
1183 remove_clicked_region ();
1187 case ControlPointItem:
1188 if (mouse_mode == MouseGain) {
1189 remove_gain_control_point (item, event);
1191 remove_control_point (item, event);
1201 switch (event->button.button) {
1204 switch (item_type) {
1205 /* see comments in button_press_handler */
1206 case PlayheadCursorItem:
1209 case AutomationLineItem:
1210 case StartSelectionTrimItem:
1211 case EndSelectionTrimItem:
1215 if (!_dragging_playhead) {
1216 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1217 snap_to (where, 0, true);
1219 mouse_add_new_marker (where);
1223 case CdMarkerBarItem:
1224 if (!_dragging_playhead) {
1225 // if we get here then a dragged range wasn't done
1226 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1227 snap_to (where, 0, true);
1229 mouse_add_new_marker (where, true);
1234 if (!_dragging_playhead) {
1235 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1238 mouse_add_new_tempo_event (where);
1243 if (!_dragging_playhead) {
1244 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1253 switch (mouse_mode) {
1255 switch (item_type) {
1256 case AutomationTrackItem:
1257 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_routeview);
1259 atv->add_automation_event (item, event, where, event->button.y);
1271 // Gain only makes sense for audio regions
1273 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1277 switch (item_type) {
1279 /* check that we didn't drag before releasing, since
1280 its really annoying to create new control
1281 points when doing this.
1283 if (drag_info.first_move) {
1284 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1289 case AutomationTrackItem:
1290 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1291 add_automation_event (item, event, where, event->button.y);
1301 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1302 if (scrubbing_direction == 0) {
1303 /* no drag, just a click */
1304 switch (item_type) {
1306 play_selected_region ();
1312 /* make sure we stop */
1313 session->request_transport_speed (0.0);
1327 switch (mouse_mode) {
1330 switch (item_type) {
1332 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1334 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1337 // Button2 click is unused
1350 // x_style_paste (where, 1.0);
1370 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1376 if (last_item_entered != item) {
1377 last_item_entered = item;
1378 last_item_entered_n = 0;
1381 switch (item_type) {
1382 case ControlPointItem:
1383 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1384 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1385 cp->set_visible (true);
1389 at_y = cp->get_y ();
1390 cp->item()->i2w (at_x, at_y);
1394 fraction = 1.0 - (cp->get_y() / cp->line().height());
1396 if (is_drawable() && !_scrubbing) {
1397 track_canvas->get_window()->set_cursor (*fader_cursor);
1400 last_item_entered_n++;
1401 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1402 if (last_item_entered_n < 10) {
1403 show_verbose_canvas_cursor ();
1409 if (mouse_mode == MouseGain) {
1410 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1412 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1413 if (is_drawable()) {
1414 track_canvas->get_window()->set_cursor (*fader_cursor);
1419 case AutomationLineItem:
1420 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1422 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1424 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1426 if (is_drawable()) {
1427 track_canvas->get_window()->set_cursor (*fader_cursor);
1432 case RegionViewNameHighlight:
1433 if (is_drawable() && mouse_mode == MouseObject) {
1434 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1438 case StartSelectionTrimItem:
1439 case EndSelectionTrimItem:
1442 case ImageFrameHandleStartItem:
1443 case ImageFrameHandleEndItem:
1444 case MarkerViewHandleStartItem:
1445 case MarkerViewHandleEndItem:
1448 if (is_drawable()) {
1449 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1453 case PlayheadCursorItem:
1454 if (is_drawable()) {
1455 switch (_edit_point) {
1457 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1460 track_canvas->get_window()->set_cursor (*grabber_cursor);
1466 case RegionViewName:
1468 /* when the name is not an active item, the entire name highlight is for trimming */
1470 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1471 if (mouse_mode == MouseObject && is_drawable()) {
1472 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1478 case AutomationTrackItem:
1479 if (is_drawable()) {
1480 Gdk::Cursor *cursor;
1481 switch (mouse_mode) {
1483 cursor = selector_cursor;
1486 cursor = zoom_cursor;
1489 cursor = cross_hair_cursor;
1493 track_canvas->get_window()->set_cursor (*cursor);
1495 AutomationTimeAxisView* atv;
1496 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1497 clear_entered_track = false;
1498 set_entered_track (atv);
1504 case RangeMarkerBarItem:
1505 case TransportMarkerBarItem:
1506 case CdMarkerBarItem:
1509 if (is_drawable()) {
1510 track_canvas->get_window()->set_cursor (*timebar_cursor);
1515 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1518 entered_marker = marker;
1519 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1521 case MeterMarkerItem:
1522 case TempoMarkerItem:
1523 if (is_drawable()) {
1524 track_canvas->get_window()->set_cursor (*timebar_cursor);
1527 case FadeInHandleItem:
1528 case FadeOutHandleItem:
1529 if (mouse_mode == MouseObject) {
1530 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1532 rect->property_fill_color_rgba() = 0;
1533 rect->property_outline_pixels() = 1;
1542 /* second pass to handle entered track status in a comprehensible way.
1545 switch (item_type) {
1547 case AutomationLineItem:
1548 case ControlPointItem:
1549 /* these do not affect the current entered track state */
1550 clear_entered_track = false;
1553 case AutomationTrackItem:
1554 /* handled above already */
1558 set_entered_track (0);
1566 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1575 switch (item_type) {
1576 case ControlPointItem:
1577 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1578 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1579 if (cp->line().npoints() > 1 && !cp->selected()) {
1580 cp->set_visible (false);
1584 if (is_drawable()) {
1585 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1588 hide_verbose_canvas_cursor ();
1591 case RegionViewNameHighlight:
1592 case StartSelectionTrimItem:
1593 case EndSelectionTrimItem:
1594 case PlayheadCursorItem:
1597 case ImageFrameHandleStartItem:
1598 case ImageFrameHandleEndItem:
1599 case MarkerViewHandleStartItem:
1600 case MarkerViewHandleEndItem:
1603 if (is_drawable()) {
1604 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1609 case AutomationLineItem:
1610 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1612 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1614 line->property_fill_color_rgba() = al->get_line_color();
1616 if (is_drawable()) {
1617 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1621 case RegionViewName:
1622 /* see enter_handler() for notes */
1623 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1624 if (is_drawable() && mouse_mode == MouseObject) {
1625 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1630 case RangeMarkerBarItem:
1631 case TransportMarkerBarItem:
1632 case CdMarkerBarItem:
1636 if (is_drawable()) {
1637 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1642 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1646 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1647 location_flags_changed (loc, this);
1650 case MeterMarkerItem:
1651 case TempoMarkerItem:
1653 if (is_drawable()) {
1654 track_canvas->get_window()->set_cursor (*timebar_cursor);
1659 case FadeInHandleItem:
1660 case FadeOutHandleItem:
1661 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1663 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1665 rect->property_fill_color_rgba() = rv->get_fill_color();
1666 rect->property_outline_pixels() = 0;
1671 case AutomationTrackItem:
1672 if (is_drawable()) {
1673 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1674 clear_entered_track = true;
1675 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1687 Editor::left_automation_track ()
1689 if (clear_entered_track) {
1690 set_entered_track (0);
1691 clear_entered_track = false;
1701 if (scrubbing_direction == 0) {
1703 session->request_locate (drag_info.current_pointer_frame, false);
1704 session->request_transport_speed (0.1);
1705 scrubbing_direction = 1;
1709 if (last_scrub_x > drag_info.current_pointer_x) {
1711 /* pointer moved to the left */
1713 if (scrubbing_direction > 0) {
1715 /* we reversed direction to go backwards */
1718 scrub_reverse_distance += (int) (last_scrub_x - drag_info.current_pointer_x);
1722 /* still moving to the left (backwards) */
1724 scrub_reversals = 0;
1725 scrub_reverse_distance = 0;
1727 delta = 0.01 * (last_scrub_x - drag_info.current_pointer_x);
1728 session->request_transport_speed (session->transport_speed() - delta);
1732 /* pointer moved to the right */
1734 if (scrubbing_direction < 0) {
1735 /* we reversed direction to go forward */
1738 scrub_reverse_distance += (int) (drag_info.current_pointer_x - last_scrub_x);
1741 /* still moving to the right */
1743 scrub_reversals = 0;
1744 scrub_reverse_distance = 0;
1746 delta = 0.01 * (drag_info.current_pointer_x - last_scrub_x);
1747 session->request_transport_speed (session->transport_speed() + delta);
1751 /* if there have been more than 2 opposite motion moves detected, or one that moves
1752 back more than 10 pixels, reverse direction
1755 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1757 if (scrubbing_direction > 0) {
1758 /* was forwards, go backwards */
1759 session->request_transport_speed (-0.1);
1760 scrubbing_direction = -1;
1762 /* was backwards, go forwards */
1763 session->request_transport_speed (0.1);
1764 scrubbing_direction = 1;
1767 scrub_reverse_distance = 0;
1768 scrub_reversals = 0;
1772 last_scrub_x = drag_info.current_pointer_x;
1776 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1778 if (event->motion.is_hint) {
1781 /* We call this so that MOTION_NOTIFY events continue to be
1782 delivered to the canvas. We need to do this because we set
1783 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1784 the density of the events, at the expense of a round-trip
1785 to the server. Given that this will mostly occur on cases
1786 where DISPLAY = :0.0, and given the cost of what the motion
1787 event might do, its a good tradeoff.
1790 track_canvas->get_pointer (x, y);
1793 if (current_stepping_trackview) {
1794 /* don't keep the persistent stepped trackview if the mouse moves */
1795 current_stepping_trackview = 0;
1796 step_timeout.disconnect ();
1799 if (session && session->actively_recording()) {
1800 /* Sorry. no dragging stuff around while we record */
1804 drag_info.item_type = item_type;
1805 drag_info.last_pointer_x = drag_info.current_pointer_x;
1806 drag_info.last_pointer_y = drag_info.current_pointer_y;
1807 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1808 &drag_info.current_pointer_y);
1811 switch (mouse_mode) {
1823 if (!from_autoscroll && drag_info.item) {
1824 /* item != 0 is the best test i can think of for dragging.
1826 if (!drag_info.move_threshold_passed) {
1828 bool x_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1829 bool y_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1831 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1833 // and change the initial grab loc/frame if this drag info wants us to
1835 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1836 drag_info.grab_frame = drag_info.current_pointer_frame;
1837 drag_info.grab_x = drag_info.current_pointer_x;
1838 drag_info.grab_y = drag_info.current_pointer_y;
1839 drag_info.last_pointer_frame = drag_info.grab_frame;
1840 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1845 switch (item_type) {
1846 case PlayheadCursorItem:
1848 case ControlPointItem:
1852 case RangeMarkerBarItem:
1853 case TransportMarkerBarItem:
1854 case CdMarkerBarItem:
1855 case TempoMarkerItem:
1856 case MeterMarkerItem:
1857 case RegionViewNameHighlight:
1858 case StartSelectionTrimItem:
1859 case EndSelectionTrimItem:
1862 case AutomationLineItem:
1863 case FadeInHandleItem:
1864 case FadeOutHandleItem:
1867 case ImageFrameHandleStartItem:
1868 case ImageFrameHandleEndItem:
1869 case MarkerViewHandleStartItem:
1870 case MarkerViewHandleEndItem:
1873 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1874 (event->motion.state & Gdk::BUTTON2_MASK))) {
1875 if (!from_autoscroll) {
1876 maybe_autoscroll_horizontally (&event->motion);
1878 if (drag_info.motion_callback) {
1879 (this->*(drag_info.motion_callback)) (item, event);
1889 switch (mouse_mode) {
1891 if (item_type == RegionItem) {
1892 if (drag_info.item && drag_info.motion_callback) {
1893 (this->*(drag_info.motion_callback)) (item, event);
1904 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1905 (event->motion.state & GDK_BUTTON2_MASK))) {
1906 if (!from_autoscroll) {
1907 maybe_autoscroll (&event->motion);
1909 if (drag_info.motion_callback) {
1910 (this->*(drag_info.motion_callback)) (item, event);
1922 track_canvas_motion (event);
1923 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1931 Editor::break_drag ()
1933 stop_canvas_autoscroll ();
1934 hide_verbose_canvas_cursor ();
1936 if (drag_info.item) {
1937 drag_info.item->ungrab (0);
1939 /* put it back where it came from */
1944 drag_info.item->i2w (cxw, cyw);
1945 drag_info.item->move (drag_info.original_x - cxw, drag_info.original_y - cyw);
1952 Editor::finalize_drag ()
1955 drag_info.copy = false;
1956 drag_info.motion_callback = 0;
1957 drag_info.finished_callback = 0;
1958 drag_info.dest_trackview = 0;
1959 drag_info.source_trackview = 0;
1960 drag_info.last_frame_position = 0;
1961 drag_info.grab_frame = 0;
1962 drag_info.last_pointer_frame = 0;
1963 drag_info.current_pointer_frame = 0;
1964 drag_info.brushing = false;
1965 range_marker_drag_rect->hide();
1966 drag_info.clear_copied_locations ();
1970 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1972 if (drag_info.item == 0) {
1973 fatal << _("programming error: start_grab called without drag item") << endmsg;
1979 cursor = which_grabber_cursor ();
1982 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1984 if (Keyboard::is_button2_event (&event->button)) {
1985 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
1986 drag_info.y_constrained = true;
1987 drag_info.x_constrained = false;
1989 drag_info.y_constrained = false;
1990 drag_info.x_constrained = true;
1993 drag_info.x_constrained = false;
1994 drag_info.y_constrained = false;
1997 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1998 drag_info.last_pointer_frame = drag_info.grab_frame;
1999 drag_info.current_pointer_frame = drag_info.grab_frame;
2000 drag_info.current_pointer_x = drag_info.grab_x;
2001 drag_info.current_pointer_y = drag_info.grab_y;
2002 drag_info.last_pointer_x = drag_info.current_pointer_x;
2003 drag_info.last_pointer_y = drag_info.current_pointer_y;
2004 drag_info.cumulative_x_drag = 0;
2005 drag_info.cumulative_y_drag = 0;
2006 drag_info.first_move = true;
2007 drag_info.move_threshold_passed = false;
2008 drag_info.want_move_threshold = false;
2009 drag_info.pointer_frame_offset = 0;
2010 drag_info.brushing = false;
2011 drag_info.clear_copied_locations ();
2013 drag_info.original_x = 0;
2014 drag_info.original_y = 0;
2015 drag_info.item->i2w (drag_info.original_x, drag_info.original_y);
2017 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
2019 event->button.time);
2021 if (session && session->transport_rolling()) {
2022 drag_info.was_rolling = true;
2024 drag_info.was_rolling = false;
2027 switch (snap_type) {
2028 case SnapToRegionStart:
2029 case SnapToRegionEnd:
2030 case SnapToRegionSync:
2031 case SnapToRegionBoundary:
2032 build_region_boundary_cache ();
2040 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
2042 drag_info.item->ungrab (0);
2043 drag_info.item = new_item;
2046 cursor = which_grabber_cursor ();
2049 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
2053 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
2055 bool did_drag = false;
2057 stop_canvas_autoscroll ();
2059 if (drag_info.item == 0) {
2063 drag_info.item->ungrab (event->button.time);
2065 if (drag_info.finished_callback) {
2066 drag_info.last_pointer_x = drag_info.current_pointer_x;
2067 drag_info.last_pointer_y = drag_info.current_pointer_y;
2068 (this->*(drag_info.finished_callback)) (item, event);
2071 did_drag = !drag_info.first_move;
2073 hide_verbose_canvas_cursor();
2081 Editor::region_gain_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2083 if (drag_info.first_move && drag_info.move_threshold_passed) {
2084 drag_info.first_move = false;
2089 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
2091 drag_info.item = item;
2092 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
2093 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
2097 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
2098 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
2102 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2105 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes64_t) arv->audio_region()->fade_in()->back()->when + arv->region()->position());
2109 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2111 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2113 nframes64_t fade_length;
2115 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2116 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2122 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2126 if (pos < (arv->region()->position() + 64)) {
2127 fade_length = 64; // this should be a minimum defined somewhere
2128 } else if (pos > arv->region()->last_frame()) {
2129 fade_length = arv->region()->length();
2131 fade_length = pos - arv->region()->position();
2133 /* mapover the region selection */
2135 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2137 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2143 tmp->reset_fade_in_shape_width (fade_length);
2146 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
2148 drag_info.first_move = false;
2152 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2154 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2156 nframes64_t fade_length;
2158 if (drag_info.first_move) return;
2160 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2161 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2166 if (pos < (arv->region()->position() + 64)) {
2167 fade_length = 64; // this should be a minimum defined somewhere
2168 } else if (pos > arv->region()->last_frame()) {
2169 fade_length = arv->region()->length();
2171 fade_length = pos - arv->region()->position();
2174 begin_reversible_command (_("change fade in length"));
2176 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2178 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2184 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2185 XMLNode &before = alist->get_state();
2187 tmp->audio_region()->set_fade_in_length (fade_length);
2188 tmp->audio_region()->set_fade_in_active (true);
2190 XMLNode &after = alist->get_state();
2191 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2194 commit_reversible_command ();
2198 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
2200 drag_info.item = item;
2201 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
2202 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
2206 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
2207 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
2211 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2213 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes64_t) arv->audio_region()->fade_out()->back()->when + arv->region()->position());
2217 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2219 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2221 nframes64_t fade_length;
2223 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2224 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2229 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2233 if (pos > (arv->region()->last_frame() - 64)) {
2234 fade_length = 64; // this should really be a minimum fade defined somewhere
2236 else if (pos < arv->region()->position()) {
2237 fade_length = arv->region()->length();
2240 fade_length = arv->region()->last_frame() - pos;
2243 /* mapover the region selection */
2245 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2247 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2253 tmp->reset_fade_out_shape_width (fade_length);
2256 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
2258 drag_info.first_move = false;
2262 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2264 if (drag_info.first_move) return;
2266 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2268 nframes64_t fade_length;
2270 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2271 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2277 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2281 if (pos > (arv->region()->last_frame() - 64)) {
2282 fade_length = 64; // this should really be a minimum fade defined somewhere
2284 else if (pos < arv->region()->position()) {
2285 fade_length = arv->region()->length();
2288 fade_length = arv->region()->last_frame() - pos;
2291 begin_reversible_command (_("change fade out length"));
2293 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2295 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2301 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2302 XMLNode &before = alist->get_state();
2304 tmp->audio_region()->set_fade_out_length (fade_length);
2305 tmp->audio_region()->set_fade_out_active (true);
2307 XMLNode &after = alist->get_state();
2308 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2311 commit_reversible_command ();
2315 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2317 drag_info.item = item;
2318 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2319 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2323 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2324 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2328 Cursor* cursor = (Cursor *) drag_info.data;
2330 if (cursor == playhead_cursor) {
2331 _dragging_playhead = true;
2333 if (session && drag_info.was_rolling) {
2334 session->request_stop ();
2337 if (session && session->is_auditioning()) {
2338 session->cancel_audition ();
2342 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2344 show_verbose_time_cursor (cursor->current_frame, 10);
2348 Editor::start_cursor_grab_no_stop (ArdourCanvas::Item* item, GdkEvent* event)
2350 drag_info.item = item;
2351 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2352 drag_info.finished_callback = &Editor::cursor_drag_finished_ensure_locate_callback;
2356 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2357 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2361 Cursor* cursor = (Cursor *) drag_info.data;
2362 nframes64_t where = event_frame (event, 0, 0);
2365 playhead_cursor->set_position (where);
2367 if (cursor == playhead_cursor) {
2368 _dragging_playhead = true;
2370 if (session && session->is_auditioning()) {
2371 session->cancel_audition ();
2375 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2377 show_verbose_time_cursor (cursor->current_frame, 10);
2381 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2383 Cursor* cursor = (Cursor *) drag_info.data;
2384 nframes64_t adjusted_frame;
2386 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2387 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2393 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2394 if (cursor == playhead_cursor) {
2395 snap_to (adjusted_frame);
2399 if (adjusted_frame == drag_info.last_pointer_frame) return;
2401 cursor->set_position (adjusted_frame);
2403 show_verbose_time_cursor (cursor->current_frame, 10);
2406 track_canvas->update_now ();
2408 UpdateAllTransportClocks (cursor->current_frame);
2410 drag_info.last_pointer_frame = adjusted_frame;
2411 drag_info.first_move = false;
2415 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2417 _dragging_playhead = false;
2419 if (drag_info.first_move) {
2423 cursor_drag_motion_callback (item, event);
2425 if (item == &playhead_cursor->canvas_item) {
2427 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2433 Editor::cursor_drag_finished_ensure_locate_callback (ArdourCanvas::Item* item, GdkEvent* event)
2435 _dragging_playhead = false;
2437 cursor_drag_motion_callback (item, event);
2439 if (item == &playhead_cursor->canvas_item) {
2441 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2447 Editor::update_marker_drag_item (Location *location)
2449 double x1 = frame_to_pixel (location->start());
2450 double x2 = frame_to_pixel (location->end());
2452 if (location->is_mark()) {
2453 marker_drag_line_points.front().set_x(x1);
2454 marker_drag_line_points.back().set_x(x1);
2455 marker_drag_line->property_points() = marker_drag_line_points;
2457 range_marker_drag_rect->property_x1() = x1;
2458 range_marker_drag_rect->property_x2() = x2;
2464 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2468 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2469 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2475 Location *location = find_location_from_marker (marker, is_start);
2477 drag_info.item = item;
2478 drag_info.data = marker;
2479 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2480 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2484 _dragging_edit_point = true;
2486 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2488 update_marker_drag_item (location);
2490 if (location->is_mark()) {
2491 // marker_drag_line->show();
2492 // marker_drag_line->raise_to_top();
2494 range_marker_drag_rect->show();
2495 //range_marker_drag_rect->raise_to_top();
2499 show_verbose_time_cursor (location->start(), 10);
2501 show_verbose_time_cursor (location->end(), 10);
2504 Selection::Operation op = Keyboard::selection_type (event->button.state);
2507 case Selection::Toggle:
2508 selection->toggle (marker);
2510 case Selection::Set:
2511 if (!selection->selected (marker)) {
2512 selection->set (marker);
2515 case Selection::Extend:
2517 Locations::LocationList ll;
2518 list<Marker*> to_add;
2520 selection->markers.range (s, e);
2521 s = min (marker->position(), s);
2522 e = max (marker->position(), e);
2525 if (e < max_frames) {
2528 session->locations()->find_all_between (s, e, ll, Location::Flags (0));
2529 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2530 LocationMarkers* lm = find_location_markers (*i);
2533 to_add.push_back (lm->start);
2536 to_add.push_back (lm->end);
2540 if (!to_add.empty()) {
2541 selection->add (to_add);
2545 case Selection::Add:
2546 selection->add (marker);
2550 /* set up copies for us to manipulate during the drag */
2552 drag_info.clear_copied_locations ();
2554 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
2555 Location *l = find_location_from_marker (*i, is_start);
2556 drag_info.copied_locations.push_back (new Location (*l));
2561 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2563 nframes64_t f_delta = 0;
2564 nframes64_t newframe;
2566 bool move_both = false;
2567 Marker* dragged_marker = (Marker*) drag_info.data;
2569 Location *real_location;
2570 Location *copy_location;
2572 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2573 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2578 nframes64_t next = newframe;
2580 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2581 snap_to (newframe, 0, true);
2584 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2588 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2592 MarkerSelection::iterator i;
2593 list<Location*>::iterator x;
2595 /* find the marker we're dragging, and compute the delta */
2597 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2598 x != drag_info.copied_locations.end() && i != selection->markers.end();
2604 if (marker == dragged_marker) {
2606 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2611 if (real_location->is_mark()) {
2612 f_delta = newframe - copy_location->start();
2616 switch (marker->type()) {
2618 case Marker::LoopStart:
2619 case Marker::PunchIn:
2620 f_delta = newframe - copy_location->start();
2624 case Marker::LoopEnd:
2625 case Marker::PunchOut:
2626 f_delta = newframe - copy_location->end();
2629 /* what kind of marker is this ? */
2637 if (i == selection->markers.end()) {
2638 /* hmm, impossible - we didn't find the dragged marker */
2642 /* now move them all */
2644 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2645 x != drag_info.copied_locations.end() && i != selection->markers.end();
2651 /* call this to find out if its the start or end */
2653 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2657 if (real_location->locked()) {
2661 if (copy_location->is_mark()) {
2665 copy_location->set_start (copy_location->start() + f_delta);
2669 nframes64_t new_start = copy_location->start() + f_delta;
2670 nframes64_t new_end = copy_location->end() + f_delta;
2672 if (is_start) { // start-of-range marker
2675 copy_location->set_start (new_start);
2676 copy_location->set_end (new_end);
2677 } else if (new_start < copy_location->end()) {
2678 copy_location->set_start (new_start);
2680 snap_to (next, 1, true);
2681 copy_location->set_end (next);
2682 copy_location->set_start (newframe);
2685 } else { // end marker
2688 copy_location->set_end (new_end);
2689 copy_location->set_start (new_start);
2690 } else if (new_end > copy_location->start()) {
2691 copy_location->set_end (new_end);
2692 } else if (newframe > 0) {
2693 snap_to (next, -1, true);
2694 copy_location->set_start (next);
2695 copy_location->set_end (newframe);
2699 update_marker_drag_item (copy_location);
2701 LocationMarkers* lm = find_location_markers (real_location);
2704 lm->set_position (copy_location->start(), copy_location->end());
2708 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2709 drag_info.first_move = false;
2711 if (drag_info.copied_locations.empty()) {
2715 edit_point_clock.set (drag_info.copied_locations.front()->start());
2716 show_verbose_time_cursor (newframe, 10);
2719 track_canvas->update_now ();
2721 edit_point_clock.set (copy_location->start());
2725 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2727 if (drag_info.first_move) {
2729 /* just a click, do nothing but finish
2730 off the selection process
2733 Selection::Operation op = Keyboard::selection_type (event->button.state);
2734 Marker* marker = (Marker *) drag_info.data;
2737 case Selection::Set:
2738 if (selection->selected (marker) && selection->markers.size() > 1) {
2739 selection->set (marker);
2743 case Selection::Toggle:
2744 case Selection::Extend:
2745 case Selection::Add:
2752 _dragging_edit_point = false;
2755 begin_reversible_command ( _("move marker") );
2756 XMLNode &before = session->locations()->get_state();
2758 MarkerSelection::iterator i;
2759 list<Location*>::iterator x;
2762 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2763 x != drag_info.copied_locations.end() && i != selection->markers.end();
2766 Location * location = find_location_from_marker ((*i), is_start);
2770 if (location->locked()) {
2774 if (location->is_mark()) {
2775 location->set_start ((*x)->start());
2777 location->set ((*x)->start(), (*x)->end());
2782 XMLNode &after = session->locations()->get_state();
2783 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2784 commit_reversible_command ();
2786 marker_drag_line->hide();
2787 range_marker_drag_rect->hide();
2791 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2794 MeterMarker* meter_marker;
2796 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2797 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2801 meter_marker = dynamic_cast<MeterMarker*> (marker);
2803 MetricSection& section (meter_marker->meter());
2805 if (!section.movable()) {
2809 drag_info.item = item;
2810 drag_info.copy = false;
2811 drag_info.data = marker;
2812 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2813 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2817 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2819 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2823 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2826 MeterMarker* meter_marker;
2828 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2829 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2833 meter_marker = dynamic_cast<MeterMarker*> (marker);
2835 // create a dummy marker for visual representation of moving the copy.
2836 // The actual copying is not done before we reach the finish callback.
2838 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2839 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2840 *new MeterSection(meter_marker->meter()));
2842 drag_info.item = &new_marker->the_item();
2843 drag_info.copy = true;
2844 drag_info.data = new_marker;
2845 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2846 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2850 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2852 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2856 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2858 MeterMarker* marker = (MeterMarker *) drag_info.data;
2859 nframes64_t adjusted_frame;
2861 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2862 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2868 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2869 snap_to (adjusted_frame);
2872 if (adjusted_frame == drag_info.last_pointer_frame) return;
2874 marker->set_position (adjusted_frame);
2877 drag_info.last_pointer_frame = adjusted_frame;
2878 drag_info.first_move = false;
2880 show_verbose_time_cursor (adjusted_frame, 10);
2884 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2886 if (drag_info.first_move) return;
2888 meter_marker_drag_motion_callback (drag_info.item, event);
2890 MeterMarker* marker = (MeterMarker *) drag_info.data;
2893 TempoMap& map (session->tempo_map());
2894 map.bbt_time (drag_info.last_pointer_frame, when);
2896 if (drag_info.copy == true) {
2897 begin_reversible_command (_("copy meter mark"));
2898 XMLNode &before = map.get_state();
2899 map.add_meter (marker->meter(), when);
2900 XMLNode &after = map.get_state();
2901 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2902 commit_reversible_command ();
2904 // delete the dummy marker we used for visual representation of copying.
2905 // a new visual marker will show up automatically.
2908 begin_reversible_command (_("move meter mark"));
2909 XMLNode &before = map.get_state();
2910 map.move_meter (marker->meter(), when);
2911 XMLNode &after = map.get_state();
2912 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2913 commit_reversible_command ();
2918 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2921 TempoMarker* tempo_marker;
2923 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2924 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2928 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2929 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2933 MetricSection& section (tempo_marker->tempo());
2935 if (!section.movable()) {
2939 drag_info.item = item;
2940 drag_info.copy = false;
2941 drag_info.data = marker;
2942 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2943 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2947 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2948 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2952 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2955 TempoMarker* tempo_marker;
2957 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2958 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2962 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2963 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2967 // create a dummy marker for visual representation of moving the copy.
2968 // The actual copying is not done before we reach the finish callback.
2970 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2971 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2972 *new TempoSection(tempo_marker->tempo()));
2974 drag_info.item = &new_marker->the_item();
2975 drag_info.copy = true;
2976 drag_info.data = new_marker;
2977 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2978 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2982 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2984 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2988 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2990 TempoMarker* marker = (TempoMarker *) drag_info.data;
2991 nframes64_t adjusted_frame;
2993 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2994 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3000 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3001 snap_to (adjusted_frame);
3004 if (adjusted_frame == drag_info.last_pointer_frame) return;
3006 /* OK, we've moved far enough to make it worth actually move the thing. */
3008 marker->set_position (adjusted_frame);
3010 show_verbose_time_cursor (adjusted_frame, 10);
3012 drag_info.last_pointer_frame = adjusted_frame;
3013 drag_info.first_move = false;
3017 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3019 if (drag_info.first_move) return;
3021 tempo_marker_drag_motion_callback (drag_info.item, event);
3023 TempoMarker* marker = (TempoMarker *) drag_info.data;
3026 TempoMap& map (session->tempo_map());
3027 map.bbt_time (drag_info.last_pointer_frame, when);
3029 if (drag_info.copy == true) {
3030 begin_reversible_command (_("copy tempo mark"));
3031 XMLNode &before = map.get_state();
3032 map.add_tempo (marker->tempo(), when);
3033 XMLNode &after = map.get_state();
3034 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3035 commit_reversible_command ();
3037 // delete the dummy marker we used for visual representation of copying.
3038 // a new visual marker will show up automatically.
3041 begin_reversible_command (_("move tempo mark"));
3042 XMLNode &before = map.get_state();
3043 map.move_tempo (marker->tempo(), when);
3044 XMLNode &after = map.get_state();
3045 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3046 commit_reversible_command ();
3051 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
3053 ControlPoint* control_point;
3055 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
3056 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
3060 // We shouldn't remove the first or last gain point
3061 if (control_point->line().is_last_point(*control_point) ||
3062 control_point->line().is_first_point(*control_point)) {
3066 control_point->line().remove_point (*control_point);
3070 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event)
3072 ControlPoint* control_point;
3074 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
3075 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
3079 control_point->line().remove_point (*control_point);
3083 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
3085 ControlPoint* control_point;
3087 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
3088 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
3092 drag_info.item = item;
3093 drag_info.data = control_point;
3094 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
3095 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
3097 start_grab (event, fader_cursor);
3099 // start the grab at the center of the control point so
3100 // the point doesn't 'jump' to the mouse after the first drag
3101 drag_info.grab_x = control_point->get_x();
3102 drag_info.grab_y = control_point->get_y();
3104 control_point->line().parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
3105 track_canvas->w2c(drag_info.grab_x, drag_info.grab_y, drag_info.grab_x, drag_info.grab_y);
3107 drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
3109 control_point->line().start_drag (control_point, drag_info.grab_frame, 0);
3111 float fraction = 1.0 - (control_point->get_y() / control_point->line().height());
3112 set_verbose_canvas_cursor (control_point->line().get_verbose_cursor_string (fraction),
3113 drag_info.current_pointer_x + 10, drag_info.current_pointer_y + 10);
3115 show_verbose_canvas_cursor ();
3119 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3121 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
3123 double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
3124 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
3126 if (event->button.state & Keyboard::SecondaryModifier) {
3131 double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
3132 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
3134 // calculate zero crossing point. back off by .01 to stay on the
3135 // positive side of zero
3137 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line().height() - .01;
3138 cp->line().parent_group().i2w(_unused, zero_gain_y);
3140 // make sure we hit zero when passing through
3141 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
3142 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
3146 if (drag_info.x_constrained) {
3147 cx = drag_info.grab_x;
3149 if (drag_info.y_constrained) {
3150 cy = drag_info.grab_y;
3153 drag_info.cumulative_x_drag = cx - drag_info.grab_x;
3154 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
3156 cp->line().parent_group().w2i (cx, cy);
3160 cy = min ((double) cp->line().height(), cy);
3162 //translate cx to frames
3163 nframes64_t cx_frames = unit_to_frame (cx);
3165 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
3166 snap_to (cx_frames);
3169 float fraction = 1.0 - (cy / cp->line().height());
3173 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3179 cp->line().point_drag (*cp, cx_frames , fraction, push);
3181 set_verbose_canvas_cursor_text (cp->line().get_verbose_cursor_string (fraction));
3183 drag_info.first_move = false;
3187 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3189 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
3191 if (drag_info.first_move) {
3195 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3196 reset_point_selection ();
3200 control_point_drag_motion_callback (item, event);
3202 cp->line().end_drag (cp);
3206 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
3208 switch (mouse_mode) {
3210 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
3211 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
3219 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
3223 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
3224 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
3228 start_line_grab (al, event);
3232 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
3236 nframes64_t frame_within_region;
3238 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3239 origin, and ditto for y.
3242 cx = event->button.x;
3243 cy = event->button.y;
3245 line->parent_group().w2i (cx, cy);
3247 frame_within_region = (nframes64_t) floor (cx * frames_per_unit);
3249 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
3250 current_line_drag_info.after)) {
3251 /* no adjacent points */
3255 drag_info.item = &line->grab_item();
3256 drag_info.data = line;
3257 drag_info.motion_callback = &Editor::line_drag_motion_callback;
3258 drag_info.finished_callback = &Editor::line_drag_finished_callback;
3260 start_grab (event, fader_cursor);
3262 /* store grab start in parent frame */
3264 drag_info.grab_x = cx;
3265 drag_info.grab_y = cy;
3267 double fraction = 1.0 - (cy / line->height());
3269 line->start_drag (0, drag_info.grab_frame, fraction);
3271 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
3272 drag_info.current_pointer_x + 10, drag_info.current_pointer_y + 10);
3273 show_verbose_canvas_cursor ();
3277 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3279 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3281 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
3283 if (event->button.state & Keyboard::SecondaryModifier) {
3287 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
3289 // calculate zero crossing point. back off by .01 to stay on the
3290 // positive side of zero
3291 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
3293 // line->parent_group().i2w(_unused, zero_gain_y);
3295 // make sure we hit zero when passing through
3296 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
3297 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
3301 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
3304 cy = min ((double) line->height(), cy);
3307 double fraction = 1.0 - (cy / line->height());
3311 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3317 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
3319 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
3323 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3325 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3326 line_drag_motion_callback (item, event);
3331 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3333 if (selection->regions.empty() || clicked_regionview == 0) {
3336 _region_motion_group->raise_to_top ();
3337 drag_info.copy = false;
3338 drag_info.item = item;
3339 drag_info.data = clicked_regionview;
3341 if (Config->get_edit_mode() == Splice) {
3342 drag_info.motion_callback = &Editor::region_drag_splice_motion_callback;
3343 drag_info.finished_callback = &Editor::region_drag_splice_finished_callback;
3345 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3346 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3352 TimeAxisView* tvp = clicked_axisview;
3353 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3355 if (tv && tv->is_track()) {
3356 speed = tv->get_diskstream()->speed();
3359 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3360 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3361 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3362 drag_info.dest_trackview = drag_info.source_trackview;
3363 // we want a move threshold
3364 drag_info.want_move_threshold = true;
3365 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3367 begin_reversible_command (_("move region(s)"));
3369 /* sync the canvas to what we think is its current state */
3370 track_canvas->update_now();
3374 Editor::start_create_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3376 drag_info.copy = false;
3377 drag_info.item = item;
3378 drag_info.data = clicked_axisview;
3379 drag_info.source_trackview = clicked_axisview;
3380 drag_info.dest_trackview = drag_info.source_trackview;
3381 drag_info.motion_callback = &Editor::create_region_drag_motion_callback;
3382 drag_info.finished_callback = &Editor::create_region_drag_finished_callback;
3388 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
3390 if (selection->regions.empty() || clicked_regionview == 0) {
3393 _region_motion_group->raise_to_top ();
3394 drag_info.copy = true;
3395 drag_info.item = item;
3396 drag_info.data = clicked_regionview;
3400 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
3401 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3404 if (rtv && rtv->is_track()) {
3405 speed = rtv->get_diskstream()->speed();
3408 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3409 drag_info.dest_trackview = drag_info.source_trackview;
3410 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3411 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3412 // we want a move threshold
3413 drag_info.want_move_threshold = true;
3414 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3415 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3416 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3420 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
3422 if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) {
3426 drag_info.copy = false;
3427 drag_info.item = item;
3428 drag_info.data = clicked_regionview;
3429 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3430 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3435 TimeAxisView* tvp = clicked_axisview;
3436 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3438 if (tv && tv->is_track()) {
3439 speed = tv->get_diskstream()->speed();
3442 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3443 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3444 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3445 drag_info.dest_trackview = drag_info.source_trackview;
3446 // we want a move threshold
3447 drag_info.want_move_threshold = true;
3448 drag_info.brushing = true;
3450 begin_reversible_command (_("Drag region brush"));
3454 Editor::possibly_copy_regions_during_grab (GdkEvent* event)
3456 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
3458 drag_info.want_move_threshold = false; // don't copy again
3460 /* duplicate the regionview(s) and region(s) */
3462 vector<RegionView*> new_regionviews;
3464 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3469 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
3470 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
3472 const boost::shared_ptr<const Region> original = rv->region();
3473 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
3476 boost::shared_ptr<AudioRegion> audioregion_copy
3477 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
3478 nrv = new AudioRegionView (*arv, audioregion_copy);
3480 boost::shared_ptr<MidiRegion> midiregion_copy
3481 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
3482 nrv = new MidiRegionView (*mrv, midiregion_copy);
3487 nrv->get_canvas_group()->show ();
3488 new_regionviews.push_back (nrv);
3491 if (new_regionviews.empty()) {
3495 /* reset selection to new regionviews. This will not set selection visual status for
3496 these regionviews since they don't belong to a track, so do that by hand too.
3499 selection->set (new_regionviews);
3501 for (vector<RegionView*>::iterator i = new_regionviews.begin(); i != new_regionviews.end(); ++i) {
3502 (*i)->set_selected (true);
3505 /* reset drag_info data to reflect the fact that we are dragging the copies */
3507 drag_info.data = new_regionviews.front();
3509 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3511 sync the canvas to what we think is its current state
3512 without it, the canvas seems to
3513 "forget" to update properly after the upcoming reparent()
3514 ..only if the mouse is in rapid motion at the time of the grab.
3515 something to do with regionview creation raking so long?
3517 track_canvas->update_now();
3522 Editor::check_region_drag_possible (RouteTimeAxisView** tv)
3524 /* Which trackview is this ? */
3526 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
3527 (*tv) = dynamic_cast<RouteTimeAxisView*>(tvp);
3529 /* The region motion is only processed if the pointer is over
3533 if (!(*tv) || !(*tv)->is_track()) {
3534 /* To make sure we hide the verbose canvas cursor when the mouse is
3535 not held over and audiotrack.
3537 hide_verbose_canvas_cursor ();
3544 struct RegionSelectionByPosition {
3545 bool operator() (RegionView*a, RegionView* b) {
3546 return a->region()->position () < b->region()->position();
3551 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3553 RouteTimeAxisView* tv;
3555 if (!check_region_drag_possible (&tv)) {
3559 if (!drag_info.move_threshold_passed) {
3565 if (drag_info.current_pointer_x - drag_info.grab_x > 0) {
3571 RegionSelection copy (selection->regions);
3573 RegionSelectionByPosition cmp;
3576 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
3578 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
3584 boost::shared_ptr<Playlist> playlist;
3586 if ((playlist = atv->playlist()) == 0) {
3590 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
3595 if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) {
3599 if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) {
3605 playlist->shuffle ((*i)->region(), dir);
3607 drag_info.grab_x = drag_info.current_pointer_x;
3612 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3617 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3621 nframes64_t pending_region_position = 0;
3622 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
3623 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
3624 bool clamp_y_axis = false;
3625 vector<int32_t> height_list(512) ;
3626 vector<int32_t>::iterator j;
3627 RouteTimeAxisView* tv;
3629 possibly_copy_regions_during_grab (event);
3631 if (!check_region_drag_possible (&tv)) {
3635 original_pointer_order = drag_info.dest_trackview->order;
3637 /************************************************************
3639 ************************************************************/
3641 if (drag_info.brushing) {
3642 clamp_y_axis = true;
3647 if ((pointer_y_span = (drag_info.dest_trackview->order - tv->order)) != 0) {
3649 int32_t children = 0, numtracks = 0;
3650 // XXX hard coding track limit, oh my, so very very bad
3651 bitset <1024> tracks (0x00);
3652 /* get a bitmask representing the visible tracks */
3654 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3655 TimeAxisView *tracklist_timeview;
3656 tracklist_timeview = (*i);
3657 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
3658 TimeAxisView::Children children_list;
3660 /* zeroes are audio tracks. ones are other types. */
3662 if (!rtv2->hidden()) {
3664 if (visible_y_high < rtv2->order) {
3665 visible_y_high = rtv2->order;
3667 if (visible_y_low > rtv2->order) {
3668 visible_y_low = rtv2->order;
3671 if (!rtv2->is_track()) {
3672 tracks = tracks |= (0x01 << rtv2->order);
3675 height_list[rtv2->order] = (*i)->current_height();
3678 if ((children_list = rtv2->get_child_list()).size() > 0) {
3679 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
3680 tracks = tracks |= (0x01 << (rtv2->order + children));
3681 height_list[rtv2->order + children] = (*j)->current_height();
3689 /* find the actual span according to the canvas */
3691 canvas_pointer_y_span = pointer_y_span;
3692 if (drag_info.dest_trackview->order >= tv->order) {
3694 for (y = tv->order; y < drag_info.dest_trackview->order; y++) {
3695 if (height_list[y] == 0 ) {
3696 canvas_pointer_y_span--;
3701 for (y = drag_info.dest_trackview->order;y <= tv->order; y++) {
3702 if ( height_list[y] == 0 ) {
3703 canvas_pointer_y_span++;
3708 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3709 RegionView* rv2 = (*i);
3710 double ix1, ix2, iy1, iy2;
3713 if (rv2->region()->locked()) {
3717 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3718 rv2->get_canvas_frame()->i2w (ix1, iy1);
3719 iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
3721 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3722 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3724 if (rtv2->order != original_pointer_order) {
3725 /* this isn't the pointer track */
3727 if (canvas_pointer_y_span > 0) {
3729 /* moving up the canvas */
3730 if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
3732 int32_t visible_tracks = 0;
3733 while (visible_tracks < canvas_pointer_y_span ) {
3736 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3737 /* we're passing through a hidden track */
3742 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3743 clamp_y_axis = true;
3747 clamp_y_axis = true;
3750 } else if (canvas_pointer_y_span < 0) {
3752 /*moving down the canvas*/
3754 if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3757 int32_t visible_tracks = 0;
3759 while (visible_tracks > canvas_pointer_y_span ) {
3762 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3766 if ( tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3767 clamp_y_axis = true;
3772 clamp_y_axis = true;
3778 /* this is the pointer's track */
3779 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3780 clamp_y_axis = true;
3781 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3782 clamp_y_axis = true;
3790 } else if (drag_info.dest_trackview == tv) {
3791 clamp_y_axis = true;
3795 if (!clamp_y_axis) {
3796 drag_info.dest_trackview = tv;
3799 /************************************************************
3801 ************************************************************/
3803 /* compute the amount of pointer motion in frames, and where
3804 the region would be if we moved it by that much.
3806 if ( drag_info.move_threshold_passed ) {
3808 if (drag_info.current_pointer_frame >= drag_info.pointer_frame_offset) {
3810 nframes64_t sync_frame;
3811 nframes64_t sync_offset;
3814 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3816 sync_offset = clicked_regionview->region()->sync_offset (sync_dir);
3818 /* we don't handle a sync point that lies before zero.
3820 if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) {
3821 sync_frame = pending_region_position + (sync_dir*sync_offset);
3823 /* we snap if the snap modifier is not enabled.
3826 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3827 snap_to (sync_frame);
3830 pending_region_position = clicked_regionview->region()->adjust_to_sync (sync_frame);
3833 pending_region_position = drag_info.last_frame_position;
3837 pending_region_position = 0;
3840 if (pending_region_position > max_frames - clicked_regionview->region()->length()) {
3841 pending_region_position = drag_info.last_frame_position;
3844 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3846 bool x_move_allowed;
3848 if (Config->get_edit_mode() == Lock) {
3849 if (drag_info.copy) {
3850 x_move_allowed = !drag_info.x_constrained;
3852 /* in locked edit mode, reverse the usual meaning of x_constrained */
3853 x_move_allowed = drag_info.x_constrained;
3856 x_move_allowed = !drag_info.x_constrained;
3859 if (( pending_region_position != drag_info.last_frame_position) && x_move_allowed ) {
3861 /* now compute the canvas unit distance we need to move the regionview
3862 to make it appear at the new location.
3865 if (pending_region_position > drag_info.last_frame_position) {
3866 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3868 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3869 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3871 RegionView* rv2 = (*i);
3873 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3875 double ix1, ix2, iy1, iy2;
3876 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3877 rv2->get_canvas_frame()->i2w (ix1, iy1);
3879 if (-x_delta > ix1 + horizontal_adjustment.get_value()) {
3882 pending_region_position = drag_info.last_frame_position;
3889 drag_info.last_frame_position = pending_region_position;
3896 /* threshold not passed */
3901 /*************************************************************
3903 ************************************************************/
3905 if (x_delta == 0 && (pointer_y_span == 0)) {
3906 /* haven't reached next snap point, and we're not switching
3907 trackviews. nothing to do.
3912 /*************************************************************
3914 ************************************************************/
3915 bool do_move = true;
3916 if (drag_info.first_move) {
3917 if (!drag_info.move_threshold_passed) {
3924 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3925 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3927 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3929 RegionView* rv = (*i);
3930 double ix1, ix2, iy1, iy2;
3931 int32_t temp_pointer_y_span = pointer_y_span;
3933 if (rv->region()->locked()) {
3937 /* get item BBox, which will be relative to parent. so we have
3938 to query on a child, then convert to world coordinates using
3942 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3943 rv->get_canvas_frame()->i2w (ix1, iy1);
3945 cerr << "adjust y from " << iy1 << " using "
3946 << vertical_adjustment.get_value() << " - "
3947 << canvas_timebars_vsize
3950 iy1 += get_trackview_group_vertical_offset ();;
3952 if (drag_info.first_move) {
3954 // hide any dependent views
3956 rv->get_time_axis_view().hide_dependent_views (*rv);
3959 reparent to a non scrolling group so that we can keep the
3960 region selection above all time axis views.
3961 reparenting means we have to move the rv as the two
3962 parent groups have different coordinates.
3965 rv->get_canvas_group()->property_y() = iy1 - 1;
3966 rv->get_canvas_group()->reparent(*_region_motion_group);
3968 rv->fake_set_opaque (true);
3971 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3972 RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3973 RouteTimeAxisView* temp_rtv;
3975 if ((pointer_y_span != 0) && !clamp_y_axis) {
3978 for (j = height_list.begin(); j!= height_list.end(); j++) {
3979 if (x == canvas_rtv->order) {
3980 /* we found the track the region is on */
3981 if (x != original_pointer_order) {
3982 /*this isn't from the same track we're dragging from */
3983 temp_pointer_y_span = canvas_pointer_y_span;
3985 while (temp_pointer_y_span > 0) {
3986 /* we're moving up canvas-wise,
3987 so we need to find the next track height
3989 if (j != height_list.begin()) {
3992 if (x != original_pointer_order) {
3993 /* we're not from the dragged track, so ignore hidden tracks. */
3995 temp_pointer_y_span++;
3999 temp_pointer_y_span--;
4002 while (temp_pointer_y_span < 0) {
4004 if (x != original_pointer_order) {
4006 temp_pointer_y_span--;
4010 if (j != height_list.end()) {
4013 temp_pointer_y_span++;
4015 /* find out where we'll be when we move and set height accordingly */
4017 tvp2 = trackview_by_y_position (iy1 + y_delta);
4018 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
4019 rv->set_height (temp_rtv->current_height());
4021 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
4022 personally, i think this can confuse things, but never mind.
4025 //const GdkColor& col (temp_rtv->view->get_region_color());
4026 //rv->set_color (const_cast<GdkColor&>(col));
4033 if (drag_info.brushing) {
4034 mouse_brush_insert_region (rv, pending_region_position);
4036 rv->move (x_delta, y_delta);
4039 } /* foreach region */
4043 if (drag_info.first_move && drag_info.move_threshold_passed) {
4044 cursor_group->raise_to_top();
4045 drag_info.first_move = false;
4048 if (x_delta != 0 && !drag_info.brushing) {
4049 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4054 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4056 bool nocommit = true;
4057 vector<RegionView*> copies;
4058 RouteTimeAxisView* source_tv;
4059 boost::shared_ptr<Diskstream> ds;
4060 boost::shared_ptr<Playlist> from_playlist;
4061 vector<RegionView*> new_selection;
4062 typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
4063 PlaylistSet modified_playlists;
4064 PlaylistSet frozen_playlists;
4065 list <sigc::connection> modified_playlist_connections;
4066 pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
4067 nframes64_t drag_delta;
4068 bool changed_tracks, changed_position;
4070 /* first_move is set to false if the regionview has been moved in the
4074 if (drag_info.first_move) {
4081 if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
4082 selection->set (pre_drag_region_selection);
4083 pre_drag_region_selection.clear ();
4086 if (drag_info.brushing) {
4087 /* all changes were made during motion event handlers */
4089 if (drag_info.copy) {
4090 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4091 copies.push_back (*i);
4100 /* reverse this here so that we have the correct logic to finalize
4104 if (Config->get_edit_mode() == Lock && !drag_info.copy) {
4105 drag_info.x_constrained = !drag_info.x_constrained;
4108 if (drag_info.copy) {
4109 if (drag_info.x_constrained) {
4110 op_string = _("fixed time region copy");
4112 op_string = _("region copy");
4115 if (drag_info.x_constrained) {
4116 op_string = _("fixed time region drag");
4118 op_string = _("region drag");
4122 begin_reversible_command (op_string);
4123 changed_position = (drag_info.last_frame_position != (nframes64_t) (clicked_regionview->region()->position()));
4124 changed_tracks = (trackview_by_y_position (drag_info.current_pointer_y) != &clicked_regionview->get_time_axis_view());
4126 drag_delta = clicked_regionview->region()->position() - drag_info.last_frame_position;
4128 track_canvas->update_now ();
4130 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
4132 RegionView* rv = (*i);
4133 double ix1, ix2, iy1, iy2;
4134 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
4135 rv->get_canvas_frame()->i2w (ix1, iy1);
4136 iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
4138 TimeAxisView* dest_tv = trackview_by_y_position (iy1);
4139 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*>(dest_tv);
4143 if (rv->region()->locked()) {
4148 if (changed_position && !drag_info.x_constrained) {
4149 where = rv->region()->position() - drag_delta;
4151 where = rv->region()->position();
4154 boost::shared_ptr<Region> new_region;
4156 if (drag_info.copy) {
4157 /* we already made a copy */
4158 new_region = rv->region();
4160 /* undo the previous hide_dependent_views so that xfades don't
4161 disappear on copying regions
4164 //rv->get_time_axis_view().reveal_dependent_views (*rv);
4166 } else if (changed_tracks && dest_rtv->playlist()) {
4167 new_region = RegionFactory::create (rv->region());
4170 if (changed_tracks || drag_info.copy) {
4172 boost::shared_ptr<Playlist> to_playlist = dest_rtv->playlist();
4178 latest_regionviews.clear ();
4180 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4182 insert_result = modified_playlists.insert (to_playlist);
4183 if (insert_result.second) {
4184 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
4187 to_playlist->add_region (new_region, where);
4191 if (!latest_regionviews.empty()) {
4192 // XXX why just the first one ? we only expect one
4193 // commented out in nick_m's canvas reworking. is that intended?
4194 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
4195 new_selection.push_back (latest_regionviews.front());
4200 motion on the same track. plonk the previously reparented region
4201 back to its original canvas group (its streamview).
4202 No need to do anything for copies as they are fake regions which will be deleted.
4205 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
4206 rv->get_canvas_group()->property_y() = 0;
4208 /* just change the model */
4210 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
4212 insert_result = modified_playlists.insert (playlist);
4213 if (insert_result.second) {
4214 session->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
4216 /* freeze to avoid lots of relayering in the case of a multi-region drag */
4217 frozen_insert_result = frozen_playlists.insert(playlist);
4218 if (frozen_insert_result.second) {
4222 rv->region()->set_position (where, (void*) this);
4225 if (changed_tracks && !drag_info.copy) {
4227 /* get the playlist where this drag started. we can't use rv->region()->playlist()
4228 because we may have copied the region and it has not been attached to a playlist.
4231 assert ((source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view())));
4232 assert ((ds = source_tv->get_diskstream()));
4233 assert ((from_playlist = ds->playlist()));
4235 /* moved to a different audio track, without copying */
4237 /* the region that used to be in the old playlist is not
4238 moved to the new one - we use a copy of it. as a result,
4239 any existing editor for the region should no longer be
4243 rv->hide_region_editor();
4244 rv->fake_set_opaque (false);
4246 /* remove the region from the old playlist */
4248 insert_result = modified_playlists.insert (from_playlist);
4249 if (insert_result.second) {
4250 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
4253 from_playlist->remove_region ((rv->region()));
4255 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
4256 was selected in all of them, then removing it from a playlist will have removed all
4257 trace of it from the selection (i.e. there were N regions selected, we removed 1,
4258 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
4259 corresponding regionview, and the selection is now empty).
4261 this could have invalidated any and all iterators into the region selection.
4263 the heuristic we use here is: if the region selection is empty, break out of the loop
4264 here. if the region selection is not empty, then restart the loop because we know that
4265 we must have removed at least the region(view) we've just been working on as well as any
4266 that we processed on previous iterations.
4268 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
4269 we can just iterate.
4272 if (selection->regions.empty()) {
4275 i = selection->regions.by_layer().begin();
4282 if (drag_info.copy) {
4283 copies.push_back (rv);
4287 if (new_selection.empty()) {
4288 if (drag_info.copy) {
4289 /* the region(view)s that are selected and being dragged around
4290 are copies and do not belong to any track. remove them
4291 from the selection right here.
4293 selection->clear_regions();
4296 /* this will clear any existing selection that would have been
4297 cleared in the other clause above
4299 selection->set (new_selection);
4302 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
4308 for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
4309 session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));
4311 commit_reversible_command ();
4314 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
4321 Editor::create_region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4323 if (drag_info.move_threshold_passed) {
4324 if (drag_info.first_move) {
4325 // TODO: create region-create-drag region view here
4326 drag_info.first_move = false;
4329 // TODO: resize region-create-drag region view here
4334 Editor::create_region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4336 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (drag_info.dest_trackview);
4340 const boost::shared_ptr<MidiDiskstream> diskstream =
4341 boost::dynamic_pointer_cast<MidiDiskstream>(mtv->view()->trackview().track()->diskstream());
4344 warning << "Cannot create non-MIDI region" << endl;
4348 if (drag_info.first_move) {
4349 begin_reversible_command (_("create region"));
4350 XMLNode &before = mtv->playlist()->get_state();
4352 nframes64_t start = drag_info.grab_frame;
4353 snap_to (start, -1);
4354 const Meter& m = session->tempo_map().meter_at(start);
4355 const Tempo& t = session->tempo_map().tempo_at(start);
4356 double length = floor (m.frames_per_bar(t, session->frame_rate()));
4358 boost::shared_ptr<Source> src = session->create_midi_source_for_session(*diskstream.get());
4360 mtv->playlist()->add_region (boost::dynamic_pointer_cast<MidiRegion>
4361 (RegionFactory::create(src, 0, (nframes_t) length,
4362 PBD::basename_nosuffix(src->name()))), start);
4363 XMLNode &after = mtv->playlist()->get_state();
4364 session->add_command(new MementoCommand<Playlist>(*mtv->playlist().get(), &before, &after));
4365 commit_reversible_command();
4368 create_region_drag_motion_callback (item, event);
4369 // TODO: create region-create-drag region here
4374 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
4376 /* Either add to or set the set the region selection, unless
4377 this is an alignment click (control used)
4380 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
4381 TimeAxisView* tv = &rv.get_time_axis_view();
4382 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
4384 if (rtv && rtv->is_track()) {
4385 speed = rtv->get_diskstream()->speed();
4388 nframes64_t where = get_preferred_edit_position();
4392 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
4394 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
4396 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
4398 align_region (rv.region(), End, (nframes64_t) (where * speed));
4402 align_region (rv.region(), Start, (nframes64_t) (where * speed));
4409 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
4415 nframes64_t frame_rate;
4424 if (Profile->get_sae() || Profile->get_small_screen()) {
4425 m = ARDOUR_UI::instance()->primary_clock.mode();
4427 m = ARDOUR_UI::instance()->secondary_clock.mode();
4431 case AudioClock::BBT:
4432 session->bbt_time (frame, bbt);
4433 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
4436 case AudioClock::SMPTE:
4437 session->smpte_time (frame, smpte);
4438 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4441 case AudioClock::MinSec:
4442 /* XXX this is copied from show_verbose_duration_cursor() */
4443 frame_rate = session->frame_rate();
4444 hours = frame / (frame_rate * 3600);
4445 frame = frame % (frame_rate * 3600);
4446 mins = frame / (frame_rate * 60);
4447 frame = frame % (frame_rate * 60);
4448 secs = (float) frame / (float) frame_rate;
4449 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4453 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
4457 if (xpos >= 0 && ypos >=0) {
4458 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4461 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);
4463 show_verbose_canvas_cursor ();
4467 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
4474 nframes64_t distance, frame_rate;
4476 Meter meter_at_start(session->tempo_map().meter_at(start));
4484 if (Profile->get_sae() || Profile->get_small_screen()) {
4485 m = ARDOUR_UI::instance()->primary_clock.mode ();
4487 m = ARDOUR_UI::instance()->secondary_clock.mode ();
4491 case AudioClock::BBT:
4492 session->bbt_time (start, sbbt);
4493 session->bbt_time (end, ebbt);
4496 /* XXX this computation won't work well if the
4497 user makes a selection that spans any meter changes.
4500 ebbt.bars -= sbbt.bars;
4501 if (ebbt.beats >= sbbt.beats) {
4502 ebbt.beats -= sbbt.beats;
4505 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
4507 if (ebbt.ticks >= sbbt.ticks) {
4508 ebbt.ticks -= sbbt.ticks;
4511 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
4514 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
4517 case AudioClock::SMPTE:
4518 session->smpte_duration (end - start, smpte);
4519 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4522 case AudioClock::MinSec:
4523 /* XXX this stuff should be elsewhere.. */
4524 distance = end - start;
4525 frame_rate = session->frame_rate();
4526 hours = distance / (frame_rate * 3600);
4527 distance = distance % (frame_rate * 3600);
4528 mins = distance / (frame_rate * 60);
4529 distance = distance % (frame_rate * 60);
4530 secs = (float) distance / (float) frame_rate;
4531 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4535 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
4539 if (xpos >= 0 && ypos >=0) {
4540 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4543 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4546 show_verbose_canvas_cursor ();
4550 Editor::collect_new_region_view (RegionView* rv)
4552 latest_regionviews.push_back (rv);
4556 Editor::collect_and_select_new_region_view (RegionView* rv)
4559 latest_regionviews.push_back (rv);
4563 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
4565 if (clicked_regionview == 0) {
4569 /* lets try to create new Region for the selection */
4571 vector<boost::shared_ptr<Region> > new_regions;
4572 create_region_from_selection (new_regions);
4574 if (new_regions.empty()) {
4578 /* XXX fix me one day to use all new regions */
4580 boost::shared_ptr<Region> region (new_regions.front());
4582 /* add it to the current stream/playlist.
4584 tricky: the streamview for the track will add a new regionview. we will
4585 catch the signal it sends when it creates the regionview to
4586 set the regionview we want to then drag.
4589 latest_regionviews.clear();
4590 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4592 /* A selection grab currently creates two undo/redo operations, one for
4593 creating the new region and another for moving it.
4596 begin_reversible_command (_("selection grab"));
4598 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
4600 XMLNode *before = &(playlist->get_state());
4601 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
4602 XMLNode *after = &(playlist->get_state());
4603 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4605 commit_reversible_command ();
4609 if (latest_regionviews.empty()) {
4610 /* something went wrong */
4614 /* we need to deselect all other regionviews, and select this one
4615 i'm ignoring undo stuff, because the region creation will take care of it
4617 selection->set (latest_regionviews);
4619 drag_info.item = latest_regionviews.front()->get_canvas_group();
4620 drag_info.data = latest_regionviews.front();
4621 drag_info.motion_callback = &Editor::region_drag_motion_callback;
4622 drag_info.finished_callback = &Editor::region_drag_finished_callback;
4626 drag_info.source_trackview = clicked_routeview;
4627 drag_info.dest_trackview = drag_info.source_trackview;
4628 drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4629 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4631 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4635 Editor::cancel_selection ()
4637 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4638 (*i)->hide_selection ();
4640 selection->clear ();
4641 clicked_selection = 0;
4645 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4647 nframes64_t start = 0;
4648 nframes64_t end = 0;
4654 drag_info.item = item;
4655 drag_info.motion_callback = &Editor::drag_selection;
4656 drag_info.finished_callback = &Editor::end_selection_op;
4661 case CreateSelection:
4662 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4663 drag_info.copy = true;
4665 drag_info.copy = false;
4667 start_grab (event, selector_cursor);
4670 case SelectionStartTrim:
4671 if (clicked_axisview) {
4672 clicked_axisview->order_selection_trims (item, true);
4674 start_grab (event, trimmer_cursor);
4675 start = selection->time[clicked_selection].start;
4676 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4679 case SelectionEndTrim:
4680 if (clicked_axisview) {
4681 clicked_axisview->order_selection_trims (item, false);
4683 start_grab (event, trimmer_cursor);
4684 end = selection->time[clicked_selection].end;
4685 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
4689 start = selection->time[clicked_selection].start;
4691 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4695 if (selection_op == SelectionMove) {
4696 show_verbose_time_cursor(start, 10);
4698 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4703 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4705 nframes64_t start = 0;
4706 nframes64_t end = 0;
4708 nframes64_t pending_position;
4710 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4711 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4713 pending_position = 0;
4716 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4717 snap_to (pending_position);
4720 /* only alter selection if the current frame is
4721 different from the last frame position (adjusted)
4724 if (pending_position == drag_info.last_pointer_frame) return;
4726 switch (selection_op) {
4727 case CreateSelection:
4729 if (drag_info.first_move) {
4730 snap_to (drag_info.grab_frame);
4733 if (pending_position < drag_info.grab_frame) {
4734 start = pending_position;
4735 end = drag_info.grab_frame;
4737 end = pending_position;
4738 start = drag_info.grab_frame;
4741 /* first drag: Either add to the selection
4742 or create a new selection->
4745 if (drag_info.first_move) {
4747 begin_reversible_command (_("range selection"));
4749 if (drag_info.copy) {
4750 /* adding to the selection */
4751 clicked_selection = selection->add (start, end);
4752 drag_info.copy = false;
4754 /* new selection-> */
4755 clicked_selection = selection->set (clicked_axisview, start, end);
4760 case SelectionStartTrim:
4762 if (drag_info.first_move) {
4763 begin_reversible_command (_("trim selection start"));
4766 start = selection->time[clicked_selection].start;
4767 end = selection->time[clicked_selection].end;
4769 if (pending_position > end) {
4772 start = pending_position;
4776 case SelectionEndTrim:
4778 if (drag_info.first_move) {
4779 begin_reversible_command (_("trim selection end"));
4782 start = selection->time[clicked_selection].start;
4783 end = selection->time[clicked_selection].end;
4785 if (pending_position < start) {
4788 end = pending_position;
4795 if (drag_info.first_move) {
4796 begin_reversible_command (_("move selection"));
4799 start = selection->time[clicked_selection].start;
4800 end = selection->time[clicked_selection].end;
4802 length = end - start;
4804 start = pending_position;
4807 end = start + length;
4812 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4813 start_canvas_autoscroll (1, 0);
4817 selection->replace (clicked_selection, start, end);
4820 drag_info.last_pointer_frame = pending_position;
4821 drag_info.first_move = false;
4823 if (selection_op == SelectionMove) {
4824 show_verbose_time_cursor(start, 10);
4826 show_verbose_time_cursor(pending_position, 10);
4831 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4833 if (!drag_info.first_move) {
4834 drag_selection (item, event);
4835 /* XXX this is not object-oriented programming at all. ick */
4836 if (selection->time.consolidate()) {
4837 selection->TimeChanged ();
4839 commit_reversible_command ();
4841 /* just a click, no pointer movement.*/
4843 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4845 selection->clear_time();
4850 /* XXX what happens if its a music selection? */
4851 session->set_audio_range (selection->time);
4852 stop_canvas_autoscroll ();
4856 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4859 TimeAxisView* tvp = clicked_axisview;
4860 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4862 if (tv && tv->is_track()) {
4863 speed = tv->get_diskstream()->speed();
4866 nframes64_t region_start = (nframes64_t) (clicked_regionview->region()->position() / speed);
4867 nframes64_t region_end = (nframes64_t) (clicked_regionview->region()->last_frame() / speed);
4868 nframes64_t region_length = (nframes64_t) (clicked_regionview->region()->length() / speed);
4870 //drag_info.item = clicked_regionview->get_name_highlight();
4871 drag_info.item = item;
4872 drag_info.motion_callback = &Editor::trim_motion_callback;
4873 drag_info.finished_callback = &Editor::trim_finished_callback;
4875 start_grab (event, trimmer_cursor);
4877 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4878 trim_op = ContentsTrim;
4880 /* These will get overridden for a point trim.*/
4881 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4882 /* closer to start */
4883 trim_op = StartTrim;
4884 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4892 show_verbose_time_cursor(region_start, 10);
4895 show_verbose_time_cursor(region_end, 10);
4898 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4904 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4906 RegionView* rv = clicked_regionview;
4907 nframes64_t frame_delta = 0;
4908 bool left_direction;
4909 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4911 /* snap modifier works differently here..
4912 its' current state has to be passed to the
4913 various trim functions in order to work properly
4917 TimeAxisView* tvp = clicked_axisview;
4918 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4919 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4921 if (tv && tv->is_track()) {
4922 speed = tv->get_diskstream()->speed();
4925 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4926 left_direction = true;
4928 left_direction = false;
4932 snap_to (drag_info.current_pointer_frame);
4935 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4939 if (drag_info.first_move) {
4945 trim_type = "Region start trim";
4948 trim_type = "Region end trim";
4951 trim_type = "Region content trim";
4955 begin_reversible_command (trim_type);
4957 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4958 (*i)->fake_set_opaque(false);
4959 (*i)->region()->freeze ();
4961 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4963 arv->temporarily_hide_envelope ();
4965 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4966 insert_result = motion_frozen_playlists.insert (pl);
4967 if (insert_result.second) {
4968 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4974 if (left_direction) {
4975 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4977 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4982 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4985 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4986 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4992 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes64_t) (rv->region()->last_frame()/speed))) {
4995 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4996 single_end_trim (**i, frame_delta, left_direction, obey_snap);
5003 bool swap_direction = false;
5005 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
5006 swap_direction = true;
5009 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5010 i != selection->regions.by_layer().end(); ++i)
5012 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
5020 show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
5023 show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
5026 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5030 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5031 drag_info.first_move = false;
5035 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
5037 boost::shared_ptr<Region> region (rv.region());
5039 if (region->locked()) {
5043 nframes64_t new_bound;
5046 TimeAxisView* tvp = clicked_axisview;
5047 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
5049 if (tv && tv->is_track()) {
5050 speed = tv->get_diskstream()->speed();
5053 if (left_direction) {
5054 if (swap_direction) {
5055 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
5057 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
5060 if (swap_direction) {
5061 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
5063 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
5068 snap_to (new_bound);
5070 region->trim_start ((nframes64_t) (new_bound * speed), this);
5071 rv.region_changed (StartChanged);
5075 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
5077 boost::shared_ptr<Region> region (rv.region());
5079 if (region->locked()) {
5083 nframes64_t new_bound;
5086 TimeAxisView* tvp = clicked_axisview;
5087 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
5089 if (tv && tv->is_track()) {
5090 speed = tv->get_diskstream()->speed();
5093 if (left_direction) {
5094 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
5096 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
5100 snap_to (new_bound, (left_direction ? 0 : 1));
5103 region->trim_front ((nframes64_t) (new_bound * speed), this);
5105 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
5109 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
5111 boost::shared_ptr<Region> region (rv.region());
5113 if (region->locked()) {
5117 nframes64_t new_bound;
5120 TimeAxisView* tvp = clicked_axisview;
5121 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
5123 if (tv && tv->is_track()) {
5124 speed = tv->get_diskstream()->speed();
5127 if (left_direction) {
5128 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
5130 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
5134 snap_to (new_bound);
5136 region->trim_end ((nframes64_t) (new_bound * speed), this);
5137 rv.region_changed (LengthChanged);
5141 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
5143 if (!drag_info.first_move) {
5144 trim_motion_callback (item, event);
5146 if (!selection->selected (clicked_regionview)) {
5147 thaw_region_after_trim (*clicked_regionview);
5150 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5151 i != selection->regions.by_layer().end(); ++i)
5153 thaw_region_after_trim (**i);
5154 (*i)->fake_set_opaque (true);
5158 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
5160 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
5163 motion_frozen_playlists.clear ();
5165 commit_reversible_command();
5167 /* no mouse movement */
5173 Editor::point_trim (GdkEvent* event)
5175 RegionView* rv = clicked_regionview;
5176 nframes64_t new_bound = drag_info.current_pointer_frame;
5178 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5179 snap_to (new_bound);
5182 /* Choose action dependant on which button was pressed */
5183 switch (event->button.button) {
5185 trim_op = StartTrim;
5186 begin_reversible_command (_("Start point trim"));
5188 if (selection->selected (rv)) {
5190 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5191 i != selection->regions.by_layer().end(); ++i)
5193 if (!(*i)->region()->locked()) {
5194 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5195 XMLNode &before = pl->get_state();
5196 (*i)->region()->trim_front (new_bound, this);
5197 XMLNode &after = pl->get_state();
5198 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5204 if (!rv->region()->locked()) {
5205 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5206 XMLNode &before = pl->get_state();
5207 rv->region()->trim_front (new_bound, this);
5208 XMLNode &after = pl->get_state();
5209 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5213 commit_reversible_command();
5218 begin_reversible_command (_("End point trim"));
5220 if (selection->selected (rv)) {
5222 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
5224 if (!(*i)->region()->locked()) {
5225 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5226 XMLNode &before = pl->get_state();
5227 (*i)->region()->trim_end (new_bound, this);
5228 XMLNode &after = pl->get_state();
5229 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5235 if (!rv->region()->locked()) {
5236 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5237 XMLNode &before = pl->get_state();
5238 rv->region()->trim_end (new_bound, this);
5239 XMLNode &after = pl->get_state();
5240 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
5244 commit_reversible_command();
5253 Editor::thaw_region_after_trim (RegionView& rv)
5255 boost::shared_ptr<Region> region (rv.region());
5257 if (region->locked()) {
5261 region->thaw (_("trimmed region"));
5262 XMLNode &after = region->playlist()->get_state();
5263 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
5265 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
5267 arv->unhide_envelope ();
5271 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
5276 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
5277 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
5281 Location* location = find_location_from_marker (marker, is_start);
5282 location->set_hidden (true, this);
5287 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
5293 drag_info.item = item;
5294 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
5295 drag_info.finished_callback = &Editor::end_range_markerbar_op;
5297 range_marker_op = op;
5299 if (!temp_location) {
5300 temp_location = new Location;
5304 case CreateRangeMarker:
5305 case CreateTransportMarker:
5306 case CreateCDMarker:
5308 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
5309 drag_info.copy = true;
5311 drag_info.copy = false;
5313 start_grab (event, selector_cursor);
5317 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5322 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5324 nframes64_t start = 0;
5325 nframes64_t end = 0;
5326 ArdourCanvas::SimpleRect *crect;
5328 switch (range_marker_op) {
5329 case CreateRangeMarker:
5330 crect = range_bar_drag_rect;
5332 case CreateTransportMarker:
5333 crect = transport_bar_drag_rect;
5335 case CreateCDMarker:
5336 crect = cd_marker_bar_drag_rect;
5339 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
5344 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5345 snap_to (drag_info.current_pointer_frame);
5348 /* only alter selection if the current frame is
5349 different from the last frame position.
5352 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5354 switch (range_marker_op) {
5355 case CreateRangeMarker:
5356 case CreateTransportMarker:
5357 case CreateCDMarker:
5358 if (drag_info.first_move) {
5359 snap_to (drag_info.grab_frame);
5362 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5363 start = drag_info.current_pointer_frame;
5364 end = drag_info.grab_frame;
5366 end = drag_info.current_pointer_frame;
5367 start = drag_info.grab_frame;
5370 /* first drag: Either add to the selection
5371 or create a new selection.
5374 if (drag_info.first_move) {
5376 temp_location->set (start, end);
5380 update_marker_drag_item (temp_location);
5381 range_marker_drag_rect->show();
5382 //range_marker_drag_rect->raise_to_top();
5388 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
5389 start_canvas_autoscroll (1, 0);
5393 temp_location->set (start, end);
5395 double x1 = frame_to_pixel (start);
5396 double x2 = frame_to_pixel (end);
5397 crect->property_x1() = x1;
5398 crect->property_x2() = x2;
5400 update_marker_drag_item (temp_location);
5403 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5404 drag_info.first_move = false;
5406 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5411 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5413 Location * newloc = 0;
5417 if (!drag_info.first_move) {
5418 drag_range_markerbar_op (item, event);
5420 switch (range_marker_op) {
5421 case CreateRangeMarker:
5422 case CreateCDMarker:
5424 begin_reversible_command (_("new range marker"));
5425 XMLNode &before = session->locations()->get_state();
5426 session->locations()->next_available_name(rangename,"unnamed");
5427 if (range_marker_op == CreateCDMarker) {
5428 flags = Location::IsRangeMarker|Location::IsCDMarker;
5429 cd_marker_bar_drag_rect->hide();
5432 flags = Location::IsRangeMarker;
5433 range_bar_drag_rect->hide();
5435 newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
5436 session->locations()->add (newloc, true);
5437 XMLNode &after = session->locations()->get_state();
5438 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
5439 commit_reversible_command ();
5441 range_marker_drag_rect->hide();
5445 case CreateTransportMarker:
5446 // popup menu to pick loop or punch
5447 new_transport_marker_context_menu (&event->button, item);
5452 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5454 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
5459 start = session->locations()->first_mark_before (drag_info.grab_frame);
5460 end = session->locations()->first_mark_after (drag_info.grab_frame);
5462 if (end == max_frames) {
5463 end = session->current_end_frame ();
5467 start = session->current_start_frame ();
5470 switch (mouse_mode) {
5472 /* find the two markers on either side and then make the selection from it */
5473 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
5477 /* find the two markers on either side of the click and make the range out of it */
5478 selection->set (0, start, end);
5487 stop_canvas_autoscroll ();
5493 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5495 drag_info.item = item;
5496 drag_info.motion_callback = &Editor::drag_mouse_zoom;
5497 drag_info.finished_callback = &Editor::end_mouse_zoom;
5499 start_grab (event, zoom_cursor);
5501 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5505 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5510 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5511 snap_to (drag_info.current_pointer_frame);
5513 if (drag_info.first_move) {
5514 snap_to (drag_info.grab_frame);
5518 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5520 /* base start and end on initial click position */
5521 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5522 start = drag_info.current_pointer_frame;
5523 end = drag_info.grab_frame;
5525 end = drag_info.current_pointer_frame;
5526 start = drag_info.grab_frame;
5531 if (drag_info.first_move) {
5533 zoom_rect->raise_to_top();
5536 reposition_zoom_rect(start, end);
5538 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5539 drag_info.first_move = false;
5541 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5546 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5548 if (!drag_info.first_move) {
5549 drag_mouse_zoom (item, event);
5551 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5552 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
5554 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
5557 temporal_zoom_to_frame (false, drag_info.grab_frame);
5559 temporal_zoom_step (false);
5560 center_screen (drag_info.grab_frame);
5568 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
5570 double x1 = frame_to_pixel (start);
5571 double x2 = frame_to_pixel (end);
5572 double y2 = full_canvas_height - 1.0;
5574 zoom_rect->property_x1() = x1;
5575 zoom_rect->property_y1() = 1.0;
5576 zoom_rect->property_x2() = x2;
5577 zoom_rect->property_y2() = y2;
5581 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5583 drag_info.item = item;
5584 drag_info.motion_callback = &Editor::drag_rubberband_select;
5585 drag_info.finished_callback = &Editor::end_rubberband_select;
5587 start_grab (event, cross_hair_cursor);
5589 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5593 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5600 /* use a bigger drag threshold than the default */
5602 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
5606 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) {
5607 if (drag_info.first_move) {
5608 snap_to (drag_info.grab_frame);
5610 snap_to (drag_info.current_pointer_frame);
5613 /* base start and end on initial click position */
5615 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5616 start = drag_info.current_pointer_frame;
5617 end = drag_info.grab_frame;
5619 end = drag_info.current_pointer_frame;
5620 start = drag_info.grab_frame;
5623 if (drag_info.current_pointer_y < drag_info.grab_y) {
5624 y1 = drag_info.current_pointer_y;
5625 y2 = drag_info.grab_y;
5627 y2 = drag_info.current_pointer_y;
5628 y1 = drag_info.grab_y;
5632 if (start != end || y1 != y2) {
5634 double x1 = frame_to_pixel (start);
5635 double x2 = frame_to_pixel (end);
5637 rubberband_rect->property_x1() = x1;
5638 rubberband_rect->property_y1() = y1;
5639 rubberband_rect->property_x2() = x2;
5640 rubberband_rect->property_y2() = y2;
5642 rubberband_rect->show();
5643 rubberband_rect->raise_to_top();
5645 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5646 drag_info.first_move = false;
5648 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5653 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5655 if (!drag_info.first_move) {
5657 drag_rubberband_select (item, event);
5660 if (drag_info.current_pointer_y < drag_info.grab_y) {
5661 y1 = drag_info.current_pointer_y;
5662 y2 = drag_info.grab_y;
5664 y2 = drag_info.current_pointer_y;
5665 y1 = drag_info.grab_y;
5669 Selection::Operation op = Keyboard::selection_type (event->button.state);
5672 begin_reversible_command (_("rubberband selection"));
5674 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5675 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5677 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5681 commit_reversible_command ();
5685 if (!getenv("ARDOUR_SAE")) {
5686 selection->clear_tracks();
5688 selection->clear_regions();
5689 selection->clear_points ();
5690 selection->clear_lines ();
5693 rubberband_rect->hide();
5698 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5700 using namespace Gtkmm2ext;
5702 ArdourPrompter prompter (false);
5704 prompter.set_prompt (_("Name for region:"));
5705 prompter.set_initial_text (clicked_regionview->region()->name());
5706 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5707 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5708 prompter.show_all ();
5709 switch (prompter.run ()) {
5710 case Gtk::RESPONSE_ACCEPT:
5712 prompter.get_result(str);
5714 clicked_regionview->region()->set_name (str);
5722 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5724 drag_info.item = item;
5725 drag_info.motion_callback = &Editor::time_fx_motion;
5726 drag_info.finished_callback = &Editor::end_time_fx;
5730 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5734 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5736 RegionView* rv = clicked_regionview;
5738 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5739 snap_to (drag_info.current_pointer_frame);
5742 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5746 if (drag_info.current_pointer_frame > rv->region()->position()) {
5747 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5750 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5751 drag_info.first_move = false;
5753 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5757 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5759 clicked_regionview->get_time_axis_view().hide_timestretch ();
5761 if (drag_info.first_move) {
5765 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5766 /* backwards drag of the left edge - not usable */
5770 nframes64_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5772 float percentage = (double) newlen / (double) clicked_regionview->region()->length();
5774 #ifndef USE_RUBBERBAND
5775 // Soundtouch uses percentage / 100 instead of normal (/ 1)
5776 if (clicked_regionview->region()->data_type() == DataType::AUDIO) {
5777 percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5781 begin_reversible_command (_("timestretch"));
5783 // XXX how do timeFX on multiple regions ?
5786 rs.add (clicked_regionview);
5788 if (time_stretch (rs, percentage) == 0) {
5789 session->commit_reversible_command ();
5794 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
5796 /* no brushing without a useful snap setting */
5798 switch (snap_mode) {
5800 return; /* can't work because it allows region to be placed anywhere */
5805 switch (snap_type) {
5813 /* don't brush a copy over the original */
5815 if (pos == rv->region()->position()) {
5819 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
5821 if (rtv == 0 || !rtv->is_track()) {
5825 boost::shared_ptr<Playlist> playlist = rtv->playlist();
5826 double speed = rtv->get_diskstream()->speed();
5828 XMLNode &before = playlist->get_state();
5829 playlist->add_region (RegionFactory::create (rv->region()), (nframes64_t) (pos * speed));
5830 XMLNode &after = playlist->get_state();
5831 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5833 // playlist is frozen, so we have to update manually
5835 playlist->Modified(); /* EMIT SIGNAL */
5839 Editor::track_height_step_timeout ()
5841 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
5842 current_stepping_trackview = 0;