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 cerr << "event at " << event->button.x
123 << ", " << event->button.y
126 switch (event->type) {
127 case GDK_BUTTON_RELEASE:
128 case GDK_BUTTON_PRESS:
129 case GDK_2BUTTON_PRESS:
130 case GDK_3BUTTON_PRESS:
132 *pcx = event->button.x;
133 *pcy = event->button.y;
134 _trackview_group->w2i(*pcx, *pcy);
136 case GDK_MOTION_NOTIFY:
138 *pcx = event->motion.x;
139 *pcy = event->motion.y;
140 _trackview_group->w2i(*pcx, *pcy);
142 case GDK_ENTER_NOTIFY:
143 case GDK_LEAVE_NOTIFY:
144 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
147 case GDK_KEY_RELEASE:
148 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
151 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
155 cerr << "report as " << (pcx ? *pcx : -9999.0)
157 << (pcy ? *pcy : -9999.0)
160 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
161 position is negative (as can be the case with motion events in particular),
162 the frame location is always positive.
165 return pixel_to_frame (*pcx);
169 Editor::mouse_mode_toggled (MouseMode m)
171 if (ignore_mouse_mode_toggle) {
177 if (mouse_select_button.get_active()) {
183 if (mouse_move_button.get_active()) {
189 if (mouse_gain_button.get_active()) {
195 if (mouse_zoom_button.get_active()) {
201 if (mouse_timefx_button.get_active()) {
207 if (mouse_audition_button.get_active()) {
218 Editor::which_grabber_cursor ()
220 switch (_edit_point) {
222 return grabber_edit_point_cursor;
227 return grabber_cursor;
231 Editor::set_canvas_cursor ()
233 switch (mouse_mode) {
235 current_canvas_cursor = selector_cursor;
239 current_canvas_cursor = which_grabber_cursor();
243 current_canvas_cursor = cross_hair_cursor;
247 current_canvas_cursor = zoom_cursor;
251 current_canvas_cursor = time_fx_cursor; // just use playhead
255 current_canvas_cursor = speaker_cursor;
260 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
265 Editor::set_mouse_mode (MouseMode m, bool force)
267 if (drag_info.item) {
271 if (!force && m == mouse_mode) {
279 if (mouse_mode != MouseRange) {
281 /* in all modes except range, hide the range selection,
282 show the object (region) selection.
285 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
286 (*i)->set_should_show_selection (true);
288 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
289 (*i)->hide_selection ();
295 in range mode,show the range selection.
298 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
299 if ((*i)->get_selected()) {
300 (*i)->show_selection (selection->time);
305 /* XXX the hack of unsetting all other buttons should go
306 away once GTK2 allows us to use regular radio buttons drawn like
307 normal buttons, rather than my silly GroupedButton hack.
310 ignore_mouse_mode_toggle = true;
312 switch (mouse_mode) {
314 mouse_select_button.set_active (true);
318 mouse_move_button.set_active (true);
322 mouse_gain_button.set_active (true);
326 mouse_zoom_button.set_active (true);
330 mouse_timefx_button.set_active (true);
334 mouse_audition_button.set_active (true);
338 ignore_mouse_mode_toggle = false;
340 set_canvas_cursor ();
344 Editor::step_mouse_mode (bool next)
346 switch (current_mouse_mode()) {
348 if (next) set_mouse_mode (MouseRange);
349 else set_mouse_mode (MouseTimeFX);
353 if (next) set_mouse_mode (MouseZoom);
354 else set_mouse_mode (MouseObject);
358 if (next) set_mouse_mode (MouseGain);
359 else set_mouse_mode (MouseRange);
363 if (next) set_mouse_mode (MouseTimeFX);
364 else set_mouse_mode (MouseZoom);
368 if (next) set_mouse_mode (MouseAudition);
369 else set_mouse_mode (MouseGain);
373 if (next) set_mouse_mode (MouseObject);
374 else set_mouse_mode (MouseTimeFX);
380 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
382 /* in object/audition/timefx mode, any button press sets
383 the selection if the object can be selected. this is a
384 bit of hack, because we want to avoid this if the
385 mouse operation is a region alignment.
387 note: not dbl-click or triple-click
390 if (((mouse_mode != MouseObject) &&
391 (mouse_mode != MouseAudition || item_type != RegionItem) &&
392 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
393 (mouse_mode != MouseRange)) ||
395 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
400 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
402 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
404 /* almost no selection action on modified button-2 or button-3 events */
406 if (item_type != RegionItem && event->button.button != 2) {
412 Selection::Operation op = Keyboard::selection_type (event->button.state);
413 bool press = (event->type == GDK_BUTTON_PRESS);
415 // begin_reversible_command (_("select on click"));
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 RegionViewNameHighlight:
428 if (mouse_mode != MouseRange) {
429 set_selected_regionview_from_click (press, op, true);
430 } else if (event->type == GDK_BUTTON_PRESS) {
431 set_selected_track_as_side_effect ();
435 case FadeInHandleItem:
437 case FadeOutHandleItem:
439 if (mouse_mode != MouseRange) {
440 set_selected_regionview_from_click (press, op, true);
441 } else if (event->type == GDK_BUTTON_PRESS) {
442 set_selected_track_as_side_effect ();
446 case GainAutomationControlPointItem:
447 case PanAutomationControlPointItem:
448 case RedirectAutomationControlPointItem:
449 set_selected_track_as_side_effect ();
450 if (mouse_mode != MouseRange) {
451 set_selected_control_point_from_click (op, false);
456 /* for context click or range selection, select track */
457 if (event->button.button == 3) {
458 set_selected_track_as_side_effect ();
459 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
460 set_selected_track_as_side_effect ();
464 case AutomationTrackItem:
465 set_selected_track_as_side_effect (true);
473 const static double ZERO_GAIN_FRACTION = gain_to_slider_position(dB_to_coefficient(0.0));
476 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
478 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
481 Glib::RefPtr<const Gdk::Window> pointer_window;
484 Gdk::ModifierType mask;
486 pointer_window = canvas_window->get_pointer (x, y, mask);
488 if (pointer_window == track_canvas->get_bin_window()) {
489 track_canvas->window_to_world (x, y, wx, wy);
490 allow_vertical_scroll = true;
492 allow_vertical_scroll = false;
496 track_canvas->grab_focus();
498 if (session && session->actively_recording()) {
502 button_selection (item, event, item_type);
504 if (drag_info.item == 0 &&
505 (Keyboard::is_delete_event (&event->button) ||
506 Keyboard::is_context_menu_event (&event->button) ||
507 Keyboard::is_edit_event (&event->button))) {
509 /* handled by button release */
513 switch (event->button.button) {
516 if (event->type == GDK_BUTTON_PRESS) {
518 if (drag_info.item) {
519 drag_info.item->ungrab (event->button.time);
522 /* single mouse clicks on any of these item types operate
523 independent of mouse mode, mostly because they are
524 not on the main track canvas or because we want
529 case PlayheadCursorItem:
530 start_cursor_grab (item, event);
534 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
535 hide_marker (item, event);
537 start_marker_grab (item, event);
541 case TempoMarkerItem:
542 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
543 start_tempo_marker_copy_grab (item, event);
545 start_tempo_marker_grab (item, event);
549 case MeterMarkerItem:
550 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
551 start_meter_marker_copy_grab (item, event);
553 start_meter_marker_grab (item, event);
563 case RangeMarkerBarItem:
564 start_range_markerbar_op (item, event, CreateRangeMarker);
568 case CdMarkerBarItem:
569 start_range_markerbar_op (item, event, CreateCDMarker);
573 case TransportMarkerBarItem:
574 start_range_markerbar_op (item, event, CreateTransportMarker);
583 switch (mouse_mode) {
586 case StartSelectionTrimItem:
587 start_selection_op (item, event, SelectionStartTrim);
590 case EndSelectionTrimItem:
591 start_selection_op (item, event, SelectionEndTrim);
595 if (Keyboard::modifier_state_contains
596 (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) {
597 // contains and not equals because I can't use alt as a modifier alone.
598 start_selection_grab (item, event);
599 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
600 /* grab selection for moving */
601 start_selection_op (item, event, SelectionMove);
603 /* this was debated, but decided the more common action was to
604 make a new selection */
605 start_selection_op (item, event, CreateSelection);
610 start_selection_op (item, event, CreateSelection);
616 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
617 event->type == GDK_BUTTON_PRESS) {
619 start_rubberband_select (item, event);
621 } else if (event->type == GDK_BUTTON_PRESS) {
624 case FadeInHandleItem:
625 start_fade_in_grab (item, event);
628 case FadeOutHandleItem:
629 start_fade_out_grab (item, event);
633 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
634 start_region_copy_grab (item, event);
635 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
636 start_region_brush_grab (item, event);
638 start_region_grab (item, event);
642 case RegionViewNameHighlight:
643 start_trim (item, event);
648 /* rename happens on edit clicks */
649 start_trim (clicked_regionview->get_name_highlight(), event);
653 case GainAutomationControlPointItem:
654 case PanAutomationControlPointItem:
655 case RedirectAutomationControlPointItem:
656 start_control_point_grab (item, event);
660 case GainAutomationLineItem:
661 case PanAutomationLineItem:
662 case RedirectAutomationLineItem:
663 start_line_grab_from_line (item, event);
668 case AutomationTrackItem:
669 start_rubberband_select (item, event);
672 /* <CMT Additions> */
673 case ImageFrameHandleStartItem:
674 imageframe_start_handle_op(item, event) ;
677 case ImageFrameHandleEndItem:
678 imageframe_end_handle_op(item, event) ;
681 case MarkerViewHandleStartItem:
682 markerview_item_start_handle_op(item, event) ;
685 case MarkerViewHandleEndItem:
686 markerview_item_end_handle_op(item, event) ;
689 /* </CMT Additions> */
691 /* <CMT Additions> */
693 start_markerview_grab(item, event) ;
696 start_imageframe_grab(item, event) ;
698 /* </CMT Additions> */
714 // start_line_grab_from_regionview (item, event);
717 case GainControlPointItem:
718 start_control_point_grab (item, event);
722 start_line_grab_from_line (item, event);
725 case GainAutomationControlPointItem:
726 case PanAutomationControlPointItem:
727 case RedirectAutomationControlPointItem:
728 start_control_point_grab (item, event);
739 case GainAutomationControlPointItem:
740 case PanAutomationControlPointItem:
741 case RedirectAutomationControlPointItem:
742 start_control_point_grab (item, event);
745 case GainAutomationLineItem:
746 case PanAutomationLineItem:
747 case RedirectAutomationLineItem:
748 start_line_grab_from_line (item, event);
752 // XXX need automation mode to identify which
754 // start_line_grab_from_regionview (item, event);
764 if (event->type == GDK_BUTTON_PRESS) {
765 start_mouse_zoom (item, event);
772 if (item_type == RegionItem) {
773 start_time_fx (item, event);
780 scrub_reverse_distance = 0;
781 last_scrub_x = event->button.x;
782 scrubbing_direction = 0;
783 track_canvas->get_window()->set_cursor (*transparent_cursor);
784 /* rest handled in motion & release */
793 switch (mouse_mode) {
795 if (event->type == GDK_BUTTON_PRESS) {
798 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
799 start_region_copy_grab (item, event);
801 start_region_grab (item, event);
806 case GainAutomationControlPointItem:
807 case PanAutomationControlPointItem:
808 case RedirectAutomationControlPointItem:
809 start_control_point_grab (item, event);
820 case RegionViewNameHighlight:
821 start_trim (item, event);
826 start_trim (clicked_regionview->get_name_highlight(), event);
837 if (event->type == GDK_BUTTON_PRESS) {
838 /* relax till release */
845 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
846 temporal_zoom_session();
848 temporal_zoom_to_frame (true, event_frame(event));
871 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
873 nframes64_t where = event_frame (event, 0, 0);
874 AutomationTimeAxisView* atv = 0;
876 /* no action if we're recording */
878 if (session && session->actively_recording()) {
882 /* first, see if we're finishing a drag ... */
884 if (drag_info.item) {
885 if (end_grab (item, event)) {
886 /* grab dragged, so do nothing else */
891 button_selection (item, event, item_type);
893 /* edit events get handled here */
895 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
901 case TempoMarkerItem:
902 edit_tempo_marker (item);
905 case MeterMarkerItem:
906 edit_meter_marker (item);
910 if (clicked_regionview->name_active()) {
911 return mouse_rename_region (item, event);
921 /* context menu events get handled here */
923 if (Keyboard::is_context_menu_event (&event->button)) {
925 if (drag_info.item == 0) {
927 /* no matter which button pops up the context menu, tell the menu
928 widget to use button 1 to drive menu selection.
933 case FadeInHandleItem:
935 case FadeOutHandleItem:
936 popup_fade_context_menu (1, event->button.time, item, item_type);
940 popup_track_context_menu (1, event->button.time, item_type, false, where);
944 case RegionViewNameHighlight:
946 popup_track_context_menu (1, event->button.time, item_type, false, where);
950 popup_track_context_menu (1, event->button.time, item_type, true, where);
953 case AutomationTrackItem:
954 popup_track_context_menu (1, event->button.time, item_type, false, where);
958 case RangeMarkerBarItem:
959 case TransportMarkerBarItem:
960 case CdMarkerBarItem:
963 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
967 marker_context_menu (&event->button, item);
970 case TempoMarkerItem:
971 tm_marker_context_menu (&event->button, item);
974 case MeterMarkerItem:
975 tm_marker_context_menu (&event->button, item);
978 case CrossfadeViewItem:
979 popup_track_context_menu (1, event->button.time, item_type, false, where);
982 /* <CMT Additions> */
984 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
986 case ImageFrameTimeAxisItem:
987 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
990 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
992 case MarkerTimeAxisItem:
993 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
995 /* <CMT Additions> */
1006 /* delete events get handled here */
1008 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
1010 switch (item_type) {
1011 case TempoMarkerItem:
1012 remove_tempo_marker (item);
1015 case MeterMarkerItem:
1016 remove_meter_marker (item);
1020 remove_marker (*item, event);
1024 if (mouse_mode == MouseObject) {
1025 remove_clicked_region ();
1029 case GainControlPointItem:
1030 if (mouse_mode == MouseGain) {
1031 remove_gain_control_point (item, event);
1035 case GainAutomationControlPointItem:
1036 case PanAutomationControlPointItem:
1037 case RedirectAutomationControlPointItem:
1038 remove_control_point (item, event);
1047 switch (event->button.button) {
1050 switch (item_type) {
1051 /* see comments in button_press_handler */
1052 case PlayheadCursorItem:
1055 case GainAutomationLineItem:
1056 case PanAutomationLineItem:
1057 case RedirectAutomationLineItem:
1058 case StartSelectionTrimItem:
1059 case EndSelectionTrimItem:
1063 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1064 snap_to (where, 0, true);
1066 mouse_add_new_marker (where);
1069 case CdMarkerBarItem:
1070 // if we get here then a dragged range wasn't done
1071 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1072 snap_to (where, 0, true);
1074 mouse_add_new_marker (where, true);
1078 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1081 mouse_add_new_tempo_event (where);
1085 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1093 switch (mouse_mode) {
1095 switch (item_type) {
1096 case AutomationTrackItem:
1097 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_trackview);
1099 atv->add_automation_event (item, event, where, event->button.y);
1111 // Gain only makes sense for audio regions
1113 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1117 switch (item_type) {
1119 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1123 case AutomationTrackItem:
1124 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1125 add_automation_event (item, event, where, event->button.y);
1135 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1136 if (scrubbing_direction == 0) {
1137 /* no drag, just a click */
1138 switch (item_type) {
1140 play_selected_region ();
1146 /* make sure we stop */
1147 session->request_transport_speed (0.0);
1161 switch (mouse_mode) {
1164 switch (item_type) {
1166 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1168 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1171 // Button2 click is unused
1184 // x_style_paste (where, 1.0);
1204 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1210 if (last_item_entered != item) {
1211 last_item_entered = item;
1212 last_item_entered_n = 0;
1215 switch (item_type) {
1216 case GainControlPointItem:
1217 if (mouse_mode == MouseGain) {
1218 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1219 cp->set_visible (true);
1223 at_y = cp->get_y ();
1224 cp->item->i2w (at_x, at_y);
1228 fraction = 1.0 - (cp->get_y() / cp->line.height());
1230 if (is_drawable() && !_scrubbing) {
1231 track_canvas->get_window()->set_cursor (*fader_cursor);
1234 last_item_entered_n++;
1235 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1236 if (last_item_entered_n < 10) {
1237 show_verbose_canvas_cursor ();
1242 case GainAutomationControlPointItem:
1243 case PanAutomationControlPointItem:
1244 case RedirectAutomationControlPointItem:
1245 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1246 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1247 cp->set_visible (true);
1251 at_y = cp->get_y ();
1252 cp->item->i2w (at_x, at_y);
1256 fraction = 1.0 - (cp->get_y() / cp->line.height());
1258 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1259 show_verbose_canvas_cursor ();
1261 if (is_drawable()) {
1262 track_canvas->get_window()->set_cursor (*fader_cursor);
1268 if (mouse_mode == MouseGain) {
1269 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1271 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1272 if (is_drawable()) {
1273 track_canvas->get_window()->set_cursor (*fader_cursor);
1278 case GainAutomationLineItem:
1279 case RedirectAutomationLineItem:
1280 case PanAutomationLineItem:
1281 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1283 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1285 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1287 if (is_drawable()) {
1288 track_canvas->get_window()->set_cursor (*fader_cursor);
1293 case RegionViewNameHighlight:
1294 if (is_drawable() && mouse_mode == MouseObject) {
1295 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1299 case StartSelectionTrimItem:
1300 case EndSelectionTrimItem:
1301 /* <CMT Additions> */
1302 case ImageFrameHandleStartItem:
1303 case ImageFrameHandleEndItem:
1304 case MarkerViewHandleStartItem:
1305 case MarkerViewHandleEndItem:
1306 /* </CMT Additions> */
1308 if (is_drawable()) {
1309 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1313 case PlayheadCursorItem:
1314 if (is_drawable()) {
1315 switch (_edit_point) {
1317 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1320 track_canvas->get_window()->set_cursor (*grabber_cursor);
1326 case RegionViewName:
1328 /* when the name is not an active item, the entire name highlight is for trimming */
1330 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1331 if (mouse_mode == MouseObject && is_drawable()) {
1332 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1338 case AutomationTrackItem:
1339 if (is_drawable()) {
1340 Gdk::Cursor *cursor;
1341 switch (mouse_mode) {
1343 cursor = selector_cursor;
1346 cursor = zoom_cursor;
1349 cursor = cross_hair_cursor;
1353 track_canvas->get_window()->set_cursor (*cursor);
1355 AutomationTimeAxisView* atv;
1356 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1357 clear_entered_track = false;
1358 set_entered_track (atv);
1364 case RangeMarkerBarItem:
1365 case TransportMarkerBarItem:
1366 case CdMarkerBarItem:
1369 if (is_drawable()) {
1370 track_canvas->get_window()->set_cursor (*timebar_cursor);
1375 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1378 entered_marker = marker;
1379 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1381 case MeterMarkerItem:
1382 case TempoMarkerItem:
1383 if (is_drawable()) {
1384 track_canvas->get_window()->set_cursor (*timebar_cursor);
1387 case FadeInHandleItem:
1388 case FadeOutHandleItem:
1389 if (mouse_mode == MouseObject) {
1390 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1392 rect->property_fill_color_rgba() = 0;
1393 rect->property_outline_pixels() = 1;
1402 /* second pass to handle entered track status in a comprehensible way.
1405 switch (item_type) {
1407 case GainAutomationLineItem:
1408 case RedirectAutomationLineItem:
1409 case PanAutomationLineItem:
1410 case GainControlPointItem:
1411 case GainAutomationControlPointItem:
1412 case PanAutomationControlPointItem:
1413 case RedirectAutomationControlPointItem:
1414 /* these do not affect the current entered track state */
1415 clear_entered_track = false;
1418 case AutomationTrackItem:
1419 /* handled above already */
1423 set_entered_track (0);
1431 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1440 switch (item_type) {
1441 case GainControlPointItem:
1442 case GainAutomationControlPointItem:
1443 case PanAutomationControlPointItem:
1444 case RedirectAutomationControlPointItem:
1445 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1446 if (cp->line.npoints() > 1) {
1447 if (!cp->selected) {
1448 cp->set_visible (false);
1452 if (is_drawable()) {
1453 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1456 hide_verbose_canvas_cursor ();
1459 case RegionViewNameHighlight:
1460 case StartSelectionTrimItem:
1461 case EndSelectionTrimItem:
1462 case PlayheadCursorItem:
1463 /* <CMT Additions> */
1464 case ImageFrameHandleStartItem:
1465 case ImageFrameHandleEndItem:
1466 case MarkerViewHandleStartItem:
1467 case MarkerViewHandleEndItem:
1468 /* </CMT Additions> */
1469 if (is_drawable()) {
1470 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1475 case GainAutomationLineItem:
1476 case RedirectAutomationLineItem:
1477 case PanAutomationLineItem:
1478 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1480 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1482 line->property_fill_color_rgba() = al->get_line_color();
1484 if (is_drawable()) {
1485 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1489 case RegionViewName:
1490 /* see enter_handler() for notes */
1491 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1492 if (is_drawable() && mouse_mode == MouseObject) {
1493 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1498 case RangeMarkerBarItem:
1499 case TransportMarkerBarItem:
1500 case CdMarkerBarItem:
1504 if (is_drawable()) {
1505 track_canvas->get_window()->set_cursor (*timebar_cursor);
1510 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1514 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1515 location_flags_changed (loc, this);
1518 case MeterMarkerItem:
1519 case TempoMarkerItem:
1521 if (is_drawable()) {
1522 track_canvas->get_window()->set_cursor (*timebar_cursor);
1527 case FadeInHandleItem:
1528 case FadeOutHandleItem:
1529 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1531 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1533 rect->property_fill_color_rgba() = rv->get_fill_color();
1534 rect->property_outline_pixels() = 0;
1539 case AutomationTrackItem:
1540 if (is_drawable()) {
1541 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1542 clear_entered_track = true;
1543 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1555 Editor::left_automation_track ()
1557 if (clear_entered_track) {
1558 set_entered_track (0);
1559 clear_entered_track = false;
1569 if (scrubbing_direction == 0) {
1571 session->request_locate (drag_info.current_pointer_frame, false);
1572 session->request_transport_speed (0.1);
1573 scrubbing_direction = 1;
1577 if (last_scrub_x > drag_info.current_pointer_x) {
1579 /* pointer moved to the left */
1581 if (scrubbing_direction > 0) {
1583 /* we reversed direction to go backwards */
1586 scrub_reverse_distance += (int) (last_scrub_x - drag_info.current_pointer_x);
1590 /* still moving to the left (backwards) */
1592 scrub_reversals = 0;
1593 scrub_reverse_distance = 0;
1595 delta = 0.01 * (last_scrub_x - drag_info.current_pointer_x);
1596 session->request_transport_speed (session->transport_speed() - delta);
1600 /* pointer moved to the right */
1602 if (scrubbing_direction < 0) {
1603 /* we reversed direction to go forward */
1606 scrub_reverse_distance += (int) (drag_info.current_pointer_x - last_scrub_x);
1609 /* still moving to the right */
1611 scrub_reversals = 0;
1612 scrub_reverse_distance = 0;
1614 delta = 0.01 * (drag_info.current_pointer_x - last_scrub_x);
1615 session->request_transport_speed (session->transport_speed() + delta);
1619 /* if there have been more than 2 opposite motion moves detected, or one that moves
1620 back more than 10 pixels, reverse direction
1623 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1625 if (scrubbing_direction > 0) {
1626 /* was forwards, go backwards */
1627 session->request_transport_speed (-0.1);
1628 scrubbing_direction = -1;
1630 /* was backwards, go forwards */
1631 session->request_transport_speed (0.1);
1632 scrubbing_direction = 1;
1635 scrub_reverse_distance = 0;
1636 scrub_reversals = 0;
1640 last_scrub_x = drag_info.current_pointer_x;
1644 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1646 if (event->motion.is_hint) {
1649 /* We call this so that MOTION_NOTIFY events continue to be
1650 delivered to the canvas. We need to do this because we set
1651 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1652 the density of the events, at the expense of a round-trip
1653 to the server. Given that this will mostly occur on cases
1654 where DISPLAY = :0.0, and given the cost of what the motion
1655 event might do, its a good tradeoff.
1658 track_canvas->get_pointer (x, y);
1661 if (current_stepping_trackview) {
1662 /* don't keep the persistent stepped trackview if the mouse moves */
1663 current_stepping_trackview = 0;
1664 step_timeout.disconnect ();
1667 if (session && session->actively_recording()) {
1668 /* Sorry. no dragging stuff around while we record */
1672 drag_info.item_type = item_type;
1673 drag_info.last_pointer_x = drag_info.current_pointer_x;
1674 drag_info.last_pointer_y = drag_info.current_pointer_y;
1675 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1676 &drag_info.current_pointer_y);
1679 switch (mouse_mode) {
1690 if (!from_autoscroll && drag_info.item) {
1691 /* item != 0 is the best test i can think of for dragging.
1693 if (!drag_info.move_threshold_passed) {
1695 bool x_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1696 bool y_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1698 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1700 // and change the initial grab loc/frame if this drag info wants us to
1702 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1703 drag_info.grab_frame = drag_info.current_pointer_frame;
1704 drag_info.grab_x = drag_info.current_pointer_x;
1705 drag_info.grab_y = drag_info.current_pointer_y;
1706 drag_info.last_pointer_frame = drag_info.grab_frame;
1707 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1712 switch (item_type) {
1713 case PlayheadCursorItem:
1715 case RangeMarkerBarItem:
1716 case TransportMarkerBarItem:
1717 case CdMarkerBarItem:
1718 case GainControlPointItem:
1719 case RedirectAutomationControlPointItem:
1720 case GainAutomationControlPointItem:
1721 case PanAutomationControlPointItem:
1722 case TempoMarkerItem:
1723 case MeterMarkerItem:
1724 case RegionViewNameHighlight:
1725 case StartSelectionTrimItem:
1726 case EndSelectionTrimItem:
1729 case RedirectAutomationLineItem:
1730 case GainAutomationLineItem:
1731 case PanAutomationLineItem:
1732 case FadeInHandleItem:
1733 case FadeOutHandleItem:
1734 /* <CMT Additions> */
1735 case ImageFrameHandleStartItem:
1736 case ImageFrameHandleEndItem:
1737 case MarkerViewHandleStartItem:
1738 case MarkerViewHandleEndItem:
1739 /* </CMT Additions> */
1740 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1741 (event->motion.state & Gdk::BUTTON2_MASK))) {
1742 if (!from_autoscroll) {
1743 maybe_autoscroll_horizontally (&event->motion);
1745 (this->*(drag_info.motion_callback)) (item, event);
1754 switch (mouse_mode) {
1759 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1760 (event->motion.state & GDK_BUTTON2_MASK))) {
1761 if (!from_autoscroll) {
1762 maybe_autoscroll (&event->motion);
1764 (this->*(drag_info.motion_callback)) (item, event);
1775 track_canvas_motion (event);
1776 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1784 Editor::break_drag ()
1786 stop_canvas_autoscroll ();
1787 hide_verbose_canvas_cursor ();
1789 if (drag_info.item) {
1790 drag_info.item->ungrab (0);
1792 /* put it back where it came from */
1797 drag_info.item->i2w (cxw, cyw);
1798 drag_info.item->move (drag_info.original_x - cxw, drag_info.original_y - cyw);
1805 Editor::finalize_drag ()
1808 drag_info.copy = false;
1809 drag_info.motion_callback = 0;
1810 drag_info.finished_callback = 0;
1811 drag_info.dest_trackview = 0;
1812 drag_info.source_trackview = 0;
1813 drag_info.last_frame_position = 0;
1814 drag_info.grab_frame = 0;
1815 drag_info.last_pointer_frame = 0;
1816 drag_info.current_pointer_frame = 0;
1817 drag_info.brushing = false;
1818 range_marker_drag_rect->hide();
1819 drag_info.clear_copied_locations ();
1823 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1825 if (drag_info.item == 0) {
1826 fatal << _("programming error: start_grab called without drag item") << endmsg;
1832 cursor = which_grabber_cursor ();
1835 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1837 if (event->button.button == 2) {
1838 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
1839 drag_info.y_constrained = true;
1840 drag_info.x_constrained = false;
1842 drag_info.y_constrained = false;
1843 drag_info.x_constrained = true;
1846 drag_info.x_constrained = false;
1847 drag_info.y_constrained = false;
1850 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1851 drag_info.last_pointer_frame = drag_info.grab_frame;
1852 drag_info.current_pointer_frame = drag_info.grab_frame;
1853 drag_info.current_pointer_x = drag_info.grab_x;
1854 drag_info.current_pointer_y = drag_info.grab_y;
1855 drag_info.last_pointer_x = drag_info.current_pointer_x;
1856 drag_info.last_pointer_y = drag_info.current_pointer_y;
1857 drag_info.cumulative_x_drag = 0;
1858 drag_info.cumulative_y_drag = 0;
1859 drag_info.first_move = true;
1860 drag_info.move_threshold_passed = false;
1861 drag_info.want_move_threshold = false;
1862 drag_info.pointer_frame_offset = 0;
1863 drag_info.brushing = false;
1864 drag_info.clear_copied_locations ();
1866 drag_info.original_x = 0;
1867 drag_info.original_y = 0;
1868 drag_info.item->i2w (drag_info.original_x, drag_info.original_y);
1870 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1872 event->button.time);
1874 if (session && session->transport_rolling()) {
1875 drag_info.was_rolling = true;
1877 drag_info.was_rolling = false;
1880 switch (snap_type) {
1881 case SnapToRegionStart:
1882 case SnapToRegionEnd:
1883 case SnapToRegionSync:
1884 case SnapToRegionBoundary:
1885 build_region_boundary_cache ();
1893 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1895 drag_info.item->ungrab (0);
1896 drag_info.item = new_item;
1899 cursor = which_grabber_cursor ();
1902 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1906 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1908 bool did_drag = false;
1910 stop_canvas_autoscroll ();
1912 if (drag_info.item == 0) {
1916 drag_info.item->ungrab (event->button.time);
1918 if (drag_info.finished_callback) {
1919 drag_info.last_pointer_x = drag_info.current_pointer_x;
1920 drag_info.last_pointer_y = drag_info.current_pointer_y;
1921 (this->*(drag_info.finished_callback)) (item, event);
1924 did_drag = !drag_info.first_move;
1926 hide_verbose_canvas_cursor();
1934 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1936 drag_info.item = item;
1937 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1938 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1942 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1943 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1947 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1949 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes64_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1953 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1955 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1957 nframes64_t fade_length;
1959 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1960 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1966 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1970 if (pos < (arv->region()->position() + 64)) {
1971 fade_length = 64; // this should be a minimum defined somewhere
1972 } else if (pos > arv->region()->last_frame()) {
1973 fade_length = arv->region()->length();
1975 fade_length = pos - arv->region()->position();
1977 /* mapover the region selection */
1979 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1981 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1987 tmp->reset_fade_in_shape_width (fade_length);
1990 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1992 drag_info.first_move = false;
1996 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1998 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2000 nframes64_t fade_length;
2002 if (drag_info.first_move) return;
2004 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2005 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2010 if (pos < (arv->region()->position() + 64)) {
2011 fade_length = 64; // this should be a minimum defined somewhere
2012 } else if (pos > arv->region()->last_frame()) {
2013 fade_length = arv->region()->length();
2015 fade_length = pos - arv->region()->position();
2018 begin_reversible_command (_("change fade in length"));
2020 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2022 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2028 AutomationList& alist = tmp->audio_region()->fade_in();
2029 XMLNode &before = alist.get_state();
2031 tmp->audio_region()->set_fade_in_length (fade_length);
2032 tmp->audio_region()->set_fade_in_active (true);
2034 XMLNode &after = alist.get_state();
2035 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
2038 commit_reversible_command ();
2042 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
2044 drag_info.item = item;
2045 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
2046 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
2050 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
2051 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
2055 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2057 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes64_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
2061 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2063 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2065 nframes64_t fade_length;
2067 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2068 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2073 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2077 if (pos > (arv->region()->last_frame() - 64)) {
2078 fade_length = 64; // this should really be a minimum fade defined somewhere
2080 else if (pos < arv->region()->position()) {
2081 fade_length = arv->region()->length();
2084 fade_length = arv->region()->last_frame() - pos;
2087 /* mapover the region selection */
2089 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2091 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2097 tmp->reset_fade_out_shape_width (fade_length);
2100 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
2102 drag_info.first_move = false;
2106 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2108 if (drag_info.first_move) return;
2110 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2112 nframes64_t fade_length;
2114 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2115 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2121 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2125 if (pos > (arv->region()->last_frame() - 64)) {
2126 fade_length = 64; // this should really be a minimum fade defined somewhere
2128 else if (pos < arv->region()->position()) {
2129 fade_length = arv->region()->length();
2132 fade_length = arv->region()->last_frame() - pos;
2135 begin_reversible_command (_("change fade out length"));
2137 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2139 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2145 AutomationList& alist = tmp->audio_region()->fade_out();
2146 XMLNode &before = alist.get_state();
2148 tmp->audio_region()->set_fade_out_length (fade_length);
2149 tmp->audio_region()->set_fade_out_active (true);
2151 XMLNode &after = alist.get_state();
2152 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
2155 commit_reversible_command ();
2159 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2161 drag_info.item = item;
2162 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2163 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2167 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2168 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2172 Cursor* cursor = (Cursor *) drag_info.data;
2174 if (cursor == playhead_cursor) {
2175 _dragging_playhead = true;
2177 if (session && drag_info.was_rolling) {
2178 session->request_stop ();
2181 if (session && session->is_auditioning()) {
2182 session->cancel_audition ();
2186 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2188 show_verbose_time_cursor (cursor->current_frame, 10);
2192 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2194 Cursor* cursor = (Cursor *) drag_info.data;
2195 nframes64_t adjusted_frame;
2197 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2198 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2204 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2205 if (cursor == playhead_cursor) {
2206 snap_to (adjusted_frame);
2210 if (adjusted_frame == drag_info.last_pointer_frame) return;
2212 cursor->set_position (adjusted_frame);
2214 show_verbose_time_cursor (cursor->current_frame, 10);
2217 track_canvas->update_now ();
2219 UpdateAllTransportClocks (cursor->current_frame);
2221 drag_info.last_pointer_frame = adjusted_frame;
2222 drag_info.first_move = false;
2226 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2228 if (drag_info.first_move) return;
2230 cursor_drag_motion_callback (item, event);
2232 _dragging_playhead = false;
2234 if (item == &playhead_cursor->canvas_item) {
2236 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2242 Editor::update_marker_drag_item (Location *location)
2244 double x1 = frame_to_pixel (location->start());
2245 double x2 = frame_to_pixel (location->end());
2247 if (location->is_mark()) {
2248 marker_drag_line_points.front().set_x(x1);
2249 marker_drag_line_points.back().set_x(x1);
2250 marker_drag_line->property_points() = marker_drag_line_points;
2252 range_marker_drag_rect->property_x1() = x1;
2253 range_marker_drag_rect->property_x2() = x2;
2258 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2262 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2263 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2269 Location *location = find_location_from_marker (marker, is_start);
2271 drag_info.item = item;
2272 drag_info.data = marker;
2273 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2274 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2278 _dragging_edit_point = true;
2280 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2282 update_marker_drag_item (location);
2284 if (location->is_mark()) {
2285 // marker_drag_line->show();
2286 // marker_drag_line->raise_to_top();
2288 range_marker_drag_rect->show();
2289 //range_marker_drag_rect->raise_to_top();
2293 show_verbose_time_cursor (location->start(), 10);
2295 show_verbose_time_cursor (location->end(), 10);
2298 Selection::Operation op = Keyboard::selection_type (event->button.state);
2301 case Selection::Toggle:
2302 selection->toggle (marker);
2304 case Selection::Set:
2305 if (!selection->selected (marker)) {
2306 selection->set (marker);
2309 case Selection::Extend:
2311 Locations::LocationList ll;
2312 list<Marker*> to_add;
2314 selection->markers.range (s, e);
2315 s = min (marker->position(), s);
2316 e = max (marker->position(), e);
2319 if (e < max_frames) {
2322 session->locations()->find_all_between (s, e, ll, Location::Flags (0));
2323 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2324 LocationMarkers* lm = find_location_markers (*i);
2327 to_add.push_back (lm->start);
2330 to_add.push_back (lm->end);
2334 if (!to_add.empty()) {
2335 selection->add (to_add);
2339 case Selection::Add:
2340 selection->add (marker);
2344 /* set up copies for us to manipulate during the drag */
2346 drag_info.clear_copied_locations ();
2348 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
2349 Location *l = find_location_from_marker (*i, is_start);
2350 drag_info.copied_locations.push_back (new Location (*l));
2355 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2357 nframes64_t f_delta = 0;
2358 nframes64_t newframe;
2360 bool move_both = false;
2361 Marker* dragged_marker = (Marker*) drag_info.data;
2363 Location *real_location;
2364 Location *copy_location;
2366 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2367 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2372 nframes64_t next = newframe;
2374 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2375 snap_to (newframe, 0, true);
2378 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2382 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2386 MarkerSelection::iterator i;
2387 list<Location*>::iterator x;
2389 /* find the marker we're dragging, and compute the delta */
2391 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2392 x != drag_info.copied_locations.end() && i != selection->markers.end();
2398 if (marker == dragged_marker) {
2400 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2405 if (real_location->is_mark()) {
2406 f_delta = newframe - copy_location->start();
2410 switch (marker->type()) {
2412 case Marker::LoopStart:
2413 case Marker::PunchIn:
2414 f_delta = newframe - copy_location->start();
2418 case Marker::LoopEnd:
2419 case Marker::PunchOut:
2420 f_delta = newframe - copy_location->end();
2423 /* what kind of marker is this ? */
2431 if (i == selection->markers.end()) {
2432 /* hmm, impossible - we didn't find the dragged marker */
2436 /* now move them all */
2438 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2439 x != drag_info.copied_locations.end() && i != selection->markers.end();
2445 /* call this to find out if its the start or end */
2447 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2451 if (real_location->locked()) {
2455 if (copy_location->is_mark()) {
2459 copy_location->set_start (copy_location->start() + f_delta);
2463 nframes64_t new_start = copy_location->start() + f_delta;
2464 nframes64_t new_end = copy_location->end() + f_delta;
2466 if (is_start) { // start-of-range marker
2469 copy_location->set_start (new_start);
2470 copy_location->set_end (new_end);
2471 } else if (new_start < copy_location->end()) {
2472 copy_location->set_start (new_start);
2474 snap_to (next, 1, true);
2475 copy_location->set_end (next);
2476 copy_location->set_start (newframe);
2479 } else { // end marker
2482 copy_location->set_end (new_end);
2483 copy_location->set_start (new_start);
2484 } else if (new_end > copy_location->start()) {
2485 copy_location->set_end (new_end);
2486 } else if (newframe > 0) {
2487 snap_to (next, -1, true);
2488 copy_location->set_start (next);
2489 copy_location->set_end (newframe);
2493 update_marker_drag_item (copy_location);
2495 LocationMarkers* lm = find_location_markers (real_location);
2498 lm->set_position (copy_location->start(), copy_location->end());
2502 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2503 drag_info.first_move = false;
2505 if (drag_info.copied_locations.empty()) {
2509 edit_point_clock.set (drag_info.copied_locations.front()->start());
2510 show_verbose_time_cursor (newframe, 10);
2513 track_canvas->update_now ();
2518 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2520 if (drag_info.first_move) {
2522 /* just a click, do nothing but finish
2523 off the selection process
2526 Selection::Operation op = Keyboard::selection_type (event->button.state);
2527 Marker* marker = (Marker *) drag_info.data;
2530 case Selection::Set:
2531 if (selection->selected (marker) && selection->markers.size() > 1) {
2532 selection->set (marker);
2536 case Selection::Toggle:
2537 case Selection::Extend:
2538 case Selection::Add:
2545 _dragging_edit_point = false;
2548 begin_reversible_command ( _("move marker") );
2549 XMLNode &before = session->locations()->get_state();
2551 MarkerSelection::iterator i;
2552 list<Location*>::iterator x;
2555 for (i = selection->markers.begin(), x = drag_info.copied_locations.begin();
2556 x != drag_info.copied_locations.end() && i != selection->markers.end();
2559 Location * location = find_location_from_marker ((*i), is_start);
2563 if (location->locked()) {
2567 if (location->is_mark()) {
2568 location->set_start ((*x)->start());
2570 location->set ((*x)->start(), (*x)->end());
2575 XMLNode &after = session->locations()->get_state();
2576 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2577 commit_reversible_command ();
2579 marker_drag_line->hide();
2580 range_marker_drag_rect->hide();
2584 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2587 MeterMarker* meter_marker;
2589 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2590 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2594 meter_marker = dynamic_cast<MeterMarker*> (marker);
2596 MetricSection& section (meter_marker->meter());
2598 if (!section.movable()) {
2602 drag_info.item = item;
2603 drag_info.copy = false;
2604 drag_info.data = marker;
2605 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2606 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2610 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2612 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2616 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2619 MeterMarker* meter_marker;
2621 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2622 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2626 meter_marker = dynamic_cast<MeterMarker*> (marker);
2628 // create a dummy marker for visual representation of moving the copy.
2629 // The actual copying is not done before we reach the finish callback.
2631 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2632 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2633 *new MeterSection(meter_marker->meter()));
2635 drag_info.item = &new_marker->the_item();
2636 drag_info.copy = true;
2637 drag_info.data = new_marker;
2638 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2639 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2643 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2645 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2649 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2651 MeterMarker* marker = (MeterMarker *) drag_info.data;
2652 nframes64_t adjusted_frame;
2654 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2655 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2661 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2662 snap_to (adjusted_frame);
2665 if (adjusted_frame == drag_info.last_pointer_frame) return;
2667 marker->set_position (adjusted_frame);
2670 drag_info.last_pointer_frame = adjusted_frame;
2671 drag_info.first_move = false;
2673 show_verbose_time_cursor (adjusted_frame, 10);
2677 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2679 if (drag_info.first_move) return;
2681 meter_marker_drag_motion_callback (drag_info.item, event);
2683 MeterMarker* marker = (MeterMarker *) drag_info.data;
2686 TempoMap& map (session->tempo_map());
2687 map.bbt_time (drag_info.last_pointer_frame, when);
2689 if (drag_info.copy == true) {
2690 begin_reversible_command (_("copy meter mark"));
2691 XMLNode &before = map.get_state();
2692 map.add_meter (marker->meter(), when);
2693 XMLNode &after = map.get_state();
2694 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2695 commit_reversible_command ();
2697 // delete the dummy marker we used for visual representation of copying.
2698 // a new visual marker will show up automatically.
2701 begin_reversible_command (_("move meter mark"));
2702 XMLNode &before = map.get_state();
2703 map.move_meter (marker->meter(), when);
2704 XMLNode &after = map.get_state();
2705 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2706 commit_reversible_command ();
2711 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2714 TempoMarker* tempo_marker;
2716 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2717 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2721 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2722 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2726 MetricSection& section (tempo_marker->tempo());
2728 if (!section.movable()) {
2732 drag_info.item = item;
2733 drag_info.copy = false;
2734 drag_info.data = marker;
2735 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2736 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2740 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2741 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2745 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2748 TempoMarker* tempo_marker;
2750 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2751 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2755 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2756 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2760 // create a dummy marker for visual representation of moving the copy.
2761 // The actual copying is not done before we reach the finish callback.
2763 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2764 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2765 *new TempoSection(tempo_marker->tempo()));
2767 drag_info.item = &new_marker->the_item();
2768 drag_info.copy = true;
2769 drag_info.data = new_marker;
2770 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2771 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2775 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2777 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2781 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2783 TempoMarker* marker = (TempoMarker *) drag_info.data;
2784 nframes64_t adjusted_frame;
2786 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2787 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2793 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2794 snap_to (adjusted_frame);
2797 if (adjusted_frame == drag_info.last_pointer_frame) return;
2799 /* OK, we've moved far enough to make it worth actually move the thing. */
2801 marker->set_position (adjusted_frame);
2803 show_verbose_time_cursor (adjusted_frame, 10);
2805 drag_info.last_pointer_frame = adjusted_frame;
2806 drag_info.first_move = false;
2810 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2812 if (drag_info.first_move) return;
2814 tempo_marker_drag_motion_callback (drag_info.item, event);
2816 TempoMarker* marker = (TempoMarker *) drag_info.data;
2819 TempoMap& map (session->tempo_map());
2820 map.bbt_time (drag_info.last_pointer_frame, when);
2822 if (drag_info.copy == true) {
2823 begin_reversible_command (_("copy tempo mark"));
2824 XMLNode &before = map.get_state();
2825 map.add_tempo (marker->tempo(), when);
2826 XMLNode &after = map.get_state();
2827 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2828 commit_reversible_command ();
2830 // delete the dummy marker we used for visual representation of copying.
2831 // a new visual marker will show up automatically.
2834 begin_reversible_command (_("move tempo mark"));
2835 XMLNode &before = map.get_state();
2836 map.move_tempo (marker->tempo(), when);
2837 XMLNode &after = map.get_state();
2838 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2839 commit_reversible_command ();
2844 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2846 ControlPoint* control_point;
2848 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2849 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2853 // We shouldn't remove the first or last gain point
2854 if (control_point->line.is_last_point(*control_point) ||
2855 control_point->line.is_first_point(*control_point)) {
2859 control_point->line.remove_point (*control_point);
2863 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2865 ControlPoint* control_point;
2867 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2868 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2872 control_point->line.remove_point (*control_point);
2876 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2878 ControlPoint* control_point;
2880 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2881 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2885 drag_info.item = item;
2886 drag_info.data = control_point;
2887 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2888 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2890 start_grab (event, fader_cursor);
2892 // start the grab at the center of the control point so
2893 // the point doesn't 'jump' to the mouse after the first drag
2894 drag_info.grab_x = control_point->get_x();
2895 drag_info.grab_y = control_point->get_y();
2896 control_point->line.parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
2897 track_canvas->w2c(drag_info.grab_x, drag_info.grab_y, drag_info.grab_x, drag_info.grab_y);
2899 drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
2901 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2903 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2904 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2905 drag_info.current_pointer_x + 10, drag_info.current_pointer_y + 10);
2907 show_verbose_canvas_cursor ();
2911 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2913 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2915 double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
2916 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2918 if (event->button.state & Keyboard::SecondaryModifier) {
2923 double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
2924 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2926 // calculate zero crossing point. back off by .01 to stay on the
2927 // positive side of zero
2929 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line.height() - .01;
2930 cp->line.parent_group().i2w(_unused, zero_gain_y);
2932 // make sure we hit zero when passing through
2933 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2934 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2938 if (drag_info.x_constrained) {
2939 cx = drag_info.grab_x;
2941 if (drag_info.y_constrained) {
2942 cy = drag_info.grab_y;
2945 drag_info.cumulative_x_drag = cx - drag_info.grab_x;
2946 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2948 cp->line.parent_group().w2i (cx, cy);
2952 cy = min ((double) cp->line.height(), cy);
2954 //translate cx to frames
2955 nframes64_t cx_frames = unit_to_frame (cx);
2957 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2958 snap_to (cx_frames);
2961 float fraction = 1.0 - (cy / cp->line.height());
2965 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2971 cp->line.point_drag (*cp, cx_frames , fraction, push);
2973 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2975 drag_info.first_move = false;
2979 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2981 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2983 if (drag_info.first_move) {
2987 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2988 reset_point_selection ();
2992 control_point_drag_motion_callback (item, event);
2994 cp->line.end_drag (cp);
2998 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
3000 switch (mouse_mode) {
3002 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
3003 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
3011 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
3015 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
3016 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
3020 start_line_grab (al, event);
3024 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
3028 nframes64_t frame_within_region;
3030 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3031 origin, and ditto for y.
3034 cx = event->button.x;
3035 cy = event->button.y;
3037 line->parent_group().w2i (cx, cy);
3039 frame_within_region = (nframes64_t) floor (cx * frames_per_unit);
3041 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
3042 current_line_drag_info.after)) {
3043 /* no adjacent points */
3047 drag_info.item = &line->grab_item();
3048 drag_info.data = line;
3049 drag_info.motion_callback = &Editor::line_drag_motion_callback;
3050 drag_info.finished_callback = &Editor::line_drag_finished_callback;
3052 start_grab (event, fader_cursor);
3056 double fraction = 1.0 - (cy / line->height());
3058 line->start_drag (0, drag_info.grab_frame, fraction);
3060 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
3061 drag_info.current_pointer_x + 10, drag_info.current_pointer_y + 10);
3062 show_verbose_canvas_cursor ();
3066 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3068 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3070 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
3072 if (event->button.state & Keyboard::SecondaryModifier) {
3076 double cx = drag_info.current_pointer_x;
3077 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
3079 // calculate zero crossing point. back off by .01 to stay on the
3080 // positive side of zero
3082 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
3084 line->parent_group().i2w(_unused, zero_gain_y);
3086 // make sure we hit zero when passing through
3087 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
3088 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
3092 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
3095 cy = min ((double) line->height(), cy);
3097 double fraction = 1.0 - (cy / line->height());
3101 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3107 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
3109 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
3113 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3115 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3116 line_drag_motion_callback (item, event);
3121 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3123 if (selection->regions.empty() || clicked_regionview == 0) {
3127 drag_info.copy = false;
3128 drag_info.item = item;
3129 drag_info.data = clicked_regionview;
3131 if (Config->get_edit_mode() == Splice) {
3132 drag_info.motion_callback = &Editor::region_drag_splice_motion_callback;
3133 drag_info.finished_callback = &Editor::region_drag_splice_finished_callback;
3135 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3136 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3142 TimeAxisView* tvp = clicked_trackview;
3143 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3145 if (tv && tv->is_audio_track()) {
3146 speed = tv->get_diskstream()->speed();
3149 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3150 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3151 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3152 drag_info.dest_trackview = drag_info.source_trackview;
3153 // we want a move threshold
3154 drag_info.want_move_threshold = true;
3156 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3158 begin_reversible_command (_("move region(s)"));
3160 _region_motion_group->raise_to_top ();
3162 /* sync the canvas to what we think is its current state */
3163 track_canvas->update_now();
3167 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
3169 if (selection->regions.empty() || clicked_regionview == 0) {
3173 drag_info.copy = true;
3174 drag_info.item = item;
3175 drag_info.data = clicked_regionview;
3179 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
3180 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
3183 if (atv && atv->is_audio_track()) {
3184 speed = atv->get_diskstream()->speed();
3187 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3188 drag_info.dest_trackview = drag_info.source_trackview;
3189 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3190 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3191 // we want a move threshold
3192 drag_info.want_move_threshold = true;
3193 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3194 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3195 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3196 _region_motion_group->raise_to_top ();
3200 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
3202 if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) {
3206 drag_info.copy = false;
3207 drag_info.item = item;
3208 drag_info.data = clicked_regionview;
3209 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3210 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3215 TimeAxisView* tvp = clicked_trackview;
3216 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3218 if (tv && tv->is_audio_track()) {
3219 speed = tv->get_diskstream()->speed();
3222 drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3223 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3224 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3225 drag_info.dest_trackview = drag_info.source_trackview;
3226 // we want a move threshold
3227 drag_info.want_move_threshold = true;
3228 drag_info.brushing = true;
3230 begin_reversible_command (_("Drag region brush"));
3234 Editor::possibly_copy_regions_during_grab (GdkEvent* event)
3236 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
3238 drag_info.want_move_threshold = false; // don't copy again
3240 /* duplicate the regionview(s) and region(s) */
3242 vector<RegionView*> new_regionviews;
3244 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3246 AudioRegionView* arv;
3248 if ((arv = dynamic_cast<AudioRegionView*>(*i)) == 0) {
3249 /* XXX handle MIDI here */
3253 const boost::shared_ptr<const Region> original = arv->region();
3254 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
3255 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (region_copy);
3257 nrv = new AudioRegionView (*arv, ar);
3258 nrv->get_canvas_group()->show ();
3260 new_regionviews.push_back (nrv);
3263 if (new_regionviews.empty()) {
3267 /* reset selection to new regionviews. This will not set selection visual status for
3268 these regionviews since they don't belong to a track, so do that by hand too.
3271 selection->set (new_regionviews);
3273 for (vector<RegionView*>::iterator i = new_regionviews.begin(); i != new_regionviews.end(); ++i) {
3274 (*i)->set_selected (true);
3277 /* reset drag_info data to reflect the fact that we are dragging the copies */
3279 drag_info.data = new_regionviews.front();
3281 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3283 sync the canvas to what we think is its current state
3284 without it, the canvas seems to
3285 "forget" to update properly after the upcoming reparent()
3286 ..only if the mouse is in rapid motion at the time of the grab.
3287 something to do with regionview creation raking so long?
3289 track_canvas->update_now();
3294 Editor::check_region_drag_possible (AudioTimeAxisView** tv)
3296 /* Which trackview is this ? */
3298 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
3299 (*tv) = dynamic_cast<AudioTimeAxisView*>(tvp);
3301 /* The region motion is only processed if the pointer is over
3305 if (!(*tv) || !(*tv)->is_audio_track()) {
3306 /* To make sure we hide the verbose canvas cursor when the mouse is
3307 not held over and audiotrack.
3309 hide_verbose_canvas_cursor ();
3316 struct RegionSelectionByPosition {
3317 bool operator() (RegionView*a, RegionView* b) {
3318 return a->region()->position () < b->region()->position();
3323 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3325 AudioTimeAxisView* tv;
3327 if (!check_region_drag_possible (&tv)) {
3331 if (!drag_info.move_threshold_passed) {
3337 if (drag_info.current_pointer_x - drag_info.grab_x > 0) {
3343 RegionSelection copy (selection->regions);
3345 RegionSelectionByPosition cmp;
3348 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
3350 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(*i)->get_time_axis_view());
3356 boost::shared_ptr<Playlist> playlist;
3358 if ((playlist = atv->playlist()) == 0) {
3362 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
3367 if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) {
3371 if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) {
3377 playlist->shuffle ((*i)->region(), dir);
3379 drag_info.grab_x = drag_info.current_pointer_x;
3384 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3389 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3393 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
3394 nframes64_t pending_region_position = 0;
3395 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
3396 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
3397 bool clamp_y_axis = false;
3398 vector<int32_t> height_list(512) ;
3399 vector<int32_t>::iterator j;
3400 AudioTimeAxisView* tv;
3402 possibly_copy_regions_during_grab (event);
3404 if (!check_region_drag_possible (&tv)) {
3408 original_pointer_order = drag_info.dest_trackview->order;
3410 /************************************************************
3412 ************************************************************/
3414 if (drag_info.brushing) {
3415 clamp_y_axis = true;
3420 if ((pointer_y_span = (drag_info.dest_trackview->order - tv->order)) != 0) {
3422 int32_t children = 0, numtracks = 0;
3423 // XXX hard coding track limit, oh my, so very very bad
3424 bitset <1024> tracks (0x00);
3425 /* get a bitmask representing the visible tracks */
3427 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3428 TimeAxisView *tracklist_timeview;
3429 tracklist_timeview = (*i);
3430 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
3431 list<TimeAxisView*> children_list;
3433 /* zeroes are audio tracks. ones are other types. */
3435 if (!atv2->hidden()) {
3437 if (visible_y_high < atv2->order) {
3438 visible_y_high = atv2->order;
3440 if (visible_y_low > atv2->order) {
3441 visible_y_low = atv2->order;
3444 if (!atv2->is_audio_track()) {
3445 tracks = tracks |= (0x01 << atv2->order);
3448 height_list[atv2->order] = (*i)->current_height();
3450 if ((children_list = atv2->get_child_list()).size() > 0) {
3451 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
3452 tracks = tracks |= (0x01 << (atv2->order + children));
3453 height_list[atv2->order + children] = (*j)->current_height();
3461 /* find the actual span according to the canvas */
3463 canvas_pointer_y_span = pointer_y_span;
3464 if (drag_info.dest_trackview->order >= tv->order) {
3466 for (y = tv->order; y < drag_info.dest_trackview->order; y++) {
3467 if (height_list[y] == 0 ) {
3468 canvas_pointer_y_span--;
3473 for (y = drag_info.dest_trackview->order;y <= tv->order; y++) {
3474 if ( height_list[y] == 0 ) {
3475 canvas_pointer_y_span++;
3480 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3481 RegionView* rv2 = (*i);
3482 double ix1, ix2, iy1, iy2;
3485 if (rv2->region()->locked()) {
3489 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3490 rv2->get_canvas_group()->i2w (ix1, iy1);
3491 iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
3493 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3494 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3496 if (atv2->order != original_pointer_order) {
3497 /* this isn't the pointer track */
3499 if (canvas_pointer_y_span > 0) {
3501 /* moving up the canvas */
3502 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
3504 int32_t visible_tracks = 0;
3505 while (visible_tracks < canvas_pointer_y_span ) {
3508 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3509 /* we're passing through a hidden track */
3514 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3515 clamp_y_axis = true;
3519 clamp_y_axis = true;
3522 } else if (canvas_pointer_y_span < 0) {
3524 /*moving down the canvas*/
3526 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3529 int32_t visible_tracks = 0;
3531 while (visible_tracks > canvas_pointer_y_span ) {
3534 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3538 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3539 clamp_y_axis = true;
3544 clamp_y_axis = true;
3550 /* this is the pointer's track */
3551 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3552 clamp_y_axis = true;
3553 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3554 clamp_y_axis = true;
3562 } else if (drag_info.dest_trackview == tv) {
3563 clamp_y_axis = true;
3567 if (!clamp_y_axis) {
3568 drag_info.dest_trackview = tv;
3571 /************************************************************
3573 ************************************************************/
3575 /* compute the amount of pointer motion in frames, and where
3576 the region would be if we moved it by that much.
3578 if ( drag_info.move_threshold_passed ) {
3580 if (drag_info.current_pointer_frame >= drag_info.pointer_frame_offset) {
3582 nframes64_t sync_frame;
3583 nframes64_t sync_offset;
3586 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3588 sync_offset = rv->region()->sync_offset (sync_dir);
3590 /* we don't handle a sync point that lies before zero.
3592 if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) {
3593 sync_frame = pending_region_position + (sync_dir*sync_offset);
3595 /* we snap if the snap modifier is not enabled.
3598 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3599 snap_to (sync_frame);
3602 pending_region_position = rv->region()->adjust_to_sync (sync_frame);
3605 pending_region_position = drag_info.last_frame_position;
3609 pending_region_position = 0;
3612 if (pending_region_position > max_frames - rv->region()->length()) {
3613 pending_region_position = drag_info.last_frame_position;
3616 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3618 bool x_move_allowed;
3620 if (Config->get_edit_mode() == Lock) {
3621 if (drag_info.copy) {
3622 x_move_allowed = !drag_info.x_constrained;
3624 /* in locked edit mode, reverse the usual meaning of x_constrained */
3625 x_move_allowed = drag_info.x_constrained;
3628 x_move_allowed = !drag_info.x_constrained;
3631 if (( pending_region_position != drag_info.last_frame_position) && x_move_allowed ) {
3633 /* now compute the canvas unit distance we need to move the regionview
3634 to make it appear at the new location.
3637 if (pending_region_position > drag_info.last_frame_position) {
3638 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3640 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3641 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3643 RegionView* rv2 = (*i);
3645 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3647 double ix1, ix2, iy1, iy2;
3648 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3649 rv2->get_canvas_group()->i2w (ix1, iy1);
3651 if (-x_delta > ix1 + horizontal_adjustment.get_value()) {
3653 pending_region_position = drag_info.last_frame_position;
3660 drag_info.last_frame_position = pending_region_position;
3667 /* threshold not passed */
3672 /*************************************************************
3674 ************************************************************/
3676 if (x_delta == 0 && (pointer_y_span == 0)) {
3677 /* haven't reached next snap point, and we're not switching
3678 trackviews. nothing to do.
3683 /*************************************************************
3685 ************************************************************/
3686 bool do_move = true;
3687 if (drag_info.first_move) {
3688 if (!drag_info.move_threshold_passed) {
3695 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3696 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3698 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3700 RegionView* rv = (*i);
3701 double ix1, ix2, iy1, iy2;
3702 int32_t temp_pointer_y_span = pointer_y_span;
3704 if (rv->region()->locked()) {
3708 /* get item BBox, which will be relative to parent. so we have
3709 to query on a child, then convert to world coordinates using
3713 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3714 rv->get_canvas_group()->i2w (ix1, iy1);
3716 /* for evaluation of the track position of iy1, we have to adjust
3717 to allow for the vertical scrolling adjustment and the height of the timebars.
3720 iy1 += get_trackview_group_vertical_offset ();
3721 if (drag_info.first_move) {
3723 // hide any dependent views
3725 rv->get_time_axis_view().hide_dependent_views (*rv);
3728 reparent to a non scrolling group so that we can keep the
3729 region selection above all time axis views.
3730 reparenting means we have to move the rv as the two
3731 parent groups have different coordinates.
3734 rv->get_canvas_group()->property_y() = iy1 - 1;
3735 rv->get_canvas_group()->reparent(*_region_motion_group);
3737 rv->fake_set_opaque (true);
3740 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3741 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3742 AudioTimeAxisView* temp_atv;
3744 if ((pointer_y_span != 0) && !clamp_y_axis) {
3747 for (j = height_list.begin(); j!= height_list.end(); j++) {
3748 if (x == canvas_atv->order) {
3749 /* we found the track the region is on */
3750 if (x != original_pointer_order) {
3751 /*this isn't from the same track we're dragging from */
3752 temp_pointer_y_span = canvas_pointer_y_span;
3754 while (temp_pointer_y_span > 0) {
3755 /* we're moving up canvas-wise,
3756 so we need to find the next track height
3758 if (j != height_list.begin()) {
3761 if (x != original_pointer_order) {
3762 /* we're not from the dragged track, so ignore hidden tracks. */
3764 temp_pointer_y_span++;
3768 temp_pointer_y_span--;
3771 while (temp_pointer_y_span < 0) {
3773 if (x != original_pointer_order) {
3775 temp_pointer_y_span--;
3779 if (j != height_list.end()) {
3782 temp_pointer_y_span++;
3784 /* find out where we'll be when we move and set height accordingly */
3786 tvp2 = trackview_by_y_position (iy1 + y_delta);
3787 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3788 rv->set_height (temp_atv->current_height());
3790 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3791 personally, i think this can confuse things, but never mind.
3794 //const GdkColor& col (temp_atv->view->get_region_color());
3795 //rv->set_color (const_cast<GdkColor&>(col));
3802 if (drag_info.brushing) {
3803 mouse_brush_insert_region (rv, pending_region_position);
3805 rv->move (x_delta, y_delta);
3808 } /* foreach region */
3812 if (drag_info.first_move && drag_info.move_threshold_passed) {
3813 cursor_group->raise_to_top();
3814 drag_info.first_move = false;
3817 if (x_delta != 0 && !drag_info.brushing) {
3818 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3823 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3825 bool nocommit = true;
3826 vector<RegionView*> copies;
3827 RouteTimeAxisView* source_tv;
3828 boost::shared_ptr<Diskstream> ds;
3829 boost::shared_ptr<Playlist> from_playlist;
3830 vector<RegionView*> new_selection;
3831 typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
3832 PlaylistSet modified_playlists;
3833 PlaylistSet frozen_playlists;
3834 list <sigc::connection> modified_playlist_connections;
3835 pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
3837 /* first_move is set to false if the regionview has been moved in the
3841 if (drag_info.first_move) {
3848 if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
3849 selection->set (pre_drag_region_selection);
3850 pre_drag_region_selection.clear ();
3853 if (drag_info.brushing) {
3854 /* all changes were made during motion event handlers */
3856 if (drag_info.copy) {
3857 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3858 copies.push_back (*i);
3867 /* reverse this here so that we have the correct logic to finalize
3871 if (Config->get_edit_mode() == Lock && !drag_info.copy) {
3872 drag_info.x_constrained = !drag_info.x_constrained;
3875 if (drag_info.copy) {
3876 if (drag_info.x_constrained) {
3877 op_string = _("fixed time region copy");
3879 op_string = _("region copy");
3882 if (drag_info.x_constrained) {
3883 op_string = _("fixed time region drag");
3885 op_string = _("region drag");
3889 begin_reversible_command (op_string);
3891 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3893 RegionView* rv = (*i);
3894 double ix1, ix2, iy1, iy2;
3895 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3896 rv->get_canvas_group()->i2w (ix1, iy1);
3897 iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
3899 TimeAxisView* dest_tv = trackview_by_y_position (iy1);
3900 AudioTimeAxisView* dest_atv = dynamic_cast<AudioTimeAxisView*>(dest_tv);
3902 bool changed_tracks, changed_position;
3905 if (rv->region()->locked()) {
3910 /* adjust for track speed */
3914 if (dest_atv && dest_atv->get_diskstream()) {
3915 speed = dest_atv->get_diskstream()->speed();
3918 changed_position = (drag_info.last_frame_position != (nframes64_t) (rv->region()->position()/speed));
3919 changed_tracks = (dest_tv != &rv->get_time_axis_view());
3921 if (changed_position && !drag_info.x_constrained) {
3922 _master_group->w2i(ix1, iy1);
3923 where = (nframes64_t) (unit_to_frame (ix1) * speed);
3925 where = rv->region()->position();
3928 boost::shared_ptr<Region> new_region;
3930 if (drag_info.copy) {
3931 /* we already made a copy */
3932 new_region = rv->region();
3934 /* undo the previous hide_dependent_views so that xfades don't
3935 disappear on copying regions
3938 //rv->get_time_axis_view().reveal_dependent_views (*rv);
3940 } else if (changed_tracks) {
3941 new_region = RegionFactory::create (rv->region());
3944 if (changed_tracks || drag_info.copy) {
3946 boost::shared_ptr<Playlist> to_playlist = dest_atv->playlist();
3948 latest_regionviews.clear ();
3950 sigc::connection c = dest_atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3952 insert_result = modified_playlists.insert (to_playlist);
3953 if (insert_result.second) {
3954 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3957 to_playlist->add_region (new_region, where);
3961 if (!latest_regionviews.empty()) {
3962 // XXX why just the first one ? we only expect one
3963 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
3964 new_selection.push_back (latest_regionviews.front());
3969 motion on the same track. plonk the previously reparented region
3970 back to its original canvas group (its streamview).
3971 No need to do anything for copies as they are fake regions which will be deleted.
3974 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (dest_atv);
3975 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
3976 rv->get_canvas_group()->property_y() = 0;
3978 /* just change the model */
3980 boost::shared_ptr<Playlist> playlist = dest_atv->playlist();
3982 insert_result = modified_playlists.insert (playlist);
3983 if (insert_result.second) {
3984 session->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
3986 /* freeze to avoid lots of relayering in the case of a multi-region drag */
3987 frozen_insert_result = frozen_playlists.insert(playlist);
3988 if (frozen_insert_result.second) {
3992 rv->region()->set_position (where, (void*) this);
3995 if (changed_tracks && !drag_info.copy) {
3997 /* get the playlist where this drag started. we can't use rv->region()->playlist()
3998 because we may have copied the region and it has not been attached to a playlist.
4001 assert ((source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view())));
4002 assert ((ds = source_tv->get_diskstream()));
4003 assert ((from_playlist = ds->playlist()));
4005 /* moved to a different audio track, without copying */
4007 /* the region that used to be in the old playlist is not
4008 moved to the new one - we use a copy of it. as a result,
4009 any existing editor for the region should no longer be
4013 rv->hide_region_editor();
4014 rv->fake_set_opaque (false);
4016 /* remove the region from the old playlist */
4018 insert_result = modified_playlists.insert (from_playlist);
4019 if (insert_result.second) {
4020 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
4023 from_playlist->remove_region ((rv->region()));
4025 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
4026 was selected in all of them, then removing it from a playlist will have removed all
4027 trace of it from the selection (i.e. there were N regions selected, we removed 1,
4028 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
4029 corresponding regionview, and the selection is now empty).
4031 this could have invalidated any and all iterators into the region selection.
4033 the heuristic we use here is: if the region selection is empty, break out of the loop
4034 here. if the region selection is not empty, then restart the loop because we know that
4035 we must have removed at least the region(view) we've just been working on as well as any
4036 that we processed on previous iterations.
4038 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
4039 we can just iterate.
4042 if (selection->regions.empty()) {
4045 i = selection->regions.by_layer().begin();
4052 if (drag_info.copy) {
4053 copies.push_back (rv);
4057 if (new_selection.empty()) {
4058 if (drag_info.copy) {
4059 /* the region(view)s that are selected and being dragged around
4060 are copies and do not belong to any track. remove them
4061 from the selection right here.
4063 selection->clear_regions();
4066 /* this will clear any existing selection that would have been
4067 cleared in the other clause above
4069 selection->set (new_selection);
4072 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
4078 for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
4079 session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));
4081 commit_reversible_command ();
4084 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
4091 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
4093 /* Either add to or set the set the region selection, unless
4094 this is an alignment click (control used)
4097 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
4098 TimeAxisView* tv = &rv.get_time_axis_view();
4099 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
4101 if (atv && atv->is_audio_track()) {
4102 speed = atv->get_diskstream()->speed();
4105 nframes64_t where = get_preferred_edit_position();
4109 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
4111 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
4113 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
4115 align_region (rv.region(), End, (nframes64_t) (where * speed));
4119 align_region (rv.region(), Start, (nframes64_t) (where * speed));
4126 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
4132 nframes64_t frame_rate;
4141 if (Profile->get_sae() || Profile->get_small_screen()) {
4142 m = ARDOUR_UI::instance()->primary_clock.mode();
4144 m = ARDOUR_UI::instance()->secondary_clock.mode();
4148 case AudioClock::BBT:
4149 session->bbt_time (frame, bbt);
4150 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
4153 case AudioClock::SMPTE:
4154 session->smpte_time (frame, smpte);
4155 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4158 case AudioClock::MinSec:
4159 /* XXX this is copied from show_verbose_duration_cursor() */
4160 frame_rate = session->frame_rate();
4161 hours = frame / (frame_rate * 3600);
4162 frame = frame % (frame_rate * 3600);
4163 mins = frame / (frame_rate * 60);
4164 frame = frame % (frame_rate * 60);
4165 secs = (float) frame / (float) frame_rate;
4166 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4170 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
4174 if (xpos >= 0 && ypos >=0) {
4175 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4178 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);
4180 show_verbose_canvas_cursor ();
4184 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
4191 nframes64_t distance, frame_rate;
4193 Meter meter_at_start(session->tempo_map().meter_at(start));
4201 if (Profile->get_sae() || Profile->get_small_screen()) {
4202 m = ARDOUR_UI::instance()->primary_clock.mode ();
4204 m = ARDOUR_UI::instance()->secondary_clock.mode ();
4208 case AudioClock::BBT:
4209 session->bbt_time (start, sbbt);
4210 session->bbt_time (end, ebbt);
4213 /* XXX this computation won't work well if the
4214 user makes a selection that spans any meter changes.
4217 ebbt.bars -= sbbt.bars;
4218 if (ebbt.beats >= sbbt.beats) {
4219 ebbt.beats -= sbbt.beats;
4222 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
4224 if (ebbt.ticks >= sbbt.ticks) {
4225 ebbt.ticks -= sbbt.ticks;
4228 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
4231 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
4234 case AudioClock::SMPTE:
4235 session->smpte_duration (end - start, smpte);
4236 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4239 case AudioClock::MinSec:
4240 /* XXX this stuff should be elsewhere.. */
4241 distance = end - start;
4242 frame_rate = session->frame_rate();
4243 hours = distance / (frame_rate * 3600);
4244 distance = distance % (frame_rate * 3600);
4245 mins = distance / (frame_rate * 60);
4246 distance = distance % (frame_rate * 60);
4247 secs = (float) distance / (float) frame_rate;
4248 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4252 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
4256 if (xpos >= 0 && ypos >=0) {
4257 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4260 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4263 show_verbose_canvas_cursor ();
4267 Editor::collect_new_region_view (RegionView* rv)
4269 latest_regionviews.push_back (rv);
4273 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
4275 if (clicked_regionview == 0) {
4279 /* lets try to create new Region for the selection */
4281 vector<boost::shared_ptr<AudioRegion> > new_regions;
4282 create_region_from_selection (new_regions);
4284 if (new_regions.empty()) {
4288 /* XXX fix me one day to use all new regions */
4290 boost::shared_ptr<Region> region (new_regions.front());
4292 /* add it to the current stream/playlist.
4294 tricky: the streamview for the track will add a new regionview. we will
4295 catch the signal it sends when it creates the regionview to
4296 set the regionview we want to then drag.
4299 latest_regionviews.clear();
4300 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4302 /* A selection grab currently creates two undo/redo operations, one for
4303 creating the new region and another for moving it.
4306 begin_reversible_command (_("selection grab"));
4308 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
4310 XMLNode *before = &(playlist->get_state());
4311 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
4312 XMLNode *after = &(playlist->get_state());
4313 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4315 commit_reversible_command ();
4319 if (latest_regionviews.empty()) {
4320 /* something went wrong */
4324 /* we need to deselect all other regionviews, and select this one
4325 i'm ignoring undo stuff, because the region creation will take care of it
4327 selection->set (latest_regionviews);
4329 drag_info.item = latest_regionviews.front()->get_canvas_group();
4330 drag_info.data = latest_regionviews.front();
4331 drag_info.motion_callback = &Editor::region_drag_motion_callback;
4332 drag_info.finished_callback = &Editor::region_drag_finished_callback;
4336 drag_info.source_trackview = clicked_trackview;
4337 drag_info.dest_trackview = drag_info.source_trackview;
4338 drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4339 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4341 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4345 Editor::cancel_selection ()
4347 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4348 (*i)->hide_selection ();
4350 selection->clear ();
4351 clicked_selection = 0;
4355 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4357 nframes64_t start = 0;
4358 nframes64_t end = 0;
4364 drag_info.item = item;
4365 drag_info.motion_callback = &Editor::drag_selection;
4366 drag_info.finished_callback = &Editor::end_selection_op;
4371 case CreateSelection:
4372 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4373 drag_info.copy = true;
4375 drag_info.copy = false;
4377 start_grab (event, selector_cursor);
4380 case SelectionStartTrim:
4381 if (clicked_trackview) {
4382 clicked_trackview->order_selection_trims (item, true);
4384 start_grab (event, trimmer_cursor);
4385 start = selection->time[clicked_selection].start;
4386 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4389 case SelectionEndTrim:
4390 if (clicked_trackview) {
4391 clicked_trackview->order_selection_trims (item, false);
4393 start_grab (event, trimmer_cursor);
4394 end = selection->time[clicked_selection].end;
4395 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
4399 start = selection->time[clicked_selection].start;
4401 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4405 if (selection_op == SelectionMove) {
4406 show_verbose_time_cursor(start, 10);
4408 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4413 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4415 nframes64_t start = 0;
4416 nframes64_t end = 0;
4418 nframes64_t pending_position;
4420 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4421 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4423 pending_position = 0;
4426 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4427 snap_to (pending_position);
4430 /* only alter selection if the current frame is
4431 different from the last frame position (adjusted)
4434 if (pending_position == drag_info.last_pointer_frame) return;
4436 switch (selection_op) {
4437 case CreateSelection:
4439 if (drag_info.first_move) {
4440 snap_to (drag_info.grab_frame);
4443 if (pending_position < drag_info.grab_frame) {
4444 start = pending_position;
4445 end = drag_info.grab_frame;
4447 end = pending_position;
4448 start = drag_info.grab_frame;
4451 /* first drag: Either add to the selection
4452 or create a new selection->
4455 if (drag_info.first_move) {
4457 begin_reversible_command (_("range selection"));
4459 if (drag_info.copy) {
4460 /* adding to the selection */
4461 clicked_selection = selection->add (start, end);
4462 drag_info.copy = false;
4464 /* new selection-> */
4465 clicked_selection = selection->set (clicked_trackview, start, end);
4470 case SelectionStartTrim:
4472 if (drag_info.first_move) {
4473 begin_reversible_command (_("trim selection start"));
4476 start = selection->time[clicked_selection].start;
4477 end = selection->time[clicked_selection].end;
4479 if (pending_position > end) {
4482 start = pending_position;
4486 case SelectionEndTrim:
4488 if (drag_info.first_move) {
4489 begin_reversible_command (_("trim selection end"));
4492 start = selection->time[clicked_selection].start;
4493 end = selection->time[clicked_selection].end;
4495 if (pending_position < start) {
4498 end = pending_position;
4505 if (drag_info.first_move) {
4506 begin_reversible_command (_("move selection"));
4509 start = selection->time[clicked_selection].start;
4510 end = selection->time[clicked_selection].end;
4512 length = end - start;
4514 start = pending_position;
4517 end = start + length;
4522 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4523 start_canvas_autoscroll (1, 0);
4527 selection->replace (clicked_selection, start, end);
4530 drag_info.last_pointer_frame = pending_position;
4531 drag_info.first_move = false;
4533 if (selection_op == SelectionMove) {
4534 show_verbose_time_cursor(start, 10);
4536 show_verbose_time_cursor(pending_position, 10);
4541 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4543 if (!drag_info.first_move) {
4544 drag_selection (item, event);
4545 /* XXX this is not object-oriented programming at all. ick */
4546 if (selection->time.consolidate()) {
4547 selection->TimeChanged ();
4549 commit_reversible_command ();
4551 /* just a click, no pointer movement.*/
4553 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4555 selection->clear_time();
4560 /* XXX what happens if its a music selection? */
4561 session->set_audio_range (selection->time);
4562 stop_canvas_autoscroll ();
4566 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4569 TimeAxisView* tvp = clicked_trackview;
4570 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4572 if (tv && tv->is_audio_track()) {
4573 speed = tv->get_diskstream()->speed();
4576 nframes64_t region_start = (nframes64_t) (clicked_regionview->region()->position() / speed);
4577 nframes64_t region_end = (nframes64_t) (clicked_regionview->region()->last_frame() / speed);
4578 nframes64_t region_length = (nframes64_t) (clicked_regionview->region()->length() / speed);
4580 //drag_info.item = clicked_regionview->get_name_highlight();
4581 drag_info.item = item;
4582 drag_info.motion_callback = &Editor::trim_motion_callback;
4583 drag_info.finished_callback = &Editor::trim_finished_callback;
4585 start_grab (event, trimmer_cursor);
4587 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4588 trim_op = ContentsTrim;
4590 /* These will get overridden for a point trim.*/
4591 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4592 /* closer to start */
4593 trim_op = StartTrim;
4594 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4602 show_verbose_time_cursor(region_start, 10);
4605 show_verbose_time_cursor(region_end, 10);
4608 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4614 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4616 RegionView* rv = clicked_regionview;
4617 nframes64_t frame_delta = 0;
4618 bool left_direction;
4619 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4621 /* snap modifier works differently here..
4622 its' current state has to be passed to the
4623 various trim functions in order to work properly
4627 TimeAxisView* tvp = clicked_trackview;
4628 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4629 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4631 if (tv && tv->is_audio_track()) {
4632 speed = tv->get_diskstream()->speed();
4635 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4636 left_direction = true;
4638 left_direction = false;
4642 snap_to (drag_info.current_pointer_frame);
4645 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4649 if (drag_info.first_move) {
4655 trim_type = "Region start trim";
4658 trim_type = "Region end trim";
4661 trim_type = "Region content trim";
4665 begin_reversible_command (trim_type);
4667 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4668 (*i)->fake_set_opaque(false);
4669 (*i)->region()->freeze ();
4671 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4673 arv->temporarily_hide_envelope ();
4675 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4676 insert_result = motion_frozen_playlists.insert (pl);
4677 if (insert_result.second) {
4678 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4683 if (left_direction) {
4684 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4686 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4691 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4694 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4695 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4701 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes64_t) (rv->region()->last_frame()/speed))) {
4704 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4705 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4712 bool swap_direction = false;
4714 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4715 swap_direction = true;
4718 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4719 i != selection->regions.by_layer().end(); ++i)
4721 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4729 show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
4732 show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
4735 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4739 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4740 drag_info.first_move = false;
4744 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4746 boost::shared_ptr<Region> region (rv.region());
4748 if (region->locked()) {
4752 nframes64_t new_bound;
4755 TimeAxisView* tvp = clicked_trackview;
4756 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4758 if (tv && tv->is_audio_track()) {
4759 speed = tv->get_diskstream()->speed();
4762 if (left_direction) {
4763 if (swap_direction) {
4764 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4766 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4769 if (swap_direction) {
4770 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4772 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4777 snap_to (new_bound);
4779 region->trim_start ((nframes64_t) (new_bound * speed), this);
4780 rv.region_changed (StartChanged);
4784 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
4786 boost::shared_ptr<Region> region (rv.region());
4788 if (region->locked()) {
4792 nframes64_t new_bound;
4795 TimeAxisView* tvp = clicked_trackview;
4796 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4798 if (tv && tv->is_audio_track()) {
4799 speed = tv->get_diskstream()->speed();
4802 if (left_direction) {
4803 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4805 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4809 snap_to (new_bound, (left_direction ? 0 : 1));
4812 region->trim_front ((nframes64_t) (new_bound * speed), this);
4814 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4818 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
4820 boost::shared_ptr<Region> region (rv.region());
4822 if (region->locked()) {
4826 nframes64_t new_bound;
4829 TimeAxisView* tvp = clicked_trackview;
4830 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4832 if (tv && tv->is_audio_track()) {
4833 speed = tv->get_diskstream()->speed();
4836 if (left_direction) {
4837 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
4839 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
4843 snap_to (new_bound);
4845 region->trim_end ((nframes64_t) (new_bound * speed), this);
4846 rv.region_changed (LengthChanged);
4850 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4852 if (!drag_info.first_move) {
4853 trim_motion_callback (item, event);
4855 if (!selection->selected (clicked_regionview)) {
4856 thaw_region_after_trim (*clicked_regionview);
4859 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4860 i != selection->regions.by_layer().end(); ++i)
4862 thaw_region_after_trim (**i);
4863 (*i)->fake_set_opaque (true);
4867 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4869 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4872 motion_frozen_playlists.clear ();
4874 commit_reversible_command();
4876 /* no mouse movement */
4882 Editor::point_trim (GdkEvent* event)
4884 RegionView* rv = clicked_regionview;
4885 nframes64_t new_bound = drag_info.current_pointer_frame;
4887 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4888 snap_to (new_bound);
4891 /* Choose action dependant on which button was pressed */
4892 switch (event->button.button) {
4894 trim_op = StartTrim;
4895 begin_reversible_command (_("Start point trim"));
4897 if (selection->selected (rv)) {
4899 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4900 i != selection->regions.by_layer().end(); ++i)
4902 if (!(*i)->region()->locked()) {
4903 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4904 XMLNode &before = pl->get_state();
4905 (*i)->region()->trim_front (new_bound, this);
4906 XMLNode &after = pl->get_state();
4907 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4913 if (!rv->region()->locked()) {
4914 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4915 XMLNode &before = pl->get_state();
4916 rv->region()->trim_front (new_bound, this);
4917 XMLNode &after = pl->get_state();
4918 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4922 commit_reversible_command();
4927 begin_reversible_command (_("End point trim"));
4929 if (selection->selected (rv)) {
4931 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4933 if (!(*i)->region()->locked()) {
4934 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4935 XMLNode &before = pl->get_state();
4936 (*i)->region()->trim_end (new_bound, this);
4937 XMLNode &after = pl->get_state();
4938 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4944 if (!rv->region()->locked()) {
4945 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4946 XMLNode &before = pl->get_state();
4947 rv->region()->trim_end (new_bound, this);
4948 XMLNode &after = pl->get_state();
4949 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4953 commit_reversible_command();
4962 Editor::thaw_region_after_trim (RegionView& rv)
4964 boost::shared_ptr<Region> region (rv.region());
4966 if (region->locked()) {
4970 region->thaw (_("trimmed region"));
4971 XMLNode &after = region->playlist()->get_state();
4972 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4974 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4976 arv->unhide_envelope ();
4980 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4985 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4986 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4990 Location* location = find_location_from_marker (marker, is_start);
4991 location->set_hidden (true, this);
4996 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
5002 drag_info.item = item;
5003 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
5004 drag_info.finished_callback = &Editor::end_range_markerbar_op;
5006 range_marker_op = op;
5008 if (!temp_location) {
5009 temp_location = new Location;
5013 case CreateRangeMarker:
5014 case CreateTransportMarker:
5015 case CreateCDMarker:
5017 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
5018 drag_info.copy = true;
5020 drag_info.copy = false;
5022 start_grab (event, selector_cursor);
5026 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5031 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5033 nframes64_t start = 0;
5034 nframes64_t end = 0;
5035 ArdourCanvas::SimpleRect *crect;
5037 switch (range_marker_op) {
5038 case CreateRangeMarker:
5039 crect = range_bar_drag_rect;
5041 case CreateTransportMarker:
5042 crect = transport_bar_drag_rect;
5044 case CreateCDMarker:
5045 crect = cd_marker_bar_drag_rect;
5048 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
5053 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5054 snap_to (drag_info.current_pointer_frame);
5057 /* only alter selection if the current frame is
5058 different from the last frame position.
5061 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5063 switch (range_marker_op) {
5064 case CreateRangeMarker:
5065 case CreateTransportMarker:
5066 case CreateCDMarker:
5067 if (drag_info.first_move) {
5068 snap_to (drag_info.grab_frame);
5071 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5072 start = drag_info.current_pointer_frame;
5073 end = drag_info.grab_frame;
5075 end = drag_info.current_pointer_frame;
5076 start = drag_info.grab_frame;
5079 /* first drag: Either add to the selection
5080 or create a new selection.
5083 if (drag_info.first_move) {
5085 temp_location->set (start, end);
5089 update_marker_drag_item (temp_location);
5090 range_marker_drag_rect->show();
5091 //range_marker_drag_rect->raise_to_top();
5097 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
5098 start_canvas_autoscroll (1, 0);
5102 temp_location->set (start, end);
5104 double x1 = frame_to_pixel (start);
5105 double x2 = frame_to_pixel (end);
5106 crect->property_x1() = x1;
5107 crect->property_x2() = x2;
5109 update_marker_drag_item (temp_location);
5112 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5113 drag_info.first_move = false;
5115 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
5120 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5122 Location * newloc = 0;
5126 if (!drag_info.first_move) {
5127 drag_range_markerbar_op (item, event);
5129 switch (range_marker_op) {
5130 case CreateRangeMarker:
5131 case CreateCDMarker:
5133 begin_reversible_command (_("new range marker"));
5134 XMLNode &before = session->locations()->get_state();
5135 session->locations()->next_available_name(rangename,"unnamed");
5136 if (range_marker_op == CreateCDMarker) {
5137 flags = Location::IsRangeMarker|Location::IsCDMarker;
5138 cd_marker_bar_drag_rect->hide();
5141 flags = Location::IsRangeMarker;
5142 range_bar_drag_rect->hide();
5144 newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
5145 session->locations()->add (newloc, true);
5146 XMLNode &after = session->locations()->get_state();
5147 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
5148 commit_reversible_command ();
5150 range_marker_drag_rect->hide();
5154 case CreateTransportMarker:
5155 // popup menu to pick loop or punch
5156 new_transport_marker_context_menu (&event->button, item);
5161 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5163 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
5168 start = session->locations()->first_mark_before (drag_info.grab_frame);
5169 end = session->locations()->first_mark_after (drag_info.grab_frame);
5171 if (end == max_frames) {
5172 end = session->current_end_frame ();
5176 start = session->current_start_frame ();
5179 switch (mouse_mode) {
5181 /* find the two markers on either side and then make the selection from it */
5182 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
5186 /* find the two markers on either side of the click and make the range out of it */
5187 selection->set (0, start, end);
5196 stop_canvas_autoscroll ();
5202 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5204 drag_info.item = item;
5205 drag_info.motion_callback = &Editor::drag_mouse_zoom;
5206 drag_info.finished_callback = &Editor::end_mouse_zoom;
5208 start_grab (event, zoom_cursor);
5210 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5214 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5219 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5220 snap_to (drag_info.current_pointer_frame);
5222 if (drag_info.first_move) {
5223 snap_to (drag_info.grab_frame);
5227 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5229 /* base start and end on initial click position */
5230 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5231 start = drag_info.current_pointer_frame;
5232 end = drag_info.grab_frame;
5234 end = drag_info.current_pointer_frame;
5235 start = drag_info.grab_frame;
5240 if (drag_info.first_move) {
5242 zoom_rect->raise_to_top();
5245 reposition_zoom_rect(start, end);
5247 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5248 drag_info.first_move = false;
5250 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5255 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5257 if (!drag_info.first_move) {
5258 drag_mouse_zoom (item, event);
5260 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5261 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
5263 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
5266 temporal_zoom_to_frame (false, drag_info.grab_frame);
5268 temporal_zoom_step (false);
5269 center_screen (drag_info.grab_frame);
5277 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
5279 double x1 = frame_to_pixel (start);
5280 double x2 = frame_to_pixel (end);
5281 double y2 = full_canvas_height - 1.0;
5283 zoom_rect->property_x1() = x1;
5284 zoom_rect->property_y1() = 1.0;
5285 zoom_rect->property_x2() = x2;
5286 zoom_rect->property_y2() = y2;
5290 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5292 drag_info.item = item;
5293 drag_info.motion_callback = &Editor::drag_rubberband_select;
5294 drag_info.finished_callback = &Editor::end_rubberband_select;
5296 start_grab (event, cross_hair_cursor);
5298 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5302 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5309 /* use a bigger drag threshold than the default */
5311 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
5315 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) {
5316 if (drag_info.first_move) {
5317 snap_to (drag_info.grab_frame);
5319 snap_to (drag_info.current_pointer_frame);
5322 /* base start and end on initial click position */
5324 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5325 start = drag_info.current_pointer_frame;
5326 end = drag_info.grab_frame;
5328 end = drag_info.current_pointer_frame;
5329 start = drag_info.grab_frame;
5332 if (drag_info.current_pointer_y < drag_info.grab_y) {
5333 y1 = drag_info.current_pointer_y;
5334 y2 = drag_info.grab_y;
5336 y2 = drag_info.current_pointer_y;
5337 y1 = drag_info.grab_y;
5341 if (start != end || y1 != y2) {
5343 double x1 = frame_to_pixel (start);
5344 double x2 = frame_to_pixel (end);
5346 rubberband_rect->property_x1() = x1;
5347 rubberband_rect->property_y1() = y1;
5348 rubberband_rect->property_x2() = x2;
5349 rubberband_rect->property_y2() = y2;
5351 rubberband_rect->show();
5352 rubberband_rect->raise_to_top();
5354 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5355 drag_info.first_move = false;
5357 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5362 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5364 if (!drag_info.first_move) {
5366 drag_rubberband_select (item, event);
5369 if (drag_info.current_pointer_y < drag_info.grab_y) {
5370 y1 = drag_info.current_pointer_y;
5371 y2 = drag_info.grab_y;
5373 y2 = drag_info.current_pointer_y;
5374 y1 = drag_info.grab_y;
5378 Selection::Operation op = Keyboard::selection_type (event->button.state);
5381 begin_reversible_command (_("rubberband selection"));
5383 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5384 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5386 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5390 commit_reversible_command ();
5394 selection->clear_tracks();
5395 selection->clear_regions();
5396 selection->clear_points ();
5397 selection->clear_lines ();
5400 rubberband_rect->hide();
5405 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5407 using namespace Gtkmm2ext;
5409 ArdourPrompter prompter (false);
5411 prompter.set_prompt (_("Name for region:"));
5412 prompter.set_initial_text (clicked_regionview->region()->name());
5413 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5414 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5415 prompter.show_all ();
5416 switch (prompter.run ()) {
5417 case Gtk::RESPONSE_ACCEPT:
5419 prompter.get_result(str);
5421 clicked_regionview->region()->set_name (str);
5429 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5431 drag_info.item = item;
5432 drag_info.motion_callback = &Editor::time_fx_motion;
5433 drag_info.finished_callback = &Editor::end_time_fx;
5437 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5441 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5443 RegionView* rv = clicked_regionview;
5445 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5446 snap_to (drag_info.current_pointer_frame);
5449 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5453 if (drag_info.current_pointer_frame > rv->region()->position()) {
5454 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5457 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5458 drag_info.first_move = false;
5460 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5464 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5466 clicked_regionview->get_time_axis_view().hide_timestretch ();
5468 if (drag_info.first_move) {
5472 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5473 /* backwards drag of the left edge - not usable */
5477 nframes64_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5478 #ifdef USE_RUBBERBAND
5479 float percentage = (float) ((double) newlen / (double) clicked_regionview->region()->length());
5481 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5484 begin_reversible_command (_("timestretch"));
5486 // XXX how do timeFX on multiple regions ?
5489 rs.add (clicked_regionview);
5491 if (time_stretch (rs, percentage) == 0) {
5492 session->commit_reversible_command ();
5497 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
5499 /* no brushing without a useful snap setting */
5502 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
5505 switch (snap_mode) {
5507 return; /* can't work because it allows region to be placed anywhere */
5512 switch (snap_type) {
5520 /* don't brush a copy over the original */
5522 if (pos == rv->region()->position()) {
5526 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
5528 if (atv == 0 || !atv->is_audio_track()) {
5532 boost::shared_ptr<Playlist> playlist = atv->playlist();
5533 double speed = atv->get_diskstream()->speed();
5535 XMLNode &before = playlist->get_state();
5536 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes64_t) (pos * speed));
5537 XMLNode &after = playlist->get_state();
5538 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5540 // playlist is frozen, so we have to update manually
5542 playlist->Modified(); /* EMIT SIGNAL */
5546 Editor::track_height_step_timeout ()
5548 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
5549 current_stepping_trackview = 0;