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 a grab so that if we finish after moving
706 we can tell what happened.
708 drag_info.item = item;
709 drag_info.motion_callback = &Editor::region_gain_motion_callback;
710 drag_info.finished_callback = 0;
711 start_grab (event, current_canvas_cursor);
714 case GainControlPointItem:
715 start_control_point_grab (item, event);
719 start_line_grab_from_line (item, event);
722 case GainAutomationControlPointItem:
723 case PanAutomationControlPointItem:
724 case RedirectAutomationControlPointItem:
725 start_control_point_grab (item, event);
736 case GainAutomationControlPointItem:
737 case PanAutomationControlPointItem:
738 case RedirectAutomationControlPointItem:
739 start_control_point_grab (item, event);
742 case GainAutomationLineItem:
743 case PanAutomationLineItem:
744 case RedirectAutomationLineItem:
745 start_line_grab_from_line (item, event);
749 // XXX need automation mode to identify which
751 // start_line_grab_from_regionview (item, event);
761 if (event->type == GDK_BUTTON_PRESS) {
762 start_mouse_zoom (item, event);
769 if (item_type == RegionItem) {
770 start_time_fx (item, event);
777 scrub_reverse_distance = 0;
778 last_scrub_x = event->button.x;
779 scrubbing_direction = 0;
780 track_canvas->get_window()->set_cursor (*transparent_cursor);
781 /* rest handled in motion & release */
790 switch (mouse_mode) {
792 if (event->type == GDK_BUTTON_PRESS) {
795 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
796 start_region_copy_grab (item, event);
798 start_region_grab (item, event);
803 case GainAutomationControlPointItem:
804 case PanAutomationControlPointItem:
805 case RedirectAutomationControlPointItem:
806 start_control_point_grab (item, event);
817 case RegionViewNameHighlight:
818 start_trim (item, event);
823 start_trim (clicked_regionview->get_name_highlight(), event);
834 if (event->type == GDK_BUTTON_PRESS) {
835 /* relax till release */
842 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
843 temporal_zoom_session();
845 temporal_zoom_to_frame (true, event_frame(event));
868 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
870 nframes64_t where = event_frame (event, 0, 0);
871 AutomationTimeAxisView* atv = 0;
873 /* no action if we're recording */
875 if (session && session->actively_recording()) {
879 /* first, see if we're finishing a drag ... */
881 if (drag_info.item) {
882 if (end_grab (item, event)) {
883 /* grab dragged, so do nothing else */
888 button_selection (item, event, item_type);
890 /* edit events get handled here */
892 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
898 case TempoMarkerItem:
899 edit_tempo_marker (item);
902 case MeterMarkerItem:
903 edit_meter_marker (item);
907 if (clicked_regionview->name_active()) {
908 return mouse_rename_region (item, event);
918 /* context menu events get handled here */
920 if (Keyboard::is_context_menu_event (&event->button)) {
922 if (drag_info.item == 0) {
924 /* no matter which button pops up the context menu, tell the menu
925 widget to use button 1 to drive menu selection.
930 case FadeInHandleItem:
932 case FadeOutHandleItem:
933 popup_fade_context_menu (1, event->button.time, item, item_type);
937 popup_track_context_menu (1, event->button.time, item_type, false, where);
941 case RegionViewNameHighlight:
943 popup_track_context_menu (1, event->button.time, item_type, false, where);
947 popup_track_context_menu (1, event->button.time, item_type, true, where);
950 case AutomationTrackItem:
951 popup_track_context_menu (1, event->button.time, item_type, false, where);
955 case RangeMarkerBarItem:
956 case TransportMarkerBarItem:
957 case CdMarkerBarItem:
960 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
964 marker_context_menu (&event->button, item);
967 case TempoMarkerItem:
968 tm_marker_context_menu (&event->button, item);
971 case MeterMarkerItem:
972 tm_marker_context_menu (&event->button, item);
975 case CrossfadeViewItem:
976 popup_track_context_menu (1, event->button.time, item_type, false, where);
979 /* <CMT Additions> */
981 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
983 case ImageFrameTimeAxisItem:
984 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
987 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
989 case MarkerTimeAxisItem:
990 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
992 /* <CMT Additions> */
1003 /* delete events get handled here */
1005 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
1007 switch (item_type) {
1008 case TempoMarkerItem:
1009 remove_tempo_marker (item);
1012 case MeterMarkerItem:
1013 remove_meter_marker (item);
1017 remove_marker (*item, event);
1021 if (mouse_mode == MouseObject) {
1022 remove_clicked_region ();
1026 case GainControlPointItem:
1027 if (mouse_mode == MouseGain) {
1028 remove_gain_control_point (item, event);
1032 case GainAutomationControlPointItem:
1033 case PanAutomationControlPointItem:
1034 case RedirectAutomationControlPointItem:
1035 remove_control_point (item, event);
1044 switch (event->button.button) {
1047 switch (item_type) {
1048 /* see comments in button_press_handler */
1049 case PlayheadCursorItem:
1052 case GainAutomationLineItem:
1053 case PanAutomationLineItem:
1054 case RedirectAutomationLineItem:
1055 case StartSelectionTrimItem:
1056 case EndSelectionTrimItem:
1060 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1061 snap_to (where, 0, true);
1063 mouse_add_new_marker (where);
1066 case CdMarkerBarItem:
1067 // if we get here then a dragged range wasn't done
1068 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1069 snap_to (where, 0, true);
1071 mouse_add_new_marker (where, true);
1075 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1078 mouse_add_new_tempo_event (where);
1082 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1090 switch (mouse_mode) {
1092 switch (item_type) {
1093 case AutomationTrackItem:
1094 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_trackview);
1096 atv->add_automation_event (item, event, where, event->button.y);
1108 // Gain only makes sense for audio regions
1110 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1114 switch (item_type) {
1116 /* check that we didn't drag before releasing, since
1117 its really annoying to create new control
1118 points when doing this.
1120 if (drag_info.first_move) {
1121 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1126 case AutomationTrackItem:
1127 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1128 add_automation_event (item, event, where, event->button.y);
1138 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1139 if (scrubbing_direction == 0) {
1140 /* no drag, just a click */
1141 switch (item_type) {
1143 play_selected_region ();
1149 /* make sure we stop */
1150 session->request_transport_speed (0.0);
1164 switch (mouse_mode) {
1167 switch (item_type) {
1169 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1171 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1174 // Button2 click is unused
1187 // x_style_paste (where, 1.0);
1207 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1213 if (last_item_entered != item) {
1214 last_item_entered = item;
1215 last_item_entered_n = 0;
1218 switch (item_type) {
1219 case GainControlPointItem:
1220 if (mouse_mode == MouseGain) {
1221 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1222 cp->set_visible (true);
1226 at_y = cp->get_y ();
1227 cp->item->i2w (at_x, at_y);
1231 fraction = 1.0 - (cp->get_y() / cp->line.height());
1233 if (is_drawable() && !_scrubbing) {
1234 track_canvas->get_window()->set_cursor (*fader_cursor);
1237 last_item_entered_n++;
1238 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1239 if (last_item_entered_n < 10) {
1240 show_verbose_canvas_cursor ();
1245 case GainAutomationControlPointItem:
1246 case PanAutomationControlPointItem:
1247 case RedirectAutomationControlPointItem:
1248 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1249 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1250 cp->set_visible (true);
1254 at_y = cp->get_y ();
1255 cp->item->i2w (at_x, at_y);
1259 fraction = 1.0 - (cp->get_y() / cp->line.height());
1261 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1262 show_verbose_canvas_cursor ();
1264 if (is_drawable()) {
1265 track_canvas->get_window()->set_cursor (*fader_cursor);
1271 if (mouse_mode == MouseGain) {
1272 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1274 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1275 if (is_drawable()) {
1276 track_canvas->get_window()->set_cursor (*fader_cursor);
1281 case GainAutomationLineItem:
1282 case RedirectAutomationLineItem:
1283 case PanAutomationLineItem:
1284 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1286 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1288 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1290 if (is_drawable()) {
1291 track_canvas->get_window()->set_cursor (*fader_cursor);
1296 case RegionViewNameHighlight:
1297 if (is_drawable() && mouse_mode == MouseObject) {
1298 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1302 case StartSelectionTrimItem:
1303 case EndSelectionTrimItem:
1304 /* <CMT Additions> */
1305 case ImageFrameHandleStartItem:
1306 case ImageFrameHandleEndItem:
1307 case MarkerViewHandleStartItem:
1308 case MarkerViewHandleEndItem:
1309 /* </CMT Additions> */
1311 if (is_drawable()) {
1312 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1316 case PlayheadCursorItem:
1317 if (is_drawable()) {
1318 switch (_edit_point) {
1320 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1323 track_canvas->get_window()->set_cursor (*grabber_cursor);
1329 case RegionViewName:
1331 /* when the name is not an active item, the entire name highlight is for trimming */
1333 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1334 if (mouse_mode == MouseObject && is_drawable()) {
1335 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1341 case AutomationTrackItem:
1342 if (is_drawable()) {
1343 Gdk::Cursor *cursor;
1344 switch (mouse_mode) {
1346 cursor = selector_cursor;
1349 cursor = zoom_cursor;
1352 cursor = cross_hair_cursor;
1356 track_canvas->get_window()->set_cursor (*cursor);
1358 AutomationTimeAxisView* atv;
1359 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1360 clear_entered_track = false;
1361 set_entered_track (atv);
1367 case RangeMarkerBarItem:
1368 case TransportMarkerBarItem:
1369 case CdMarkerBarItem:
1372 if (is_drawable()) {
1373 track_canvas->get_window()->set_cursor (*timebar_cursor);
1378 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1381 entered_marker = marker;
1382 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1384 case MeterMarkerItem:
1385 case TempoMarkerItem:
1386 if (is_drawable()) {
1387 track_canvas->get_window()->set_cursor (*timebar_cursor);
1390 case FadeInHandleItem:
1391 case FadeOutHandleItem:
1392 if (mouse_mode == MouseObject) {
1393 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1395 rect->property_fill_color_rgba() = 0;
1396 rect->property_outline_pixels() = 1;
1405 /* second pass to handle entered track status in a comprehensible way.
1408 switch (item_type) {
1410 case GainAutomationLineItem:
1411 case RedirectAutomationLineItem:
1412 case PanAutomationLineItem:
1413 case GainControlPointItem:
1414 case GainAutomationControlPointItem:
1415 case PanAutomationControlPointItem:
1416 case RedirectAutomationControlPointItem:
1417 /* these do not affect the current entered track state */
1418 clear_entered_track = false;
1421 case AutomationTrackItem:
1422 /* handled above already */
1426 set_entered_track (0);
1434 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1443 switch (item_type) {
1444 case GainControlPointItem:
1445 case GainAutomationControlPointItem:
1446 case PanAutomationControlPointItem:
1447 case RedirectAutomationControlPointItem:
1448 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1449 if (cp->line.npoints() > 1) {
1450 if (!cp->selected) {
1451 cp->set_visible (false);
1455 if (is_drawable()) {
1456 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1459 hide_verbose_canvas_cursor ();
1462 case RegionViewNameHighlight:
1463 case StartSelectionTrimItem:
1464 case EndSelectionTrimItem:
1465 case PlayheadCursorItem:
1466 /* <CMT Additions> */
1467 case ImageFrameHandleStartItem:
1468 case ImageFrameHandleEndItem:
1469 case MarkerViewHandleStartItem:
1470 case MarkerViewHandleEndItem:
1471 /* </CMT Additions> */
1472 if (is_drawable()) {
1473 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1478 case GainAutomationLineItem:
1479 case RedirectAutomationLineItem:
1480 case PanAutomationLineItem:
1481 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1483 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1485 line->property_fill_color_rgba() = al->get_line_color();
1487 if (is_drawable()) {
1488 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1492 case RegionViewName:
1493 /* see enter_handler() for notes */
1494 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1495 if (is_drawable() && mouse_mode == MouseObject) {
1496 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1501 case RangeMarkerBarItem:
1502 case TransportMarkerBarItem:
1503 case CdMarkerBarItem:
1507 if (is_drawable()) {
1508 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1513 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1517 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1518 location_flags_changed (loc, this);
1521 case MeterMarkerItem:
1522 case TempoMarkerItem:
1524 if (is_drawable()) {
1525 track_canvas->get_window()->set_cursor (*timebar_cursor);
1530 case FadeInHandleItem:
1531 case FadeOutHandleItem:
1532 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1534 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1536 rect->property_fill_color_rgba() = rv->get_fill_color();
1537 rect->property_outline_pixels() = 0;
1542 case AutomationTrackItem:
1543 if (is_drawable()) {
1544 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1545 clear_entered_track = true;
1546 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1558 Editor::left_automation_track ()
1560 if (clear_entered_track) {
1561 set_entered_track (0);
1562 clear_entered_track = false;
1572 if (scrubbing_direction == 0) {
1574 session->request_locate (drag_info.current_pointer_frame, false);
1575 session->request_transport_speed (0.1);
1576 scrubbing_direction = 1;
1580 if (last_scrub_x > drag_info.current_pointer_x) {
1582 /* pointer moved to the left */
1584 if (scrubbing_direction > 0) {
1586 /* we reversed direction to go backwards */
1589 scrub_reverse_distance += (int) (last_scrub_x - drag_info.current_pointer_x);
1593 /* still moving to the left (backwards) */
1595 scrub_reversals = 0;
1596 scrub_reverse_distance = 0;
1598 delta = 0.01 * (last_scrub_x - drag_info.current_pointer_x);
1599 session->request_transport_speed (session->transport_speed() - delta);
1603 /* pointer moved to the right */
1605 if (scrubbing_direction < 0) {
1606 /* we reversed direction to go forward */
1609 scrub_reverse_distance += (int) (drag_info.current_pointer_x - last_scrub_x);
1612 /* still moving to the right */
1614 scrub_reversals = 0;
1615 scrub_reverse_distance = 0;
1617 delta = 0.01 * (drag_info.current_pointer_x - last_scrub_x);
1618 session->request_transport_speed (session->transport_speed() + delta);
1622 /* if there have been more than 2 opposite motion moves detected, or one that moves
1623 back more than 10 pixels, reverse direction
1626 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1628 if (scrubbing_direction > 0) {
1629 /* was forwards, go backwards */
1630 session->request_transport_speed (-0.1);
1631 scrubbing_direction = -1;
1633 /* was backwards, go forwards */
1634 session->request_transport_speed (0.1);
1635 scrubbing_direction = 1;
1638 scrub_reverse_distance = 0;
1639 scrub_reversals = 0;
1643 last_scrub_x = drag_info.current_pointer_x;
1647 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1649 if (event->motion.is_hint) {
1652 /* We call this so that MOTION_NOTIFY events continue to be
1653 delivered to the canvas. We need to do this because we set
1654 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1655 the density of the events, at the expense of a round-trip
1656 to the server. Given that this will mostly occur on cases
1657 where DISPLAY = :0.0, and given the cost of what the motion
1658 event might do, its a good tradeoff.
1661 track_canvas->get_pointer (x, y);
1664 if (current_stepping_trackview) {
1665 /* don't keep the persistent stepped trackview if the mouse moves */
1666 current_stepping_trackview = 0;
1667 step_timeout.disconnect ();
1670 if (session && session->actively_recording()) {
1671 /* Sorry. no dragging stuff around while we record */
1675 drag_info.item_type = item_type;
1676 drag_info.last_pointer_x = drag_info.current_pointer_x;
1677 drag_info.last_pointer_y = drag_info.current_pointer_y;
1678 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1679 &drag_info.current_pointer_y);
1682 switch (mouse_mode) {
1693 if (!from_autoscroll && drag_info.item) {
1694 /* item != 0 is the best test i can think of for dragging.
1696 if (!drag_info.move_threshold_passed) {
1698 bool x_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1699 bool y_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1701 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1703 // and change the initial grab loc/frame if this drag info wants us to
1705 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1706 drag_info.grab_frame = drag_info.current_pointer_frame;
1707 drag_info.grab_x = drag_info.current_pointer_x;
1708 drag_info.grab_y = drag_info.current_pointer_y;
1709 drag_info.last_pointer_frame = drag_info.grab_frame;
1710 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1715 switch (item_type) {
1716 case PlayheadCursorItem:
1718 case RangeMarkerBarItem:
1719 case TransportMarkerBarItem:
1720 case CdMarkerBarItem:
1721 case GainControlPointItem:
1722 case RedirectAutomationControlPointItem:
1723 case GainAutomationControlPointItem:
1724 case PanAutomationControlPointItem:
1725 case TempoMarkerItem:
1726 case MeterMarkerItem:
1727 case RegionViewNameHighlight:
1728 case StartSelectionTrimItem:
1729 case EndSelectionTrimItem:
1732 case RedirectAutomationLineItem:
1733 case GainAutomationLineItem:
1734 case PanAutomationLineItem:
1735 case FadeInHandleItem:
1736 case FadeOutHandleItem:
1737 /* <CMT Additions> */
1738 case ImageFrameHandleStartItem:
1739 case ImageFrameHandleEndItem:
1740 case MarkerViewHandleStartItem:
1741 case MarkerViewHandleEndItem:
1742 /* </CMT Additions> */
1743 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1744 (event->motion.state & Gdk::BUTTON2_MASK))) {
1745 if (!from_autoscroll) {
1746 maybe_autoscroll_horizontally (&event->motion);
1748 if (drag_info.motion_callback) {
1749 (this->*(drag_info.motion_callback)) (item, event);
1759 switch (mouse_mode) {
1761 if (item_type == RegionItem) {
1762 if (drag_info.item && drag_info.motion_callback) {
1763 (this->*(drag_info.motion_callback)) (item, event);
1773 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1774 (event->motion.state & GDK_BUTTON2_MASK))) {
1775 if (!from_autoscroll) {
1776 maybe_autoscroll (&event->motion);
1778 if (drag_info.motion_callback) {
1779 (this->*(drag_info.motion_callback)) (item, event);
1791 track_canvas_motion (event);
1792 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1800 Editor::break_drag ()
1802 stop_canvas_autoscroll ();
1803 hide_verbose_canvas_cursor ();
1805 if (drag_info.item) {
1806 drag_info.item->ungrab (0);
1808 /* put it back where it came from */
1813 drag_info.item->i2w (cxw, cyw);
1814 drag_info.item->move (drag_info.original_x - cxw, drag_info.original_y - cyw);
1821 Editor::finalize_drag ()
1824 drag_info.copy = false;
1825 drag_info.motion_callback = 0;
1826 drag_info.finished_callback = 0;
1827 drag_info.dest_trackview = 0;
1828 drag_info.source_trackview = 0;
1829 drag_info.last_frame_position = 0;
1830 drag_info.grab_frame = 0;
1831 drag_info.last_pointer_frame = 0;
1832 drag_info.current_pointer_frame = 0;
1833 drag_info.brushing = false;
1834 range_marker_drag_rect->hide();
1835 drag_info.clear_copied_locations ();
1839 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1841 if (drag_info.item == 0) {
1842 fatal << _("programming error: start_grab called without drag item") << endmsg;
1848 cursor = which_grabber_cursor ();
1851 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1853 if (event->button.button == 2) {
1854 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
1855 drag_info.y_constrained = true;
1856 drag_info.x_constrained = false;
1858 drag_info.y_constrained = false;
1859 drag_info.x_constrained = true;
1862 drag_info.x_constrained = false;
1863 drag_info.y_constrained = false;
1866 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1867 drag_info.last_pointer_frame = drag_info.grab_frame;
1868 drag_info.current_pointer_frame = drag_info.grab_frame;
1869 drag_info.current_pointer_x = drag_info.grab_x;
1870 drag_info.current_pointer_y = drag_info.grab_y;
1871 drag_info.last_pointer_x = drag_info.current_pointer_x;
1872 drag_info.last_pointer_y = drag_info.current_pointer_y;
1873 drag_info.cumulative_x_drag = 0;
1874 drag_info.cumulative_y_drag = 0;
1875 drag_info.first_move = true;
1876 drag_info.move_threshold_passed = false;
1877 drag_info.want_move_threshold = false;
1878 drag_info.pointer_frame_offset = 0;
1879 drag_info.brushing = false;
1880 drag_info.clear_copied_locations ();
1882 drag_info.original_x = 0;
1883 drag_info.original_y = 0;
1884 drag_info.item->i2w (drag_info.original_x, drag_info.original_y);
1886 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1888 event->button.time);
1890 if (session && session->transport_rolling()) {
1891 drag_info.was_rolling = true;
1893 drag_info.was_rolling = false;
1896 switch (snap_type) {
1897 case SnapToRegionStart:
1898 case SnapToRegionEnd:
1899 case SnapToRegionSync:
1900 case SnapToRegionBoundary:
1901 build_region_boundary_cache ();
1909 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1911 drag_info.item->ungrab (0);
1912 drag_info.item = new_item;
1915 cursor = which_grabber_cursor ();
1918 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1922 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1924 bool did_drag = false;
1926 stop_canvas_autoscroll ();
1928 if (drag_info.item == 0) {
1932 drag_info.item->ungrab (event->button.time);
1934 if (drag_info.finished_callback) {
1935 drag_info.last_pointer_x = drag_info.current_pointer_x;
1936 drag_info.last_pointer_y = drag_info.current_pointer_y;
1937 (this->*(drag_info.finished_callback)) (item, event);
1940 did_drag = !drag_info.first_move;
1942 hide_verbose_canvas_cursor();
1950 Editor::region_gain_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1952 if (drag_info.first_move && drag_info.move_threshold_passed) {
1953 drag_info.first_move = false;
1958 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1960 drag_info.item = item;
1961 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1962 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1966 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1967 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1971 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1973 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes64_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1977 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1979 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1981 nframes64_t fade_length;
1983 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1984 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1990 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1994 if (pos < (arv->region()->position() + 64)) {
1995 fade_length = 64; // this should be a minimum defined somewhere
1996 } else if (pos > arv->region()->last_frame()) {
1997 fade_length = arv->region()->length();
1999 fade_length = pos - arv->region()->position();
2001 /* mapover the region selection */
2003 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2005 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2011 tmp->reset_fade_in_shape_width (fade_length);
2014 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
2016 drag_info.first_move = false;
2020 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2022 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2024 nframes64_t fade_length;
2026 if (drag_info.first_move) return;
2028 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2029 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2034 if (pos < (arv->region()->position() + 64)) {
2035 fade_length = 64; // this should be a minimum defined somewhere
2036 } else if (pos > arv->region()->last_frame()) {
2037 fade_length = arv->region()->length();
2039 fade_length = pos - arv->region()->position();
2042 begin_reversible_command (_("change fade in length"));
2044 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2046 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2052 AutomationList& alist = tmp->audio_region()->fade_in();
2053 XMLNode &before = alist.get_state();
2055 tmp->audio_region()->set_fade_in_length (fade_length);
2056 tmp->audio_region()->set_fade_in_active (true);
2058 XMLNode &after = alist.get_state();
2059 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
2062 commit_reversible_command ();
2066 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
2068 drag_info.item = item;
2069 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
2070 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
2074 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
2075 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
2079 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2081 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes64_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
2085 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2087 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2089 nframes64_t fade_length;
2091 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2092 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2097 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2101 if (pos > (arv->region()->last_frame() - 64)) {
2102 fade_length = 64; // this should really be a minimum fade defined somewhere
2104 else if (pos < arv->region()->position()) {
2105 fade_length = arv->region()->length();
2108 fade_length = arv->region()->last_frame() - pos;
2111 /* mapover the region selection */
2113 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2115 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2121 tmp->reset_fade_out_shape_width (fade_length);
2124 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
2126 drag_info.first_move = false;
2130 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2132 if (drag_info.first_move) return;
2134 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2136 nframes64_t fade_length;
2138 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2139 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2145 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2149 if (pos > (arv->region()->last_frame() - 64)) {
2150 fade_length = 64; // this should really be a minimum fade defined somewhere
2152 else if (pos < arv->region()->position()) {
2153 fade_length = arv->region()->length();
2156 fade_length = arv->region()->last_frame() - pos;
2159 begin_reversible_command (_("change fade out length"));
2161 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2163 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2169 AutomationList& alist = tmp->audio_region()->fade_out();
2170 XMLNode &before = alist.get_state();
2172 tmp->audio_region()->set_fade_out_length (fade_length);
2173 tmp->audio_region()->set_fade_out_active (true);
2175 XMLNode &after = alist.get_state();
2176 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
2179 commit_reversible_command ();
2183 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2185 drag_info.item = item;
2186 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2187 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2191 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2192 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2196 Cursor* cursor = (Cursor *) drag_info.data;
2198 if (cursor == playhead_cursor) {
2199 _dragging_playhead = true;
2201 if (session && drag_info.was_rolling) {
2202 session->request_stop ();
2205 if (session && session->is_auditioning()) {
2206 session->cancel_audition ();
2210 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2212 show_verbose_time_cursor (cursor->current_frame, 10);
2216 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2218 Cursor* cursor = (Cursor *) drag_info.data;
2219 nframes64_t adjusted_frame;
2221 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2222 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2228 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2229 if (cursor == playhead_cursor) {
2230 snap_to (adjusted_frame);
2234 if (adjusted_frame == drag_info.last_pointer_frame) return;
2236 cursor->set_position (adjusted_frame);
2238 show_verbose_time_cursor (cursor->current_frame, 10);
2241 track_canvas->update_now ();
2243 UpdateAllTransportClocks (cursor->current_frame);
2245 drag_info.last_pointer_frame = adjusted_frame;
2246 drag_info.first_move = false;
2250 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2252 if (drag_info.first_move) return;
2254 cursor_drag_motion_callback (item, event);
2256 _dragging_playhead = false;
2258 if (item == &playhead_cursor->canvas_item) {
2260 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2266 Editor::update_marker_drag_item (Location *location)
2268 double x1 = frame_to_pixel (location->start());
2269 double x2 = frame_to_pixel (location->end());
2271 if (location->is_mark()) {
2272 marker_drag_line_points.front().set_x(x1);
2273 marker_drag_line_points.back().set_x(x1);
2274 marker_drag_line->property_points() = marker_drag_line_points;
2276 range_marker_drag_rect->property_x1() = x1;
2277 range_marker_drag_rect->property_x2() = x2;
2282 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2286 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2287 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2293 Location *location = find_location_from_marker (marker, is_start);
2295 drag_info.item = item;
2296 drag_info.data = marker;
2297 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2298 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2302 _dragging_edit_point = true;
2304 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2306 update_marker_drag_item (location);
2308 if (location->is_mark()) {
2309 // marker_drag_line->show();
2310 // marker_drag_line->raise_to_top();
2312 range_marker_drag_rect->show();
2313 //range_marker_drag_rect->raise_to_top();
2317 show_verbose_time_cursor (location->start(), 10);
2319 show_verbose_time_cursor (location->end(), 10);
2322 Selection::Operation op = Keyboard::selection_type (event->button.state);
2325 case Selection::Toggle:
2326 selection->toggle (marker);
2328 case Selection::Set:
2329 if (!selection->selected (marker)) {
2330 selection->set (marker);
2333 case Selection::Extend:
2335 Locations::LocationList ll;
2336 list<Marker*> to_add;
2338 selection->markers.range (s, e);
2339 s = min (marker->position(), s);
2340 e = max (marker->position(), e);
2343 if (e < max_frames) {
2346 session->locations()->find_all_between (s, e, ll, Location::Flags (0));
2347 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2348 LocationMarkers* lm = find_location_markers (*i);
2351 to_add.push_back (lm->start);
2354 to_add.push_back (lm->end);
2358 if (!to_add.empty()) {
2359 selection->add (to_add);
2363 case Selection::Add:
2364 selection->add (marker);
2368 /* set up copies for us to manipulate during the drag */
2370 drag_info.clear_copied_locations ();
2372 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
2373 Location *l = find_location_from_marker (*i, is_start);
2374 drag_info.copied_locations.push_back (new Location (*l));
2379 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2381 nframes64_t f_delta = 0;
2382 nframes64_t newframe;
2384 bool move_both = false;
2385 Marker* dragged_marker = (Marker*) drag_info.data;
2387 Location *real_location;
2388 Location *copy_location;
2390 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2391 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2396 nframes64_t next = newframe;
2398 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2399 snap_to (newframe, 0, true);
2402 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2406 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2410 MarkerSelection::iterator i;
2411 list<Location*>::iterator x;
2413 /* find the marker we're dragging, and compute the delta */
2415 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2416 x != drag_info.copied_locations.end() && i != selection->markers.end();
2422 if (marker == dragged_marker) {
2424 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2429 if (real_location->is_mark()) {
2430 f_delta = newframe - copy_location->start();
2434 switch (marker->type()) {
2436 case Marker::LoopStart:
2437 case Marker::PunchIn:
2438 f_delta = newframe - copy_location->start();
2442 case Marker::LoopEnd:
2443 case Marker::PunchOut:
2444 f_delta = newframe - copy_location->end();
2447 /* what kind of marker is this ? */
2455 if (i == selection->markers.end()) {
2456 /* hmm, impossible - we didn't find the dragged marker */
2460 /* now move them all */
2462 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2463 x != drag_info.copied_locations.end() && i != selection->markers.end();
2469 /* call this to find out if its the start or end */
2471 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2475 if (real_location->locked()) {
2479 if (copy_location->is_mark()) {
2483 copy_location->set_start (copy_location->start() + f_delta);
2487 nframes64_t new_start = copy_location->start() + f_delta;
2488 nframes64_t new_end = copy_location->end() + f_delta;
2490 if (is_start) { // start-of-range marker
2493 copy_location->set_start (new_start);
2494 copy_location->set_end (new_end);
2495 } else if (new_start < copy_location->end()) {
2496 copy_location->set_start (new_start);
2498 snap_to (next, 1, true);
2499 copy_location->set_end (next);
2500 copy_location->set_start (newframe);
2503 } else { // end marker
2506 copy_location->set_end (new_end);
2507 copy_location->set_start (new_start);
2508 } else if (new_end > copy_location->start()) {
2509 copy_location->set_end (new_end);
2510 } else if (newframe > 0) {
2511 snap_to (next, -1, true);
2512 copy_location->set_start (next);
2513 copy_location->set_end (newframe);
2517 update_marker_drag_item (copy_location);
2519 LocationMarkers* lm = find_location_markers (real_location);
2522 lm->set_position (copy_location->start(), copy_location->end());
2526 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2527 drag_info.first_move = false;
2529 if (drag_info.copied_locations.empty()) {
2533 edit_point_clock.set (drag_info.copied_locations.front()->start());
2534 show_verbose_time_cursor (newframe, 10);
2537 track_canvas->update_now ();
2542 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2544 if (drag_info.first_move) {
2546 /* just a click, do nothing but finish
2547 off the selection process
2550 Selection::Operation op = Keyboard::selection_type (event->button.state);
2551 Marker* marker = (Marker *) drag_info.data;
2554 case Selection::Set:
2555 if (selection->selected (marker) && selection->markers.size() > 1) {
2556 selection->set (marker);
2560 case Selection::Toggle:
2561 case Selection::Extend:
2562 case Selection::Add:
2569 _dragging_edit_point = false;
2572 begin_reversible_command ( _("move marker") );
2573 XMLNode &before = session->locations()->get_state();
2575 MarkerSelection::iterator i;
2576 list<Location*>::iterator x;
2579 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2580 x != drag_info.copied_locations.end() && i != selection->markers.end();
2583 Location * location = find_location_from_marker ((*i), is_start);
2587 if (location->locked()) {
2591 if (location->is_mark()) {
2592 location->set_start ((*x)->start());
2594 location->set ((*x)->start(), (*x)->end());
2599 XMLNode &after = session->locations()->get_state();
2600 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2601 commit_reversible_command ();
2603 marker_drag_line->hide();
2604 range_marker_drag_rect->hide();
2608 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2611 MeterMarker* meter_marker;
2613 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2614 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2618 meter_marker = dynamic_cast<MeterMarker*> (marker);
2620 MetricSection& section (meter_marker->meter());
2622 if (!section.movable()) {
2626 drag_info.item = item;
2627 drag_info.copy = false;
2628 drag_info.data = 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::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2643 MeterMarker* meter_marker;
2645 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2646 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2650 meter_marker = dynamic_cast<MeterMarker*> (marker);
2652 // create a dummy marker for visual representation of moving the copy.
2653 // The actual copying is not done before we reach the finish callback.
2655 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2656 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2657 *new MeterSection(meter_marker->meter()));
2659 drag_info.item = &new_marker->the_item();
2660 drag_info.copy = true;
2661 drag_info.data = new_marker;
2662 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2663 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2667 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2669 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2673 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2675 MeterMarker* marker = (MeterMarker *) drag_info.data;
2676 nframes64_t adjusted_frame;
2678 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2679 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2685 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2686 snap_to (adjusted_frame);
2689 if (adjusted_frame == drag_info.last_pointer_frame) return;
2691 marker->set_position (adjusted_frame);
2694 drag_info.last_pointer_frame = adjusted_frame;
2695 drag_info.first_move = false;
2697 show_verbose_time_cursor (adjusted_frame, 10);
2701 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2703 if (drag_info.first_move) return;
2705 meter_marker_drag_motion_callback (drag_info.item, event);
2707 MeterMarker* marker = (MeterMarker *) drag_info.data;
2710 TempoMap& map (session->tempo_map());
2711 map.bbt_time (drag_info.last_pointer_frame, when);
2713 if (drag_info.copy == true) {
2714 begin_reversible_command (_("copy meter mark"));
2715 XMLNode &before = map.get_state();
2716 map.add_meter (marker->meter(), when);
2717 XMLNode &after = map.get_state();
2718 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2719 commit_reversible_command ();
2721 // delete the dummy marker we used for visual representation of copying.
2722 // a new visual marker will show up automatically.
2725 begin_reversible_command (_("move meter mark"));
2726 XMLNode &before = map.get_state();
2727 map.move_meter (marker->meter(), when);
2728 XMLNode &after = map.get_state();
2729 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2730 commit_reversible_command ();
2735 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2738 TempoMarker* tempo_marker;
2740 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2741 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2745 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2746 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2750 MetricSection& section (tempo_marker->tempo());
2752 if (!section.movable()) {
2756 drag_info.item = item;
2757 drag_info.copy = false;
2758 drag_info.data = marker;
2759 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2760 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2764 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2765 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2769 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2772 TempoMarker* tempo_marker;
2774 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2775 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2779 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2780 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2784 // create a dummy marker for visual representation of moving the copy.
2785 // The actual copying is not done before we reach the finish callback.
2787 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2788 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2789 *new TempoSection(tempo_marker->tempo()));
2791 drag_info.item = &new_marker->the_item();
2792 drag_info.copy = true;
2793 drag_info.data = new_marker;
2794 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2795 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2799 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2801 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2805 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2807 TempoMarker* marker = (TempoMarker *) drag_info.data;
2808 nframes64_t adjusted_frame;
2810 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2811 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2817 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2818 snap_to (adjusted_frame);
2821 if (adjusted_frame == drag_info.last_pointer_frame) return;
2823 /* OK, we've moved far enough to make it worth actually move the thing. */
2825 marker->set_position (adjusted_frame);
2827 show_verbose_time_cursor (adjusted_frame, 10);
2829 drag_info.last_pointer_frame = adjusted_frame;
2830 drag_info.first_move = false;
2834 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2836 if (drag_info.first_move) return;
2838 tempo_marker_drag_motion_callback (drag_info.item, event);
2840 TempoMarker* marker = (TempoMarker *) drag_info.data;
2843 TempoMap& map (session->tempo_map());
2844 map.bbt_time (drag_info.last_pointer_frame, when);
2846 if (drag_info.copy == true) {
2847 begin_reversible_command (_("copy tempo mark"));
2848 XMLNode &before = map.get_state();
2849 map.add_tempo (marker->tempo(), when);
2850 XMLNode &after = map.get_state();
2851 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2852 commit_reversible_command ();
2854 // delete the dummy marker we used for visual representation of copying.
2855 // a new visual marker will show up automatically.
2858 begin_reversible_command (_("move tempo mark"));
2859 XMLNode &before = map.get_state();
2860 map.move_tempo (marker->tempo(), when);
2861 XMLNode &after = map.get_state();
2862 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2863 commit_reversible_command ();
2868 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2870 ControlPoint* control_point;
2872 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2873 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2877 // We shouldn't remove the first or last gain point
2878 if (control_point->line.is_last_point(*control_point) ||
2879 control_point->line.is_first_point(*control_point)) {
2883 control_point->line.remove_point (*control_point);
2887 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2889 ControlPoint* control_point;
2891 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2892 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2896 control_point->line.remove_point (*control_point);
2900 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2902 ControlPoint* control_point;
2904 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2905 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2909 drag_info.item = item;
2910 drag_info.data = control_point;
2911 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2912 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2914 start_grab (event, fader_cursor);
2916 // start the grab at the center of the control point so
2917 // the point doesn't 'jump' to the mouse after the first drag
2918 drag_info.grab_x = control_point->get_x();
2919 drag_info.grab_y = control_point->get_y();
2920 control_point->line.parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
2921 track_canvas->w2c(drag_info.grab_x, drag_info.grab_y, drag_info.grab_x, drag_info.grab_y);
2923 drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
2925 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2927 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2928 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2929 drag_info.current_pointer_x + 10, drag_info.current_pointer_y + 10);
2931 show_verbose_canvas_cursor ();
2935 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2937 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2939 double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
2940 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2942 if (event->button.state & Keyboard::SecondaryModifier) {
2947 double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
2948 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2950 // calculate zero crossing point. back off by .01 to stay on the
2951 // positive side of zero
2953 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line.height() - .01;
2954 cp->line.parent_group().i2w(_unused, zero_gain_y);
2956 // make sure we hit zero when passing through
2957 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2958 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2962 if (drag_info.x_constrained) {
2963 cx = drag_info.grab_x;
2965 if (drag_info.y_constrained) {
2966 cy = drag_info.grab_y;
2969 drag_info.cumulative_x_drag = cx - drag_info.grab_x;
2970 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2972 cp->line.parent_group().w2i (cx, cy);
2976 cy = min ((double) cp->line.height(), cy);
2978 //translate cx to frames
2979 nframes64_t cx_frames = unit_to_frame (cx);
2981 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2982 snap_to (cx_frames);
2985 float fraction = 1.0 - (cy / cp->line.height());
2989 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2995 cp->line.point_drag (*cp, cx_frames , fraction, push);
2997 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2999 drag_info.first_move = false;
3003 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3005 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
3007 if (drag_info.first_move) {
3011 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3012 reset_point_selection ();
3016 control_point_drag_motion_callback (item, event);
3018 cp->line.end_drag (cp);
3022 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
3024 switch (mouse_mode) {
3026 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
3027 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
3035 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
3039 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
3040 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
3044 start_line_grab (al, event);
3048 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
3052 nframes64_t frame_within_region;
3054 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3055 origin, and ditto for y.
3058 cx = event->button.x;
3059 cy = event->button.y;
3061 line->parent_group().w2i (cx, cy);
3063 frame_within_region = (nframes64_t) floor (cx * frames_per_unit);
3065 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
3066 current_line_drag_info.after)) {
3067 /* no adjacent points */
3071 drag_info.item = &line->grab_item();
3072 drag_info.data = line;
3073 drag_info.motion_callback = &Editor::line_drag_motion_callback;
3074 drag_info.finished_callback = &Editor::line_drag_finished_callback;
3076 start_grab (event, fader_cursor);
3078 /* store grab start in parent frame */
3080 drag_info.grab_x = cx;
3081 drag_info.grab_y = cy;
3083 double fraction = 1.0 - (cy / line->height());
3085 line->start_drag (0, drag_info.grab_frame, fraction);
3087 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
3088 drag_info.current_pointer_x + 10, drag_info.current_pointer_y + 10);
3089 show_verbose_canvas_cursor ();
3093 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3095 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3097 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
3099 if (event->button.state & Keyboard::SecondaryModifier) {
3103 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
3105 // calculate zero crossing point. back off by .01 to stay on the
3106 // positive side of zero
3107 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
3109 // line->parent_group().i2w(_unused, zero_gain_y);
3111 // make sure we hit zero when passing through
3112 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
3113 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
3117 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
3120 cy = min ((double) line->height(), cy);
3123 double fraction = 1.0 - (cy / line->height());
3127 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3133 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
3135 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
3139 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3141 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3142 line_drag_motion_callback (item, event);
3147 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3149 if (selection->regions.empty() || clicked_regionview == 0) {
3153 drag_info.copy = false;
3154 drag_info.item = item;
3155 drag_info.data = clicked_regionview;
3157 if (Config->get_edit_mode() == Splice) {
3158 drag_info.motion_callback = &Editor::region_drag_splice_motion_callback;
3159 drag_info.finished_callback = &Editor::region_drag_splice_finished_callback;
3161 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3162 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3168 TimeAxisView* tvp = clicked_trackview;
3169 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3171 if (tv && tv->is_audio_track()) {
3172 speed = tv->get_diskstream()->speed();
3175 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3176 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3177 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3178 drag_info.dest_trackview = drag_info.source_trackview;
3179 // we want a move threshold
3180 drag_info.want_move_threshold = true;
3182 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3184 begin_reversible_command (_("move region(s)"));
3186 _region_motion_group->raise_to_top ();
3188 /* sync the canvas to what we think is its current state */
3189 track_canvas->update_now();
3193 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
3195 if (selection->regions.empty() || clicked_regionview == 0) {
3199 drag_info.copy = true;
3200 drag_info.item = item;
3201 drag_info.data = clicked_regionview;
3205 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
3206 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
3209 if (atv && atv->is_audio_track()) {
3210 speed = atv->get_diskstream()->speed();
3213 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3214 drag_info.dest_trackview = drag_info.source_trackview;
3215 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3216 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3217 // we want a move threshold
3218 drag_info.want_move_threshold = true;
3219 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3220 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3221 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3222 _region_motion_group->raise_to_top ();
3226 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
3228 if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) {
3232 drag_info.copy = false;
3233 drag_info.item = item;
3234 drag_info.data = clicked_regionview;
3235 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3236 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3241 TimeAxisView* tvp = clicked_trackview;
3242 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3244 if (tv && tv->is_audio_track()) {
3245 speed = tv->get_diskstream()->speed();
3248 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3249 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3250 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3251 drag_info.dest_trackview = drag_info.source_trackview;
3252 // we want a move threshold
3253 drag_info.want_move_threshold = true;
3254 drag_info.brushing = true;
3256 begin_reversible_command (_("Drag region brush"));
3260 Editor::possibly_copy_regions_during_grab (GdkEvent* event)
3262 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
3264 drag_info.want_move_threshold = false; // don't copy again
3266 /* duplicate the regionview(s) and region(s) */
3268 vector<RegionView*> new_regionviews;
3270 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3272 AudioRegionView* arv;
3274 if ((arv = dynamic_cast<AudioRegionView*>(*i)) == 0) {
3275 /* XXX handle MIDI here */
3279 const boost::shared_ptr<const Region> original = arv->region();
3280 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
3281 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (region_copy);
3283 nrv = new AudioRegionView (*arv, ar);
3284 nrv->get_canvas_group()->show ();
3286 new_regionviews.push_back (nrv);
3289 if (new_regionviews.empty()) {
3293 /* reset selection to new regionviews. This will not set selection visual status for
3294 these regionviews since they don't belong to a track, so do that by hand too.
3297 selection->set (new_regionviews);
3299 for (vector<RegionView*>::iterator i = new_regionviews.begin(); i != new_regionviews.end(); ++i) {
3300 (*i)->set_selected (true);
3303 /* reset drag_info data to reflect the fact that we are dragging the copies */
3305 drag_info.data = new_regionviews.front();
3307 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3309 sync the canvas to what we think is its current state
3310 without it, the canvas seems to
3311 "forget" to update properly after the upcoming reparent()
3312 ..only if the mouse is in rapid motion at the time of the grab.
3313 something to do with regionview creation raking so long?
3315 track_canvas->update_now();
3320 Editor::check_region_drag_possible (AudioTimeAxisView** tv)
3322 /* Which trackview is this ? */
3324 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
3325 (*tv) = dynamic_cast<AudioTimeAxisView*>(tvp);
3327 /* The region motion is only processed if the pointer is over
3331 if (!(*tv) || !(*tv)->is_audio_track()) {
3332 /* To make sure we hide the verbose canvas cursor when the mouse is
3333 not held over and audiotrack.
3335 hide_verbose_canvas_cursor ();
3342 struct RegionSelectionByPosition {
3343 bool operator() (RegionView*a, RegionView* b) {
3344 return a->region()->position () < b->region()->position();
3349 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3351 AudioTimeAxisView* tv;
3353 if (!check_region_drag_possible (&tv)) {
3357 if (!drag_info.move_threshold_passed) {
3363 if (drag_info.current_pointer_x - drag_info.grab_x > 0) {
3369 RegionSelection copy (selection->regions);
3371 RegionSelectionByPosition cmp;
3374 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
3376 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(*i)->get_time_axis_view());
3382 boost::shared_ptr<Playlist> playlist;
3384 if ((playlist = atv->playlist()) == 0) {
3388 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
3393 if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) {
3397 if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) {
3403 playlist->shuffle ((*i)->region(), dir);
3405 drag_info.grab_x = drag_info.current_pointer_x;
3410 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3415 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3419 nframes64_t pending_region_position = 0;
3420 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
3421 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
3422 bool clamp_y_axis = false;
3423 vector<int32_t> height_list(512) ;
3424 vector<int32_t>::iterator j;
3425 AudioTimeAxisView* tv;
3427 possibly_copy_regions_during_grab (event);
3429 if (!check_region_drag_possible (&tv)) {
3433 original_pointer_order = drag_info.dest_trackview->order;
3435 /************************************************************
3437 ************************************************************/
3439 if (drag_info.brushing) {
3440 clamp_y_axis = true;
3445 if ((pointer_y_span = (drag_info.dest_trackview->order - tv->order)) != 0) {
3447 int32_t children = 0, numtracks = 0;
3448 // XXX hard coding track limit, oh my, so very very bad
3449 bitset <1024> tracks (0x00);
3450 /* get a bitmask representing the visible tracks */
3452 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3453 TimeAxisView *tracklist_timeview;
3454 tracklist_timeview = (*i);
3455 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
3456 list<TimeAxisView*> children_list;
3458 /* zeroes are audio tracks. ones are other types. */
3460 if (!atv2->hidden()) {
3462 if (visible_y_high < atv2->order) {
3463 visible_y_high = atv2->order;
3465 if (visible_y_low > atv2->order) {
3466 visible_y_low = atv2->order;
3469 if (!atv2->is_audio_track()) {
3470 tracks = tracks |= (0x01 << atv2->order);
3473 height_list[atv2->order] = (*i)->current_height();
3475 if ((children_list = atv2->get_child_list()).size() > 0) {
3476 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
3477 tracks = tracks |= (0x01 << (atv2->order + children));
3478 height_list[atv2->order + children] = (*j)->current_height();
3486 /* find the actual span according to the canvas */
3488 canvas_pointer_y_span = pointer_y_span;
3489 if (drag_info.dest_trackview->order >= tv->order) {
3491 for (y = tv->order; y < drag_info.dest_trackview->order; y++) {
3492 if (height_list[y] == 0 ) {
3493 canvas_pointer_y_span--;
3498 for (y = drag_info.dest_trackview->order;y <= tv->order; y++) {
3499 if ( height_list[y] == 0 ) {
3500 canvas_pointer_y_span++;
3505 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3506 RegionView* rv2 = (*i);
3507 double ix1, ix2, iy1, iy2;
3510 if (rv2->region()->locked()) {
3514 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3515 rv2->get_canvas_group()->i2w (ix1, iy1);
3516 iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
3518 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3519 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3521 if (atv2->order != original_pointer_order) {
3522 /* this isn't the pointer track */
3524 if (canvas_pointer_y_span > 0) {
3526 /* moving up the canvas */
3527 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
3529 int32_t visible_tracks = 0;
3530 while (visible_tracks < canvas_pointer_y_span ) {
3533 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3534 /* we're passing through a hidden track */
3539 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3540 clamp_y_axis = true;
3544 clamp_y_axis = true;
3547 } else if (canvas_pointer_y_span < 0) {
3549 /*moving down the canvas*/
3551 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3554 int32_t visible_tracks = 0;
3556 while (visible_tracks > canvas_pointer_y_span ) {
3559 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3563 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3564 clamp_y_axis = true;
3569 clamp_y_axis = true;
3575 /* this is the pointer's track */
3576 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3577 clamp_y_axis = true;
3578 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3579 clamp_y_axis = true;
3587 } else if (drag_info.dest_trackview == tv) {
3588 clamp_y_axis = true;
3592 if (!clamp_y_axis) {
3593 drag_info.dest_trackview = tv;
3596 /************************************************************
3598 ************************************************************/
3600 /* compute the amount of pointer motion in frames, and where
3601 the region would be if we moved it by that much.
3603 if ( drag_info.move_threshold_passed ) {
3605 if (drag_info.current_pointer_frame >= drag_info.pointer_frame_offset) {
3607 nframes64_t sync_frame;
3608 nframes64_t sync_offset;
3611 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3613 sync_offset = clicked_regionview->region()->sync_offset (sync_dir);
3615 /* we don't handle a sync point that lies before zero.
3617 if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) {
3618 sync_frame = pending_region_position + (sync_dir*sync_offset);
3620 /* we snap if the snap modifier is not enabled.
3623 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3624 snap_to (sync_frame);
3627 pending_region_position = clicked_regionview->region()->adjust_to_sync (sync_frame);
3630 pending_region_position = drag_info.last_frame_position;
3634 pending_region_position = 0;
3637 if (pending_region_position > max_frames - clicked_regionview->region()->length()) {
3638 pending_region_position = drag_info.last_frame_position;
3641 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3643 bool x_move_allowed;
3645 if (Config->get_edit_mode() == Lock) {
3646 if (drag_info.copy) {
3647 x_move_allowed = !drag_info.x_constrained;
3649 /* in locked edit mode, reverse the usual meaning of x_constrained */
3650 x_move_allowed = drag_info.x_constrained;
3653 x_move_allowed = !drag_info.x_constrained;
3656 if (( pending_region_position != drag_info.last_frame_position) && x_move_allowed ) {
3658 /* now compute the canvas unit distance we need to move the regionview
3659 to make it appear at the new location.
3662 if (pending_region_position > drag_info.last_frame_position) {
3663 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3665 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3666 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3668 RegionView* rv2 = (*i);
3670 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3672 double ix1, ix2, iy1, iy2;
3673 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3674 rv2->get_canvas_group()->i2w (ix1, iy1);
3676 if (-x_delta > ix1 + horizontal_adjustment.get_value()) {
3678 pending_region_position = drag_info.last_frame_position;
3685 drag_info.last_frame_position = pending_region_position;
3692 /* threshold not passed */
3697 /*************************************************************
3699 ************************************************************/
3701 if (x_delta == 0 && (pointer_y_span == 0)) {
3702 /* haven't reached next snap point, and we're not switching
3703 trackviews. nothing to do.
3708 /*************************************************************
3710 ************************************************************/
3711 bool do_move = true;
3712 if (drag_info.first_move) {
3713 if (!drag_info.move_threshold_passed) {
3720 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3721 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3723 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3725 RegionView* rv = (*i);
3726 double ix1, ix2, iy1, iy2;
3727 int32_t temp_pointer_y_span = pointer_y_span;
3729 if (rv->region()->locked()) {
3733 /* get item BBox, which will be relative to parent. so we have
3734 to query on a child, then convert to world coordinates using
3738 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3739 rv->get_canvas_group()->i2w (ix1, iy1);
3741 /* for evaluation of the track position of iy1, we have to adjust
3742 to allow for the vertical scrolling adjustment and the height of the timebars.
3745 iy1 += get_trackview_group_vertical_offset ();
3746 if (drag_info.first_move) {
3748 // hide any dependent views
3750 rv->get_time_axis_view().hide_dependent_views (*rv);
3753 reparent to a non scrolling group so that we can keep the
3754 region selection above all time axis views.
3755 reparenting means we have to move the rv as the two
3756 parent groups have different coordinates.
3759 rv->get_canvas_group()->property_y() = iy1 - 1;
3760 rv->get_canvas_group()->reparent(*_region_motion_group);
3762 rv->fake_set_opaque (true);
3765 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3766 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3767 AudioTimeAxisView* temp_atv;
3769 if ((pointer_y_span != 0) && !clamp_y_axis) {
3772 for (j = height_list.begin(); j!= height_list.end(); j++) {
3773 if (x == canvas_atv->order) {
3774 /* we found the track the region is on */
3775 if (x != original_pointer_order) {
3776 /*this isn't from the same track we're dragging from */
3777 temp_pointer_y_span = canvas_pointer_y_span;
3779 while (temp_pointer_y_span > 0) {
3780 /* we're moving up canvas-wise,
3781 so we need to find the next track height
3783 if (j != height_list.begin()) {
3786 if (x != original_pointer_order) {
3787 /* we're not from the dragged track, so ignore hidden tracks. */
3789 temp_pointer_y_span++;
3793 temp_pointer_y_span--;
3796 while (temp_pointer_y_span < 0) {
3798 if (x != original_pointer_order) {
3800 temp_pointer_y_span--;
3804 if (j != height_list.end()) {
3807 temp_pointer_y_span++;
3809 /* find out where we'll be when we move and set height accordingly */
3811 tvp2 = trackview_by_y_position (iy1 + y_delta);
3812 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3813 rv->set_height (temp_atv->current_height());
3815 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3816 personally, i think this can confuse things, but never mind.
3819 //const GdkColor& col (temp_atv->view->get_region_color());
3820 //rv->set_color (const_cast<GdkColor&>(col));
3827 if (drag_info.brushing) {
3828 mouse_brush_insert_region (rv, pending_region_position);
3830 rv->move (x_delta, y_delta);
3833 } /* foreach region */
3837 if (drag_info.first_move && drag_info.move_threshold_passed) {
3838 cursor_group->raise_to_top();
3839 drag_info.first_move = false;
3842 if (x_delta != 0 && !drag_info.brushing) {
3843 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3848 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3850 bool nocommit = true;
3851 vector<RegionView*> copies;
3852 RouteTimeAxisView* source_tv;
3853 boost::shared_ptr<Diskstream> ds;
3854 boost::shared_ptr<Playlist> from_playlist;
3855 vector<RegionView*> new_selection;
3856 typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
3857 PlaylistSet modified_playlists;
3858 PlaylistSet frozen_playlists;
3859 list <sigc::connection> modified_playlist_connections;
3860 pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
3861 nframes64_t drag_delta;
3862 bool changed_tracks, changed_position;
3864 /* first_move is set to false if the regionview has been moved in the
3868 if (drag_info.first_move) {
3875 if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
3876 selection->set (pre_drag_region_selection);
3877 pre_drag_region_selection.clear ();
3880 if (drag_info.brushing) {
3881 /* all changes were made during motion event handlers */
3883 if (drag_info.copy) {
3884 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3885 copies.push_back (*i);
3894 /* reverse this here so that we have the correct logic to finalize
3898 if (Config->get_edit_mode() == Lock && !drag_info.copy) {
3899 drag_info.x_constrained = !drag_info.x_constrained;
3902 if (drag_info.copy) {
3903 if (drag_info.x_constrained) {
3904 op_string = _("fixed time region copy");
3906 op_string = _("region copy");
3909 if (drag_info.x_constrained) {
3910 op_string = _("fixed time region drag");
3912 op_string = _("region drag");
3916 begin_reversible_command (op_string);
3917 changed_position = (drag_info.last_frame_position != (nframes64_t) (clicked_regionview->region()->position()));
3918 changed_tracks = (trackview_by_y_position (drag_info.current_pointer_y) != &clicked_regionview->get_time_axis_view());
3920 drag_delta = clicked_regionview->region()->position() - drag_info.last_frame_position;
3922 track_canvas->update_now ();
3924 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3926 RegionView* rv = (*i);
3927 double ix1, ix2, iy1, iy2;
3928 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3929 rv->get_canvas_group()->i2w (ix1, iy1);
3930 iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
3932 TimeAxisView* dest_tv = trackview_by_y_position (iy1);
3933 AudioTimeAxisView* dest_atv = dynamic_cast<AudioTimeAxisView*>(dest_tv);
3936 if (rv->region()->locked()) {
3941 if (changed_position && !drag_info.x_constrained) {
3942 where = rv->region()->position() - drag_delta;
3944 where = rv->region()->position();
3947 boost::shared_ptr<Region> new_region;
3949 if (drag_info.copy) {
3950 /* we already made a copy */
3951 new_region = rv->region();
3953 /* undo the previous hide_dependent_views so that xfades don't
3954 disappear on copying regions
3957 //rv->get_time_axis_view().reveal_dependent_views (*rv);
3959 } else if (changed_tracks) {
3960 new_region = RegionFactory::create (rv->region());
3963 if (changed_tracks || drag_info.copy) {
3965 boost::shared_ptr<Playlist> to_playlist = dest_atv->playlist();
3967 latest_regionviews.clear ();
3969 sigc::connection c = dest_atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3971 insert_result = modified_playlists.insert (to_playlist);
3972 if (insert_result.second) {
3973 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3976 to_playlist->add_region (new_region, where);
3980 if (!latest_regionviews.empty()) {
3981 // XXX why just the first one ? we only expect one
3982 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
3983 new_selection.push_back (latest_regionviews.front());
3988 motion on the same track. plonk the previously reparented region
3989 back to its original canvas group (its streamview).
3990 No need to do anything for copies as they are fake regions which will be deleted.
3993 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (dest_atv);
3994 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
3995 rv->get_canvas_group()->property_y() = 0;
3997 /* just change the model */
3999 boost::shared_ptr<Playlist> playlist = dest_atv->playlist();
4001 insert_result = modified_playlists.insert (playlist);
4002 if (insert_result.second) {
4003 session->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
4005 /* freeze to avoid lots of relayering in the case of a multi-region drag */
4006 frozen_insert_result = frozen_playlists.insert(playlist);
4007 if (frozen_insert_result.second) {
4011 rv->region()->set_position (where, (void*) this);
4014 if (changed_tracks && !drag_info.copy) {
4016 /* get the playlist where this drag started. we can't use rv->region()->playlist()
4017 because we may have copied the region and it has not been attached to a playlist.
4020 assert ((source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view())));
4021 assert ((ds = source_tv->get_diskstream()));
4022 assert ((from_playlist = ds->playlist()));
4024 /* moved to a different audio track, without copying */
4026 /* the region that used to be in the old playlist is not
4027 moved to the new one - we use a copy of it. as a result,
4028 any existing editor for the region should no longer be
4032 rv->hide_region_editor();
4033 rv->fake_set_opaque (false);
4035 /* remove the region from the old playlist */
4037 insert_result = modified_playlists.insert (from_playlist);
4038 if (insert_result.second) {
4039 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
4042 from_playlist->remove_region ((rv->region()));
4044 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
4045 was selected in all of them, then removing it from a playlist will have removed all
4046 trace of it from the selection (i.e. there were N regions selected, we removed 1,
4047 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
4048 corresponding regionview, and the selection is now empty).
4050 this could have invalidated any and all iterators into the region selection.
4052 the heuristic we use here is: if the region selection is empty, break out of the loop
4053 here. if the region selection is not empty, then restart the loop because we know that
4054 we must have removed at least the region(view) we've just been working on as well as any
4055 that we processed on previous iterations.
4057 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
4058 we can just iterate.
4061 if (selection->regions.empty()) {
4064 i = selection->regions.by_layer().begin();
4071 if (drag_info.copy) {
4072 copies.push_back (rv);
4076 if (new_selection.empty()) {
4077 if (drag_info.copy) {
4078 /* the region(view)s that are selected and being dragged around
4079 are copies and do not belong to any track. remove them
4080 from the selection right here.
4082 selection->clear_regions();
4085 /* this will clear any existing selection that would have been
4086 cleared in the other clause above
4088 selection->set (new_selection);
4091 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
4097 for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
4098 session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));
4100 commit_reversible_command ();
4103 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
4110 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
4112 /* Either add to or set the set the region selection, unless
4113 this is an alignment click (control used)
4116 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
4117 TimeAxisView* tv = &rv.get_time_axis_view();
4118 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
4120 if (atv && atv->is_audio_track()) {
4121 speed = atv->get_diskstream()->speed();
4124 nframes64_t where = get_preferred_edit_position();
4128 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
4130 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
4132 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
4134 align_region (rv.region(), End, (nframes64_t) (where * speed));
4138 align_region (rv.region(), Start, (nframes64_t) (where * speed));
4145 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
4151 nframes64_t frame_rate;
4160 if (Profile->get_sae() || Profile->get_small_screen()) {
4161 m = ARDOUR_UI::instance()->primary_clock.mode();
4163 m = ARDOUR_UI::instance()->secondary_clock.mode();
4167 case AudioClock::BBT:
4168 session->bbt_time (frame, bbt);
4169 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
4172 case AudioClock::SMPTE:
4173 session->smpte_time (frame, smpte);
4174 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4177 case AudioClock::MinSec:
4178 /* XXX this is copied from show_verbose_duration_cursor() */
4179 frame_rate = session->frame_rate();
4180 hours = frame / (frame_rate * 3600);
4181 frame = frame % (frame_rate * 3600);
4182 mins = frame / (frame_rate * 60);
4183 frame = frame % (frame_rate * 60);
4184 secs = (float) frame / (float) frame_rate;
4185 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4189 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
4193 if (xpos >= 0 && ypos >=0) {
4194 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4197 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);
4199 show_verbose_canvas_cursor ();
4203 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
4210 nframes64_t distance, frame_rate;
4212 Meter meter_at_start(session->tempo_map().meter_at(start));
4220 if (Profile->get_sae() || Profile->get_small_screen()) {
4221 m = ARDOUR_UI::instance()->primary_clock.mode ();
4223 m = ARDOUR_UI::instance()->secondary_clock.mode ();
4227 case AudioClock::BBT:
4228 session->bbt_time (start, sbbt);
4229 session->bbt_time (end, ebbt);
4232 /* XXX this computation won't work well if the
4233 user makes a selection that spans any meter changes.
4236 ebbt.bars -= sbbt.bars;
4237 if (ebbt.beats >= sbbt.beats) {
4238 ebbt.beats -= sbbt.beats;
4241 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
4243 if (ebbt.ticks >= sbbt.ticks) {
4244 ebbt.ticks -= sbbt.ticks;
4247 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
4250 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
4253 case AudioClock::SMPTE:
4254 session->smpte_duration (end - start, smpte);
4255 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4258 case AudioClock::MinSec:
4259 /* XXX this stuff should be elsewhere.. */
4260 distance = end - start;
4261 frame_rate = session->frame_rate();
4262 hours = distance / (frame_rate * 3600);
4263 distance = distance % (frame_rate * 3600);
4264 mins = distance / (frame_rate * 60);
4265 distance = distance % (frame_rate * 60);
4266 secs = (float) distance / (float) frame_rate;
4267 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4271 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
4275 if (xpos >= 0 && ypos >=0) {
4276 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4279 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4282 show_verbose_canvas_cursor ();
4286 Editor::collect_new_region_view (RegionView* rv)
4288 latest_regionviews.push_back (rv);
4292 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
4294 if (clicked_regionview == 0) {
4298 /* lets try to create new Region for the selection */
4300 vector<boost::shared_ptr<AudioRegion> > new_regions;
4301 create_region_from_selection (new_regions);
4303 if (new_regions.empty()) {
4307 /* XXX fix me one day to use all new regions */
4309 boost::shared_ptr<Region> region (new_regions.front());
4311 /* add it to the current stream/playlist.
4313 tricky: the streamview for the track will add a new regionview. we will
4314 catch the signal it sends when it creates the regionview to
4315 set the regionview we want to then drag.
4318 latest_regionviews.clear();
4319 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4321 /* A selection grab currently creates two undo/redo operations, one for
4322 creating the new region and another for moving it.
4325 begin_reversible_command (_("selection grab"));
4327 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
4329 XMLNode *before = &(playlist->get_state());
4330 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
4331 XMLNode *after = &(playlist->get_state());
4332 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4334 commit_reversible_command ();
4338 if (latest_regionviews.empty()) {
4339 /* something went wrong */
4343 /* we need to deselect all other regionviews, and select this one
4344 i'm ignoring undo stuff, because the region creation will take care of it
4346 selection->set (latest_regionviews);
4348 drag_info.item = latest_regionviews.front()->get_canvas_group();
4349 drag_info.data = latest_regionviews.front();
4350 drag_info.motion_callback = &Editor::region_drag_motion_callback;
4351 drag_info.finished_callback = &Editor::region_drag_finished_callback;
4355 drag_info.source_trackview = clicked_trackview;
4356 drag_info.dest_trackview = drag_info.source_trackview;
4357 drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4358 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4360 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4364 Editor::cancel_selection ()
4366 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4367 (*i)->hide_selection ();
4369 selection->clear ();
4370 clicked_selection = 0;
4374 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4376 nframes64_t start = 0;
4377 nframes64_t end = 0;
4383 drag_info.item = item;
4384 drag_info.motion_callback = &Editor::drag_selection;
4385 drag_info.finished_callback = &Editor::end_selection_op;
4390 case CreateSelection:
4391 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4392 drag_info.copy = true;
4394 drag_info.copy = false;
4396 start_grab (event, selector_cursor);
4399 case SelectionStartTrim:
4400 if (clicked_trackview) {
4401 clicked_trackview->order_selection_trims (item, true);
4403 start_grab (event, trimmer_cursor);
4404 start = selection->time[clicked_selection].start;
4405 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4408 case SelectionEndTrim:
4409 if (clicked_trackview) {
4410 clicked_trackview->order_selection_trims (item, false);
4412 start_grab (event, trimmer_cursor);
4413 end = selection->time[clicked_selection].end;
4414 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
4418 start = selection->time[clicked_selection].start;
4420 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4424 if (selection_op == SelectionMove) {
4425 show_verbose_time_cursor(start, 10);
4427 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4432 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4434 nframes64_t start = 0;
4435 nframes64_t end = 0;
4437 nframes64_t pending_position;
4439 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4440 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4442 pending_position = 0;
4445 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4446 snap_to (pending_position);
4449 /* only alter selection if the current frame is
4450 different from the last frame position (adjusted)
4453 if (pending_position == drag_info.last_pointer_frame) return;
4455 switch (selection_op) {
4456 case CreateSelection:
4458 if (drag_info.first_move) {
4459 snap_to (drag_info.grab_frame);
4462 if (pending_position < drag_info.grab_frame) {
4463 start = pending_position;
4464 end = drag_info.grab_frame;
4466 end = pending_position;
4467 start = drag_info.grab_frame;
4470 /* first drag: Either add to the selection
4471 or create a new selection->
4474 if (drag_info.first_move) {
4476 begin_reversible_command (_("range selection"));
4478 if (drag_info.copy) {
4479 /* adding to the selection */
4480 clicked_selection = selection->add (start, end);
4481 drag_info.copy = false;
4483 /* new selection-> */
4484 clicked_selection = selection->set (clicked_trackview, start, end);
4489 case SelectionStartTrim:
4491 if (drag_info.first_move) {
4492 begin_reversible_command (_("trim selection start"));
4495 start = selection->time[clicked_selection].start;
4496 end = selection->time[clicked_selection].end;
4498 if (pending_position > end) {
4501 start = pending_position;
4505 case SelectionEndTrim:
4507 if (drag_info.first_move) {
4508 begin_reversible_command (_("trim selection end"));
4511 start = selection->time[clicked_selection].start;
4512 end = selection->time[clicked_selection].end;
4514 if (pending_position < start) {
4517 end = pending_position;
4524 if (drag_info.first_move) {
4525 begin_reversible_command (_("move selection"));
4528 start = selection->time[clicked_selection].start;
4529 end = selection->time[clicked_selection].end;
4531 length = end - start;
4533 start = pending_position;
4536 end = start + length;
4541 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4542 start_canvas_autoscroll (1, 0);
4546 selection->replace (clicked_selection, start, end);
4549 drag_info.last_pointer_frame = pending_position;
4550 drag_info.first_move = false;
4552 if (selection_op == SelectionMove) {
4553 show_verbose_time_cursor(start, 10);
4555 show_verbose_time_cursor(pending_position, 10);
4560 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4562 if (!drag_info.first_move) {
4563 drag_selection (item, event);
4564 /* XXX this is not object-oriented programming at all. ick */
4565 if (selection->time.consolidate()) {
4566 selection->TimeChanged ();
4568 commit_reversible_command ();
4570 /* just a click, no pointer movement.*/
4572 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4574 selection->clear_time();
4579 /* XXX what happens if its a music selection? */
4580 session->set_audio_range (selection->time);
4581 stop_canvas_autoscroll ();
4585 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4588 TimeAxisView* tvp = clicked_trackview;
4589 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4591 if (tv && tv->is_audio_track()) {
4592 speed = tv->get_diskstream()->speed();
4595 nframes64_t region_start = (nframes64_t) (clicked_regionview->region()->position() / speed);
4596 nframes64_t region_end = (nframes64_t) (clicked_regionview->region()->last_frame() / speed);
4597 nframes64_t region_length = (nframes64_t) (clicked_regionview->region()->length() / speed);
4599 //drag_info.item = clicked_regionview->get_name_highlight();
4600 drag_info.item = item;
4601 drag_info.motion_callback = &Editor::trim_motion_callback;
4602 drag_info.finished_callback = &Editor::trim_finished_callback;
4604 start_grab (event, trimmer_cursor);
4606 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4607 trim_op = ContentsTrim;
4609 /* These will get overridden for a point trim.*/
4610 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4611 /* closer to start */
4612 trim_op = StartTrim;
4613 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4621 show_verbose_time_cursor(region_start, 10);
4624 show_verbose_time_cursor(region_end, 10);
4627 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4633 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4635 RegionView* rv = clicked_regionview;
4636 nframes64_t frame_delta = 0;
4637 bool left_direction;
4638 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4640 /* snap modifier works differently here..
4641 its' current state has to be passed to the
4642 various trim functions in order to work properly
4646 TimeAxisView* tvp = clicked_trackview;
4647 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4648 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4650 if (tv && tv->is_audio_track()) {
4651 speed = tv->get_diskstream()->speed();
4654 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4655 left_direction = true;
4657 left_direction = false;
4661 snap_to (drag_info.current_pointer_frame);
4664 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4668 if (drag_info.first_move) {
4674 trim_type = "Region start trim";
4677 trim_type = "Region end trim";
4680 trim_type = "Region content trim";
4684 begin_reversible_command (trim_type);
4686 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4687 (*i)->fake_set_opaque(false);
4688 (*i)->region()->freeze ();
4690 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4692 arv->temporarily_hide_envelope ();
4694 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4695 insert_result = motion_frozen_playlists.insert (pl);
4696 if (insert_result.second) {
4697 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4702 if (left_direction) {
4703 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4705 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4710 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4713 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4714 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4720 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes64_t) (rv->region()->last_frame()/speed))) {
4723 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4724 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4731 bool swap_direction = false;
4733 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4734 swap_direction = true;
4737 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4738 i != selection->regions.by_layer().end(); ++i)
4740 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4748 show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
4751 show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
4754 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4758 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4759 drag_info.first_move = false;
4763 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4765 boost::shared_ptr<Region> region (rv.region());
4767 if (region->locked()) {
4771 nframes64_t new_bound;
4774 TimeAxisView* tvp = clicked_trackview;
4775 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4777 if (tv && tv->is_audio_track()) {
4778 speed = tv->get_diskstream()->speed();
4781 if (left_direction) {
4782 if (swap_direction) {
4783 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4785 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4788 if (swap_direction) {
4789 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4791 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4796 snap_to (new_bound);
4798 region->trim_start ((nframes64_t) (new_bound * speed), this);
4799 rv.region_changed (StartChanged);
4803 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
4805 boost::shared_ptr<Region> region (rv.region());
4807 if (region->locked()) {
4811 nframes64_t new_bound;
4814 TimeAxisView* tvp = clicked_trackview;
4815 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4817 if (tv && tv->is_audio_track()) {
4818 speed = tv->get_diskstream()->speed();
4821 if (left_direction) {
4822 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4824 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4828 snap_to (new_bound, (left_direction ? 0 : 1));
4831 region->trim_front ((nframes64_t) (new_bound * speed), this);
4833 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4837 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
4839 boost::shared_ptr<Region> region (rv.region());
4841 if (region->locked()) {
4845 nframes64_t new_bound;
4848 TimeAxisView* tvp = clicked_trackview;
4849 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4851 if (tv && tv->is_audio_track()) {
4852 speed = tv->get_diskstream()->speed();
4855 if (left_direction) {
4856 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
4858 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
4862 snap_to (new_bound);
4864 region->trim_end ((nframes64_t) (new_bound * speed), this);
4865 rv.region_changed (LengthChanged);
4869 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4871 if (!drag_info.first_move) {
4872 trim_motion_callback (item, event);
4874 if (!selection->selected (clicked_regionview)) {
4875 thaw_region_after_trim (*clicked_regionview);
4878 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4879 i != selection->regions.by_layer().end(); ++i)
4881 thaw_region_after_trim (**i);
4882 (*i)->fake_set_opaque (true);
4886 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4888 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4891 motion_frozen_playlists.clear ();
4893 commit_reversible_command();
4895 /* no mouse movement */
4901 Editor::point_trim (GdkEvent* event)
4903 RegionView* rv = clicked_regionview;
4904 nframes64_t new_bound = drag_info.current_pointer_frame;
4906 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4907 snap_to (new_bound);
4910 /* Choose action dependant on which button was pressed */
4911 switch (event->button.button) {
4913 trim_op = StartTrim;
4914 begin_reversible_command (_("Start point trim"));
4916 if (selection->selected (rv)) {
4918 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4919 i != selection->regions.by_layer().end(); ++i)
4921 if (!(*i)->region()->locked()) {
4922 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4923 XMLNode &before = pl->get_state();
4924 (*i)->region()->trim_front (new_bound, this);
4925 XMLNode &after = pl->get_state();
4926 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4932 if (!rv->region()->locked()) {
4933 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4934 XMLNode &before = pl->get_state();
4935 rv->region()->trim_front (new_bound, this);
4936 XMLNode &after = pl->get_state();
4937 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4941 commit_reversible_command();
4946 begin_reversible_command (_("End point trim"));
4948 if (selection->selected (rv)) {
4950 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4952 if (!(*i)->region()->locked()) {
4953 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4954 XMLNode &before = pl->get_state();
4955 (*i)->region()->trim_end (new_bound, this);
4956 XMLNode &after = pl->get_state();
4957 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4963 if (!rv->region()->locked()) {
4964 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4965 XMLNode &before = pl->get_state();
4966 rv->region()->trim_end (new_bound, this);
4967 XMLNode &after = pl->get_state();
4968 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4972 commit_reversible_command();
4981 Editor::thaw_region_after_trim (RegionView& rv)
4983 boost::shared_ptr<Region> region (rv.region());
4985 if (region->locked()) {
4989 region->thaw (_("trimmed region"));
4990 XMLNode &after = region->playlist()->get_state();
4991 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4993 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4995 arv->unhide_envelope ();
4999 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
5004 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
5005 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
5009 Location* location = find_location_from_marker (marker, is_start);
5010 location->set_hidden (true, this);
5015 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
5021 drag_info.item = item;
5022 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
5023 drag_info.finished_callback = &Editor::end_range_markerbar_op;
5025 range_marker_op = op;
5027 if (!temp_location) {
5028 temp_location = new Location;
5032 case CreateRangeMarker:
5033 case CreateTransportMarker:
5034 case CreateCDMarker:
5036 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
5037 drag_info.copy = true;
5039 drag_info.copy = false;
5041 start_grab (event, selector_cursor);
5045 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5050 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5052 nframes64_t start = 0;
5053 nframes64_t end = 0;
5054 ArdourCanvas::SimpleRect *crect;
5056 switch (range_marker_op) {
5057 case CreateRangeMarker:
5058 crect = range_bar_drag_rect;
5060 case CreateTransportMarker:
5061 crect = transport_bar_drag_rect;
5063 case CreateCDMarker:
5064 crect = cd_marker_bar_drag_rect;
5067 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
5072 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5073 snap_to (drag_info.current_pointer_frame);
5076 /* only alter selection if the current frame is
5077 different from the last frame position.
5080 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5082 switch (range_marker_op) {
5083 case CreateRangeMarker:
5084 case CreateTransportMarker:
5085 case CreateCDMarker:
5086 if (drag_info.first_move) {
5087 snap_to (drag_info.grab_frame);
5090 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5091 start = drag_info.current_pointer_frame;
5092 end = drag_info.grab_frame;
5094 end = drag_info.current_pointer_frame;
5095 start = drag_info.grab_frame;
5098 /* first drag: Either add to the selection
5099 or create a new selection.
5102 if (drag_info.first_move) {
5104 temp_location->set (start, end);
5108 update_marker_drag_item (temp_location);
5109 range_marker_drag_rect->show();
5110 //range_marker_drag_rect->raise_to_top();
5116 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
5117 start_canvas_autoscroll (1, 0);
5121 temp_location->set (start, end);
5123 double x1 = frame_to_pixel (start);
5124 double x2 = frame_to_pixel (end);
5125 crect->property_x1() = x1;
5126 crect->property_x2() = x2;
5128 update_marker_drag_item (temp_location);
5131 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5132 drag_info.first_move = false;
5134 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5139 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5141 Location * newloc = 0;
5145 if (!drag_info.first_move) {
5146 drag_range_markerbar_op (item, event);
5148 switch (range_marker_op) {
5149 case CreateRangeMarker:
5150 case CreateCDMarker:
5152 begin_reversible_command (_("new range marker"));
5153 XMLNode &before = session->locations()->get_state();
5154 session->locations()->next_available_name(rangename,"unnamed");
5155 if (range_marker_op == CreateCDMarker) {
5156 flags = Location::IsRangeMarker|Location::IsCDMarker;
5157 cd_marker_bar_drag_rect->hide();
5160 flags = Location::IsRangeMarker;
5161 range_bar_drag_rect->hide();
5163 newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
5164 session->locations()->add (newloc, true);
5165 XMLNode &after = session->locations()->get_state();
5166 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
5167 commit_reversible_command ();
5169 range_marker_drag_rect->hide();
5173 case CreateTransportMarker:
5174 // popup menu to pick loop or punch
5175 new_transport_marker_context_menu (&event->button, item);
5180 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5182 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
5187 start = session->locations()->first_mark_before (drag_info.grab_frame);
5188 end = session->locations()->first_mark_after (drag_info.grab_frame);
5190 if (end == max_frames) {
5191 end = session->current_end_frame ();
5195 start = session->current_start_frame ();
5198 switch (mouse_mode) {
5200 /* find the two markers on either side and then make the selection from it */
5201 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
5205 /* find the two markers on either side of the click and make the range out of it */
5206 selection->set (0, start, end);
5215 stop_canvas_autoscroll ();
5221 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5223 drag_info.item = item;
5224 drag_info.motion_callback = &Editor::drag_mouse_zoom;
5225 drag_info.finished_callback = &Editor::end_mouse_zoom;
5227 start_grab (event, zoom_cursor);
5229 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5233 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5238 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5239 snap_to (drag_info.current_pointer_frame);
5241 if (drag_info.first_move) {
5242 snap_to (drag_info.grab_frame);
5246 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5248 /* base start and end on initial click position */
5249 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5250 start = drag_info.current_pointer_frame;
5251 end = drag_info.grab_frame;
5253 end = drag_info.current_pointer_frame;
5254 start = drag_info.grab_frame;
5259 if (drag_info.first_move) {
5261 zoom_rect->raise_to_top();
5264 reposition_zoom_rect(start, end);
5266 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5267 drag_info.first_move = false;
5269 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5274 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5276 if (!drag_info.first_move) {
5277 drag_mouse_zoom (item, event);
5279 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5280 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
5282 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
5285 temporal_zoom_to_frame (false, drag_info.grab_frame);
5287 temporal_zoom_step (false);
5288 center_screen (drag_info.grab_frame);
5296 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
5298 double x1 = frame_to_pixel (start);
5299 double x2 = frame_to_pixel (end);
5300 double y2 = full_canvas_height - 1.0;
5302 zoom_rect->property_x1() = x1;
5303 zoom_rect->property_y1() = 1.0;
5304 zoom_rect->property_x2() = x2;
5305 zoom_rect->property_y2() = y2;
5309 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5311 drag_info.item = item;
5312 drag_info.motion_callback = &Editor::drag_rubberband_select;
5313 drag_info.finished_callback = &Editor::end_rubberband_select;
5315 start_grab (event, cross_hair_cursor);
5317 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5321 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5328 /* use a bigger drag threshold than the default */
5330 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
5334 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) {
5335 if (drag_info.first_move) {
5336 snap_to (drag_info.grab_frame);
5338 snap_to (drag_info.current_pointer_frame);
5341 /* base start and end on initial click position */
5343 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5344 start = drag_info.current_pointer_frame;
5345 end = drag_info.grab_frame;
5347 end = drag_info.current_pointer_frame;
5348 start = drag_info.grab_frame;
5351 if (drag_info.current_pointer_y < drag_info.grab_y) {
5352 y1 = drag_info.current_pointer_y;
5353 y2 = drag_info.grab_y;
5355 y2 = drag_info.current_pointer_y;
5356 y1 = drag_info.grab_y;
5360 if (start != end || y1 != y2) {
5362 double x1 = frame_to_pixel (start);
5363 double x2 = frame_to_pixel (end);
5365 rubberband_rect->property_x1() = x1;
5366 rubberband_rect->property_y1() = y1;
5367 rubberband_rect->property_x2() = x2;
5368 rubberband_rect->property_y2() = y2;
5370 rubberband_rect->show();
5371 rubberband_rect->raise_to_top();
5373 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5374 drag_info.first_move = false;
5376 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5381 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5383 if (!drag_info.first_move) {
5385 drag_rubberband_select (item, event);
5388 if (drag_info.current_pointer_y < drag_info.grab_y) {
5389 y1 = drag_info.current_pointer_y;
5390 y2 = drag_info.grab_y;
5392 y2 = drag_info.current_pointer_y;
5393 y1 = drag_info.grab_y;
5397 Selection::Operation op = Keyboard::selection_type (event->button.state);
5400 begin_reversible_command (_("rubberband selection"));
5402 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5403 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5405 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5409 commit_reversible_command ();
5413 if (!getenv("ARDOUR_SAE")) {
5414 selection->clear_tracks();
5416 selection->clear_regions();
5417 selection->clear_points ();
5418 selection->clear_lines ();
5421 rubberband_rect->hide();
5426 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5428 using namespace Gtkmm2ext;
5430 ArdourPrompter prompter (false);
5432 prompter.set_prompt (_("Name for region:"));
5433 prompter.set_initial_text (clicked_regionview->region()->name());
5434 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5435 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5436 prompter.show_all ();
5437 switch (prompter.run ()) {
5438 case Gtk::RESPONSE_ACCEPT:
5440 prompter.get_result(str);
5442 clicked_regionview->region()->set_name (str);
5450 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5452 drag_info.item = item;
5453 drag_info.motion_callback = &Editor::time_fx_motion;
5454 drag_info.finished_callback = &Editor::end_time_fx;
5458 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5462 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5464 RegionView* rv = clicked_regionview;
5466 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5467 snap_to (drag_info.current_pointer_frame);
5470 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5474 if (drag_info.current_pointer_frame > rv->region()->position()) {
5475 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5478 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5479 drag_info.first_move = false;
5481 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5485 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5487 clicked_regionview->get_time_axis_view().hide_timestretch ();
5489 if (drag_info.first_move) {
5493 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5494 /* backwards drag of the left edge - not usable */
5498 nframes64_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5499 #ifdef USE_RUBBERBAND
5500 float percentage = (float) ((double) newlen / (double) clicked_regionview->region()->length());
5502 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5505 begin_reversible_command (_("timestretch"));
5507 // XXX how do timeFX on multiple regions ?
5510 rs.add (clicked_regionview);
5512 if (time_stretch (rs, percentage) == 0) {
5513 session->commit_reversible_command ();
5518 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
5520 /* no brushing without a useful snap setting */
5523 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
5526 switch (snap_mode) {
5528 return; /* can't work because it allows region to be placed anywhere */
5533 switch (snap_type) {
5541 /* don't brush a copy over the original */
5543 if (pos == rv->region()->position()) {
5547 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
5549 if (atv == 0 || !atv->is_audio_track()) {
5553 boost::shared_ptr<Playlist> playlist = atv->playlist();
5554 double speed = atv->get_diskstream()->speed();
5556 XMLNode &before = playlist->get_state();
5557 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes64_t) (pos * speed));
5558 XMLNode &after = playlist->get_state();
5559 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5561 // playlist is frozen, so we have to update manually
5563 playlist->Modified(); /* EMIT SIGNAL */
5567 Editor::track_height_step_timeout ()
5569 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
5570 current_stepping_trackview = 0;