3 Copyright (C) 2000-2001 Paul Davis
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <pbd/error.h>
30 #include <gtkmm2ext/utils.h>
31 #include <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"
52 #include <ardour/types.h>
53 #include <ardour/profile.h>
54 #include <ardour/route.h>
55 #include <ardour/audio_track.h>
56 #include <ardour/audio_diskstream.h>
57 #include <ardour/midi_diskstream.h>
58 #include <ardour/playlist.h>
59 #include <ardour/audioplaylist.h>
60 #include <ardour/audioregion.h>
61 #include <ardour/midi_region.h>
62 #include <ardour/dB.h>
63 #include <ardour/utils.h>
64 #include <ardour/region_factory.h>
65 #include <ardour/source_factory.h>
72 using namespace ARDOUR;
76 using namespace Editing;
78 const static double ZERO_GAIN_FRACTION = gain_to_slider_position(dB_to_coefficient(0.0));
81 Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
85 Gdk::ModifierType mask;
86 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
87 Glib::RefPtr<const Gdk::Window> pointer_window;
93 pointer_window = canvas_window->get_pointer (x, y, mask);
95 if (pointer_window == track_canvas->get_bin_window()) {
98 in_track_canvas = true;
101 in_track_canvas = false;
106 event.type = GDK_BUTTON_RELEASE;
110 where = event_frame (&event, 0, 0);
115 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy) const
129 switch (event->type) {
130 case GDK_BUTTON_RELEASE:
131 case GDK_BUTTON_PRESS:
132 case GDK_2BUTTON_PRESS:
133 case GDK_3BUTTON_PRESS:
135 *pcx = event->button.x;
136 *pcy = event->button.y;
137 _trackview_group->w2i(*pcx, *pcy);
139 case GDK_MOTION_NOTIFY:
141 *pcx = event->motion.x;
142 *pcy = event->motion.y;
143 _trackview_group->w2i(*pcx, *pcy);
145 case GDK_ENTER_NOTIFY:
146 case GDK_LEAVE_NOTIFY:
147 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
150 case GDK_KEY_RELEASE:
151 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
154 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
158 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
159 position is negative (as can be the case with motion events in particular),
160 the frame location is always positive.
163 return pixel_to_frame (*pcx);
167 Editor::mouse_mode_toggled (MouseMode m)
169 if (ignore_mouse_mode_toggle) {
175 if (mouse_select_button.get_active()) {
181 if (mouse_move_button.get_active()) {
187 if (mouse_gain_button.get_active()) {
193 if (mouse_zoom_button.get_active()) {
199 if (mouse_timefx_button.get_active()) {
205 if (mouse_audition_button.get_active()) {
211 if (mouse_note_button.get_active()) {
222 Editor::which_grabber_cursor ()
224 switch (_edit_point) {
226 return grabber_edit_point_cursor;
231 return grabber_cursor;
235 Editor::set_canvas_cursor ()
237 switch (mouse_mode) {
239 current_canvas_cursor = selector_cursor;
243 current_canvas_cursor = which_grabber_cursor();
247 current_canvas_cursor = cross_hair_cursor;
251 current_canvas_cursor = zoom_cursor;
255 current_canvas_cursor = time_fx_cursor; // just use playhead
259 current_canvas_cursor = speaker_cursor;
263 set_midi_edit_cursor (current_midi_edit_mode());
268 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
273 Editor::set_mouse_mode (MouseMode m, bool force)
275 if (drag_info.item) {
279 if (!force && m == mouse_mode) {
287 if (mouse_mode != MouseRange) {
289 /* in all modes except range, hide the range selection,
290 show the object (region) selection.
293 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
294 (*i)->set_should_show_selection (true);
296 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
297 (*i)->hide_selection ();
303 in range mode,show the range selection.
306 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
307 if ((*i)->get_selected()) {
308 (*i)->show_selection (selection->time);
313 /* XXX the hack of unsetting all other buttons should go
314 away once GTK2 allows us to use regular radio buttons drawn like
315 normal buttons, rather than my silly GroupedButton hack.
318 ignore_mouse_mode_toggle = true;
320 switch (mouse_mode) {
322 mouse_select_button.set_active (true);
326 mouse_move_button.set_active (true);
330 mouse_gain_button.set_active (true);
334 mouse_zoom_button.set_active (true);
338 mouse_timefx_button.set_active (true);
342 mouse_audition_button.set_active (true);
346 mouse_note_button.set_active (true);
347 set_midi_edit_cursor (current_midi_edit_mode());
351 if (mouse_mode == MouseNote)
352 midi_toolbar_frame.show();
354 midi_toolbar_frame.hide();
356 ignore_mouse_mode_toggle = false;
358 set_canvas_cursor ();
362 Editor::step_mouse_mode (bool next)
364 switch (current_mouse_mode()) {
366 if (next) set_mouse_mode (MouseRange);
367 else set_mouse_mode (MouseTimeFX);
371 if (next) set_mouse_mode (MouseZoom);
372 else set_mouse_mode (MouseObject);
376 if (next) set_mouse_mode (MouseGain);
377 else set_mouse_mode (MouseRange);
381 if (next) set_mouse_mode (MouseTimeFX);
382 else set_mouse_mode (MouseZoom);
386 if (next) set_mouse_mode (MouseAudition);
387 else set_mouse_mode (MouseGain);
391 if (next) set_mouse_mode (MouseObject);
392 else set_mouse_mode (MouseTimeFX);
396 if (next) set_mouse_mode (MouseObject);
397 else set_mouse_mode (MouseAudition);
403 Editor::midi_edit_mode_toggled (MidiEditMode m)
405 if (ignore_midi_edit_mode_toggle) {
411 if (midi_tool_pencil_button.get_active())
412 set_midi_edit_mode (m);
416 if (midi_tool_select_button.get_active())
417 set_midi_edit_mode (m);
421 if (midi_tool_resize_button.get_active())
422 set_midi_edit_mode (m);
426 if (midi_tool_erase_button.get_active())
427 set_midi_edit_mode (m);
434 set_midi_edit_cursor(m);
439 Editor::set_midi_edit_mode (MidiEditMode m, bool force)
441 if (drag_info.item) {
445 if (!force && m == midi_edit_mode) {
453 ignore_midi_edit_mode_toggle = true;
455 switch (midi_edit_mode) {
457 midi_tool_pencil_button.set_active (true);
461 midi_tool_select_button.set_active (true);
465 midi_tool_resize_button.set_active (true);
469 midi_tool_erase_button.set_active (true);
473 ignore_midi_edit_mode_toggle = false;
475 set_midi_edit_cursor (current_midi_edit_mode());
478 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
483 Editor::set_midi_edit_cursor (MidiEditMode m)
485 switch (midi_edit_mode) {
487 current_canvas_cursor = midi_pencil_cursor;
491 current_canvas_cursor = midi_select_cursor;
495 current_canvas_cursor = midi_resize_cursor;
499 current_canvas_cursor = midi_erase_cursor;
505 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
507 /* in object/audition/timefx mode, any button press sets
508 the selection if the object can be selected. this is a
509 bit of hack, because we want to avoid this if the
510 mouse operation is a region alignment.
512 note: not dbl-click or triple-click
515 if (((mouse_mode != MouseObject) &&
516 (mouse_mode != MouseAudition || item_type != RegionItem) &&
517 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
518 (mouse_mode != MouseRange)) ||
520 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
525 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
527 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
529 /* almost no selection action on modified button-2 or button-3 events */
531 if (item_type != RegionItem && event->button.button != 2) {
537 Selection::Operation op = Keyboard::selection_type (event->button.state);
538 bool press = (event->type == GDK_BUTTON_PRESS);
540 // begin_reversible_command (_("select on click"));
544 if (mouse_mode != MouseRange) {
545 set_selected_regionview_from_click (press, op, true);
546 } else if (event->type == GDK_BUTTON_PRESS) {
547 set_selected_track_as_side_effect ();
551 case RegionViewNameHighlight:
553 if (mouse_mode != MouseRange) {
554 set_selected_regionview_from_click (press, op, true);
555 } else if (event->type == GDK_BUTTON_PRESS) {
556 set_selected_track_as_side_effect ();
561 case FadeInHandleItem:
563 case FadeOutHandleItem:
565 if (mouse_mode != MouseRange) {
566 set_selected_regionview_from_click (press, op, true);
567 } else if (event->type == GDK_BUTTON_PRESS) {
568 set_selected_track_as_side_effect ();
572 case ControlPointItem:
573 set_selected_track_as_side_effect ();
574 if (mouse_mode != MouseRange) {
575 set_selected_control_point_from_click (op, false);
580 /* for context click or range selection, select track */
581 if (event->button.button == 3) {
582 set_selected_track_as_side_effect ();
583 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
584 set_selected_track_as_side_effect ();
588 case AutomationTrackItem:
589 set_selected_track_as_side_effect (true);
598 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
600 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
603 Glib::RefPtr<const Gdk::Window> pointer_window;
606 Gdk::ModifierType mask;
608 pointer_window = canvas_window->get_pointer (x, y, mask);
610 if (pointer_window == track_canvas->get_bin_window()) {
611 track_canvas->window_to_world (x, y, wx, wy);
612 allow_vertical_scroll = true;
614 allow_vertical_scroll = false;
618 track_canvas->grab_focus();
620 if (session && session->actively_recording()) {
624 button_selection (item, event, item_type);
626 if (drag_info.item == 0 &&
627 (Keyboard::is_delete_event (&event->button) ||
628 Keyboard::is_context_menu_event (&event->button) ||
629 Keyboard::is_edit_event (&event->button))) {
631 /* handled by button release */
635 switch (event->button.button) {
638 if (event->type == GDK_BUTTON_PRESS) {
640 if (drag_info.item) {
641 drag_info.item->ungrab (event->button.time);
644 /* single mouse clicks on any of these item types operate
645 independent of mouse mode, mostly because they are
646 not on the main track canvas or because we want
651 case PlayheadCursorItem:
652 start_cursor_grab (item, event);
656 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
657 hide_marker (item, event);
659 start_marker_grab (item, event);
663 case TempoMarkerItem:
664 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
665 start_tempo_marker_copy_grab (item, event);
667 start_tempo_marker_grab (item, event);
671 case MeterMarkerItem:
672 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
673 start_meter_marker_copy_grab (item, event);
675 start_meter_marker_grab (item, event);
685 case RangeMarkerBarItem:
686 start_range_markerbar_op (item, event, CreateRangeMarker);
690 case CdMarkerBarItem:
691 start_range_markerbar_op (item, event, CreateCDMarker);
695 case TransportMarkerBarItem:
696 start_range_markerbar_op (item, event, CreateTransportMarker);
705 switch (mouse_mode) {
708 case StartSelectionTrimItem:
709 start_selection_op (item, event, SelectionStartTrim);
712 case EndSelectionTrimItem:
713 start_selection_op (item, event, SelectionEndTrim);
717 if (Keyboard::modifier_state_contains
718 (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) {
719 // contains and not equals because I can't use alt as a modifier alone.
720 start_selection_grab (item, event);
721 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
722 /* grab selection for moving */
723 start_selection_op (item, event, SelectionMove);
725 /* this was debated, but decided the more common action was to
726 make a new selection */
727 start_selection_op (item, event, CreateSelection);
732 start_selection_op (item, event, CreateSelection);
738 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
739 event->type == GDK_BUTTON_PRESS) {
741 start_rubberband_select (item, event);
743 } else if (event->type == GDK_BUTTON_PRESS) {
746 case FadeInHandleItem:
747 start_fade_in_grab (item, event);
750 case FadeOutHandleItem:
751 start_fade_out_grab (item, event);
755 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
756 start_region_copy_grab (item, event);
757 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
758 start_region_brush_grab (item, event);
760 start_region_grab (item, event);
764 case RegionViewNameHighlight:
765 start_trim (item, event);
770 /* rename happens on edit clicks */
771 start_trim (clicked_regionview->get_name_highlight(), event);
775 case ControlPointItem:
776 start_control_point_grab (item, event);
780 case AutomationLineItem:
781 start_line_grab_from_line (item, event);
786 case AutomationTrackItem:
787 start_rubberband_select (item, event);
791 case ImageFrameHandleStartItem:
792 imageframe_start_handle_op(item, event) ;
795 case ImageFrameHandleEndItem:
796 imageframe_end_handle_op(item, event) ;
799 case MarkerViewHandleStartItem:
800 markerview_item_start_handle_op(item, event) ;
803 case MarkerViewHandleEndItem:
804 markerview_item_end_handle_op(item, event) ;
808 start_markerview_grab(item, event) ;
811 start_imageframe_grab(item, event) ;
829 // start_line_grab_from_regionview (item, event);
833 start_line_grab_from_line (item, event);
836 case ControlPointItem:
837 start_control_point_grab (item, event);
848 case ControlPointItem:
849 start_control_point_grab (item, event);
852 case AutomationLineItem:
853 start_line_grab_from_line (item, event);
857 // XXX need automation mode to identify which
859 // start_line_grab_from_regionview (item, event);
869 if (event->type == GDK_BUTTON_PRESS) {
870 start_mouse_zoom (item, event);
877 if (item_type == RegionItem) {
878 start_time_fx (item, event);
885 scrub_reverse_distance = 0;
886 last_scrub_x = event->button.x;
887 scrubbing_direction = 0;
888 track_canvas->get_window()->set_cursor (*transparent_cursor);
889 /* rest handled in motion & release */
893 start_create_region_grab (item, event);
902 switch (mouse_mode) {
904 if (event->type == GDK_BUTTON_PRESS) {
907 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
908 start_region_copy_grab (item, event);
910 start_region_grab (item, event);
914 case ControlPointItem:
915 start_control_point_grab (item, event);
926 case RegionViewNameHighlight:
927 start_trim (item, event);
932 start_trim (clicked_regionview->get_name_highlight(), event);
943 if (event->type == GDK_BUTTON_PRESS) {
944 /* relax till release */
951 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
952 temporal_zoom_session();
954 temporal_zoom_to_frame (true, event_frame(event));
977 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
979 nframes64_t where = event_frame (event, 0, 0);
980 AutomationTimeAxisView* atv = 0;
982 /* no action if we're recording */
984 if (session && session->actively_recording()) {
988 /* first, see if we're finishing a drag ... */
990 if (drag_info.item) {
991 if (end_grab (item, event)) {
992 /* grab dragged, so do nothing else */
997 button_selection (item, event, item_type);
999 /* edit events get handled here */
1001 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
1002 switch (item_type) {
1007 case TempoMarkerItem:
1008 edit_tempo_marker (item);
1011 case MeterMarkerItem:
1012 edit_meter_marker (item);
1015 case RegionViewName:
1016 if (clicked_regionview->name_active()) {
1017 return mouse_rename_region (item, event);
1027 /* context menu events get handled here */
1029 if (Keyboard::is_context_menu_event (&event->button)) {
1031 if (drag_info.item == 0) {
1033 /* no matter which button pops up the context menu, tell the menu
1034 widget to use button 1 to drive menu selection.
1037 switch (item_type) {
1039 case FadeInHandleItem:
1041 case FadeOutHandleItem:
1042 popup_fade_context_menu (1, event->button.time, item, item_type);
1046 popup_track_context_menu (1, event->button.time, item_type, false, where);
1050 case RegionViewNameHighlight:
1051 case RegionViewName:
1052 popup_track_context_menu (1, event->button.time, item_type, false, where);
1056 popup_track_context_menu (1, event->button.time, item_type, true, where);
1059 case AutomationTrackItem:
1060 popup_track_context_menu (1, event->button.time, item_type, false, where);
1064 case RangeMarkerBarItem:
1065 case TransportMarkerBarItem:
1066 case CdMarkerBarItem:
1069 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
1073 marker_context_menu (&event->button, item);
1076 case TempoMarkerItem:
1077 tm_marker_context_menu (&event->button, item);
1080 case MeterMarkerItem:
1081 tm_marker_context_menu (&event->button, item);
1084 case CrossfadeViewItem:
1085 popup_track_context_menu (1, event->button.time, item_type, false, where);
1089 case ImageFrameItem:
1090 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1092 case ImageFrameTimeAxisItem:
1093 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1095 case MarkerViewItem:
1096 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1098 case MarkerTimeAxisItem:
1099 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1111 /* delete events get handled here */
1113 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
1115 switch (item_type) {
1116 case TempoMarkerItem:
1117 remove_tempo_marker (item);
1120 case MeterMarkerItem:
1121 remove_meter_marker (item);
1125 remove_marker (*item, event);
1129 if (mouse_mode == MouseObject) {
1130 remove_clicked_region ();
1134 case ControlPointItem:
1135 if (mouse_mode == MouseGain) {
1136 remove_gain_control_point (item, event);
1138 remove_control_point (item, event);
1148 switch (event->button.button) {
1151 switch (item_type) {
1152 /* see comments in button_press_handler */
1153 case PlayheadCursorItem:
1156 case AutomationLineItem:
1157 case StartSelectionTrimItem:
1158 case EndSelectionTrimItem:
1162 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1163 snap_to (where, 0, true);
1165 mouse_add_new_marker (where);
1168 case CdMarkerBarItem:
1169 // if we get here then a dragged range wasn't done
1170 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1171 snap_to (where, 0, true);
1173 mouse_add_new_marker (where, true);
1177 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1180 mouse_add_new_tempo_event (where);
1184 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1192 switch (mouse_mode) {
1194 switch (item_type) {
1195 case AutomationTrackItem:
1196 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_routeview);
1198 atv->add_automation_event (item, event, where, event->button.y);
1210 // Gain only makes sense for audio regions
1212 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1216 switch (item_type) {
1218 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1222 case AutomationTrackItem:
1223 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1224 add_automation_event (item, event, where, event->button.y);
1234 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1235 if (scrubbing_direction == 0) {
1236 /* no drag, just a click */
1237 switch (item_type) {
1239 play_selected_region ();
1245 /* make sure we stop */
1246 session->request_transport_speed (0.0);
1260 switch (mouse_mode) {
1263 switch (item_type) {
1265 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1267 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1270 // Button2 click is unused
1283 // x_style_paste (where, 1.0);
1303 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1309 if (last_item_entered != item) {
1310 last_item_entered = item;
1311 last_item_entered_n = 0;
1314 switch (item_type) {
1315 case ControlPointItem:
1316 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1317 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1318 cp->set_visible (true);
1322 at_y = cp->get_y ();
1323 cp->item()->i2w (at_x, at_y);
1327 fraction = 1.0 - (cp->get_y() / cp->line().height());
1329 if (is_drawable() && !_scrubbing) {
1330 track_canvas->get_window()->set_cursor (*fader_cursor);
1333 last_item_entered_n++;
1334 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1335 if (last_item_entered_n < 10) {
1336 show_verbose_canvas_cursor ();
1342 if (mouse_mode == MouseGain) {
1343 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1345 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1346 if (is_drawable()) {
1347 track_canvas->get_window()->set_cursor (*fader_cursor);
1352 case AutomationLineItem:
1353 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1355 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1357 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1359 if (is_drawable()) {
1360 track_canvas->get_window()->set_cursor (*fader_cursor);
1365 case RegionViewNameHighlight:
1366 if (is_drawable() && mouse_mode == MouseObject) {
1367 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1371 case StartSelectionTrimItem:
1372 case EndSelectionTrimItem:
1375 case ImageFrameHandleStartItem:
1376 case ImageFrameHandleEndItem:
1377 case MarkerViewHandleStartItem:
1378 case MarkerViewHandleEndItem:
1381 if (is_drawable()) {
1382 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1386 case PlayheadCursorItem:
1387 if (is_drawable()) {
1388 switch (_edit_point) {
1390 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1393 track_canvas->get_window()->set_cursor (*grabber_cursor);
1399 case RegionViewName:
1401 /* when the name is not an active item, the entire name highlight is for trimming */
1403 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1404 if (mouse_mode == MouseObject && is_drawable()) {
1405 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1411 case AutomationTrackItem:
1412 if (is_drawable()) {
1413 Gdk::Cursor *cursor;
1414 switch (mouse_mode) {
1416 cursor = selector_cursor;
1419 cursor = zoom_cursor;
1422 cursor = cross_hair_cursor;
1426 track_canvas->get_window()->set_cursor (*cursor);
1428 AutomationTimeAxisView* atv;
1429 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1430 clear_entered_track = false;
1431 set_entered_track (atv);
1437 case RangeMarkerBarItem:
1438 case TransportMarkerBarItem:
1439 case CdMarkerBarItem:
1442 if (is_drawable()) {
1443 track_canvas->get_window()->set_cursor (*timebar_cursor);
1448 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1451 entered_marker = marker;
1452 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1454 case MeterMarkerItem:
1455 case TempoMarkerItem:
1456 if (is_drawable()) {
1457 track_canvas->get_window()->set_cursor (*timebar_cursor);
1460 case FadeInHandleItem:
1461 case FadeOutHandleItem:
1462 if (mouse_mode == MouseObject) {
1463 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1465 rect->property_fill_color_rgba() = 0;
1466 rect->property_outline_pixels() = 1;
1475 /* second pass to handle entered track status in a comprehensible way.
1478 switch (item_type) {
1480 case AutomationLineItem:
1481 case ControlPointItem:
1482 /* these do not affect the current entered track state */
1483 clear_entered_track = false;
1486 case AutomationTrackItem:
1487 /* handled above already */
1491 set_entered_track (0);
1499 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1508 switch (item_type) {
1509 case ControlPointItem:
1510 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1511 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1512 if (cp->line().npoints() > 1 && !cp->selected()) {
1513 cp->set_visible (false);
1517 if (is_drawable()) {
1518 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1521 hide_verbose_canvas_cursor ();
1524 case RegionViewNameHighlight:
1525 case StartSelectionTrimItem:
1526 case EndSelectionTrimItem:
1527 case PlayheadCursorItem:
1530 case ImageFrameHandleStartItem:
1531 case ImageFrameHandleEndItem:
1532 case MarkerViewHandleStartItem:
1533 case MarkerViewHandleEndItem:
1536 if (is_drawable()) {
1537 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1542 case AutomationLineItem:
1543 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1545 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1547 line->property_fill_color_rgba() = al->get_line_color();
1549 if (is_drawable()) {
1550 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1554 case RegionViewName:
1555 /* see enter_handler() for notes */
1556 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1557 if (is_drawable() && mouse_mode == MouseObject) {
1558 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1563 case RangeMarkerBarItem:
1564 case TransportMarkerBarItem:
1565 case CdMarkerBarItem:
1569 if (is_drawable()) {
1570 track_canvas->get_window()->set_cursor (*timebar_cursor);
1575 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1579 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1580 location_flags_changed (loc, this);
1583 case MeterMarkerItem:
1584 case TempoMarkerItem:
1586 if (is_drawable()) {
1587 track_canvas->get_window()->set_cursor (*timebar_cursor);
1592 case FadeInHandleItem:
1593 case FadeOutHandleItem:
1594 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1596 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1598 rect->property_fill_color_rgba() = rv->get_fill_color();
1599 rect->property_outline_pixels() = 0;
1604 case AutomationTrackItem:
1605 if (is_drawable()) {
1606 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1607 clear_entered_track = true;
1608 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1620 Editor::left_automation_track ()
1622 if (clear_entered_track) {
1623 set_entered_track (0);
1624 clear_entered_track = false;
1634 if (scrubbing_direction == 0) {
1636 session->request_locate (drag_info.current_pointer_frame, false);
1637 session->request_transport_speed (0.1);
1638 scrubbing_direction = 1;
1642 if (last_scrub_x > drag_info.current_pointer_x) {
1644 /* pointer moved to the left */
1646 if (scrubbing_direction > 0) {
1648 /* we reversed direction to go backwards */
1651 scrub_reverse_distance += (int) (last_scrub_x - drag_info.current_pointer_x);
1655 /* still moving to the left (backwards) */
1657 scrub_reversals = 0;
1658 scrub_reverse_distance = 0;
1660 delta = 0.01 * (last_scrub_x - drag_info.current_pointer_x);
1661 session->request_transport_speed (session->transport_speed() - delta);
1665 /* pointer moved to the right */
1667 if (scrubbing_direction < 0) {
1668 /* we reversed direction to go forward */
1671 scrub_reverse_distance += (int) (drag_info.current_pointer_x - last_scrub_x);
1674 /* still moving to the right */
1676 scrub_reversals = 0;
1677 scrub_reverse_distance = 0;
1679 delta = 0.01 * (drag_info.current_pointer_x - last_scrub_x);
1680 session->request_transport_speed (session->transport_speed() + delta);
1684 /* if there have been more than 2 opposite motion moves detected, or one that moves
1685 back more than 10 pixels, reverse direction
1688 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1690 if (scrubbing_direction > 0) {
1691 /* was forwards, go backwards */
1692 session->request_transport_speed (-0.1);
1693 scrubbing_direction = -1;
1695 /* was backwards, go forwards */
1696 session->request_transport_speed (0.1);
1697 scrubbing_direction = 1;
1700 scrub_reverse_distance = 0;
1701 scrub_reversals = 0;
1705 last_scrub_x = drag_info.current_pointer_x;
1709 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1711 if (event->motion.is_hint) {
1714 /* We call this so that MOTION_NOTIFY events continue to be
1715 delivered to the canvas. We need to do this because we set
1716 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1717 the density of the events, at the expense of a round-trip
1718 to the server. Given that this will mostly occur on cases
1719 where DISPLAY = :0.0, and given the cost of what the motion
1720 event might do, its a good tradeoff.
1723 track_canvas->get_pointer (x, y);
1726 if (current_stepping_trackview) {
1727 /* don't keep the persistent stepped trackview if the mouse moves */
1728 current_stepping_trackview = 0;
1729 step_timeout.disconnect ();
1732 if (session && session->actively_recording()) {
1733 /* Sorry. no dragging stuff around while we record */
1737 drag_info.item_type = item_type;
1738 drag_info.last_pointer_x = drag_info.current_pointer_x;
1739 drag_info.last_pointer_y = drag_info.current_pointer_y;
1740 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1741 &drag_info.current_pointer_y);
1744 switch (mouse_mode) {
1756 if (!from_autoscroll && drag_info.item) {
1757 /* item != 0 is the best test i can think of for dragging.
1759 if (!drag_info.move_threshold_passed) {
1761 bool x_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1762 bool y_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1764 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1766 // and change the initial grab loc/frame if this drag info wants us to
1768 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1769 drag_info.grab_frame = drag_info.current_pointer_frame;
1770 drag_info.grab_x = drag_info.current_pointer_x;
1771 drag_info.grab_y = drag_info.current_pointer_y;
1772 drag_info.last_pointer_frame = drag_info.grab_frame;
1773 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1778 switch (item_type) {
1779 case PlayheadCursorItem:
1781 case ControlPointItem:
1782 case RangeMarkerBarItem:
1783 case TransportMarkerBarItem:
1784 case CdMarkerBarItem:
1785 case TempoMarkerItem:
1786 case MeterMarkerItem:
1787 case RegionViewNameHighlight:
1788 case StartSelectionTrimItem:
1789 case EndSelectionTrimItem:
1792 case AutomationLineItem:
1793 case FadeInHandleItem:
1794 case FadeOutHandleItem:
1797 case ImageFrameHandleStartItem:
1798 case ImageFrameHandleEndItem:
1799 case MarkerViewHandleStartItem:
1800 case MarkerViewHandleEndItem:
1803 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1804 (event->motion.state & Gdk::BUTTON2_MASK))) {
1805 if (!from_autoscroll) {
1806 maybe_autoscroll_horizontally (&event->motion);
1808 (this->*(drag_info.motion_callback)) (item, event);
1817 switch (mouse_mode) {
1823 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1824 (event->motion.state & GDK_BUTTON2_MASK))) {
1825 if (!from_autoscroll) {
1826 maybe_autoscroll (&event->motion);
1828 (this->*(drag_info.motion_callback)) (item, event);
1839 track_canvas_motion (event);
1840 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1848 Editor::break_drag ()
1850 stop_canvas_autoscroll ();
1851 hide_verbose_canvas_cursor ();
1853 if (drag_info.item) {
1854 drag_info.item->ungrab (0);
1856 /* put it back where it came from */
1861 drag_info.item->i2w (cxw, cyw);
1862 drag_info.item->move (drag_info.original_x - cxw, drag_info.original_y - cyw);
1869 Editor::finalize_drag ()
1872 drag_info.copy = false;
1873 drag_info.motion_callback = 0;
1874 drag_info.finished_callback = 0;
1875 drag_info.dest_trackview = 0;
1876 drag_info.source_trackview = 0;
1877 drag_info.last_frame_position = 0;
1878 drag_info.grab_frame = 0;
1879 drag_info.last_pointer_frame = 0;
1880 drag_info.current_pointer_frame = 0;
1881 drag_info.brushing = false;
1882 range_marker_drag_rect->hide();
1883 drag_info.clear_copied_locations ();
1887 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1889 if (drag_info.item == 0) {
1890 fatal << _("programming error: start_grab called without drag item") << endmsg;
1896 cursor = which_grabber_cursor ();
1899 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1901 if (event->button.button == 2) {
1902 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
1903 drag_info.y_constrained = true;
1904 drag_info.x_constrained = false;
1906 drag_info.y_constrained = false;
1907 drag_info.x_constrained = true;
1910 drag_info.x_constrained = false;
1911 drag_info.y_constrained = false;
1914 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1915 drag_info.last_pointer_frame = drag_info.grab_frame;
1916 drag_info.current_pointer_frame = drag_info.grab_frame;
1917 drag_info.current_pointer_x = drag_info.grab_x;
1918 drag_info.current_pointer_y = drag_info.grab_y;
1919 drag_info.last_pointer_x = drag_info.current_pointer_x;
1920 drag_info.last_pointer_y = drag_info.current_pointer_y;
1921 drag_info.cumulative_x_drag = 0;
1922 drag_info.cumulative_y_drag = 0;
1923 drag_info.first_move = true;
1924 drag_info.move_threshold_passed = false;
1925 drag_info.want_move_threshold = false;
1926 drag_info.pointer_frame_offset = 0;
1927 drag_info.brushing = false;
1928 drag_info.clear_copied_locations ();
1930 drag_info.original_x = 0;
1931 drag_info.original_y = 0;
1932 drag_info.item->i2w (drag_info.original_x, drag_info.original_y);
1934 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1936 event->button.time);
1938 if (session && session->transport_rolling()) {
1939 drag_info.was_rolling = true;
1941 drag_info.was_rolling = false;
1944 switch (snap_type) {
1945 case SnapToRegionStart:
1946 case SnapToRegionEnd:
1947 case SnapToRegionSync:
1948 case SnapToRegionBoundary:
1949 build_region_boundary_cache ();
1957 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1959 drag_info.item->ungrab (0);
1960 drag_info.item = new_item;
1963 cursor = which_grabber_cursor ();
1966 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1970 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1972 bool did_drag = false;
1974 stop_canvas_autoscroll ();
1976 if (drag_info.item == 0) {
1980 drag_info.item->ungrab (event->button.time);
1982 if (drag_info.finished_callback) {
1983 drag_info.last_pointer_x = drag_info.current_pointer_x;
1984 drag_info.last_pointer_y = drag_info.current_pointer_y;
1985 (this->*(drag_info.finished_callback)) (item, event);
1988 did_drag = !drag_info.first_move;
1990 hide_verbose_canvas_cursor();
1998 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
2000 drag_info.item = item;
2001 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
2002 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
2006 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
2007 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
2011 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2014 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes64_t) arv->audio_region()->fade_in()->back()->when + arv->region()->position());
2018 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2020 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2022 nframes64_t fade_length;
2024 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2025 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2031 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2035 if (pos < (arv->region()->position() + 64)) {
2036 fade_length = 64; // this should be a minimum defined somewhere
2037 } else if (pos > arv->region()->last_frame()) {
2038 fade_length = arv->region()->length();
2040 fade_length = pos - arv->region()->position();
2042 /* mapover the region selection */
2044 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2046 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2052 tmp->reset_fade_in_shape_width (fade_length);
2055 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
2057 drag_info.first_move = false;
2061 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2063 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2065 nframes64_t fade_length;
2067 if (drag_info.first_move) return;
2069 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2070 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2075 if (pos < (arv->region()->position() + 64)) {
2076 fade_length = 64; // this should be a minimum defined somewhere
2077 } else if (pos > arv->region()->last_frame()) {
2078 fade_length = arv->region()->length();
2080 fade_length = pos - arv->region()->position();
2083 begin_reversible_command (_("change fade in length"));
2085 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2087 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2093 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2094 XMLNode &before = alist->get_state();
2096 tmp->audio_region()->set_fade_in_length (fade_length);
2097 tmp->audio_region()->set_fade_in_active (true);
2099 XMLNode &after = alist->get_state();
2100 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2103 commit_reversible_command ();
2107 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
2109 drag_info.item = item;
2110 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
2111 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
2115 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
2116 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
2120 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2122 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes64_t) arv->audio_region()->fade_out()->back()->when + arv->region()->position());
2126 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2128 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2130 nframes64_t fade_length;
2132 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2133 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2138 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2142 if (pos > (arv->region()->last_frame() - 64)) {
2143 fade_length = 64; // this should really be a minimum fade defined somewhere
2145 else if (pos < arv->region()->position()) {
2146 fade_length = arv->region()->length();
2149 fade_length = arv->region()->last_frame() - pos;
2152 /* mapover the region selection */
2154 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2156 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2162 tmp->reset_fade_out_shape_width (fade_length);
2165 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
2167 drag_info.first_move = false;
2171 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2173 if (drag_info.first_move) return;
2175 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2177 nframes64_t fade_length;
2179 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2180 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2186 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2190 if (pos > (arv->region()->last_frame() - 64)) {
2191 fade_length = 64; // this should really be a minimum fade defined somewhere
2193 else if (pos < arv->region()->position()) {
2194 fade_length = arv->region()->length();
2197 fade_length = arv->region()->last_frame() - pos;
2200 begin_reversible_command (_("change fade out length"));
2202 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2204 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2210 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2211 XMLNode &before = alist->get_state();
2213 tmp->audio_region()->set_fade_out_length (fade_length);
2214 tmp->audio_region()->set_fade_out_active (true);
2216 XMLNode &after = alist->get_state();
2217 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2220 commit_reversible_command ();
2224 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2226 drag_info.item = item;
2227 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2228 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2232 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2233 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2237 Cursor* cursor = (Cursor *) drag_info.data;
2239 if (cursor == playhead_cursor) {
2240 _dragging_playhead = true;
2242 if (session && drag_info.was_rolling) {
2243 session->request_stop ();
2246 if (session && session->is_auditioning()) {
2247 session->cancel_audition ();
2251 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2253 show_verbose_time_cursor (cursor->current_frame, 10);
2257 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2259 Cursor* cursor = (Cursor *) drag_info.data;
2260 nframes64_t adjusted_frame;
2262 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2263 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2269 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2270 if (cursor == playhead_cursor) {
2271 snap_to (adjusted_frame);
2275 if (adjusted_frame == drag_info.last_pointer_frame) return;
2277 cursor->set_position (adjusted_frame);
2279 UpdateAllTransportClocks (cursor->current_frame);
2281 show_verbose_time_cursor (cursor->current_frame, 10);
2283 drag_info.last_pointer_frame = adjusted_frame;
2284 drag_info.first_move = false;
2288 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2290 if (drag_info.first_move) return;
2292 cursor_drag_motion_callback (item, event);
2294 _dragging_playhead = false;
2296 if (item == &playhead_cursor->canvas_item) {
2298 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2304 Editor::update_marker_drag_item (Location *location)
2306 double x1 = frame_to_pixel (location->start());
2307 double x2 = frame_to_pixel (location->end());
2309 if (location->is_mark()) {
2310 marker_drag_line_points.front().set_x(x1);
2311 marker_drag_line_points.back().set_x(x1);
2312 marker_drag_line->property_points() = marker_drag_line_points;
2315 range_marker_drag_rect->property_x1() = x1;
2316 range_marker_drag_rect->property_x2() = x2;
2322 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2326 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2327 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2333 Location *location = find_location_from_marker (marker, is_start);
2335 drag_info.item = item;
2336 drag_info.data = marker;
2337 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2338 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2342 _dragging_edit_point = true;
2344 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2346 update_marker_drag_item (location);
2348 if (location->is_mark()) {
2349 // marker_drag_line->show();
2350 // marker_drag_line->raise_to_top();
2352 range_marker_drag_rect->show();
2353 //range_marker_drag_rect->raise_to_top();
2357 show_verbose_time_cursor (location->start(), 10);
2359 show_verbose_time_cursor (location->end(), 10);
2362 Selection::Operation op = Keyboard::selection_type (event->button.state);
2365 case Selection::Toggle:
2366 selection->toggle (marker);
2368 case Selection::Set:
2369 if (!selection->selected (marker)) {
2370 selection->set (marker);
2373 case Selection::Extend:
2375 Locations::LocationList ll;
2376 list<Marker*> to_add;
2378 selection->markers.range (s, e);
2379 s = min (marker->position(), s);
2380 e = max (marker->position(), e);
2383 if (e < max_frames) {
2386 session->locations()->find_all_between (s, e, ll, Location::Flags (0));
2387 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2388 LocationMarkers* lm = find_location_markers (*i);
2391 to_add.push_back (lm->start);
2394 to_add.push_back (lm->end);
2398 if (!to_add.empty()) {
2399 selection->add (to_add);
2403 case Selection::Add:
2404 selection->add (marker);
2408 /* set up copies for us to manipulate during the drag */
2410 drag_info.clear_copied_locations ();
2412 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
2413 Location *l = find_location_from_marker (*i, is_start);
2414 drag_info.copied_locations.push_back (new Location (*l));
2419 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2421 nframes64_t f_delta;
2422 nframes64_t newframe;
2424 bool move_both = false;
2425 Marker* dragged_marker = (Marker*) drag_info.data;
2427 Location *real_location;
2428 Location *copy_location;
2430 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2431 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2436 nframes64_t next = newframe;
2438 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2439 snap_to (newframe, 0, true);
2442 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2446 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2450 MarkerSelection::iterator i;
2451 list<Location*>::iterator x;
2453 /* find the marker we're dragging, and compute the delta */
2455 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2456 x != drag_info.copied_locations.end() && i != selection->markers.end();
2462 if (marker == dragged_marker) {
2464 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2469 if (real_location->is_mark()) {
2470 f_delta = newframe - copy_location->start();
2474 switch (marker->type()) {
2476 case Marker::LoopStart:
2477 case Marker::PunchIn:
2478 f_delta = newframe - copy_location->start();
2482 case Marker::LoopEnd:
2483 case Marker::PunchOut:
2484 f_delta = newframe - copy_location->end();
2487 /* what kind of marker is this ? */
2495 if (i == selection->markers.end()) {
2496 /* hmm, impossible - we didn't find the dragged marker */
2500 /* now move them all */
2502 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2503 x != drag_info.copied_locations.end() && i != selection->markers.end();
2509 /* call this to find out if its the start or end */
2511 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2515 if (real_location->locked()) {
2519 if (copy_location->is_mark()) {
2523 copy_location->set_start (copy_location->start() + f_delta);
2527 nframes64_t new_start = copy_location->start() + f_delta;
2528 nframes64_t new_end = copy_location->end() + f_delta;
2530 if (is_start) { // start-of-range marker
2533 copy_location->set_start (new_start);
2534 copy_location->set_end (new_end);
2535 } else if (new_start < copy_location->end()) {
2536 copy_location->set_start (new_start);
2538 snap_to (next, 1, true);
2539 copy_location->set_end (next);
2540 copy_location->set_start (newframe);
2543 } else { // end marker
2546 copy_location->set_end (new_end);
2547 copy_location->set_start (new_start);
2548 } else if (new_end > copy_location->start()) {
2549 copy_location->set_end (new_end);
2550 } else if (newframe > 0) {
2551 snap_to (next, -1, true);
2552 copy_location->set_start (next);
2553 copy_location->set_end (newframe);
2557 update_marker_drag_item (copy_location);
2559 LocationMarkers* lm = find_location_markers (real_location);
2562 lm->set_position (copy_location->start(), copy_location->end());
2566 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2567 drag_info.first_move = false;
2569 if (drag_info.copied_locations.empty()) {
2573 edit_point_clock.set (drag_info.copied_locations.front()->start());
2574 show_verbose_time_cursor (newframe, 10);
2577 track_canvas->update_now ();
2582 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2584 if (drag_info.first_move) {
2586 /* just a click, do nothing but finish
2587 off the selection process
2590 Selection::Operation op = Keyboard::selection_type (event->button.state);
2591 Marker* marker = (Marker *) drag_info.data;
2594 case Selection::Set:
2595 if (selection->selected (marker) && selection->markers.size() > 1) {
2596 selection->set (marker);
2600 case Selection::Toggle:
2601 case Selection::Extend:
2602 case Selection::Add:
2609 _dragging_edit_point = false;
2612 begin_reversible_command ( _("move marker") );
2613 XMLNode &before = session->locations()->get_state();
2615 MarkerSelection::iterator i;
2616 list<Location*>::iterator x;
2619 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2620 x != drag_info.copied_locations.end() && i != selection->markers.end();
2623 Location * location = find_location_from_marker ((*i), is_start);
2627 if (location->locked()) {
2631 if (location->is_mark()) {
2632 location->set_start ((*x)->start());
2634 location->set ((*x)->start(), (*x)->end());
2639 XMLNode &after = session->locations()->get_state();
2640 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2641 commit_reversible_command ();
2643 marker_drag_line->hide();
2644 range_marker_drag_rect->hide();
2648 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2651 MeterMarker* meter_marker;
2653 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2654 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2658 meter_marker = dynamic_cast<MeterMarker*> (marker);
2660 MetricSection& section (meter_marker->meter());
2662 if (!section.movable()) {
2666 drag_info.item = item;
2667 drag_info.copy = false;
2668 drag_info.data = marker;
2669 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2670 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2674 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2676 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2680 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2683 MeterMarker* meter_marker;
2685 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2686 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2690 meter_marker = dynamic_cast<MeterMarker*> (marker);
2692 // create a dummy marker for visual representation of moving the copy.
2693 // The actual copying is not done before we reach the finish callback.
2695 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2696 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2697 *new MeterSection(meter_marker->meter()));
2699 drag_info.item = &new_marker->the_item();
2700 drag_info.copy = true;
2701 drag_info.data = new_marker;
2702 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2703 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2707 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2709 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2713 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2715 MeterMarker* marker = (MeterMarker *) drag_info.data;
2716 nframes64_t adjusted_frame;
2718 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2719 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2725 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2726 snap_to (adjusted_frame);
2729 if (adjusted_frame == drag_info.last_pointer_frame) return;
2731 marker->set_position (adjusted_frame);
2734 drag_info.last_pointer_frame = adjusted_frame;
2735 drag_info.first_move = false;
2737 show_verbose_time_cursor (adjusted_frame, 10);
2741 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2743 if (drag_info.first_move) return;
2745 meter_marker_drag_motion_callback (drag_info.item, event);
2747 MeterMarker* marker = (MeterMarker *) drag_info.data;
2750 TempoMap& map (session->tempo_map());
2751 map.bbt_time (drag_info.last_pointer_frame, when);
2753 if (drag_info.copy == true) {
2754 begin_reversible_command (_("copy meter mark"));
2755 XMLNode &before = map.get_state();
2756 map.add_meter (marker->meter(), when);
2757 XMLNode &after = map.get_state();
2758 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2759 commit_reversible_command ();
2761 // delete the dummy marker we used for visual representation of copying.
2762 // a new visual marker will show up automatically.
2765 begin_reversible_command (_("move meter mark"));
2766 XMLNode &before = map.get_state();
2767 map.move_meter (marker->meter(), when);
2768 XMLNode &after = map.get_state();
2769 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2770 commit_reversible_command ();
2775 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2778 TempoMarker* tempo_marker;
2780 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2781 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2785 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2786 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2790 MetricSection& section (tempo_marker->tempo());
2792 if (!section.movable()) {
2796 drag_info.item = item;
2797 drag_info.copy = false;
2798 drag_info.data = marker;
2799 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2800 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2804 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2805 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2809 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2812 TempoMarker* tempo_marker;
2814 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2815 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2819 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2820 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2824 // create a dummy marker for visual representation of moving the copy.
2825 // The actual copying is not done before we reach the finish callback.
2827 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2828 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2829 *new TempoSection(tempo_marker->tempo()));
2831 drag_info.item = &new_marker->the_item();
2832 drag_info.copy = true;
2833 drag_info.data = new_marker;
2834 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2835 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2839 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2841 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2845 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2847 TempoMarker* marker = (TempoMarker *) drag_info.data;
2848 nframes64_t adjusted_frame;
2850 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2851 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2857 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2858 snap_to (adjusted_frame);
2861 if (adjusted_frame == drag_info.last_pointer_frame) return;
2863 /* OK, we've moved far enough to make it worth actually move the thing. */
2865 marker->set_position (adjusted_frame);
2867 show_verbose_time_cursor (adjusted_frame, 10);
2869 drag_info.last_pointer_frame = adjusted_frame;
2870 drag_info.first_move = false;
2874 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2876 if (drag_info.first_move) return;
2878 tempo_marker_drag_motion_callback (drag_info.item, event);
2880 TempoMarker* marker = (TempoMarker *) drag_info.data;
2883 TempoMap& map (session->tempo_map());
2884 map.bbt_time (drag_info.last_pointer_frame, when);
2886 if (drag_info.copy == true) {
2887 begin_reversible_command (_("copy tempo mark"));
2888 XMLNode &before = map.get_state();
2889 map.add_tempo (marker->tempo(), when);
2890 XMLNode &after = map.get_state();
2891 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2892 commit_reversible_command ();
2894 // delete the dummy marker we used for visual representation of copying.
2895 // a new visual marker will show up automatically.
2898 begin_reversible_command (_("move tempo mark"));
2899 XMLNode &before = map.get_state();
2900 map.move_tempo (marker->tempo(), when);
2901 XMLNode &after = map.get_state();
2902 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2903 commit_reversible_command ();
2908 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2910 ControlPoint* control_point;
2912 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2913 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2917 // We shouldn't remove the first or last gain point
2918 if (control_point->line().is_last_point(*control_point) ||
2919 control_point->line().is_first_point(*control_point)) {
2923 control_point->line().remove_point (*control_point);
2927 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event)
2929 ControlPoint* control_point;
2931 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2932 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2936 control_point->line().remove_point (*control_point);
2940 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2942 ControlPoint* control_point;
2944 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2945 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2949 drag_info.item = item;
2950 drag_info.data = control_point;
2951 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2952 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2954 start_grab (event, fader_cursor);
2956 // start the grab at the center of the control point so
2957 // the point doesn't 'jump' to the mouse after the first drag
2958 drag_info.grab_x = control_point->get_x();
2959 drag_info.grab_y = control_point->get_y();
2961 control_point->line().parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
2962 track_canvas->w2c(drag_info.grab_x, drag_info.grab_y, drag_info.grab_x, drag_info.grab_y);
2964 drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
2966 control_point->line().start_drag (control_point, drag_info.grab_frame, 0);
2968 float fraction = 1.0 - (control_point->get_y() / control_point->line().height());
2969 set_verbose_canvas_cursor (control_point->line().get_verbose_cursor_string (fraction),
2970 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2972 show_verbose_canvas_cursor ();
2976 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2978 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2980 double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
2981 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2983 if (event->button.state & Keyboard::SecondaryModifier) {
2988 double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
2989 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2991 // calculate zero crossing point. back off by .01 to stay on the
2992 // positive side of zero
2994 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line().height() - .01;
2995 cp->line().parent_group().i2w(_unused, zero_gain_y);
2997 // make sure we hit zero when passing through
2998 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2999 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
3003 if (drag_info.x_constrained) {
3004 cx = drag_info.grab_x;
3006 if (drag_info.y_constrained) {
3007 cy = drag_info.grab_y;
3010 drag_info.cumulative_x_drag = cx - drag_info.grab_x;
3011 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
3013 cp->line().parent_group().w2i (cx, cy);
3017 cy = min ((double) cp->line().height(), cy);
3019 //translate cx to frames
3020 nframes64_t cx_frames = unit_to_frame (cx);
3022 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
3023 snap_to (cx_frames);
3026 float fraction = 1.0 - (cy / cp->line().height());
3030 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3036 cp->line().point_drag (*cp, cx_frames , fraction, push);
3038 set_verbose_canvas_cursor_text (cp->line().get_verbose_cursor_string (fraction));
3040 drag_info.first_move = false;
3044 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3046 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
3048 if (drag_info.first_move) {
3052 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3053 reset_point_selection ();
3057 control_point_drag_motion_callback (item, event);
3059 cp->line().end_drag (cp);
3063 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
3065 switch (mouse_mode) {
3067 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
3068 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
3076 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
3080 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
3081 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
3085 start_line_grab (al, event);
3089 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
3093 nframes64_t frame_within_region;
3095 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3099 cx = event->button.x;
3100 cy = event->button.y;
3101 line->parent_group().w2i (cx, cy);
3102 frame_within_region = (nframes64_t) floor (cx * frames_per_unit);
3104 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
3105 current_line_drag_info.after)) {
3106 /* no adjacent points */
3110 drag_info.item = &line->grab_item();
3111 drag_info.data = line;
3112 drag_info.motion_callback = &Editor::line_drag_motion_callback;
3113 drag_info.finished_callback = &Editor::line_drag_finished_callback;
3115 start_grab (event, fader_cursor);
3117 double fraction = 1.0 - (cy / line->height());
3119 line->start_drag (0, drag_info.grab_frame, fraction);
3121 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
3122 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
3123 show_verbose_canvas_cursor ();
3127 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3129 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3131 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
3133 if (event->button.state & Keyboard::SecondaryModifier) {
3137 double cx = drag_info.current_pointer_x;
3138 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
3140 // calculate zero crossing point. back off by .01 to stay on the
3141 // positive side of zero
3143 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
3144 line->parent_group().i2w(_unused, zero_gain_y);
3146 // make sure we hit zero when passing through
3147 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
3148 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
3152 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
3154 line->parent_group().w2i (cx, cy);
3157 cy = min ((double) line->height(), cy);
3159 double fraction = 1.0 - (cy / line->height());
3163 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3169 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
3171 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
3175 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3177 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3178 line_drag_motion_callback (item, event);
3183 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3185 if (selection->regions.empty() || clicked_regionview == 0) {
3188 _region_motion_group->raise_to_top ();
3189 drag_info.copy = false;
3190 drag_info.item = item;
3191 drag_info.data = clicked_regionview;
3193 if (Config->get_edit_mode() == Splice) {
3194 drag_info.motion_callback = &Editor::region_drag_splice_motion_callback;
3195 drag_info.finished_callback = &Editor::region_drag_splice_finished_callback;
3197 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3198 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3204 TimeAxisView* tvp = clicked_axisview;
3205 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3207 if (tv && tv->is_track()) {
3208 speed = tv->get_diskstream()->speed();
3211 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3212 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3213 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3214 drag_info.dest_trackview = drag_info.source_trackview;
3215 // we want a move threshold
3216 drag_info.want_move_threshold = true;
3217 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3219 begin_reversible_command (_("move region(s)"));
3221 /* sync the canvas to what we think is its current state */
3222 track_canvas->update_now();
3226 Editor::start_create_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3228 drag_info.copy = false;
3229 drag_info.item = item;
3230 drag_info.data = clicked_axisview;
3231 drag_info.source_trackview = clicked_axisview;
3232 drag_info.dest_trackview = drag_info.source_trackview;
3233 drag_info.motion_callback = &Editor::create_region_drag_motion_callback;
3234 drag_info.finished_callback = &Editor::create_region_drag_finished_callback;
3240 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
3242 if (selection->regions.empty() || clicked_regionview == 0) {
3245 _region_motion_group->raise_to_top ();
3246 drag_info.copy = true;
3247 drag_info.item = item;
3248 drag_info.data = clicked_regionview;
3252 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
3253 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3256 if (rtv && rtv->is_track()) {
3257 speed = rtv->get_diskstream()->speed();
3260 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3261 drag_info.dest_trackview = drag_info.source_trackview;
3262 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3263 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3264 // we want a move threshold
3265 drag_info.want_move_threshold = true;
3266 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3267 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3268 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3272 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
3274 if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) {
3278 drag_info.copy = false;
3279 drag_info.item = item;
3280 drag_info.data = clicked_regionview;
3281 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3282 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3287 TimeAxisView* tvp = clicked_axisview;
3288 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3290 if (tv && tv->is_track()) {
3291 speed = tv->get_diskstream()->speed();
3294 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3295 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3296 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3297 drag_info.dest_trackview = drag_info.source_trackview;
3298 // we want a move threshold
3299 drag_info.want_move_threshold = true;
3300 drag_info.brushing = true;
3302 begin_reversible_command (_("Drag region brush"));
3306 Editor::possibly_copy_regions_during_grab (GdkEvent* event)
3308 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
3310 drag_info.want_move_threshold = false; // don't copy again
3312 /* duplicate the regionview(s) and region(s) */
3314 vector<RegionView*> new_regionviews;
3316 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3321 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
3322 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
3324 const boost::shared_ptr<const Region> original = rv->region();
3325 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
3328 boost::shared_ptr<AudioRegion> audioregion_copy
3329 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
3330 nrv = new AudioRegionView (*arv, audioregion_copy);
3332 boost::shared_ptr<MidiRegion> midiregion_copy
3333 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
3334 nrv = new MidiRegionView (*mrv, midiregion_copy);
3339 nrv->get_canvas_group()->show ();
3340 new_regionviews.push_back (nrv);
3343 if (new_regionviews.empty()) {
3347 /* reset selection to new regionviews. This will not set selection visual status for
3348 these regionviews since they don't belong to a track, so do that by hand too.
3351 selection->set (new_regionviews);
3353 for (vector<RegionView*>::iterator i = new_regionviews.begin(); i != new_regionviews.end(); ++i) {
3354 (*i)->set_selected (true);
3357 /* reset drag_info data to reflect the fact that we are dragging the copies */
3359 drag_info.data = new_regionviews.front();
3361 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3363 sync the canvas to what we think is its current state
3364 without it, the canvas seems to
3365 "forget" to update properly after the upcoming reparent()
3366 ..only if the mouse is in rapid motion at the time of the grab.
3367 something to do with regionview creation raking so long?
3369 track_canvas->update_now();
3374 Editor::check_region_drag_possible (RouteTimeAxisView** tv)
3376 /* Which trackview is this ? */
3378 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
3379 (*tv) = dynamic_cast<RouteTimeAxisView*>(tvp);
3381 /* The region motion is only processed if the pointer is over
3385 if (!(*tv) || !(*tv)->is_track()) {
3386 /* To make sure we hide the verbose canvas cursor when the mouse is
3387 not held over and audiotrack.
3389 hide_verbose_canvas_cursor ();
3396 struct RegionSelectionByPosition {
3397 bool operator() (RegionView*a, RegionView* b) {
3398 return a->region()->position () < b->region()->position();
3403 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3405 RouteTimeAxisView* tv;
3407 if (!check_region_drag_possible (&tv)) {
3411 if (!drag_info.move_threshold_passed) {
3417 if (drag_info.current_pointer_x - drag_info.grab_x > 0) {
3423 RegionSelection copy (selection->regions);
3425 RegionSelectionByPosition cmp;
3428 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
3430 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
3436 boost::shared_ptr<Playlist> playlist;
3438 if ((playlist = atv->playlist()) == 0) {
3442 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
3447 if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) {
3451 if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) {
3457 playlist->shuffle ((*i)->region(), dir);
3459 drag_info.grab_x = drag_info.current_pointer_x;
3464 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3469 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3473 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
3474 nframes64_t pending_region_position = 0;
3475 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
3476 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
3477 bool clamp_y_axis = false;
3478 vector<int32_t> height_list(512) ;
3479 vector<int32_t>::iterator j;
3480 RouteTimeAxisView* tv;
3482 possibly_copy_regions_during_grab (event);
3484 if (!check_region_drag_possible (&tv)) {
3488 original_pointer_order = drag_info.dest_trackview->order;
3490 /************************************************************
3492 ************************************************************/
3494 if (drag_info.brushing) {
3495 clamp_y_axis = true;
3500 if ((pointer_y_span = (drag_info.dest_trackview->order - tv->order)) != 0) {
3502 int32_t children = 0, numtracks = 0;
3503 // XXX hard coding track limit, oh my, so very very bad
3504 bitset <1024> tracks (0x00);
3505 /* get a bitmask representing the visible tracks */
3507 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3508 TimeAxisView *tracklist_timeview;
3509 tracklist_timeview = (*i);
3510 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
3511 TimeAxisView::Children children_list;
3513 /* zeroes are audio tracks. ones are other types. */
3515 if (!rtv2->hidden()) {
3517 if (visible_y_high < rtv2->order) {
3518 visible_y_high = rtv2->order;
3520 if (visible_y_low > rtv2->order) {
3521 visible_y_low = rtv2->order;
3524 if (!rtv2->is_track()) {
3525 tracks = tracks |= (0x01 << rtv2->order);
3528 height_list[rtv2->order] = (*i)->current_height();
3531 if ((children_list = rtv2->get_child_list()).size() > 0) {
3532 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
3533 tracks = tracks |= (0x01 << (rtv2->order + children));
3534 height_list[rtv2->order + children] = (*j)->current_height();
3542 /* find the actual span according to the canvas */
3544 canvas_pointer_y_span = pointer_y_span;
3545 if (drag_info.dest_trackview->order >= tv->order) {
3547 for (y = tv->order; y < drag_info.dest_trackview->order; y++) {
3548 if (height_list[y] == 0 ) {
3549 canvas_pointer_y_span--;
3554 for (y = drag_info.dest_trackview->order;y <= tv->order; y++) {
3555 if ( height_list[y] == 0 ) {
3556 canvas_pointer_y_span++;
3561 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3562 RegionView* rv2 = (*i);
3563 double ix1, ix2, iy1, iy2;
3566 if (rv2->region()->locked()) {
3570 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3571 rv2->get_canvas_frame()->i2w (ix1, iy1);
3572 iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
3574 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3575 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3577 if (rtv2->order != original_pointer_order) {
3578 /* this isn't the pointer track */
3580 if (canvas_pointer_y_span > 0) {
3582 /* moving up the canvas */
3583 if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
3585 int32_t visible_tracks = 0;
3586 while (visible_tracks < canvas_pointer_y_span ) {
3589 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3590 /* we're passing through a hidden track */
3595 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3596 clamp_y_axis = true;
3600 clamp_y_axis = true;
3603 } else if (canvas_pointer_y_span < 0) {
3605 /*moving down the canvas*/
3607 if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3610 int32_t visible_tracks = 0;
3612 while (visible_tracks > canvas_pointer_y_span ) {
3615 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3619 if ( tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3620 clamp_y_axis = true;
3625 clamp_y_axis = true;
3631 /* this is the pointer's track */
3632 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3633 clamp_y_axis = true;
3634 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3635 clamp_y_axis = true;
3643 } else if (drag_info.dest_trackview == tv) {
3644 clamp_y_axis = true;
3648 if (!clamp_y_axis) {
3649 drag_info.dest_trackview = tv;
3652 /************************************************************
3654 ************************************************************/
3656 /* compute the amount of pointer motion in frames, and where
3657 the region would be if we moved it by that much.
3659 if ( drag_info.move_threshold_passed ) {
3661 if (drag_info.current_pointer_frame >= drag_info.pointer_frame_offset) {
3663 nframes64_t sync_frame;
3664 nframes64_t sync_offset;
3667 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3669 sync_offset = rv->region()->sync_offset (sync_dir);
3671 /* we don't handle a sync point that lies before zero.
3673 if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) {
3674 sync_frame = pending_region_position + (sync_dir*sync_offset);
3676 /* we snap if the snap modifier is not enabled.
3679 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3680 snap_to (sync_frame);
3683 pending_region_position = rv->region()->adjust_to_sync (sync_frame);
3686 pending_region_position = drag_info.last_frame_position;
3690 pending_region_position = 0;
3693 if (pending_region_position > max_frames - rv->region()->length()) {
3694 pending_region_position = drag_info.last_frame_position;
3697 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3699 bool x_move_allowed;
3701 if (Config->get_edit_mode() == Lock) {
3702 if (drag_info.copy) {
3703 x_move_allowed = !drag_info.x_constrained;
3705 /* in locked edit mode, reverse the usual meaning of x_constrained */
3706 x_move_allowed = drag_info.x_constrained;
3709 x_move_allowed = !drag_info.x_constrained;
3712 if (( pending_region_position != drag_info.last_frame_position) && x_move_allowed ) {
3714 /* now compute the canvas unit distance we need to move the regionview
3715 to make it appear at the new location.
3718 if (pending_region_position > drag_info.last_frame_position) {
3719 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3721 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3722 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3724 RegionView* rv2 = (*i);
3726 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3728 double ix1, ix2, iy1, iy2;
3729 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3730 rv2->get_canvas_frame()->i2w (ix1, iy1);
3732 if (-x_delta > ix1 + horizontal_adjustment.get_value()) {
3735 pending_region_position = drag_info.last_frame_position;
3742 drag_info.last_frame_position = pending_region_position;
3749 /* threshold not passed */
3754 /*************************************************************
3756 ************************************************************/
3758 if (x_delta == 0 && (pointer_y_span == 0)) {
3759 /* haven't reached next snap point, and we're not switching
3760 trackviews. nothing to do.
3765 /*************************************************************
3767 ************************************************************/
3768 bool do_move = true;
3769 if (drag_info.first_move) {
3770 if (!drag_info.move_threshold_passed) {
3777 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3778 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3780 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3782 RegionView* rv = (*i);
3783 double ix1, ix2, iy1, iy2;
3784 int32_t temp_pointer_y_span = pointer_y_span;
3786 if (rv->region()->locked()) {
3790 /* get item BBox, which will be relative to parent. so we have
3791 to query on a child, then convert to world coordinates using
3795 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3796 rv->get_canvas_frame()->i2w (ix1, iy1);
3798 cerr << "adjust y from " << iy1 << " using "
3799 << vertical_adjustment.get_value() << " - "
3800 << canvas_timebars_vsize
3803 iy1 += get_trackview_group_vertical_offset ();;
3805 if (drag_info.first_move) {
3807 // hide any dependent views
3809 rv->get_time_axis_view().hide_dependent_views (*rv);
3812 reparent to a non scrolling group so that we can keep the
3813 region selection above all time axis views.
3814 reparenting means we have to move the rv as the two
3815 parent groups have different coordinates.
3818 rv->get_canvas_group()->property_y() = iy1 - 1;
3819 rv->get_canvas_group()->reparent(*_region_motion_group);
3821 rv->fake_set_opaque (true);
3824 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3825 RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3826 RouteTimeAxisView* temp_rtv;
3828 if ((pointer_y_span != 0) && !clamp_y_axis) {
3831 for (j = height_list.begin(); j!= height_list.end(); j++) {
3832 if (x == canvas_rtv->order) {
3833 /* we found the track the region is on */
3834 if (x != original_pointer_order) {
3835 /*this isn't from the same track we're dragging from */
3836 temp_pointer_y_span = canvas_pointer_y_span;
3838 while (temp_pointer_y_span > 0) {
3839 /* we're moving up canvas-wise,
3840 so we need to find the next track height
3842 if (j != height_list.begin()) {
3845 if (x != original_pointer_order) {
3846 /* we're not from the dragged track, so ignore hidden tracks. */
3848 temp_pointer_y_span++;
3852 temp_pointer_y_span--;
3855 while (temp_pointer_y_span < 0) {
3857 if (x != original_pointer_order) {
3859 temp_pointer_y_span--;
3863 if (j != height_list.end()) {
3866 temp_pointer_y_span++;
3868 /* find out where we'll be when we move and set height accordingly */
3870 tvp2 = trackview_by_y_position (iy1 + y_delta);
3871 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3872 rv->set_height (temp_rtv->current_height());
3874 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3875 personally, i think this can confuse things, but never mind.
3878 //const GdkColor& col (temp_rtv->view->get_region_color());
3879 //rv->set_color (const_cast<GdkColor&>(col));
3886 /* prevent the regionview from being moved to before
3887 the zero position on the canvas.
3892 if (-x_delta > ix1) {
3895 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3896 x_delta = max_frames - rv->region()->last_frame();
3899 if (drag_info.brushing) {
3900 mouse_brush_insert_region (rv, pending_region_position);
3902 rv->move (x_delta, y_delta);
3905 } /* foreach region */
3909 if (drag_info.first_move && drag_info.move_threshold_passed) {
3910 cursor_group->raise_to_top();
3911 drag_info.first_move = false;
3914 if (x_delta != 0 && !drag_info.brushing) {
3915 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3920 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3922 bool nocommit = true;
3923 vector<RegionView*> copies;
3924 RouteTimeAxisView* source_tv;
3925 boost::shared_ptr<Diskstream> ds;
3926 boost::shared_ptr<Playlist> from_playlist;
3927 vector<RegionView*> new_selection;
3928 typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
3929 PlaylistSet modified_playlists;
3930 PlaylistSet frozen_playlists;
3931 list <sigc::connection> modified_playlist_connections;
3932 pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
3934 /* first_move is set to false if the regionview has been moved in the
3938 if (drag_info.first_move) {
3945 if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
3946 selection->set (pre_drag_region_selection);
3947 pre_drag_region_selection.clear ();
3950 if (drag_info.brushing) {
3951 /* all changes were made during motion event handlers */
3953 if (drag_info.copy) {
3954 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3955 copies.push_back (*i);
3964 /* reverse this here so that we have the correct logic to finalize
3968 if (Config->get_edit_mode() == Lock && !drag_info.copy) {
3969 drag_info.x_constrained = !drag_info.x_constrained;
3972 if (drag_info.copy) {
3973 if (drag_info.x_constrained) {
3974 op_string = _("fixed time region copy");
3976 op_string = _("region copy");
3979 if (drag_info.x_constrained) {
3980 op_string = _("fixed time region drag");
3982 op_string = _("region drag");
3986 begin_reversible_command (op_string);
3988 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3990 RegionView* rv = (*i);
3991 double ix1, ix2, iy1, iy2;
3992 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3993 rv->get_canvas_frame()->i2w (ix1, iy1);
3994 iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
3996 TimeAxisView* dest_tv = trackview_by_y_position (iy1);
3997 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*>(dest_tv);
3999 bool changed_tracks, changed_position;
4002 if (rv->region()->locked()) {
4007 /* adjust for track speed */
4011 if (dest_rtv && dest_rtv->get_diskstream()) {
4012 speed = dest_rtv->get_diskstream()->speed();
4015 changed_position = (drag_info.last_frame_position != (nframes64_t) (rv->region()->position()/speed));
4016 changed_tracks = (dest_tv != &rv->get_time_axis_view());
4018 if (changed_position && !drag_info.x_constrained) {
4019 _master_group->w2i(ix1, iy1);
4020 where = (nframes64_t) (unit_to_frame (ix1) * speed);
4022 where = rv->region()->position();
4025 boost::shared_ptr<Region> new_region;
4028 if (drag_info.copy) {
4029 /* we already made a copy */
4030 new_region = rv->region();
4032 /* undo the previous hide_dependent_views so that xfades don't
4033 disappear on copying regions
4036 //rv->get_time_axis_view().reveal_dependent_views (*rv);
4038 } else if (changed_tracks && dest_rtv->playlist()) {
4039 new_region = RegionFactory::create (rv->region());
4042 if (changed_tracks || drag_info.copy) {
4044 boost::shared_ptr<Playlist> to_playlist = dest_rtv->playlist();
4050 latest_regionviews.clear ();
4052 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4054 insert_result = modified_playlists.insert (to_playlist);
4055 if (insert_result.second) {
4056 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
4059 to_playlist->add_region (new_region, where);
4063 if (!latest_regionviews.empty()) {
4064 // XXX why just the first one ? we only expect one
4065 // commented out in nick_m's canvas reworking. is that intended?
4066 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
4067 new_selection.push_back (latest_regionviews.front());
4072 motion on the same track. plonk the previously reparented region
4073 back to its original canvas group (its streamview).
4074 No need to do anything for copies as they are fake regions which will be deleted.
4077 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
4078 rv->get_canvas_group()->property_y() = 0;
4080 /* just change the model */
4082 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
4084 insert_result = modified_playlists.insert (playlist);
4085 if (insert_result.second) {
4086 session->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
4088 /* freeze to avoid lots of relayering in the case of a multi-region drag */
4089 frozen_insert_result = frozen_playlists.insert(playlist);
4090 if (frozen_insert_result.second) {
4094 rv->region()->set_position (where, (void*) this);
4097 if (changed_tracks && !drag_info.copy) {
4099 /* get the playlist where this drag started. we can't use rv->region()->playlist()
4100 because we may have copied the region and it has not been attached to a playlist.
4103 assert ((source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view())));
4104 assert ((ds = source_tv->get_diskstream()));
4105 assert ((from_playlist = ds->playlist()));
4107 /* moved to a different audio track, without copying */
4109 /* the region that used to be in the old playlist is not
4110 moved to the new one - we use a copy of it. as a result,
4111 any existing editor for the region should no longer be
4115 rv->hide_region_editor();
4116 rv->fake_set_opaque (false);
4118 /* remove the region from the old playlist */
4120 insert_result = modified_playlists.insert (from_playlist);
4121 if (insert_result.second) {
4122 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
4125 from_playlist->remove_region ((rv->region()));
4127 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
4128 was selected in all of them, then removing it from a playlist will have removed all
4129 trace of it from the selection (i.e. there were N regions selected, we removed 1,
4130 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
4131 corresponding regionview, and the selection is now empty).
4133 this could have invalidated any and all iterators into the region selection.
4135 the heuristic we use here is: if the region selection is empty, break out of the loop
4136 here. if the region selection is not empty, then restart the loop because we know that
4137 we must have removed at least the region(view) we've just been working on as well as any
4138 that we processed on previous iterations.
4140 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
4141 we can just iterate.
4144 if (selection->regions.empty()) {
4147 i = selection->regions.by_layer().begin();
4154 if (drag_info.copy) {
4155 copies.push_back (rv);
4159 if (new_selection.empty()) {
4160 if (drag_info.copy) {
4161 /* the region(view)s that are selected and being dragged around
4162 are copies and do not belong to any track. remove them
4163 from the selection right here.
4165 selection->clear_regions();
4168 /* this will clear any existing selection that would have been
4169 cleared in the other clause above
4171 selection->set (new_selection);
4174 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
4180 for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
4181 session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));
4183 commit_reversible_command ();
4186 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
4193 Editor::create_region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4195 if (drag_info.move_threshold_passed) {
4196 if (drag_info.first_move) {
4197 // TODO: create region-create-drag region view here
4198 drag_info.first_move = false;
4201 // TODO: resize region-create-drag region view here
4206 Editor::create_region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4208 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (drag_info.dest_trackview);
4212 const boost::shared_ptr<MidiDiskstream> diskstream =
4213 boost::dynamic_pointer_cast<MidiDiskstream>(mtv->view()->trackview().track()->diskstream());
4216 warning << "Cannot create non-MIDI region" << endl;
4220 if (drag_info.first_move) {
4221 begin_reversible_command (_("create region"));
4222 XMLNode &before = mtv->playlist()->get_state();
4224 nframes64_t start = drag_info.grab_frame;
4225 snap_to (start, -1);
4226 const Meter& m = session->tempo_map().meter_at(start);
4227 const Tempo& t = session->tempo_map().tempo_at(start);
4228 double length = floor (m.frames_per_bar(t, session->frame_rate()));
4230 boost::shared_ptr<Source> src = session->create_midi_source_for_session(*diskstream.get());
4232 mtv->playlist()->add_region (boost::dynamic_pointer_cast<MidiRegion>
4233 (RegionFactory::create(src, 0, (nframes_t) length,
4234 PBD::basename_nosuffix(src->name()))), start);
4235 XMLNode &after = mtv->playlist()->get_state();
4236 session->add_command(new MementoCommand<Playlist>(*mtv->playlist().get(), &before, &after));
4237 commit_reversible_command();
4240 create_region_drag_motion_callback (item, event);
4241 // TODO: create region-create-drag region here
4246 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
4248 /* Either add to or set the set the region selection, unless
4249 this is an alignment click (control used)
4252 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
4253 TimeAxisView* tv = &rv.get_time_axis_view();
4254 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
4256 if (rtv && rtv->is_track()) {
4257 speed = rtv->get_diskstream()->speed();
4260 nframes64_t where = get_preferred_edit_position();
4264 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
4266 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
4268 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
4270 align_region (rv.region(), End, (nframes64_t) (where * speed));
4274 align_region (rv.region(), Start, (nframes64_t) (where * speed));
4281 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
4287 nframes64_t frame_rate;
4296 if (Profile->get_sae() || Profile->get_small_screen()) {
4297 m = ARDOUR_UI::instance()->primary_clock.mode();
4299 m = ARDOUR_UI::instance()->secondary_clock.mode();
4303 case AudioClock::BBT:
4304 session->bbt_time (frame, bbt);
4305 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
4308 case AudioClock::SMPTE:
4309 session->smpte_time (frame, smpte);
4310 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4313 case AudioClock::MinSec:
4314 /* XXX this is copied from show_verbose_duration_cursor() */
4315 frame_rate = session->frame_rate();
4316 hours = frame / (frame_rate * 3600);
4317 frame = frame % (frame_rate * 3600);
4318 mins = frame / (frame_rate * 60);
4319 frame = frame % (frame_rate * 60);
4320 secs = (float) frame / (float) frame_rate;
4321 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4325 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
4329 if (xpos >= 0 && ypos >=0) {
4330 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4333 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);
4335 show_verbose_canvas_cursor ();
4339 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
4346 nframes64_t distance, frame_rate;
4348 Meter meter_at_start(session->tempo_map().meter_at(start));
4356 if (Profile->get_sae() || Profile->get_small_screen()) {
4357 m = ARDOUR_UI::instance()->primary_clock.mode ();
4359 m = ARDOUR_UI::instance()->secondary_clock.mode ();
4363 case AudioClock::BBT:
4364 session->bbt_time (start, sbbt);
4365 session->bbt_time (end, ebbt);
4368 /* XXX this computation won't work well if the
4369 user makes a selection that spans any meter changes.
4372 ebbt.bars -= sbbt.bars;
4373 if (ebbt.beats >= sbbt.beats) {
4374 ebbt.beats -= sbbt.beats;
4377 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
4379 if (ebbt.ticks >= sbbt.ticks) {
4380 ebbt.ticks -= sbbt.ticks;
4383 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
4386 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
4389 case AudioClock::SMPTE:
4390 session->smpte_duration (end - start, smpte);
4391 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4394 case AudioClock::MinSec:
4395 /* XXX this stuff should be elsewhere.. */
4396 distance = end - start;
4397 frame_rate = session->frame_rate();
4398 hours = distance / (frame_rate * 3600);
4399 distance = distance % (frame_rate * 3600);
4400 mins = distance / (frame_rate * 60);
4401 distance = distance % (frame_rate * 60);
4402 secs = (float) distance / (float) frame_rate;
4403 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4407 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
4411 if (xpos >= 0 && ypos >=0) {
4412 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4415 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4418 show_verbose_canvas_cursor ();
4422 Editor::collect_new_region_view (RegionView* rv)
4424 latest_regionviews.push_back (rv);
4428 Editor::collect_and_select_new_region_view (RegionView* rv)
4431 latest_regionviews.push_back (rv);
4435 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
4437 if (clicked_regionview == 0) {
4441 /* lets try to create new Region for the selection */
4443 vector<boost::shared_ptr<Region> > new_regions;
4444 create_region_from_selection (new_regions);
4446 if (new_regions.empty()) {
4450 /* XXX fix me one day to use all new regions */
4452 boost::shared_ptr<Region> region (new_regions.front());
4454 /* add it to the current stream/playlist.
4456 tricky: the streamview for the track will add a new regionview. we will
4457 catch the signal it sends when it creates the regionview to
4458 set the regionview we want to then drag.
4461 latest_regionviews.clear();
4462 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4464 /* A selection grab currently creates two undo/redo operations, one for
4465 creating the new region and another for moving it.
4468 begin_reversible_command (_("selection grab"));
4470 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
4472 XMLNode *before = &(playlist->get_state());
4473 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
4474 XMLNode *after = &(playlist->get_state());
4475 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4477 commit_reversible_command ();
4481 if (latest_regionviews.empty()) {
4482 /* something went wrong */
4486 /* we need to deselect all other regionviews, and select this one
4487 i'm ignoring undo stuff, because the region creation will take care of it
4489 selection->set (latest_regionviews);
4491 drag_info.item = latest_regionviews.front()->get_canvas_group();
4492 drag_info.data = latest_regionviews.front();
4493 drag_info.motion_callback = &Editor::region_drag_motion_callback;
4494 drag_info.finished_callback = &Editor::region_drag_finished_callback;
4498 drag_info.source_trackview = clicked_routeview;
4499 drag_info.dest_trackview = drag_info.source_trackview;
4500 drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4501 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4503 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4507 Editor::cancel_selection ()
4509 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4510 (*i)->hide_selection ();
4512 selection->clear ();
4513 clicked_selection = 0;
4517 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4519 nframes64_t start = 0;
4520 nframes64_t end = 0;
4526 drag_info.item = item;
4527 drag_info.motion_callback = &Editor::drag_selection;
4528 drag_info.finished_callback = &Editor::end_selection_op;
4533 case CreateSelection:
4534 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4535 drag_info.copy = true;
4537 drag_info.copy = false;
4539 start_grab (event, selector_cursor);
4542 case SelectionStartTrim:
4543 if (clicked_axisview) {
4544 clicked_axisview->order_selection_trims (item, true);
4546 start_grab (event, trimmer_cursor);
4547 start = selection->time[clicked_selection].start;
4548 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4551 case SelectionEndTrim:
4552 if (clicked_axisview) {
4553 clicked_axisview->order_selection_trims (item, false);
4555 start_grab (event, trimmer_cursor);
4556 end = selection->time[clicked_selection].end;
4557 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
4561 start = selection->time[clicked_selection].start;
4563 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4567 if (selection_op == SelectionMove) {
4568 show_verbose_time_cursor(start, 10);
4570 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4575 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4577 nframes64_t start = 0;
4578 nframes64_t end = 0;
4580 nframes64_t pending_position;
4582 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4583 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4585 pending_position = 0;
4588 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4589 snap_to (pending_position);
4592 /* only alter selection if the current frame is
4593 different from the last frame position (adjusted)
4596 if (pending_position == drag_info.last_pointer_frame) return;
4598 switch (selection_op) {
4599 case CreateSelection:
4601 if (drag_info.first_move) {
4602 snap_to (drag_info.grab_frame);
4605 if (pending_position < drag_info.grab_frame) {
4606 start = pending_position;
4607 end = drag_info.grab_frame;
4609 end = pending_position;
4610 start = drag_info.grab_frame;
4613 /* first drag: Either add to the selection
4614 or create a new selection->
4617 if (drag_info.first_move) {
4619 begin_reversible_command (_("range selection"));
4621 if (drag_info.copy) {
4622 /* adding to the selection */
4623 clicked_selection = selection->add (start, end);
4624 drag_info.copy = false;
4626 /* new selection-> */
4627 clicked_selection = selection->set (clicked_axisview, start, end);
4632 case SelectionStartTrim:
4634 if (drag_info.first_move) {
4635 begin_reversible_command (_("trim selection start"));
4638 start = selection->time[clicked_selection].start;
4639 end = selection->time[clicked_selection].end;
4641 if (pending_position > end) {
4644 start = pending_position;
4648 case SelectionEndTrim:
4650 if (drag_info.first_move) {
4651 begin_reversible_command (_("trim selection end"));
4654 start = selection->time[clicked_selection].start;
4655 end = selection->time[clicked_selection].end;
4657 if (pending_position < start) {
4660 end = pending_position;
4667 if (drag_info.first_move) {
4668 begin_reversible_command (_("move selection"));
4671 start = selection->time[clicked_selection].start;
4672 end = selection->time[clicked_selection].end;
4674 length = end - start;
4676 start = pending_position;
4679 end = start + length;
4684 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4685 start_canvas_autoscroll (1, 0);
4689 selection->replace (clicked_selection, start, end);
4692 drag_info.last_pointer_frame = pending_position;
4693 drag_info.first_move = false;
4695 if (selection_op == SelectionMove) {
4696 show_verbose_time_cursor(start, 10);
4698 show_verbose_time_cursor(pending_position, 10);
4703 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4705 if (!drag_info.first_move) {
4706 drag_selection (item, event);
4707 /* XXX this is not object-oriented programming at all. ick */
4708 if (selection->time.consolidate()) {
4709 selection->TimeChanged ();
4711 commit_reversible_command ();
4713 /* just a click, no pointer movement.*/
4715 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4717 selection->clear_time();
4722 /* XXX what happens if its a music selection? */
4723 session->set_audio_range (selection->time);
4724 stop_canvas_autoscroll ();
4728 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4731 TimeAxisView* tvp = clicked_axisview;
4732 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4734 if (tv && tv->is_track()) {
4735 speed = tv->get_diskstream()->speed();
4738 nframes64_t region_start = (nframes64_t) (clicked_regionview->region()->position() / speed);
4739 nframes64_t region_end = (nframes64_t) (clicked_regionview->region()->last_frame() / speed);
4740 nframes64_t region_length = (nframes64_t) (clicked_regionview->region()->length() / speed);
4742 //drag_info.item = clicked_regionview->get_name_highlight();
4743 drag_info.item = item;
4744 drag_info.motion_callback = &Editor::trim_motion_callback;
4745 drag_info.finished_callback = &Editor::trim_finished_callback;
4747 start_grab (event, trimmer_cursor);
4749 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4750 trim_op = ContentsTrim;
4752 /* These will get overridden for a point trim.*/
4753 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4754 /* closer to start */
4755 trim_op = StartTrim;
4756 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4764 show_verbose_time_cursor(region_start, 10);
4767 show_verbose_time_cursor(region_end, 10);
4770 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4776 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4778 RegionView* rv = clicked_regionview;
4779 nframes64_t frame_delta = 0;
4780 bool left_direction;
4781 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4783 /* snap modifier works differently here..
4784 its' current state has to be passed to the
4785 various trim functions in order to work properly
4789 TimeAxisView* tvp = clicked_axisview;
4790 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4791 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4793 if (tv && tv->is_track()) {
4794 speed = tv->get_diskstream()->speed();
4797 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4798 left_direction = true;
4800 left_direction = false;
4804 snap_to (drag_info.current_pointer_frame);
4807 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4811 if (drag_info.first_move) {
4817 trim_type = "Region start trim";
4820 trim_type = "Region end trim";
4823 trim_type = "Region content trim";
4827 begin_reversible_command (trim_type);
4829 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4830 (*i)->fake_set_opaque(false);
4831 (*i)->region()->freeze ();
4833 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4835 arv->temporarily_hide_envelope ();
4837 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4838 insert_result = motion_frozen_playlists.insert (pl);
4839 if (insert_result.second) {
4840 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4846 if (left_direction) {
4847 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4849 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4854 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4857 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4858 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4864 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes64_t) (rv->region()->last_frame()/speed))) {
4867 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4868 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4875 bool swap_direction = false;
4877 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4878 swap_direction = true;
4881 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4882 i != selection->regions.by_layer().end(); ++i)
4884 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4892 show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
4895 show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
4898 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4902 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4903 drag_info.first_move = false;
4907 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4909 boost::shared_ptr<Region> region (rv.region());
4911 if (region->locked()) {
4915 nframes64_t new_bound;
4918 TimeAxisView* tvp = clicked_axisview;
4919 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4921 if (tv && tv->is_track()) {
4922 speed = tv->get_diskstream()->speed();
4925 if (left_direction) {
4926 if (swap_direction) {
4927 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4929 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4932 if (swap_direction) {
4933 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4935 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4940 snap_to (new_bound);
4942 region->trim_start ((nframes64_t) (new_bound * speed), this);
4943 rv.region_changed (StartChanged);
4947 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
4949 boost::shared_ptr<Region> region (rv.region());
4951 if (region->locked()) {
4955 nframes64_t new_bound;
4958 TimeAxisView* tvp = clicked_axisview;
4959 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4961 if (tv && tv->is_track()) {
4962 speed = tv->get_diskstream()->speed();
4965 if (left_direction) {
4966 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4968 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4972 snap_to (new_bound, (left_direction ? 0 : 1));
4975 region->trim_front ((nframes64_t) (new_bound * speed), this);
4977 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4981 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
4983 boost::shared_ptr<Region> region (rv.region());
4985 if (region->locked()) {
4989 nframes64_t new_bound;
4992 TimeAxisView* tvp = clicked_axisview;
4993 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4995 if (tv && tv->is_track()) {
4996 speed = tv->get_diskstream()->speed();
4999 if (left_direction) {
5000 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
5002 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
5006 snap_to (new_bound);
5008 region->trim_end ((nframes64_t) (new_bound * speed), this);
5009 rv.region_changed (LengthChanged);
5013 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
5015 if (!drag_info.first_move) {
5016 trim_motion_callback (item, event);
5018 if (!selection->selected (clicked_regionview)) {
5019 thaw_region_after_trim (*clicked_regionview);
5022 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5023 i != selection->regions.by_layer().end(); ++i)
5025 thaw_region_after_trim (**i);
5026 (*i)->fake_set_opaque (true);
5030 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
5032 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
5035 motion_frozen_playlists.clear ();
5037 commit_reversible_command();
5039 /* no mouse movement */
5045 Editor::point_trim (GdkEvent* event)
5047 RegionView* rv = clicked_regionview;
5048 nframes64_t new_bound = drag_info.current_pointer_frame;
5050 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5051 snap_to (new_bound);
5054 /* Choose action dependant on which button was pressed */
5055 switch (event->button.button) {
5057 trim_op = StartTrim;
5058 begin_reversible_command (_("Start point trim"));
5060 if (selection->selected (rv)) {
5062 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5063 i != selection->regions.by_layer().end(); ++i)
5065 if (!(*i)->region()->locked()) {
5066 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5067 XMLNode &before = pl->get_state();
5068 (*i)->region()->trim_front (new_bound, this);
5069 XMLNode &after = pl->get_state();
5070 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5076 if (!rv->region()->locked()) {
5077 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5078 XMLNode &before = pl->get_state();
5079 rv->region()->trim_front (new_bound, this);
5080 XMLNode &after = pl->get_state();
5081 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5085 commit_reversible_command();
5090 begin_reversible_command (_("End point trim"));
5092 if (selection->selected (rv)) {
5094 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
5096 if (!(*i)->region()->locked()) {
5097 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5098 XMLNode &before = pl->get_state();
5099 (*i)->region()->trim_end (new_bound, this);
5100 XMLNode &after = pl->get_state();
5101 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5107 if (!rv->region()->locked()) {
5108 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5109 XMLNode &before = pl->get_state();
5110 rv->region()->trim_end (new_bound, this);
5111 XMLNode &after = pl->get_state();
5112 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
5116 commit_reversible_command();
5125 Editor::thaw_region_after_trim (RegionView& rv)
5127 boost::shared_ptr<Region> region (rv.region());
5129 if (region->locked()) {
5133 region->thaw (_("trimmed region"));
5134 XMLNode &after = region->playlist()->get_state();
5135 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
5137 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
5139 arv->unhide_envelope ();
5143 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
5148 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
5149 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
5153 Location* location = find_location_from_marker (marker, is_start);
5154 location->set_hidden (true, this);
5159 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
5165 drag_info.item = item;
5166 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
5167 drag_info.finished_callback = &Editor::end_range_markerbar_op;
5169 range_marker_op = op;
5171 if (!temp_location) {
5172 temp_location = new Location;
5176 case CreateRangeMarker:
5177 case CreateTransportMarker:
5178 case CreateCDMarker:
5180 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
5181 drag_info.copy = true;
5183 drag_info.copy = false;
5185 start_grab (event, selector_cursor);
5189 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5194 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5196 nframes64_t start = 0;
5197 nframes64_t end = 0;
5198 ArdourCanvas::SimpleRect *crect;
5200 switch (range_marker_op) {
5201 case CreateRangeMarker:
5202 crect = range_bar_drag_rect;
5204 case CreateTransportMarker:
5205 crect = transport_bar_drag_rect;
5207 case CreateCDMarker:
5208 crect = cd_marker_bar_drag_rect;
5211 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
5216 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5217 snap_to (drag_info.current_pointer_frame);
5220 /* only alter selection if the current frame is
5221 different from the last frame position.
5224 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5226 switch (range_marker_op) {
5227 case CreateRangeMarker:
5228 case CreateTransportMarker:
5229 case CreateCDMarker:
5230 if (drag_info.first_move) {
5231 snap_to (drag_info.grab_frame);
5234 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5235 start = drag_info.current_pointer_frame;
5236 end = drag_info.grab_frame;
5238 end = drag_info.current_pointer_frame;
5239 start = drag_info.grab_frame;
5242 /* first drag: Either add to the selection
5243 or create a new selection.
5246 if (drag_info.first_move) {
5248 temp_location->set (start, end);
5252 update_marker_drag_item (temp_location);
5253 range_marker_drag_rect->show();
5254 //range_marker_drag_rect->raise_to_top();
5260 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
5261 start_canvas_autoscroll (1, 0);
5265 temp_location->set (start, end);
5267 double x1 = frame_to_pixel (start);
5268 double x2 = frame_to_pixel (end);
5269 crect->property_x1() = x1;
5270 crect->property_x2() = x2;
5272 update_marker_drag_item (temp_location);
5275 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5276 drag_info.first_move = false;
5278 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5283 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5285 Location * newloc = 0;
5289 if (!drag_info.first_move) {
5290 drag_range_markerbar_op (item, event);
5292 switch (range_marker_op) {
5293 case CreateRangeMarker:
5294 case CreateCDMarker:
5296 begin_reversible_command (_("new range marker"));
5297 XMLNode &before = session->locations()->get_state();
5298 session->locations()->next_available_name(rangename,"unnamed");
5299 if (range_marker_op == CreateCDMarker) {
5300 flags = Location::IsRangeMarker|Location::IsCDMarker;
5301 cd_marker_bar_drag_rect->hide();
5304 flags = Location::IsRangeMarker;
5305 range_bar_drag_rect->hide();
5307 newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
5308 session->locations()->add (newloc, true);
5309 XMLNode &after = session->locations()->get_state();
5310 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
5311 commit_reversible_command ();
5313 range_marker_drag_rect->hide();
5317 case CreateTransportMarker:
5318 // popup menu to pick loop or punch
5319 new_transport_marker_context_menu (&event->button, item);
5324 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5326 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
5331 start = session->locations()->first_mark_before (drag_info.grab_frame);
5332 end = session->locations()->first_mark_after (drag_info.grab_frame);
5334 if (end == max_frames) {
5335 end = session->current_end_frame ();
5339 start = session->current_start_frame ();
5342 switch (mouse_mode) {
5344 /* find the two markers on either side and then make the selection from it */
5345 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
5349 /* find the two markers on either side of the click and make the range out of it */
5350 selection->set (0, start, end);
5359 stop_canvas_autoscroll ();
5365 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5367 drag_info.item = item;
5368 drag_info.motion_callback = &Editor::drag_mouse_zoom;
5369 drag_info.finished_callback = &Editor::end_mouse_zoom;
5371 start_grab (event, zoom_cursor);
5373 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5377 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5382 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5383 snap_to (drag_info.current_pointer_frame);
5385 if (drag_info.first_move) {
5386 snap_to (drag_info.grab_frame);
5390 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5392 /* base start and end on initial click position */
5393 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5394 start = drag_info.current_pointer_frame;
5395 end = drag_info.grab_frame;
5397 end = drag_info.current_pointer_frame;
5398 start = drag_info.grab_frame;
5403 if (drag_info.first_move) {
5405 zoom_rect->raise_to_top();
5408 reposition_zoom_rect(start, end);
5410 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5411 drag_info.first_move = false;
5413 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5418 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5420 if (!drag_info.first_move) {
5421 drag_mouse_zoom (item, event);
5423 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5424 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
5426 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
5429 temporal_zoom_to_frame (false, drag_info.grab_frame);
5431 temporal_zoom_step (false);
5432 center_screen (drag_info.grab_frame);
5440 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
5442 double x1 = frame_to_pixel (start);
5443 double x2 = frame_to_pixel (end);
5444 double y2 = full_canvas_height - 1.0;
5446 zoom_rect->property_x1() = x1;
5447 zoom_rect->property_y1() = 1.0;
5448 zoom_rect->property_x2() = x2;
5449 zoom_rect->property_y2() = y2;
5453 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5455 drag_info.item = item;
5456 drag_info.motion_callback = &Editor::drag_rubberband_select;
5457 drag_info.finished_callback = &Editor::end_rubberband_select;
5459 start_grab (event, cross_hair_cursor);
5461 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5465 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5472 /* use a bigger drag threshold than the default */
5474 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
5478 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) {
5479 if (drag_info.first_move) {
5480 snap_to (drag_info.grab_frame);
5482 snap_to (drag_info.current_pointer_frame);
5485 /* base start and end on initial click position */
5487 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5488 start = drag_info.current_pointer_frame;
5489 end = drag_info.grab_frame;
5491 end = drag_info.current_pointer_frame;
5492 start = drag_info.grab_frame;
5495 if (drag_info.current_pointer_y < drag_info.grab_y) {
5496 y1 = drag_info.current_pointer_y;
5497 y2 = drag_info.grab_y;
5499 y2 = drag_info.current_pointer_y;
5500 y1 = drag_info.grab_y;
5504 if (start != end || y1 != y2) {
5506 double x1 = frame_to_pixel (start);
5507 double x2 = frame_to_pixel (end);
5509 rubberband_rect->property_x1() = x1;
5510 rubberband_rect->property_y1() = y1;
5511 rubberband_rect->property_x2() = x2;
5512 rubberband_rect->property_y2() = y2;
5514 rubberband_rect->show();
5515 rubberband_rect->raise_to_top();
5517 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5518 drag_info.first_move = false;
5520 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5525 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5527 if (!drag_info.first_move) {
5529 drag_rubberband_select (item, event);
5532 if (drag_info.current_pointer_y < drag_info.grab_y) {
5533 y1 = drag_info.current_pointer_y;
5534 y2 = drag_info.grab_y;
5536 y2 = drag_info.current_pointer_y;
5537 y1 = drag_info.grab_y;
5541 Selection::Operation op = Keyboard::selection_type (event->button.state);
5544 begin_reversible_command (_("rubberband selection"));
5546 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5547 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5549 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5553 commit_reversible_command ();
5557 selection->clear_tracks();
5558 selection->clear_regions();
5559 selection->clear_points ();
5560 selection->clear_lines ();
5563 rubberband_rect->hide();
5568 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5570 using namespace Gtkmm2ext;
5572 ArdourPrompter prompter (false);
5574 prompter.set_prompt (_("Name for region:"));
5575 prompter.set_initial_text (clicked_regionview->region()->name());
5576 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5577 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5578 prompter.show_all ();
5579 switch (prompter.run ()) {
5580 case Gtk::RESPONSE_ACCEPT:
5582 prompter.get_result(str);
5584 clicked_regionview->region()->set_name (str);
5592 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5594 drag_info.item = item;
5595 drag_info.motion_callback = &Editor::time_fx_motion;
5596 drag_info.finished_callback = &Editor::end_time_fx;
5600 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5604 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5606 RegionView* rv = clicked_regionview;
5608 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5609 snap_to (drag_info.current_pointer_frame);
5612 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5616 if (drag_info.current_pointer_frame > rv->region()->position()) {
5617 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5620 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5621 drag_info.first_move = false;
5623 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5627 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5629 clicked_regionview->get_time_axis_view().hide_timestretch ();
5631 if (drag_info.first_move) {
5635 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5636 /* backwards drag of the left edge - not usable */
5640 nframes64_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5642 float percentage = (double) newlen / (double) clicked_regionview->region()->length();
5644 #ifndef USE_RUBBERBAND
5645 // Soundtouch uses percentage / 100 instead of normal (/ 1)
5646 if (clicked_regionview->region()->data_type() == DataType::AUDIO) {
5647 percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5651 begin_reversible_command (_("timestretch"));
5653 // XXX how do timeFX on multiple regions ?
5656 rs.add (clicked_regionview);
5658 if (time_stretch (rs, percentage) == 0) {
5659 session->commit_reversible_command ();
5664 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
5666 /* no brushing without a useful snap setting */
5668 switch (snap_mode) {
5670 return; /* can't work because it allows region to be placed anywhere */
5675 switch (snap_type) {
5683 /* don't brush a copy over the original */
5685 if (pos == rv->region()->position()) {
5689 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
5691 if (rtv == 0 || !rtv->is_track()) {
5695 boost::shared_ptr<Playlist> playlist = rtv->playlist();
5696 double speed = rtv->get_diskstream()->speed();
5698 XMLNode &before = playlist->get_state();
5699 playlist->add_region (RegionFactory::create (rv->region()), (nframes64_t) (pos * speed));
5700 XMLNode &after = playlist->get_state();
5701 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5703 // playlist is frozen, so we have to update manually
5705 playlist->Modified(); /* EMIT SIGNAL */
5709 Editor::track_height_step_timeout ()
5711 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
5712 current_stepping_trackview = 0;