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 drag_info.clear_copied_locations ();
1886 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1888 if (drag_info.item == 0) {
1889 fatal << _("programming error: start_grab called without drag item") << endmsg;
1895 cursor = which_grabber_cursor ();
1898 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1900 if (event->button.button == 2) {
1901 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
1902 drag_info.y_constrained = true;
1903 drag_info.x_constrained = false;
1905 drag_info.y_constrained = false;
1906 drag_info.x_constrained = true;
1909 drag_info.x_constrained = false;
1910 drag_info.y_constrained = false;
1913 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1914 drag_info.last_pointer_frame = drag_info.grab_frame;
1915 drag_info.current_pointer_frame = drag_info.grab_frame;
1916 drag_info.current_pointer_x = drag_info.grab_x;
1917 drag_info.current_pointer_y = drag_info.grab_y;
1918 drag_info.last_pointer_x = drag_info.current_pointer_x;
1919 drag_info.last_pointer_y = drag_info.current_pointer_y;
1920 drag_info.cumulative_x_drag = 0;
1921 drag_info.cumulative_y_drag = 0;
1922 drag_info.first_move = true;
1923 drag_info.move_threshold_passed = false;
1924 drag_info.want_move_threshold = false;
1925 drag_info.pointer_frame_offset = 0;
1926 drag_info.brushing = false;
1927 drag_info.clear_copied_locations ();
1929 drag_info.original_x = 0;
1930 drag_info.original_y = 0;
1931 drag_info.item->i2w (drag_info.original_x, drag_info.original_y);
1933 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1935 event->button.time);
1937 if (session && session->transport_rolling()) {
1938 drag_info.was_rolling = true;
1940 drag_info.was_rolling = false;
1943 switch (snap_type) {
1944 case SnapToRegionStart:
1945 case SnapToRegionEnd:
1946 case SnapToRegionSync:
1947 case SnapToRegionBoundary:
1948 build_region_boundary_cache ();
1956 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1958 drag_info.item->ungrab (0);
1959 drag_info.item = new_item;
1962 cursor = which_grabber_cursor ();
1965 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1969 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1971 bool did_drag = false;
1973 stop_canvas_autoscroll ();
1975 if (drag_info.item == 0) {
1979 drag_info.item->ungrab (event->button.time);
1981 if (drag_info.finished_callback) {
1982 drag_info.last_pointer_x = drag_info.current_pointer_x;
1983 drag_info.last_pointer_y = drag_info.current_pointer_y;
1984 (this->*(drag_info.finished_callback)) (item, event);
1987 did_drag = !drag_info.first_move;
1989 hide_verbose_canvas_cursor();
1997 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1999 drag_info.item = item;
2000 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
2001 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
2005 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
2006 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
2010 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2013 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes64_t) arv->audio_region()->fade_in()->back()->when + arv->region()->position());
2017 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2019 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2021 nframes64_t fade_length;
2023 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2024 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2030 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2034 if (pos < (arv->region()->position() + 64)) {
2035 fade_length = 64; // this should be a minimum defined somewhere
2036 } else if (pos > arv->region()->last_frame()) {
2037 fade_length = arv->region()->length();
2039 fade_length = pos - arv->region()->position();
2041 /* mapover the region selection */
2043 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2045 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2051 tmp->reset_fade_in_shape_width (fade_length);
2054 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
2056 drag_info.first_move = false;
2060 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2062 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2064 nframes64_t fade_length;
2066 if (drag_info.first_move) return;
2068 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2069 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2074 if (pos < (arv->region()->position() + 64)) {
2075 fade_length = 64; // this should be a minimum defined somewhere
2076 } else if (pos > arv->region()->last_frame()) {
2077 fade_length = arv->region()->length();
2079 fade_length = pos - arv->region()->position();
2082 begin_reversible_command (_("change fade in length"));
2084 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2086 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2092 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2093 XMLNode &before = alist->get_state();
2095 tmp->audio_region()->set_fade_in_length (fade_length);
2096 tmp->audio_region()->set_fade_in_active (true);
2098 XMLNode &after = alist->get_state();
2099 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2102 commit_reversible_command ();
2106 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
2108 drag_info.item = item;
2109 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
2110 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
2114 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
2115 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
2119 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2121 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes64_t) arv->audio_region()->fade_out()->back()->when + arv->region()->position());
2125 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2127 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2129 nframes64_t fade_length;
2131 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2132 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2137 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2141 if (pos > (arv->region()->last_frame() - 64)) {
2142 fade_length = 64; // this should really be a minimum fade defined somewhere
2144 else if (pos < arv->region()->position()) {
2145 fade_length = arv->region()->length();
2148 fade_length = arv->region()->last_frame() - pos;
2151 /* mapover the region selection */
2153 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2155 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2161 tmp->reset_fade_out_shape_width (fade_length);
2164 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
2166 drag_info.first_move = false;
2170 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2172 if (drag_info.first_move) return;
2174 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2176 nframes64_t fade_length;
2178 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2179 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2185 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2189 if (pos > (arv->region()->last_frame() - 64)) {
2190 fade_length = 64; // this should really be a minimum fade defined somewhere
2192 else if (pos < arv->region()->position()) {
2193 fade_length = arv->region()->length();
2196 fade_length = arv->region()->last_frame() - pos;
2199 begin_reversible_command (_("change fade out length"));
2201 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2203 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2209 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2210 XMLNode &before = alist->get_state();
2212 tmp->audio_region()->set_fade_out_length (fade_length);
2213 tmp->audio_region()->set_fade_out_active (true);
2215 XMLNode &after = alist->get_state();
2216 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2219 commit_reversible_command ();
2223 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2225 drag_info.item = item;
2226 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2227 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2231 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2232 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2236 Cursor* cursor = (Cursor *) drag_info.data;
2238 if (cursor == playhead_cursor) {
2239 _dragging_playhead = true;
2241 if (session && drag_info.was_rolling) {
2242 session->request_stop ();
2245 if (session && session->is_auditioning()) {
2246 session->cancel_audition ();
2250 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2252 show_verbose_time_cursor (cursor->current_frame, 10);
2256 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2258 Cursor* cursor = (Cursor *) drag_info.data;
2259 nframes64_t adjusted_frame;
2261 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2262 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2268 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2269 if (cursor == playhead_cursor) {
2270 snap_to (adjusted_frame);
2274 if (adjusted_frame == drag_info.last_pointer_frame) return;
2276 cursor->set_position (adjusted_frame);
2278 UpdateAllTransportClocks (cursor->current_frame);
2280 show_verbose_time_cursor (cursor->current_frame, 10);
2282 drag_info.last_pointer_frame = adjusted_frame;
2283 drag_info.first_move = false;
2287 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2289 if (drag_info.first_move) return;
2291 cursor_drag_motion_callback (item, event);
2293 _dragging_playhead = false;
2295 if (item == &playhead_cursor->canvas_item) {
2297 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2303 Editor::update_marker_drag_item (Location *location)
2305 double x1 = frame_to_pixel (location->start());
2306 double x2 = frame_to_pixel (location->end());
2308 if (location->is_mark()) {
2309 marker_drag_line_points.front().set_x(x1);
2310 marker_drag_line_points.back().set_x(x1);
2311 marker_drag_line->property_points() = marker_drag_line_points;
2314 range_marker_drag_rect->property_x1() = x1;
2315 range_marker_drag_rect->property_x2() = x2;
2321 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2325 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2326 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2332 Location *location = find_location_from_marker (marker, is_start);
2334 drag_info.item = item;
2335 drag_info.data = marker;
2336 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2337 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2341 _dragging_edit_point = true;
2343 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2345 update_marker_drag_item (location);
2347 if (location->is_mark()) {
2348 // marker_drag_line->show();
2349 // marker_drag_line->raise_to_top();
2351 range_marker_drag_rect->show();
2352 //range_marker_drag_rect->raise_to_top();
2356 show_verbose_time_cursor (location->start(), 10);
2358 show_verbose_time_cursor (location->end(), 10);
2361 Selection::Operation op = Keyboard::selection_type (event->button.state);
2364 case Selection::Toggle:
2365 selection->toggle (marker);
2367 case Selection::Set:
2368 if (!selection->selected (marker)) {
2369 selection->set (marker);
2372 case Selection::Extend:
2374 Locations::LocationList ll;
2375 list<Marker*> to_add;
2377 selection->markers.range (s, e);
2378 s = min (marker->position(), s);
2379 e = max (marker->position(), e);
2382 if (e < max_frames) {
2385 session->locations()->find_all_between (s, e, ll, Location::Flags (0));
2386 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2387 LocationMarkers* lm = find_location_markers (*i);
2390 to_add.push_back (lm->start);
2393 to_add.push_back (lm->end);
2397 if (!to_add.empty()) {
2398 selection->add (to_add);
2402 case Selection::Add:
2403 selection->add (marker);
2407 /* set up copies for us to manipulate during the drag */
2409 drag_info.clear_copied_locations ();
2411 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
2412 Location *l = find_location_from_marker (*i, is_start);
2413 drag_info.copied_locations.push_back (new Location (*l));
2418 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2420 nframes64_t f_delta;
2421 nframes64_t newframe;
2423 bool move_both = false;
2424 Marker* dragged_marker = (Marker*) drag_info.data;
2426 Location *real_location;
2427 Location *copy_location;
2429 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2430 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2435 nframes64_t next = newframe;
2437 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2438 snap_to (newframe, 0, true);
2441 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2445 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2449 MarkerSelection::iterator i;
2450 list<Location*>::iterator x;
2452 /* find the marker we're dragging, and compute the delta */
2454 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2455 x != drag_info.copied_locations.end() && i != selection->markers.end();
2461 if (marker == dragged_marker) {
2463 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2468 if (real_location->is_mark()) {
2469 f_delta = newframe - copy_location->start();
2473 switch (marker->type()) {
2475 case Marker::LoopStart:
2476 case Marker::PunchIn:
2477 f_delta = newframe - copy_location->start();
2481 case Marker::LoopEnd:
2482 case Marker::PunchOut:
2483 f_delta = newframe - copy_location->end();
2486 /* what kind of marker is this ? */
2494 if (i == selection->markers.end()) {
2495 /* hmm, impossible - we didn't find the dragged marker */
2499 /* now move them all */
2501 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2502 x != drag_info.copied_locations.end() && i != selection->markers.end();
2508 /* call this to find out if its the start or end */
2510 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2514 if (real_location->locked()) {
2518 if (copy_location->is_mark()) {
2522 copy_location->set_start (copy_location->start() + f_delta);
2526 nframes64_t new_start = copy_location->start() + f_delta;
2527 nframes64_t new_end = copy_location->end() + f_delta;
2529 if (is_start) { // start-of-range marker
2532 copy_location->set_start (new_start);
2533 copy_location->set_end (new_end);
2534 } else if (new_start < copy_location->end()) {
2535 copy_location->set_start (new_start);
2537 snap_to (next, 1, true);
2538 copy_location->set_end (next);
2539 copy_location->set_start (newframe);
2542 } else { // end marker
2545 copy_location->set_end (new_end);
2546 copy_location->set_start (new_start);
2547 } else if (new_end > copy_location->start()) {
2548 copy_location->set_end (new_end);
2549 } else if (newframe > 0) {
2550 snap_to (next, -1, true);
2551 copy_location->set_start (next);
2552 copy_location->set_end (newframe);
2556 update_marker_drag_item (copy_location);
2558 LocationMarkers* lm = find_location_markers (real_location);
2561 lm->set_position (copy_location->start(), copy_location->end());
2565 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2566 drag_info.first_move = false;
2568 if (drag_info.copied_locations.empty()) {
2572 edit_point_clock.set (drag_info.copied_locations.front()->start());
2573 show_verbose_time_cursor (newframe, 10);
2576 track_canvas->update_now ();
2581 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2583 if (drag_info.first_move) {
2585 /* just a click, do nothing but finish
2586 off the selection process
2589 Selection::Operation op = Keyboard::selection_type (event->button.state);
2590 Marker* marker = (Marker *) drag_info.data;
2593 case Selection::Set:
2594 if (selection->selected (marker) && selection->markers.size() > 1) {
2595 selection->set (marker);
2599 case Selection::Toggle:
2600 case Selection::Extend:
2601 case Selection::Add:
2608 _dragging_edit_point = false;
2611 begin_reversible_command ( _("move marker") );
2612 XMLNode &before = session->locations()->get_state();
2614 MarkerSelection::iterator i;
2615 list<Location*>::iterator x;
2618 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2619 x != drag_info.copied_locations.end() && i != selection->markers.end();
2622 Location * location = find_location_from_marker ((*i), is_start);
2626 if (location->locked()) {
2630 if (location->is_mark()) {
2631 location->set_start ((*x)->start());
2633 location->set ((*x)->start(), (*x)->end());
2638 XMLNode &after = session->locations()->get_state();
2639 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2640 commit_reversible_command ();
2642 marker_drag_line->hide();
2643 range_marker_drag_rect->hide();
2647 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2650 MeterMarker* meter_marker;
2652 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2653 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2657 meter_marker = dynamic_cast<MeterMarker*> (marker);
2659 MetricSection& section (meter_marker->meter());
2661 if (!section.movable()) {
2665 drag_info.item = item;
2666 drag_info.copy = false;
2667 drag_info.data = marker;
2668 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2669 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2673 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2675 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2679 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2682 MeterMarker* meter_marker;
2684 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2685 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2689 meter_marker = dynamic_cast<MeterMarker*> (marker);
2691 // create a dummy marker for visual representation of moving the copy.
2692 // The actual copying is not done before we reach the finish callback.
2694 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2695 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2696 *new MeterSection(meter_marker->meter()));
2698 drag_info.item = &new_marker->the_item();
2699 drag_info.copy = true;
2700 drag_info.data = new_marker;
2701 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2702 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2706 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2708 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2712 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2714 MeterMarker* marker = (MeterMarker *) drag_info.data;
2715 nframes64_t adjusted_frame;
2717 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2718 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2724 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2725 snap_to (adjusted_frame);
2728 if (adjusted_frame == drag_info.last_pointer_frame) return;
2730 marker->set_position (adjusted_frame);
2733 drag_info.last_pointer_frame = adjusted_frame;
2734 drag_info.first_move = false;
2736 show_verbose_time_cursor (adjusted_frame, 10);
2740 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2742 if (drag_info.first_move) return;
2744 meter_marker_drag_motion_callback (drag_info.item, event);
2746 MeterMarker* marker = (MeterMarker *) drag_info.data;
2749 TempoMap& map (session->tempo_map());
2750 map.bbt_time (drag_info.last_pointer_frame, when);
2752 if (drag_info.copy == true) {
2753 begin_reversible_command (_("copy meter mark"));
2754 XMLNode &before = map.get_state();
2755 map.add_meter (marker->meter(), when);
2756 XMLNode &after = map.get_state();
2757 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2758 commit_reversible_command ();
2760 // delete the dummy marker we used for visual representation of copying.
2761 // a new visual marker will show up automatically.
2764 begin_reversible_command (_("move meter mark"));
2765 XMLNode &before = map.get_state();
2766 map.move_meter (marker->meter(), when);
2767 XMLNode &after = map.get_state();
2768 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2769 commit_reversible_command ();
2774 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2777 TempoMarker* tempo_marker;
2779 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2780 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2784 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2785 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2789 MetricSection& section (tempo_marker->tempo());
2791 if (!section.movable()) {
2795 drag_info.item = item;
2796 drag_info.copy = false;
2797 drag_info.data = marker;
2798 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2799 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2803 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2804 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2808 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2811 TempoMarker* tempo_marker;
2813 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2814 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2818 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2819 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2823 // create a dummy marker for visual representation of moving the copy.
2824 // The actual copying is not done before we reach the finish callback.
2826 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2827 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2828 *new TempoSection(tempo_marker->tempo()));
2830 drag_info.item = &new_marker->the_item();
2831 drag_info.copy = true;
2832 drag_info.data = new_marker;
2833 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2834 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2838 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2840 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2844 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2846 TempoMarker* marker = (TempoMarker *) drag_info.data;
2847 nframes64_t adjusted_frame;
2849 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2850 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2856 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2857 snap_to (adjusted_frame);
2860 if (adjusted_frame == drag_info.last_pointer_frame) return;
2862 /* OK, we've moved far enough to make it worth actually move the thing. */
2864 marker->set_position (adjusted_frame);
2866 show_verbose_time_cursor (adjusted_frame, 10);
2868 drag_info.last_pointer_frame = adjusted_frame;
2869 drag_info.first_move = false;
2873 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2875 if (drag_info.first_move) return;
2877 tempo_marker_drag_motion_callback (drag_info.item, event);
2879 TempoMarker* marker = (TempoMarker *) drag_info.data;
2882 TempoMap& map (session->tempo_map());
2883 map.bbt_time (drag_info.last_pointer_frame, when);
2885 if (drag_info.copy == true) {
2886 begin_reversible_command (_("copy tempo mark"));
2887 XMLNode &before = map.get_state();
2888 map.add_tempo (marker->tempo(), when);
2889 XMLNode &after = map.get_state();
2890 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2891 commit_reversible_command ();
2893 // delete the dummy marker we used for visual representation of copying.
2894 // a new visual marker will show up automatically.
2897 begin_reversible_command (_("move tempo mark"));
2898 XMLNode &before = map.get_state();
2899 map.move_tempo (marker->tempo(), when);
2900 XMLNode &after = map.get_state();
2901 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2902 commit_reversible_command ();
2907 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2909 ControlPoint* control_point;
2911 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2912 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2916 // We shouldn't remove the first or last gain point
2917 if (control_point->line().is_last_point(*control_point) ||
2918 control_point->line().is_first_point(*control_point)) {
2922 control_point->line().remove_point (*control_point);
2926 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event)
2928 ControlPoint* control_point;
2930 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2931 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2935 control_point->line().remove_point (*control_point);
2939 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2941 ControlPoint* control_point;
2943 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2944 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2948 drag_info.item = item;
2949 drag_info.data = control_point;
2950 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2951 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2953 start_grab (event, fader_cursor);
2955 // start the grab at the center of the control point so
2956 // the point doesn't 'jump' to the mouse after the first drag
2957 drag_info.grab_x = control_point->get_x();
2958 drag_info.grab_y = control_point->get_y();
2960 control_point->line().parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
2961 track_canvas->w2c(drag_info.grab_x, drag_info.grab_y, drag_info.grab_x, drag_info.grab_y);
2963 drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
2965 control_point->line().start_drag (control_point, drag_info.grab_frame, 0);
2967 double fraction = 1.0 - ((control_point->get_y() - control_point->line().y_position()) / (double)control_point->line().height());
2968 set_verbose_canvas_cursor (control_point->line().get_verbose_cursor_string (fraction),
2969 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2971 show_verbose_canvas_cursor ();
2975 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2977 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2979 double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
2980 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2982 if (event->button.state & Keyboard::SecondaryModifier) {
2987 double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
2988 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2990 // calculate zero crossing point. back off by .01 to stay on the
2991 // positive side of zero
2993 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line().height() - .01;
2994 cp->line().parent_group().i2w(_unused, zero_gain_y);
2996 // make sure we hit zero when passing through
2997 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2998 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
3002 if (drag_info.x_constrained) {
3003 cx = drag_info.grab_x;
3005 if (drag_info.y_constrained) {
3006 cy = drag_info.grab_y;
3009 drag_info.cumulative_x_drag = cx - drag_info.grab_x;
3010 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
3012 cp->line().parent_group().w2i (cx, cy);
3016 cy = min ((double) (cp->line().y_position() + cp->line().height()), cy);
3018 //translate cx to frames
3019 nframes64_t cx_frames = unit_to_frame (cx);
3021 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
3022 snap_to (cx_frames);
3025 const double fraction = 1.0 - ((cy - cp->line().y_position()) / (double)cp->line().height());
3029 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3035 cp->line().point_drag (*cp, cx_frames , fraction, push);
3037 set_verbose_canvas_cursor_text (cp->line().get_verbose_cursor_string (fraction));
3039 drag_info.first_move = false;
3043 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3045 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
3047 if (drag_info.first_move) {
3051 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3052 reset_point_selection ();
3056 control_point_drag_motion_callback (item, event);
3058 cp->line().end_drag (cp);
3062 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
3064 switch (mouse_mode) {
3066 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
3067 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
3075 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
3079 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
3080 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
3084 start_line_grab (al, event);
3088 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
3092 nframes64_t frame_within_region;
3094 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3098 cx = event->button.x;
3099 cy = event->button.y;
3100 line->parent_group().w2i (cx, cy);
3101 frame_within_region = (nframes64_t) floor (cx * frames_per_unit);
3103 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
3104 current_line_drag_info.after)) {
3105 /* no adjacent points */
3109 drag_info.item = &line->grab_item();
3110 drag_info.data = line;
3111 drag_info.motion_callback = &Editor::line_drag_motion_callback;
3112 drag_info.finished_callback = &Editor::line_drag_finished_callback;
3114 start_grab (event, fader_cursor);
3116 const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height());
3118 line->start_drag (0, drag_info.grab_frame, fraction);
3120 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
3121 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
3122 show_verbose_canvas_cursor ();
3126 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3128 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3130 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
3132 if (event->button.state & Keyboard::SecondaryModifier) {
3136 double cx = drag_info.current_pointer_x;
3137 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
3139 // calculate zero crossing point. back off by .01 to stay on the
3140 // positive side of zero
3142 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
3143 line->parent_group().i2w(_unused, zero_gain_y);
3145 // make sure we hit zero when passing through
3146 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
3147 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
3151 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
3153 line->parent_group().w2i (cx, cy);
3156 cy = min ((double) line->height(), cy);
3158 const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height());
3162 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3168 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
3170 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
3174 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3176 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3177 line_drag_motion_callback (item, event);
3182 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3184 if (selection->regions.empty() || clicked_regionview == 0) {
3188 drag_info.copy = false;
3189 drag_info.item = item;
3190 drag_info.data = clicked_regionview;
3192 if (Config->get_edit_mode() == Splice) {
3193 drag_info.motion_callback = &Editor::region_drag_splice_motion_callback;
3194 drag_info.finished_callback = &Editor::region_drag_splice_finished_callback;
3196 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3197 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3203 TimeAxisView* tvp = clicked_axisview;
3204 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3206 if (tv && tv->is_track()) {
3207 speed = tv->get_diskstream()->speed();
3210 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3211 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3212 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3213 drag_info.dest_trackview = drag_info.source_trackview;
3214 // we want a move threshold
3215 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 the group containing moved regions may have been
3222 offset during autoscroll. reset its y offset
3223 (we should really handle this in the same way
3224 we do with the x axis, but a simple way of achieving that
3225 eludes me right now).
3228 _region_motion_group->property_y() = 0;
3230 /* sync the canvas to what we think is its current state */
3231 track_canvas->update_now();
3235 Editor::start_create_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3237 drag_info.copy = false;
3238 drag_info.item = item;
3239 drag_info.data = clicked_axisview;
3240 drag_info.source_trackview = clicked_axisview;
3241 drag_info.dest_trackview = drag_info.source_trackview;
3242 drag_info.motion_callback = &Editor::create_region_drag_motion_callback;
3243 drag_info.finished_callback = &Editor::create_region_drag_finished_callback;
3249 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
3251 if (selection->regions.empty() || clicked_regionview == 0) {
3255 drag_info.copy = true;
3256 drag_info.item = item;
3257 drag_info.data = clicked_regionview;
3261 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
3262 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3265 if (rtv && rtv->is_track()) {
3266 speed = rtv->get_diskstream()->speed();
3269 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3270 drag_info.dest_trackview = drag_info.source_trackview;
3271 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3272 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3273 // we want a move threshold
3274 drag_info.want_move_threshold = true;
3275 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3276 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3277 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3279 _region_motion_group->property_y() = 0;
3283 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
3285 if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) {
3289 drag_info.copy = false;
3290 drag_info.item = item;
3291 drag_info.data = clicked_regionview;
3292 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3293 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3298 TimeAxisView* tvp = clicked_axisview;
3299 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3301 if (tv && tv->is_track()) {
3302 speed = tv->get_diskstream()->speed();
3305 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3306 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3307 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3308 drag_info.dest_trackview = drag_info.source_trackview;
3309 // we want a move threshold
3310 drag_info.want_move_threshold = true;
3311 drag_info.brushing = true;
3313 begin_reversible_command (_("Drag region brush"));
3317 Editor::possibly_copy_regions_during_grab (GdkEvent* event)
3319 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
3321 drag_info.want_move_threshold = false; // don't copy again
3323 /* duplicate the regionview(s) and region(s) */
3325 vector<RegionView*> new_regionviews;
3327 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3333 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
3334 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
3337 nrv = new AudioRegionView (*arv);
3339 nrv = new MidiRegionView (*mrv);
3344 const boost::shared_ptr<const Region> original = rv->region();
3345 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
3347 nrv->get_canvas_group()->show ();
3348 new_regionviews.push_back (nrv);
3351 if (new_regionviews.empty()) {
3355 /* reset selection to new regionviews. This will not set selection visual status for
3356 these regionviews since they don't belong to a track, so do that by hand too.
3359 selection->set (new_regionviews);
3361 for (vector<RegionView*>::iterator i = new_regionviews.begin(); i != new_regionviews.end(); ++i) {
3362 (*i)->set_selected (true);
3365 /* reset drag_info data to reflect the fact that we are dragging the copies */
3367 drag_info.data = new_regionviews.front();
3369 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3371 sync the canvas to what we think is its current state
3372 without it, the canvas seems to
3373 "forget" to update properly after the upcoming reparent()
3374 ..only if the mouse is in rapid motion at the time of the grab.
3375 something to do with regionview creation raking so long?
3377 track_canvas->update_now();
3382 Editor::check_region_drag_possible (RouteTimeAxisView** tv)
3384 /* Which trackview is this ? */
3386 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
3387 (*tv) = dynamic_cast<RouteTimeAxisView*>(tvp);
3389 /* The region motion is only processed if the pointer is over
3393 if (!(*tv) || !(*tv)->is_track()) {
3394 /* To make sure we hide the verbose canvas cursor when the mouse is
3395 not held over and audiotrack.
3397 hide_verbose_canvas_cursor ();
3404 struct RegionSelectionByPosition {
3405 bool operator() (RegionView*a, RegionView* b) {
3406 return a->region()->position () < b->region()->position();
3411 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3413 RouteTimeAxisView* tv;
3415 if (!check_region_drag_possible (&tv)) {
3419 if (!drag_info.move_threshold_passed) {
3425 if (drag_info.current_pointer_x - drag_info.grab_x > 0) {
3431 RegionSelection copy (selection->regions);
3433 RegionSelectionByPosition cmp;
3436 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
3438 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
3444 boost::shared_ptr<Playlist> playlist;
3446 if ((playlist = atv->playlist()) == 0) {
3450 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
3455 if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) {
3459 if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) {
3465 playlist->shuffle ((*i)->region(), dir);
3467 drag_info.grab_x = drag_info.current_pointer_x;
3472 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3477 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3481 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
3482 nframes64_t pending_region_position = 0;
3483 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
3484 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
3485 bool clamp_y_axis = false;
3486 vector<int32_t> height_list(512) ;
3487 vector<int32_t>::iterator j;
3488 RouteTimeAxisView* tv;
3490 possibly_copy_regions_during_grab (event);
3492 if (!check_region_drag_possible (&tv)) {
3496 original_pointer_order = drag_info.dest_trackview->order;
3498 /************************************************************
3500 ************************************************************/
3502 if (drag_info.brushing) {
3503 clamp_y_axis = true;
3508 if ((pointer_y_span = (drag_info.dest_trackview->order - tv->order)) != 0) {
3510 int32_t children = 0, numtracks = 0;
3511 // XXX hard coding track limit, oh my, so very very bad
3512 bitset <1024> tracks (0x00);
3513 /* get a bitmask representing the visible tracks */
3515 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3516 TimeAxisView *tracklist_timeview;
3517 tracklist_timeview = (*i);
3518 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
3519 TimeAxisView::Children children_list;
3521 /* zeroes are audio tracks. ones are other types. */
3523 if (!rtv2->hidden()) {
3525 if (visible_y_high < rtv2->order) {
3526 visible_y_high = rtv2->order;
3528 if (visible_y_low > rtv2->order) {
3529 visible_y_low = rtv2->order;
3532 if (!rtv2->is_track()) {
3533 tracks = tracks |= (0x01 << rtv2->order);
3536 height_list[rtv2->order] = (*i)->current_height();
3539 if ((children_list = rtv2->get_child_list()).size() > 0) {
3540 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
3541 tracks = tracks |= (0x01 << (rtv2->order + children));
3542 height_list[rtv2->order + children] = (*j)->current_height();
3550 /* find the actual span according to the canvas */
3552 canvas_pointer_y_span = pointer_y_span;
3553 if (drag_info.dest_trackview->order >= tv->order) {
3555 for (y = tv->order; y < drag_info.dest_trackview->order; y++) {
3556 if (height_list[y] == 0 ) {
3557 canvas_pointer_y_span--;
3562 for (y = drag_info.dest_trackview->order;y <= tv->order; y++) {
3563 if ( height_list[y] == 0 ) {
3564 canvas_pointer_y_span++;
3569 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3570 RegionView* rv2 = (*i);
3571 double ix1, ix2, iy1, iy2;
3574 if (rv2->region()->locked()) {
3578 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3579 rv2->get_canvas_group()->i2w (ix1, iy1);
3580 iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
3582 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3583 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3585 if (rtv2->order != original_pointer_order) {
3586 /* this isn't the pointer track */
3588 if (canvas_pointer_y_span > 0) {
3590 /* moving up the canvas */
3591 if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
3593 int32_t visible_tracks = 0;
3594 while (visible_tracks < canvas_pointer_y_span ) {
3597 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3598 /* we're passing through a hidden track */
3603 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3604 clamp_y_axis = true;
3608 clamp_y_axis = true;
3611 } else if (canvas_pointer_y_span < 0) {
3613 /*moving down the canvas*/
3615 if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3618 int32_t visible_tracks = 0;
3620 while (visible_tracks > canvas_pointer_y_span ) {
3623 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3627 if ( tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3628 clamp_y_axis = true;
3633 clamp_y_axis = true;
3639 /* this is the pointer's track */
3640 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3641 clamp_y_axis = true;
3642 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3643 clamp_y_axis = true;
3651 } else if (drag_info.dest_trackview == tv) {
3652 clamp_y_axis = true;
3656 if (!clamp_y_axis) {
3657 drag_info.dest_trackview = tv;
3660 /************************************************************
3662 ************************************************************/
3664 /* compute the amount of pointer motion in frames, and where
3665 the region would be if we moved it by that much.
3667 if ( drag_info.move_threshold_passed ) {
3669 if (drag_info.current_pointer_frame >= drag_info.pointer_frame_offset) {
3671 nframes64_t sync_frame;
3672 nframes64_t sync_offset;
3675 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3677 sync_offset = rv->region()->sync_offset (sync_dir);
3679 /* we don't handle a sync point that lies before zero.
3681 if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) {
3682 sync_frame = pending_region_position + (sync_dir*sync_offset);
3684 /* we snap if the snap modifier is not enabled.
3687 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3688 snap_to (sync_frame);
3691 pending_region_position = rv->region()->adjust_to_sync (sync_frame);
3694 pending_region_position = drag_info.last_frame_position;
3698 pending_region_position = 0;
3701 if (pending_region_position > max_frames - rv->region()->length()) {
3702 pending_region_position = drag_info.last_frame_position;
3705 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3707 bool x_move_allowed;
3709 if (Config->get_edit_mode() == Lock) {
3710 if (drag_info.copy) {
3711 x_move_allowed = !drag_info.x_constrained;
3713 /* in locked edit mode, reverse the usual meaning of x_constrained */
3714 x_move_allowed = drag_info.x_constrained;
3717 x_move_allowed = !drag_info.x_constrained;
3720 if (( pending_region_position != drag_info.last_frame_position) && x_move_allowed ) {
3722 /* now compute the canvas unit distance we need to move the regionview
3723 to make it appear at the new location.
3726 if (pending_region_position > drag_info.last_frame_position) {
3727 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3729 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3730 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3732 RegionView* rv2 = (*i);
3734 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3736 double ix1, ix2, iy1, iy2;
3737 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3738 rv2->get_canvas_group()->i2w (ix1, iy1);
3740 if (-x_delta > ix1 + horizontal_adjustment.get_value()) {
3742 cerr << "illegal move" << endl;
3744 pending_region_position = drag_info.last_frame_position;
3751 drag_info.last_frame_position = pending_region_position;
3758 /* threshold not passed */
3763 /*************************************************************
3765 ************************************************************/
3767 if (x_delta == 0 && (pointer_y_span == 0)) {
3768 /* haven't reached next snap point, and we're not switching
3769 trackviews. nothing to do.
3774 /*************************************************************
3776 ************************************************************/
3777 bool do_move = true;
3778 if (drag_info.first_move) {
3779 if (!drag_info.move_threshold_passed) {
3786 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3787 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3789 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3791 RegionView* rv = (*i);
3792 double ix1, ix2, iy1, iy2;
3793 int32_t temp_pointer_y_span = pointer_y_span;
3795 if (rv->region()->locked()) {
3799 /* get item BBox, which will be relative to parent. so we have
3800 to query on a child, then convert to world coordinates using
3804 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3805 rv->get_canvas_group()->i2w (ix1, iy1);
3807 if (drag_info.first_move) {
3809 // hide any dependent views
3811 rv->get_time_axis_view().hide_dependent_views (*rv);
3814 reparent to a non scrolling group so that we can keep the
3815 region selection above all time axis views.
3816 reparenting means we have to move the rv as the two
3817 parent groups have different coordinates.
3820 rv->get_canvas_group()->property_y() = iy1 - 1;
3821 rv->get_canvas_group()->reparent(*_region_motion_group);
3823 rv->fake_set_opaque (true);
3825 /* for evaluation of the track position of iy1, we have to adjust
3826 to allow for the vertical scrolling adjustment and the height of the timebars.
3829 cerr << "adjust y from " << iy1 << " using "
3830 << vertical_adjustment.get_value() << " - "
3831 << canvas_timebars_vsize
3834 iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
3836 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3837 RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3838 RouteTimeAxisView* temp_rtv;
3840 if ((pointer_y_span != 0) && !clamp_y_axis) {
3843 for (j = height_list.begin(); j!= height_list.end(); j++) {
3844 if (x == canvas_rtv->order) {
3845 /* we found the track the region is on */
3846 if (x != original_pointer_order) {
3847 /*this isn't from the same track we're dragging from */
3848 temp_pointer_y_span = canvas_pointer_y_span;
3850 while (temp_pointer_y_span > 0) {
3851 /* we're moving up canvas-wise,
3852 so we need to find the next track height
3854 if (j != height_list.begin()) {
3857 if (x != original_pointer_order) {
3858 /* we're not from the dragged track, so ignore hidden tracks. */
3860 temp_pointer_y_span++;
3864 temp_pointer_y_span--;
3867 while (temp_pointer_y_span < 0) {
3869 if (x != original_pointer_order) {
3871 temp_pointer_y_span--;
3875 if (j != height_list.end()) {
3878 temp_pointer_y_span++;
3880 /* find out where we'll be when we move and set height accordingly */
3882 tvp2 = trackview_by_y_position (iy1 + y_delta);
3883 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3884 rv->set_y_position_and_height (0, temp_rtv->current_height());
3886 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3887 personally, i think this can confuse things, but never mind.
3890 //const GdkColor& col (temp_rtv->view->get_region_color());
3891 //rv->set_color (const_cast<GdkColor&>(col));
3898 /* prevent the regionview from being moved to before
3899 the zero position on the canvas.
3904 if (-x_delta > ix1) {
3907 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3908 x_delta = max_frames - rv->region()->last_frame();
3911 if (drag_info.brushing) {
3912 mouse_brush_insert_region (rv, pending_region_position);
3914 rv->move (x_delta, y_delta);
3917 } /* foreach region */
3921 if (drag_info.first_move && drag_info.move_threshold_passed) {
3922 cursor_group->raise_to_top();
3923 drag_info.first_move = false;
3926 if (x_delta != 0 && !drag_info.brushing) {
3927 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3932 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3934 bool nocommit = true;
3935 vector<RegionView*> copies;
3936 RouteTimeAxisView* source_tv;
3937 boost::shared_ptr<Diskstream> ds;
3938 boost::shared_ptr<Playlist> from_playlist;
3939 vector<RegionView*> new_selection;
3940 typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
3941 PlaylistSet modified_playlists;
3942 PlaylistSet frozen_playlists;
3943 list <sigc::connection> modified_playlist_connections;
3944 pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
3946 /* first_move is set to false if the regionview has been moved in the
3950 if (drag_info.first_move) {
3957 /* XXX is this true??? i can''t tell the difference.
3958 The regionview has been moved at some stage during the grab so we need
3959 to account for any mouse movement between this event and the last one.
3962 //region_drag_motion_callback (item, event);
3964 if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
3965 selection->set (pre_drag_region_selection);
3966 pre_drag_region_selection.clear ();
3969 if (drag_info.brushing) {
3970 /* all changes were made during motion event handlers */
3972 if (drag_info.copy) {
3973 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3974 copies.push_back (*i);
3983 /* reverse this here so that we have the correct logic to finalize
3987 if (Config->get_edit_mode() == Lock && !drag_info.copy) {
3988 drag_info.x_constrained = !drag_info.x_constrained;
3991 if (drag_info.copy) {
3992 if (drag_info.x_constrained) {
3993 op_string = _("fixed time region copy");
3995 op_string = _("region copy");
3998 if (drag_info.x_constrained) {
3999 op_string = _("fixed time region drag");
4001 op_string = _("region drag");
4005 begin_reversible_command (op_string);
4007 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
4009 RegionView* rv = (*i);
4010 double ix1, ix2, iy1, iy2;
4011 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
4012 rv->get_canvas_group()->i2w (ix1, iy1);
4013 iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
4015 TimeAxisView* dest_tv = trackview_by_y_position (iy1);
4016 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*>(dest_tv);
4018 bool changed_tracks, changed_position;
4021 if (rv->region()->locked()) {
4026 /* adjust for track speed */
4030 if (dest_rtv && dest_rtv->get_diskstream()) {
4031 speed = dest_rtv->get_diskstream()->speed();
4034 changed_position = (drag_info.last_frame_position != (nframes64_t) (rv->region()->position()/speed));
4035 changed_tracks = (dest_tv != &rv->get_time_axis_view());
4037 if (changed_position && !drag_info.x_constrained) {
4038 _master_group->w2i(ix1, iy1);
4039 where = (nframes64_t) (unit_to_frame (ix1) * speed);
4041 where = rv->region()->position();
4044 boost::shared_ptr<Region> new_region;
4047 if (drag_info.copy) {
4048 /* we already made a copy */
4049 new_region = rv->region();
4051 /* undo the previous hide_dependent_views so that xfades don't
4052 disappear on copying regions
4055 //rv->get_time_axis_view().reveal_dependent_views (*rv);
4057 } else if (changed_tracks && dest_rtv->playlist()) {
4058 new_region = RegionFactory::create (rv->region());
4061 if (changed_tracks || drag_info.copy) {
4063 boost::shared_ptr<Playlist> to_playlist = dest_rtv->playlist();
4069 latest_regionviews.clear ();
4071 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4073 insert_result = modified_playlists.insert (to_playlist);
4074 if (insert_result.second) {
4075 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
4078 to_playlist->add_region (new_region, where);
4082 if (!latest_regionviews.empty()) {
4083 // XXX why just the first one ? we only expect one
4084 // commented out in nick_m's canvas reworking. is that intended?
4085 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
4086 new_selection.push_back (latest_regionviews.front());
4091 motion on the same track. plonk the previously reparented region
4092 back to its original canvas group (its streamview).
4093 No need to do anything for copies as they are fake regions which will be deleted.
4096 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
4097 rv->get_canvas_group()->property_y() = 0;
4099 /* just change the model */
4101 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
4103 insert_result = modified_playlists.insert (playlist);
4104 if (insert_result.second) {
4105 session->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
4107 /* freeze to avoid lots of relayering in the case of a multi-region drag */
4108 frozen_insert_result = frozen_playlists.insert(playlist);
4109 if (frozen_insert_result.second) {
4113 rv->region()->set_position (where, (void*) this);
4116 if (changed_tracks && !drag_info.copy) {
4118 /* get the playlist where this drag started. we can't use rv->region()->playlist()
4119 because we may have copied the region and it has not been attached to a playlist.
4122 assert ((source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view())));
4123 assert ((ds = source_tv->get_diskstream()));
4124 assert ((from_playlist = ds->playlist()));
4126 /* moved to a different audio track, without copying */
4128 /* the region that used to be in the old playlist is not
4129 moved to the new one - we use a copy of it. as a result,
4130 any existing editor for the region should no longer be
4134 rv->hide_region_editor();
4135 rv->fake_set_opaque (false);
4137 /* remove the region from the old playlist */
4139 insert_result = modified_playlists.insert (from_playlist);
4140 if (insert_result.second) {
4141 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
4144 from_playlist->remove_region ((rv->region()));
4146 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
4147 was selected in all of them, then removing it from a playlist will have removed all
4148 trace of it from the selection (i.e. there were N regions selected, we removed 1,
4149 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
4150 corresponding regionview, and the selection is now empty).
4152 this could have invalidated any and all iterators into the region selection.
4154 the heuristic we use here is: if the region selection is empty, break out of the loop
4155 here. if the region selection is not empty, then restart the loop because we know that
4156 we must have removed at least the region(view) we've just been working on as well as any
4157 that we processed on previous iterations.
4159 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
4160 we can just iterate.
4163 if (selection->regions.empty()) {
4166 i = selection->regions.by_layer().begin();
4173 if (drag_info.copy) {
4174 copies.push_back (rv);
4178 if (new_selection.empty()) {
4179 if (drag_info.copy) {
4180 /* the region(view)s that are selected and being dragged around
4181 are copies and do not belong to any track. remove them
4182 from the selection right here.
4184 selection->clear_regions();
4187 /* this will clear any existing selection that would have been
4188 cleared in the other clause above
4190 selection->set (new_selection);
4193 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
4199 for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
4200 session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));
4202 commit_reversible_command ();
4205 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
4212 Editor::create_region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4214 if (drag_info.move_threshold_passed) {
4215 if (drag_info.first_move) {
4216 // TODO: create region-create-drag region view here
4217 drag_info.first_move = false;
4220 // TODO: resize region-create-drag region view here
4225 Editor::create_region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4227 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (drag_info.dest_trackview);
4231 const boost::shared_ptr<MidiDiskstream> diskstream =
4232 boost::dynamic_pointer_cast<MidiDiskstream>(mtv->view()->trackview().track()->diskstream());
4235 warning << "Cannot create non-MIDI region" << endl;
4239 if (drag_info.first_move) {
4240 begin_reversible_command (_("create region"));
4241 XMLNode &before = mtv->playlist()->get_state();
4243 nframes64_t start = drag_info.grab_frame;
4244 snap_to (start, -1);
4245 const Meter& m = session->tempo_map().meter_at(start);
4246 const Tempo& t = session->tempo_map().tempo_at(start);
4247 double length = floor (m.frames_per_bar(t, session->frame_rate()));
4249 boost::shared_ptr<Source> src = session->create_midi_source_for_session(*diskstream.get());
4251 mtv->playlist()->add_region (boost::dynamic_pointer_cast<MidiRegion>
4252 (RegionFactory::create(src, 0, (nframes_t) length,
4253 PBD::basename_nosuffix(src->name()))), start);
4254 XMLNode &after = mtv->playlist()->get_state();
4255 session->add_command(new MementoCommand<Playlist>(*mtv->playlist().get(), &before, &after));
4256 commit_reversible_command();
4259 create_region_drag_motion_callback (item, event);
4260 // TODO: create region-create-drag region here
4265 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
4267 /* Either add to or set the set the region selection, unless
4268 this is an alignment click (control used)
4271 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
4272 TimeAxisView* tv = &rv.get_time_axis_view();
4273 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
4275 if (rtv && rtv->is_track()) {
4276 speed = rtv->get_diskstream()->speed();
4279 nframes64_t where = get_preferred_edit_position();
4283 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
4285 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
4287 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
4289 align_region (rv.region(), End, (nframes64_t) (where * speed));
4293 align_region (rv.region(), Start, (nframes64_t) (where * speed));
4300 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
4306 nframes64_t frame_rate;
4315 if (Profile->get_sae() || Profile->get_small_screen()) {
4316 m = ARDOUR_UI::instance()->primary_clock.mode();
4318 m = ARDOUR_UI::instance()->secondary_clock.mode();
4322 case AudioClock::BBT:
4323 session->bbt_time (frame, bbt);
4324 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
4327 case AudioClock::SMPTE:
4328 session->smpte_time (frame, smpte);
4329 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4332 case AudioClock::MinSec:
4333 /* XXX this is copied from show_verbose_duration_cursor() */
4334 frame_rate = session->frame_rate();
4335 hours = frame / (frame_rate * 3600);
4336 frame = frame % (frame_rate * 3600);
4337 mins = frame / (frame_rate * 60);
4338 frame = frame % (frame_rate * 60);
4339 secs = (float) frame / (float) frame_rate;
4340 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4344 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
4348 if (xpos >= 0 && ypos >=0) {
4349 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4352 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);
4354 show_verbose_canvas_cursor ();
4358 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
4365 nframes64_t distance, frame_rate;
4367 Meter meter_at_start(session->tempo_map().meter_at(start));
4375 if (Profile->get_sae() || Profile->get_small_screen()) {
4376 m = ARDOUR_UI::instance()->primary_clock.mode ();
4378 m = ARDOUR_UI::instance()->secondary_clock.mode ();
4382 case AudioClock::BBT:
4383 session->bbt_time (start, sbbt);
4384 session->bbt_time (end, ebbt);
4387 /* XXX this computation won't work well if the
4388 user makes a selection that spans any meter changes.
4391 ebbt.bars -= sbbt.bars;
4392 if (ebbt.beats >= sbbt.beats) {
4393 ebbt.beats -= sbbt.beats;
4396 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
4398 if (ebbt.ticks >= sbbt.ticks) {
4399 ebbt.ticks -= sbbt.ticks;
4402 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
4405 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
4408 case AudioClock::SMPTE:
4409 session->smpte_duration (end - start, smpte);
4410 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4413 case AudioClock::MinSec:
4414 /* XXX this stuff should be elsewhere.. */
4415 distance = end - start;
4416 frame_rate = session->frame_rate();
4417 hours = distance / (frame_rate * 3600);
4418 distance = distance % (frame_rate * 3600);
4419 mins = distance / (frame_rate * 60);
4420 distance = distance % (frame_rate * 60);
4421 secs = (float) distance / (float) frame_rate;
4422 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4426 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
4430 if (xpos >= 0 && ypos >=0) {
4431 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4434 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4437 show_verbose_canvas_cursor ();
4441 Editor::collect_new_region_view (RegionView* rv)
4443 latest_regionviews.push_back (rv);
4447 Editor::collect_and_select_new_region_view (RegionView* rv)
4450 latest_regionviews.push_back (rv);
4454 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
4456 if (clicked_regionview == 0) {
4460 /* lets try to create new Region for the selection */
4462 vector<boost::shared_ptr<Region> > new_regions;
4463 create_region_from_selection (new_regions);
4465 if (new_regions.empty()) {
4469 /* XXX fix me one day to use all new regions */
4471 boost::shared_ptr<Region> region (new_regions.front());
4473 /* add it to the current stream/playlist.
4475 tricky: the streamview for the track will add a new regionview. we will
4476 catch the signal it sends when it creates the regionview to
4477 set the regionview we want to then drag.
4480 latest_regionviews.clear();
4481 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4483 /* A selection grab currently creates two undo/redo operations, one for
4484 creating the new region and another for moving it.
4487 begin_reversible_command (_("selection grab"));
4489 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
4491 XMLNode *before = &(playlist->get_state());
4492 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
4493 XMLNode *after = &(playlist->get_state());
4494 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4496 commit_reversible_command ();
4500 if (latest_regionviews.empty()) {
4501 /* something went wrong */
4505 /* we need to deselect all other regionviews, and select this one
4506 i'm ignoring undo stuff, because the region creation will take care of it
4508 selection->set (latest_regionviews);
4510 drag_info.item = latest_regionviews.front()->get_canvas_group();
4511 drag_info.data = latest_regionviews.front();
4512 drag_info.motion_callback = &Editor::region_drag_motion_callback;
4513 drag_info.finished_callback = &Editor::region_drag_finished_callback;
4517 drag_info.source_trackview = clicked_routeview;
4518 drag_info.dest_trackview = drag_info.source_trackview;
4519 drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4520 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4522 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4526 Editor::cancel_selection ()
4528 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4529 (*i)->hide_selection ();
4531 selection->clear ();
4532 clicked_selection = 0;
4536 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4538 nframes64_t start = 0;
4539 nframes64_t end = 0;
4545 drag_info.item = item;
4546 drag_info.motion_callback = &Editor::drag_selection;
4547 drag_info.finished_callback = &Editor::end_selection_op;
4552 case CreateSelection:
4553 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4554 drag_info.copy = true;
4556 drag_info.copy = false;
4558 start_grab (event, selector_cursor);
4561 case SelectionStartTrim:
4562 if (clicked_axisview) {
4563 clicked_axisview->order_selection_trims (item, true);
4565 start_grab (event, trimmer_cursor);
4566 start = selection->time[clicked_selection].start;
4567 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4570 case SelectionEndTrim:
4571 if (clicked_axisview) {
4572 clicked_axisview->order_selection_trims (item, false);
4574 start_grab (event, trimmer_cursor);
4575 end = selection->time[clicked_selection].end;
4576 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
4580 start = selection->time[clicked_selection].start;
4582 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4586 if (selection_op == SelectionMove) {
4587 show_verbose_time_cursor(start, 10);
4589 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4594 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4596 nframes64_t start = 0;
4597 nframes64_t end = 0;
4599 nframes64_t pending_position;
4601 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4602 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4604 pending_position = 0;
4607 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4608 snap_to (pending_position);
4611 /* only alter selection if the current frame is
4612 different from the last frame position (adjusted)
4615 if (pending_position == drag_info.last_pointer_frame) return;
4617 switch (selection_op) {
4618 case CreateSelection:
4620 if (drag_info.first_move) {
4621 snap_to (drag_info.grab_frame);
4624 if (pending_position < drag_info.grab_frame) {
4625 start = pending_position;
4626 end = drag_info.grab_frame;
4628 end = pending_position;
4629 start = drag_info.grab_frame;
4632 /* first drag: Either add to the selection
4633 or create a new selection->
4636 if (drag_info.first_move) {
4638 begin_reversible_command (_("range selection"));
4640 if (drag_info.copy) {
4641 /* adding to the selection */
4642 clicked_selection = selection->add (start, end);
4643 drag_info.copy = false;
4645 /* new selection-> */
4646 clicked_selection = selection->set (clicked_axisview, start, end);
4651 case SelectionStartTrim:
4653 if (drag_info.first_move) {
4654 begin_reversible_command (_("trim selection start"));
4657 start = selection->time[clicked_selection].start;
4658 end = selection->time[clicked_selection].end;
4660 if (pending_position > end) {
4663 start = pending_position;
4667 case SelectionEndTrim:
4669 if (drag_info.first_move) {
4670 begin_reversible_command (_("trim selection end"));
4673 start = selection->time[clicked_selection].start;
4674 end = selection->time[clicked_selection].end;
4676 if (pending_position < start) {
4679 end = pending_position;
4686 if (drag_info.first_move) {
4687 begin_reversible_command (_("move selection"));
4690 start = selection->time[clicked_selection].start;
4691 end = selection->time[clicked_selection].end;
4693 length = end - start;
4695 start = pending_position;
4698 end = start + length;
4703 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4704 start_canvas_autoscroll (1, 0);
4708 selection->replace (clicked_selection, start, end);
4711 drag_info.last_pointer_frame = pending_position;
4712 drag_info.first_move = false;
4714 if (selection_op == SelectionMove) {
4715 show_verbose_time_cursor(start, 10);
4717 show_verbose_time_cursor(pending_position, 10);
4722 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4724 if (!drag_info.first_move) {
4725 drag_selection (item, event);
4726 /* XXX this is not object-oriented programming at all. ick */
4727 if (selection->time.consolidate()) {
4728 selection->TimeChanged ();
4730 commit_reversible_command ();
4732 /* just a click, no pointer movement.*/
4734 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4736 selection->clear_time();
4741 /* XXX what happens if its a music selection? */
4742 session->set_audio_range (selection->time);
4743 stop_canvas_autoscroll ();
4747 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4750 TimeAxisView* tvp = clicked_axisview;
4751 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4753 if (tv && tv->is_track()) {
4754 speed = tv->get_diskstream()->speed();
4757 nframes64_t region_start = (nframes64_t) (clicked_regionview->region()->position() / speed);
4758 nframes64_t region_end = (nframes64_t) (clicked_regionview->region()->last_frame() / speed);
4759 nframes64_t region_length = (nframes64_t) (clicked_regionview->region()->length() / speed);
4761 //drag_info.item = clicked_regionview->get_name_highlight();
4762 drag_info.item = item;
4763 drag_info.motion_callback = &Editor::trim_motion_callback;
4764 drag_info.finished_callback = &Editor::trim_finished_callback;
4766 start_grab (event, trimmer_cursor);
4768 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4769 trim_op = ContentsTrim;
4771 /* These will get overridden for a point trim.*/
4772 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4773 /* closer to start */
4774 trim_op = StartTrim;
4775 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4783 show_verbose_time_cursor(region_start, 10);
4786 show_verbose_time_cursor(region_end, 10);
4789 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4795 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4797 RegionView* rv = clicked_regionview;
4798 nframes64_t frame_delta = 0;
4799 bool left_direction;
4800 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4802 /* snap modifier works differently here..
4803 its' current state has to be passed to the
4804 various trim functions in order to work properly
4808 TimeAxisView* tvp = clicked_axisview;
4809 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4810 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4812 if (tv && tv->is_track()) {
4813 speed = tv->get_diskstream()->speed();
4816 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4817 left_direction = true;
4819 left_direction = false;
4823 snap_to (drag_info.current_pointer_frame);
4826 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4830 if (drag_info.first_move) {
4836 trim_type = "Region start trim";
4839 trim_type = "Region end trim";
4842 trim_type = "Region content trim";
4846 begin_reversible_command (trim_type);
4848 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4849 (*i)->fake_set_opaque(false);
4850 (*i)->region()->freeze ();
4852 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4854 arv->temporarily_hide_envelope ();
4856 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4857 insert_result = motion_frozen_playlists.insert (pl);
4858 if (insert_result.second) {
4859 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4865 if (left_direction) {
4866 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4868 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4873 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4876 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4877 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4883 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes64_t) (rv->region()->last_frame()/speed))) {
4886 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4887 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4894 bool swap_direction = false;
4896 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4897 swap_direction = true;
4900 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4901 i != selection->regions.by_layer().end(); ++i)
4903 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4911 show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
4914 show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
4917 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4921 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4922 drag_info.first_move = false;
4926 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4928 boost::shared_ptr<Region> region (rv.region());
4930 if (region->locked()) {
4934 nframes64_t new_bound;
4937 TimeAxisView* tvp = clicked_axisview;
4938 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4940 if (tv && tv->is_track()) {
4941 speed = tv->get_diskstream()->speed();
4944 if (left_direction) {
4945 if (swap_direction) {
4946 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4948 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4951 if (swap_direction) {
4952 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4954 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4959 snap_to (new_bound);
4961 region->trim_start ((nframes64_t) (new_bound * speed), this);
4962 rv.region_changed (StartChanged);
4966 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
4968 boost::shared_ptr<Region> region (rv.region());
4970 if (region->locked()) {
4974 nframes64_t new_bound;
4977 TimeAxisView* tvp = clicked_axisview;
4978 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4980 if (tv && tv->is_track()) {
4981 speed = tv->get_diskstream()->speed();
4984 if (left_direction) {
4985 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4987 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4991 snap_to (new_bound, (left_direction ? 0 : 1));
4994 region->trim_front ((nframes64_t) (new_bound * speed), this);
4996 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
5000 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
5002 boost::shared_ptr<Region> region (rv.region());
5004 if (region->locked()) {
5008 nframes64_t new_bound;
5011 TimeAxisView* tvp = clicked_axisview;
5012 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
5014 if (tv && tv->is_track()) {
5015 speed = tv->get_diskstream()->speed();
5018 if (left_direction) {
5019 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
5021 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
5025 snap_to (new_bound);
5027 region->trim_end ((nframes64_t) (new_bound * speed), this);
5028 rv.region_changed (LengthChanged);
5032 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
5034 if (!drag_info.first_move) {
5035 trim_motion_callback (item, event);
5037 if (!selection->selected (clicked_regionview)) {
5038 thaw_region_after_trim (*clicked_regionview);
5041 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5042 i != selection->regions.by_layer().end(); ++i)
5044 thaw_region_after_trim (**i);
5045 (*i)->fake_set_opaque (true);
5049 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
5051 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
5054 motion_frozen_playlists.clear ();
5056 commit_reversible_command();
5058 /* no mouse movement */
5064 Editor::point_trim (GdkEvent* event)
5066 RegionView* rv = clicked_regionview;
5067 nframes64_t new_bound = drag_info.current_pointer_frame;
5069 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5070 snap_to (new_bound);
5073 /* Choose action dependant on which button was pressed */
5074 switch (event->button.button) {
5076 trim_op = StartTrim;
5077 begin_reversible_command (_("Start point trim"));
5079 if (selection->selected (rv)) {
5081 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5082 i != selection->regions.by_layer().end(); ++i)
5084 if (!(*i)->region()->locked()) {
5085 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5086 XMLNode &before = pl->get_state();
5087 (*i)->region()->trim_front (new_bound, this);
5088 XMLNode &after = pl->get_state();
5089 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5095 if (!rv->region()->locked()) {
5096 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5097 XMLNode &before = pl->get_state();
5098 rv->region()->trim_front (new_bound, this);
5099 XMLNode &after = pl->get_state();
5100 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5104 commit_reversible_command();
5109 begin_reversible_command (_("End point trim"));
5111 if (selection->selected (rv)) {
5113 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
5115 if (!(*i)->region()->locked()) {
5116 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5117 XMLNode &before = pl->get_state();
5118 (*i)->region()->trim_end (new_bound, this);
5119 XMLNode &after = pl->get_state();
5120 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5126 if (!rv->region()->locked()) {
5127 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5128 XMLNode &before = pl->get_state();
5129 rv->region()->trim_end (new_bound, this);
5130 XMLNode &after = pl->get_state();
5131 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
5135 commit_reversible_command();
5144 Editor::thaw_region_after_trim (RegionView& rv)
5146 boost::shared_ptr<Region> region (rv.region());
5148 if (region->locked()) {
5152 region->thaw (_("trimmed region"));
5153 XMLNode &after = region->playlist()->get_state();
5154 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
5156 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
5158 arv->unhide_envelope ();
5162 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
5167 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
5168 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
5172 Location* location = find_location_from_marker (marker, is_start);
5173 location->set_hidden (true, this);
5178 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
5184 drag_info.item = item;
5185 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
5186 drag_info.finished_callback = &Editor::end_range_markerbar_op;
5188 range_marker_op = op;
5190 if (!temp_location) {
5191 temp_location = new Location;
5195 case CreateRangeMarker:
5196 case CreateTransportMarker:
5197 case CreateCDMarker:
5199 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
5200 drag_info.copy = true;
5202 drag_info.copy = false;
5204 start_grab (event, selector_cursor);
5208 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5213 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5215 nframes64_t start = 0;
5216 nframes64_t end = 0;
5217 ArdourCanvas::SimpleRect *crect;
5219 switch (range_marker_op) {
5220 case CreateRangeMarker:
5221 crect = range_bar_drag_rect;
5223 case CreateTransportMarker:
5224 crect = transport_bar_drag_rect;
5226 case CreateCDMarker:
5227 crect = cd_marker_bar_drag_rect;
5230 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
5235 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5236 snap_to (drag_info.current_pointer_frame);
5239 /* only alter selection if the current frame is
5240 different from the last frame position.
5243 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5245 switch (range_marker_op) {
5246 case CreateRangeMarker:
5247 case CreateTransportMarker:
5248 case CreateCDMarker:
5249 if (drag_info.first_move) {
5250 snap_to (drag_info.grab_frame);
5253 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5254 start = drag_info.current_pointer_frame;
5255 end = drag_info.grab_frame;
5257 end = drag_info.current_pointer_frame;
5258 start = drag_info.grab_frame;
5261 /* first drag: Either add to the selection
5262 or create a new selection.
5265 if (drag_info.first_move) {
5267 temp_location->set (start, end);
5271 update_marker_drag_item (temp_location);
5272 range_marker_drag_rect->show();
5273 //range_marker_drag_rect->raise_to_top();
5279 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
5280 start_canvas_autoscroll (1, 0);
5284 temp_location->set (start, end);
5286 double x1 = frame_to_pixel (start);
5287 double x2 = frame_to_pixel (end);
5288 crect->property_x1() = x1;
5289 crect->property_x2() = x2;
5291 update_marker_drag_item (temp_location);
5294 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5295 drag_info.first_move = false;
5297 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5302 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5304 Location * newloc = 0;
5308 if (!drag_info.first_move) {
5309 drag_range_markerbar_op (item, event);
5311 switch (range_marker_op) {
5312 case CreateRangeMarker:
5313 case CreateCDMarker:
5315 begin_reversible_command (_("new range marker"));
5316 XMLNode &before = session->locations()->get_state();
5317 session->locations()->next_available_name(rangename,"unnamed");
5318 if (range_marker_op == CreateCDMarker) {
5319 flags = Location::IsRangeMarker|Location::IsCDMarker;
5320 cd_marker_bar_drag_rect->hide();
5323 flags = Location::IsRangeMarker;
5324 range_bar_drag_rect->hide();
5326 newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
5327 session->locations()->add (newloc, true);
5328 XMLNode &after = session->locations()->get_state();
5329 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
5330 commit_reversible_command ();
5332 range_marker_drag_rect->hide();
5336 case CreateTransportMarker:
5337 // popup menu to pick loop or punch
5338 new_transport_marker_context_menu (&event->button, item);
5343 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5345 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
5350 start = session->locations()->first_mark_before (drag_info.grab_frame);
5351 end = session->locations()->first_mark_after (drag_info.grab_frame);
5353 if (end == max_frames) {
5354 end = session->current_end_frame ();
5358 start = session->current_start_frame ();
5361 switch (mouse_mode) {
5363 /* find the two markers on either side and then make the selection from it */
5364 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
5368 /* find the two markers on either side of the click and make the range out of it */
5369 selection->set (0, start, end);
5378 stop_canvas_autoscroll ();
5384 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5386 drag_info.item = item;
5387 drag_info.motion_callback = &Editor::drag_mouse_zoom;
5388 drag_info.finished_callback = &Editor::end_mouse_zoom;
5390 start_grab (event, zoom_cursor);
5392 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5396 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5401 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5402 snap_to (drag_info.current_pointer_frame);
5404 if (drag_info.first_move) {
5405 snap_to (drag_info.grab_frame);
5409 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5411 /* base start and end on initial click position */
5412 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5413 start = drag_info.current_pointer_frame;
5414 end = drag_info.grab_frame;
5416 end = drag_info.current_pointer_frame;
5417 start = drag_info.grab_frame;
5422 if (drag_info.first_move) {
5424 zoom_rect->raise_to_top();
5427 reposition_zoom_rect(start, end);
5429 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5430 drag_info.first_move = false;
5432 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5437 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5439 if (!drag_info.first_move) {
5440 drag_mouse_zoom (item, event);
5442 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5443 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
5445 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
5448 temporal_zoom_to_frame (false, drag_info.grab_frame);
5450 temporal_zoom_step (false);
5451 center_screen (drag_info.grab_frame);
5459 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
5461 double x1 = frame_to_pixel (start);
5462 double x2 = frame_to_pixel (end);
5463 double y2 = full_canvas_height - 1.0;
5465 zoom_rect->property_x1() = x1;
5466 zoom_rect->property_y1() = 1.0;
5467 zoom_rect->property_x2() = x2;
5468 zoom_rect->property_y2() = y2;
5472 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5474 drag_info.item = item;
5475 drag_info.motion_callback = &Editor::drag_rubberband_select;
5476 drag_info.finished_callback = &Editor::end_rubberband_select;
5478 start_grab (event, cross_hair_cursor);
5480 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5484 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5491 /* use a bigger drag threshold than the default */
5493 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
5497 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) {
5498 if (drag_info.first_move) {
5499 snap_to (drag_info.grab_frame);
5501 snap_to (drag_info.current_pointer_frame);
5504 /* base start and end on initial click position */
5506 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5507 start = drag_info.current_pointer_frame;
5508 end = drag_info.grab_frame;
5510 end = drag_info.current_pointer_frame;
5511 start = drag_info.grab_frame;
5514 if (drag_info.current_pointer_y < drag_info.grab_y) {
5515 y1 = drag_info.current_pointer_y;
5516 y2 = drag_info.grab_y;
5518 y2 = drag_info.current_pointer_y;
5519 y1 = drag_info.grab_y;
5523 if (start != end || y1 != y2) {
5525 double x1 = frame_to_pixel (start);
5526 double x2 = frame_to_pixel (end);
5528 rubberband_rect->property_x1() = x1;
5529 rubberband_rect->property_y1() = y1;
5530 rubberband_rect->property_x2() = x2;
5531 rubberband_rect->property_y2() = y2;
5533 rubberband_rect->show();
5534 rubberband_rect->raise_to_top();
5536 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5537 drag_info.first_move = false;
5539 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5544 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5546 if (!drag_info.first_move) {
5548 drag_rubberband_select (item, event);
5551 if (drag_info.current_pointer_y < drag_info.grab_y) {
5552 y1 = drag_info.current_pointer_y;
5553 y2 = drag_info.grab_y;
5555 y2 = drag_info.current_pointer_y;
5556 y1 = drag_info.grab_y;
5560 Selection::Operation op = Keyboard::selection_type (event->button.state);
5563 begin_reversible_command (_("rubberband selection"));
5565 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5566 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5568 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5572 commit_reversible_command ();
5576 selection->clear_tracks();
5577 selection->clear_regions();
5578 selection->clear_points ();
5579 selection->clear_lines ();
5582 rubberband_rect->hide();
5587 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5589 using namespace Gtkmm2ext;
5591 ArdourPrompter prompter (false);
5593 prompter.set_prompt (_("Name for region:"));
5594 prompter.set_initial_text (clicked_regionview->region()->name());
5595 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5596 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5597 prompter.show_all ();
5598 switch (prompter.run ()) {
5599 case Gtk::RESPONSE_ACCEPT:
5601 prompter.get_result(str);
5603 clicked_regionview->region()->set_name (str);
5611 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5613 drag_info.item = item;
5614 drag_info.motion_callback = &Editor::time_fx_motion;
5615 drag_info.finished_callback = &Editor::end_time_fx;
5619 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5623 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5625 RegionView* rv = clicked_regionview;
5627 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5628 snap_to (drag_info.current_pointer_frame);
5631 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5635 if (drag_info.current_pointer_frame > rv->region()->position()) {
5636 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5639 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5640 drag_info.first_move = false;
5642 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5646 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5648 clicked_regionview->get_time_axis_view().hide_timestretch ();
5650 if (drag_info.first_move) {
5654 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5655 /* backwards drag of the left edge - not usable */
5659 nframes64_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5661 float percentage = (double) newlen / (double) clicked_regionview->region()->length();
5663 #ifndef USE_RUBBERBAND
5664 // Soundtouch uses percentage / 100 instead of normal (/ 1)
5665 if (clicked_regionview->region()->data_type() == DataType::AUDIO) {
5666 percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5670 begin_reversible_command (_("timestretch"));
5672 // XXX how do timeFX on multiple regions ?
5675 rs.add (clicked_regionview);
5677 if (time_stretch (rs, percentage) == 0) {
5678 session->commit_reversible_command ();
5683 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
5685 /* no brushing without a useful snap setting */
5687 switch (snap_mode) {
5689 return; /* can't work because it allows region to be placed anywhere */
5694 switch (snap_type) {
5702 /* don't brush a copy over the original */
5704 if (pos == rv->region()->position()) {
5708 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
5710 if (rtv == 0 || !rtv->is_track()) {
5714 boost::shared_ptr<Playlist> playlist = rtv->playlist();
5715 double speed = rtv->get_diskstream()->speed();
5717 XMLNode &before = playlist->get_state();
5718 playlist->add_region (RegionFactory::create (rv->region()), (nframes64_t) (pos * speed));
5719 XMLNode &after = playlist->get_state();
5720 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5722 // playlist is frozen, so we have to update manually
5724 playlist->Modified(); /* EMIT SIGNAL */
5728 Editor::track_height_step_timeout ()
5730 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
5731 current_stepping_trackview = 0;