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.
30 #include <pbd/error.h>
31 #include <gtkmm2ext/utils.h>
32 #include <pbd/memento_command.h>
34 #include "ardour_ui.h"
36 #include "time_axis_view.h"
37 #include "audio_time_axis.h"
38 #include "audio_region_view.h"
40 #include "streamview.h"
41 #include "region_gain_line.h"
42 #include "automation_time_axis.h"
45 #include "selection.h"
48 #include "rgb_macros.h"
50 #include <ardour/types.h>
51 #include <ardour/profile.h>
52 #include <ardour/route.h>
53 #include <ardour/audio_track.h>
54 #include <ardour/audio_diskstream.h>
55 #include <ardour/playlist.h>
56 #include <ardour/audioplaylist.h>
57 #include <ardour/audioregion.h>
58 #include <ardour/dB.h>
59 #include <ardour/utils.h>
60 #include <ardour/region_factory.h>
67 using namespace ARDOUR;
71 using namespace Editing;
74 Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
78 Gdk::ModifierType mask;
79 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
80 Glib::RefPtr<const Gdk::Window> pointer_window;
86 pointer_window = canvas_window->get_pointer (x, y, mask);
88 if (pointer_window == track_canvas->get_bin_window()) {
91 in_track_canvas = true;
94 in_track_canvas = false;
99 event.type = GDK_BUTTON_RELEASE;
103 where = event_frame (&event, 0, 0);
108 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy) const
122 switch (event->type) {
123 case GDK_BUTTON_RELEASE:
124 case GDK_BUTTON_PRESS:
125 case GDK_2BUTTON_PRESS:
126 case GDK_3BUTTON_PRESS:
128 *pcx = event->button.x;
129 *pcy = event->button.y;
130 _trackview_group->w2i(*pcx, *pcy);
132 case GDK_MOTION_NOTIFY:
134 *pcx = event->motion.x;
135 *pcy = event->motion.y;
136 _trackview_group->w2i(*pcx, *pcy);
138 case GDK_ENTER_NOTIFY:
139 case GDK_LEAVE_NOTIFY:
140 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
143 case GDK_KEY_RELEASE:
144 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
147 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
151 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
152 position is negative (as can be the case with motion events in particular),
153 the frame location is always positive.
156 return pixel_to_frame (*pcx);
160 Editor::mouse_mode_toggled (MouseMode m)
162 if (ignore_mouse_mode_toggle) {
168 if (mouse_select_button.get_active()) {
174 if (mouse_move_button.get_active()) {
180 if (mouse_gain_button.get_active()) {
186 if (mouse_zoom_button.get_active()) {
192 if (mouse_timefx_button.get_active()) {
198 if (mouse_audition_button.get_active()) {
209 Editor::which_grabber_cursor ()
211 switch (_edit_point) {
213 return grabber_edit_point_cursor;
218 return grabber_cursor;
222 Editor::set_canvas_cursor ()
224 switch (mouse_mode) {
226 current_canvas_cursor = selector_cursor;
230 current_canvas_cursor = which_grabber_cursor();
234 current_canvas_cursor = cross_hair_cursor;
238 current_canvas_cursor = zoom_cursor;
242 current_canvas_cursor = time_fx_cursor; // just use playhead
246 current_canvas_cursor = speaker_cursor;
251 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
256 Editor::set_mouse_mode (MouseMode m, bool force)
258 if (drag_info.item) {
262 if (!force && m == mouse_mode) {
270 if (mouse_mode != MouseRange) {
272 /* in all modes except range, hide the range selection,
273 show the object (region) selection.
276 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
277 (*i)->set_should_show_selection (true);
279 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
280 (*i)->hide_selection ();
286 in range mode,show the range selection.
289 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
290 if ((*i)->get_selected()) {
291 (*i)->show_selection (selection->time);
296 /* XXX the hack of unsetting all other buttons should go
297 away once GTK2 allows us to use regular radio buttons drawn like
298 normal buttons, rather than my silly GroupedButton hack.
301 ignore_mouse_mode_toggle = true;
303 switch (mouse_mode) {
305 mouse_select_button.set_active (true);
309 mouse_move_button.set_active (true);
313 mouse_gain_button.set_active (true);
317 mouse_zoom_button.set_active (true);
321 mouse_timefx_button.set_active (true);
325 mouse_audition_button.set_active (true);
329 ignore_mouse_mode_toggle = false;
331 set_canvas_cursor ();
335 Editor::step_mouse_mode (bool next)
337 switch (current_mouse_mode()) {
339 if (next) set_mouse_mode (MouseRange);
340 else set_mouse_mode (MouseTimeFX);
344 if (next) set_mouse_mode (MouseZoom);
345 else set_mouse_mode (MouseObject);
349 if (next) set_mouse_mode (MouseGain);
350 else set_mouse_mode (MouseRange);
354 if (next) set_mouse_mode (MouseTimeFX);
355 else set_mouse_mode (MouseZoom);
359 if (next) set_mouse_mode (MouseAudition);
360 else set_mouse_mode (MouseGain);
364 if (next) set_mouse_mode (MouseObject);
365 else set_mouse_mode (MouseTimeFX);
371 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
373 /* in object/audition/timefx mode, any button press sets
374 the selection if the object can be selected. this is a
375 bit of hack, because we want to avoid this if the
376 mouse operation is a region alignment.
378 note: not dbl-click or triple-click
381 if (((mouse_mode != MouseObject) &&
382 (mouse_mode != MouseAudition || item_type != RegionItem) &&
383 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
384 (mouse_mode != MouseRange)) ||
386 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
391 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
393 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
395 /* almost no selection action on modified button-2 or button-3 events */
397 if (item_type != RegionItem && event->button.button != 2) {
403 Selection::Operation op = Keyboard::selection_type (event->button.state);
404 bool press = (event->type == GDK_BUTTON_PRESS);
406 // begin_reversible_command (_("select on click"));
410 if (mouse_mode != MouseRange) {
411 set_selected_regionview_from_click (press, op, true);
412 } else if (event->type == GDK_BUTTON_PRESS) {
413 set_selected_track_as_side_effect ();
417 case RegionViewNameHighlight:
419 if (mouse_mode != MouseRange) {
420 set_selected_regionview_from_click (press, op, true);
421 } else if (event->type == GDK_BUTTON_PRESS) {
422 set_selected_track_as_side_effect ();
426 case FadeInHandleItem:
428 case FadeOutHandleItem:
430 if (mouse_mode != MouseRange) {
431 set_selected_regionview_from_click (press, op, true);
432 } else if (event->type == GDK_BUTTON_PRESS) {
433 set_selected_track_as_side_effect ();
437 case GainAutomationControlPointItem:
438 case PanAutomationControlPointItem:
439 case RedirectAutomationControlPointItem:
440 set_selected_track_as_side_effect ();
441 if (mouse_mode != MouseRange) {
442 set_selected_control_point_from_click (op, false);
447 /* for context click or range selection, select track */
448 if (event->button.button == 3) {
449 set_selected_track_as_side_effect ();
450 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
451 set_selected_track_as_side_effect ();
455 case AutomationTrackItem:
456 set_selected_track_as_side_effect (true);
464 const static double ZERO_GAIN_FRACTION = gain_to_slider_position(dB_to_coefficient(0.0));
467 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
469 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
472 Glib::RefPtr<const Gdk::Window> pointer_window;
475 Gdk::ModifierType mask;
477 pointer_window = canvas_window->get_pointer (x, y, mask);
479 if (pointer_window == track_canvas->get_bin_window()) {
480 track_canvas->window_to_world (x, y, wx, wy);
481 allow_vertical_scroll = true;
483 allow_vertical_scroll = false;
487 track_canvas->grab_focus();
489 if (session && session->actively_recording()) {
493 button_selection (item, event, item_type);
495 if (drag_info.item == 0 &&
496 (Keyboard::is_delete_event (&event->button) ||
497 Keyboard::is_context_menu_event (&event->button) ||
498 Keyboard::is_edit_event (&event->button))) {
500 /* handled by button release */
504 switch (event->button.button) {
507 if (event->type == GDK_BUTTON_PRESS) {
509 if (drag_info.item) {
510 drag_info.item->ungrab (event->button.time);
513 /* single mouse clicks on any of these item types operate
514 independent of mouse mode, mostly because they are
515 not on the main track canvas or because we want
520 case PlayheadCursorItem:
521 start_cursor_grab (item, event);
525 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
526 hide_marker (item, event);
528 start_marker_grab (item, event);
532 case TempoMarkerItem:
533 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
534 start_tempo_marker_copy_grab (item, event);
536 start_tempo_marker_grab (item, event);
540 case MeterMarkerItem:
541 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
542 start_meter_marker_copy_grab (item, event);
544 start_meter_marker_grab (item, event);
554 case RangeMarkerBarItem:
555 start_range_markerbar_op (item, event, CreateRangeMarker);
559 case CdMarkerBarItem:
560 start_range_markerbar_op (item, event, CreateCDMarker);
564 case TransportMarkerBarItem:
565 start_range_markerbar_op (item, event, CreateTransportMarker);
574 switch (mouse_mode) {
577 case StartSelectionTrimItem:
578 start_selection_op (item, event, SelectionStartTrim);
581 case EndSelectionTrimItem:
582 start_selection_op (item, event, SelectionEndTrim);
586 if (Keyboard::modifier_state_contains
587 (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) {
588 // contains and not equals because I can't use alt as a modifier alone.
589 start_selection_grab (item, event);
590 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
591 /* grab selection for moving */
592 start_selection_op (item, event, SelectionMove);
594 /* this was debated, but decided the more common action was to
595 make a new selection */
596 start_selection_op (item, event, CreateSelection);
601 start_selection_op (item, event, CreateSelection);
607 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
608 event->type == GDK_BUTTON_PRESS) {
610 start_rubberband_select (item, event);
612 } else if (event->type == GDK_BUTTON_PRESS) {
615 case FadeInHandleItem:
616 start_fade_in_grab (item, event);
619 case FadeOutHandleItem:
620 start_fade_out_grab (item, event);
624 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
625 start_region_copy_grab (item, event);
626 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
627 start_region_brush_grab (item, event);
629 start_region_grab (item, event);
633 case RegionViewNameHighlight:
634 start_trim (item, event);
639 /* rename happens on edit clicks */
640 start_trim (clicked_regionview->get_name_highlight(), event);
644 case GainAutomationControlPointItem:
645 case PanAutomationControlPointItem:
646 case RedirectAutomationControlPointItem:
647 start_control_point_grab (item, event);
651 case GainAutomationLineItem:
652 case PanAutomationLineItem:
653 case RedirectAutomationLineItem:
654 start_line_grab_from_line (item, event);
659 case AutomationTrackItem:
660 start_rubberband_select (item, event);
663 /* <CMT Additions> */
664 case ImageFrameHandleStartItem:
665 imageframe_start_handle_op(item, event) ;
668 case ImageFrameHandleEndItem:
669 imageframe_end_handle_op(item, event) ;
672 case MarkerViewHandleStartItem:
673 markerview_item_start_handle_op(item, event) ;
676 case MarkerViewHandleEndItem:
677 markerview_item_end_handle_op(item, event) ;
680 /* </CMT Additions> */
682 /* <CMT Additions> */
684 start_markerview_grab(item, event) ;
687 start_imageframe_grab(item, event) ;
689 /* </CMT Additions> */
705 // start_line_grab_from_regionview (item, event);
708 case GainControlPointItem:
709 start_control_point_grab (item, event);
713 start_line_grab_from_line (item, event);
716 case GainAutomationControlPointItem:
717 case PanAutomationControlPointItem:
718 case RedirectAutomationControlPointItem:
719 start_control_point_grab (item, event);
730 case GainAutomationControlPointItem:
731 case PanAutomationControlPointItem:
732 case RedirectAutomationControlPointItem:
733 start_control_point_grab (item, event);
736 case GainAutomationLineItem:
737 case PanAutomationLineItem:
738 case RedirectAutomationLineItem:
739 start_line_grab_from_line (item, event);
743 // XXX need automation mode to identify which
745 // start_line_grab_from_regionview (item, event);
755 if (event->type == GDK_BUTTON_PRESS) {
756 start_mouse_zoom (item, event);
763 if (item_type == RegionItem) {
764 start_time_fx (item, event);
771 scrub_reverse_distance = 0;
772 last_scrub_x = event->button.x;
773 scrubbing_direction = 0;
774 track_canvas->get_window()->set_cursor (*transparent_cursor);
775 /* rest handled in motion & release */
784 switch (mouse_mode) {
786 if (event->type == GDK_BUTTON_PRESS) {
789 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
790 start_region_copy_grab (item, event);
792 start_region_grab (item, event);
797 case GainAutomationControlPointItem:
798 case PanAutomationControlPointItem:
799 case RedirectAutomationControlPointItem:
800 start_control_point_grab (item, event);
811 case RegionViewNameHighlight:
812 start_trim (item, event);
817 start_trim (clicked_regionview->get_name_highlight(), event);
828 if (event->type == GDK_BUTTON_PRESS) {
829 /* relax till release */
836 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
837 temporal_zoom_session();
839 temporal_zoom_to_frame (true, event_frame(event));
862 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
864 nframes64_t where = event_frame (event, 0, 0);
865 AutomationTimeAxisView* atv = 0;
867 /* no action if we're recording */
869 if (session && session->actively_recording()) {
873 /* first, see if we're finishing a drag ... */
875 if (drag_info.item) {
876 if (end_grab (item, event)) {
877 /* grab dragged, so do nothing else */
882 button_selection (item, event, item_type);
884 /* edit events get handled here */
886 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
892 case TempoMarkerItem:
893 edit_tempo_marker (item);
896 case MeterMarkerItem:
897 edit_meter_marker (item);
901 if (clicked_regionview->name_active()) {
902 return mouse_rename_region (item, event);
912 /* context menu events get handled here */
914 if (Keyboard::is_context_menu_event (&event->button)) {
916 if (drag_info.item == 0) {
918 /* no matter which button pops up the context menu, tell the menu
919 widget to use button 1 to drive menu selection.
924 case FadeInHandleItem:
926 case FadeOutHandleItem:
927 popup_fade_context_menu (1, event->button.time, item, item_type);
931 popup_track_context_menu (1, event->button.time, item_type, false, where);
935 case RegionViewNameHighlight:
937 popup_track_context_menu (1, event->button.time, item_type, false, where);
941 popup_track_context_menu (1, event->button.time, item_type, true, where);
944 case AutomationTrackItem:
945 popup_track_context_menu (1, event->button.time, item_type, false, where);
949 case RangeMarkerBarItem:
950 case TransportMarkerBarItem:
951 case CdMarkerBarItem:
954 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
958 marker_context_menu (&event->button, item);
961 case TempoMarkerItem:
962 tm_marker_context_menu (&event->button, item);
965 case MeterMarkerItem:
966 tm_marker_context_menu (&event->button, item);
969 case CrossfadeViewItem:
970 popup_track_context_menu (1, event->button.time, item_type, false, where);
973 /* <CMT Additions> */
975 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
977 case ImageFrameTimeAxisItem:
978 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
981 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
983 case MarkerTimeAxisItem:
984 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
986 /* <CMT Additions> */
997 /* delete events get handled here */
999 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
1001 switch (item_type) {
1002 case TempoMarkerItem:
1003 remove_tempo_marker (item);
1006 case MeterMarkerItem:
1007 remove_meter_marker (item);
1011 remove_marker (*item, event);
1015 if (mouse_mode == MouseObject) {
1016 remove_clicked_region ();
1020 case GainControlPointItem:
1021 if (mouse_mode == MouseGain) {
1022 remove_gain_control_point (item, event);
1026 case GainAutomationControlPointItem:
1027 case PanAutomationControlPointItem:
1028 case RedirectAutomationControlPointItem:
1029 remove_control_point (item, event);
1038 switch (event->button.button) {
1041 switch (item_type) {
1042 /* see comments in button_press_handler */
1043 case PlayheadCursorItem:
1046 case GainAutomationLineItem:
1047 case PanAutomationLineItem:
1048 case RedirectAutomationLineItem:
1049 case StartSelectionTrimItem:
1050 case EndSelectionTrimItem:
1054 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1055 snap_to (where, 0, true);
1057 mouse_add_new_marker (where);
1060 case CdMarkerBarItem:
1061 // if we get here then a dragged range wasn't done
1062 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1063 snap_to (where, 0, true);
1065 mouse_add_new_marker (where, true);
1069 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1072 mouse_add_new_tempo_event (where);
1076 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1084 switch (mouse_mode) {
1086 switch (item_type) {
1087 case AutomationTrackItem:
1088 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_trackview);
1090 atv->add_automation_event (item, event, where, event->button.y);
1102 // Gain only makes sense for audio regions
1104 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1108 switch (item_type) {
1110 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1114 case AutomationTrackItem:
1115 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1116 add_automation_event (item, event, where, event->button.y);
1126 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1127 if (scrubbing_direction == 0) {
1128 /* no drag, just a click */
1129 switch (item_type) {
1131 play_selected_region ();
1137 /* make sure we stop */
1138 session->request_transport_speed (0.0);
1152 switch (mouse_mode) {
1155 switch (item_type) {
1157 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1159 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1162 // Button2 click is unused
1175 // x_style_paste (where, 1.0);
1195 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1201 if (last_item_entered != item) {
1202 last_item_entered = item;
1203 last_item_entered_n = 0;
1206 switch (item_type) {
1207 case GainControlPointItem:
1208 if (mouse_mode == MouseGain) {
1209 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1210 cp->set_visible (true);
1214 at_y = cp->get_y ();
1215 cp->item->i2w (at_x, at_y);
1219 fraction = 1.0 - (cp->get_y() / cp->line.height());
1221 if (is_drawable() && !_scrubbing) {
1222 track_canvas->get_window()->set_cursor (*fader_cursor);
1225 last_item_entered_n++;
1226 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1227 if (last_item_entered_n < 10) {
1228 show_verbose_canvas_cursor ();
1233 case GainAutomationControlPointItem:
1234 case PanAutomationControlPointItem:
1235 case RedirectAutomationControlPointItem:
1236 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1237 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1238 cp->set_visible (true);
1242 at_y = cp->get_y ();
1243 cp->item->i2w (at_x, at_y);
1247 fraction = 1.0 - (cp->get_y() / cp->line.height());
1249 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1250 show_verbose_canvas_cursor ();
1252 if (is_drawable()) {
1253 track_canvas->get_window()->set_cursor (*fader_cursor);
1259 if (mouse_mode == MouseGain) {
1260 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1262 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1263 if (is_drawable()) {
1264 track_canvas->get_window()->set_cursor (*fader_cursor);
1269 case GainAutomationLineItem:
1270 case RedirectAutomationLineItem:
1271 case PanAutomationLineItem:
1272 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1274 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1276 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1278 if (is_drawable()) {
1279 track_canvas->get_window()->set_cursor (*fader_cursor);
1284 case RegionViewNameHighlight:
1285 if (is_drawable() && mouse_mode == MouseObject) {
1286 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1290 case StartSelectionTrimItem:
1291 case EndSelectionTrimItem:
1292 /* <CMT Additions> */
1293 case ImageFrameHandleStartItem:
1294 case ImageFrameHandleEndItem:
1295 case MarkerViewHandleStartItem:
1296 case MarkerViewHandleEndItem:
1297 /* </CMT Additions> */
1299 if (is_drawable()) {
1300 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1304 case PlayheadCursorItem:
1305 if (is_drawable()) {
1306 switch (_edit_point) {
1308 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1311 track_canvas->get_window()->set_cursor (*grabber_cursor);
1317 case RegionViewName:
1319 /* when the name is not an active item, the entire name highlight is for trimming */
1321 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1322 if (mouse_mode == MouseObject && is_drawable()) {
1323 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1329 case AutomationTrackItem:
1330 if (is_drawable()) {
1331 Gdk::Cursor *cursor;
1332 switch (mouse_mode) {
1334 cursor = selector_cursor;
1337 cursor = zoom_cursor;
1340 cursor = cross_hair_cursor;
1344 track_canvas->get_window()->set_cursor (*cursor);
1346 AutomationTimeAxisView* atv;
1347 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1348 clear_entered_track = false;
1349 set_entered_track (atv);
1355 case RangeMarkerBarItem:
1356 case TransportMarkerBarItem:
1357 case CdMarkerBarItem:
1360 if (is_drawable()) {
1361 track_canvas->get_window()->set_cursor (*timebar_cursor);
1366 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1369 entered_marker = marker;
1370 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1372 case MeterMarkerItem:
1373 case TempoMarkerItem:
1374 if (is_drawable()) {
1375 track_canvas->get_window()->set_cursor (*timebar_cursor);
1378 case FadeInHandleItem:
1379 case FadeOutHandleItem:
1380 if (mouse_mode == MouseObject) {
1381 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1383 rect->property_fill_color_rgba() = 0;
1384 rect->property_outline_pixels() = 1;
1393 /* second pass to handle entered track status in a comprehensible way.
1396 switch (item_type) {
1398 case GainAutomationLineItem:
1399 case RedirectAutomationLineItem:
1400 case PanAutomationLineItem:
1401 case GainControlPointItem:
1402 case GainAutomationControlPointItem:
1403 case PanAutomationControlPointItem:
1404 case RedirectAutomationControlPointItem:
1405 /* these do not affect the current entered track state */
1406 clear_entered_track = false;
1409 case AutomationTrackItem:
1410 /* handled above already */
1414 set_entered_track (0);
1422 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1431 switch (item_type) {
1432 case GainControlPointItem:
1433 case GainAutomationControlPointItem:
1434 case PanAutomationControlPointItem:
1435 case RedirectAutomationControlPointItem:
1436 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1437 if (cp->line.npoints() > 1) {
1438 if (!cp->selected) {
1439 cp->set_visible (false);
1443 if (is_drawable()) {
1444 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1447 hide_verbose_canvas_cursor ();
1450 case RegionViewNameHighlight:
1451 case StartSelectionTrimItem:
1452 case EndSelectionTrimItem:
1453 case PlayheadCursorItem:
1454 /* <CMT Additions> */
1455 case ImageFrameHandleStartItem:
1456 case ImageFrameHandleEndItem:
1457 case MarkerViewHandleStartItem:
1458 case MarkerViewHandleEndItem:
1459 /* </CMT Additions> */
1460 if (is_drawable()) {
1461 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1466 case GainAutomationLineItem:
1467 case RedirectAutomationLineItem:
1468 case PanAutomationLineItem:
1469 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1471 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1473 line->property_fill_color_rgba() = al->get_line_color();
1475 if (is_drawable()) {
1476 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1480 case RegionViewName:
1481 /* see enter_handler() for notes */
1482 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1483 if (is_drawable() && mouse_mode == MouseObject) {
1484 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1489 case RangeMarkerBarItem:
1490 case TransportMarkerBarItem:
1491 case CdMarkerBarItem:
1495 if (is_drawable()) {
1496 track_canvas->get_window()->set_cursor (*timebar_cursor);
1501 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1505 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1506 location_flags_changed (loc, this);
1509 case MeterMarkerItem:
1510 case TempoMarkerItem:
1512 if (is_drawable()) {
1513 track_canvas->get_window()->set_cursor (*timebar_cursor);
1518 case FadeInHandleItem:
1519 case FadeOutHandleItem:
1520 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1522 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1524 rect->property_fill_color_rgba() = rv->get_fill_color();
1525 rect->property_outline_pixels() = 0;
1530 case AutomationTrackItem:
1531 if (is_drawable()) {
1532 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1533 clear_entered_track = true;
1534 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1546 Editor::left_automation_track ()
1548 if (clear_entered_track) {
1549 set_entered_track (0);
1550 clear_entered_track = false;
1560 if (scrubbing_direction == 0) {
1562 session->request_locate (drag_info.current_pointer_frame, false);
1563 session->request_transport_speed (0.1);
1564 scrubbing_direction = 1;
1568 if (last_scrub_x > drag_info.current_pointer_x) {
1570 /* pointer moved to the left */
1572 if (scrubbing_direction > 0) {
1574 /* we reversed direction to go backwards */
1577 scrub_reverse_distance += (int) (last_scrub_x - drag_info.current_pointer_x);
1581 /* still moving to the left (backwards) */
1583 scrub_reversals = 0;
1584 scrub_reverse_distance = 0;
1586 delta = 0.01 * (last_scrub_x - drag_info.current_pointer_x);
1587 session->request_transport_speed (session->transport_speed() - delta);
1591 /* pointer moved to the right */
1593 if (scrubbing_direction < 0) {
1594 /* we reversed direction to go forward */
1597 scrub_reverse_distance += (int) (drag_info.current_pointer_x - last_scrub_x);
1600 /* still moving to the right */
1602 scrub_reversals = 0;
1603 scrub_reverse_distance = 0;
1605 delta = 0.01 * (drag_info.current_pointer_x - last_scrub_x);
1606 session->request_transport_speed (session->transport_speed() + delta);
1610 /* if there have been more than 2 opposite motion moves detected, or one that moves
1611 back more than 10 pixels, reverse direction
1614 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1616 if (scrubbing_direction > 0) {
1617 /* was forwards, go backwards */
1618 session->request_transport_speed (-0.1);
1619 scrubbing_direction = -1;
1621 /* was backwards, go forwards */
1622 session->request_transport_speed (0.1);
1623 scrubbing_direction = 1;
1626 scrub_reverse_distance = 0;
1627 scrub_reversals = 0;
1631 last_scrub_x = drag_info.current_pointer_x;
1635 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1637 if (event->motion.is_hint) {
1640 /* We call this so that MOTION_NOTIFY events continue to be
1641 delivered to the canvas. We need to do this because we set
1642 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1643 the density of the events, at the expense of a round-trip
1644 to the server. Given that this will mostly occur on cases
1645 where DISPLAY = :0.0, and given the cost of what the motion
1646 event might do, its a good tradeoff.
1649 track_canvas->get_pointer (x, y);
1652 if (current_stepping_trackview) {
1653 /* don't keep the persistent stepped trackview if the mouse moves */
1654 current_stepping_trackview = 0;
1655 step_timeout.disconnect ();
1658 if (session && session->actively_recording()) {
1659 /* Sorry. no dragging stuff around while we record */
1663 drag_info.item_type = item_type;
1664 drag_info.last_pointer_x = drag_info.current_pointer_x;
1665 drag_info.last_pointer_y = drag_info.current_pointer_y;
1666 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1667 &drag_info.current_pointer_y);
1670 switch (mouse_mode) {
1681 if (!from_autoscroll && drag_info.item) {
1682 /* item != 0 is the best test i can think of for dragging.
1684 if (!drag_info.move_threshold_passed) {
1686 bool x_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1687 bool y_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1689 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1691 // and change the initial grab loc/frame if this drag info wants us to
1693 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1694 drag_info.grab_frame = drag_info.current_pointer_frame;
1695 drag_info.grab_x = drag_info.current_pointer_x;
1696 drag_info.grab_y = drag_info.current_pointer_y;
1697 drag_info.last_pointer_frame = drag_info.grab_frame;
1698 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1703 switch (item_type) {
1704 case PlayheadCursorItem:
1706 case RangeMarkerBarItem:
1707 case TransportMarkerBarItem:
1708 case CdMarkerBarItem:
1709 case GainControlPointItem:
1710 case RedirectAutomationControlPointItem:
1711 case GainAutomationControlPointItem:
1712 case PanAutomationControlPointItem:
1713 case TempoMarkerItem:
1714 case MeterMarkerItem:
1715 case RegionViewNameHighlight:
1716 case StartSelectionTrimItem:
1717 case EndSelectionTrimItem:
1720 case RedirectAutomationLineItem:
1721 case GainAutomationLineItem:
1722 case PanAutomationLineItem:
1723 case FadeInHandleItem:
1724 case FadeOutHandleItem:
1725 /* <CMT Additions> */
1726 case ImageFrameHandleStartItem:
1727 case ImageFrameHandleEndItem:
1728 case MarkerViewHandleStartItem:
1729 case MarkerViewHandleEndItem:
1730 /* </CMT Additions> */
1731 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1732 (event->motion.state & Gdk::BUTTON2_MASK))) {
1733 if (!from_autoscroll) {
1734 maybe_autoscroll_horizontally (&event->motion);
1736 (this->*(drag_info.motion_callback)) (item, event);
1745 switch (mouse_mode) {
1750 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1751 (event->motion.state & GDK_BUTTON2_MASK))) {
1752 if (!from_autoscroll) {
1753 maybe_autoscroll (&event->motion);
1755 (this->*(drag_info.motion_callback)) (item, event);
1766 track_canvas_motion (event);
1767 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1775 Editor::break_drag ()
1777 stop_canvas_autoscroll ();
1778 hide_verbose_canvas_cursor ();
1780 if (drag_info.item) {
1781 drag_info.item->ungrab (0);
1783 /* put it back where it came from */
1788 drag_info.item->i2w (cxw, cyw);
1789 drag_info.item->move (drag_info.original_x - cxw, drag_info.original_y - cyw);
1796 Editor::finalize_drag ()
1799 drag_info.copy = false;
1800 drag_info.motion_callback = 0;
1801 drag_info.finished_callback = 0;
1802 drag_info.dest_trackview = 0;
1803 drag_info.source_trackview = 0;
1804 drag_info.last_frame_position = 0;
1805 drag_info.grab_frame = 0;
1806 drag_info.last_pointer_frame = 0;
1807 drag_info.current_pointer_frame = 0;
1808 drag_info.brushing = false;
1809 range_marker_drag_rect->hide();
1810 drag_info.clear_copied_locations ();
1814 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1816 if (drag_info.item == 0) {
1817 fatal << _("programming error: start_grab called without drag item") << endmsg;
1823 cursor = which_grabber_cursor ();
1826 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1828 if (event->button.button == 2) {
1829 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
1830 drag_info.y_constrained = true;
1831 drag_info.x_constrained = false;
1833 drag_info.y_constrained = false;
1834 drag_info.x_constrained = true;
1837 drag_info.x_constrained = false;
1838 drag_info.y_constrained = false;
1841 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1842 drag_info.last_pointer_frame = drag_info.grab_frame;
1843 drag_info.current_pointer_frame = drag_info.grab_frame;
1844 drag_info.current_pointer_x = drag_info.grab_x;
1845 drag_info.current_pointer_y = drag_info.grab_y;
1846 drag_info.last_pointer_x = drag_info.current_pointer_x;
1847 drag_info.last_pointer_y = drag_info.current_pointer_y;
1848 drag_info.cumulative_x_drag = 0;
1849 drag_info.cumulative_y_drag = 0;
1850 drag_info.first_move = true;
1851 drag_info.move_threshold_passed = false;
1852 drag_info.want_move_threshold = false;
1853 drag_info.pointer_frame_offset = 0;
1854 drag_info.brushing = false;
1855 drag_info.clear_copied_locations ();
1857 drag_info.original_x = 0;
1858 drag_info.original_y = 0;
1859 drag_info.item->i2w (drag_info.original_x, drag_info.original_y);
1861 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1863 event->button.time);
1865 if (session && session->transport_rolling()) {
1866 drag_info.was_rolling = true;
1868 drag_info.was_rolling = false;
1871 switch (snap_type) {
1872 case SnapToRegionStart:
1873 case SnapToRegionEnd:
1874 case SnapToRegionSync:
1875 case SnapToRegionBoundary:
1876 build_region_boundary_cache ();
1884 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1886 drag_info.item->ungrab (0);
1887 drag_info.item = new_item;
1890 cursor = which_grabber_cursor ();
1893 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1897 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1899 bool did_drag = false;
1901 stop_canvas_autoscroll ();
1903 if (drag_info.item == 0) {
1907 drag_info.item->ungrab (event->button.time);
1909 if (drag_info.finished_callback) {
1910 drag_info.last_pointer_x = drag_info.current_pointer_x;
1911 drag_info.last_pointer_y = drag_info.current_pointer_y;
1912 (this->*(drag_info.finished_callback)) (item, event);
1915 did_drag = !drag_info.first_move;
1917 hide_verbose_canvas_cursor();
1925 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1927 drag_info.item = item;
1928 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1929 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1933 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1934 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1938 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1940 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes64_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1944 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1946 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1948 nframes64_t fade_length;
1950 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1951 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1957 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1961 if (pos < (arv->region()->position() + 64)) {
1962 fade_length = 64; // this should be a minimum defined somewhere
1963 } else if (pos > arv->region()->last_frame()) {
1964 fade_length = arv->region()->length();
1966 fade_length = pos - arv->region()->position();
1968 /* mapover the region selection */
1970 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1972 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1978 tmp->reset_fade_in_shape_width (fade_length);
1981 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1983 drag_info.first_move = false;
1987 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1989 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1991 nframes64_t fade_length;
1993 if (drag_info.first_move) return;
1995 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1996 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2001 if (pos < (arv->region()->position() + 64)) {
2002 fade_length = 64; // this should be a minimum defined somewhere
2003 } else if (pos > arv->region()->last_frame()) {
2004 fade_length = arv->region()->length();
2006 fade_length = pos - arv->region()->position();
2009 begin_reversible_command (_("change fade in length"));
2011 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2013 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2019 AutomationList& alist = tmp->audio_region()->fade_in();
2020 XMLNode &before = alist.get_state();
2022 tmp->audio_region()->set_fade_in_length (fade_length);
2023 tmp->audio_region()->set_fade_in_active (true);
2025 XMLNode &after = alist.get_state();
2026 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
2029 commit_reversible_command ();
2033 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
2035 drag_info.item = item;
2036 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
2037 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
2041 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
2042 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
2046 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2048 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes64_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
2052 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2054 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2056 nframes64_t fade_length;
2058 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2059 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2064 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2068 if (pos > (arv->region()->last_frame() - 64)) {
2069 fade_length = 64; // this should really be a minimum fade defined somewhere
2071 else if (pos < arv->region()->position()) {
2072 fade_length = arv->region()->length();
2075 fade_length = arv->region()->last_frame() - pos;
2078 /* mapover the region selection */
2080 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2082 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2088 tmp->reset_fade_out_shape_width (fade_length);
2091 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
2093 drag_info.first_move = false;
2097 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2099 if (drag_info.first_move) return;
2101 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2103 nframes64_t fade_length;
2105 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2106 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2112 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2116 if (pos > (arv->region()->last_frame() - 64)) {
2117 fade_length = 64; // this should really be a minimum fade defined somewhere
2119 else if (pos < arv->region()->position()) {
2120 fade_length = arv->region()->length();
2123 fade_length = arv->region()->last_frame() - pos;
2126 begin_reversible_command (_("change fade out length"));
2128 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2130 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2136 AutomationList& alist = tmp->audio_region()->fade_out();
2137 XMLNode &before = alist.get_state();
2139 tmp->audio_region()->set_fade_out_length (fade_length);
2140 tmp->audio_region()->set_fade_out_active (true);
2142 XMLNode &after = alist.get_state();
2143 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
2146 commit_reversible_command ();
2150 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2152 drag_info.item = item;
2153 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2154 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2158 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2159 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2163 Cursor* cursor = (Cursor *) drag_info.data;
2165 if (cursor == playhead_cursor) {
2166 _dragging_playhead = true;
2168 if (session && drag_info.was_rolling) {
2169 session->request_stop ();
2172 if (session && session->is_auditioning()) {
2173 session->cancel_audition ();
2177 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2179 show_verbose_time_cursor (cursor->current_frame, 10);
2183 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2185 Cursor* cursor = (Cursor *) drag_info.data;
2186 nframes64_t adjusted_frame;
2188 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2189 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2195 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2196 if (cursor == playhead_cursor) {
2197 snap_to (adjusted_frame);
2201 if (adjusted_frame == drag_info.last_pointer_frame) return;
2203 cursor->set_position (adjusted_frame);
2205 show_verbose_time_cursor (cursor->current_frame, 10);
2208 track_canvas->update_now ();
2210 UpdateAllTransportClocks (cursor->current_frame);
2212 drag_info.last_pointer_frame = adjusted_frame;
2213 drag_info.first_move = false;
2217 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2219 if (drag_info.first_move) return;
2221 cursor_drag_motion_callback (item, event);
2223 _dragging_playhead = false;
2225 if (item == &playhead_cursor->canvas_item) {
2227 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2233 Editor::update_marker_drag_item (Location *location)
2235 double x1 = frame_to_pixel (location->start());
2236 double x2 = frame_to_pixel (location->end());
2238 if (location->is_mark()) {
2239 marker_drag_line_points.front().set_x(x1);
2240 marker_drag_line_points.back().set_x(x1);
2241 marker_drag_line->property_points() = marker_drag_line_points;
2243 range_marker_drag_rect->property_x1() = x1;
2244 range_marker_drag_rect->property_x2() = x2;
2249 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2253 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2254 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2260 Location *location = find_location_from_marker (marker, is_start);
2262 drag_info.item = item;
2263 drag_info.data = marker;
2264 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2265 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2269 _dragging_edit_point = true;
2271 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2273 update_marker_drag_item (location);
2275 if (location->is_mark()) {
2276 // marker_drag_line->show();
2277 // marker_drag_line->raise_to_top();
2279 range_marker_drag_rect->show();
2280 //range_marker_drag_rect->raise_to_top();
2284 show_verbose_time_cursor (location->start(), 10);
2286 show_verbose_time_cursor (location->end(), 10);
2289 Selection::Operation op = Keyboard::selection_type (event->button.state);
2292 case Selection::Toggle:
2293 selection->toggle (marker);
2295 case Selection::Set:
2296 if (!selection->selected (marker)) {
2297 selection->set (marker);
2300 case Selection::Extend:
2302 Locations::LocationList ll;
2303 list<Marker*> to_add;
2305 selection->markers.range (s, e);
2306 s = min (marker->position(), s);
2307 e = max (marker->position(), e);
2310 if (e < max_frames) {
2313 session->locations()->find_all_between (s, e, ll, Location::Flags (0));
2314 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2315 LocationMarkers* lm = find_location_markers (*i);
2318 to_add.push_back (lm->start);
2321 to_add.push_back (lm->end);
2325 if (!to_add.empty()) {
2326 selection->add (to_add);
2330 case Selection::Add:
2331 selection->add (marker);
2335 /* set up copies for us to manipulate during the drag */
2337 drag_info.clear_copied_locations ();
2339 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
2340 Location *l = find_location_from_marker (*i, is_start);
2341 drag_info.copied_locations.push_back (new Location (*l));
2346 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2348 nframes64_t f_delta = 0;
2349 nframes64_t newframe;
2351 bool move_both = false;
2352 Marker* dragged_marker = (Marker*) drag_info.data;
2354 Location *real_location;
2355 Location *copy_location;
2357 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2358 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2363 nframes64_t next = newframe;
2365 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2366 snap_to (newframe, 0, true);
2369 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2373 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2377 MarkerSelection::iterator i;
2378 list<Location*>::iterator x;
2380 /* find the marker we're dragging, and compute the delta */
2382 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2383 x != drag_info.copied_locations.end() && i != selection->markers.end();
2389 if (marker == dragged_marker) {
2391 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2396 if (real_location->is_mark()) {
2397 f_delta = newframe - copy_location->start();
2401 switch (marker->type()) {
2403 case Marker::LoopStart:
2404 case Marker::PunchIn:
2405 f_delta = newframe - copy_location->start();
2409 case Marker::LoopEnd:
2410 case Marker::PunchOut:
2411 f_delta = newframe - copy_location->end();
2414 /* what kind of marker is this ? */
2422 if (i == selection->markers.end()) {
2423 /* hmm, impossible - we didn't find the dragged marker */
2427 /* now move them all */
2429 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2430 x != drag_info.copied_locations.end() && i != selection->markers.end();
2436 /* call this to find out if its the start or end */
2438 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2442 if (real_location->locked()) {
2446 if (copy_location->is_mark()) {
2450 copy_location->set_start (copy_location->start() + f_delta);
2454 nframes64_t new_start = copy_location->start() + f_delta;
2455 nframes64_t new_end = copy_location->end() + f_delta;
2457 if (is_start) { // start-of-range marker
2460 copy_location->set_start (new_start);
2461 copy_location->set_end (new_end);
2462 } else if (new_start < copy_location->end()) {
2463 copy_location->set_start (new_start);
2465 snap_to (next, 1, true);
2466 copy_location->set_end (next);
2467 copy_location->set_start (newframe);
2470 } else { // end marker
2473 copy_location->set_end (new_end);
2474 copy_location->set_start (new_start);
2475 } else if (new_end > copy_location->start()) {
2476 copy_location->set_end (new_end);
2477 } else if (newframe > 0) {
2478 snap_to (next, -1, true);
2479 copy_location->set_start (next);
2480 copy_location->set_end (newframe);
2484 update_marker_drag_item (copy_location);
2486 LocationMarkers* lm = find_location_markers (real_location);
2489 lm->set_position (copy_location->start(), copy_location->end());
2493 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2494 drag_info.first_move = false;
2496 if (drag_info.copied_locations.empty()) {
2500 edit_point_clock.set (drag_info.copied_locations.front()->start());
2501 show_verbose_time_cursor (newframe, 10);
2504 track_canvas->update_now ();
2509 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2511 if (drag_info.first_move) {
2513 /* just a click, do nothing but finish
2514 off the selection process
2517 Selection::Operation op = Keyboard::selection_type (event->button.state);
2518 Marker* marker = (Marker *) drag_info.data;
2521 case Selection::Set:
2522 if (selection->selected (marker) && selection->markers.size() > 1) {
2523 selection->set (marker);
2527 case Selection::Toggle:
2528 case Selection::Extend:
2529 case Selection::Add:
2536 _dragging_edit_point = false;
2539 begin_reversible_command ( _("move marker") );
2540 XMLNode &before = session->locations()->get_state();
2542 MarkerSelection::iterator i;
2543 list<Location*>::iterator x;
2546 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2547 x != drag_info.copied_locations.end() && i != selection->markers.end();
2550 Location * location = find_location_from_marker ((*i), is_start);
2554 if (location->locked()) {
2558 if (location->is_mark()) {
2559 location->set_start ((*x)->start());
2561 location->set ((*x)->start(), (*x)->end());
2566 XMLNode &after = session->locations()->get_state();
2567 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2568 commit_reversible_command ();
2570 marker_drag_line->hide();
2571 range_marker_drag_rect->hide();
2575 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2578 MeterMarker* meter_marker;
2580 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2581 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2585 meter_marker = dynamic_cast<MeterMarker*> (marker);
2587 MetricSection& section (meter_marker->meter());
2589 if (!section.movable()) {
2593 drag_info.item = item;
2594 drag_info.copy = false;
2595 drag_info.data = marker;
2596 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2597 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2601 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2603 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2607 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2610 MeterMarker* meter_marker;
2612 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2613 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2617 meter_marker = dynamic_cast<MeterMarker*> (marker);
2619 // create a dummy marker for visual representation of moving the copy.
2620 // The actual copying is not done before we reach the finish callback.
2622 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2623 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2624 *new MeterSection(meter_marker->meter()));
2626 drag_info.item = &new_marker->the_item();
2627 drag_info.copy = true;
2628 drag_info.data = new_marker;
2629 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2630 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2634 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2636 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2640 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2642 MeterMarker* marker = (MeterMarker *) drag_info.data;
2643 nframes64_t adjusted_frame;
2645 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2646 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2652 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2653 snap_to (adjusted_frame);
2656 if (adjusted_frame == drag_info.last_pointer_frame) return;
2658 marker->set_position (adjusted_frame);
2661 drag_info.last_pointer_frame = adjusted_frame;
2662 drag_info.first_move = false;
2664 show_verbose_time_cursor (adjusted_frame, 10);
2668 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2670 if (drag_info.first_move) return;
2672 meter_marker_drag_motion_callback (drag_info.item, event);
2674 MeterMarker* marker = (MeterMarker *) drag_info.data;
2677 TempoMap& map (session->tempo_map());
2678 map.bbt_time (drag_info.last_pointer_frame, when);
2680 if (drag_info.copy == true) {
2681 begin_reversible_command (_("copy meter mark"));
2682 XMLNode &before = map.get_state();
2683 map.add_meter (marker->meter(), when);
2684 XMLNode &after = map.get_state();
2685 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2686 commit_reversible_command ();
2688 // delete the dummy marker we used for visual representation of copying.
2689 // a new visual marker will show up automatically.
2692 begin_reversible_command (_("move meter mark"));
2693 XMLNode &before = map.get_state();
2694 map.move_meter (marker->meter(), when);
2695 XMLNode &after = map.get_state();
2696 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2697 commit_reversible_command ();
2702 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2705 TempoMarker* tempo_marker;
2707 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2708 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2712 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2713 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2717 MetricSection& section (tempo_marker->tempo());
2719 if (!section.movable()) {
2723 drag_info.item = item;
2724 drag_info.copy = false;
2725 drag_info.data = marker;
2726 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2727 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2731 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2732 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2736 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2739 TempoMarker* tempo_marker;
2741 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2742 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2746 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2747 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2751 // create a dummy marker for visual representation of moving the copy.
2752 // The actual copying is not done before we reach the finish callback.
2754 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2755 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2756 *new TempoSection(tempo_marker->tempo()));
2758 drag_info.item = &new_marker->the_item();
2759 drag_info.copy = true;
2760 drag_info.data = new_marker;
2761 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2762 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2766 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2768 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2772 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2774 TempoMarker* marker = (TempoMarker *) drag_info.data;
2775 nframes64_t adjusted_frame;
2777 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2778 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2784 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2785 snap_to (adjusted_frame);
2788 if (adjusted_frame == drag_info.last_pointer_frame) return;
2790 /* OK, we've moved far enough to make it worth actually move the thing. */
2792 marker->set_position (adjusted_frame);
2794 show_verbose_time_cursor (adjusted_frame, 10);
2796 drag_info.last_pointer_frame = adjusted_frame;
2797 drag_info.first_move = false;
2801 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2803 if (drag_info.first_move) return;
2805 tempo_marker_drag_motion_callback (drag_info.item, event);
2807 TempoMarker* marker = (TempoMarker *) drag_info.data;
2810 TempoMap& map (session->tempo_map());
2811 map.bbt_time (drag_info.last_pointer_frame, when);
2813 if (drag_info.copy == true) {
2814 begin_reversible_command (_("copy tempo mark"));
2815 XMLNode &before = map.get_state();
2816 map.add_tempo (marker->tempo(), when);
2817 XMLNode &after = map.get_state();
2818 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2819 commit_reversible_command ();
2821 // delete the dummy marker we used for visual representation of copying.
2822 // a new visual marker will show up automatically.
2825 begin_reversible_command (_("move tempo mark"));
2826 XMLNode &before = map.get_state();
2827 map.move_tempo (marker->tempo(), when);
2828 XMLNode &after = map.get_state();
2829 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2830 commit_reversible_command ();
2835 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2837 ControlPoint* control_point;
2839 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2840 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2844 // We shouldn't remove the first or last gain point
2845 if (control_point->line.is_last_point(*control_point) ||
2846 control_point->line.is_first_point(*control_point)) {
2850 control_point->line.remove_point (*control_point);
2854 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2856 ControlPoint* control_point;
2858 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2859 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2863 control_point->line.remove_point (*control_point);
2867 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2869 ControlPoint* control_point;
2871 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2872 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2876 drag_info.item = item;
2877 drag_info.data = control_point;
2878 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2879 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2881 start_grab (event, fader_cursor);
2883 // start the grab at the center of the control point so
2884 // the point doesn't 'jump' to the mouse after the first drag
2885 drag_info.grab_x = control_point->get_x();
2886 drag_info.grab_y = control_point->get_y();
2887 control_point->line.parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
2888 track_canvas->w2c(drag_info.grab_x, drag_info.grab_y,
2889 drag_info.grab_x, drag_info.grab_y);
2891 drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
2893 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2895 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2896 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2897 drag_info.current_pointer_x + 10, drag_info.current_pointer_y + 10);
2899 show_verbose_canvas_cursor ();
2903 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2905 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2907 double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
2908 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2910 if (event->button.state & Keyboard::SecondaryModifier) {
2915 double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
2916 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2918 // calculate zero crossing point. back off by .01 to stay on the
2919 // positive side of zero
2921 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line.height() - .01;
2922 cp->line.parent_group().i2w(_unused, zero_gain_y);
2924 // make sure we hit zero when passing through
2925 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2926 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2930 if (drag_info.x_constrained) {
2931 cx = drag_info.grab_x;
2933 if (drag_info.y_constrained) {
2934 cy = drag_info.grab_y;
2937 drag_info.cumulative_x_drag = cx - drag_info.grab_x;
2938 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2940 cp->line.parent_group().w2i (cx, cy);
2944 cy = min ((double) cp->line.height(), cy);
2946 //translate cx to frames
2947 nframes64_t cx_frames = unit_to_frame (cx);
2949 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2950 snap_to (cx_frames);
2953 float fraction = 1.0 - (cy / cp->line.height());
2957 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2963 cp->line.point_drag (*cp, cx_frames , fraction, push);
2965 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2967 drag_info.first_move = false;
2971 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2973 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2975 if (drag_info.first_move) {
2979 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2980 reset_point_selection ();
2984 control_point_drag_motion_callback (item, event);
2986 cp->line.end_drag (cp);
2990 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2992 switch (mouse_mode) {
2994 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2995 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
3003 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
3007 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
3008 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
3012 start_line_grab (al, event);
3016 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
3020 nframes64_t frame_within_region;
3022 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3026 cx = event->button.x;
3027 cy = event->button.y;
3028 line->parent_group().w2i (cx, cy);
3029 frame_within_region = (nframes64_t) floor (cx * frames_per_unit);
3031 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
3032 current_line_drag_info.after)) {
3033 /* no adjacent points */
3037 drag_info.item = &line->grab_item();
3038 drag_info.data = line;
3039 drag_info.motion_callback = &Editor::line_drag_motion_callback;
3040 drag_info.finished_callback = &Editor::line_drag_finished_callback;
3042 start_grab (event, fader_cursor);
3044 double fraction = 1.0 - (cy / line->height());
3046 line->start_drag (0, drag_info.grab_frame, fraction);
3048 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
3049 drag_info.current_pointer_x + 10, drag_info.current_pointer_y + 10);
3050 show_verbose_canvas_cursor ();
3054 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3056 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3058 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
3060 if (event->button.state & Keyboard::SecondaryModifier) {
3064 double cx = drag_info.current_pointer_x;
3065 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
3067 // calculate zero crossing point. back off by .01 to stay on the
3068 // positive side of zero
3070 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
3071 line->parent_group().i2w(_unused, zero_gain_y);
3073 // make sure we hit zero when passing through
3074 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
3075 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
3079 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
3081 line->parent_group().w2i (cx, cy);
3084 cy = min ((double) line->height(), cy);
3086 double fraction = 1.0 - (cy / line->height());
3090 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3096 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
3098 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
3102 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3104 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3105 line_drag_motion_callback (item, event);
3110 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3112 if (selection->regions.empty() || clicked_regionview == 0) {
3116 drag_info.copy = false;
3117 drag_info.item = item;
3118 drag_info.data = clicked_regionview;
3120 if (Config->get_edit_mode() == Splice) {
3121 drag_info.motion_callback = &Editor::region_drag_splice_motion_callback;
3122 drag_info.finished_callback = &Editor::region_drag_splice_finished_callback;
3124 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3125 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3131 TimeAxisView* tvp = clicked_trackview;
3132 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3134 if (tv && tv->is_audio_track()) {
3135 speed = tv->get_diskstream()->speed();
3138 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3139 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3140 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3141 drag_info.dest_trackview = drag_info.source_trackview;
3142 // we want a move threshold
3143 drag_info.want_move_threshold = true;
3145 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3147 begin_reversible_command (_("move region(s)"));
3149 _region_motion_group->raise_to_top ();
3151 /* sync the canvas to what we think is its current state */
3152 track_canvas->update_now();
3156 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
3158 if (selection->regions.empty() || clicked_regionview == 0) {
3162 drag_info.copy = true;
3163 drag_info.item = item;
3164 drag_info.data = clicked_regionview;
3168 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
3169 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
3172 if (atv && atv->is_audio_track()) {
3173 speed = atv->get_diskstream()->speed();
3176 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3177 drag_info.dest_trackview = drag_info.source_trackview;
3178 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3179 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3180 // we want a move threshold
3181 drag_info.want_move_threshold = true;
3182 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3183 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3184 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3185 _region_motion_group->raise_to_top ();
3189 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
3191 if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) {
3195 drag_info.copy = false;
3196 drag_info.item = item;
3197 drag_info.data = clicked_regionview;
3198 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3199 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3204 TimeAxisView* tvp = clicked_trackview;
3205 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3207 if (tv && tv->is_audio_track()) {
3208 speed = tv->get_diskstream()->speed();
3211 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3212 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3213 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3214 drag_info.dest_trackview = drag_info.source_trackview;
3215 // we want a move threshold
3216 drag_info.want_move_threshold = true;
3217 drag_info.brushing = true;
3219 begin_reversible_command (_("Drag region brush"));
3223 Editor::possibly_copy_regions_during_grab (GdkEvent* event)
3225 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
3227 drag_info.want_move_threshold = false; // don't copy again
3229 /* duplicate the regionview(s) and region(s) */
3231 vector<RegionView*> new_regionviews;
3233 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3235 AudioRegionView* arv;
3237 if ((arv = dynamic_cast<AudioRegionView*>(*i)) == 0) {
3238 /* XXX handle MIDI here */
3242 const boost::shared_ptr<const Region> original = arv->region();
3243 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
3244 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (region_copy);
3246 nrv = new AudioRegionView (*arv, ar);
3247 nrv->get_canvas_group()->show ();
3249 new_regionviews.push_back (nrv);
3252 if (new_regionviews.empty()) {
3256 /* reset selection to new regionviews. This will not set selection visual status for
3257 these regionviews since they don't belong to a track, so do that by hand too.
3260 selection->set (new_regionviews);
3262 for (vector<RegionView*>::iterator i = new_regionviews.begin(); i != new_regionviews.end(); ++i) {
3263 (*i)->set_selected (true);
3266 /* reset drag_info data to reflect the fact that we are dragging the copies */
3268 drag_info.data = new_regionviews.front();
3270 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3272 sync the canvas to what we think is its current state
3273 without it, the canvas seems to
3274 "forget" to update properly after the upcoming reparent()
3275 ..only if the mouse is in rapid motion at the time of the grab.
3276 something to do with regionview creation raking so long?
3278 track_canvas->update_now();
3283 Editor::check_region_drag_possible (AudioTimeAxisView** tv)
3285 /* Which trackview is this ? */
3287 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
3288 (*tv) = dynamic_cast<AudioTimeAxisView*>(tvp);
3290 /* The region motion is only processed if the pointer is over
3294 if (!(*tv) || !(*tv)->is_audio_track()) {
3295 /* To make sure we hide the verbose canvas cursor when the mouse is
3296 not held over and audiotrack.
3298 hide_verbose_canvas_cursor ();
3305 struct RegionSelectionByPosition {
3306 bool operator() (RegionView*a, RegionView* b) {
3307 return a->region()->position () < b->region()->position();
3312 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3314 AudioTimeAxisView* tv;
3316 if (!check_region_drag_possible (&tv)) {
3320 if (!drag_info.move_threshold_passed) {
3326 if (drag_info.current_pointer_x - drag_info.grab_x > 0) {
3332 RegionSelection copy (selection->regions);
3334 RegionSelectionByPosition cmp;
3337 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
3339 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(*i)->get_time_axis_view());
3345 boost::shared_ptr<Playlist> playlist;
3347 if ((playlist = atv->playlist()) == 0) {
3351 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
3356 if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) {
3360 if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) {
3366 playlist->shuffle ((*i)->region(), dir);
3368 drag_info.grab_x = drag_info.current_pointer_x;
3373 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3378 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3382 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
3383 nframes64_t pending_region_position = 0;
3384 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
3385 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
3386 bool clamp_y_axis = false;
3387 vector<int32_t> height_list(512) ;
3388 vector<int32_t>::iterator j;
3389 AudioTimeAxisView* tv;
3391 possibly_copy_regions_during_grab (event);
3393 if (!check_region_drag_possible (&tv)) {
3397 original_pointer_order = drag_info.dest_trackview->order;
3399 /************************************************************
3401 ************************************************************/
3403 if (drag_info.brushing) {
3404 clamp_y_axis = true;
3409 if ((pointer_y_span = (drag_info.dest_trackview->order - tv->order)) != 0) {
3411 int32_t children = 0, numtracks = 0;
3412 // XXX hard coding track limit, oh my, so very very bad
3413 bitset <1024> tracks (0x00);
3414 /* get a bitmask representing the visible tracks */
3416 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3417 TimeAxisView *tracklist_timeview;
3418 tracklist_timeview = (*i);
3419 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
3420 list<TimeAxisView*> children_list;
3422 /* zeroes are audio tracks. ones are other types. */
3424 if (!atv2->hidden()) {
3426 if (visible_y_high < atv2->order) {
3427 visible_y_high = atv2->order;
3429 if (visible_y_low > atv2->order) {
3430 visible_y_low = atv2->order;
3433 if (!atv2->is_audio_track()) {
3434 tracks = tracks |= (0x01 << atv2->order);
3437 height_list[atv2->order] = (*i)->current_height();
3439 if ((children_list = atv2->get_child_list()).size() > 0) {
3440 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
3441 tracks = tracks |= (0x01 << (atv2->order + children));
3442 height_list[atv2->order + children] = (*j)->current_height();
3450 /* find the actual span according to the canvas */
3452 canvas_pointer_y_span = pointer_y_span;
3453 if (drag_info.dest_trackview->order >= tv->order) {
3455 for (y = tv->order; y < drag_info.dest_trackview->order; y++) {
3456 if (height_list[y] == 0 ) {
3457 canvas_pointer_y_span--;
3462 for (y = drag_info.dest_trackview->order;y <= tv->order; y++) {
3463 if ( height_list[y] == 0 ) {
3464 canvas_pointer_y_span++;
3469 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3470 RegionView* rv2 = (*i);
3471 double ix1, ix2, iy1, iy2;
3474 if (rv2->region()->locked()) {
3478 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3479 rv2->get_canvas_group()->i2w (ix1, iy1);
3480 iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
3482 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3483 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3485 if (atv2->order != original_pointer_order) {
3486 /* this isn't the pointer track */
3488 if (canvas_pointer_y_span > 0) {
3490 /* moving up the canvas */
3491 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
3493 int32_t visible_tracks = 0;
3494 while (visible_tracks < canvas_pointer_y_span ) {
3497 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3498 /* we're passing through a hidden track */
3503 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3504 clamp_y_axis = true;
3508 clamp_y_axis = true;
3511 } else if (canvas_pointer_y_span < 0) {
3513 /*moving down the canvas*/
3515 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3518 int32_t visible_tracks = 0;
3520 while (visible_tracks > canvas_pointer_y_span ) {
3523 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3527 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3528 clamp_y_axis = true;
3533 clamp_y_axis = true;
3539 /* this is the pointer's track */
3540 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3541 clamp_y_axis = true;
3542 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3543 clamp_y_axis = true;
3551 } else if (drag_info.dest_trackview == tv) {
3552 clamp_y_axis = true;
3556 if (!clamp_y_axis) {
3557 drag_info.dest_trackview = tv;
3560 /************************************************************
3562 ************************************************************/
3564 /* compute the amount of pointer motion in frames, and where
3565 the region would be if we moved it by that much.
3567 if ( drag_info.move_threshold_passed ) {
3569 if (drag_info.current_pointer_frame >= drag_info.pointer_frame_offset) {
3571 nframes64_t sync_frame;
3572 nframes64_t sync_offset;
3575 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3577 sync_offset = rv->region()->sync_offset (sync_dir);
3579 /* we don't handle a sync point that lies before zero.
3581 if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) {
3582 sync_frame = pending_region_position + (sync_dir*sync_offset);
3584 /* we snap if the snap modifier is not enabled.
3587 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3588 snap_to (sync_frame);
3591 pending_region_position = rv->region()->adjust_to_sync (sync_frame);
3594 pending_region_position = drag_info.last_frame_position;
3598 pending_region_position = 0;
3601 if (pending_region_position > max_frames - rv->region()->length()) {
3602 pending_region_position = drag_info.last_frame_position;
3605 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3607 bool x_move_allowed;
3609 if (Config->get_edit_mode() == Lock) {
3610 if (drag_info.copy) {
3611 x_move_allowed = !drag_info.x_constrained;
3613 /* in locked edit mode, reverse the usual meaning of x_constrained */
3614 x_move_allowed = drag_info.x_constrained;
3617 x_move_allowed = !drag_info.x_constrained;
3620 if (( pending_region_position != drag_info.last_frame_position) && x_move_allowed ) {
3622 /* now compute the canvas unit distance we need to move the regionview
3623 to make it appear at the new location.
3626 if (pending_region_position > drag_info.last_frame_position) {
3627 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3629 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3630 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3632 RegionView* rv2 = (*i);
3634 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3636 double ix1, ix2, iy1, iy2;
3637 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3638 rv2->get_canvas_group()->i2w (ix1, iy1);
3640 if (-x_delta > ix1 + horizontal_adjustment.get_value()) {
3642 pending_region_position = drag_info.last_frame_position;
3649 drag_info.last_frame_position = pending_region_position;
3656 /* threshold not passed */
3661 /*************************************************************
3663 ************************************************************/
3665 if (x_delta == 0 && (pointer_y_span == 0)) {
3666 /* haven't reached next snap point, and we're not switching
3667 trackviews. nothing to do.
3672 /*************************************************************
3674 ************************************************************/
3675 bool do_move = true;
3676 if (drag_info.first_move) {
3677 if (!drag_info.move_threshold_passed) {
3684 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3685 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3687 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3689 RegionView* rv = (*i);
3690 double ix1, ix2, iy1, iy2;
3691 int32_t temp_pointer_y_span = pointer_y_span;
3693 if (rv->region()->locked()) {
3697 /* get item BBox, which will be relative to parent. so we have
3698 to query on a child, then convert to world coordinates using
3702 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3703 rv->get_canvas_group()->i2w (ix1, iy1);
3705 /* for evaluation of the track position of iy1, we have to adjust
3706 to allow for the vertical scrolling adjustment and the height of the timebars.
3709 iy1 += get_trackview_group_vertical_offset ();
3710 if (drag_info.first_move) {
3712 // hide any dependent views
3714 rv->get_time_axis_view().hide_dependent_views (*rv);
3717 reparent to a non scrolling group so that we can keep the
3718 region selection above all time axis views.
3719 reparenting means we have to move the rv as the two
3720 parent groups have different coordinates.
3723 rv->get_canvas_group()->property_y() = iy1 - 1;
3724 rv->get_canvas_group()->reparent(*_region_motion_group);
3726 rv->fake_set_opaque (true);
3729 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3730 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3731 AudioTimeAxisView* temp_atv;
3733 if ((pointer_y_span != 0) && !clamp_y_axis) {
3736 for (j = height_list.begin(); j!= height_list.end(); j++) {
3737 if (x == canvas_atv->order) {
3738 /* we found the track the region is on */
3739 if (x != original_pointer_order) {
3740 /*this isn't from the same track we're dragging from */
3741 temp_pointer_y_span = canvas_pointer_y_span;
3743 while (temp_pointer_y_span > 0) {
3744 /* we're moving up canvas-wise,
3745 so we need to find the next track height
3747 if (j != height_list.begin()) {
3750 if (x != original_pointer_order) {
3751 /* we're not from the dragged track, so ignore hidden tracks. */
3753 temp_pointer_y_span++;
3757 temp_pointer_y_span--;
3760 while (temp_pointer_y_span < 0) {
3762 if (x != original_pointer_order) {
3764 temp_pointer_y_span--;
3768 if (j != height_list.end()) {
3771 temp_pointer_y_span++;
3773 /* find out where we'll be when we move and set height accordingly */
3775 tvp2 = trackview_by_y_position (iy1 + y_delta);
3776 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3777 rv->set_height (temp_atv->current_height());
3779 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3780 personally, i think this can confuse things, but never mind.
3783 //const GdkColor& col (temp_atv->view->get_region_color());
3784 //rv->set_color (const_cast<GdkColor&>(col));
3791 if (drag_info.brushing) {
3792 mouse_brush_insert_region (rv, pending_region_position);
3794 rv->move (x_delta, y_delta);
3797 } /* foreach region */
3801 if (drag_info.first_move && drag_info.move_threshold_passed) {
3802 cursor_group->raise_to_top();
3803 drag_info.first_move = false;
3806 if (x_delta != 0 && !drag_info.brushing) {
3807 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3812 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3814 bool nocommit = true;
3815 vector<RegionView*> copies;
3816 RouteTimeAxisView* source_tv;
3817 boost::shared_ptr<Diskstream> ds;
3818 boost::shared_ptr<Playlist> from_playlist;
3819 vector<RegionView*> new_selection;
3820 typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
3821 PlaylistSet modified_playlists;
3822 PlaylistSet frozen_playlists;
3823 list <sigc::connection> modified_playlist_connections;
3824 pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
3826 /* first_move is set to false if the regionview has been moved in the
3830 if (drag_info.first_move) {
3837 if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
3838 selection->set (pre_drag_region_selection);
3839 pre_drag_region_selection.clear ();
3842 if (drag_info.brushing) {
3843 /* all changes were made during motion event handlers */
3845 if (drag_info.copy) {
3846 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3847 copies.push_back (*i);
3856 /* reverse this here so that we have the correct logic to finalize
3860 if (Config->get_edit_mode() == Lock && !drag_info.copy) {
3861 drag_info.x_constrained = !drag_info.x_constrained;
3864 if (drag_info.copy) {
3865 if (drag_info.x_constrained) {
3866 op_string = _("fixed time region copy");
3868 op_string = _("region copy");
3871 if (drag_info.x_constrained) {
3872 op_string = _("fixed time region drag");
3874 op_string = _("region drag");
3878 begin_reversible_command (op_string);
3880 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3882 RegionView* rv = (*i);
3883 double ix1, ix2, iy1, iy2;
3884 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3885 rv->get_canvas_group()->i2w (ix1, iy1);
3886 iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
3888 TimeAxisView* dest_tv = trackview_by_y_position (iy1);
3889 AudioTimeAxisView* dest_atv = dynamic_cast<AudioTimeAxisView*>(dest_tv);
3891 bool changed_tracks, changed_position;
3894 if (rv->region()->locked()) {
3899 /* adjust for track speed */
3903 if (dest_atv && dest_atv->get_diskstream()) {
3904 speed = dest_atv->get_diskstream()->speed();
3907 changed_position = (drag_info.last_frame_position != (nframes64_t) (rv->region()->position()/speed));
3908 changed_tracks = (dest_tv != &rv->get_time_axis_view());
3910 if (changed_position && !drag_info.x_constrained) {
3911 _master_group->w2i(ix1, iy1);
3912 where = (nframes64_t) (unit_to_frame (ix1) * speed);
3914 where = rv->region()->position();
3917 boost::shared_ptr<Region> new_region;
3919 if (drag_info.copy) {
3920 /* we already made a copy */
3921 new_region = rv->region();
3923 /* undo the previous hide_dependent_views so that xfades don't
3924 disappear on copying regions
3927 //rv->get_time_axis_view().reveal_dependent_views (*rv);
3929 } else if (changed_tracks) {
3930 new_region = RegionFactory::create (rv->region());
3933 if (changed_tracks || drag_info.copy) {
3935 boost::shared_ptr<Playlist> to_playlist = dest_atv->playlist();
3937 latest_regionviews.clear ();
3939 sigc::connection c = dest_atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3941 insert_result = modified_playlists.insert (to_playlist);
3942 if (insert_result.second) {
3943 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3946 to_playlist->add_region (new_region, where);
3950 if (!latest_regionviews.empty()) {
3951 // XXX why just the first one ? we only expect one
3952 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
3953 new_selection.push_back (latest_regionviews.front());
3958 motion on the same track. plonk the previously reparented region
3959 back to its original canvas group (its streamview).
3960 No need to do anything for copies as they are fake regions which will be deleted.
3963 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (dest_atv);
3964 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
3965 rv->get_canvas_group()->property_y() = 0;
3967 /* just change the model */
3969 boost::shared_ptr<Playlist> playlist = dest_atv->playlist();
3971 insert_result = modified_playlists.insert (playlist);
3972 if (insert_result.second) {
3973 session->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
3975 /* freeze to avoid lots of relayering in the case of a multi-region drag */
3976 frozen_insert_result = frozen_playlists.insert(playlist);
3977 if (frozen_insert_result.second) {
3981 rv->region()->set_position (where, (void*) this);
3984 if (changed_tracks && !drag_info.copy) {
3986 /* get the playlist where this drag started. we can't use rv->region()->playlist()
3987 because we may have copied the region and it has not been attached to a playlist.
3990 assert ((source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view())));
3991 assert ((ds = source_tv->get_diskstream()));
3992 assert ((from_playlist = ds->playlist()));
3994 /* moved to a different audio track, without copying */
3996 /* the region that used to be in the old playlist is not
3997 moved to the new one - we use a copy of it. as a result,
3998 any existing editor for the region should no longer be
4002 rv->hide_region_editor();
4003 rv->fake_set_opaque (false);
4005 /* remove the region from the old playlist */
4007 insert_result = modified_playlists.insert (from_playlist);
4008 if (insert_result.second) {
4009 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
4012 from_playlist->remove_region ((rv->region()));
4014 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
4015 was selected in all of them, then removing it from a playlist will have removed all
4016 trace of it from the selection (i.e. there were N regions selected, we removed 1,
4017 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
4018 corresponding regionview, and the selection is now empty).
4020 this could have invalidated any and all iterators into the region selection.
4022 the heuristic we use here is: if the region selection is empty, break out of the loop
4023 here. if the region selection is not empty, then restart the loop because we know that
4024 we must have removed at least the region(view) we've just been working on as well as any
4025 that we processed on previous iterations.
4027 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
4028 we can just iterate.
4031 if (selection->regions.empty()) {
4034 i = selection->regions.by_layer().begin();
4041 if (drag_info.copy) {
4042 copies.push_back (rv);
4046 if (new_selection.empty()) {
4047 if (drag_info.copy) {
4048 /* the region(view)s that are selected and being dragged around
4049 are copies and do not belong to any track. remove them
4050 from the selection right here.
4052 selection->clear_regions();
4055 /* this will clear any existing selection that would have been
4056 cleared in the other clause above
4058 selection->set (new_selection);
4061 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
4067 for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
4068 session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));
4070 commit_reversible_command ();
4073 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
4080 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
4082 /* Either add to or set the set the region selection, unless
4083 this is an alignment click (control used)
4086 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
4087 TimeAxisView* tv = &rv.get_time_axis_view();
4088 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
4090 if (atv && atv->is_audio_track()) {
4091 speed = atv->get_diskstream()->speed();
4094 nframes64_t where = get_preferred_edit_position();
4098 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
4100 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
4102 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
4104 align_region (rv.region(), End, (nframes64_t) (where * speed));
4108 align_region (rv.region(), Start, (nframes64_t) (where * speed));
4115 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
4121 nframes64_t frame_rate;
4130 if (Profile->get_sae() || Profile->get_small_screen()) {
4131 m = ARDOUR_UI::instance()->primary_clock.mode();
4133 m = ARDOUR_UI::instance()->secondary_clock.mode();
4137 case AudioClock::BBT:
4138 session->bbt_time (frame, bbt);
4139 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
4142 case AudioClock::SMPTE:
4143 session->smpte_time (frame, smpte);
4144 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4147 case AudioClock::MinSec:
4148 /* XXX this is copied from show_verbose_duration_cursor() */
4149 frame_rate = session->frame_rate();
4150 hours = frame / (frame_rate * 3600);
4151 frame = frame % (frame_rate * 3600);
4152 mins = frame / (frame_rate * 60);
4153 frame = frame % (frame_rate * 60);
4154 secs = (float) frame / (float) frame_rate;
4155 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4159 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
4163 if (xpos >= 0 && ypos >=0) {
4164 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4167 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);
4169 show_verbose_canvas_cursor ();
4173 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
4180 nframes64_t distance, frame_rate;
4182 Meter meter_at_start(session->tempo_map().meter_at(start));
4190 if (Profile->get_sae() || Profile->get_small_screen()) {
4191 m = ARDOUR_UI::instance()->primary_clock.mode ();
4193 m = ARDOUR_UI::instance()->secondary_clock.mode ();
4197 case AudioClock::BBT:
4198 session->bbt_time (start, sbbt);
4199 session->bbt_time (end, ebbt);
4202 /* XXX this computation won't work well if the
4203 user makes a selection that spans any meter changes.
4206 ebbt.bars -= sbbt.bars;
4207 if (ebbt.beats >= sbbt.beats) {
4208 ebbt.beats -= sbbt.beats;
4211 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
4213 if (ebbt.ticks >= sbbt.ticks) {
4214 ebbt.ticks -= sbbt.ticks;
4217 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
4220 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
4223 case AudioClock::SMPTE:
4224 session->smpte_duration (end - start, smpte);
4225 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4228 case AudioClock::MinSec:
4229 /* XXX this stuff should be elsewhere.. */
4230 distance = end - start;
4231 frame_rate = session->frame_rate();
4232 hours = distance / (frame_rate * 3600);
4233 distance = distance % (frame_rate * 3600);
4234 mins = distance / (frame_rate * 60);
4235 distance = distance % (frame_rate * 60);
4236 secs = (float) distance / (float) frame_rate;
4237 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4241 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
4245 if (xpos >= 0 && ypos >=0) {
4246 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4249 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4252 show_verbose_canvas_cursor ();
4256 Editor::collect_new_region_view (RegionView* rv)
4258 latest_regionviews.push_back (rv);
4262 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
4264 if (clicked_regionview == 0) {
4268 /* lets try to create new Region for the selection */
4270 vector<boost::shared_ptr<AudioRegion> > new_regions;
4271 create_region_from_selection (new_regions);
4273 if (new_regions.empty()) {
4277 /* XXX fix me one day to use all new regions */
4279 boost::shared_ptr<Region> region (new_regions.front());
4281 /* add it to the current stream/playlist.
4283 tricky: the streamview for the track will add a new regionview. we will
4284 catch the signal it sends when it creates the regionview to
4285 set the regionview we want to then drag.
4288 latest_regionviews.clear();
4289 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4291 /* A selection grab currently creates two undo/redo operations, one for
4292 creating the new region and another for moving it.
4295 begin_reversible_command (_("selection grab"));
4297 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
4299 XMLNode *before = &(playlist->get_state());
4300 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
4301 XMLNode *after = &(playlist->get_state());
4302 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4304 commit_reversible_command ();
4308 if (latest_regionviews.empty()) {
4309 /* something went wrong */
4313 /* we need to deselect all other regionviews, and select this one
4314 i'm ignoring undo stuff, because the region creation will take care of it
4316 selection->set (latest_regionviews);
4318 drag_info.item = latest_regionviews.front()->get_canvas_group();
4319 drag_info.data = latest_regionviews.front();
4320 drag_info.motion_callback = &Editor::region_drag_motion_callback;
4321 drag_info.finished_callback = &Editor::region_drag_finished_callback;
4325 drag_info.source_trackview = clicked_trackview;
4326 drag_info.dest_trackview = drag_info.source_trackview;
4327 drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4328 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4330 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4334 Editor::cancel_selection ()
4336 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4337 (*i)->hide_selection ();
4339 selection->clear ();
4340 clicked_selection = 0;
4344 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4346 nframes64_t start = 0;
4347 nframes64_t end = 0;
4353 drag_info.item = item;
4354 drag_info.motion_callback = &Editor::drag_selection;
4355 drag_info.finished_callback = &Editor::end_selection_op;
4360 case CreateSelection:
4361 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4362 drag_info.copy = true;
4364 drag_info.copy = false;
4366 start_grab (event, selector_cursor);
4369 case SelectionStartTrim:
4370 if (clicked_trackview) {
4371 clicked_trackview->order_selection_trims (item, true);
4373 start_grab (event, trimmer_cursor);
4374 start = selection->time[clicked_selection].start;
4375 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4378 case SelectionEndTrim:
4379 if (clicked_trackview) {
4380 clicked_trackview->order_selection_trims (item, false);
4382 start_grab (event, trimmer_cursor);
4383 end = selection->time[clicked_selection].end;
4384 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
4388 start = selection->time[clicked_selection].start;
4390 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4394 if (selection_op == SelectionMove) {
4395 show_verbose_time_cursor(start, 10);
4397 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4402 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4404 nframes64_t start = 0;
4405 nframes64_t end = 0;
4407 nframes64_t pending_position;
4409 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4410 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4412 pending_position = 0;
4415 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4416 snap_to (pending_position);
4419 /* only alter selection if the current frame is
4420 different from the last frame position (adjusted)
4423 if (pending_position == drag_info.last_pointer_frame) return;
4425 switch (selection_op) {
4426 case CreateSelection:
4428 if (drag_info.first_move) {
4429 snap_to (drag_info.grab_frame);
4432 if (pending_position < drag_info.grab_frame) {
4433 start = pending_position;
4434 end = drag_info.grab_frame;
4436 end = pending_position;
4437 start = drag_info.grab_frame;
4440 /* first drag: Either add to the selection
4441 or create a new selection->
4444 if (drag_info.first_move) {
4446 begin_reversible_command (_("range selection"));
4448 if (drag_info.copy) {
4449 /* adding to the selection */
4450 clicked_selection = selection->add (start, end);
4451 drag_info.copy = false;
4453 /* new selection-> */
4454 clicked_selection = selection->set (clicked_trackview, start, end);
4459 case SelectionStartTrim:
4461 if (drag_info.first_move) {
4462 begin_reversible_command (_("trim selection start"));
4465 start = selection->time[clicked_selection].start;
4466 end = selection->time[clicked_selection].end;
4468 if (pending_position > end) {
4471 start = pending_position;
4475 case SelectionEndTrim:
4477 if (drag_info.first_move) {
4478 begin_reversible_command (_("trim selection end"));
4481 start = selection->time[clicked_selection].start;
4482 end = selection->time[clicked_selection].end;
4484 if (pending_position < start) {
4487 end = pending_position;
4494 if (drag_info.first_move) {
4495 begin_reversible_command (_("move selection"));
4498 start = selection->time[clicked_selection].start;
4499 end = selection->time[clicked_selection].end;
4501 length = end - start;
4503 start = pending_position;
4506 end = start + length;
4511 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4512 start_canvas_autoscroll (1, 0);
4516 selection->replace (clicked_selection, start, end);
4519 drag_info.last_pointer_frame = pending_position;
4520 drag_info.first_move = false;
4522 if (selection_op == SelectionMove) {
4523 show_verbose_time_cursor(start, 10);
4525 show_verbose_time_cursor(pending_position, 10);
4530 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4532 if (!drag_info.first_move) {
4533 drag_selection (item, event);
4534 /* XXX this is not object-oriented programming at all. ick */
4535 if (selection->time.consolidate()) {
4536 selection->TimeChanged ();
4538 commit_reversible_command ();
4540 /* just a click, no pointer movement.*/
4542 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4544 selection->clear_time();
4549 /* XXX what happens if its a music selection? */
4550 session->set_audio_range (selection->time);
4551 stop_canvas_autoscroll ();
4555 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4558 TimeAxisView* tvp = clicked_trackview;
4559 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4561 if (tv && tv->is_audio_track()) {
4562 speed = tv->get_diskstream()->speed();
4565 nframes64_t region_start = (nframes64_t) (clicked_regionview->region()->position() / speed);
4566 nframes64_t region_end = (nframes64_t) (clicked_regionview->region()->last_frame() / speed);
4567 nframes64_t region_length = (nframes64_t) (clicked_regionview->region()->length() / speed);
4569 //drag_info.item = clicked_regionview->get_name_highlight();
4570 drag_info.item = item;
4571 drag_info.motion_callback = &Editor::trim_motion_callback;
4572 drag_info.finished_callback = &Editor::trim_finished_callback;
4574 start_grab (event, trimmer_cursor);
4576 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4577 trim_op = ContentsTrim;
4579 /* These will get overridden for a point trim.*/
4580 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4581 /* closer to start */
4582 trim_op = StartTrim;
4583 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4591 show_verbose_time_cursor(region_start, 10);
4594 show_verbose_time_cursor(region_end, 10);
4597 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4603 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4605 RegionView* rv = clicked_regionview;
4606 nframes64_t frame_delta = 0;
4607 bool left_direction;
4608 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4610 /* snap modifier works differently here..
4611 its' current state has to be passed to the
4612 various trim functions in order to work properly
4616 TimeAxisView* tvp = clicked_trackview;
4617 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4618 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4620 if (tv && tv->is_audio_track()) {
4621 speed = tv->get_diskstream()->speed();
4624 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4625 left_direction = true;
4627 left_direction = false;
4631 snap_to (drag_info.current_pointer_frame);
4634 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4638 if (drag_info.first_move) {
4644 trim_type = "Region start trim";
4647 trim_type = "Region end trim";
4650 trim_type = "Region content trim";
4654 begin_reversible_command (trim_type);
4656 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4657 (*i)->fake_set_opaque(false);
4658 (*i)->region()->freeze ();
4660 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4662 arv->temporarily_hide_envelope ();
4664 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4665 insert_result = motion_frozen_playlists.insert (pl);
4666 if (insert_result.second) {
4667 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4672 if (left_direction) {
4673 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4675 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4680 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4683 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4684 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4690 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes64_t) (rv->region()->last_frame()/speed))) {
4693 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4694 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4701 bool swap_direction = false;
4703 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4704 swap_direction = true;
4707 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4708 i != selection->regions.by_layer().end(); ++i)
4710 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4718 show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
4721 show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
4724 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4728 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4729 drag_info.first_move = false;
4733 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4735 boost::shared_ptr<Region> region (rv.region());
4737 if (region->locked()) {
4741 nframes64_t new_bound;
4744 TimeAxisView* tvp = clicked_trackview;
4745 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4747 if (tv && tv->is_audio_track()) {
4748 speed = tv->get_diskstream()->speed();
4751 if (left_direction) {
4752 if (swap_direction) {
4753 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4755 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4758 if (swap_direction) {
4759 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4761 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4766 snap_to (new_bound);
4768 region->trim_start ((nframes64_t) (new_bound * speed), this);
4769 rv.region_changed (StartChanged);
4773 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
4775 boost::shared_ptr<Region> region (rv.region());
4777 if (region->locked()) {
4781 nframes64_t new_bound;
4784 TimeAxisView* tvp = clicked_trackview;
4785 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4787 if (tv && tv->is_audio_track()) {
4788 speed = tv->get_diskstream()->speed();
4791 if (left_direction) {
4792 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4794 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4798 snap_to (new_bound, (left_direction ? 0 : 1));
4801 region->trim_front ((nframes64_t) (new_bound * speed), this);
4803 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4807 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
4809 boost::shared_ptr<Region> region (rv.region());
4811 if (region->locked()) {
4815 nframes64_t new_bound;
4818 TimeAxisView* tvp = clicked_trackview;
4819 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4821 if (tv && tv->is_audio_track()) {
4822 speed = tv->get_diskstream()->speed();
4825 if (left_direction) {
4826 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
4828 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
4832 snap_to (new_bound);
4834 region->trim_end ((nframes64_t) (new_bound * speed), this);
4835 rv.region_changed (LengthChanged);
4839 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4841 if (!drag_info.first_move) {
4842 trim_motion_callback (item, event);
4844 if (!selection->selected (clicked_regionview)) {
4845 thaw_region_after_trim (*clicked_regionview);
4848 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4849 i != selection->regions.by_layer().end(); ++i)
4851 thaw_region_after_trim (**i);
4852 (*i)->fake_set_opaque (true);
4856 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4858 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4861 motion_frozen_playlists.clear ();
4863 commit_reversible_command();
4865 /* no mouse movement */
4871 Editor::point_trim (GdkEvent* event)
4873 RegionView* rv = clicked_regionview;
4874 nframes64_t new_bound = drag_info.current_pointer_frame;
4876 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4877 snap_to (new_bound);
4880 /* Choose action dependant on which button was pressed */
4881 switch (event->button.button) {
4883 trim_op = StartTrim;
4884 begin_reversible_command (_("Start point trim"));
4886 if (selection->selected (rv)) {
4888 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4889 i != selection->regions.by_layer().end(); ++i)
4891 if (!(*i)->region()->locked()) {
4892 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4893 XMLNode &before = pl->get_state();
4894 (*i)->region()->trim_front (new_bound, this);
4895 XMLNode &after = pl->get_state();
4896 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4902 if (!rv->region()->locked()) {
4903 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4904 XMLNode &before = pl->get_state();
4905 rv->region()->trim_front (new_bound, this);
4906 XMLNode &after = pl->get_state();
4907 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4911 commit_reversible_command();
4916 begin_reversible_command (_("End point trim"));
4918 if (selection->selected (rv)) {
4920 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4922 if (!(*i)->region()->locked()) {
4923 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4924 XMLNode &before = pl->get_state();
4925 (*i)->region()->trim_end (new_bound, this);
4926 XMLNode &after = pl->get_state();
4927 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4933 if (!rv->region()->locked()) {
4934 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4935 XMLNode &before = pl->get_state();
4936 rv->region()->trim_end (new_bound, this);
4937 XMLNode &after = pl->get_state();
4938 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4942 commit_reversible_command();
4951 Editor::thaw_region_after_trim (RegionView& rv)
4953 boost::shared_ptr<Region> region (rv.region());
4955 if (region->locked()) {
4959 region->thaw (_("trimmed region"));
4960 XMLNode &after = region->playlist()->get_state();
4961 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4963 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4965 arv->unhide_envelope ();
4969 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4974 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4975 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4979 Location* location = find_location_from_marker (marker, is_start);
4980 location->set_hidden (true, this);
4985 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4991 drag_info.item = item;
4992 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4993 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4995 range_marker_op = op;
4997 if (!temp_location) {
4998 temp_location = new Location;
5002 case CreateRangeMarker:
5003 case CreateTransportMarker:
5004 case CreateCDMarker:
5006 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
5007 drag_info.copy = true;
5009 drag_info.copy = false;
5011 start_grab (event, selector_cursor);
5015 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5020 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5022 nframes64_t start = 0;
5023 nframes64_t end = 0;
5024 ArdourCanvas::SimpleRect *crect;
5026 switch (range_marker_op) {
5027 case CreateRangeMarker:
5028 crect = range_bar_drag_rect;
5030 case CreateTransportMarker:
5031 crect = transport_bar_drag_rect;
5033 case CreateCDMarker:
5034 crect = cd_marker_bar_drag_rect;
5037 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
5042 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5043 snap_to (drag_info.current_pointer_frame);
5046 /* only alter selection if the current frame is
5047 different from the last frame position.
5050 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5052 switch (range_marker_op) {
5053 case CreateRangeMarker:
5054 case CreateTransportMarker:
5055 case CreateCDMarker:
5056 if (drag_info.first_move) {
5057 snap_to (drag_info.grab_frame);
5060 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5061 start = drag_info.current_pointer_frame;
5062 end = drag_info.grab_frame;
5064 end = drag_info.current_pointer_frame;
5065 start = drag_info.grab_frame;
5068 /* first drag: Either add to the selection
5069 or create a new selection.
5072 if (drag_info.first_move) {
5074 temp_location->set (start, end);
5078 update_marker_drag_item (temp_location);
5079 range_marker_drag_rect->show();
5080 //range_marker_drag_rect->raise_to_top();
5086 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
5087 start_canvas_autoscroll (1, 0);
5091 temp_location->set (start, end);
5093 double x1 = frame_to_pixel (start);
5094 double x2 = frame_to_pixel (end);
5095 crect->property_x1() = x1;
5096 crect->property_x2() = x2;
5098 update_marker_drag_item (temp_location);
5101 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5102 drag_info.first_move = false;
5104 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5109 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5111 Location * newloc = 0;
5115 if (!drag_info.first_move) {
5116 drag_range_markerbar_op (item, event);
5118 switch (range_marker_op) {
5119 case CreateRangeMarker:
5120 case CreateCDMarker:
5122 begin_reversible_command (_("new range marker"));
5123 XMLNode &before = session->locations()->get_state();
5124 session->locations()->next_available_name(rangename,"unnamed");
5125 if (range_marker_op == CreateCDMarker) {
5126 flags = Location::IsRangeMarker|Location::IsCDMarker;
5127 cd_marker_bar_drag_rect->hide();
5130 flags = Location::IsRangeMarker;
5131 range_bar_drag_rect->hide();
5133 newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
5134 session->locations()->add (newloc, true);
5135 XMLNode &after = session->locations()->get_state();
5136 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
5137 commit_reversible_command ();
5139 range_marker_drag_rect->hide();
5143 case CreateTransportMarker:
5144 // popup menu to pick loop or punch
5145 new_transport_marker_context_menu (&event->button, item);
5150 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5152 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
5157 start = session->locations()->first_mark_before (drag_info.grab_frame);
5158 end = session->locations()->first_mark_after (drag_info.grab_frame);
5160 if (end == max_frames) {
5161 end = session->current_end_frame ();
5165 start = session->current_start_frame ();
5168 switch (mouse_mode) {
5170 /* find the two markers on either side and then make the selection from it */
5171 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
5175 /* find the two markers on either side of the click and make the range out of it */
5176 selection->set (0, start, end);
5185 stop_canvas_autoscroll ();
5191 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5193 drag_info.item = item;
5194 drag_info.motion_callback = &Editor::drag_mouse_zoom;
5195 drag_info.finished_callback = &Editor::end_mouse_zoom;
5197 start_grab (event, zoom_cursor);
5199 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5203 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5208 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5209 snap_to (drag_info.current_pointer_frame);
5211 if (drag_info.first_move) {
5212 snap_to (drag_info.grab_frame);
5216 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5218 /* base start and end on initial click position */
5219 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5220 start = drag_info.current_pointer_frame;
5221 end = drag_info.grab_frame;
5223 end = drag_info.current_pointer_frame;
5224 start = drag_info.grab_frame;
5229 if (drag_info.first_move) {
5231 zoom_rect->raise_to_top();
5234 reposition_zoom_rect(start, end);
5236 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5237 drag_info.first_move = false;
5239 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5244 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5246 if (!drag_info.first_move) {
5247 drag_mouse_zoom (item, event);
5249 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5250 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
5252 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
5255 temporal_zoom_to_frame (false, drag_info.grab_frame);
5257 temporal_zoom_step (false);
5258 center_screen (drag_info.grab_frame);
5266 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
5268 double x1 = frame_to_pixel (start);
5269 double x2 = frame_to_pixel (end);
5270 double y2 = full_canvas_height - 1.0;
5272 zoom_rect->property_x1() = x1;
5273 zoom_rect->property_y1() = 1.0;
5274 zoom_rect->property_x2() = x2;
5275 zoom_rect->property_y2() = y2;
5279 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5281 drag_info.item = item;
5282 drag_info.motion_callback = &Editor::drag_rubberband_select;
5283 drag_info.finished_callback = &Editor::end_rubberband_select;
5285 start_grab (event, cross_hair_cursor);
5287 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5291 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5298 /* use a bigger drag threshold than the default */
5300 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
5304 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) {
5305 if (drag_info.first_move) {
5306 snap_to (drag_info.grab_frame);
5308 snap_to (drag_info.current_pointer_frame);
5311 /* base start and end on initial click position */
5313 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5314 start = drag_info.current_pointer_frame;
5315 end = drag_info.grab_frame;
5317 end = drag_info.current_pointer_frame;
5318 start = drag_info.grab_frame;
5321 if (drag_info.current_pointer_y < drag_info.grab_y) {
5322 y1 = drag_info.current_pointer_y;
5323 y2 = drag_info.grab_y;
5325 y2 = drag_info.current_pointer_y;
5326 y1 = drag_info.grab_y;
5330 if (start != end || y1 != y2) {
5332 double x1 = frame_to_pixel (start);
5333 double x2 = frame_to_pixel (end);
5335 rubberband_rect->property_x1() = x1;
5336 rubberband_rect->property_y1() = y1;
5337 rubberband_rect->property_x2() = x2;
5338 rubberband_rect->property_y2() = y2;
5340 rubberband_rect->show();
5341 rubberband_rect->raise_to_top();
5343 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5344 drag_info.first_move = false;
5346 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5351 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5353 if (!drag_info.first_move) {
5355 drag_rubberband_select (item, event);
5358 if (drag_info.current_pointer_y < drag_info.grab_y) {
5359 y1 = drag_info.current_pointer_y;
5360 y2 = drag_info.grab_y;
5362 y2 = drag_info.current_pointer_y;
5363 y1 = drag_info.grab_y;
5367 Selection::Operation op = Keyboard::selection_type (event->button.state);
5370 begin_reversible_command (_("rubberband selection"));
5372 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5373 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5375 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5379 commit_reversible_command ();
5383 selection->clear_tracks();
5384 selection->clear_regions();
5385 selection->clear_points ();
5386 selection->clear_lines ();
5389 rubberband_rect->hide();
5394 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5396 using namespace Gtkmm2ext;
5398 ArdourPrompter prompter (false);
5400 prompter.set_prompt (_("Name for region:"));
5401 prompter.set_initial_text (clicked_regionview->region()->name());
5402 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5403 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5404 prompter.show_all ();
5405 switch (prompter.run ()) {
5406 case Gtk::RESPONSE_ACCEPT:
5408 prompter.get_result(str);
5410 clicked_regionview->region()->set_name (str);
5418 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5420 drag_info.item = item;
5421 drag_info.motion_callback = &Editor::time_fx_motion;
5422 drag_info.finished_callback = &Editor::end_time_fx;
5426 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5430 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5432 RegionView* rv = clicked_regionview;
5434 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5435 snap_to (drag_info.current_pointer_frame);
5438 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5442 if (drag_info.current_pointer_frame > rv->region()->position()) {
5443 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5446 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5447 drag_info.first_move = false;
5449 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5453 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5455 clicked_regionview->get_time_axis_view().hide_timestretch ();
5457 if (drag_info.first_move) {
5461 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5462 /* backwards drag of the left edge - not usable */
5466 nframes64_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5467 #ifdef USE_RUBBERBAND
5468 float percentage = (float) ((double) newlen / (double) clicked_regionview->region()->length());
5470 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5473 begin_reversible_command (_("timestretch"));
5475 // XXX how do timeFX on multiple regions ?
5478 rs.add (clicked_regionview);
5480 if (time_stretch (rs, percentage) == 0) {
5481 session->commit_reversible_command ();
5486 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
5488 /* no brushing without a useful snap setting */
5491 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
5494 switch (snap_mode) {
5496 return; /* can't work because it allows region to be placed anywhere */
5501 switch (snap_type) {
5509 /* don't brush a copy over the original */
5511 if (pos == rv->region()->position()) {
5515 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
5517 if (atv == 0 || !atv->is_audio_track()) {
5521 boost::shared_ptr<Playlist> playlist = atv->playlist();
5522 double speed = atv->get_diskstream()->speed();
5524 XMLNode &before = playlist->get_state();
5525 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes64_t) (pos * speed));
5526 XMLNode &after = playlist->get_state();
5527 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5529 // playlist is frozen, so we have to update manually
5531 playlist->Modified(); /* EMIT SIGNAL */
5535 Editor::track_height_step_timeout ()
5537 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
5538 current_stepping_trackview = 0;