2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <pbd/error.h>
30 #include <gtkmm2ext/utils.h>
31 #include <pbd/memento_command.h>
33 #include "ardour_ui.h"
35 #include "time_axis_view.h"
36 #include "audio_time_axis.h"
37 #include "audio_region_view.h"
39 #include "streamview.h"
40 #include "region_gain_line.h"
41 #include "automation_time_axis.h"
44 #include "selection.h"
47 #include "rgb_macros.h"
49 #include <ardour/types.h>
50 #include <ardour/profile.h>
51 #include <ardour/route.h>
52 #include <ardour/audio_track.h>
53 #include <ardour/audio_diskstream.h>
54 #include <ardour/playlist.h>
55 #include <ardour/audioplaylist.h>
56 #include <ardour/audioregion.h>
57 #include <ardour/dB.h>
58 #include <ardour/utils.h>
59 #include <ardour/region_factory.h>
66 using namespace ARDOUR;
70 using namespace Editing;
73 Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
77 Gdk::ModifierType mask;
78 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas.get_window();
79 Glib::RefPtr<const Gdk::Window> pointer_window;
81 pointer_window = canvas_window->get_pointer (x, y, mask);
83 if (pointer_window == track_canvas.get_bin_window()) {
85 track_canvas.window_to_world (x, y, wx, wy);
86 in_track_canvas = true;
89 in_track_canvas = false;
91 if (pointer_window == time_canvas.get_bin_window()) {
92 time_canvas.window_to_world (x, y, wx, wy);
98 wx += horizontal_adjustment.get_value();
99 wy += vertical_adjustment.get_value();
102 event.type = GDK_BUTTON_RELEASE;
106 where = event_frame (&event, 0, 0);
111 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy) const
125 switch (event->type) {
126 case GDK_BUTTON_RELEASE:
127 case GDK_BUTTON_PRESS:
128 case GDK_2BUTTON_PRESS:
129 case GDK_3BUTTON_PRESS:
130 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
132 case GDK_MOTION_NOTIFY:
133 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
135 case GDK_ENTER_NOTIFY:
136 case GDK_LEAVE_NOTIFY:
137 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
140 case GDK_KEY_RELEASE:
141 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
144 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
148 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
149 position is negative (as can be the case with motion events in particular),
150 the frame location is always positive.
153 return pixel_to_frame (*pcx);
157 Editor::mouse_mode_toggled (MouseMode m)
159 if (ignore_mouse_mode_toggle) {
165 if (mouse_select_button.get_active()) {
171 if (mouse_move_button.get_active()) {
177 if (mouse_gain_button.get_active()) {
183 if (mouse_zoom_button.get_active()) {
189 if (mouse_timefx_button.get_active()) {
195 if (mouse_audition_button.get_active()) {
206 Editor::set_mouse_mode (MouseMode m, bool force)
208 if (drag_info.item) {
212 if (!force && m == mouse_mode) {
220 if (mouse_mode != MouseRange) {
222 /* in all modes except range, hide the range selection,
223 show the object (region) selection.
226 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
227 (*i)->set_should_show_selection (true);
229 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
230 (*i)->hide_selection ();
236 in range mode,show the range selection.
239 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
240 if ((*i)->get_selected()) {
241 (*i)->show_selection (selection->time);
246 /* XXX the hack of unsetting all other buttons should go
247 away once GTK2 allows us to use regular radio buttons drawn like
248 normal buttons, rather than my silly GroupedButton hack.
251 ignore_mouse_mode_toggle = true;
253 switch (mouse_mode) {
255 mouse_select_button.set_active (true);
256 current_canvas_cursor = selector_cursor;
260 mouse_move_button.set_active (true);
261 if (Profile->get_sae()) {
262 current_canvas_cursor = timebar_cursor;
264 current_canvas_cursor = grabber_cursor;
269 mouse_gain_button.set_active (true);
270 current_canvas_cursor = cross_hair_cursor;
274 mouse_zoom_button.set_active (true);
275 current_canvas_cursor = zoom_cursor;
279 mouse_timefx_button.set_active (true);
280 current_canvas_cursor = time_fx_cursor; // just use playhead
284 mouse_audition_button.set_active (true);
285 current_canvas_cursor = speaker_cursor;
289 ignore_mouse_mode_toggle = false;
292 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
297 Editor::step_mouse_mode (bool next)
299 switch (current_mouse_mode()) {
301 if (next) set_mouse_mode (MouseRange);
302 else set_mouse_mode (MouseTimeFX);
306 if (next) set_mouse_mode (MouseZoom);
307 else set_mouse_mode (MouseObject);
311 if (next) set_mouse_mode (MouseGain);
312 else set_mouse_mode (MouseRange);
316 if (next) set_mouse_mode (MouseTimeFX);
317 else set_mouse_mode (MouseZoom);
321 if (next) set_mouse_mode (MouseAudition);
322 else set_mouse_mode (MouseGain);
326 if (next) set_mouse_mode (MouseObject);
327 else set_mouse_mode (MouseTimeFX);
333 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
335 /* in object/audition/timefx mode, any button press sets
336 the selection if the object can be selected. this is a
337 bit of hack, because we want to avoid this if the
338 mouse operation is a region alignment.
340 note: not dbl-click or triple-click
343 if (((mouse_mode != MouseObject) &&
344 (mouse_mode != MouseAudition || item_type != RegionItem) &&
345 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
346 (mouse_mode != MouseRange)) ||
348 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
353 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
355 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
357 /* almost no selection action on modified button-2 or button-3 events */
359 if (item_type != RegionItem && event->button.button != 2) {
365 Selection::Operation op = Keyboard::selection_type (event->button.state);
366 bool press = (event->type == GDK_BUTTON_PRESS);
368 // begin_reversible_command (_("select on click"));
372 if (mouse_mode != MouseRange) {
373 set_selected_regionview_from_click (press, op, true);
374 } else if (event->type == GDK_BUTTON_PRESS) {
375 set_selected_track_as_side_effect ();
379 case RegionViewNameHighlight:
381 if (mouse_mode != MouseRange) {
382 set_selected_regionview_from_click (press, op, true);
383 } else if (event->type == GDK_BUTTON_PRESS) {
384 set_selected_track_as_side_effect ();
388 case FadeInHandleItem:
390 case FadeOutHandleItem:
392 if (mouse_mode != MouseRange) {
393 set_selected_regionview_from_click (press, op, true);
394 } else if (event->type == GDK_BUTTON_PRESS) {
395 set_selected_track_as_side_effect ();
399 case GainAutomationControlPointItem:
400 case PanAutomationControlPointItem:
401 case RedirectAutomationControlPointItem:
402 set_selected_track_as_side_effect ();
403 if (mouse_mode != MouseRange) {
404 set_selected_control_point_from_click (op, false);
409 /* for context click or range selection, select track */
410 if (event->button.button == 3) {
411 set_selected_track_as_side_effect ();
412 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
413 set_selected_track_as_side_effect ();
417 case AutomationTrackItem:
418 set_selected_track_as_side_effect (true);
426 const static double ZERO_GAIN_FRACTION = gain_to_slider_position(dB_to_coefficient(0.0));
429 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
431 track_canvas.grab_focus();
433 if (session && session->actively_recording()) {
437 button_selection (item, event, item_type);
439 if (drag_info.item == 0 &&
440 (Keyboard::is_delete_event (&event->button) ||
441 Keyboard::is_context_menu_event (&event->button) ||
442 Keyboard::is_edit_event (&event->button))) {
444 /* handled by button release */
448 switch (event->button.button) {
451 if (event->type == GDK_BUTTON_PRESS) {
453 if (drag_info.item) {
454 drag_info.item->ungrab (event->button.time);
457 /* single mouse clicks on any of these item types operate
458 independent of mouse mode, mostly because they are
459 not on the main track canvas or because we want
464 case PlayheadCursorItem:
465 start_cursor_grab (item, event);
469 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
470 hide_marker (item, event);
472 start_marker_grab (item, event);
476 case TempoMarkerItem:
477 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
478 start_tempo_marker_copy_grab (item, event);
480 start_tempo_marker_grab (item, event);
484 case MeterMarkerItem:
485 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
486 start_meter_marker_copy_grab (item, event);
488 start_meter_marker_grab (item, event);
498 case RangeMarkerBarItem:
499 start_range_markerbar_op (item, event, CreateRangeMarker);
503 case CdMarkerBarItem:
504 start_range_markerbar_op (item, event, CreateCDMarker);
508 case TransportMarkerBarItem:
509 start_range_markerbar_op (item, event, CreateTransportMarker);
518 switch (mouse_mode) {
521 case StartSelectionTrimItem:
522 start_selection_op (item, event, SelectionStartTrim);
525 case EndSelectionTrimItem:
526 start_selection_op (item, event, SelectionEndTrim);
530 if (Keyboard::modifier_state_contains
531 (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) {
532 // contains and not equals because I can't use alt as a modifier alone.
533 start_selection_grab (item, event);
534 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
535 /* grab selection for moving */
536 start_selection_op (item, event, SelectionMove);
538 /* this was debated, but decided the more common action was to
539 make a new selection */
540 start_selection_op (item, event, CreateSelection);
545 start_selection_op (item, event, CreateSelection);
551 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
552 event->type == GDK_BUTTON_PRESS) {
554 start_rubberband_select (item, event);
556 } else if (event->type == GDK_BUTTON_PRESS) {
559 case FadeInHandleItem:
560 start_fade_in_grab (item, event);
563 case FadeOutHandleItem:
564 start_fade_out_grab (item, event);
568 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
569 start_region_copy_grab (item, event);
570 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
571 start_region_brush_grab (item, event);
573 start_region_grab (item, event);
577 case RegionViewNameHighlight:
578 start_trim (item, event);
583 /* rename happens on edit clicks */
584 start_trim (clicked_regionview->get_name_highlight(), event);
588 case GainAutomationControlPointItem:
589 case PanAutomationControlPointItem:
590 case RedirectAutomationControlPointItem:
591 start_control_point_grab (item, event);
595 case GainAutomationLineItem:
596 case PanAutomationLineItem:
597 case RedirectAutomationLineItem:
598 start_line_grab_from_line (item, event);
603 case AutomationTrackItem:
604 start_rubberband_select (item, event);
607 /* <CMT Additions> */
608 case ImageFrameHandleStartItem:
609 imageframe_start_handle_op(item, event) ;
612 case ImageFrameHandleEndItem:
613 imageframe_end_handle_op(item, event) ;
616 case MarkerViewHandleStartItem:
617 markerview_item_start_handle_op(item, event) ;
620 case MarkerViewHandleEndItem:
621 markerview_item_end_handle_op(item, event) ;
624 /* </CMT Additions> */
626 /* <CMT Additions> */
628 start_markerview_grab(item, event) ;
631 start_imageframe_grab(item, event) ;
633 /* </CMT Additions> */
649 // start_line_grab_from_regionview (item, event);
652 case GainControlPointItem:
653 start_control_point_grab (item, event);
657 start_line_grab_from_line (item, event);
660 case GainAutomationControlPointItem:
661 case PanAutomationControlPointItem:
662 case RedirectAutomationControlPointItem:
663 start_control_point_grab (item, event);
674 case GainAutomationControlPointItem:
675 case PanAutomationControlPointItem:
676 case RedirectAutomationControlPointItem:
677 start_control_point_grab (item, event);
680 case GainAutomationLineItem:
681 case PanAutomationLineItem:
682 case RedirectAutomationLineItem:
683 start_line_grab_from_line (item, event);
687 // XXX need automation mode to identify which
689 // start_line_grab_from_regionview (item, event);
699 if (event->type == GDK_BUTTON_PRESS) {
700 start_mouse_zoom (item, event);
707 if (item_type == RegionItem) {
708 start_time_fx (item, event);
715 scrub_reverse_distance = 0;
716 last_scrub_x = event->button.x;
717 scrubbing_direction = 0;
718 track_canvas.get_window()->set_cursor (*transparent_cursor);
719 /* rest handled in motion & release */
728 switch (mouse_mode) {
730 if (event->type == GDK_BUTTON_PRESS) {
733 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
734 start_region_copy_grab (item, event);
736 start_region_grab (item, event);
741 case GainAutomationControlPointItem:
742 case PanAutomationControlPointItem:
743 case RedirectAutomationControlPointItem:
744 start_control_point_grab (item, event);
755 case RegionViewNameHighlight:
756 start_trim (item, event);
761 start_trim (clicked_regionview->get_name_highlight(), event);
772 if (event->type == GDK_BUTTON_PRESS) {
773 /* relax till release */
780 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
781 temporal_zoom_session();
783 temporal_zoom_to_frame (true, event_frame(event));
806 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
808 nframes_t where = event_frame (event, 0, 0);
810 /* no action if we're recording */
812 if (session && session->actively_recording()) {
816 /* first, see if we're finishing a drag ... */
818 if (drag_info.item) {
819 if (end_grab (item, event)) {
820 /* grab dragged, so do nothing else */
825 button_selection (item, event, item_type);
827 /* edit events get handled here */
829 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
835 case TempoMarkerItem:
836 edit_tempo_marker (item);
839 case MeterMarkerItem:
840 edit_meter_marker (item);
844 if (clicked_regionview->name_active()) {
845 return mouse_rename_region (item, event);
855 /* context menu events get handled here */
857 if (Keyboard::is_context_menu_event (&event->button)) {
859 if (drag_info.item == 0) {
861 /* no matter which button pops up the context menu, tell the menu
862 widget to use button 1 to drive menu selection.
867 case FadeInHandleItem:
869 case FadeOutHandleItem:
870 popup_fade_context_menu (1, event->button.time, item, item_type);
874 popup_track_context_menu (1, event->button.time, item_type, false, where);
878 case RegionViewNameHighlight:
880 popup_track_context_menu (1, event->button.time, item_type, false, where);
884 popup_track_context_menu (1, event->button.time, item_type, true, where);
887 case AutomationTrackItem:
888 popup_track_context_menu (1, event->button.time, item_type, false, where);
892 case RangeMarkerBarItem:
893 case TransportMarkerBarItem:
894 case CdMarkerBarItem:
897 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
901 marker_context_menu (&event->button, item);
904 case TempoMarkerItem:
905 tm_marker_context_menu (&event->button, item);
908 case MeterMarkerItem:
909 tm_marker_context_menu (&event->button, item);
912 case CrossfadeViewItem:
913 popup_track_context_menu (1, event->button.time, item_type, false, where);
916 /* <CMT Additions> */
918 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
920 case ImageFrameTimeAxisItem:
921 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
924 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
926 case MarkerTimeAxisItem:
927 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
929 /* <CMT Additions> */
940 /* delete events get handled here */
942 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
945 case TempoMarkerItem:
946 remove_tempo_marker (item);
949 case MeterMarkerItem:
950 remove_meter_marker (item);
954 remove_marker (*item, event);
958 if (mouse_mode == MouseObject) {
959 remove_clicked_region ();
963 case GainControlPointItem:
964 if (mouse_mode == MouseGain) {
965 remove_gain_control_point (item, event);
969 case GainAutomationControlPointItem:
970 case PanAutomationControlPointItem:
971 case RedirectAutomationControlPointItem:
972 remove_control_point (item, event);
981 switch (event->button.button) {
985 /* see comments in button_press_handler */
986 case PlayheadCursorItem:
989 case GainAutomationLineItem:
990 case PanAutomationLineItem:
991 case RedirectAutomationLineItem:
992 case StartSelectionTrimItem:
993 case EndSelectionTrimItem:
997 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
998 snap_to (where, 0, true);
1000 mouse_add_new_marker (where);
1003 case CdMarkerBarItem:
1004 // if we get here then a dragged range wasn't done
1005 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1006 snap_to (where, 0, true);
1008 mouse_add_new_marker (where, true);
1012 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1015 mouse_add_new_tempo_event (where);
1019 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1027 switch (mouse_mode) {
1029 switch (item_type) {
1030 case AutomationTrackItem:
1031 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->add_automation_event
1045 // Gain only makes sense for audio regions
1047 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1051 switch (item_type) {
1053 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1057 case AutomationTrackItem:
1058 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1059 add_automation_event (item, event, where, event->button.y);
1069 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1070 if (scrubbing_direction == 0) {
1071 /* no drag, just a click */
1072 switch (item_type) {
1074 play_selected_region ();
1080 /* make sure we stop */
1081 session->request_transport_speed (0.0);
1095 switch (mouse_mode) {
1098 switch (item_type) {
1100 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1102 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1105 // Button2 click is unused
1118 // x_style_paste (where, 1.0);
1138 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1144 switch (item_type) {
1145 case GainControlPointItem:
1146 if (mouse_mode == MouseGain) {
1147 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1148 cp->set_visible (true);
1152 at_y = cp->get_y ();
1153 cp->item->i2w (at_x, at_y);
1157 fraction = 1.0 - (cp->get_y() / cp->line.height());
1159 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1160 show_verbose_canvas_cursor ();
1162 if (is_drawable() && !_scrubbing) {
1163 track_canvas.get_window()->set_cursor (*fader_cursor);
1168 case GainAutomationControlPointItem:
1169 case PanAutomationControlPointItem:
1170 case RedirectAutomationControlPointItem:
1171 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1172 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1173 cp->set_visible (true);
1177 at_y = cp->get_y ();
1178 cp->item->i2w (at_x, at_y);
1182 fraction = 1.0 - (cp->get_y() / cp->line.height());
1184 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1185 show_verbose_canvas_cursor ();
1187 if (is_drawable()) {
1188 track_canvas.get_window()->set_cursor (*fader_cursor);
1194 if (mouse_mode == MouseGain) {
1195 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1197 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1198 if (is_drawable()) {
1199 track_canvas.get_window()->set_cursor (*fader_cursor);
1204 case GainAutomationLineItem:
1205 case RedirectAutomationLineItem:
1206 case PanAutomationLineItem:
1207 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1209 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1211 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1213 if (is_drawable()) {
1214 track_canvas.get_window()->set_cursor (*fader_cursor);
1219 case RegionViewNameHighlight:
1220 if (is_drawable() && mouse_mode == MouseObject) {
1221 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1225 case StartSelectionTrimItem:
1226 case EndSelectionTrimItem:
1227 /* <CMT Additions> */
1228 case ImageFrameHandleStartItem:
1229 case ImageFrameHandleEndItem:
1230 case MarkerViewHandleStartItem:
1231 case MarkerViewHandleEndItem:
1232 /* </CMT Additions> */
1234 if (is_drawable()) {
1235 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1239 case PlayheadCursorItem:
1240 if (is_drawable()) {
1241 track_canvas.get_window()->set_cursor (*grabber_cursor);
1245 case RegionViewName:
1247 /* when the name is not an active item, the entire name highlight is for trimming */
1249 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1250 if (mouse_mode == MouseObject && is_drawable()) {
1251 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1257 case AutomationTrackItem:
1258 if (is_drawable()) {
1259 Gdk::Cursor *cursor;
1260 switch (mouse_mode) {
1262 cursor = selector_cursor;
1265 cursor = zoom_cursor;
1268 cursor = cross_hair_cursor;
1272 track_canvas.get_window()->set_cursor (*cursor);
1274 AutomationTimeAxisView* atv;
1275 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1276 clear_entered_track = false;
1277 set_entered_track (atv);
1283 case RangeMarkerBarItem:
1284 case TransportMarkerBarItem:
1285 case CdMarkerBarItem:
1288 if (is_drawable()) {
1289 time_canvas.get_window()->set_cursor (*timebar_cursor);
1294 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1297 entered_marker = marker;
1298 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1300 case MeterMarkerItem:
1301 case TempoMarkerItem:
1302 if (is_drawable()) {
1303 time_canvas.get_window()->set_cursor (*timebar_cursor);
1306 case FadeInHandleItem:
1307 case FadeOutHandleItem:
1308 if (mouse_mode == MouseObject) {
1309 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1311 rect->property_fill_color_rgba() = 0;
1312 rect->property_outline_pixels() = 1;
1321 /* second pass to handle entered track status in a comprehensible way.
1324 switch (item_type) {
1326 case GainAutomationLineItem:
1327 case RedirectAutomationLineItem:
1328 case PanAutomationLineItem:
1329 case GainControlPointItem:
1330 case GainAutomationControlPointItem:
1331 case PanAutomationControlPointItem:
1332 case RedirectAutomationControlPointItem:
1333 /* these do not affect the current entered track state */
1334 clear_entered_track = false;
1337 case AutomationTrackItem:
1338 /* handled above already */
1342 set_entered_track (0);
1350 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1359 switch (item_type) {
1360 case GainControlPointItem:
1361 case GainAutomationControlPointItem:
1362 case PanAutomationControlPointItem:
1363 case RedirectAutomationControlPointItem:
1364 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1365 if (cp->line.npoints() > 1) {
1366 if (!cp->selected) {
1367 cp->set_visible (false);
1371 if (is_drawable()) {
1372 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1375 hide_verbose_canvas_cursor ();
1378 case RegionViewNameHighlight:
1379 case StartSelectionTrimItem:
1380 case EndSelectionTrimItem:
1381 case PlayheadCursorItem:
1382 /* <CMT Additions> */
1383 case ImageFrameHandleStartItem:
1384 case ImageFrameHandleEndItem:
1385 case MarkerViewHandleStartItem:
1386 case MarkerViewHandleEndItem:
1387 /* </CMT Additions> */
1388 if (is_drawable()) {
1389 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1394 case GainAutomationLineItem:
1395 case RedirectAutomationLineItem:
1396 case PanAutomationLineItem:
1397 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1399 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1401 line->property_fill_color_rgba() = al->get_line_color();
1403 if (is_drawable()) {
1404 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1408 case RegionViewName:
1409 /* see enter_handler() for notes */
1410 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1411 if (is_drawable() && mouse_mode == MouseObject) {
1412 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1417 case RangeMarkerBarItem:
1418 case TransportMarkerBarItem:
1419 case CdMarkerBarItem:
1423 if (is_drawable()) {
1424 time_canvas.get_window()->set_cursor (*timebar_cursor);
1429 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1433 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1434 location_flags_changed (loc, this);
1437 case MeterMarkerItem:
1438 case TempoMarkerItem:
1440 if (is_drawable()) {
1441 time_canvas.get_window()->set_cursor (*timebar_cursor);
1446 case FadeInHandleItem:
1447 case FadeOutHandleItem:
1448 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1450 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1452 rect->property_fill_color_rgba() = rv->get_fill_color();
1453 rect->property_outline_pixels() = 0;
1458 case AutomationTrackItem:
1459 if (is_drawable()) {
1460 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1461 clear_entered_track = true;
1462 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1474 Editor::left_automation_track ()
1476 if (clear_entered_track) {
1477 set_entered_track (0);
1478 clear_entered_track = false;
1484 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1486 if (event->motion.is_hint) {
1489 /* We call this so that MOTION_NOTIFY events continue to be
1490 delivered to the canvas. We need to do this because we set
1491 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1492 the density of the events, at the expense of a round-trip
1493 to the server. Given that this will mostly occur on cases
1494 where DISPLAY = :0.0, and given the cost of what the motion
1495 event might do, its a good tradeoff.
1498 track_canvas.get_pointer (x, y);
1501 if (current_stepping_trackview) {
1502 /* don't keep the persistent stepped trackview if the mouse moves */
1503 current_stepping_trackview = 0;
1504 step_timeout.disconnect ();
1507 if (session && session->actively_recording()) {
1508 /* Sorry. no dragging stuff around while we record */
1512 drag_info.item_type = item_type;
1513 drag_info.last_pointer_x = drag_info.current_pointer_x;
1514 drag_info.last_pointer_y = drag_info.current_pointer_y;
1515 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1516 &drag_info.current_pointer_y);
1518 switch (mouse_mode) {
1524 if (scrubbing_direction == 0) {
1526 session->request_locate (drag_info.current_pointer_frame, false);
1527 session->request_transport_speed (0.1);
1528 scrubbing_direction = 1;
1532 if (last_scrub_x > drag_info.current_pointer_x) {
1534 /* pointer moved to the left */
1536 if (scrubbing_direction > 0) {
1538 /* we reversed direction to go backwards */
1541 scrub_reverse_distance += (int) (last_scrub_x - drag_info.current_pointer_x);
1545 /* still moving to the left (backwards) */
1547 scrub_reversals = 0;
1548 scrub_reverse_distance = 0;
1550 delta = 0.01 * (last_scrub_x - drag_info.current_pointer_x);
1551 session->request_transport_speed (session->transport_speed() - delta);
1555 /* pointer moved to the right */
1557 if (scrubbing_direction < 0) {
1558 /* we reversed direction to go forward */
1561 scrub_reverse_distance += (int) (drag_info.current_pointer_x - last_scrub_x);
1564 /* still moving to the right */
1566 scrub_reversals = 0;
1567 scrub_reverse_distance = 0;
1569 delta = 0.01 * (drag_info.current_pointer_x - last_scrub_x);
1570 session->request_transport_speed (session->transport_speed() + delta);
1574 /* if there have been more than 2 opposite motion moves detected, or one that moves
1575 back more than 10 pixels, reverse direction
1578 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1580 if (scrubbing_direction > 0) {
1581 /* was forwards, go backwards */
1582 session->request_transport_speed (-0.1);
1583 scrubbing_direction = -1;
1585 /* was backwards, go forwards */
1586 session->request_transport_speed (0.1);
1587 scrubbing_direction = 1;
1590 scrub_reverse_distance = 0;
1591 scrub_reversals = 0;
1595 last_scrub_x = drag_info.current_pointer_x;
1602 if (!from_autoscroll && drag_info.item) {
1603 /* item != 0 is the best test i can think of for dragging.
1605 if (!drag_info.move_threshold_passed) {
1607 bool x_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1608 bool y_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1610 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1612 // and change the initial grab loc/frame if this drag info wants us to
1614 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1615 drag_info.grab_frame = drag_info.current_pointer_frame;
1616 drag_info.grab_x = drag_info.current_pointer_x;
1617 drag_info.grab_y = drag_info.current_pointer_y;
1618 drag_info.last_pointer_frame = drag_info.grab_frame;
1619 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1624 switch (item_type) {
1625 case PlayheadCursorItem:
1627 case GainControlPointItem:
1628 case RedirectAutomationControlPointItem:
1629 case GainAutomationControlPointItem:
1630 case PanAutomationControlPointItem:
1631 case TempoMarkerItem:
1632 case MeterMarkerItem:
1633 case RegionViewNameHighlight:
1634 case StartSelectionTrimItem:
1635 case EndSelectionTrimItem:
1638 case RedirectAutomationLineItem:
1639 case GainAutomationLineItem:
1640 case PanAutomationLineItem:
1641 case FadeInHandleItem:
1642 case FadeOutHandleItem:
1643 /* <CMT Additions> */
1644 case ImageFrameHandleStartItem:
1645 case ImageFrameHandleEndItem:
1646 case MarkerViewHandleStartItem:
1647 case MarkerViewHandleEndItem:
1648 /* </CMT Additions> */
1649 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1650 (event->motion.state & Gdk::BUTTON2_MASK))) {
1651 if (!from_autoscroll) {
1652 maybe_autoscroll (event);
1654 (this->*(drag_info.motion_callback)) (item, event);
1663 switch (mouse_mode) {
1668 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1669 (event->motion.state & GDK_BUTTON2_MASK))) {
1670 if (!from_autoscroll) {
1671 maybe_autoscroll (event);
1673 (this->*(drag_info.motion_callback)) (item, event);
1684 track_canvas_motion (event);
1685 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1693 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1695 if (drag_info.item == 0) {
1696 fatal << _("programming error: start_grab called without drag item") << endmsg;
1702 cursor = grabber_cursor;
1705 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1707 if (event->button.button == 2) {
1708 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
1709 drag_info.y_constrained = true;
1710 drag_info.x_constrained = false;
1712 drag_info.y_constrained = false;
1713 drag_info.x_constrained = true;
1716 drag_info.x_constrained = false;
1717 drag_info.y_constrained = false;
1720 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1721 drag_info.last_pointer_frame = drag_info.grab_frame;
1722 drag_info.current_pointer_frame = drag_info.grab_frame;
1723 drag_info.current_pointer_x = drag_info.grab_x;
1724 drag_info.current_pointer_y = drag_info.grab_y;
1725 drag_info.last_pointer_x = drag_info.current_pointer_x;
1726 drag_info.last_pointer_y = drag_info.current_pointer_y;
1727 drag_info.cumulative_x_drag = 0;
1728 drag_info.cumulative_y_drag = 0;
1729 drag_info.first_move = true;
1730 drag_info.move_threshold_passed = false;
1731 drag_info.want_move_threshold = false;
1732 drag_info.pointer_frame_offset = 0;
1733 drag_info.brushing = false;
1734 drag_info.copied_location = 0;
1736 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1738 event->button.time);
1740 if (session && session->transport_rolling()) {
1741 drag_info.was_rolling = true;
1743 drag_info.was_rolling = false;
1746 switch (snap_type) {
1747 case SnapToRegionStart:
1748 case SnapToRegionEnd:
1749 case SnapToRegionSync:
1750 case SnapToRegionBoundary:
1751 build_region_boundary_cache ();
1759 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1761 drag_info.item->ungrab (0);
1762 drag_info.item = new_item;
1765 cursor = grabber_cursor;
1768 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1772 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1774 bool did_drag = false;
1776 stop_canvas_autoscroll ();
1778 if (drag_info.item == 0) {
1782 drag_info.item->ungrab (event->button.time);
1784 if (drag_info.finished_callback) {
1785 drag_info.last_pointer_x = drag_info.current_pointer_x;
1786 drag_info.last_pointer_y = drag_info.current_pointer_y;
1787 (this->*(drag_info.finished_callback)) (item, event);
1790 did_drag = !drag_info.first_move;
1792 hide_verbose_canvas_cursor();
1795 drag_info.copy = false;
1796 drag_info.motion_callback = 0;
1797 drag_info.finished_callback = 0;
1798 drag_info.last_trackview = 0;
1799 drag_info.last_frame_position = 0;
1800 drag_info.grab_frame = 0;
1801 drag_info.last_pointer_frame = 0;
1802 drag_info.current_pointer_frame = 0;
1803 drag_info.brushing = false;
1805 if (drag_info.copied_location) {
1806 delete drag_info.copied_location;
1807 drag_info.copied_location = 0;
1814 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1816 drag_info.item = item;
1817 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1818 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1822 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1823 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1827 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1829 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1833 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1835 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1837 nframes_t fade_length;
1839 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1840 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1846 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1850 if (pos < (arv->region()->position() + 64)) {
1851 fade_length = 64; // this should be a minimum defined somewhere
1852 } else if (pos > arv->region()->last_frame()) {
1853 fade_length = arv->region()->length();
1855 fade_length = pos - arv->region()->position();
1857 /* mapover the region selection */
1859 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1861 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1867 tmp->reset_fade_in_shape_width (fade_length);
1870 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1872 drag_info.first_move = false;
1876 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1878 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1880 nframes_t fade_length;
1882 if (drag_info.first_move) return;
1884 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1885 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1890 if (pos < (arv->region()->position() + 64)) {
1891 fade_length = 64; // this should be a minimum defined somewhere
1892 } else if (pos > arv->region()->last_frame()) {
1893 fade_length = arv->region()->length();
1895 fade_length = pos - arv->region()->position();
1898 begin_reversible_command (_("change fade in length"));
1900 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1902 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1908 AutomationList& alist = tmp->audio_region()->fade_in();
1909 XMLNode &before = alist.get_state();
1911 tmp->audio_region()->set_fade_in_length (fade_length);
1913 XMLNode &after = alist.get_state();
1914 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1917 commit_reversible_command ();
1921 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1923 drag_info.item = item;
1924 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1925 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1929 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1930 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1934 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1936 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
1940 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1942 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1944 nframes_t fade_length;
1946 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1947 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1952 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1956 if (pos > (arv->region()->last_frame() - 64)) {
1957 fade_length = 64; // this should really be a minimum fade defined somewhere
1959 else if (pos < arv->region()->position()) {
1960 fade_length = arv->region()->length();
1963 fade_length = arv->region()->last_frame() - pos;
1966 /* mapover the region selection */
1968 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1970 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1976 tmp->reset_fade_out_shape_width (fade_length);
1979 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1981 drag_info.first_move = false;
1985 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1987 if (drag_info.first_move) return;
1989 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1991 nframes_t fade_length;
1993 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1994 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2000 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2004 if (pos > (arv->region()->last_frame() - 64)) {
2005 fade_length = 64; // this should really be a minimum fade defined somewhere
2007 else if (pos < arv->region()->position()) {
2008 fade_length = arv->region()->length();
2011 fade_length = arv->region()->last_frame() - pos;
2014 begin_reversible_command (_("change fade out length"));
2016 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2018 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2024 AutomationList& alist = tmp->audio_region()->fade_out();
2025 XMLNode &before = alist.get_state();
2027 tmp->audio_region()->set_fade_out_length (fade_length);
2029 XMLNode &after = alist.get_state();
2030 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
2033 commit_reversible_command ();
2037 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2039 drag_info.item = item;
2040 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2041 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2045 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2046 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2050 Cursor* cursor = (Cursor *) drag_info.data;
2052 if (cursor == playhead_cursor) {
2053 _dragging_playhead = true;
2055 if (session && drag_info.was_rolling) {
2056 session->request_stop ();
2059 if (session && session->is_auditioning()) {
2060 session->cancel_audition ();
2064 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2066 show_verbose_time_cursor (cursor->current_frame, 10);
2070 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2072 Cursor* cursor = (Cursor *) drag_info.data;
2073 nframes_t adjusted_frame;
2075 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2076 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2082 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2083 if (cursor == playhead_cursor) {
2084 snap_to (adjusted_frame);
2088 if (adjusted_frame == drag_info.last_pointer_frame) return;
2090 cursor->set_position (adjusted_frame);
2092 UpdateAllTransportClocks (cursor->current_frame);
2094 show_verbose_time_cursor (cursor->current_frame, 10);
2096 drag_info.last_pointer_frame = adjusted_frame;
2097 drag_info.first_move = false;
2101 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2103 if (drag_info.first_move) return;
2105 cursor_drag_motion_callback (item, event);
2107 _dragging_playhead = false;
2109 if (item == &playhead_cursor->canvas_item) {
2111 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2117 Editor::update_marker_drag_item (Location *location)
2119 double x1 = frame_to_pixel (location->start());
2120 double x2 = frame_to_pixel (location->end());
2122 if (location->is_mark()) {
2123 marker_drag_line_points.front().set_x(x1);
2124 marker_drag_line_points.back().set_x(x1);
2125 marker_drag_line->property_points() = marker_drag_line_points;
2128 range_marker_drag_rect->property_x1() = x1;
2129 range_marker_drag_rect->property_x2() = x2;
2134 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2138 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2139 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2145 Location *location = find_location_from_marker (marker, is_start);
2147 drag_info.item = item;
2148 drag_info.data = marker;
2149 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2150 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2154 _dragging_edit_point = true;
2156 drag_info.copied_location = new Location (*location);
2157 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2159 update_marker_drag_item (location);
2161 if (location->is_mark()) {
2162 // marker_drag_line->show();
2163 // marker_drag_line->raise_to_top();
2165 range_marker_drag_rect->show();
2166 range_marker_drag_rect->raise_to_top();
2170 show_verbose_time_cursor (location->start(), 10);
2172 show_verbose_time_cursor (location->end(), 10);
2175 Selection::Operation op = Keyboard::selection_type (event->button.state);
2178 case Selection::Toggle:
2179 selection->toggle (marker);
2181 case Selection::Set:
2182 selection->set (marker);
2184 case Selection::Extend:
2185 selection->add (marker);
2187 case Selection::Add:
2188 selection->add (marker);
2194 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2197 Marker* marker = (Marker *) drag_info.data;
2198 Location *real_location;
2199 Location *copy_location;
2201 bool move_both = false;
2204 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2205 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2210 nframes_t next = newframe;
2212 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2213 snap_to (newframe, 0, true);
2216 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2220 /* call this to find out if its the start or end */
2222 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2226 if (real_location->locked()) {
2230 /* use the copy that we're "dragging" around */
2232 copy_location = drag_info.copied_location;
2234 f_delta = copy_location->end() - copy_location->start();
2236 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2240 if (copy_location->is_mark()) {
2243 copy_location->set_start (newframe);
2247 if (is_start) { // start-of-range marker
2250 copy_location->set_start (newframe);
2251 copy_location->set_end (newframe + f_delta);
2252 } else if (newframe < copy_location->end()) {
2253 copy_location->set_start (newframe);
2255 snap_to (next, 1, true);
2256 copy_location->set_end (next);
2257 copy_location->set_start (newframe);
2260 } else { // end marker
2263 copy_location->set_end (newframe);
2264 copy_location->set_start (newframe - f_delta);
2265 } else if (newframe > copy_location->start()) {
2266 copy_location->set_end (newframe);
2268 } else if (newframe > 0) {
2269 snap_to (next, -1, true);
2270 copy_location->set_start (next);
2271 copy_location->set_end (newframe);
2276 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2277 drag_info.first_move = false;
2279 update_marker_drag_item (copy_location);
2281 LocationMarkers* lm = find_location_markers (real_location);
2282 lm->set_position (copy_location->start(), copy_location->end());
2283 edit_point_clock.set (copy_location->start());
2285 show_verbose_time_cursor (newframe, 10);
2289 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2291 if (drag_info.first_move) {
2292 marker_drag_motion_callback (item, event);
2296 _dragging_edit_point = false;
2298 Marker* marker = (Marker *) drag_info.data;
2301 begin_reversible_command ( _("move marker") );
2302 XMLNode &before = session->locations()->get_state();
2304 Location * location = find_location_from_marker (marker, is_start);
2308 if (location->locked()) {
2312 if (location->is_mark()) {
2313 location->set_start (drag_info.copied_location->start());
2315 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2319 XMLNode &after = session->locations()->get_state();
2320 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2321 commit_reversible_command ();
2323 marker_drag_line->hide();
2324 range_marker_drag_rect->hide();
2328 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2331 MeterMarker* meter_marker;
2333 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2334 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2338 meter_marker = dynamic_cast<MeterMarker*> (marker);
2340 MetricSection& section (meter_marker->meter());
2342 if (!section.movable()) {
2346 drag_info.item = item;
2347 drag_info.copy = false;
2348 drag_info.data = marker;
2349 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2350 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2354 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2356 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2360 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2363 MeterMarker* meter_marker;
2365 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2366 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2370 meter_marker = dynamic_cast<MeterMarker*> (marker);
2372 // create a dummy marker for visual representation of moving the copy.
2373 // The actual copying is not done before we reach the finish callback.
2375 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2376 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2377 *new MeterSection(meter_marker->meter()));
2379 drag_info.item = &new_marker->the_item();
2380 drag_info.copy = true;
2381 drag_info.data = new_marker;
2382 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2383 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2387 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2389 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2393 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2395 MeterMarker* marker = (MeterMarker *) drag_info.data;
2396 nframes_t adjusted_frame;
2398 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2399 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2405 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2406 snap_to (adjusted_frame);
2409 if (adjusted_frame == drag_info.last_pointer_frame) return;
2411 marker->set_position (adjusted_frame);
2414 drag_info.last_pointer_frame = adjusted_frame;
2415 drag_info.first_move = false;
2417 show_verbose_time_cursor (adjusted_frame, 10);
2421 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2423 if (drag_info.first_move) return;
2425 meter_marker_drag_motion_callback (drag_info.item, event);
2427 MeterMarker* marker = (MeterMarker *) drag_info.data;
2430 TempoMap& map (session->tempo_map());
2431 map.bbt_time (drag_info.last_pointer_frame, when);
2433 if (drag_info.copy == true) {
2434 begin_reversible_command (_("copy meter mark"));
2435 XMLNode &before = map.get_state();
2436 map.add_meter (marker->meter(), when);
2437 XMLNode &after = map.get_state();
2438 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2439 commit_reversible_command ();
2441 // delete the dummy marker we used for visual representation of copying.
2442 // a new visual marker will show up automatically.
2445 begin_reversible_command (_("move meter mark"));
2446 XMLNode &before = map.get_state();
2447 map.move_meter (marker->meter(), when);
2448 XMLNode &after = map.get_state();
2449 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2450 commit_reversible_command ();
2455 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2458 TempoMarker* tempo_marker;
2460 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2461 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2465 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2466 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2470 MetricSection& section (tempo_marker->tempo());
2472 if (!section.movable()) {
2476 drag_info.item = item;
2477 drag_info.copy = false;
2478 drag_info.data = marker;
2479 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2480 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2484 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2485 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2489 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2492 TempoMarker* tempo_marker;
2494 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2495 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2499 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2500 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2504 // create a dummy marker for visual representation of moving the copy.
2505 // The actual copying is not done before we reach the finish callback.
2507 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2508 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2509 *new TempoSection(tempo_marker->tempo()));
2511 drag_info.item = &new_marker->the_item();
2512 drag_info.copy = true;
2513 drag_info.data = new_marker;
2514 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2515 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2519 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2521 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2525 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2527 TempoMarker* marker = (TempoMarker *) drag_info.data;
2528 nframes_t adjusted_frame;
2530 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2531 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2537 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2538 snap_to (adjusted_frame);
2541 if (adjusted_frame == drag_info.last_pointer_frame) return;
2543 /* OK, we've moved far enough to make it worth actually move the thing. */
2545 marker->set_position (adjusted_frame);
2547 show_verbose_time_cursor (adjusted_frame, 10);
2549 drag_info.last_pointer_frame = adjusted_frame;
2550 drag_info.first_move = false;
2554 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2556 if (drag_info.first_move) return;
2558 tempo_marker_drag_motion_callback (drag_info.item, event);
2560 TempoMarker* marker = (TempoMarker *) drag_info.data;
2563 TempoMap& map (session->tempo_map());
2564 map.bbt_time (drag_info.last_pointer_frame, when);
2566 if (drag_info.copy == true) {
2567 begin_reversible_command (_("copy tempo mark"));
2568 XMLNode &before = map.get_state();
2569 map.add_tempo (marker->tempo(), when);
2570 XMLNode &after = map.get_state();
2571 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2572 commit_reversible_command ();
2574 // delete the dummy marker we used for visual representation of copying.
2575 // a new visual marker will show up automatically.
2578 begin_reversible_command (_("move tempo mark"));
2579 XMLNode &before = map.get_state();
2580 map.move_tempo (marker->tempo(), when);
2581 XMLNode &after = map.get_state();
2582 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2583 commit_reversible_command ();
2588 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2590 ControlPoint* control_point;
2592 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2593 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2597 // We shouldn't remove the first or last gain point
2598 if (control_point->line.is_last_point(*control_point) ||
2599 control_point->line.is_first_point(*control_point)) {
2603 control_point->line.remove_point (*control_point);
2607 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2609 ControlPoint* control_point;
2611 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2612 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2616 control_point->line.remove_point (*control_point);
2620 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2622 ControlPoint* control_point;
2624 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2625 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2629 drag_info.item = item;
2630 drag_info.data = control_point;
2631 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2632 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2634 start_grab (event, fader_cursor);
2636 // start the grab at the center of the control point so
2637 // the point doesn't 'jump' to the mouse after the first drag
2638 drag_info.grab_x = control_point->get_x();
2639 drag_info.grab_y = control_point->get_y();
2640 control_point->line.parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
2641 track_canvas.w2c(drag_info.grab_x, drag_info.grab_y,
2642 drag_info.grab_x, drag_info.grab_y);
2644 drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
2646 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2648 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2649 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2650 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2652 show_verbose_canvas_cursor ();
2656 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2658 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2660 double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
2661 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2663 if (event->button.state & Keyboard::SecondaryModifier) {
2668 double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
2669 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2671 // calculate zero crossing point. back off by .01 to stay on the
2672 // positive side of zero
2674 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line.height() - .01;
2675 cp->line.parent_group().i2w(_unused, zero_gain_y);
2677 // make sure we hit zero when passing through
2678 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2679 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2683 if (drag_info.x_constrained) {
2684 cx = drag_info.grab_x;
2686 if (drag_info.y_constrained) {
2687 cy = drag_info.grab_y;
2690 drag_info.cumulative_x_drag = cx - drag_info.grab_x;
2691 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2693 cp->line.parent_group().w2i (cx, cy);
2697 cy = min ((double) cp->line.height(), cy);
2699 //translate cx to frames
2700 nframes_t cx_frames = unit_to_frame (cx);
2702 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2703 snap_to (cx_frames);
2706 float fraction = 1.0 - (cy / cp->line.height());
2710 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2716 cp->line.point_drag (*cp, cx_frames , fraction, push);
2718 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2720 drag_info.first_move = false;
2724 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2726 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2728 if (drag_info.first_move) {
2732 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2733 reset_point_selection ();
2737 control_point_drag_motion_callback (item, event);
2739 cp->line.end_drag (cp);
2743 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2745 switch (mouse_mode) {
2747 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2748 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2756 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2760 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2761 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2765 start_line_grab (al, event);
2769 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2773 nframes_t frame_within_region;
2775 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2779 cx = event->button.x;
2780 cy = event->button.y;
2781 line->parent_group().w2i (cx, cy);
2782 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2784 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2785 current_line_drag_info.after)) {
2786 /* no adjacent points */
2790 drag_info.item = &line->grab_item();
2791 drag_info.data = line;
2792 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2793 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2795 start_grab (event, fader_cursor);
2797 double fraction = 1.0 - (cy / line->height());
2799 line->start_drag (0, drag_info.grab_frame, fraction);
2801 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2802 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2803 show_verbose_canvas_cursor ();
2807 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2809 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2811 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2813 if (event->button.state & Keyboard::SecondaryModifier) {
2817 double cx = drag_info.current_pointer_x;
2818 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2820 // calculate zero crossing point. back off by .01 to stay on the
2821 // positive side of zero
2823 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
2824 line->parent_group().i2w(_unused, zero_gain_y);
2826 // make sure we hit zero when passing through
2827 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2828 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2832 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2834 line->parent_group().w2i (cx, cy);
2837 cy = min ((double) line->height(), cy);
2840 fraction = 1.0 - (cy / line->height());
2844 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2850 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2852 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2856 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2858 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2859 line_drag_motion_callback (item, event);
2864 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2866 if (selection->regions.empty() || clicked_regionview == 0) {
2870 drag_info.copy = false;
2871 drag_info.item = item;
2872 drag_info.data = clicked_regionview;
2874 if (Config->get_edit_mode() == Splice) {
2875 drag_info.motion_callback = &Editor::region_drag_splice_motion_callback;
2876 drag_info.finished_callback = &Editor::region_drag_splice_finished_callback;
2878 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2879 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2885 TimeAxisView* tvp = clicked_trackview;
2886 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2888 if (tv && tv->is_audio_track()) {
2889 speed = tv->get_diskstream()->speed();
2892 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2893 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2894 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2895 // we want a move threshold
2896 drag_info.want_move_threshold = true;
2898 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2900 begin_reversible_command (_("move region(s)"));
2904 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2906 if (selection->regions.empty() || clicked_regionview == 0) {
2910 drag_info.copy = true;
2911 drag_info.item = item;
2912 drag_info.data = clicked_regionview;
2916 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2917 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
2920 if (atv && atv->is_audio_track()) {
2921 speed = atv->get_diskstream()->speed();
2924 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2925 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2926 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2927 // we want a move threshold
2928 drag_info.want_move_threshold = true;
2929 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2930 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2931 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2935 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2937 if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) {
2941 drag_info.copy = false;
2942 drag_info.item = item;
2943 drag_info.data = clicked_regionview;
2944 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2945 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2950 TimeAxisView* tvp = clicked_trackview;
2951 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2953 if (tv && tv->is_audio_track()) {
2954 speed = tv->get_diskstream()->speed();
2957 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2958 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2959 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2960 // we want a move threshold
2961 drag_info.want_move_threshold = true;
2962 drag_info.brushing = true;
2964 begin_reversible_command (_("Drag region brush"));
2968 Editor::possibly_copy_regions_during_grab (GdkEvent* event)
2970 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2972 drag_info.want_move_threshold = false; // don't copy again
2974 /* duplicate the region(s) */
2976 vector<RegionView*> new_regionviews;
2978 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2981 AudioRegionView* arv;
2986 if ((arv = dynamic_cast<AudioRegionView*>(rv)) == 0) {
2987 /* XXX handle MIDI here */
2991 nrv = new AudioRegionView (*arv);
2992 nrv->get_canvas_group()->show ();
2994 new_regionviews.push_back (nrv);
2997 if (new_regionviews.empty()) {
3001 /* reset selection to new regionviews */
3003 selection->set (new_regionviews);
3005 /* reset drag_info data to reflect the fact that we are dragging the copies */
3007 drag_info.data = new_regionviews.front();
3009 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3014 Editor::check_region_drag_possible (AudioTimeAxisView** tv)
3016 /* Which trackview is this ? */
3018 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
3019 (*tv) = dynamic_cast<AudioTimeAxisView*>(tvp);
3021 /* The region motion is only processed if the pointer is over
3025 if (!(*tv) || !(*tv)->is_audio_track()) {
3026 /* To make sure we hide the verbose canvas cursor when the mouse is
3027 not held over and audiotrack.
3029 hide_verbose_canvas_cursor ();
3036 struct RegionSelectionByPosition {
3037 bool operator() (RegionView*a, RegionView* b) {
3038 return a->region()->position () < b->region()->position();
3043 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3045 AudioTimeAxisView* tv;
3047 if (!check_region_drag_possible (&tv)) {
3051 if (!drag_info.move_threshold_passed) {
3057 if (drag_info.current_pointer_x - drag_info.grab_x > 0) {
3063 RegionSelection copy (selection->regions);
3065 RegionSelectionByPosition cmp;
3068 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
3070 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(*i)->get_time_axis_view());
3076 boost::shared_ptr<Playlist> playlist;
3078 if ((playlist = atv->playlist()) == 0) {
3082 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
3087 if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) {
3091 if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) {
3097 playlist->shuffle ((*i)->region(), dir);
3099 drag_info.grab_x = drag_info.current_pointer_x;
3104 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3109 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3113 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
3114 nframes_t pending_region_position = 0;
3115 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
3116 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
3117 bool clamp_y_axis = false;
3118 vector<int32_t> height_list(512) ;
3119 vector<int32_t>::iterator j;
3120 AudioTimeAxisView* tv;
3122 possibly_copy_regions_during_grab (event);
3124 if (!check_region_drag_possible (&tv)) {
3128 original_pointer_order = drag_info.last_trackview->order;
3130 /************************************************************
3132 ************************************************************/
3134 if (drag_info.brushing) {
3135 clamp_y_axis = true;
3140 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
3142 int32_t children = 0, numtracks = 0;
3143 // XXX hard coding track limit, oh my, so very very bad
3144 bitset <1024> tracks (0x00);
3145 /* get a bitmask representing the visible tracks */
3147 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3148 TimeAxisView *tracklist_timeview;
3149 tracklist_timeview = (*i);
3150 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
3151 list<TimeAxisView*> children_list;
3153 /* zeroes are audio tracks. ones are other types. */
3155 if (!atv2->hidden()) {
3157 if (visible_y_high < atv2->order) {
3158 visible_y_high = atv2->order;
3160 if (visible_y_low > atv2->order) {
3161 visible_y_low = atv2->order;
3164 if (!atv2->is_audio_track()) {
3165 tracks = tracks |= (0x01 << atv2->order);
3168 height_list[atv2->order] = (*i)->height;
3170 if ((children_list = atv2->get_child_list()).size() > 0) {
3171 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
3172 tracks = tracks |= (0x01 << (atv2->order + children));
3173 height_list[atv2->order + children] = (*j)->height;
3181 /* find the actual span according to the canvas */
3183 canvas_pointer_y_span = pointer_y_span;
3184 if (drag_info.last_trackview->order >= tv->order) {
3186 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
3187 if (height_list[y] == 0 ) {
3188 canvas_pointer_y_span--;
3193 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
3194 if ( height_list[y] == 0 ) {
3195 canvas_pointer_y_span++;
3200 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3201 RegionView* rv2 = (*i);
3202 double ix1, ix2, iy1, iy2;
3205 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3206 rv2->get_canvas_group()->i2w (ix1, iy1);
3207 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3208 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3210 if (atv2->order != original_pointer_order) {
3211 /* this isn't the pointer track */
3213 if (canvas_pointer_y_span > 0) {
3215 /* moving up the canvas */
3216 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
3218 int32_t visible_tracks = 0;
3219 while (visible_tracks < canvas_pointer_y_span ) {
3222 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3223 /* we're passing through a hidden track */
3228 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3229 clamp_y_axis = true;
3233 clamp_y_axis = true;
3236 } else if (canvas_pointer_y_span < 0) {
3238 /*moving down the canvas*/
3240 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3243 int32_t visible_tracks = 0;
3245 while (visible_tracks > canvas_pointer_y_span ) {
3248 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3252 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3253 clamp_y_axis = true;
3258 clamp_y_axis = true;
3264 /* this is the pointer's track */
3265 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3266 clamp_y_axis = true;
3267 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3268 clamp_y_axis = true;
3276 } else if (drag_info.last_trackview == tv) {
3277 clamp_y_axis = true;
3281 if (!clamp_y_axis) {
3282 drag_info.last_trackview = tv;
3285 /************************************************************
3287 ************************************************************/
3289 /* compute the amount of pointer motion in frames, and where
3290 the region would be if we moved it by that much.
3293 if (drag_info.move_threshold_passed) {
3295 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3297 nframes_t sync_frame;
3298 nframes_t sync_offset;
3301 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3303 sync_offset = rv->region()->sync_offset (sync_dir);
3304 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3306 /* we snap if the snap modifier is not enabled.
3309 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3310 snap_to (sync_frame);
3313 if (sync_frame - sync_offset <= sync_frame) {
3314 pending_region_position = sync_frame - (sync_dir*sync_offset);
3316 pending_region_position = 0;
3320 pending_region_position = 0;
3323 if (pending_region_position > max_frames - rv->region()->length()) {
3324 pending_region_position = drag_info.last_frame_position;
3327 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3329 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3331 /* now compute the canvas unit distance we need to move the regionview
3332 to make it appear at the new location.
3335 if (pending_region_position > drag_info.last_frame_position) {
3336 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3338 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3341 drag_info.last_frame_position = pending_region_position;
3348 /* threshold not passed */
3353 /*************************************************************
3355 ************************************************************/
3357 if (x_delta == 0 && (pointer_y_span == 0)) {
3358 /* haven't reached next snap point, and we're not switching
3359 trackviews. nothing to do.
3366 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3368 RegionView* rv2 = (*i);
3370 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3372 double ix1, ix2, iy1, iy2;
3373 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3374 rv2->get_canvas_group()->i2w (ix1, iy1);
3383 /*************************************************************
3385 ************************************************************/
3389 if (drag_info.first_move) {
3390 if (drag_info.move_threshold_passed) {
3401 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3402 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3404 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3406 RegionView* rv = (*i);
3407 double ix1, ix2, iy1, iy2;
3408 int32_t temp_pointer_y_span = pointer_y_span;
3410 /* get item BBox, which will be relative to parent. so we have
3411 to query on a child, then convert to world coordinates using
3415 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3416 rv->get_canvas_group()->i2w (ix1, iy1);
3417 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3418 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3419 AudioTimeAxisView* temp_atv;
3421 if ((pointer_y_span != 0) && !clamp_y_axis) {
3424 for (j = height_list.begin(); j!= height_list.end(); j++) {
3425 if (x == canvas_atv->order) {
3426 /* we found the track the region is on */
3427 if (x != original_pointer_order) {
3428 /*this isn't from the same track we're dragging from */
3429 temp_pointer_y_span = canvas_pointer_y_span;
3431 while (temp_pointer_y_span > 0) {
3432 /* we're moving up canvas-wise,
3433 so we need to find the next track height
3435 if (j != height_list.begin()) {
3438 if (x != original_pointer_order) {
3439 /* we're not from the dragged track, so ignore hidden tracks. */
3441 temp_pointer_y_span++;
3445 temp_pointer_y_span--;
3447 while (temp_pointer_y_span < 0) {
3449 if (x != original_pointer_order) {
3451 temp_pointer_y_span--;
3455 if (j != height_list.end()) {
3458 temp_pointer_y_span++;
3460 /* find out where we'll be when we move and set height accordingly */
3462 tvp2 = trackview_by_y_position (iy1 + y_delta);
3463 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3464 rv->set_height (temp_atv->height);
3466 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3467 personally, i think this can confuse things, but never mind.
3470 //const GdkColor& col (temp_atv->view->get_region_color());
3471 //rv->set_color (const_cast<GdkColor&>(col));
3478 /* prevent the regionview from being moved to before
3479 the zero position on the canvas.
3484 if (-x_delta > ix1) {
3487 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3488 x_delta = max_frames - rv->region()->last_frame();
3492 if (drag_info.first_move) {
3494 /* hide any dependent views */
3496 rv->get_time_axis_view().hide_dependent_views (*rv);
3498 /* this is subtle. raising the regionview itself won't help,
3499 because raise_to_top() just puts the item on the top of
3500 its parent's stack. so, we need to put the trackview canvas_display group
3501 on the top, since its parent is the whole canvas.
3504 rv->get_canvas_group()->raise_to_top();
3505 rv->get_time_axis_view().canvas_display->raise_to_top();
3506 cursor_group->raise_to_top();
3507 rv->fake_set_opaque (true);
3510 if (drag_info.brushing) {
3511 mouse_brush_insert_region (rv, pending_region_position);
3513 rv->move (x_delta, y_delta);
3516 } /* foreach region */
3520 if (drag_info.first_move && drag_info.move_threshold_passed) {
3521 cursor_group->raise_to_top();
3522 drag_info.first_move = false;
3525 if (x_delta != 0 && !drag_info.brushing) {
3526 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3531 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3534 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3535 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3536 bool nocommit = true;
3538 RouteTimeAxisView* atv;
3539 bool regionview_y_movement;
3540 bool regionview_x_movement;
3541 vector<RegionView*> copies;
3543 /* first_move is set to false if the regionview has been moved in the
3547 if (drag_info.first_move) {
3554 /* The regionview has been moved at some stage during the grab so we need
3555 to account for any mouse movement between this event and the last one.
3558 region_drag_motion_callback (item, event);
3560 if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
3561 selection->set (pre_drag_region_selection);
3562 pre_drag_region_selection.clear ();
3565 if (drag_info.brushing) {
3566 /* all changes were made during motion event handlers */
3568 if (drag_info.copy) {
3569 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3570 copies.push_back (*i);
3577 /* adjust for track speed */
3580 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3581 if (atv && atv->get_diskstream()) {
3582 speed = atv->get_diskstream()->speed();
3585 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3586 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3588 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3589 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3593 if (drag_info.copy) {
3594 if (drag_info.x_constrained) {
3595 op_string = _("fixed time region copy");
3597 op_string = _("region copy");
3600 if (drag_info.x_constrained) {
3601 op_string = _("fixed time region drag");
3603 op_string = _("region drag");
3607 begin_reversible_command (op_string);
3609 if (regionview_y_movement) {
3611 /* moved to a different audio track. */
3613 vector<RegionView*> new_selection;
3615 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3617 RegionView* rv = (*i);
3619 double ix1, ix2, iy1, iy2;
3621 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3622 rv->get_canvas_group()->i2w (ix1, iy1);
3623 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3624 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3626 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3627 boost::shared_ptr<Playlist> to_playlist = atv2->playlist();
3629 where = (nframes_t) (unit_to_frame (ix1) * speed);
3630 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3632 /* undo the previous hide_dependent_views so that xfades don't
3633 disappear on copying regions
3636 rv->get_time_axis_view().reveal_dependent_views (*rv);
3638 if (!drag_info.copy) {
3640 /* the region that used to be in the old playlist is not
3641 moved to the new one - we make a copy of it. as a result,
3642 any existing editor for the region should no longer be
3646 rv->hide_region_editor();
3647 rv->fake_set_opaque (false);
3649 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3650 from_playlist->remove_region ((rv->region()));
3651 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3655 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3657 copies.push_back (rv);
3660 latest_regionviews.clear ();
3662 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3663 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3664 to_playlist->add_region (new_region, where);
3665 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3668 if (!latest_regionviews.empty()) {
3669 new_selection.insert (new_selection.end(), latest_regionviews.begin(), latest_regionviews.end());
3672 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3673 was selected in all of them, then removing it from the playlist will have removed all
3674 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3675 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3676 corresponding regionview, and the selection is now empty).
3678 this could have invalidated any and all iterators into the region selection.
3680 the heuristic we use here is: if the region selection is empty, break out of the loop
3681 here. if the region selection is not empty, then restart the loop because we know that
3682 we must have removed at least the region(view) we've just been working on as well as any
3683 that we processed on previous iterations.
3685 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3686 we can just iterate.
3689 if (drag_info.copy) {
3692 if (selection->regions.empty()) {
3695 i = selection->regions.by_layer().begin();
3700 selection->set (new_selection);
3704 /* motion within a single track */
3706 list<RegionView*> regions = selection->regions.by_layer();
3708 if (drag_info.copy) {
3709 selection->clear_regions();
3712 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3716 if (rv->region()->locked()) {
3721 if (regionview_x_movement) {
3722 double ownspeed = 1.0;
3723 atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3725 if (atv && atv->get_diskstream()) {
3726 ownspeed = atv->get_diskstream()->speed();
3729 /* base the new region position on the current position of the regionview.*/
3731 double ix1, ix2, iy1, iy2;
3733 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3734 rv->get_canvas_group()->i2w (ix1, iy1);
3735 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3739 where = rv->region()->position();
3742 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3744 assert (to_playlist);
3748 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3750 if (drag_info.copy) {
3752 boost::shared_ptr<Region> newregion;
3753 boost::shared_ptr<Region> ar;
3755 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3756 newregion = RegionFactory::create (ar);
3758 /* XXX MIDI HERE drobilla */
3764 latest_regionviews.clear ();
3765 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3766 to_playlist->add_region (newregion, (nframes_t) (where * atv->get_diskstream()->speed()));
3769 if (!latest_regionviews.empty()) {
3770 // XXX why just the first one ? we only expect one
3771 atv->reveal_dependent_views (*latest_regionviews.front());
3772 selection->add (latest_regionviews);
3775 /* if the original region was locked, we don't care for the new one */
3777 newregion->set_locked (false);
3781 /* just change the model */
3783 rv->region()->set_position (where, (void*) this);
3789 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3791 if (drag_info.copy) {
3792 copies.push_back (rv);
3800 commit_reversible_command ();
3803 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3809 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3811 /* Either add to or set the set the region selection, unless
3812 this is an alignment click (control used)
3815 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
3816 TimeAxisView* tv = &rv.get_time_axis_view();
3817 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3819 if (atv && atv->is_audio_track()) {
3820 speed = atv->get_diskstream()->speed();
3823 nframes64_t where = get_preferred_edit_position();
3827 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
3829 align_region (rv.region(), SyncPoint, (nframes_t) (where * speed));
3831 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
3833 align_region (rv.region(), End, (nframes_t) (where * speed));
3837 align_region (rv.region(), Start, (nframes_t) (where * speed));
3844 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3850 nframes_t frame_rate;
3857 switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3858 case AudioClock::BBT:
3859 session->bbt_time (frame, bbt);
3860 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3863 case AudioClock::SMPTE:
3864 session->smpte_time (frame, smpte);
3865 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3868 case AudioClock::MinSec:
3869 /* XXX this is copied from show_verbose_duration_cursor() */
3870 frame_rate = session->frame_rate();
3871 hours = frame / (frame_rate * 3600);
3872 frame = frame % (frame_rate * 3600);
3873 mins = frame / (frame_rate * 60);
3874 frame = frame % (frame_rate * 60);
3875 secs = (float) frame / (float) frame_rate;
3876 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3880 snprintf (buf, sizeof(buf), "%u", frame);
3884 if (xpos >= 0 && ypos >=0) {
3885 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3888 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3890 show_verbose_canvas_cursor ();
3894 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3901 nframes_t distance, frame_rate;
3903 Meter meter_at_start(session->tempo_map().meter_at(start));
3909 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3910 case AudioClock::BBT:
3911 session->bbt_time (start, sbbt);
3912 session->bbt_time (end, ebbt);
3915 /* XXX this computation won't work well if the
3916 user makes a selection that spans any meter changes.
3919 ebbt.bars -= sbbt.bars;
3920 if (ebbt.beats >= sbbt.beats) {
3921 ebbt.beats -= sbbt.beats;
3924 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3926 if (ebbt.ticks >= sbbt.ticks) {
3927 ebbt.ticks -= sbbt.ticks;
3930 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3933 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3936 case AudioClock::SMPTE:
3937 session->smpte_duration (end - start, smpte);
3938 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3941 case AudioClock::MinSec:
3942 /* XXX this stuff should be elsewhere.. */
3943 distance = end - start;
3944 frame_rate = session->frame_rate();
3945 hours = distance / (frame_rate * 3600);
3946 distance = distance % (frame_rate * 3600);
3947 mins = distance / (frame_rate * 60);
3948 distance = distance % (frame_rate * 60);
3949 secs = (float) distance / (float) frame_rate;
3950 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3954 snprintf (buf, sizeof(buf), "%u", end - start);
3958 if (xpos >= 0 && ypos >=0) {
3959 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3962 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3964 show_verbose_canvas_cursor ();
3968 Editor::collect_new_region_view (RegionView* rv)
3970 latest_regionviews.push_back (rv);
3974 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3976 if (clicked_regionview == 0) {
3980 /* lets try to create new Region for the selection */
3982 vector<boost::shared_ptr<AudioRegion> > new_regions;
3983 create_region_from_selection (new_regions);
3985 if (new_regions.empty()) {
3989 /* XXX fix me one day to use all new regions */
3991 boost::shared_ptr<Region> region (new_regions.front());
3993 /* add it to the current stream/playlist.
3995 tricky: the streamview for the track will add a new regionview. we will
3996 catch the signal it sends when it creates the regionview to
3997 set the regionview we want to then drag.
4000 latest_regionviews.clear();
4001 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4003 /* A selection grab currently creates two undo/redo operations, one for
4004 creating the new region and another for moving it.
4007 begin_reversible_command (_("selection grab"));
4009 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
4011 XMLNode *before = &(playlist->get_state());
4012 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
4013 XMLNode *after = &(playlist->get_state());
4014 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4016 commit_reversible_command ();
4020 if (latest_regionviews.empty()) {
4021 /* something went wrong */
4025 /* we need to deselect all other regionviews, and select this one
4026 i'm ignoring undo stuff, because the region creation will take care of it
4028 selection->set (latest_regionviews);
4030 drag_info.item = latest_regionviews.front()->get_canvas_group();
4031 drag_info.data = latest_regionviews.front();
4032 drag_info.motion_callback = &Editor::region_drag_motion_callback;
4033 drag_info.finished_callback = &Editor::region_drag_finished_callback;
4037 drag_info.last_trackview = clicked_trackview;
4038 drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4039 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4041 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4045 Editor::cancel_selection ()
4047 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4048 (*i)->hide_selection ();
4050 selection->clear ();
4051 clicked_selection = 0;
4055 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4057 nframes_t start = 0;
4064 drag_info.item = item;
4065 drag_info.motion_callback = &Editor::drag_selection;
4066 drag_info.finished_callback = &Editor::end_selection_op;
4071 case CreateSelection:
4072 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4073 drag_info.copy = true;
4075 drag_info.copy = false;
4077 start_grab (event, selector_cursor);
4080 case SelectionStartTrim:
4081 if (clicked_trackview) {
4082 clicked_trackview->order_selection_trims (item, true);
4084 start_grab (event, trimmer_cursor);
4085 start = selection->time[clicked_selection].start;
4086 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4089 case SelectionEndTrim:
4090 if (clicked_trackview) {
4091 clicked_trackview->order_selection_trims (item, false);
4093 start_grab (event, trimmer_cursor);
4094 end = selection->time[clicked_selection].end;
4095 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
4099 start = selection->time[clicked_selection].start;
4101 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4105 if (selection_op == SelectionMove) {
4106 show_verbose_time_cursor(start, 10);
4108 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4113 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4115 nframes_t start = 0;
4118 nframes_t pending_position;
4120 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4121 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4123 pending_position = 0;
4126 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4127 snap_to (pending_position);
4130 /* only alter selection if the current frame is
4131 different from the last frame position (adjusted)
4134 if (pending_position == drag_info.last_pointer_frame) return;
4136 switch (selection_op) {
4137 case CreateSelection:
4139 if (drag_info.first_move) {
4140 snap_to (drag_info.grab_frame);
4143 if (pending_position < drag_info.grab_frame) {
4144 start = pending_position;
4145 end = drag_info.grab_frame;
4147 end = pending_position;
4148 start = drag_info.grab_frame;
4151 /* first drag: Either add to the selection
4152 or create a new selection->
4155 if (drag_info.first_move) {
4157 begin_reversible_command (_("range selection"));
4159 if (drag_info.copy) {
4160 /* adding to the selection */
4161 clicked_selection = selection->add (start, end);
4162 drag_info.copy = false;
4164 /* new selection-> */
4165 clicked_selection = selection->set (clicked_trackview, start, end);
4170 case SelectionStartTrim:
4172 if (drag_info.first_move) {
4173 begin_reversible_command (_("trim selection start"));
4176 start = selection->time[clicked_selection].start;
4177 end = selection->time[clicked_selection].end;
4179 if (pending_position > end) {
4182 start = pending_position;
4186 case SelectionEndTrim:
4188 if (drag_info.first_move) {
4189 begin_reversible_command (_("trim selection end"));
4192 start = selection->time[clicked_selection].start;
4193 end = selection->time[clicked_selection].end;
4195 if (pending_position < start) {
4198 end = pending_position;
4205 if (drag_info.first_move) {
4206 begin_reversible_command (_("move selection"));
4209 start = selection->time[clicked_selection].start;
4210 end = selection->time[clicked_selection].end;
4212 length = end - start;
4214 start = pending_position;
4217 end = start + length;
4222 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4223 start_canvas_autoscroll (1);
4227 selection->replace (clicked_selection, start, end);
4230 drag_info.last_pointer_frame = pending_position;
4231 drag_info.first_move = false;
4233 if (selection_op == SelectionMove) {
4234 show_verbose_time_cursor(start, 10);
4236 show_verbose_time_cursor(pending_position, 10);
4241 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4243 if (!drag_info.first_move) {
4244 drag_selection (item, event);
4245 /* XXX this is not object-oriented programming at all. ick */
4246 if (selection->time.consolidate()) {
4247 selection->TimeChanged ();
4249 commit_reversible_command ();
4251 /* just a click, no pointer movement.*/
4253 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4255 selection->clear_time();
4260 /* XXX what happens if its a music selection? */
4261 session->set_audio_range (selection->time);
4262 stop_canvas_autoscroll ();
4266 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4269 TimeAxisView* tvp = clicked_trackview;
4270 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4272 if (tv && tv->is_audio_track()) {
4273 speed = tv->get_diskstream()->speed();
4276 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
4277 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
4278 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
4280 //drag_info.item = clicked_regionview->get_name_highlight();
4281 drag_info.item = item;
4282 drag_info.motion_callback = &Editor::trim_motion_callback;
4283 drag_info.finished_callback = &Editor::trim_finished_callback;
4285 start_grab (event, trimmer_cursor);
4287 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4288 trim_op = ContentsTrim;
4290 /* These will get overridden for a point trim.*/
4291 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4292 /* closer to start */
4293 trim_op = StartTrim;
4294 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4302 show_verbose_time_cursor(region_start, 10);
4305 show_verbose_time_cursor(region_end, 10);
4308 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4314 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4316 RegionView* rv = clicked_regionview;
4317 nframes_t frame_delta = 0;
4318 bool left_direction;
4319 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4321 /* snap modifier works differently here..
4322 its' current state has to be passed to the
4323 various trim functions in order to work properly
4327 TimeAxisView* tvp = clicked_trackview;
4328 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4329 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4331 if (tv && tv->is_audio_track()) {
4332 speed = tv->get_diskstream()->speed();
4335 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4336 left_direction = true;
4338 left_direction = false;
4342 snap_to (drag_info.current_pointer_frame);
4345 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4349 if (drag_info.first_move) {
4355 trim_type = "Region start trim";
4358 trim_type = "Region end trim";
4361 trim_type = "Region content trim";
4365 begin_reversible_command (trim_type);
4367 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4368 (*i)->fake_set_opaque(false);
4369 (*i)->region()->freeze ();
4371 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4373 arv->temporarily_hide_envelope ();
4375 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4376 insert_result = motion_frozen_playlists.insert (pl);
4377 if (insert_result.second) {
4378 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4383 if (left_direction) {
4384 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4386 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4391 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4394 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4395 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4401 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4404 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4405 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4412 bool swap_direction = false;
4414 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4415 swap_direction = true;
4418 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4419 i != selection->regions.by_layer().end(); ++i)
4421 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4429 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4432 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4435 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4439 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4440 drag_info.first_move = false;
4444 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4446 boost::shared_ptr<Region> region (rv.region());
4448 if (region->locked()) {
4452 nframes_t new_bound;
4455 TimeAxisView* tvp = clicked_trackview;
4456 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4458 if (tv && tv->is_audio_track()) {
4459 speed = tv->get_diskstream()->speed();
4462 if (left_direction) {
4463 if (swap_direction) {
4464 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4466 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4469 if (swap_direction) {
4470 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4472 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4477 snap_to (new_bound);
4479 region->trim_start ((nframes_t) (new_bound * speed), this);
4480 rv.region_changed (StartChanged);
4484 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4486 boost::shared_ptr<Region> region (rv.region());
4488 if (region->locked()) {
4492 nframes_t new_bound;
4495 TimeAxisView* tvp = clicked_trackview;
4496 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4498 if (tv && tv->is_audio_track()) {
4499 speed = tv->get_diskstream()->speed();
4502 if (left_direction) {
4503 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4505 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4509 snap_to (new_bound, (left_direction ? 0 : 1));
4512 region->trim_front ((nframes_t) (new_bound * speed), this);
4514 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4518 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4520 boost::shared_ptr<Region> region (rv.region());
4522 if (region->locked()) {
4526 nframes_t new_bound;
4529 TimeAxisView* tvp = clicked_trackview;
4530 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4532 if (tv && tv->is_audio_track()) {
4533 speed = tv->get_diskstream()->speed();
4536 if (left_direction) {
4537 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4539 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4543 snap_to (new_bound);
4545 region->trim_end ((nframes_t) (new_bound * speed), this);
4546 rv.region_changed (LengthChanged);
4550 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4552 if (!drag_info.first_move) {
4553 trim_motion_callback (item, event);
4555 if (!selection->selected (clicked_regionview)) {
4556 thaw_region_after_trim (*clicked_regionview);
4559 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4560 i != selection->regions.by_layer().end(); ++i)
4562 thaw_region_after_trim (**i);
4563 (*i)->fake_set_opaque (true);
4567 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4569 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4572 motion_frozen_playlists.clear ();
4574 commit_reversible_command();
4576 /* no mouse movement */
4582 Editor::point_trim (GdkEvent* event)
4584 RegionView* rv = clicked_regionview;
4585 nframes_t new_bound = drag_info.current_pointer_frame;
4587 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4588 snap_to (new_bound);
4591 /* Choose action dependant on which button was pressed */
4592 switch (event->button.button) {
4594 trim_op = StartTrim;
4595 begin_reversible_command (_("Start point trim"));
4597 if (selection->selected (rv)) {
4599 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4600 i != selection->regions.by_layer().end(); ++i)
4602 if (!(*i)->region()->locked()) {
4603 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4604 XMLNode &before = pl->get_state();
4605 (*i)->region()->trim_front (new_bound, this);
4606 XMLNode &after = pl->get_state();
4607 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4613 if (!rv->region()->locked()) {
4614 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4615 XMLNode &before = pl->get_state();
4616 rv->region()->trim_front (new_bound, this);
4617 XMLNode &after = pl->get_state();
4618 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4622 commit_reversible_command();
4627 begin_reversible_command (_("End point trim"));
4629 if (selection->selected (rv)) {
4631 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4633 if (!(*i)->region()->locked()) {
4634 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4635 XMLNode &before = pl->get_state();
4636 (*i)->region()->trim_end (new_bound, this);
4637 XMLNode &after = pl->get_state();
4638 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4644 if (!rv->region()->locked()) {
4645 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4646 XMLNode &before = pl->get_state();
4647 rv->region()->trim_end (new_bound, this);
4648 XMLNode &after = pl->get_state();
4649 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4653 commit_reversible_command();
4662 Editor::thaw_region_after_trim (RegionView& rv)
4664 boost::shared_ptr<Region> region (rv.region());
4666 if (region->locked()) {
4670 region->thaw (_("trimmed region"));
4671 XMLNode &after = region->playlist()->get_state();
4672 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4674 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4676 arv->unhide_envelope ();
4680 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4685 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4686 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4690 Location* location = find_location_from_marker (marker, is_start);
4691 location->set_hidden (true, this);
4696 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4702 drag_info.item = item;
4703 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4704 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4706 range_marker_op = op;
4708 if (!temp_location) {
4709 temp_location = new Location;
4713 case CreateRangeMarker:
4714 case CreateTransportMarker:
4715 case CreateCDMarker:
4717 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4718 drag_info.copy = true;
4720 drag_info.copy = false;
4722 start_grab (event, selector_cursor);
4726 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4731 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4733 nframes_t start = 0;
4735 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4737 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4738 snap_to (drag_info.current_pointer_frame);
4741 /* only alter selection if the current frame is
4742 different from the last frame position.
4745 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4747 switch (range_marker_op) {
4748 case CreateRangeMarker:
4749 case CreateTransportMarker:
4750 case CreateCDMarker:
4751 if (drag_info.first_move) {
4752 snap_to (drag_info.grab_frame);
4755 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4756 start = drag_info.current_pointer_frame;
4757 end = drag_info.grab_frame;
4759 end = drag_info.current_pointer_frame;
4760 start = drag_info.grab_frame;
4763 /* first drag: Either add to the selection
4764 or create a new selection.
4767 if (drag_info.first_move) {
4769 temp_location->set (start, end);
4773 update_marker_drag_item (temp_location);
4774 range_marker_drag_rect->show();
4775 range_marker_drag_rect->raise_to_top();
4781 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4782 start_canvas_autoscroll (1);
4786 temp_location->set (start, end);
4788 double x1 = frame_to_pixel (start);
4789 double x2 = frame_to_pixel (end);
4790 crect->property_x1() = x1;
4791 crect->property_x2() = x2;
4793 update_marker_drag_item (temp_location);
4796 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4797 drag_info.first_move = false;
4799 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4804 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4806 Location * newloc = 0;
4810 if (!drag_info.first_move) {
4811 drag_range_markerbar_op (item, event);
4813 switch (range_marker_op) {
4814 case CreateRangeMarker:
4815 case CreateCDMarker:
4817 begin_reversible_command (_("new range marker"));
4818 XMLNode &before = session->locations()->get_state();
4819 session->locations()->next_available_name(rangename,"unnamed");
4820 if (range_marker_op == CreateCDMarker) {
4821 flags = Location::IsRangeMarker|Location::IsCDMarker;
4824 flags = Location::IsRangeMarker;
4826 newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
4827 session->locations()->add (newloc, true);
4828 XMLNode &after = session->locations()->get_state();
4829 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4830 commit_reversible_command ();
4832 range_bar_drag_rect->hide();
4833 range_marker_drag_rect->hide();
4837 case CreateTransportMarker:
4838 // popup menu to pick loop or punch
4839 new_transport_marker_context_menu (&event->button, item);
4844 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4846 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
4851 start = session->locations()->first_mark_before (drag_info.grab_frame);
4852 end = session->locations()->first_mark_after (drag_info.grab_frame);
4854 if (end == max_frames) {
4855 end = session->current_end_frame ();
4859 start = session->current_start_frame ();
4862 switch (mouse_mode) {
4864 /* find the two markers on either side and then make the selection from it */
4865 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4869 /* find the two markers on either side of the click and make the range out of it */
4870 selection->set (0, start, end);
4879 stop_canvas_autoscroll ();
4885 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4887 drag_info.item = item;
4888 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4889 drag_info.finished_callback = &Editor::end_mouse_zoom;
4891 start_grab (event, zoom_cursor);
4893 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4897 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4902 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4903 snap_to (drag_info.current_pointer_frame);
4905 if (drag_info.first_move) {
4906 snap_to (drag_info.grab_frame);
4910 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4912 /* base start and end on initial click position */
4913 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4914 start = drag_info.current_pointer_frame;
4915 end = drag_info.grab_frame;
4917 end = drag_info.current_pointer_frame;
4918 start = drag_info.grab_frame;
4923 if (drag_info.first_move) {
4925 zoom_rect->raise_to_top();
4928 reposition_zoom_rect(start, end);
4930 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4931 drag_info.first_move = false;
4933 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4938 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4940 if (!drag_info.first_move) {
4941 drag_mouse_zoom (item, event);
4943 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4944 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4946 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4949 temporal_zoom_to_frame (false, drag_info.grab_frame);
4951 temporal_zoom_step (false);
4952 center_screen (drag_info.grab_frame);
4960 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4962 double x1 = frame_to_pixel (start);
4963 double x2 = frame_to_pixel (end);
4964 double y2 = full_canvas_height - 1.0;
4966 zoom_rect->property_x1() = x1;
4967 zoom_rect->property_y1() = 1.0;
4968 zoom_rect->property_x2() = x2;
4969 zoom_rect->property_y2() = y2;
4973 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4975 drag_info.item = item;
4976 drag_info.motion_callback = &Editor::drag_rubberband_select;
4977 drag_info.finished_callback = &Editor::end_rubberband_select;
4979 start_grab (event, cross_hair_cursor);
4981 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4985 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4992 /* use a bigger drag threshold than the default */
4994 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4998 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4999 if (drag_info.first_move) {
5000 snap_to (drag_info.grab_frame);
5002 snap_to (drag_info.current_pointer_frame);
5005 /* base start and end on initial click position */
5007 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5008 start = drag_info.current_pointer_frame;
5009 end = drag_info.grab_frame;
5011 end = drag_info.current_pointer_frame;
5012 start = drag_info.grab_frame;
5015 if (drag_info.current_pointer_y < drag_info.grab_y) {
5016 y1 = drag_info.current_pointer_y;
5017 y2 = drag_info.grab_y;
5019 y2 = drag_info.current_pointer_y;
5020 y1 = drag_info.grab_y;
5024 if (start != end || y1 != y2) {
5026 double x1 = frame_to_pixel (start);
5027 double x2 = frame_to_pixel (end);
5029 rubberband_rect->property_x1() = x1;
5030 rubberband_rect->property_y1() = y1;
5031 rubberband_rect->property_x2() = x2;
5032 rubberband_rect->property_y2() = y2;
5034 rubberband_rect->show();
5035 rubberband_rect->raise_to_top();
5037 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5038 drag_info.first_move = false;
5040 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5045 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5047 if (!drag_info.first_move) {
5049 drag_rubberband_select (item, event);
5052 if (drag_info.current_pointer_y < drag_info.grab_y) {
5053 y1 = drag_info.current_pointer_y;
5054 y2 = drag_info.grab_y;
5057 y2 = drag_info.current_pointer_y;
5058 y1 = drag_info.grab_y;
5062 Selection::Operation op = Keyboard::selection_type (event->button.state);
5065 begin_reversible_command (_("rubberband selection"));
5067 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5068 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5070 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5074 commit_reversible_command ();
5078 selection->clear_tracks();
5079 selection->clear_regions();
5080 selection->clear_points ();
5081 selection->clear_lines ();
5084 rubberband_rect->hide();
5089 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5091 using namespace Gtkmm2ext;
5093 ArdourPrompter prompter (false);
5095 prompter.set_prompt (_("Name for region:"));
5096 prompter.set_initial_text (clicked_regionview->region()->name());
5097 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5098 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5099 prompter.show_all ();
5100 switch (prompter.run ()) {
5101 case Gtk::RESPONSE_ACCEPT:
5103 prompter.get_result(str);
5105 clicked_regionview->region()->set_name (str);
5113 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5115 drag_info.item = item;
5116 drag_info.motion_callback = &Editor::time_fx_motion;
5117 drag_info.finished_callback = &Editor::end_time_fx;
5121 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5125 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5127 RegionView* rv = clicked_regionview;
5129 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5130 snap_to (drag_info.current_pointer_frame);
5133 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5137 if (drag_info.current_pointer_frame > rv->region()->position()) {
5138 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5141 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5142 drag_info.first_move = false;
5144 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5148 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5150 clicked_regionview->get_time_axis_view().hide_timestretch ();
5152 if (drag_info.first_move) {
5156 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5157 /* backwards drag of the left edge - not usable */
5161 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5162 #ifdef USE_RUBBERBAND
5163 float percentage = (float) ((double) newlen / (double) clicked_regionview->region()->length());
5165 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5168 begin_reversible_command (_("timestretch"));
5170 // XXX how do timeFX on multiple regions ?
5173 rs.add (clicked_regionview);
5175 if (time_stretch (rs, percentage) == 0) {
5176 session->commit_reversible_command ();
5181 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
5183 /* no brushing without a useful snap setting */
5186 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
5189 switch (snap_mode) {
5191 return; /* can't work because it allows region to be placed anywhere */
5196 switch (snap_type) {
5204 /* don't brush a copy over the original */
5206 if (pos == rv->region()->position()) {
5210 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
5212 if (atv == 0 || !atv->is_audio_track()) {
5216 boost::shared_ptr<Playlist> playlist = atv->playlist();
5217 double speed = atv->get_diskstream()->speed();
5219 XMLNode &before = playlist->get_state();
5220 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
5221 XMLNode &after = playlist->get_state();
5222 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5224 // playlist is frozen, so we have to update manually
5226 playlist->Modified(); /* EMIT SIGNAL */
5230 Editor::track_height_step_timeout ()
5233 struct timeval delta;
5235 gettimeofday (&now, 0);
5236 timersub (&now, &last_track_height_step_timestamp, &delta);
5238 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
5239 current_stepping_trackview = 0;