2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #include <pbd/error.h>
29 #include <gtkmm2ext/utils.h>
30 #include <gtkmm2ext/tearoff.h>
31 #include <pbd/memento_command.h>
32 #include <pbd/basename.h>
34 #include "ardour_ui.h"
36 #include "time_axis_view.h"
37 #include "audio_time_axis.h"
38 #include "audio_region_view.h"
39 #include "midi_region_view.h"
41 #include "streamview.h"
42 #include "region_gain_line.h"
43 #include "automation_time_axis.h"
44 #include "control_point.h"
47 #include "selection.h"
50 #include "rgb_macros.h"
51 #include "control_point_dialog.h"
53 #include <ardour/types.h>
54 #include <ardour/profile.h>
55 #include <ardour/route.h>
56 #include <ardour/audio_track.h>
57 #include <ardour/audio_diskstream.h>
58 #include <ardour/midi_diskstream.h>
59 #include <ardour/playlist.h>
60 #include <ardour/audioplaylist.h>
61 #include <ardour/audioregion.h>
62 #include <ardour/midi_region.h>
63 #include <ardour/dB.h>
64 #include <ardour/utils.h>
65 #include <ardour/region_factory.h>
66 #include <ardour/source_factory.h>
73 using namespace ARDOUR;
77 using namespace Editing;
79 const static double ZERO_GAIN_FRACTION = gain_to_slider_position(dB_to_coefficient(0.0));
82 Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
86 Gdk::ModifierType mask;
87 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
88 Glib::RefPtr<const Gdk::Window> pointer_window;
94 pointer_window = canvas_window->get_pointer (x, y, mask);
96 if (pointer_window == track_canvas->get_bin_window()) {
99 in_track_canvas = true;
102 in_track_canvas = false;
107 event.type = GDK_BUTTON_RELEASE;
111 where = event_frame (&event, 0, 0);
116 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy) const
130 switch (event->type) {
131 case GDK_BUTTON_RELEASE:
132 case GDK_BUTTON_PRESS:
133 case GDK_2BUTTON_PRESS:
134 case GDK_3BUTTON_PRESS:
136 *pcx = event->button.x;
137 *pcy = event->button.y;
138 _trackview_group->w2i(*pcx, *pcy);
140 case GDK_MOTION_NOTIFY:
142 *pcx = event->motion.x;
143 *pcy = event->motion.y;
144 _trackview_group->w2i(*pcx, *pcy);
146 case GDK_ENTER_NOTIFY:
147 case GDK_LEAVE_NOTIFY:
148 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
151 case GDK_KEY_RELEASE:
152 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
155 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
159 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
160 position is negative (as can be the case with motion events in particular),
161 the frame location is always positive.
164 return pixel_to_frame (*pcx);
168 Editor::mouse_mode_toggled (MouseMode m)
170 if (ignore_mouse_mode_toggle) {
176 if (mouse_select_button.get_active()) {
182 if (mouse_move_button.get_active()) {
188 if (mouse_gain_button.get_active()) {
194 if (mouse_zoom_button.get_active()) {
200 if (mouse_timefx_button.get_active()) {
206 if (mouse_audition_button.get_active()) {
212 if (mouse_note_button.get_active()) {
223 Editor::which_grabber_cursor ()
225 switch (_edit_point) {
227 return grabber_edit_point_cursor;
232 return grabber_cursor;
236 Editor::set_canvas_cursor ()
238 switch (mouse_mode) {
240 current_canvas_cursor = selector_cursor;
244 current_canvas_cursor = which_grabber_cursor();
248 current_canvas_cursor = cross_hair_cursor;
252 current_canvas_cursor = zoom_cursor;
256 current_canvas_cursor = time_fx_cursor; // just use playhead
260 current_canvas_cursor = speaker_cursor;
264 set_midi_edit_cursor (current_midi_edit_mode());
269 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
274 Editor::set_mouse_mode (MouseMode m, bool force)
276 if (drag_info.item) {
280 if (!force && m == mouse_mode) {
288 if (mouse_mode != MouseRange) {
290 /* in all modes except range, hide the range selection,
291 show the object (region) selection.
294 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
295 (*i)->set_should_show_selection (true);
297 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
298 (*i)->hide_selection ();
304 in range mode,show the range selection.
307 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
308 if ((*i)->get_selected()) {
309 (*i)->show_selection (selection->time);
314 /* XXX the hack of unsetting all other buttons should go
315 away once GTK2 allows us to use regular radio buttons drawn like
316 normal buttons, rather than my silly GroupedButton hack.
319 ignore_mouse_mode_toggle = true;
321 switch (mouse_mode) {
323 mouse_select_button.set_active (true);
327 mouse_move_button.set_active (true);
331 mouse_gain_button.set_active (true);
335 mouse_zoom_button.set_active (true);
339 mouse_timefx_button.set_active (true);
343 mouse_audition_button.set_active (true);
347 mouse_note_button.set_active (true);
348 set_midi_edit_cursor (current_midi_edit_mode());
352 if (midi_tools_tearoff) {
353 if (mouse_mode == MouseNote) {
354 midi_tools_tearoff->show();
356 midi_tools_tearoff->hide();
360 ignore_mouse_mode_toggle = false;
362 set_canvas_cursor ();
366 Editor::step_mouse_mode (bool next)
368 switch (current_mouse_mode()) {
371 if (Profile->get_sae()) {
372 set_mouse_mode (MouseZoom);
374 set_mouse_mode (MouseRange);
377 set_mouse_mode (MouseTimeFX);
382 if (next) set_mouse_mode (MouseZoom);
383 else set_mouse_mode (MouseObject);
388 if (Profile->get_sae()) {
389 set_mouse_mode (MouseTimeFX);
391 set_mouse_mode (MouseGain);
394 if (Profile->get_sae()) {
395 set_mouse_mode (MouseObject);
397 set_mouse_mode (MouseRange);
403 if (next) set_mouse_mode (MouseTimeFX);
404 else set_mouse_mode (MouseZoom);
409 set_mouse_mode (MouseAudition);
411 if (Profile->get_sae()) {
412 set_mouse_mode (MouseZoom);
414 set_mouse_mode (MouseGain);
420 if (next) set_mouse_mode (MouseObject);
421 else set_mouse_mode (MouseTimeFX);
425 if (next) set_mouse_mode (MouseObject);
426 else set_mouse_mode (MouseAudition);
432 Editor::midi_edit_mode_toggled (MidiEditMode m)
434 if (ignore_midi_edit_mode_toggle) {
440 if (midi_tool_pencil_button.get_active())
441 set_midi_edit_mode (m);
445 if (midi_tool_select_button.get_active())
446 set_midi_edit_mode (m);
450 if (midi_tool_resize_button.get_active())
451 set_midi_edit_mode (m);
455 if (midi_tool_erase_button.get_active())
456 set_midi_edit_mode (m);
463 set_midi_edit_cursor(m);
468 Editor::set_midi_edit_mode (MidiEditMode m, bool force)
470 if (drag_info.item) {
474 if (!force && m == midi_edit_mode) {
482 ignore_midi_edit_mode_toggle = true;
484 switch (midi_edit_mode) {
486 midi_tool_pencil_button.set_active (true);
490 midi_tool_select_button.set_active (true);
494 midi_tool_resize_button.set_active (true);
498 midi_tool_erase_button.set_active (true);
502 ignore_midi_edit_mode_toggle = false;
504 set_midi_edit_cursor (current_midi_edit_mode());
507 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
512 Editor::set_midi_edit_cursor (MidiEditMode m)
514 switch (midi_edit_mode) {
516 current_canvas_cursor = midi_pencil_cursor;
520 current_canvas_cursor = midi_select_cursor;
524 current_canvas_cursor = midi_resize_cursor;
528 current_canvas_cursor = midi_erase_cursor;
534 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
536 /* in object/audition/timefx/gain-automation mode,
537 any button press sets the selection if the object
538 can be selected. this is a bit of hack, because
539 we want to avoid this if the mouse operation is a
542 note: not dbl-click or triple-click
545 if (((mouse_mode != MouseObject) &&
546 (mouse_mode != MouseAudition || item_type != RegionItem) &&
547 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
548 (mouse_mode != MouseGain) &&
549 (mouse_mode != MouseRange)) ||
551 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
556 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
558 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
560 /* almost no selection action on modified button-2 or button-3 events */
562 if (item_type != RegionItem && event->button.button != 2) {
568 Selection::Operation op = Keyboard::selection_type (event->button.state);
569 bool press = (event->type == GDK_BUTTON_PRESS);
571 // begin_reversible_command (_("select on click"));
575 if (mouse_mode != MouseRange) {
576 set_selected_regionview_from_click (press, op, true);
577 } else if (event->type == GDK_BUTTON_PRESS) {
578 set_selected_track_as_side_effect ();
582 case RegionViewNameHighlight:
584 if (mouse_mode != MouseRange) {
585 set_selected_regionview_from_click (press, op, true);
586 } else if (event->type == GDK_BUTTON_PRESS) {
587 set_selected_track_as_side_effect ();
592 case FadeInHandleItem:
594 case FadeOutHandleItem:
596 if (mouse_mode != MouseRange) {
597 set_selected_regionview_from_click (press, op, true);
598 } else if (event->type == GDK_BUTTON_PRESS) {
599 set_selected_track_as_side_effect ();
603 case ControlPointItem:
604 set_selected_track_as_side_effect ();
605 if (mouse_mode != MouseRange) {
606 set_selected_control_point_from_click (op, false);
611 /* for context click or range selection, select track */
612 if (event->button.button == 3) {
613 set_selected_track_as_side_effect ();
614 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
615 set_selected_track_as_side_effect ();
619 case AutomationTrackItem:
620 set_selected_track_as_side_effect (true);
629 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
631 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
634 Glib::RefPtr<const Gdk::Window> pointer_window;
637 Gdk::ModifierType mask;
639 pointer_window = canvas_window->get_pointer (x, y, mask);
641 if (pointer_window == track_canvas->get_bin_window()) {
642 track_canvas->window_to_world (x, y, wx, wy);
643 allow_vertical_scroll = true;
645 allow_vertical_scroll = false;
649 track_canvas->grab_focus();
651 if (session && session->actively_recording()) {
655 button_selection (item, event, item_type);
657 if (drag_info.item == 0 &&
658 (Keyboard::is_delete_event (&event->button) ||
659 Keyboard::is_context_menu_event (&event->button) ||
660 Keyboard::is_edit_event (&event->button))) {
662 /* handled by button release */
666 switch (event->button.button) {
669 if (event->type == GDK_BUTTON_PRESS) {
671 if (drag_info.item) {
672 drag_info.item->ungrab (event->button.time);
675 /* single mouse clicks on any of these item types operate
676 independent of mouse mode, mostly because they are
677 not on the main track canvas or because we want
682 case PlayheadCursorItem:
683 start_cursor_grab (item, event);
687 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
688 hide_marker (item, event);
690 start_marker_grab (item, event);
694 case TempoMarkerItem:
695 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
696 start_tempo_marker_copy_grab (item, event);
698 start_tempo_marker_grab (item, event);
702 case MeterMarkerItem:
703 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
704 start_meter_marker_copy_grab (item, event);
706 start_meter_marker_grab (item, event);
713 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
714 start_cursor_grab_no_stop(&playhead_cursor->canvas_item, event);
720 case RangeMarkerBarItem:
721 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
722 start_cursor_grab_no_stop(&playhead_cursor->canvas_item, event);
724 start_range_markerbar_op (item, event, CreateRangeMarker);
729 case CdMarkerBarItem:
730 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
731 start_cursor_grab_no_stop(&playhead_cursor->canvas_item, event);
733 start_range_markerbar_op (item, event, CreateCDMarker);
738 case TransportMarkerBarItem:
739 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
740 start_cursor_grab_no_stop(&playhead_cursor->canvas_item, event);
742 start_range_markerbar_op (item, event, CreateTransportMarker);
752 switch (mouse_mode) {
755 case StartSelectionTrimItem:
756 start_selection_op (item, event, SelectionStartTrim);
759 case EndSelectionTrimItem:
760 start_selection_op (item, event, SelectionEndTrim);
764 if (Keyboard::modifier_state_contains
765 (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) {
766 // contains and not equals because I can't use alt as a modifier alone.
767 start_selection_grab (item, event);
768 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
769 /* grab selection for moving */
770 start_selection_op (item, event, SelectionMove);
772 /* this was debated, but decided the more common action was to
773 make a new selection */
774 start_selection_op (item, event, CreateSelection);
779 start_selection_op (item, event, CreateSelection);
785 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
786 event->type == GDK_BUTTON_PRESS) {
788 start_rubberband_select (item, event);
790 } else if (event->type == GDK_BUTTON_PRESS) {
793 case FadeInHandleItem:
794 start_fade_in_grab (item, event);
797 case FadeOutHandleItem:
798 start_fade_out_grab (item, event);
802 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
803 start_region_copy_grab (item, event);
804 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
805 start_region_brush_grab (item, event);
807 start_region_grab (item, event);
811 case RegionViewNameHighlight:
812 start_trim (item, event);
817 /* rename happens on edit clicks */
818 start_trim (clicked_regionview->get_name_highlight(), event);
822 case ControlPointItem:
823 start_control_point_grab (item, event);
827 case AutomationLineItem:
828 start_line_grab_from_line (item, event);
833 case AutomationTrackItem:
834 start_rubberband_select (item, event);
838 case ImageFrameHandleStartItem:
839 imageframe_start_handle_op(item, event) ;
842 case ImageFrameHandleEndItem:
843 imageframe_end_handle_op(item, event) ;
846 case MarkerViewHandleStartItem:
847 markerview_item_start_handle_op(item, event) ;
850 case MarkerViewHandleEndItem:
851 markerview_item_end_handle_op(item, event) ;
855 start_markerview_grab(item, event) ;
858 start_imageframe_grab(item, event) ;
876 /* start a grab so that if we finish after moving
877 we can tell what happened.
879 drag_info.item = item;
880 drag_info.motion_callback = &Editor::region_gain_motion_callback;
881 drag_info.finished_callback = 0;
882 start_grab (event, current_canvas_cursor);
886 start_line_grab_from_line (item, event);
889 case ControlPointItem:
890 start_control_point_grab (item, event);
901 case ControlPointItem:
902 start_control_point_grab (item, event);
905 case AutomationLineItem:
906 start_line_grab_from_line (item, event);
910 // XXX need automation mode to identify which
912 // start_line_grab_from_regionview (item, event);
922 if (event->type == GDK_BUTTON_PRESS) {
923 start_mouse_zoom (item, event);
930 if (item_type == RegionItem) {
931 start_time_fx (item, event);
938 scrub_reverse_distance = 0;
939 last_scrub_x = event->button.x;
940 scrubbing_direction = 0;
941 track_canvas->get_window()->set_cursor (*transparent_cursor);
942 /* rest handled in motion & release */
946 start_create_region_grab (item, event);
955 switch (mouse_mode) {
957 if (event->type == GDK_BUTTON_PRESS) {
960 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
961 start_region_copy_grab (item, event);
963 start_region_grab (item, event);
967 case ControlPointItem:
968 start_control_point_grab (item, event);
979 case RegionViewNameHighlight:
980 start_trim (item, event);
985 start_trim (clicked_regionview->get_name_highlight(), event);
996 if (event->type == GDK_BUTTON_PRESS) {
997 /* relax till release */
1004 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1005 temporal_zoom_session();
1007 temporal_zoom_to_frame (true, event_frame(event));
1030 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1032 nframes64_t where = event_frame (event, 0, 0);
1033 AutomationTimeAxisView* atv = 0;
1035 /* no action if we're recording */
1037 if (session && session->actively_recording()) {
1041 /* first, see if we're finishing a drag ... */
1043 if (drag_info.item) {
1044 if (end_grab (item, event)) {
1045 /* grab dragged, so do nothing else */
1050 button_selection (item, event, item_type);
1052 /* edit events get handled here */
1054 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
1055 switch (item_type) {
1060 case TempoMarkerItem:
1061 edit_tempo_marker (item);
1064 case MeterMarkerItem:
1065 edit_meter_marker (item);
1068 case RegionViewName:
1069 if (clicked_regionview->name_active()) {
1070 return mouse_rename_region (item, event);
1074 case ControlPointItem:
1075 edit_control_point (item);
1084 /* context menu events get handled here */
1086 if (Keyboard::is_context_menu_event (&event->button)) {
1088 if (drag_info.item == 0) {
1090 /* no matter which button pops up the context menu, tell the menu
1091 widget to use button 1 to drive menu selection.
1094 switch (item_type) {
1096 case FadeInHandleItem:
1098 case FadeOutHandleItem:
1099 popup_fade_context_menu (1, event->button.time, item, item_type);
1103 popup_track_context_menu (1, event->button.time, item_type, false, where);
1107 case RegionViewNameHighlight:
1108 case RegionViewName:
1109 popup_track_context_menu (1, event->button.time, item_type, false, where);
1113 popup_track_context_menu (1, event->button.time, item_type, true, where);
1116 case AutomationTrackItem:
1117 popup_track_context_menu (1, event->button.time, item_type, false, where);
1121 case RangeMarkerBarItem:
1122 case TransportMarkerBarItem:
1123 case CdMarkerBarItem:
1126 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
1130 marker_context_menu (&event->button, item);
1133 case TempoMarkerItem:
1134 tm_marker_context_menu (&event->button, item);
1137 case MeterMarkerItem:
1138 tm_marker_context_menu (&event->button, item);
1141 case CrossfadeViewItem:
1142 popup_track_context_menu (1, event->button.time, item_type, false, where);
1146 case ImageFrameItem:
1147 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1149 case ImageFrameTimeAxisItem:
1150 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1152 case MarkerViewItem:
1153 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1155 case MarkerTimeAxisItem:
1156 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1168 /* delete events get handled here */
1170 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
1172 switch (item_type) {
1173 case TempoMarkerItem:
1174 remove_tempo_marker (item);
1177 case MeterMarkerItem:
1178 remove_meter_marker (item);
1182 remove_marker (*item, event);
1186 if (mouse_mode == MouseObject) {
1187 remove_clicked_region ();
1191 case ControlPointItem:
1192 if (mouse_mode == MouseGain) {
1193 remove_gain_control_point (item, event);
1195 remove_control_point (item, event);
1205 switch (event->button.button) {
1208 switch (item_type) {
1209 /* see comments in button_press_handler */
1210 case PlayheadCursorItem:
1213 case AutomationLineItem:
1214 case StartSelectionTrimItem:
1215 case EndSelectionTrimItem:
1219 if (!_dragging_playhead) {
1220 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1221 snap_to (where, 0, true);
1223 mouse_add_new_marker (where);
1227 case CdMarkerBarItem:
1228 if (!_dragging_playhead) {
1229 // if we get here then a dragged range wasn't done
1230 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1231 snap_to (where, 0, true);
1233 mouse_add_new_marker (where, true);
1238 if (!_dragging_playhead) {
1239 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1242 mouse_add_new_tempo_event (where);
1247 if (!_dragging_playhead) {
1248 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1257 switch (mouse_mode) {
1259 switch (item_type) {
1260 case AutomationTrackItem:
1261 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_routeview);
1263 atv->add_automation_event (item, event, where, event->button.y);
1275 // Gain only makes sense for audio regions
1277 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1281 switch (item_type) {
1283 /* check that we didn't drag before releasing, since
1284 its really annoying to create new control
1285 points when doing this.
1287 if (drag_info.first_move) {
1288 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1293 case AutomationTrackItem:
1294 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1295 add_automation_event (item, event, where, event->button.y);
1305 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1306 if (scrubbing_direction == 0) {
1307 /* no drag, just a click */
1308 switch (item_type) {
1310 play_selected_region ();
1316 /* make sure we stop */
1317 session->request_transport_speed (0.0);
1331 switch (mouse_mode) {
1334 switch (item_type) {
1336 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1338 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1341 // Button2 click is unused
1354 // x_style_paste (where, 1.0);
1374 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1380 if (last_item_entered != item) {
1381 last_item_entered = item;
1382 last_item_entered_n = 0;
1385 switch (item_type) {
1386 case ControlPointItem:
1387 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1388 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1389 cp->set_visible (true);
1393 at_y = cp->get_y ();
1394 cp->item()->i2w (at_x, at_y);
1398 fraction = 1.0 - (cp->get_y() / cp->line().height());
1400 if (is_drawable() && !_scrubbing) {
1401 track_canvas->get_window()->set_cursor (*fader_cursor);
1404 last_item_entered_n++;
1405 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1406 if (last_item_entered_n < 10) {
1407 show_verbose_canvas_cursor ();
1413 if (mouse_mode == MouseGain) {
1414 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1416 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1417 if (is_drawable()) {
1418 track_canvas->get_window()->set_cursor (*fader_cursor);
1423 case AutomationLineItem:
1424 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1426 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1428 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1430 if (is_drawable()) {
1431 track_canvas->get_window()->set_cursor (*fader_cursor);
1436 case RegionViewNameHighlight:
1437 if (is_drawable() && mouse_mode == MouseObject) {
1438 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1442 case StartSelectionTrimItem:
1443 case EndSelectionTrimItem:
1446 case ImageFrameHandleStartItem:
1447 case ImageFrameHandleEndItem:
1448 case MarkerViewHandleStartItem:
1449 case MarkerViewHandleEndItem:
1452 if (is_drawable()) {
1453 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1457 case PlayheadCursorItem:
1458 if (is_drawable()) {
1459 switch (_edit_point) {
1461 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1464 track_canvas->get_window()->set_cursor (*grabber_cursor);
1470 case RegionViewName:
1472 /* when the name is not an active item, the entire name highlight is for trimming */
1474 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1475 if (mouse_mode == MouseObject && is_drawable()) {
1476 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1482 case AutomationTrackItem:
1483 if (is_drawable()) {
1484 Gdk::Cursor *cursor;
1485 switch (mouse_mode) {
1487 cursor = selector_cursor;
1490 cursor = zoom_cursor;
1493 cursor = cross_hair_cursor;
1497 track_canvas->get_window()->set_cursor (*cursor);
1499 AutomationTimeAxisView* atv;
1500 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1501 clear_entered_track = false;
1502 set_entered_track (atv);
1508 case RangeMarkerBarItem:
1509 case TransportMarkerBarItem:
1510 case CdMarkerBarItem:
1513 if (is_drawable()) {
1514 track_canvas->get_window()->set_cursor (*timebar_cursor);
1519 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1522 entered_marker = marker;
1523 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1525 case MeterMarkerItem:
1526 case TempoMarkerItem:
1527 if (is_drawable()) {
1528 track_canvas->get_window()->set_cursor (*timebar_cursor);
1531 case FadeInHandleItem:
1532 case FadeOutHandleItem:
1533 if (mouse_mode == MouseObject) {
1534 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1536 rect->property_fill_color_rgba() = 0;
1537 rect->property_outline_pixels() = 1;
1546 /* second pass to handle entered track status in a comprehensible way.
1549 switch (item_type) {
1551 case AutomationLineItem:
1552 case ControlPointItem:
1553 /* these do not affect the current entered track state */
1554 clear_entered_track = false;
1557 case AutomationTrackItem:
1558 /* handled above already */
1562 set_entered_track (0);
1570 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1579 switch (item_type) {
1580 case ControlPointItem:
1581 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1582 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1583 if (cp->line().npoints() > 1 && !cp->selected()) {
1584 cp->set_visible (false);
1588 if (is_drawable()) {
1589 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1592 hide_verbose_canvas_cursor ();
1595 case RegionViewNameHighlight:
1596 case StartSelectionTrimItem:
1597 case EndSelectionTrimItem:
1598 case PlayheadCursorItem:
1601 case ImageFrameHandleStartItem:
1602 case ImageFrameHandleEndItem:
1603 case MarkerViewHandleStartItem:
1604 case MarkerViewHandleEndItem:
1607 if (is_drawable()) {
1608 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1613 case AutomationLineItem:
1614 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1616 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1618 line->property_fill_color_rgba() = al->get_line_color();
1620 if (is_drawable()) {
1621 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1625 case RegionViewName:
1626 /* see enter_handler() for notes */
1627 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1628 if (is_drawable() && mouse_mode == MouseObject) {
1629 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1634 case RangeMarkerBarItem:
1635 case TransportMarkerBarItem:
1636 case CdMarkerBarItem:
1640 if (is_drawable()) {
1641 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1646 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1650 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1651 location_flags_changed (loc, this);
1654 case MeterMarkerItem:
1655 case TempoMarkerItem:
1657 if (is_drawable()) {
1658 track_canvas->get_window()->set_cursor (*timebar_cursor);
1663 case FadeInHandleItem:
1664 case FadeOutHandleItem:
1665 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1667 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1669 rect->property_fill_color_rgba() = rv->get_fill_color();
1670 rect->property_outline_pixels() = 0;
1675 case AutomationTrackItem:
1676 if (is_drawable()) {
1677 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1678 clear_entered_track = true;
1679 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1691 Editor::left_automation_track ()
1693 if (clear_entered_track) {
1694 set_entered_track (0);
1695 clear_entered_track = false;
1705 if (scrubbing_direction == 0) {
1707 session->request_locate (drag_info.current_pointer_frame, false);
1708 session->request_transport_speed (0.1);
1709 scrubbing_direction = 1;
1713 if (last_scrub_x > drag_info.current_pointer_x) {
1715 /* pointer moved to the left */
1717 if (scrubbing_direction > 0) {
1719 /* we reversed direction to go backwards */
1722 scrub_reverse_distance += (int) (last_scrub_x - drag_info.current_pointer_x);
1726 /* still moving to the left (backwards) */
1728 scrub_reversals = 0;
1729 scrub_reverse_distance = 0;
1731 delta = 0.01 * (last_scrub_x - drag_info.current_pointer_x);
1732 session->request_transport_speed (session->transport_speed() - delta);
1736 /* pointer moved to the right */
1738 if (scrubbing_direction < 0) {
1739 /* we reversed direction to go forward */
1742 scrub_reverse_distance += (int) (drag_info.current_pointer_x - last_scrub_x);
1745 /* still moving to the right */
1747 scrub_reversals = 0;
1748 scrub_reverse_distance = 0;
1750 delta = 0.01 * (drag_info.current_pointer_x - last_scrub_x);
1751 session->request_transport_speed (session->transport_speed() + delta);
1755 /* if there have been more than 2 opposite motion moves detected, or one that moves
1756 back more than 10 pixels, reverse direction
1759 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1761 if (scrubbing_direction > 0) {
1762 /* was forwards, go backwards */
1763 session->request_transport_speed (-0.1);
1764 scrubbing_direction = -1;
1766 /* was backwards, go forwards */
1767 session->request_transport_speed (0.1);
1768 scrubbing_direction = 1;
1771 scrub_reverse_distance = 0;
1772 scrub_reversals = 0;
1776 last_scrub_x = drag_info.current_pointer_x;
1780 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1782 if (event->motion.is_hint) {
1785 /* We call this so that MOTION_NOTIFY events continue to be
1786 delivered to the canvas. We need to do this because we set
1787 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1788 the density of the events, at the expense of a round-trip
1789 to the server. Given that this will mostly occur on cases
1790 where DISPLAY = :0.0, and given the cost of what the motion
1791 event might do, its a good tradeoff.
1794 track_canvas->get_pointer (x, y);
1797 if (current_stepping_trackview) {
1798 /* don't keep the persistent stepped trackview if the mouse moves */
1799 current_stepping_trackview = 0;
1800 step_timeout.disconnect ();
1803 if (session && session->actively_recording()) {
1804 /* Sorry. no dragging stuff around while we record */
1808 drag_info.item_type = item_type;
1809 drag_info.last_pointer_x = drag_info.current_pointer_x;
1810 drag_info.last_pointer_y = drag_info.current_pointer_y;
1811 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1812 &drag_info.current_pointer_y);
1815 switch (mouse_mode) {
1827 if (!from_autoscroll && drag_info.item) {
1828 /* item != 0 is the best test i can think of for dragging.
1830 if (!drag_info.move_threshold_passed) {
1832 bool x_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1833 bool y_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1835 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1837 // and change the initial grab loc/frame if this drag info wants us to
1839 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1840 drag_info.grab_frame = drag_info.current_pointer_frame;
1841 drag_info.grab_x = drag_info.current_pointer_x;
1842 drag_info.grab_y = drag_info.current_pointer_y;
1843 drag_info.last_pointer_frame = drag_info.grab_frame;
1844 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1849 switch (item_type) {
1850 case PlayheadCursorItem:
1852 case ControlPointItem:
1856 case RangeMarkerBarItem:
1857 case TransportMarkerBarItem:
1858 case CdMarkerBarItem:
1859 case TempoMarkerItem:
1860 case MeterMarkerItem:
1861 case RegionViewNameHighlight:
1862 case StartSelectionTrimItem:
1863 case EndSelectionTrimItem:
1866 case AutomationLineItem:
1867 case FadeInHandleItem:
1868 case FadeOutHandleItem:
1871 case ImageFrameHandleStartItem:
1872 case ImageFrameHandleEndItem:
1873 case MarkerViewHandleStartItem:
1874 case MarkerViewHandleEndItem:
1877 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1878 (event->motion.state & Gdk::BUTTON2_MASK))) {
1879 if (!from_autoscroll) {
1880 maybe_autoscroll_horizontally (&event->motion);
1882 if (drag_info.motion_callback) {
1883 (this->*(drag_info.motion_callback)) (item, event);
1893 switch (mouse_mode) {
1895 if (item_type == RegionItem) {
1896 if (drag_info.item && drag_info.motion_callback) {
1897 (this->*(drag_info.motion_callback)) (item, event);
1908 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1909 (event->motion.state & GDK_BUTTON2_MASK))) {
1910 if (!from_autoscroll) {
1911 maybe_autoscroll (&event->motion);
1913 if (drag_info.motion_callback) {
1914 (this->*(drag_info.motion_callback)) (item, event);
1926 track_canvas_motion (event);
1927 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1935 Editor::break_drag ()
1937 stop_canvas_autoscroll ();
1938 hide_verbose_canvas_cursor ();
1940 if (drag_info.item) {
1941 drag_info.item->ungrab (0);
1943 /* put it back where it came from */
1948 drag_info.item->i2w (cxw, cyw);
1949 drag_info.item->move (drag_info.original_x - cxw, drag_info.original_y - cyw);
1956 Editor::finalize_drag ()
1959 drag_info.copy = false;
1960 drag_info.motion_callback = 0;
1961 drag_info.finished_callback = 0;
1962 drag_info.dest_trackview = 0;
1963 drag_info.source_trackview = 0;
1964 drag_info.last_frame_position = 0;
1965 drag_info.grab_frame = 0;
1966 drag_info.last_pointer_frame = 0;
1967 drag_info.current_pointer_frame = 0;
1968 drag_info.brushing = false;
1969 range_marker_drag_rect->hide();
1970 drag_info.clear_copied_locations ();
1974 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1976 if (drag_info.item == 0) {
1977 fatal << _("programming error: start_grab called without drag item") << endmsg;
1983 cursor = which_grabber_cursor ();
1986 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1988 if (Keyboard::is_button2_event (&event->button)) {
1989 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
1990 drag_info.y_constrained = true;
1991 drag_info.x_constrained = false;
1993 drag_info.y_constrained = false;
1994 drag_info.x_constrained = true;
1997 drag_info.x_constrained = false;
1998 drag_info.y_constrained = false;
2001 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
2002 drag_info.last_pointer_frame = drag_info.grab_frame;
2003 drag_info.current_pointer_frame = drag_info.grab_frame;
2004 drag_info.current_pointer_x = drag_info.grab_x;
2005 drag_info.current_pointer_y = drag_info.grab_y;
2006 drag_info.last_pointer_x = drag_info.current_pointer_x;
2007 drag_info.last_pointer_y = drag_info.current_pointer_y;
2008 drag_info.cumulative_x_drag = 0;
2009 drag_info.cumulative_y_drag = 0;
2010 drag_info.first_move = true;
2011 drag_info.move_threshold_passed = false;
2012 drag_info.want_move_threshold = false;
2013 drag_info.pointer_frame_offset = 0;
2014 drag_info.brushing = false;
2015 drag_info.clear_copied_locations ();
2017 drag_info.original_x = 0;
2018 drag_info.original_y = 0;
2019 drag_info.item->i2w (drag_info.original_x, drag_info.original_y);
2021 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
2023 event->button.time);
2025 if (session && session->transport_rolling()) {
2026 drag_info.was_rolling = true;
2028 drag_info.was_rolling = false;
2031 switch (snap_type) {
2032 case SnapToRegionStart:
2033 case SnapToRegionEnd:
2034 case SnapToRegionSync:
2035 case SnapToRegionBoundary:
2036 build_region_boundary_cache ();
2044 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
2046 drag_info.item->ungrab (0);
2047 drag_info.item = new_item;
2050 cursor = which_grabber_cursor ();
2053 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
2057 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
2059 bool did_drag = false;
2061 stop_canvas_autoscroll ();
2063 if (drag_info.item == 0) {
2067 drag_info.item->ungrab (event->button.time);
2069 if (drag_info.finished_callback) {
2070 drag_info.last_pointer_x = drag_info.current_pointer_x;
2071 drag_info.last_pointer_y = drag_info.current_pointer_y;
2072 (this->*(drag_info.finished_callback)) (item, event);
2075 did_drag = !drag_info.first_move;
2077 hide_verbose_canvas_cursor();
2085 Editor::region_gain_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2087 if (drag_info.first_move && drag_info.move_threshold_passed) {
2088 drag_info.first_move = false;
2093 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
2095 drag_info.item = item;
2096 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
2097 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
2101 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
2102 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
2106 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2109 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes64_t) arv->audio_region()->fade_in()->back()->when + arv->region()->position());
2113 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2115 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2117 nframes64_t fade_length;
2119 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2120 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2126 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2130 if (pos < (arv->region()->position() + 64)) {
2131 fade_length = 64; // this should be a minimum defined somewhere
2132 } else if (pos > arv->region()->last_frame()) {
2133 fade_length = arv->region()->length();
2135 fade_length = pos - arv->region()->position();
2137 /* mapover the region selection */
2139 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2141 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2147 tmp->reset_fade_in_shape_width (fade_length);
2150 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
2152 drag_info.first_move = false;
2156 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2158 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2160 nframes64_t fade_length;
2162 if (drag_info.first_move) return;
2164 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2165 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2170 if (pos < (arv->region()->position() + 64)) {
2171 fade_length = 64; // this should be a minimum defined somewhere
2172 } else if (pos > arv->region()->last_frame()) {
2173 fade_length = arv->region()->length();
2175 fade_length = pos - arv->region()->position();
2178 begin_reversible_command (_("change fade in length"));
2180 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2182 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2188 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2189 XMLNode &before = alist->get_state();
2191 tmp->audio_region()->set_fade_in_length (fade_length);
2192 tmp->audio_region()->set_fade_in_active (true);
2194 XMLNode &after = alist->get_state();
2195 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2198 commit_reversible_command ();
2202 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
2204 drag_info.item = item;
2205 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
2206 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
2210 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
2211 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
2215 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2217 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes64_t) arv->audio_region()->fade_out()->back()->when + arv->region()->position());
2221 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2223 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2225 nframes64_t fade_length;
2227 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2228 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2233 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2237 if (pos > (arv->region()->last_frame() - 64)) {
2238 fade_length = 64; // this should really be a minimum fade defined somewhere
2240 else if (pos < arv->region()->position()) {
2241 fade_length = arv->region()->length();
2244 fade_length = arv->region()->last_frame() - pos;
2247 /* mapover the region selection */
2249 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2251 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2257 tmp->reset_fade_out_shape_width (fade_length);
2260 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
2262 drag_info.first_move = false;
2266 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2268 if (drag_info.first_move) return;
2270 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2272 nframes64_t fade_length;
2274 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2275 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2281 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2285 if (pos > (arv->region()->last_frame() - 64)) {
2286 fade_length = 64; // this should really be a minimum fade defined somewhere
2288 else if (pos < arv->region()->position()) {
2289 fade_length = arv->region()->length();
2292 fade_length = arv->region()->last_frame() - pos;
2295 begin_reversible_command (_("change fade out length"));
2297 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2299 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2305 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2306 XMLNode &before = alist->get_state();
2308 tmp->audio_region()->set_fade_out_length (fade_length);
2309 tmp->audio_region()->set_fade_out_active (true);
2311 XMLNode &after = alist->get_state();
2312 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2315 commit_reversible_command ();
2319 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2321 drag_info.item = item;
2322 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2323 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2327 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2328 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2332 Cursor* cursor = (Cursor *) drag_info.data;
2334 if (cursor == playhead_cursor) {
2335 _dragging_playhead = true;
2337 if (session && drag_info.was_rolling) {
2338 session->request_stop ();
2341 if (session && session->is_auditioning()) {
2342 session->cancel_audition ();
2346 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2348 show_verbose_time_cursor (cursor->current_frame, 10);
2352 Editor::start_cursor_grab_no_stop (ArdourCanvas::Item* item, GdkEvent* event)
2354 drag_info.item = item;
2355 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2356 drag_info.finished_callback = &Editor::cursor_drag_finished_ensure_locate_callback;
2360 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2361 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2365 Cursor* cursor = (Cursor *) drag_info.data;
2366 nframes64_t where = event_frame (event, 0, 0);
2369 playhead_cursor->set_position (where);
2371 if (cursor == playhead_cursor) {
2372 _dragging_playhead = true;
2374 if (session && session->is_auditioning()) {
2375 session->cancel_audition ();
2379 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2381 show_verbose_time_cursor (cursor->current_frame, 10);
2385 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2387 Cursor* cursor = (Cursor *) drag_info.data;
2388 nframes64_t adjusted_frame;
2390 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2391 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2397 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2398 if (cursor == playhead_cursor) {
2399 snap_to (adjusted_frame);
2403 if (adjusted_frame == drag_info.last_pointer_frame) return;
2405 cursor->set_position (adjusted_frame);
2407 show_verbose_time_cursor (cursor->current_frame, 10);
2410 track_canvas->update_now ();
2412 UpdateAllTransportClocks (cursor->current_frame);
2414 drag_info.last_pointer_frame = adjusted_frame;
2415 drag_info.first_move = false;
2419 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2421 _dragging_playhead = false;
2423 if (drag_info.first_move) {
2427 cursor_drag_motion_callback (item, event);
2429 if (item == &playhead_cursor->canvas_item) {
2431 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2437 Editor::cursor_drag_finished_ensure_locate_callback (ArdourCanvas::Item* item, GdkEvent* event)
2439 _dragging_playhead = false;
2441 cursor_drag_motion_callback (item, event);
2443 if (item == &playhead_cursor->canvas_item) {
2445 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2451 Editor::update_marker_drag_item (Location *location)
2453 double x1 = frame_to_pixel (location->start());
2454 double x2 = frame_to_pixel (location->end());
2456 if (location->is_mark()) {
2457 marker_drag_line_points.front().set_x(x1);
2458 marker_drag_line_points.back().set_x(x1);
2459 marker_drag_line->property_points() = marker_drag_line_points;
2461 range_marker_drag_rect->property_x1() = x1;
2462 range_marker_drag_rect->property_x2() = x2;
2468 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2472 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2473 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2479 Location *location = find_location_from_marker (marker, is_start);
2481 drag_info.item = item;
2482 drag_info.data = marker;
2483 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2484 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2488 _dragging_edit_point = true;
2490 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2492 update_marker_drag_item (location);
2494 if (location->is_mark()) {
2495 // marker_drag_line->show();
2496 // marker_drag_line->raise_to_top();
2498 range_marker_drag_rect->show();
2499 //range_marker_drag_rect->raise_to_top();
2503 show_verbose_time_cursor (location->start(), 10);
2505 show_verbose_time_cursor (location->end(), 10);
2508 Selection::Operation op = Keyboard::selection_type (event->button.state);
2511 case Selection::Toggle:
2512 selection->toggle (marker);
2514 case Selection::Set:
2515 if (!selection->selected (marker)) {
2516 selection->set (marker);
2519 case Selection::Extend:
2521 Locations::LocationList ll;
2522 list<Marker*> to_add;
2524 selection->markers.range (s, e);
2525 s = min (marker->position(), s);
2526 e = max (marker->position(), e);
2529 if (e < max_frames) {
2532 session->locations()->find_all_between (s, e, ll, Location::Flags (0));
2533 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2534 LocationMarkers* lm = find_location_markers (*i);
2537 to_add.push_back (lm->start);
2540 to_add.push_back (lm->end);
2544 if (!to_add.empty()) {
2545 selection->add (to_add);
2549 case Selection::Add:
2550 selection->add (marker);
2554 /* set up copies for us to manipulate during the drag */
2556 drag_info.clear_copied_locations ();
2558 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
2559 Location *l = find_location_from_marker (*i, is_start);
2560 drag_info.copied_locations.push_back (new Location (*l));
2565 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2567 nframes64_t f_delta = 0;
2568 nframes64_t newframe;
2570 bool move_both = false;
2571 Marker* dragged_marker = (Marker*) drag_info.data;
2573 Location *real_location;
2574 Location *copy_location = 0;
2576 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2577 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2582 nframes64_t next = newframe;
2584 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2585 snap_to (newframe, 0, true);
2588 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2592 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2596 MarkerSelection::iterator i;
2597 list<Location*>::iterator x;
2599 /* find the marker we're dragging, and compute the delta */
2601 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2602 x != drag_info.copied_locations.end() && i != selection->markers.end();
2608 if (marker == dragged_marker) {
2610 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2615 if (real_location->is_mark()) {
2616 f_delta = newframe - copy_location->start();
2620 switch (marker->type()) {
2622 case Marker::LoopStart:
2623 case Marker::PunchIn:
2624 f_delta = newframe - copy_location->start();
2628 case Marker::LoopEnd:
2629 case Marker::PunchOut:
2630 f_delta = newframe - copy_location->end();
2633 /* what kind of marker is this ? */
2641 if (i == selection->markers.end()) {
2642 /* hmm, impossible - we didn't find the dragged marker */
2646 /* now move them all */
2648 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2649 x != drag_info.copied_locations.end() && i != selection->markers.end();
2655 /* call this to find out if its the start or end */
2657 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2661 if (real_location->locked()) {
2665 if (copy_location->is_mark()) {
2669 copy_location->set_start (copy_location->start() + f_delta);
2673 nframes64_t new_start = copy_location->start() + f_delta;
2674 nframes64_t new_end = copy_location->end() + f_delta;
2676 if (is_start) { // start-of-range marker
2679 copy_location->set_start (new_start);
2680 copy_location->set_end (new_end);
2681 } else if (new_start < copy_location->end()) {
2682 copy_location->set_start (new_start);
2684 snap_to (next, 1, true);
2685 copy_location->set_end (next);
2686 copy_location->set_start (newframe);
2689 } else { // end marker
2692 copy_location->set_end (new_end);
2693 copy_location->set_start (new_start);
2694 } else if (new_end > copy_location->start()) {
2695 copy_location->set_end (new_end);
2696 } else if (newframe > 0) {
2697 snap_to (next, -1, true);
2698 copy_location->set_start (next);
2699 copy_location->set_end (newframe);
2703 update_marker_drag_item (copy_location);
2705 LocationMarkers* lm = find_location_markers (real_location);
2708 lm->set_position (copy_location->start(), copy_location->end());
2712 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2713 drag_info.first_move = false;
2715 if (drag_info.copied_locations.empty()) {
2719 edit_point_clock.set (drag_info.copied_locations.front()->start());
2720 show_verbose_time_cursor (newframe, 10);
2723 track_canvas->update_now ();
2725 edit_point_clock.set (copy_location->start());
2729 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2731 if (drag_info.first_move) {
2733 /* just a click, do nothing but finish
2734 off the selection process
2737 Selection::Operation op = Keyboard::selection_type (event->button.state);
2738 Marker* marker = (Marker *) drag_info.data;
2741 case Selection::Set:
2742 if (selection->selected (marker) && selection->markers.size() > 1) {
2743 selection->set (marker);
2747 case Selection::Toggle:
2748 case Selection::Extend:
2749 case Selection::Add:
2756 _dragging_edit_point = false;
2759 begin_reversible_command ( _("move marker") );
2760 XMLNode &before = session->locations()->get_state();
2762 MarkerSelection::iterator i;
2763 list<Location*>::iterator x;
2766 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2767 x != drag_info.copied_locations.end() && i != selection->markers.end();
2770 Location * location = find_location_from_marker ((*i), is_start);
2774 if (location->locked()) {
2778 if (location->is_mark()) {
2779 location->set_start ((*x)->start());
2781 location->set ((*x)->start(), (*x)->end());
2786 XMLNode &after = session->locations()->get_state();
2787 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2788 commit_reversible_command ();
2790 marker_drag_line->hide();
2791 range_marker_drag_rect->hide();
2795 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2798 MeterMarker* meter_marker;
2800 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2801 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2805 meter_marker = dynamic_cast<MeterMarker*> (marker);
2807 MetricSection& section (meter_marker->meter());
2809 if (!section.movable()) {
2813 drag_info.item = item;
2814 drag_info.copy = false;
2815 drag_info.data = marker;
2816 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2817 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2821 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2823 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2827 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2830 MeterMarker* meter_marker;
2832 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2833 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2837 meter_marker = dynamic_cast<MeterMarker*> (marker);
2839 // create a dummy marker for visual representation of moving the copy.
2840 // The actual copying is not done before we reach the finish callback.
2842 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2843 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2844 *new MeterSection(meter_marker->meter()));
2846 drag_info.item = &new_marker->the_item();
2847 drag_info.copy = true;
2848 drag_info.data = new_marker;
2849 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2850 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2854 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2856 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2860 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2862 MeterMarker* marker = (MeterMarker *) drag_info.data;
2863 nframes64_t adjusted_frame;
2865 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2866 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2872 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2873 snap_to (adjusted_frame);
2876 if (adjusted_frame == drag_info.last_pointer_frame) return;
2878 marker->set_position (adjusted_frame);
2881 drag_info.last_pointer_frame = adjusted_frame;
2882 drag_info.first_move = false;
2884 show_verbose_time_cursor (adjusted_frame, 10);
2888 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2890 if (drag_info.first_move) return;
2892 meter_marker_drag_motion_callback (drag_info.item, event);
2894 MeterMarker* marker = (MeterMarker *) drag_info.data;
2897 TempoMap& map (session->tempo_map());
2898 map.bbt_time (drag_info.last_pointer_frame, when);
2900 if (drag_info.copy == true) {
2901 begin_reversible_command (_("copy meter mark"));
2902 XMLNode &before = map.get_state();
2903 map.add_meter (marker->meter(), when);
2904 XMLNode &after = map.get_state();
2905 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2906 commit_reversible_command ();
2908 // delete the dummy marker we used for visual representation of copying.
2909 // a new visual marker will show up automatically.
2912 begin_reversible_command (_("move meter mark"));
2913 XMLNode &before = map.get_state();
2914 map.move_meter (marker->meter(), when);
2915 XMLNode &after = map.get_state();
2916 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2917 commit_reversible_command ();
2922 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2925 TempoMarker* tempo_marker;
2927 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2928 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2932 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2933 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2937 MetricSection& section (tempo_marker->tempo());
2939 if (!section.movable()) {
2943 drag_info.item = item;
2944 drag_info.copy = false;
2945 drag_info.data = marker;
2946 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2947 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2951 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2952 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2956 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2959 TempoMarker* tempo_marker;
2961 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2962 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2966 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2967 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2971 // create a dummy marker for visual representation of moving the copy.
2972 // The actual copying is not done before we reach the finish callback.
2974 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2975 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2976 *new TempoSection(tempo_marker->tempo()));
2978 drag_info.item = &new_marker->the_item();
2979 drag_info.copy = true;
2980 drag_info.data = new_marker;
2981 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2982 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2986 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2988 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2992 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2994 TempoMarker* marker = (TempoMarker *) drag_info.data;
2995 nframes64_t adjusted_frame;
2997 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2998 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3004 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3005 snap_to (adjusted_frame);
3008 if (adjusted_frame == drag_info.last_pointer_frame) return;
3010 /* OK, we've moved far enough to make it worth actually move the thing. */
3012 marker->set_position (adjusted_frame);
3014 show_verbose_time_cursor (adjusted_frame, 10);
3016 drag_info.last_pointer_frame = adjusted_frame;
3017 drag_info.first_move = false;
3021 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3023 if (drag_info.first_move) return;
3025 tempo_marker_drag_motion_callback (drag_info.item, event);
3027 TempoMarker* marker = (TempoMarker *) drag_info.data;
3030 TempoMap& map (session->tempo_map());
3031 map.bbt_time (drag_info.last_pointer_frame, when);
3033 if (drag_info.copy == true) {
3034 begin_reversible_command (_("copy tempo mark"));
3035 XMLNode &before = map.get_state();
3036 map.add_tempo (marker->tempo(), when);
3037 XMLNode &after = map.get_state();
3038 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3039 commit_reversible_command ();
3041 // delete the dummy marker we used for visual representation of copying.
3042 // a new visual marker will show up automatically.
3045 begin_reversible_command (_("move tempo mark"));
3046 XMLNode &before = map.get_state();
3047 map.move_tempo (marker->tempo(), when);
3048 XMLNode &after = map.get_state();
3049 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3050 commit_reversible_command ();
3055 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
3057 ControlPoint* control_point;
3059 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
3060 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
3064 // We shouldn't remove the first or last gain point
3065 if (control_point->line().is_last_point(*control_point) ||
3066 control_point->line().is_first_point(*control_point)) {
3070 control_point->line().remove_point (*control_point);
3074 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event)
3076 ControlPoint* control_point;
3078 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
3079 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
3083 control_point->line().remove_point (*control_point);
3087 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
3089 ControlPoint* control_point;
3091 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
3092 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
3096 drag_info.item = item;
3097 drag_info.data = control_point;
3098 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
3099 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
3101 start_grab (event, fader_cursor);
3103 // start the grab at the center of the control point so
3104 // the point doesn't 'jump' to the mouse after the first drag
3105 drag_info.grab_x = control_point->get_x();
3106 drag_info.grab_y = control_point->get_y();
3108 control_point->line().parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
3109 track_canvas->w2c(drag_info.grab_x, drag_info.grab_y, drag_info.grab_x, drag_info.grab_y);
3111 drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
3113 control_point->line().start_drag (control_point, drag_info.grab_frame, 0);
3115 float fraction = 1.0 - (control_point->get_y() / control_point->line().height());
3116 set_verbose_canvas_cursor (control_point->line().get_verbose_cursor_string (fraction),
3117 drag_info.current_pointer_x + 10, drag_info.current_pointer_y + 10);
3119 show_verbose_canvas_cursor ();
3123 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3125 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
3127 double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
3128 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
3130 if (event->button.state & Keyboard::SecondaryModifier) {
3135 double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
3136 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
3138 // calculate zero crossing point. back off by .01 to stay on the
3139 // positive side of zero
3141 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line().height() - .01;
3142 cp->line().parent_group().i2w(_unused, zero_gain_y);
3144 // make sure we hit zero when passing through
3145 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
3146 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
3150 if (drag_info.x_constrained) {
3151 cx = drag_info.grab_x;
3153 if (drag_info.y_constrained) {
3154 cy = drag_info.grab_y;
3157 drag_info.cumulative_x_drag = cx - drag_info.grab_x;
3158 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
3160 cp->line().parent_group().w2i (cx, cy);
3164 cy = min ((double) cp->line().height(), cy);
3166 //translate cx to frames
3167 nframes64_t cx_frames = unit_to_frame (cx);
3169 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
3170 snap_to (cx_frames);
3173 float fraction = 1.0 - (cy / cp->line().height());
3177 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3183 cp->line().point_drag (*cp, cx_frames , fraction, push);
3185 set_verbose_canvas_cursor_text (cp->line().get_verbose_cursor_string (fraction));
3187 drag_info.first_move = false;
3191 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3193 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
3195 if (drag_info.first_move) {
3199 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3200 reset_point_selection ();
3204 control_point_drag_motion_callback (item, event);
3206 cp->line().end_drag (cp);
3210 Editor::edit_control_point (ArdourCanvas::Item* item)
3212 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
3215 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
3219 ControlPointDialog d (p);
3220 d.set_position (Gtk::WIN_POS_MOUSE);
3223 if (d.run () != RESPONSE_ACCEPT) {
3227 p->line().modify_point_y (*p, d.get_y_fraction ());
3232 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
3234 switch (mouse_mode) {
3236 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
3237 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
3245 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
3249 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
3250 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
3254 start_line_grab (al, event);
3258 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
3262 nframes64_t frame_within_region;
3264 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3265 origin, and ditto for y.
3268 cx = event->button.x;
3269 cy = event->button.y;
3271 line->parent_group().w2i (cx, cy);
3273 frame_within_region = (nframes64_t) floor (cx * frames_per_unit);
3275 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
3276 current_line_drag_info.after)) {
3277 /* no adjacent points */
3281 drag_info.item = &line->grab_item();
3282 drag_info.data = line;
3283 drag_info.motion_callback = &Editor::line_drag_motion_callback;
3284 drag_info.finished_callback = &Editor::line_drag_finished_callback;
3286 start_grab (event, fader_cursor);
3288 /* store grab start in parent frame */
3290 drag_info.grab_x = cx;
3291 drag_info.grab_y = cy;
3293 double fraction = 1.0 - (cy / line->height());
3295 line->start_drag (0, drag_info.grab_frame, fraction);
3297 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
3298 drag_info.current_pointer_x + 10, drag_info.current_pointer_y + 10);
3299 show_verbose_canvas_cursor ();
3303 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3305 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3307 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
3309 if (event->button.state & Keyboard::SecondaryModifier) {
3313 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
3315 // calculate zero crossing point. back off by .01 to stay on the
3316 // positive side of zero
3317 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
3319 // line->parent_group().i2w(_unused, zero_gain_y);
3321 // make sure we hit zero when passing through
3322 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
3323 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
3327 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
3330 cy = min ((double) line->height(), cy);
3333 double fraction = 1.0 - (cy / line->height());
3337 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3343 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
3345 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
3349 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3351 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3352 line_drag_motion_callback (item, event);
3357 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3359 if (selection->regions.empty() || clicked_regionview == 0) {
3362 _region_motion_group->raise_to_top ();
3363 drag_info.copy = false;
3364 drag_info.item = item;
3365 drag_info.data = clicked_regionview;
3367 if (Config->get_edit_mode() == Splice) {
3368 drag_info.motion_callback = &Editor::region_drag_splice_motion_callback;
3369 drag_info.finished_callback = &Editor::region_drag_splice_finished_callback;
3371 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3372 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3378 TimeAxisView* tvp = clicked_axisview;
3379 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3381 if (tv && tv->is_track()) {
3382 speed = tv->get_diskstream()->speed();
3385 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3386 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3387 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3388 drag_info.source_layer = clicked_regionview->region()->layer();
3389 drag_info.dest_trackview = drag_info.source_trackview;
3390 drag_info.dest_layer = drag_info.source_layer;
3391 // we want a move threshold
3392 drag_info.want_move_threshold = true;
3393 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3395 begin_reversible_command (_("move region(s)"));
3397 /* sync the canvas to what we think is its current state */
3398 track_canvas->update_now();
3402 Editor::start_create_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3404 drag_info.copy = false;
3405 drag_info.item = item;
3406 drag_info.data = clicked_axisview;
3407 drag_info.source_trackview = clicked_axisview;
3408 drag_info.dest_trackview = drag_info.source_trackview;
3409 drag_info.dest_layer = drag_info.source_layer;
3410 drag_info.motion_callback = &Editor::create_region_drag_motion_callback;
3411 drag_info.finished_callback = &Editor::create_region_drag_finished_callback;
3417 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
3419 if (selection->regions.empty() || clicked_regionview == 0) {
3422 _region_motion_group->raise_to_top ();
3423 drag_info.copy = true;
3424 drag_info.item = item;
3425 drag_info.data = clicked_regionview;
3429 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
3430 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3433 if (rtv && rtv->is_track()) {
3434 speed = rtv->get_diskstream()->speed();
3437 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3438 drag_info.dest_trackview = drag_info.source_trackview;
3439 drag_info.dest_layer = drag_info.source_layer;
3440 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3441 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3442 // we want a move threshold
3443 drag_info.want_move_threshold = true;
3444 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3445 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3446 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3450 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
3452 if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) {
3456 drag_info.copy = false;
3457 drag_info.item = item;
3458 drag_info.data = clicked_regionview;
3459 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3460 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3465 TimeAxisView* tvp = clicked_axisview;
3466 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3468 if (tv && tv->is_track()) {
3469 speed = tv->get_diskstream()->speed();
3472 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3473 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3474 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3475 drag_info.dest_trackview = drag_info.source_trackview;
3476 drag_info.dest_layer = drag_info.source_layer;
3477 // we want a move threshold
3478 drag_info.want_move_threshold = true;
3479 drag_info.brushing = true;
3481 begin_reversible_command (_("Drag region brush"));
3485 Editor::possibly_copy_regions_during_grab (GdkEvent* event)
3487 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
3489 drag_info.want_move_threshold = false; // don't copy again
3491 /* duplicate the regionview(s) and region(s) */
3493 vector<RegionView*> new_regionviews;
3495 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3500 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
3501 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
3503 const boost::shared_ptr<const Region> original = rv->region();
3504 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
3507 boost::shared_ptr<AudioRegion> audioregion_copy
3508 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
3509 nrv = new AudioRegionView (*arv, audioregion_copy);
3511 boost::shared_ptr<MidiRegion> midiregion_copy
3512 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
3513 nrv = new MidiRegionView (*mrv, midiregion_copy);
3518 nrv->get_canvas_group()->show ();
3519 new_regionviews.push_back (nrv);
3522 if (new_regionviews.empty()) {
3526 /* reset selection to new regionviews. This will not set selection visual status for
3527 these regionviews since they don't belong to a track, so do that by hand too.
3530 selection->set (new_regionviews);
3532 for (vector<RegionView*>::iterator i = new_regionviews.begin(); i != new_regionviews.end(); ++i) {
3533 (*i)->set_selected (true);
3536 /* reset drag_info data to reflect the fact that we are dragging the copies */
3538 drag_info.data = new_regionviews.front();
3540 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3542 sync the canvas to what we think is its current state
3543 without it, the canvas seems to
3544 "forget" to update properly after the upcoming reparent()
3545 ..only if the mouse is in rapid motion at the time of the grab.
3546 something to do with regionview creation raking so long?
3548 track_canvas->update_now();
3553 Editor::check_region_drag_possible (RouteTimeAxisView** tv, layer_t* layer)
3555 /* Which trackview is this ? */
3557 std::pair<TimeAxisView*, int> const tvp = trackview_by_y_position (drag_info.current_pointer_y);
3558 (*tv) = dynamic_cast<RouteTimeAxisView*> (tvp.first);
3559 (*layer) = tvp.second;
3561 /* The region motion is only processed if the pointer is over
3565 if (!(*tv) || !(*tv)->is_track()) {
3566 /* To make sure we hide the verbose canvas cursor when the mouse is
3567 not held over and audiotrack.
3569 hide_verbose_canvas_cursor ();
3576 struct RegionSelectionByPosition {
3577 bool operator() (RegionView*a, RegionView* b) {
3578 return a->region()->position () < b->region()->position();
3583 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3585 RouteTimeAxisView* tv;
3588 if (!check_region_drag_possible (&tv, &layer)) {
3592 if (!drag_info.move_threshold_passed) {
3598 if (drag_info.current_pointer_x - drag_info.grab_x > 0) {
3604 RegionSelection copy (selection->regions);
3606 RegionSelectionByPosition cmp;
3609 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
3611 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
3617 boost::shared_ptr<Playlist> playlist;
3619 if ((playlist = atv->playlist()) == 0) {
3623 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
3628 if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) {
3632 if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) {
3638 playlist->shuffle ((*i)->region(), dir);
3640 drag_info.grab_x = drag_info.current_pointer_x;
3645 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3650 Editor::visible_order_range (int* low, int* high) const
3652 *low = TimeAxisView::max_order ();
3655 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
3657 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3659 if (!rtv->hidden()) {
3661 if (*high < rtv->order()) {
3662 *high = rtv->order ();
3665 if (*low > rtv->order()) {
3666 *low = rtv->order ();
3672 /** @param new_order New track order.
3673 * @param old_order Old track order.
3674 * @param visible_y_low Lowest visible order.
3675 * @param visible_y_high Highest visible order.
3676 * @param tracks Bitset of tracks indexed by order; 0 means a audio/MIDI track, 1 means something else.
3677 * @param heigh_list Heights of tracks indexed by order.
3678 * @return true if y movement should not happen, otherwise false.
3681 Editor::y_movement_disallowed (
3682 int new_order, int old_order, int y_span, int visible_y_low, int visible_y_high,
3683 bitset<512> const & tracks, vector<int32_t> const & height_list
3686 if (new_order != old_order) {
3688 /* this isn't the pointer track */
3692 /* moving up the canvas */
3693 if ( (new_order - y_span) >= visible_y_low) {
3697 /* work out where we'll end up with this y span, taking hidden TimeAxisViews into account */
3698 int32_t visible_tracks = 0;
3699 while (visible_tracks < y_span ) {
3701 while (height_list[new_order - (visible_tracks - n)] == 0) {
3702 /* passing through a hidden track */
3707 if (tracks[new_order - (y_span - n)] != 0x00) {
3708 /* moving to a non-track; disallow */
3714 /* moving beyond the lowest visible track; disallow */
3718 } else if (y_span < 0) {
3720 /* moving down the canvas */
3721 if ((new_order - y_span) <= visible_y_high) {
3723 int32_t visible_tracks = 0;
3725 while (visible_tracks > y_span ) {
3728 while (height_list[new_order - (visible_tracks - n)] == 0) {
3729 /* passing through a hidden track */
3734 if (tracks[new_order - (y_span - n)] != 0x00) {
3735 /* moving to a non-track; disallow */
3742 /* moving beyond the highest visible track; disallow */
3749 /* this is the pointer's track */
3751 if ((new_order - y_span) > visible_y_high) {
3752 /* we will overflow */
3754 } else if ((new_order - y_span) < visible_y_low) {
3755 /* we will overflow */
3764 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3768 nframes64_t pending_region_position = 0;
3769 int32_t pointer_order_span = 0, canvas_pointer_order_span = 0;
3770 int32_t pointer_layer_span = 0;
3772 bool clamp_y_axis = false;
3773 vector<int32_t>::iterator j;
3775 possibly_copy_regions_during_grab (event);
3777 /* *pointer* variables reflect things about the pointer; as we may be moving
3778 multiple regions, much detail must be computed per-region */
3780 /* current_pointer_view will become the TimeAxisView that we're currently pointing at, and
3781 current_pointer_layer the current layer on that TimeAxisView */
3782 RouteTimeAxisView* current_pointer_view;
3783 layer_t current_pointer_layer;
3784 if (!check_region_drag_possible (¤t_pointer_view, ¤t_pointer_layer)) {
3788 /* TimeAxisView that we were pointing at last time we entered this method */
3789 TimeAxisView const * const last_pointer_view = drag_info.dest_trackview;
3790 /* the order of the track that we were pointing at last time we entered this method */
3791 int32_t const last_pointer_order = last_pointer_view->order ();
3792 /* the layer that we were pointing at last time we entered this method */
3793 layer_t const last_pointer_layer = drag_info.dest_layer;
3795 /************************************************************
3797 ************************************************************/
3799 /* Height of TimeAxisViews, indexed by order */
3800 /* XXX: hard-coded limit of TimeAxisViews */
3801 vector<int32_t> height_list (512);
3803 if (drag_info.brushing) {
3804 clamp_y_axis = true;
3805 pointer_order_span = 0;
3809 /* the change in track order between this callback and the last */
3810 pointer_order_span = last_pointer_view->order() - current_pointer_view->order();
3811 /* the change in layer between this callback and the last;
3812 only meaningful if pointer_order_span == 0 (ie we've not moved tracks) */
3813 pointer_layer_span = last_pointer_layer - current_pointer_layer;
3815 if (pointer_order_span != 0) {
3817 int32_t children = 0;
3818 /* XXX: hard-coded limit of tracks */
3819 bitset <512> tracks (0x00);
3823 visible_order_range (&visible_y_low, &visible_y_high);
3825 /* get a bitmask representing the visible tracks */
3827 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3828 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3829 TimeAxisView::Children children_list;
3831 /* zeroes are audio/MIDI tracks. ones are other types. */
3833 if (!rtv->hidden()) {
3835 if (!rtv->is_track()) {
3836 /* not an audio nor MIDI track */
3837 tracks = tracks |= (0x01 << rtv->order());
3840 height_list[rtv->order()] = (*i)->current_height();
3843 if ((children_list = rtv->get_child_list()).size() > 0) {
3844 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
3845 tracks = tracks |= (0x01 << (rtv->order() + children));
3846 height_list[rtv->order() + children] = (*j)->current_height();
3853 /* find the actual pointer span, in terms of the number of visible tracks;
3854 to do this, we reduce |pointer_order_span| by the number of hidden tracks
3857 canvas_pointer_order_span = pointer_order_span;
3858 if (last_pointer_view->order() >= current_pointer_view->order()) {
3859 for (int32_t y = current_pointer_view->order(); y < last_pointer_view->order(); y++) {
3860 if (height_list[y] == 0) {
3861 canvas_pointer_order_span--;
3865 for (int32_t y = last_pointer_view->order(); y <= current_pointer_view->order(); y++) {
3866 if (height_list[y] == 0) {
3867 canvas_pointer_order_span++;
3872 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3874 RegionView* rv = (*i);
3876 if (rv->region()->locked()) {
3880 double ix1, ix2, iy1, iy2;
3881 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3882 rv->get_canvas_frame()->i2w (ix1, iy1);
3883 iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
3885 /* get the new trackview for this particular region */
3886 std::pair<TimeAxisView*, int> const tvp = trackview_by_y_position (iy1);
3888 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
3890 /* I know this method has a slightly excessive argument list, but I think
3891 it's nice to separate the code out all the same, since it has such a
3892 simple result, and it makes it clear that there are no other
3896 /* XXX: not sure that we should be passing canvas_pointer_order_span in here,
3897 as surely this is a per-region thing... */
3899 clamp_y_axis = y_movement_disallowed (
3900 rtv->order(), last_pointer_order, canvas_pointer_order_span, visible_y_low, visible_y_high,
3909 } else if (drag_info.dest_trackview == current_pointer_view) {
3911 if (current_pointer_layer == last_pointer_layer) {
3912 /* No movement; clamp */
3913 clamp_y_axis = true;
3918 if (!clamp_y_axis) {
3919 drag_info.dest_trackview = current_pointer_view;
3920 drag_info.dest_layer = current_pointer_layer;
3923 /************************************************************
3925 ************************************************************/
3927 /* compute the amount of pointer motion in frames, and where
3928 the region would be if we moved it by that much.
3930 if ( drag_info.move_threshold_passed ) {
3932 if (drag_info.current_pointer_frame >= drag_info.pointer_frame_offset) {
3934 nframes64_t sync_frame;
3935 nframes64_t sync_offset;
3938 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3940 sync_offset = clicked_regionview->region()->sync_offset (sync_dir);
3942 /* we don't handle a sync point that lies before zero.
3944 if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) {
3945 sync_frame = pending_region_position + (sync_dir*sync_offset);
3947 /* we snap if the snap modifier is not enabled.
3950 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3951 snap_to (sync_frame);
3954 pending_region_position = clicked_regionview->region()->adjust_to_sync (sync_frame);
3957 pending_region_position = drag_info.last_frame_position;
3961 pending_region_position = 0;
3964 if (pending_region_position > max_frames - clicked_regionview->region()->length()) {
3965 pending_region_position = drag_info.last_frame_position;
3968 bool x_move_allowed;
3970 if (Config->get_edit_mode() == Lock) {
3971 if (drag_info.copy) {
3972 x_move_allowed = !drag_info.x_constrained;
3974 /* in locked edit mode, reverse the usual meaning of x_constrained */
3975 x_move_allowed = drag_info.x_constrained;
3978 x_move_allowed = !drag_info.x_constrained;
3981 if (( pending_region_position != drag_info.last_frame_position) && x_move_allowed ) {
3983 /* now compute the canvas unit distance we need to move the regionview
3984 to make it appear at the new location.
3987 if (pending_region_position > drag_info.last_frame_position) {
3988 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3990 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3991 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3993 RegionView* rv = (*i);
3995 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3997 double ix1, ix2, iy1, iy2;
3998 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3999 rv->get_canvas_frame()->i2w (ix1, iy1);
4001 if (-x_delta > ix1 + horizontal_adjustment.get_value()) {
4003 pending_region_position = drag_info.last_frame_position;
4010 drag_info.last_frame_position = pending_region_position;
4017 /* threshold not passed */
4022 /*************************************************************
4024 ************************************************************/
4026 if (x_delta == 0 && pointer_order_span == 0 && pointer_layer_span == 0) {
4027 /* haven't reached next snap point, and we're not switching
4028 trackviews nor layers. nothing to do.
4033 /*************************************************************
4035 ************************************************************/
4036 bool do_move = true;
4037 if (drag_info.first_move) {
4038 if (!drag_info.move_threshold_passed) {
4045 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4046 const list<RegionView*>& layered_regions = selection->regions.by_layer();
4048 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
4050 RegionView* rv = (*i);
4052 if (rv->region()->locked()) {
4056 /* here we are calculating the y distance from the
4057 top of the first track view to the top of the region
4058 area of the track view that we're working on */
4060 /* this x value is just a dummy value so that we have something
4061 to pass to i2w () */
4065 /* distance from the top of this track view to the region area
4066 of our track view is always 1 */
4070 /* convert to world coordinates, ie distance from the top of
4071 the ruler section */
4073 rv->get_canvas_frame()->i2w (ix1, iy1);
4075 /* compensate for the ruler section and the vertical scrollbar position */
4076 iy1 += get_trackview_group_vertical_offset ();
4078 if (drag_info.first_move) {
4080 // hide any dependent views
4082 rv->get_time_axis_view().hide_dependent_views (*rv);
4085 reparent to a non scrolling group so that we can keep the
4086 region selection above all time axis views.
4087 reparenting means we have to move the rv as the two
4088 parent groups have different coordinates.
4091 rv->get_canvas_group()->property_y() = iy1 - 1;
4092 rv->get_canvas_group()->reparent(*_region_motion_group);
4094 rv->fake_set_opaque (true);
4097 /* current view for this particular region */
4098 std::pair<TimeAxisView*, int> pos = trackview_by_y_position (iy1);
4099 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
4101 if (pointer_order_span != 0 && !clamp_y_axis) {
4103 /* INTER-TRACK MOVEMENT */
4105 /* move through the height list to the track that the region is currently on */
4106 vector<int32_t>::iterator j = height_list.begin ();
4108 while (j != height_list.end () && x != rtv->order ()) {
4114 int32_t temp_pointer_order_span = canvas_pointer_order_span;
4116 if (j != height_list.end ()) {
4118 /* Account for layers in the original and
4119 destination tracks. If we're moving around in layers we assume
4120 that only one track is involved, so it's ok to use *pointer*
4123 StreamView* lv = last_pointer_view->view ();
4126 /* move to the top of the last trackview */
4127 if (lv->layer_display () == Stacked) {
4128 y_delta -= (lv->layers() - last_pointer_layer - 1) * lv->child_height ();
4131 StreamView* cv = current_pointer_view->view ();
4134 /* move to the right layer on the current trackview */
4135 if (cv->layer_display () == Stacked) {
4136 y_delta += (cv->layers() - current_pointer_layer - 1) * cv->child_height ();
4139 /* And for being on a non-topmost layer on the new
4142 while (temp_pointer_order_span > 0) {
4143 /* we're moving up canvas-wise,
4144 so we need to find the next track height
4146 if (j != height_list.begin()) {
4150 if (x != last_pointer_order) {
4152 ++temp_pointer_order_span;
4157 temp_pointer_order_span--;
4160 while (temp_pointer_order_span < 0) {
4164 if (x != last_pointer_order) {
4166 --temp_pointer_order_span;
4170 if (j != height_list.end()) {
4174 temp_pointer_order_span++;
4178 /* find out where we'll be when we move and set height accordingly */
4180 std::pair<TimeAxisView*, int> const pos = trackview_by_y_position (iy1 + y_delta);
4181 RouteTimeAxisView const * temp_rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
4182 rv->set_height (temp_rtv->view()->child_height());
4184 /* if you un-comment the following, the region colours will follow
4185 the track colours whilst dragging; personally
4186 i think this can confuse things, but never mind.
4189 //const GdkColor& col (temp_rtv->view->get_region_color());
4190 //rv->set_color (const_cast<GdkColor&>(col));
4194 if (pointer_order_span == 0 && pointer_layer_span != 0 && !clamp_y_axis) {
4196 /* INTER-LAYER MOVEMENT in the same track */
4197 y_delta = rtv->view()->child_height () * pointer_layer_span;
4201 if (drag_info.brushing) {
4202 mouse_brush_insert_region (rv, pending_region_position);
4204 rv->move (x_delta, y_delta);
4207 } /* foreach region */
4211 if (drag_info.first_move && drag_info.move_threshold_passed) {
4212 cursor_group->raise_to_top();
4213 drag_info.first_move = false;
4216 if (x_delta != 0 && !drag_info.brushing) {
4217 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4222 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4224 bool nocommit = true;
4225 vector<RegionView*> copies;
4226 RouteTimeAxisView* source_tv;
4227 boost::shared_ptr<Diskstream> ds;
4228 boost::shared_ptr<Playlist> from_playlist;
4229 vector<RegionView*> new_selection;
4230 typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
4231 PlaylistSet modified_playlists;
4232 PlaylistSet frozen_playlists;
4233 list <sigc::connection> modified_playlist_connections;
4234 pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
4235 nframes64_t drag_delta;
4236 bool changed_tracks, changed_position;
4237 std::pair<TimeAxisView*, int> tvp;
4238 std::map<RegionView*, RouteTimeAxisView*> final;
4240 /* first_move is set to false if the regionview has been moved in the
4244 if (drag_info.first_move) {
4251 if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
4252 selection->set (pre_drag_region_selection);
4253 pre_drag_region_selection.clear ();
4256 if (drag_info.brushing) {
4257 /* all changes were made during motion event handlers */
4259 if (drag_info.copy) {
4260 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4261 copies.push_back (*i);
4270 /* reverse this here so that we have the correct logic to finalize
4274 if (Config->get_edit_mode() == Lock && !drag_info.copy) {
4275 drag_info.x_constrained = !drag_info.x_constrained;
4278 if (drag_info.copy) {
4279 if (drag_info.x_constrained) {
4280 op_string = _("fixed time region copy");
4282 op_string = _("region copy");
4285 if (drag_info.x_constrained) {
4286 op_string = _("fixed time region drag");
4288 op_string = _("region drag");
4292 begin_reversible_command (op_string);
4293 changed_position = (drag_info.last_frame_position != (nframes64_t) (clicked_regionview->region()->position()));
4294 tvp = trackview_by_y_position (drag_info.current_pointer_y);
4295 changed_tracks = (tvp.first != &clicked_regionview->get_time_axis_view());
4297 drag_delta = clicked_regionview->region()->position() - drag_info.last_frame_position;
4299 track_canvas->update_now ();
4301 /* make a list of where each region ended up */
4302 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4304 double ix1, ix2, iy1, iy2;
4305 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
4306 (*i)->get_canvas_frame()->i2w (ix1, iy1);
4307 iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
4309 std::pair<TimeAxisView*, int> tv = trackview_by_y_position (iy1);
4310 final[*i] = dynamic_cast<RouteTimeAxisView*> (tv.first);
4313 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
4315 RegionView* rv = (*i);
4316 RouteTimeAxisView* dest_rtv = final[*i];
4320 if (rv->region()->locked()) {
4325 if (changed_position && !drag_info.x_constrained) {
4326 where = rv->region()->position() - drag_delta;
4328 where = rv->region()->position();
4331 boost::shared_ptr<Region> new_region;
4333 if (drag_info.copy) {
4334 /* we already made a copy */
4335 new_region = rv->region();
4337 /* undo the previous hide_dependent_views so that xfades don't
4338 disappear on copying regions
4341 //rv->get_time_axis_view().reveal_dependent_views (*rv);
4343 } else if (changed_tracks && dest_rtv->playlist()) {
4344 new_region = RegionFactory::create (rv->region());
4347 if (changed_tracks || drag_info.copy) {
4349 boost::shared_ptr<Playlist> to_playlist = dest_rtv->playlist();
4355 latest_regionviews.clear ();
4357 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4359 insert_result = modified_playlists.insert (to_playlist);
4360 if (insert_result.second) {
4361 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
4364 to_playlist->add_region (new_region, where);
4368 if (!latest_regionviews.empty()) {
4369 // XXX why just the first one ? we only expect one
4370 // commented out in nick_m's canvas reworking. is that intended?
4371 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
4372 new_selection.push_back (latest_regionviews.front());
4377 motion on the same track. plonk the previously reparented region
4378 back to its original canvas group (its streamview).
4379 No need to do anything for copies as they are fake regions which will be deleted.
4382 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
4383 rv->get_canvas_group()->property_y() = 0;
4385 /* just change the model */
4387 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
4389 insert_result = modified_playlists.insert (playlist);
4390 if (insert_result.second) {
4391 session->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
4393 /* freeze to avoid lots of relayering in the case of a multi-region drag */
4394 frozen_insert_result = frozen_playlists.insert(playlist);
4395 if (frozen_insert_result.second) {
4399 rv->region()->set_position (where, (void*) this);
4402 if (changed_tracks && !drag_info.copy) {
4404 /* get the playlist where this drag started. we can't use rv->region()->playlist()
4405 because we may have copied the region and it has not been attached to a playlist.
4408 assert ((source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view())));
4409 assert ((ds = source_tv->get_diskstream()));
4410 assert ((from_playlist = ds->playlist()));
4412 /* moved to a different audio track, without copying */
4414 /* the region that used to be in the old playlist is not
4415 moved to the new one - we use a copy of it. as a result,
4416 any existing editor for the region should no longer be
4420 rv->hide_region_editor();
4421 rv->fake_set_opaque (false);
4423 /* remove the region from the old playlist */
4425 insert_result = modified_playlists.insert (from_playlist);
4426 if (insert_result.second) {
4427 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
4430 from_playlist->remove_region ((rv->region()));
4432 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
4433 was selected in all of them, then removing it from a playlist will have removed all
4434 trace of it from the selection (i.e. there were N regions selected, we removed 1,
4435 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
4436 corresponding regionview, and the selection is now empty).
4438 this could have invalidated any and all iterators into the region selection.
4440 the heuristic we use here is: if the region selection is empty, break out of the loop
4441 here. if the region selection is not empty, then restart the loop because we know that
4442 we must have removed at least the region(view) we've just been working on as well as any
4443 that we processed on previous iterations.
4445 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
4446 we can just iterate.
4449 if (selection->regions.empty()) {
4452 i = selection->regions.by_layer().begin();
4459 if (drag_info.copy) {
4460 copies.push_back (rv);
4464 if (new_selection.empty()) {
4465 if (drag_info.copy) {
4466 /* the region(view)s that are selected and being dragged around
4467 are copies and do not belong to any track. remove them
4468 from the selection right here.
4470 selection->clear_regions();
4473 /* this will clear any existing selection that would have been
4474 cleared in the other clause above
4476 selection->set (new_selection);
4479 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
4485 for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
4486 session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));
4488 commit_reversible_command ();
4491 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
4498 Editor::create_region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4500 if (drag_info.move_threshold_passed) {
4501 if (drag_info.first_move) {
4502 // TODO: create region-create-drag region view here
4503 drag_info.first_move = false;
4506 // TODO: resize region-create-drag region view here
4511 Editor::create_region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4513 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (drag_info.dest_trackview);
4517 const boost::shared_ptr<MidiDiskstream> diskstream =
4518 boost::dynamic_pointer_cast<MidiDiskstream>(mtv->view()->trackview().track()->diskstream());
4521 warning << "Cannot create non-MIDI region" << endl;
4525 if (drag_info.first_move) {
4526 begin_reversible_command (_("create region"));
4527 XMLNode &before = mtv->playlist()->get_state();
4529 nframes64_t start = drag_info.grab_frame;
4530 snap_to (start, -1);
4531 const Meter& m = session->tempo_map().meter_at(start);
4532 const Tempo& t = session->tempo_map().tempo_at(start);
4533 double length = floor (m.frames_per_bar(t, session->frame_rate()));
4535 boost::shared_ptr<Source> src = session->create_midi_source_for_session(*diskstream.get());
4537 mtv->playlist()->add_region (boost::dynamic_pointer_cast<MidiRegion>
4538 (RegionFactory::create(src, 0, (nframes_t) length,
4539 PBD::basename_nosuffix(src->name()))), start);
4540 XMLNode &after = mtv->playlist()->get_state();
4541 session->add_command(new MementoCommand<Playlist>(*mtv->playlist().get(), &before, &after));
4542 commit_reversible_command();
4545 create_region_drag_motion_callback (item, event);
4546 // TODO: create region-create-drag region here
4551 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
4553 /* Either add to or set the set the region selection, unless
4554 this is an alignment click (control used)
4557 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
4558 TimeAxisView* tv = &rv.get_time_axis_view();
4559 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
4561 if (rtv && rtv->is_track()) {
4562 speed = rtv->get_diskstream()->speed();
4565 nframes64_t where = get_preferred_edit_position();
4569 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
4571 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
4573 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
4575 align_region (rv.region(), End, (nframes64_t) (where * speed));
4579 align_region (rv.region(), Start, (nframes64_t) (where * speed));
4586 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
4592 nframes64_t frame_rate;
4601 if (Profile->get_sae() || Profile->get_small_screen()) {
4602 m = ARDOUR_UI::instance()->primary_clock.mode();
4604 m = ARDOUR_UI::instance()->secondary_clock.mode();
4608 case AudioClock::BBT:
4609 session->bbt_time (frame, bbt);
4610 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
4613 case AudioClock::SMPTE:
4614 session->smpte_time (frame, smpte);
4615 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4618 case AudioClock::MinSec:
4619 /* XXX this is copied from show_verbose_duration_cursor() */
4620 frame_rate = session->frame_rate();
4621 hours = frame / (frame_rate * 3600);
4622 frame = frame % (frame_rate * 3600);
4623 mins = frame / (frame_rate * 60);
4624 frame = frame % (frame_rate * 60);
4625 secs = (float) frame / (float) frame_rate;
4626 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4630 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
4634 if (xpos >= 0 && ypos >=0) {
4635 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4638 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);
4640 show_verbose_canvas_cursor ();
4644 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
4651 nframes64_t distance, frame_rate;
4653 Meter meter_at_start(session->tempo_map().meter_at(start));
4661 if (Profile->get_sae() || Profile->get_small_screen()) {
4662 m = ARDOUR_UI::instance()->primary_clock.mode ();
4664 m = ARDOUR_UI::instance()->secondary_clock.mode ();
4668 case AudioClock::BBT:
4669 session->bbt_time (start, sbbt);
4670 session->bbt_time (end, ebbt);
4673 /* XXX this computation won't work well if the
4674 user makes a selection that spans any meter changes.
4677 ebbt.bars -= sbbt.bars;
4678 if (ebbt.beats >= sbbt.beats) {
4679 ebbt.beats -= sbbt.beats;
4682 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
4684 if (ebbt.ticks >= sbbt.ticks) {
4685 ebbt.ticks -= sbbt.ticks;
4688 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
4691 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
4694 case AudioClock::SMPTE:
4695 session->smpte_duration (end - start, smpte);
4696 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4699 case AudioClock::MinSec:
4700 /* XXX this stuff should be elsewhere.. */
4701 distance = end - start;
4702 frame_rate = session->frame_rate();
4703 hours = distance / (frame_rate * 3600);
4704 distance = distance % (frame_rate * 3600);
4705 mins = distance / (frame_rate * 60);
4706 distance = distance % (frame_rate * 60);
4707 secs = (float) distance / (float) frame_rate;
4708 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4712 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
4716 if (xpos >= 0 && ypos >=0) {
4717 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4720 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4723 show_verbose_canvas_cursor ();
4727 Editor::collect_new_region_view (RegionView* rv)
4729 latest_regionviews.push_back (rv);
4733 Editor::collect_and_select_new_region_view (RegionView* rv)
4736 latest_regionviews.push_back (rv);
4740 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
4742 if (clicked_regionview == 0) {
4746 /* lets try to create new Region for the selection */
4748 vector<boost::shared_ptr<Region> > new_regions;
4749 create_region_from_selection (new_regions);
4751 if (new_regions.empty()) {
4755 /* XXX fix me one day to use all new regions */
4757 boost::shared_ptr<Region> region (new_regions.front());
4759 /* add it to the current stream/playlist.
4761 tricky: the streamview for the track will add a new regionview. we will
4762 catch the signal it sends when it creates the regionview to
4763 set the regionview we want to then drag.
4766 latest_regionviews.clear();
4767 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4769 /* A selection grab currently creates two undo/redo operations, one for
4770 creating the new region and another for moving it.
4773 begin_reversible_command (_("selection grab"));
4775 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
4777 XMLNode *before = &(playlist->get_state());
4778 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
4779 XMLNode *after = &(playlist->get_state());
4780 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4782 commit_reversible_command ();
4786 if (latest_regionviews.empty()) {
4787 /* something went wrong */
4791 /* we need to deselect all other regionviews, and select this one
4792 i'm ignoring undo stuff, because the region creation will take care of it
4794 selection->set (latest_regionviews);
4796 drag_info.item = latest_regionviews.front()->get_canvas_group();
4797 drag_info.data = latest_regionviews.front();
4798 drag_info.motion_callback = &Editor::region_drag_motion_callback;
4799 drag_info.finished_callback = &Editor::region_drag_finished_callback;
4803 drag_info.source_trackview = clicked_routeview;
4804 drag_info.dest_trackview = drag_info.source_trackview;
4805 drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4806 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4808 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4812 Editor::cancel_selection ()
4814 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4815 (*i)->hide_selection ();
4817 selection->clear ();
4818 clicked_selection = 0;
4822 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4824 nframes64_t start = 0;
4825 nframes64_t end = 0;
4831 drag_info.item = item;
4832 drag_info.motion_callback = &Editor::drag_selection;
4833 drag_info.finished_callback = &Editor::end_selection_op;
4838 case CreateSelection:
4839 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4840 drag_info.copy = true;
4842 drag_info.copy = false;
4844 start_grab (event, selector_cursor);
4847 case SelectionStartTrim:
4848 if (clicked_axisview) {
4849 clicked_axisview->order_selection_trims (item, true);
4851 start_grab (event, trimmer_cursor);
4852 start = selection->time[clicked_selection].start;
4853 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4856 case SelectionEndTrim:
4857 if (clicked_axisview) {
4858 clicked_axisview->order_selection_trims (item, false);
4860 start_grab (event, trimmer_cursor);
4861 end = selection->time[clicked_selection].end;
4862 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
4866 start = selection->time[clicked_selection].start;
4868 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4872 if (selection_op == SelectionMove) {
4873 show_verbose_time_cursor(start, 10);
4875 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4880 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4882 nframes64_t start = 0;
4883 nframes64_t end = 0;
4885 nframes64_t pending_position;
4887 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4888 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4890 pending_position = 0;
4893 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4894 snap_to (pending_position);
4897 /* only alter selection if the current frame is
4898 different from the last frame position (adjusted)
4901 if (pending_position == drag_info.last_pointer_frame) return;
4903 switch (selection_op) {
4904 case CreateSelection:
4906 if (drag_info.first_move) {
4907 snap_to (drag_info.grab_frame);
4910 if (pending_position < drag_info.grab_frame) {
4911 start = pending_position;
4912 end = drag_info.grab_frame;
4914 end = pending_position;
4915 start = drag_info.grab_frame;
4918 /* first drag: Either add to the selection
4919 or create a new selection->
4922 if (drag_info.first_move) {
4924 begin_reversible_command (_("range selection"));
4926 if (drag_info.copy) {
4927 /* adding to the selection */
4928 clicked_selection = selection->add (start, end);
4929 drag_info.copy = false;
4931 /* new selection-> */
4932 clicked_selection = selection->set (clicked_axisview, start, end);
4937 case SelectionStartTrim:
4939 if (drag_info.first_move) {
4940 begin_reversible_command (_("trim selection start"));
4943 start = selection->time[clicked_selection].start;
4944 end = selection->time[clicked_selection].end;
4946 if (pending_position > end) {
4949 start = pending_position;
4953 case SelectionEndTrim:
4955 if (drag_info.first_move) {
4956 begin_reversible_command (_("trim selection end"));
4959 start = selection->time[clicked_selection].start;
4960 end = selection->time[clicked_selection].end;
4962 if (pending_position < start) {
4965 end = pending_position;
4972 if (drag_info.first_move) {
4973 begin_reversible_command (_("move selection"));
4976 start = selection->time[clicked_selection].start;
4977 end = selection->time[clicked_selection].end;
4979 length = end - start;
4981 start = pending_position;
4984 end = start + length;
4989 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4990 start_canvas_autoscroll (1, 0);
4994 selection->replace (clicked_selection, start, end);
4997 drag_info.last_pointer_frame = pending_position;
4998 drag_info.first_move = false;
5000 if (selection_op == SelectionMove) {
5001 show_verbose_time_cursor(start, 10);
5003 show_verbose_time_cursor(pending_position, 10);
5008 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
5010 if (!drag_info.first_move) {
5011 drag_selection (item, event);
5012 /* XXX this is not object-oriented programming at all. ick */
5013 if (selection->time.consolidate()) {
5014 selection->TimeChanged ();
5016 commit_reversible_command ();
5018 /* just a click, no pointer movement.*/
5020 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
5022 selection->clear_time();
5027 /* XXX what happens if its a music selection? */
5028 session->set_audio_range (selection->time);
5029 stop_canvas_autoscroll ();
5033 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
5036 TimeAxisView* tvp = clicked_axisview;
5037 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
5039 if (tv && tv->is_track()) {
5040 speed = tv->get_diskstream()->speed();
5043 nframes64_t region_start = (nframes64_t) (clicked_regionview->region()->position() / speed);
5044 nframes64_t region_end = (nframes64_t) (clicked_regionview->region()->last_frame() / speed);
5045 nframes64_t region_length = (nframes64_t) (clicked_regionview->region()->length() / speed);
5047 //drag_info.item = clicked_regionview->get_name_highlight();
5048 drag_info.item = item;
5049 drag_info.motion_callback = &Editor::trim_motion_callback;
5050 drag_info.finished_callback = &Editor::trim_finished_callback;
5052 start_grab (event, trimmer_cursor);
5054 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
5055 trim_op = ContentsTrim;
5057 /* These will get overridden for a point trim.*/
5058 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
5059 /* closer to start */
5060 trim_op = StartTrim;
5061 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
5069 show_verbose_time_cursor(region_start, 10);
5072 show_verbose_time_cursor(region_end, 10);
5075 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5081 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
5083 RegionView* rv = clicked_regionview;
5084 nframes64_t frame_delta = 0;
5085 bool left_direction;
5086 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
5088 /* snap modifier works differently here..
5089 its' current state has to be passed to the
5090 various trim functions in order to work properly
5094 TimeAxisView* tvp = clicked_axisview;
5095 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
5096 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
5098 if (tv && tv->is_track()) {
5099 speed = tv->get_diskstream()->speed();
5102 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
5103 left_direction = true;
5105 left_direction = false;
5109 snap_to (drag_info.current_pointer_frame);
5112 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5116 if (drag_info.first_move) {
5122 trim_type = "Region start trim";
5125 trim_type = "Region end trim";
5128 trim_type = "Region content trim";
5132 begin_reversible_command (trim_type);
5134 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
5135 (*i)->fake_set_opaque(false);
5136 (*i)->region()->freeze ();
5138 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5140 arv->temporarily_hide_envelope ();
5142 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5143 insert_result = motion_frozen_playlists.insert (pl);
5144 if (insert_result.second) {
5145 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
5151 if (left_direction) {
5152 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
5154 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
5159 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
5162 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
5163 single_start_trim (**i, frame_delta, left_direction, obey_snap);
5169 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes64_t) (rv->region()->last_frame()/speed))) {
5172 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
5173 single_end_trim (**i, frame_delta, left_direction, obey_snap);
5180 bool swap_direction = false;
5182 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
5183 swap_direction = true;
5186 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5187 i != selection->regions.by_layer().end(); ++i)
5189 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
5197 show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
5200 show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
5203 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5207 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5208 drag_info.first_move = false;
5212 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
5214 boost::shared_ptr<Region> region (rv.region());
5216 if (region->locked()) {
5220 nframes64_t new_bound;
5223 TimeAxisView* tvp = clicked_axisview;
5224 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
5226 if (tv && tv->is_track()) {
5227 speed = tv->get_diskstream()->speed();
5230 if (left_direction) {
5231 if (swap_direction) {
5232 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
5234 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
5237 if (swap_direction) {
5238 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
5240 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
5245 snap_to (new_bound);
5247 region->trim_start ((nframes64_t) (new_bound * speed), this);
5248 rv.region_changed (StartChanged);
5252 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
5254 boost::shared_ptr<Region> region (rv.region());
5256 if (region->locked()) {
5260 nframes64_t new_bound;
5263 TimeAxisView* tvp = clicked_axisview;
5264 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
5266 if (tv && tv->is_track()) {
5267 speed = tv->get_diskstream()->speed();
5270 if (left_direction) {
5271 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
5273 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
5277 snap_to (new_bound, (left_direction ? 0 : 1));
5280 region->trim_front ((nframes64_t) (new_bound * speed), this);
5282 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
5286 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
5288 boost::shared_ptr<Region> region (rv.region());
5290 if (region->locked()) {
5294 nframes64_t new_bound;
5297 TimeAxisView* tvp = clicked_axisview;
5298 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
5300 if (tv && tv->is_track()) {
5301 speed = tv->get_diskstream()->speed();
5304 if (left_direction) {
5305 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
5307 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
5311 snap_to (new_bound);
5313 region->trim_end ((nframes64_t) (new_bound * speed), this);
5314 rv.region_changed (LengthChanged);
5318 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
5320 if (!drag_info.first_move) {
5321 trim_motion_callback (item, event);
5323 if (!selection->selected (clicked_regionview)) {
5324 thaw_region_after_trim (*clicked_regionview);
5327 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5328 i != selection->regions.by_layer().end(); ++i)
5330 thaw_region_after_trim (**i);
5331 (*i)->fake_set_opaque (true);
5335 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
5337 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
5340 motion_frozen_playlists.clear ();
5342 commit_reversible_command();
5344 /* no mouse movement */
5350 Editor::point_trim (GdkEvent* event)
5352 RegionView* rv = clicked_regionview;
5353 nframes64_t new_bound = drag_info.current_pointer_frame;
5355 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5356 snap_to (new_bound);
5359 /* Choose action dependant on which button was pressed */
5360 switch (event->button.button) {
5362 trim_op = StartTrim;
5363 begin_reversible_command (_("Start point trim"));
5365 if (selection->selected (rv)) {
5367 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5368 i != selection->regions.by_layer().end(); ++i)
5370 if (!(*i)->region()->locked()) {
5371 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5372 XMLNode &before = pl->get_state();
5373 (*i)->region()->trim_front (new_bound, this);
5374 XMLNode &after = pl->get_state();
5375 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5381 if (!rv->region()->locked()) {
5382 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5383 XMLNode &before = pl->get_state();
5384 rv->region()->trim_front (new_bound, this);
5385 XMLNode &after = pl->get_state();
5386 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5390 commit_reversible_command();
5395 begin_reversible_command (_("End point trim"));
5397 if (selection->selected (rv)) {
5399 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
5401 if (!(*i)->region()->locked()) {
5402 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5403 XMLNode &before = pl->get_state();
5404 (*i)->region()->trim_end (new_bound, this);
5405 XMLNode &after = pl->get_state();
5406 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5412 if (!rv->region()->locked()) {
5413 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5414 XMLNode &before = pl->get_state();
5415 rv->region()->trim_end (new_bound, this);
5416 XMLNode &after = pl->get_state();
5417 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
5421 commit_reversible_command();
5430 Editor::thaw_region_after_trim (RegionView& rv)
5432 boost::shared_ptr<Region> region (rv.region());
5434 if (region->locked()) {
5438 region->thaw (_("trimmed region"));
5439 XMLNode &after = region->playlist()->get_state();
5440 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
5442 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
5444 arv->unhide_envelope ();
5448 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
5453 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
5454 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
5458 Location* location = find_location_from_marker (marker, is_start);
5459 location->set_hidden (true, this);
5464 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
5470 drag_info.item = item;
5471 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
5472 drag_info.finished_callback = &Editor::end_range_markerbar_op;
5474 range_marker_op = op;
5476 if (!temp_location) {
5477 temp_location = new Location;
5481 case CreateRangeMarker:
5482 case CreateTransportMarker:
5483 case CreateCDMarker:
5485 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
5486 drag_info.copy = true;
5488 drag_info.copy = false;
5490 start_grab (event, selector_cursor);
5494 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5499 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5501 nframes64_t start = 0;
5502 nframes64_t end = 0;
5503 ArdourCanvas::SimpleRect *crect;
5505 switch (range_marker_op) {
5506 case CreateRangeMarker:
5507 crect = range_bar_drag_rect;
5509 case CreateTransportMarker:
5510 crect = transport_bar_drag_rect;
5512 case CreateCDMarker:
5513 crect = cd_marker_bar_drag_rect;
5516 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
5521 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5522 snap_to (drag_info.current_pointer_frame);
5525 /* only alter selection if the current frame is
5526 different from the last frame position.
5529 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5531 switch (range_marker_op) {
5532 case CreateRangeMarker:
5533 case CreateTransportMarker:
5534 case CreateCDMarker:
5535 if (drag_info.first_move) {
5536 snap_to (drag_info.grab_frame);
5539 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5540 start = drag_info.current_pointer_frame;
5541 end = drag_info.grab_frame;
5543 end = drag_info.current_pointer_frame;
5544 start = drag_info.grab_frame;
5547 /* first drag: Either add to the selection
5548 or create a new selection.
5551 if (drag_info.first_move) {
5553 temp_location->set (start, end);
5557 update_marker_drag_item (temp_location);
5558 range_marker_drag_rect->show();
5559 //range_marker_drag_rect->raise_to_top();
5565 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
5566 start_canvas_autoscroll (1, 0);
5570 temp_location->set (start, end);
5572 double x1 = frame_to_pixel (start);
5573 double x2 = frame_to_pixel (end);
5574 crect->property_x1() = x1;
5575 crect->property_x2() = x2;
5577 update_marker_drag_item (temp_location);
5580 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5581 drag_info.first_move = false;
5583 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5588 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5590 Location * newloc = 0;
5594 if (!drag_info.first_move) {
5595 drag_range_markerbar_op (item, event);
5597 switch (range_marker_op) {
5598 case CreateRangeMarker:
5599 case CreateCDMarker:
5601 begin_reversible_command (_("new range marker"));
5602 XMLNode &before = session->locations()->get_state();
5603 session->locations()->next_available_name(rangename,"unnamed");
5604 if (range_marker_op == CreateCDMarker) {
5605 flags = Location::IsRangeMarker|Location::IsCDMarker;
5606 cd_marker_bar_drag_rect->hide();
5609 flags = Location::IsRangeMarker;
5610 range_bar_drag_rect->hide();
5612 newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
5613 session->locations()->add (newloc, true);
5614 XMLNode &after = session->locations()->get_state();
5615 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
5616 commit_reversible_command ();
5618 range_marker_drag_rect->hide();
5622 case CreateTransportMarker:
5623 // popup menu to pick loop or punch
5624 new_transport_marker_context_menu (&event->button, item);
5629 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5631 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
5636 start = session->locations()->first_mark_before (drag_info.grab_frame);
5637 end = session->locations()->first_mark_after (drag_info.grab_frame);
5639 if (end == max_frames) {
5640 end = session->current_end_frame ();
5644 start = session->current_start_frame ();
5647 switch (mouse_mode) {
5649 /* find the two markers on either side and then make the selection from it */
5650 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
5654 /* find the two markers on either side of the click and make the range out of it */
5655 selection->set (0, start, end);
5664 stop_canvas_autoscroll ();
5670 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5672 drag_info.item = item;
5673 drag_info.motion_callback = &Editor::drag_mouse_zoom;
5674 drag_info.finished_callback = &Editor::end_mouse_zoom;
5676 start_grab (event, zoom_cursor);
5678 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5682 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5687 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5688 snap_to (drag_info.current_pointer_frame);
5690 if (drag_info.first_move) {
5691 snap_to (drag_info.grab_frame);
5695 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5697 /* base start and end on initial click position */
5698 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5699 start = drag_info.current_pointer_frame;
5700 end = drag_info.grab_frame;
5702 end = drag_info.current_pointer_frame;
5703 start = drag_info.grab_frame;
5708 if (drag_info.first_move) {
5710 zoom_rect->raise_to_top();
5713 reposition_zoom_rect(start, end);
5715 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5716 drag_info.first_move = false;
5718 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5723 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5725 if (!drag_info.first_move) {
5726 drag_mouse_zoom (item, event);
5728 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5729 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
5731 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
5734 temporal_zoom_to_frame (false, drag_info.grab_frame);
5736 temporal_zoom_step (false);
5737 center_screen (drag_info.grab_frame);
5745 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
5747 double x1 = frame_to_pixel (start);
5748 double x2 = frame_to_pixel (end);
5749 double y2 = full_canvas_height - 1.0;
5751 zoom_rect->property_x1() = x1;
5752 zoom_rect->property_y1() = 1.0;
5753 zoom_rect->property_x2() = x2;
5754 zoom_rect->property_y2() = y2;
5758 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5760 drag_info.item = item;
5761 drag_info.motion_callback = &Editor::drag_rubberband_select;
5762 drag_info.finished_callback = &Editor::end_rubberband_select;
5764 start_grab (event, cross_hair_cursor);
5766 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5770 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5777 /* use a bigger drag threshold than the default */
5779 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
5783 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) {
5784 if (drag_info.first_move) {
5785 snap_to (drag_info.grab_frame);
5787 snap_to (drag_info.current_pointer_frame);
5790 /* base start and end on initial click position */
5792 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5793 start = drag_info.current_pointer_frame;
5794 end = drag_info.grab_frame;
5796 end = drag_info.current_pointer_frame;
5797 start = drag_info.grab_frame;
5800 if (drag_info.current_pointer_y < drag_info.grab_y) {
5801 y1 = drag_info.current_pointer_y;
5802 y2 = drag_info.grab_y;
5804 y2 = drag_info.current_pointer_y;
5805 y1 = drag_info.grab_y;
5809 if (start != end || y1 != y2) {
5811 double x1 = frame_to_pixel (start);
5812 double x2 = frame_to_pixel (end);
5814 rubberband_rect->property_x1() = x1;
5815 rubberband_rect->property_y1() = y1;
5816 rubberband_rect->property_x2() = x2;
5817 rubberband_rect->property_y2() = y2;
5819 rubberband_rect->show();
5820 rubberband_rect->raise_to_top();
5822 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5823 drag_info.first_move = false;
5825 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5830 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5832 if (!drag_info.first_move) {
5834 drag_rubberband_select (item, event);
5837 if (drag_info.current_pointer_y < drag_info.grab_y) {
5838 y1 = drag_info.current_pointer_y;
5839 y2 = drag_info.grab_y;
5841 y2 = drag_info.current_pointer_y;
5842 y1 = drag_info.grab_y;
5846 Selection::Operation op = Keyboard::selection_type (event->button.state);
5849 begin_reversible_command (_("rubberband selection"));
5851 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5852 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5854 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5858 commit_reversible_command ();
5862 if (!getenv("ARDOUR_SAE")) {
5863 selection->clear_tracks();
5865 selection->clear_regions();
5866 selection->clear_points ();
5867 selection->clear_lines ();
5870 rubberband_rect->hide();
5875 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5877 using namespace Gtkmm2ext;
5879 ArdourPrompter prompter (false);
5881 prompter.set_prompt (_("Name for region:"));
5882 prompter.set_initial_text (clicked_regionview->region()->name());
5883 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5884 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5885 prompter.show_all ();
5886 switch (prompter.run ()) {
5887 case Gtk::RESPONSE_ACCEPT:
5889 prompter.get_result(str);
5891 clicked_regionview->region()->set_name (str);
5899 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5901 drag_info.item = item;
5902 drag_info.motion_callback = &Editor::time_fx_motion;
5903 drag_info.finished_callback = &Editor::end_time_fx;
5907 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5911 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5913 RegionView* rv = clicked_regionview;
5915 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5916 snap_to (drag_info.current_pointer_frame);
5919 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5923 if (drag_info.current_pointer_frame > rv->region()->position()) {
5924 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5927 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5928 drag_info.first_move = false;
5930 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5934 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5936 clicked_regionview->get_time_axis_view().hide_timestretch ();
5938 if (drag_info.first_move) {
5942 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5943 /* backwards drag of the left edge - not usable */
5947 nframes64_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5949 float percentage = (double) newlen / (double) clicked_regionview->region()->length();
5951 #ifndef USE_RUBBERBAND
5952 // Soundtouch uses percentage / 100 instead of normal (/ 1)
5953 if (clicked_regionview->region()->data_type() == DataType::AUDIO) {
5954 percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5958 begin_reversible_command (_("timestretch"));
5960 // XXX how do timeFX on multiple regions ?
5963 rs.add (clicked_regionview);
5965 if (time_stretch (rs, percentage) == 0) {
5966 session->commit_reversible_command ();
5971 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
5973 /* no brushing without a useful snap setting */
5975 switch (snap_mode) {
5977 return; /* can't work because it allows region to be placed anywhere */
5982 switch (snap_type) {
5990 /* don't brush a copy over the original */
5992 if (pos == rv->region()->position()) {
5996 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
5998 if (rtv == 0 || !rtv->is_track()) {
6002 boost::shared_ptr<Playlist> playlist = rtv->playlist();
6003 double speed = rtv->get_diskstream()->speed();
6005 XMLNode &before = playlist->get_state();
6006 playlist->add_region (RegionFactory::create (rv->region()), (nframes64_t) (pos * speed));
6007 XMLNode &after = playlist->get_state();
6008 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
6010 // playlist is frozen, so we have to update manually
6012 playlist->Modified(); /* EMIT SIGNAL */
6016 Editor::track_height_step_timeout ()
6018 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
6019 current_stepping_trackview = 0;