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);
740 case GainAutomationControlPointItem:
741 case PanAutomationControlPointItem:
742 case RedirectAutomationControlPointItem:
743 start_control_point_grab (item, event);
754 case RegionViewNameHighlight:
755 start_trim (item, event);
760 start_trim (clicked_regionview->get_name_highlight(), event);
771 if (event->type == GDK_BUTTON_PRESS) {
772 /* relax till release */
779 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
780 temporal_zoom_session();
782 temporal_zoom_to_frame (true, event_frame(event));
805 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
807 nframes_t where = event_frame (event, 0, 0);
809 /* no action if we're recording */
811 if (session && session->actively_recording()) {
815 /* first, see if we're finishing a drag ... */
817 if (drag_info.item) {
818 if (end_grab (item, event)) {
819 /* grab dragged, so do nothing else */
824 button_selection (item, event, item_type);
826 /* edit events get handled here */
828 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
834 case TempoMarkerItem:
835 edit_tempo_marker (item);
838 case MeterMarkerItem:
839 edit_meter_marker (item);
843 if (clicked_regionview->name_active()) {
844 return mouse_rename_region (item, event);
854 /* context menu events get handled here */
856 if (Keyboard::is_context_menu_event (&event->button)) {
858 if (drag_info.item == 0) {
860 /* no matter which button pops up the context menu, tell the menu
861 widget to use button 1 to drive menu selection.
866 case FadeInHandleItem:
868 case FadeOutHandleItem:
869 popup_fade_context_menu (1, event->button.time, item, item_type);
873 popup_track_context_menu (1, event->button.time, item_type, false, where);
877 case RegionViewNameHighlight:
879 popup_track_context_menu (1, event->button.time, item_type, false, where);
883 popup_track_context_menu (1, event->button.time, item_type, true, where);
886 case AutomationTrackItem:
887 popup_track_context_menu (1, event->button.time, item_type, false, where);
891 case RangeMarkerBarItem:
892 case TransportMarkerBarItem:
893 case CdMarkerBarItem:
896 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
900 marker_context_menu (&event->button, item);
903 case TempoMarkerItem:
904 tm_marker_context_menu (&event->button, item);
907 case MeterMarkerItem:
908 tm_marker_context_menu (&event->button, item);
911 case CrossfadeViewItem:
912 popup_track_context_menu (1, event->button.time, item_type, false, where);
915 /* <CMT Additions> */
917 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
919 case ImageFrameTimeAxisItem:
920 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
923 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
925 case MarkerTimeAxisItem:
926 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
928 /* <CMT Additions> */
939 /* delete events get handled here */
941 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
944 case TempoMarkerItem:
945 remove_tempo_marker (item);
948 case MeterMarkerItem:
949 remove_meter_marker (item);
953 remove_marker (*item, event);
957 if (mouse_mode == MouseObject) {
958 remove_clicked_region ();
962 case GainControlPointItem:
963 if (mouse_mode == MouseGain) {
964 remove_gain_control_point (item, event);
968 case GainAutomationControlPointItem:
969 case PanAutomationControlPointItem:
970 case RedirectAutomationControlPointItem:
971 remove_control_point (item, event);
980 switch (event->button.button) {
984 /* see comments in button_press_handler */
985 case PlayheadCursorItem:
988 case GainAutomationLineItem:
989 case PanAutomationLineItem:
990 case RedirectAutomationLineItem:
991 case StartSelectionTrimItem:
992 case EndSelectionTrimItem:
996 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
997 snap_to (where, 0, true);
999 mouse_add_new_marker (where);
1002 case CdMarkerBarItem:
1003 // if we get here then a dragged range wasn't done
1004 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1005 snap_to (where, 0, true);
1007 mouse_add_new_marker (where, true);
1011 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1014 mouse_add_new_tempo_event (where);
1018 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1026 switch (mouse_mode) {
1028 switch (item_type) {
1029 case AutomationTrackItem:
1030 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->add_automation_event
1044 // Gain only makes sense for audio regions
1046 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1050 switch (item_type) {
1052 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1056 case AutomationTrackItem:
1057 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1058 add_automation_event (item, event, where, event->button.y);
1068 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1069 if (scrubbing_direction == 0) {
1070 /* no drag, just a click */
1071 switch (item_type) {
1073 audition_selected_region ();
1079 /* make sure we stop */
1080 session->request_transport_speed (0.0);
1094 switch (mouse_mode) {
1097 switch (item_type) {
1099 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1101 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1104 // Button2 click is unused
1117 // x_style_paste (where, 1.0);
1137 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1143 switch (item_type) {
1144 case GainControlPointItem:
1145 if (mouse_mode == MouseGain) {
1146 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1147 cp->set_visible (true);
1151 at_y = cp->get_y ();
1152 cp->item->i2w (at_x, at_y);
1156 fraction = 1.0 - (cp->get_y() / cp->line.height());
1158 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1159 show_verbose_canvas_cursor ();
1161 if (is_drawable() && !_scrubbing) {
1162 track_canvas.get_window()->set_cursor (*fader_cursor);
1167 case GainAutomationControlPointItem:
1168 case PanAutomationControlPointItem:
1169 case RedirectAutomationControlPointItem:
1170 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1171 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1172 cp->set_visible (true);
1176 at_y = cp->get_y ();
1177 cp->item->i2w (at_x, at_y);
1181 fraction = 1.0 - (cp->get_y() / cp->line.height());
1183 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1184 show_verbose_canvas_cursor ();
1186 if (is_drawable()) {
1187 track_canvas.get_window()->set_cursor (*fader_cursor);
1193 if (mouse_mode == MouseGain) {
1194 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1196 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1197 if (is_drawable()) {
1198 track_canvas.get_window()->set_cursor (*fader_cursor);
1203 case GainAutomationLineItem:
1204 case RedirectAutomationLineItem:
1205 case PanAutomationLineItem:
1206 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1208 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1210 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1212 if (is_drawable()) {
1213 track_canvas.get_window()->set_cursor (*fader_cursor);
1218 case RegionViewNameHighlight:
1219 if (is_drawable() && mouse_mode == MouseObject) {
1220 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1224 case StartSelectionTrimItem:
1225 case EndSelectionTrimItem:
1226 /* <CMT Additions> */
1227 case ImageFrameHandleStartItem:
1228 case ImageFrameHandleEndItem:
1229 case MarkerViewHandleStartItem:
1230 case MarkerViewHandleEndItem:
1231 /* </CMT Additions> */
1233 if (is_drawable()) {
1234 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1238 case PlayheadCursorItem:
1239 if (is_drawable()) {
1240 track_canvas.get_window()->set_cursor (*grabber_cursor);
1244 case RegionViewName:
1246 /* when the name is not an active item, the entire name highlight is for trimming */
1248 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1249 if (mouse_mode == MouseObject && is_drawable()) {
1250 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1256 case AutomationTrackItem:
1257 if (is_drawable()) {
1258 Gdk::Cursor *cursor;
1259 switch (mouse_mode) {
1261 cursor = selector_cursor;
1264 cursor = zoom_cursor;
1267 cursor = cross_hair_cursor;
1271 track_canvas.get_window()->set_cursor (*cursor);
1273 AutomationTimeAxisView* atv;
1274 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1275 clear_entered_track = false;
1276 set_entered_track (atv);
1282 case RangeMarkerBarItem:
1283 case TransportMarkerBarItem:
1284 case CdMarkerBarItem:
1287 if (is_drawable()) {
1288 time_canvas.get_window()->set_cursor (*timebar_cursor);
1293 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1296 entered_marker = marker;
1297 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1299 case MeterMarkerItem:
1300 case TempoMarkerItem:
1301 if (is_drawable()) {
1302 time_canvas.get_window()->set_cursor (*timebar_cursor);
1305 case FadeInHandleItem:
1306 case FadeOutHandleItem:
1307 if (mouse_mode == MouseObject) {
1308 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1310 rect->property_fill_color_rgba() = 0;
1311 rect->property_outline_pixels() = 1;
1320 /* second pass to handle entered track status in a comprehensible way.
1323 switch (item_type) {
1325 case GainAutomationLineItem:
1326 case RedirectAutomationLineItem:
1327 case PanAutomationLineItem:
1328 case GainControlPointItem:
1329 case GainAutomationControlPointItem:
1330 case PanAutomationControlPointItem:
1331 case RedirectAutomationControlPointItem:
1332 /* these do not affect the current entered track state */
1333 clear_entered_track = false;
1336 case AutomationTrackItem:
1337 /* handled above already */
1341 set_entered_track (0);
1349 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1358 switch (item_type) {
1359 case GainControlPointItem:
1360 case GainAutomationControlPointItem:
1361 case PanAutomationControlPointItem:
1362 case RedirectAutomationControlPointItem:
1363 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1364 if (cp->line.npoints() > 1) {
1365 if (!cp->selected) {
1366 cp->set_visible (false);
1370 if (is_drawable()) {
1371 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1374 hide_verbose_canvas_cursor ();
1377 case RegionViewNameHighlight:
1378 case StartSelectionTrimItem:
1379 case EndSelectionTrimItem:
1380 case PlayheadCursorItem:
1381 /* <CMT Additions> */
1382 case ImageFrameHandleStartItem:
1383 case ImageFrameHandleEndItem:
1384 case MarkerViewHandleStartItem:
1385 case MarkerViewHandleEndItem:
1386 /* </CMT Additions> */
1387 if (is_drawable()) {
1388 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1393 case GainAutomationLineItem:
1394 case RedirectAutomationLineItem:
1395 case PanAutomationLineItem:
1396 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1398 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1400 line->property_fill_color_rgba() = al->get_line_color();
1402 if (is_drawable()) {
1403 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1407 case RegionViewName:
1408 /* see enter_handler() for notes */
1409 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1410 if (is_drawable() && mouse_mode == MouseObject) {
1411 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1416 case RangeMarkerBarItem:
1417 case TransportMarkerBarItem:
1418 case CdMarkerBarItem:
1422 if (is_drawable()) {
1423 time_canvas.get_window()->set_cursor (*timebar_cursor);
1428 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1432 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1433 location_flags_changed (loc, this);
1436 case MeterMarkerItem:
1437 case TempoMarkerItem:
1439 if (is_drawable()) {
1440 time_canvas.get_window()->set_cursor (*timebar_cursor);
1445 case FadeInHandleItem:
1446 case FadeOutHandleItem:
1447 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1449 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1451 rect->property_fill_color_rgba() = rv->get_fill_color();
1452 rect->property_outline_pixels() = 0;
1457 case AutomationTrackItem:
1458 if (is_drawable()) {
1459 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1460 clear_entered_track = true;
1461 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1473 Editor::left_automation_track ()
1475 if (clear_entered_track) {
1476 set_entered_track (0);
1477 clear_entered_track = false;
1483 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1485 if (event->motion.is_hint) {
1488 /* We call this so that MOTION_NOTIFY events continue to be
1489 delivered to the canvas. We need to do this because we set
1490 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1491 the density of the events, at the expense of a round-trip
1492 to the server. Given that this will mostly occur on cases
1493 where DISPLAY = :0.0, and given the cost of what the motion
1494 event might do, its a good tradeoff.
1497 track_canvas.get_pointer (x, y);
1500 if (current_stepping_trackview) {
1501 /* don't keep the persistent stepped trackview if the mouse moves */
1502 current_stepping_trackview = 0;
1503 step_timeout.disconnect ();
1506 if (session && session->actively_recording()) {
1507 /* Sorry. no dragging stuff around while we record */
1511 drag_info.item_type = item_type;
1512 drag_info.last_pointer_x = drag_info.current_pointer_x;
1513 drag_info.last_pointer_y = drag_info.current_pointer_y;
1514 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1515 &drag_info.current_pointer_y);
1517 switch (mouse_mode) {
1523 if (scrubbing_direction == 0) {
1525 session->request_locate (drag_info.current_pointer_frame, false);
1526 session->request_transport_speed (0.1);
1527 scrubbing_direction = 1;
1531 if (last_scrub_x > drag_info.current_pointer_x) {
1533 /* pointer moved to the left */
1535 if (scrubbing_direction > 0) {
1537 /* we reversed direction to go backwards */
1540 scrub_reverse_distance += (int) (last_scrub_x - drag_info.current_pointer_x);
1544 /* still moving to the left (backwards) */
1546 scrub_reversals = 0;
1547 scrub_reverse_distance = 0;
1549 delta = 0.01 * (last_scrub_x - drag_info.current_pointer_x);
1550 session->request_transport_speed (session->transport_speed() - delta);
1554 /* pointer moved to the right */
1556 if (scrubbing_direction < 0) {
1557 /* we reversed direction to go forward */
1560 scrub_reverse_distance += (int) (drag_info.current_pointer_x - last_scrub_x);
1563 /* still moving to the right */
1565 scrub_reversals = 0;
1566 scrub_reverse_distance = 0;
1568 delta = 0.01 * (drag_info.current_pointer_x - last_scrub_x);
1569 session->request_transport_speed (session->transport_speed() + delta);
1573 /* if there have been more than 2 opposite motion moves detected, or one that moves
1574 back more than 10 pixels, reverse direction
1577 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1579 if (scrubbing_direction > 0) {
1580 /* was forwards, go backwards */
1581 session->request_transport_speed (-0.1);
1582 scrubbing_direction = -1;
1584 /* was backwards, go forwards */
1585 session->request_transport_speed (0.1);
1586 scrubbing_direction = 1;
1589 scrub_reverse_distance = 0;
1590 scrub_reversals = 0;
1594 last_scrub_x = drag_info.current_pointer_x;
1601 if (!from_autoscroll && drag_info.item) {
1602 /* item != 0 is the best test i can think of for dragging.
1604 if (!drag_info.move_threshold_passed) {
1606 bool x_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1607 bool y_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1609 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1611 // and change the initial grab loc/frame if this drag info wants us to
1613 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1614 drag_info.grab_frame = drag_info.current_pointer_frame;
1615 drag_info.grab_x = drag_info.current_pointer_x;
1616 drag_info.grab_y = drag_info.current_pointer_y;
1617 drag_info.last_pointer_frame = drag_info.grab_frame;
1618 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1623 switch (item_type) {
1624 case PlayheadCursorItem:
1626 case GainControlPointItem:
1627 case RedirectAutomationControlPointItem:
1628 case GainAutomationControlPointItem:
1629 case PanAutomationControlPointItem:
1630 case TempoMarkerItem:
1631 case MeterMarkerItem:
1632 case RegionViewNameHighlight:
1633 case StartSelectionTrimItem:
1634 case EndSelectionTrimItem:
1637 case RedirectAutomationLineItem:
1638 case GainAutomationLineItem:
1639 case PanAutomationLineItem:
1640 case FadeInHandleItem:
1641 case FadeOutHandleItem:
1642 /* <CMT Additions> */
1643 case ImageFrameHandleStartItem:
1644 case ImageFrameHandleEndItem:
1645 case MarkerViewHandleStartItem:
1646 case MarkerViewHandleEndItem:
1647 /* </CMT Additions> */
1648 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1649 (event->motion.state & Gdk::BUTTON2_MASK))) {
1650 if (!from_autoscroll) {
1651 maybe_autoscroll (event);
1653 (this->*(drag_info.motion_callback)) (item, event);
1662 switch (mouse_mode) {
1667 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1668 (event->motion.state & GDK_BUTTON2_MASK))) {
1669 if (!from_autoscroll) {
1670 maybe_autoscroll (event);
1672 (this->*(drag_info.motion_callback)) (item, event);
1683 track_canvas_motion (event);
1684 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1692 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1694 if (drag_info.item == 0) {
1695 fatal << _("programming error: start_grab called without drag item") << endmsg;
1701 cursor = grabber_cursor;
1704 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1706 if (event->button.button == 2) {
1707 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
1708 drag_info.y_constrained = true;
1709 drag_info.x_constrained = false;
1711 drag_info.y_constrained = false;
1712 drag_info.x_constrained = true;
1715 drag_info.x_constrained = false;
1716 drag_info.y_constrained = false;
1719 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1720 drag_info.last_pointer_frame = drag_info.grab_frame;
1721 drag_info.current_pointer_frame = drag_info.grab_frame;
1722 drag_info.current_pointer_x = drag_info.grab_x;
1723 drag_info.current_pointer_y = drag_info.grab_y;
1724 drag_info.last_pointer_x = drag_info.current_pointer_x;
1725 drag_info.last_pointer_y = drag_info.current_pointer_y;
1726 drag_info.cumulative_x_drag = 0;
1727 drag_info.cumulative_y_drag = 0;
1728 drag_info.first_move = true;
1729 drag_info.move_threshold_passed = false;
1730 drag_info.want_move_threshold = false;
1731 drag_info.pointer_frame_offset = 0;
1732 drag_info.brushing = false;
1733 drag_info.copied_location = 0;
1735 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1737 event->button.time);
1739 if (session && session->transport_rolling()) {
1740 drag_info.was_rolling = true;
1742 drag_info.was_rolling = false;
1745 switch (snap_type) {
1746 case SnapToRegionStart:
1747 case SnapToRegionEnd:
1748 case SnapToRegionSync:
1749 case SnapToRegionBoundary:
1750 build_region_boundary_cache ();
1758 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1760 drag_info.item->ungrab (0);
1761 drag_info.item = new_item;
1764 cursor = grabber_cursor;
1767 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1771 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1773 bool did_drag = false;
1775 stop_canvas_autoscroll ();
1777 if (drag_info.item == 0) {
1781 drag_info.item->ungrab (event->button.time);
1783 if (drag_info.finished_callback) {
1784 drag_info.last_pointer_x = drag_info.current_pointer_x;
1785 drag_info.last_pointer_y = drag_info.current_pointer_y;
1786 (this->*(drag_info.finished_callback)) (item, event);
1789 did_drag = !drag_info.first_move;
1791 hide_verbose_canvas_cursor();
1794 drag_info.copy = false;
1795 drag_info.motion_callback = 0;
1796 drag_info.finished_callback = 0;
1797 drag_info.last_trackview = 0;
1798 drag_info.last_frame_position = 0;
1799 drag_info.grab_frame = 0;
1800 drag_info.last_pointer_frame = 0;
1801 drag_info.current_pointer_frame = 0;
1802 drag_info.brushing = false;
1804 if (drag_info.copied_location) {
1805 delete drag_info.copied_location;
1806 drag_info.copied_location = 0;
1813 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1815 drag_info.item = item;
1816 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1817 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1821 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1822 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1826 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1828 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1832 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1834 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1836 nframes_t fade_length;
1838 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1839 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1845 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1849 if (pos < (arv->region()->position() + 64)) {
1850 fade_length = 64; // this should be a minimum defined somewhere
1851 } else if (pos > arv->region()->last_frame()) {
1852 fade_length = arv->region()->length();
1854 fade_length = pos - arv->region()->position();
1856 /* mapover the region selection */
1858 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1860 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1866 tmp->reset_fade_in_shape_width (fade_length);
1869 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1871 drag_info.first_move = false;
1875 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1877 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1879 nframes_t fade_length;
1881 if (drag_info.first_move) return;
1883 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1884 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1889 if (pos < (arv->region()->position() + 64)) {
1890 fade_length = 64; // this should be a minimum defined somewhere
1891 } else if (pos > arv->region()->last_frame()) {
1892 fade_length = arv->region()->length();
1894 fade_length = pos - arv->region()->position();
1897 begin_reversible_command (_("change fade in length"));
1899 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1901 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1907 AutomationList& alist = tmp->audio_region()->fade_in();
1908 XMLNode &before = alist.get_state();
1910 tmp->audio_region()->set_fade_in_length (fade_length);
1912 XMLNode &after = alist.get_state();
1913 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1916 commit_reversible_command ();
1920 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1922 drag_info.item = item;
1923 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1924 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1928 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1929 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1933 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1935 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
1939 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1941 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1943 nframes_t fade_length;
1945 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1946 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1951 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1955 if (pos > (arv->region()->last_frame() - 64)) {
1956 fade_length = 64; // this should really be a minimum fade defined somewhere
1958 else if (pos < arv->region()->position()) {
1959 fade_length = arv->region()->length();
1962 fade_length = arv->region()->last_frame() - pos;
1965 /* mapover the region selection */
1967 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1969 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1975 tmp->reset_fade_out_shape_width (fade_length);
1978 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1980 drag_info.first_move = false;
1984 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1986 if (drag_info.first_move) return;
1988 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1990 nframes_t fade_length;
1992 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1993 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1999 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2003 if (pos > (arv->region()->last_frame() - 64)) {
2004 fade_length = 64; // this should really be a minimum fade defined somewhere
2006 else if (pos < arv->region()->position()) {
2007 fade_length = arv->region()->length();
2010 fade_length = arv->region()->last_frame() - pos;
2013 begin_reversible_command (_("change fade out length"));
2015 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2017 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2023 AutomationList& alist = tmp->audio_region()->fade_out();
2024 XMLNode &before = alist.get_state();
2026 tmp->audio_region()->set_fade_out_length (fade_length);
2028 XMLNode &after = alist.get_state();
2029 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
2032 commit_reversible_command ();
2036 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2038 drag_info.item = item;
2039 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2040 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2044 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2045 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2049 Cursor* cursor = (Cursor *) drag_info.data;
2051 if (cursor == playhead_cursor) {
2052 _dragging_playhead = true;
2054 if (session && drag_info.was_rolling) {
2055 session->request_stop ();
2058 if (session && session->is_auditioning()) {
2059 session->cancel_audition ();
2063 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2065 show_verbose_time_cursor (cursor->current_frame, 10);
2069 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2071 Cursor* cursor = (Cursor *) drag_info.data;
2072 nframes_t adjusted_frame;
2074 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2075 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2081 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2082 if (cursor == playhead_cursor) {
2083 snap_to (adjusted_frame);
2087 if (adjusted_frame == drag_info.last_pointer_frame) return;
2089 cursor->set_position (adjusted_frame);
2091 UpdateAllTransportClocks (cursor->current_frame);
2093 show_verbose_time_cursor (cursor->current_frame, 10);
2095 drag_info.last_pointer_frame = adjusted_frame;
2096 drag_info.first_move = false;
2100 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2102 if (drag_info.first_move) return;
2104 cursor_drag_motion_callback (item, event);
2106 _dragging_playhead = false;
2108 if (item == &playhead_cursor->canvas_item) {
2110 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2116 Editor::update_marker_drag_item (Location *location)
2118 double x1 = frame_to_pixel (location->start());
2119 double x2 = frame_to_pixel (location->end());
2121 if (location->is_mark()) {
2122 marker_drag_line_points.front().set_x(x1);
2123 marker_drag_line_points.back().set_x(x1);
2124 marker_drag_line->property_points() = marker_drag_line_points;
2127 range_marker_drag_rect->property_x1() = x1;
2128 range_marker_drag_rect->property_x2() = x2;
2133 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2137 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2138 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2144 Location *location = find_location_from_marker (marker, is_start);
2146 drag_info.item = item;
2147 drag_info.data = marker;
2148 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2149 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2153 _dragging_edit_point = true;
2155 drag_info.copied_location = new Location (*location);
2156 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2158 update_marker_drag_item (location);
2160 if (location->is_mark()) {
2161 // marker_drag_line->show();
2162 // marker_drag_line->raise_to_top();
2164 range_marker_drag_rect->show();
2165 range_marker_drag_rect->raise_to_top();
2169 show_verbose_time_cursor (location->start(), 10);
2171 show_verbose_time_cursor (location->end(), 10);
2174 Selection::Operation op = Keyboard::selection_type (event->button.state);
2177 case Selection::Toggle:
2178 selection->toggle (marker);
2180 case Selection::Set:
2181 selection->set (marker);
2183 case Selection::Extend:
2184 selection->add (marker);
2186 case Selection::Add:
2187 selection->add (marker);
2193 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2196 Marker* marker = (Marker *) drag_info.data;
2197 Location *real_location;
2198 Location *copy_location;
2200 bool move_both = false;
2203 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2204 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2209 nframes_t next = newframe;
2211 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2212 snap_to (newframe, 0, true);
2215 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2219 /* call this to find out if its the start or end */
2221 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2225 if (real_location->locked()) {
2229 /* use the copy that we're "dragging" around */
2231 copy_location = drag_info.copied_location;
2233 f_delta = copy_location->end() - copy_location->start();
2235 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2239 if (copy_location->is_mark()) {
2242 copy_location->set_start (newframe);
2246 if (is_start) { // start-of-range marker
2249 copy_location->set_start (newframe);
2250 copy_location->set_end (newframe + f_delta);
2251 } else if (newframe < copy_location->end()) {
2252 copy_location->set_start (newframe);
2254 snap_to (next, 1, true);
2255 copy_location->set_end (next);
2256 copy_location->set_start (newframe);
2259 } else { // end marker
2262 copy_location->set_end (newframe);
2263 copy_location->set_start (newframe - f_delta);
2264 } else if (newframe > copy_location->start()) {
2265 copy_location->set_end (newframe);
2267 } else if (newframe > 0) {
2268 snap_to (next, -1, true);
2269 copy_location->set_start (next);
2270 copy_location->set_end (newframe);
2275 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2276 drag_info.first_move = false;
2278 update_marker_drag_item (copy_location);
2280 LocationMarkers* lm = find_location_markers (real_location);
2281 lm->set_position (copy_location->start(), copy_location->end());
2282 edit_point_clock.set (copy_location->start());
2284 show_verbose_time_cursor (newframe, 10);
2288 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2290 if (drag_info.first_move) {
2291 marker_drag_motion_callback (item, event);
2295 _dragging_edit_point = false;
2297 Marker* marker = (Marker *) drag_info.data;
2300 begin_reversible_command ( _("move marker") );
2301 XMLNode &before = session->locations()->get_state();
2303 Location * location = find_location_from_marker (marker, is_start);
2307 if (location->locked()) {
2311 if (location->is_mark()) {
2312 location->set_start (drag_info.copied_location->start());
2314 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2318 XMLNode &after = session->locations()->get_state();
2319 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2320 commit_reversible_command ();
2322 marker_drag_line->hide();
2323 range_marker_drag_rect->hide();
2327 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2330 MeterMarker* meter_marker;
2332 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2333 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2337 meter_marker = dynamic_cast<MeterMarker*> (marker);
2339 MetricSection& section (meter_marker->meter());
2341 if (!section.movable()) {
2345 drag_info.item = item;
2346 drag_info.copy = false;
2347 drag_info.data = marker;
2348 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2349 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2353 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2355 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2359 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2362 MeterMarker* meter_marker;
2364 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2365 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2369 meter_marker = dynamic_cast<MeterMarker*> (marker);
2371 // create a dummy marker for visual representation of moving the copy.
2372 // The actual copying is not done before we reach the finish callback.
2374 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2375 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2376 *new MeterSection(meter_marker->meter()));
2378 drag_info.item = &new_marker->the_item();
2379 drag_info.copy = true;
2380 drag_info.data = new_marker;
2381 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2382 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2386 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2388 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2392 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2394 MeterMarker* marker = (MeterMarker *) drag_info.data;
2395 nframes_t adjusted_frame;
2397 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2398 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2404 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2405 snap_to (adjusted_frame);
2408 if (adjusted_frame == drag_info.last_pointer_frame) return;
2410 marker->set_position (adjusted_frame);
2413 drag_info.last_pointer_frame = adjusted_frame;
2414 drag_info.first_move = false;
2416 show_verbose_time_cursor (adjusted_frame, 10);
2420 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2422 if (drag_info.first_move) return;
2424 meter_marker_drag_motion_callback (drag_info.item, event);
2426 MeterMarker* marker = (MeterMarker *) drag_info.data;
2429 TempoMap& map (session->tempo_map());
2430 map.bbt_time (drag_info.last_pointer_frame, when);
2432 if (drag_info.copy == true) {
2433 begin_reversible_command (_("copy meter mark"));
2434 XMLNode &before = map.get_state();
2435 map.add_meter (marker->meter(), when);
2436 XMLNode &after = map.get_state();
2437 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2438 commit_reversible_command ();
2440 // delete the dummy marker we used for visual representation of copying.
2441 // a new visual marker will show up automatically.
2444 begin_reversible_command (_("move meter mark"));
2445 XMLNode &before = map.get_state();
2446 map.move_meter (marker->meter(), when);
2447 XMLNode &after = map.get_state();
2448 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2449 commit_reversible_command ();
2454 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2457 TempoMarker* tempo_marker;
2459 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2460 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2464 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2465 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2469 MetricSection& section (tempo_marker->tempo());
2471 if (!section.movable()) {
2475 drag_info.item = item;
2476 drag_info.copy = false;
2477 drag_info.data = marker;
2478 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2479 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2483 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2484 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2488 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2491 TempoMarker* tempo_marker;
2493 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2494 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2498 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2499 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2503 // create a dummy marker for visual representation of moving the copy.
2504 // The actual copying is not done before we reach the finish callback.
2506 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2507 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2508 *new TempoSection(tempo_marker->tempo()));
2510 drag_info.item = &new_marker->the_item();
2511 drag_info.copy = true;
2512 drag_info.data = new_marker;
2513 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2514 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2518 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2520 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2524 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2526 TempoMarker* marker = (TempoMarker *) drag_info.data;
2527 nframes_t adjusted_frame;
2529 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2530 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2536 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2537 snap_to (adjusted_frame);
2540 if (adjusted_frame == drag_info.last_pointer_frame) return;
2542 /* OK, we've moved far enough to make it worth actually move the thing. */
2544 marker->set_position (adjusted_frame);
2546 show_verbose_time_cursor (adjusted_frame, 10);
2548 drag_info.last_pointer_frame = adjusted_frame;
2549 drag_info.first_move = false;
2553 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2555 if (drag_info.first_move) return;
2557 tempo_marker_drag_motion_callback (drag_info.item, event);
2559 TempoMarker* marker = (TempoMarker *) drag_info.data;
2562 TempoMap& map (session->tempo_map());
2563 map.bbt_time (drag_info.last_pointer_frame, when);
2565 if (drag_info.copy == true) {
2566 begin_reversible_command (_("copy tempo mark"));
2567 XMLNode &before = map.get_state();
2568 map.add_tempo (marker->tempo(), when);
2569 XMLNode &after = map.get_state();
2570 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2571 commit_reversible_command ();
2573 // delete the dummy marker we used for visual representation of copying.
2574 // a new visual marker will show up automatically.
2577 begin_reversible_command (_("move tempo mark"));
2578 XMLNode &before = map.get_state();
2579 map.move_tempo (marker->tempo(), when);
2580 XMLNode &after = map.get_state();
2581 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2582 commit_reversible_command ();
2587 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2589 ControlPoint* control_point;
2591 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2592 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2596 // We shouldn't remove the first or last gain point
2597 if (control_point->line.is_last_point(*control_point) ||
2598 control_point->line.is_first_point(*control_point)) {
2602 control_point->line.remove_point (*control_point);
2606 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2608 ControlPoint* control_point;
2610 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2611 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2615 control_point->line.remove_point (*control_point);
2619 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2621 ControlPoint* control_point;
2623 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2624 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2628 drag_info.item = item;
2629 drag_info.data = control_point;
2630 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2631 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2633 start_grab (event, fader_cursor);
2635 // start the grab at the center of the control point so
2636 // the point doesn't 'jump' to the mouse after the first drag
2637 drag_info.grab_x = control_point->get_x();
2638 drag_info.grab_y = control_point->get_y();
2639 control_point->line.parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
2640 track_canvas.w2c(drag_info.grab_x, drag_info.grab_y,
2641 drag_info.grab_x, drag_info.grab_y);
2643 drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
2645 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2647 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2648 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2649 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2651 show_verbose_canvas_cursor ();
2655 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2657 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2659 double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
2660 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2662 if (event->button.state & Keyboard::SecondaryModifier) {
2667 double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
2668 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2670 // calculate zero crossing point. back off by .01 to stay on the
2671 // positive side of zero
2673 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line.height() - .01;
2674 cp->line.parent_group().i2w(_unused, zero_gain_y);
2676 // make sure we hit zero when passing through
2677 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2678 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2682 if (drag_info.x_constrained) {
2683 cx = drag_info.grab_x;
2685 if (drag_info.y_constrained) {
2686 cy = drag_info.grab_y;
2689 drag_info.cumulative_x_drag = cx - drag_info.grab_x;
2690 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2692 cp->line.parent_group().w2i (cx, cy);
2696 cy = min ((double) cp->line.height(), cy);
2698 //translate cx to frames
2699 nframes_t cx_frames = unit_to_frame (cx);
2701 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2702 snap_to (cx_frames);
2705 float fraction = 1.0 - (cy / cp->line.height());
2709 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2715 cp->line.point_drag (*cp, cx_frames , fraction, push);
2717 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2719 drag_info.first_move = false;
2723 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2725 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2727 if (drag_info.first_move) {
2731 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2732 reset_point_selection ();
2736 control_point_drag_motion_callback (item, event);
2738 cp->line.end_drag (cp);
2742 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2744 switch (mouse_mode) {
2746 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2747 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2755 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2759 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2760 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2764 start_line_grab (al, event);
2768 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2772 nframes_t frame_within_region;
2774 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2778 cx = event->button.x;
2779 cy = event->button.y;
2780 line->parent_group().w2i (cx, cy);
2781 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2783 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2784 current_line_drag_info.after)) {
2785 /* no adjacent points */
2789 drag_info.item = &line->grab_item();
2790 drag_info.data = line;
2791 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2792 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2794 start_grab (event, fader_cursor);
2796 double fraction = 1.0 - (cy / line->height());
2798 line->start_drag (0, drag_info.grab_frame, fraction);
2800 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2801 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2802 show_verbose_canvas_cursor ();
2806 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2808 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2810 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2812 if (event->button.state & Keyboard::SecondaryModifier) {
2816 double cx = drag_info.current_pointer_x;
2817 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2819 // calculate zero crossing point. back off by .01 to stay on the
2820 // positive side of zero
2822 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
2823 line->parent_group().i2w(_unused, zero_gain_y);
2825 // make sure we hit zero when passing through
2826 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2827 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2831 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2833 line->parent_group().w2i (cx, cy);
2836 cy = min ((double) line->height(), cy);
2839 fraction = 1.0 - (cy / line->height());
2843 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2849 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2851 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2855 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2857 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2858 line_drag_motion_callback (item, event);
2863 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2865 if (selection->regions.empty() || clicked_regionview == 0) {
2869 drag_info.copy = false;
2870 drag_info.item = item;
2871 drag_info.data = clicked_regionview;
2873 if (Config->get_edit_mode() == Splice) {
2874 drag_info.motion_callback = &Editor::region_drag_splice_motion_callback;
2875 drag_info.finished_callback = &Editor::region_drag_splice_finished_callback;
2877 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2878 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2884 TimeAxisView* tvp = clicked_trackview;
2885 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2887 if (tv && tv->is_audio_track()) {
2888 speed = tv->get_diskstream()->speed();
2891 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2892 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2893 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2894 // we want a move threshold
2895 drag_info.want_move_threshold = true;
2897 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2899 begin_reversible_command (_("move region(s)"));
2903 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2905 if (selection->regions.empty() || clicked_regionview == 0) {
2909 drag_info.copy = true;
2910 drag_info.item = item;
2911 drag_info.data = clicked_regionview;
2915 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2916 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
2919 if (atv && atv->is_audio_track()) {
2920 speed = atv->get_diskstream()->speed();
2923 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2924 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2925 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2926 // we want a move threshold
2927 drag_info.want_move_threshold = true;
2928 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2929 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2930 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2934 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2936 if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) {
2940 drag_info.copy = false;
2941 drag_info.item = item;
2942 drag_info.data = clicked_regionview;
2943 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2944 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2949 TimeAxisView* tvp = clicked_trackview;
2950 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2952 if (tv && tv->is_audio_track()) {
2953 speed = tv->get_diskstream()->speed();
2956 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2957 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2958 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2959 // we want a move threshold
2960 drag_info.want_move_threshold = true;
2961 drag_info.brushing = true;
2963 begin_reversible_command (_("Drag region brush"));
2967 Editor::possibly_copy_regions_during_grab (GdkEvent* event)
2969 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2971 drag_info.want_move_threshold = false; // don't copy again
2973 /* duplicate the region(s) */
2975 vector<RegionView*> new_regionviews;
2977 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2980 AudioRegionView* arv;
2985 if ((arv = dynamic_cast<AudioRegionView*>(rv)) == 0) {
2986 /* XXX handle MIDI here */
2990 nrv = new AudioRegionView (*arv);
2991 nrv->get_canvas_group()->show ();
2993 new_regionviews.push_back (nrv);
2996 if (new_regionviews.empty()) {
3000 /* reset selection to new regionviews */
3002 selection->set (new_regionviews);
3004 /* reset drag_info data to reflect the fact that we are dragging the copies */
3006 drag_info.data = new_regionviews.front();
3008 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3013 Editor::check_region_drag_possible (AudioTimeAxisView** tv)
3015 /* Which trackview is this ? */
3017 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
3018 (*tv) = dynamic_cast<AudioTimeAxisView*>(tvp);
3020 /* The region motion is only processed if the pointer is over
3024 if (!(*tv) || !(*tv)->is_audio_track()) {
3025 /* To make sure we hide the verbose canvas cursor when the mouse is
3026 not held over and audiotrack.
3028 hide_verbose_canvas_cursor ();
3035 struct RegionSelectionByPosition {
3036 bool operator() (RegionView*a, RegionView* b) {
3037 return a->region()->position () < b->region()->position();
3042 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3044 AudioTimeAxisView* tv;
3046 if (!check_region_drag_possible (&tv)) {
3050 if (!drag_info.move_threshold_passed) {
3056 if (drag_info.current_pointer_x - drag_info.grab_x > 0) {
3062 RegionSelection copy (selection->regions);
3064 RegionSelectionByPosition cmp;
3067 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
3069 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(*i)->get_time_axis_view());
3075 boost::shared_ptr<Playlist> playlist;
3077 if ((playlist = atv->playlist()) == 0) {
3081 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
3086 if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) {
3090 if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) {
3096 playlist->shuffle ((*i)->region(), dir);
3098 drag_info.grab_x = drag_info.current_pointer_x;
3103 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3108 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3112 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
3113 nframes_t pending_region_position = 0;
3114 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
3115 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
3116 bool clamp_y_axis = false;
3117 vector<int32_t> height_list(512) ;
3118 vector<int32_t>::iterator j;
3119 AudioTimeAxisView* tv;
3121 possibly_copy_regions_during_grab (event);
3123 if (!check_region_drag_possible (&tv)) {
3127 original_pointer_order = drag_info.last_trackview->order;
3129 /************************************************************
3131 ************************************************************/
3133 if (drag_info.brushing) {
3134 clamp_y_axis = true;
3139 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
3141 int32_t children = 0, numtracks = 0;
3142 // XXX hard coding track limit, oh my, so very very bad
3143 bitset <1024> tracks (0x00);
3144 /* get a bitmask representing the visible tracks */
3146 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3147 TimeAxisView *tracklist_timeview;
3148 tracklist_timeview = (*i);
3149 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
3150 list<TimeAxisView*> children_list;
3152 /* zeroes are audio tracks. ones are other types. */
3154 if (!atv2->hidden()) {
3156 if (visible_y_high < atv2->order) {
3157 visible_y_high = atv2->order;
3159 if (visible_y_low > atv2->order) {
3160 visible_y_low = atv2->order;
3163 if (!atv2->is_audio_track()) {
3164 tracks = tracks |= (0x01 << atv2->order);
3167 height_list[atv2->order] = (*i)->height;
3169 if ((children_list = atv2->get_child_list()).size() > 0) {
3170 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
3171 tracks = tracks |= (0x01 << (atv2->order + children));
3172 height_list[atv2->order + children] = (*j)->height;
3180 /* find the actual span according to the canvas */
3182 canvas_pointer_y_span = pointer_y_span;
3183 if (drag_info.last_trackview->order >= tv->order) {
3185 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
3186 if (height_list[y] == 0 ) {
3187 canvas_pointer_y_span--;
3192 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
3193 if ( height_list[y] == 0 ) {
3194 canvas_pointer_y_span++;
3199 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3200 RegionView* rv2 = (*i);
3201 double ix1, ix2, iy1, iy2;
3204 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3205 rv2->get_canvas_group()->i2w (ix1, iy1);
3206 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3207 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3209 if (atv2->order != original_pointer_order) {
3210 /* this isn't the pointer track */
3212 if (canvas_pointer_y_span > 0) {
3214 /* moving up the canvas */
3215 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
3217 int32_t visible_tracks = 0;
3218 while (visible_tracks < canvas_pointer_y_span ) {
3221 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3222 /* we're passing through a hidden track */
3227 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3228 clamp_y_axis = true;
3232 clamp_y_axis = true;
3235 } else if (canvas_pointer_y_span < 0) {
3237 /*moving down the canvas*/
3239 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3242 int32_t visible_tracks = 0;
3244 while (visible_tracks > canvas_pointer_y_span ) {
3247 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3251 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3252 clamp_y_axis = true;
3257 clamp_y_axis = true;
3263 /* this is the pointer's track */
3264 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3265 clamp_y_axis = true;
3266 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3267 clamp_y_axis = true;
3275 } else if (drag_info.last_trackview == tv) {
3276 clamp_y_axis = true;
3280 if (!clamp_y_axis) {
3281 drag_info.last_trackview = tv;
3284 /************************************************************
3286 ************************************************************/
3288 /* compute the amount of pointer motion in frames, and where
3289 the region would be if we moved it by that much.
3292 if (drag_info.move_threshold_passed) {
3294 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3296 nframes_t sync_frame;
3297 nframes_t sync_offset;
3300 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3302 sync_offset = rv->region()->sync_offset (sync_dir);
3303 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3305 /* we snap if the snap modifier is not enabled.
3308 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3309 snap_to (sync_frame);
3312 if (sync_frame - sync_offset <= sync_frame) {
3313 pending_region_position = sync_frame + (sync_dir*sync_offset);
3315 pending_region_position = 0;
3319 pending_region_position = 0;
3322 if (pending_region_position > max_frames - rv->region()->length()) {
3323 pending_region_position = drag_info.last_frame_position;
3326 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3328 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3330 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3331 to make it appear at the new location.
3334 if (pending_region_position > drag_info.last_frame_position) {
3335 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3337 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3340 drag_info.last_frame_position = pending_region_position;
3347 /* threshold not passed */
3352 /*************************************************************
3354 ************************************************************/
3356 if (x_delta == 0 && (pointer_y_span == 0)) {
3357 /* haven't reached next snap point, and we're not switching
3358 trackviews. nothing to do.
3365 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3367 RegionView* rv2 = (*i);
3369 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3371 double ix1, ix2, iy1, iy2;
3372 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3373 rv2->get_canvas_group()->i2w (ix1, iy1);
3382 /*************************************************************
3384 ************************************************************/
3388 if (drag_info.first_move) {
3389 if (drag_info.move_threshold_passed) {
3400 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3401 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3403 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3405 RegionView* rv = (*i);
3406 double ix1, ix2, iy1, iy2;
3407 int32_t temp_pointer_y_span = pointer_y_span;
3409 /* get item BBox, which will be relative to parent. so we have
3410 to query on a child, then convert to world coordinates using
3414 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3415 rv->get_canvas_group()->i2w (ix1, iy1);
3416 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3417 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3418 AudioTimeAxisView* temp_atv;
3420 if ((pointer_y_span != 0) && !clamp_y_axis) {
3423 for (j = height_list.begin(); j!= height_list.end(); j++) {
3424 if (x == canvas_atv->order) {
3425 /* we found the track the region is on */
3426 if (x != original_pointer_order) {
3427 /*this isn't from the same track we're dragging from */
3428 temp_pointer_y_span = canvas_pointer_y_span;
3430 while (temp_pointer_y_span > 0) {
3431 /* we're moving up canvas-wise,
3432 so we need to find the next track height
3434 if (j != height_list.begin()) {
3437 if (x != original_pointer_order) {
3438 /* we're not from the dragged track, so ignore hidden tracks. */
3440 temp_pointer_y_span++;
3444 temp_pointer_y_span--;
3446 while (temp_pointer_y_span < 0) {
3448 if (x != original_pointer_order) {
3450 temp_pointer_y_span--;
3454 if (j != height_list.end()) {
3457 temp_pointer_y_span++;
3459 /* find out where we'll be when we move and set height accordingly */
3461 tvp2 = trackview_by_y_position (iy1 + y_delta);
3462 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3463 rv->set_height (temp_atv->height);
3465 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3466 personally, i think this can confuse things, but never mind.
3469 //const GdkColor& col (temp_atv->view->get_region_color());
3470 //rv->set_color (const_cast<GdkColor&>(col));
3477 /* prevent the regionview from being moved to before
3478 the zero position on the canvas.
3483 if (-x_delta > ix1) {
3486 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3487 x_delta = max_frames - rv->region()->last_frame();
3491 if (drag_info.first_move) {
3493 /* hide any dependent views */
3495 rv->get_time_axis_view().hide_dependent_views (*rv);
3497 /* this is subtle. raising the regionview itself won't help,
3498 because raise_to_top() just puts the item on the top of
3499 its parent's stack. so, we need to put the trackview canvas_display group
3500 on the top, since its parent is the whole canvas.
3503 rv->get_canvas_group()->raise_to_top();
3504 rv->get_time_axis_view().canvas_display->raise_to_top();
3505 cursor_group->raise_to_top();
3506 rv->fake_set_opaque (true);
3509 if (drag_info.brushing) {
3510 mouse_brush_insert_region (rv, pending_region_position);
3512 rv->move (x_delta, y_delta);
3515 } /* foreach region */
3519 if (drag_info.first_move && drag_info.move_threshold_passed) {
3520 cursor_group->raise_to_top();
3521 drag_info.first_move = false;
3524 if (x_delta != 0 && !drag_info.brushing) {
3525 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3530 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3533 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3534 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3535 bool nocommit = true;
3537 RouteTimeAxisView* atv;
3538 bool regionview_y_movement;
3539 bool regionview_x_movement;
3540 vector<RegionView*> copies;
3542 /* first_move is set to false if the regionview has been moved in the
3546 if (drag_info.first_move) {
3553 /* The regionview has been moved at some stage during the grab so we need
3554 to account for any mouse movement between this event and the last one.
3557 region_drag_motion_callback (item, event);
3559 if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
3560 selection->set (pre_drag_region_selection);
3561 pre_drag_region_selection.clear ();
3564 if (drag_info.brushing) {
3565 /* all changes were made during motion event handlers */
3567 if (drag_info.copy) {
3568 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3569 copies.push_back (*i);
3576 /* adjust for track speed */
3579 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3580 if (atv && atv->get_diskstream()) {
3581 speed = atv->get_diskstream()->speed();
3584 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3585 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3587 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3588 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3592 if (drag_info.copy) {
3593 if (drag_info.x_constrained) {
3594 op_string = _("fixed time region copy");
3596 op_string = _("region copy");
3599 if (drag_info.x_constrained) {
3600 op_string = _("fixed time region drag");
3602 op_string = _("region drag");
3606 begin_reversible_command (op_string);
3608 if (regionview_y_movement) {
3610 /* moved to a different audio track. */
3612 vector<RegionView*> new_selection;
3614 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3616 RegionView* rv = (*i);
3618 double ix1, ix2, iy1, iy2;
3620 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3621 rv->get_canvas_group()->i2w (ix1, iy1);
3622 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3623 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3625 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3626 boost::shared_ptr<Playlist> to_playlist = atv2->playlist();
3628 where = (nframes_t) (unit_to_frame (ix1) * speed);
3629 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3631 /* undo the previous hide_dependent_views so that xfades don't
3632 disappear on copying regions
3635 rv->get_time_axis_view().reveal_dependent_views (*rv);
3637 if (!drag_info.copy) {
3639 /* the region that used to be in the old playlist is not
3640 moved to the new one - we make a copy of it. as a result,
3641 any existing editor for the region should no longer be
3645 rv->hide_region_editor();
3646 rv->fake_set_opaque (false);
3648 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3649 from_playlist->remove_region ((rv->region()));
3650 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3654 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3656 copies.push_back (rv);
3659 latest_regionviews.clear ();
3661 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3662 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3663 to_playlist->add_region (new_region, where);
3664 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3667 if (!latest_regionviews.empty()) {
3668 new_selection.insert (new_selection.end(), latest_regionviews.begin(), latest_regionviews.end());
3671 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3672 was selected in all of them, then removing it from the playlist will have removed all
3673 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3674 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3675 corresponding regionview, and the selection is now empty).
3677 this could have invalidated any and all iterators into the region selection.
3679 the heuristic we use here is: if the region selection is empty, break out of the loop
3680 here. if the region selection is not empty, then restart the loop because we know that
3681 we must have removed at least the region(view) we've just been working on as well as any
3682 that we processed on previous iterations.
3684 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3685 we can just iterate.
3688 if (drag_info.copy) {
3691 if (selection->regions.empty()) {
3694 i = selection->regions.by_layer().begin();
3699 selection->set (new_selection);
3703 /* motion within a single track */
3705 list<RegionView*> regions = selection->regions.by_layer();
3707 if (drag_info.copy) {
3708 selection->clear_regions();
3711 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3715 if (rv->region()->locked()) {
3720 if (regionview_x_movement) {
3721 double ownspeed = 1.0;
3722 atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3724 if (atv && atv->get_diskstream()) {
3725 ownspeed = atv->get_diskstream()->speed();
3728 /* base the new region position on the current position of the regionview.*/
3730 double ix1, ix2, iy1, iy2;
3732 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3733 rv->get_canvas_group()->i2w (ix1, iy1);
3734 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3738 where = rv->region()->position();
3741 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3743 assert (to_playlist);
3747 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3749 if (drag_info.copy) {
3751 boost::shared_ptr<Region> newregion;
3752 boost::shared_ptr<Region> ar;
3754 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3755 newregion = RegionFactory::create (ar);
3757 /* XXX MIDI HERE drobilla */
3763 latest_regionviews.clear ();
3764 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3765 to_playlist->add_region (newregion, (nframes_t) (where * atv->get_diskstream()->speed()));
3768 if (!latest_regionviews.empty()) {
3769 // XXX why just the first one ? we only expect one
3770 atv->reveal_dependent_views (*latest_regionviews.front());
3771 selection->add (latest_regionviews);
3774 /* if the original region was locked, we don't care for the new one */
3776 newregion->set_locked (false);
3780 /* just change the model */
3782 rv->region()->set_position (where, (void*) this);
3788 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3790 if (drag_info.copy) {
3791 copies.push_back (rv);
3799 commit_reversible_command ();
3802 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3808 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3810 /* Either add to or set the set the region selection, unless
3811 this is an alignment click (control used)
3814 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
3815 TimeAxisView* tv = &rv.get_time_axis_view();
3816 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3818 if (atv && atv->is_audio_track()) {
3819 speed = atv->get_diskstream()->speed();
3822 nframes64_t where = get_preferred_edit_position();
3826 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
3828 align_region (rv.region(), SyncPoint, (nframes_t) (where * speed));
3830 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
3832 align_region (rv.region(), End, (nframes_t) (where * speed));
3836 align_region (rv.region(), Start, (nframes_t) (where * speed));
3843 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3849 nframes_t frame_rate;
3856 switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3857 case AudioClock::BBT:
3858 session->bbt_time (frame, bbt);
3859 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3862 case AudioClock::SMPTE:
3863 session->smpte_time (frame, smpte);
3864 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3867 case AudioClock::MinSec:
3868 /* XXX this is copied from show_verbose_duration_cursor() */
3869 frame_rate = session->frame_rate();
3870 hours = frame / (frame_rate * 3600);
3871 frame = frame % (frame_rate * 3600);
3872 mins = frame / (frame_rate * 60);
3873 frame = frame % (frame_rate * 60);
3874 secs = (float) frame / (float) frame_rate;
3875 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3879 snprintf (buf, sizeof(buf), "%u", frame);
3883 if (xpos >= 0 && ypos >=0) {
3884 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3887 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3889 show_verbose_canvas_cursor ();
3893 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3900 nframes_t distance, frame_rate;
3902 Meter meter_at_start(session->tempo_map().meter_at(start));
3908 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3909 case AudioClock::BBT:
3910 session->bbt_time (start, sbbt);
3911 session->bbt_time (end, ebbt);
3914 /* XXX this computation won't work well if the
3915 user makes a selection that spans any meter changes.
3918 ebbt.bars -= sbbt.bars;
3919 if (ebbt.beats >= sbbt.beats) {
3920 ebbt.beats -= sbbt.beats;
3923 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3925 if (ebbt.ticks >= sbbt.ticks) {
3926 ebbt.ticks -= sbbt.ticks;
3929 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3932 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3935 case AudioClock::SMPTE:
3936 session->smpte_duration (end - start, smpte);
3937 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3940 case AudioClock::MinSec:
3941 /* XXX this stuff should be elsewhere.. */
3942 distance = end - start;
3943 frame_rate = session->frame_rate();
3944 hours = distance / (frame_rate * 3600);
3945 distance = distance % (frame_rate * 3600);
3946 mins = distance / (frame_rate * 60);
3947 distance = distance % (frame_rate * 60);
3948 secs = (float) distance / (float) frame_rate;
3949 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3953 snprintf (buf, sizeof(buf), "%u", end - start);
3957 if (xpos >= 0 && ypos >=0) {
3958 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3961 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3963 show_verbose_canvas_cursor ();
3967 Editor::collect_new_region_view (RegionView* rv)
3969 latest_regionviews.push_back (rv);
3973 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3975 if (clicked_regionview == 0) {
3979 /* lets try to create new Region for the selection */
3981 vector<boost::shared_ptr<AudioRegion> > new_regions;
3982 create_region_from_selection (new_regions);
3984 if (new_regions.empty()) {
3988 /* XXX fix me one day to use all new regions */
3990 boost::shared_ptr<Region> region (new_regions.front());
3992 /* add it to the current stream/playlist.
3994 tricky: the streamview for the track will add a new regionview. we will
3995 catch the signal it sends when it creates the regionview to
3996 set the regionview we want to then drag.
3999 latest_regionviews.clear();
4000 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4002 /* A selection grab currently creates two undo/redo operations, one for
4003 creating the new region and another for moving it.
4006 begin_reversible_command (_("selection grab"));
4008 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
4010 XMLNode *before = &(playlist->get_state());
4011 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
4012 XMLNode *after = &(playlist->get_state());
4013 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4015 commit_reversible_command ();
4019 if (latest_regionviews.empty()) {
4020 /* something went wrong */
4024 /* we need to deselect all other regionviews, and select this one
4025 i'm ignoring undo stuff, because the region creation will take care of it
4027 selection->set (latest_regionviews);
4029 drag_info.item = latest_regionviews.front()->get_canvas_group();
4030 drag_info.data = latest_regionviews.front();
4031 drag_info.motion_callback = &Editor::region_drag_motion_callback;
4032 drag_info.finished_callback = &Editor::region_drag_finished_callback;
4036 drag_info.last_trackview = clicked_trackview;
4037 drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4038 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4040 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4044 Editor::cancel_selection ()
4046 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4047 (*i)->hide_selection ();
4049 selection->clear ();
4050 clicked_selection = 0;
4054 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4056 nframes_t start = 0;
4063 drag_info.item = item;
4064 drag_info.motion_callback = &Editor::drag_selection;
4065 drag_info.finished_callback = &Editor::end_selection_op;
4070 case CreateSelection:
4071 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4072 drag_info.copy = true;
4074 drag_info.copy = false;
4076 start_grab (event, selector_cursor);
4079 case SelectionStartTrim:
4080 if (clicked_trackview) {
4081 clicked_trackview->order_selection_trims (item, true);
4083 start_grab (event, trimmer_cursor);
4084 start = selection->time[clicked_selection].start;
4085 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4088 case SelectionEndTrim:
4089 if (clicked_trackview) {
4090 clicked_trackview->order_selection_trims (item, false);
4092 start_grab (event, trimmer_cursor);
4093 end = selection->time[clicked_selection].end;
4094 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
4098 start = selection->time[clicked_selection].start;
4100 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4104 if (selection_op == SelectionMove) {
4105 show_verbose_time_cursor(start, 10);
4107 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4112 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4114 nframes_t start = 0;
4117 nframes_t pending_position;
4119 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4120 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4122 pending_position = 0;
4125 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4126 snap_to (pending_position);
4129 /* only alter selection if the current frame is
4130 different from the last frame position (adjusted)
4133 if (pending_position == drag_info.last_pointer_frame) return;
4135 switch (selection_op) {
4136 case CreateSelection:
4138 if (drag_info.first_move) {
4139 snap_to (drag_info.grab_frame);
4142 if (pending_position < drag_info.grab_frame) {
4143 start = pending_position;
4144 end = drag_info.grab_frame;
4146 end = pending_position;
4147 start = drag_info.grab_frame;
4150 /* first drag: Either add to the selection
4151 or create a new selection->
4154 if (drag_info.first_move) {
4156 begin_reversible_command (_("range selection"));
4158 if (drag_info.copy) {
4159 /* adding to the selection */
4160 clicked_selection = selection->add (start, end);
4161 drag_info.copy = false;
4163 /* new selection-> */
4164 clicked_selection = selection->set (clicked_trackview, start, end);
4169 case SelectionStartTrim:
4171 if (drag_info.first_move) {
4172 begin_reversible_command (_("trim selection start"));
4175 start = selection->time[clicked_selection].start;
4176 end = selection->time[clicked_selection].end;
4178 if (pending_position > end) {
4181 start = pending_position;
4185 case SelectionEndTrim:
4187 if (drag_info.first_move) {
4188 begin_reversible_command (_("trim selection end"));
4191 start = selection->time[clicked_selection].start;
4192 end = selection->time[clicked_selection].end;
4194 if (pending_position < start) {
4197 end = pending_position;
4204 if (drag_info.first_move) {
4205 begin_reversible_command (_("move selection"));
4208 start = selection->time[clicked_selection].start;
4209 end = selection->time[clicked_selection].end;
4211 length = end - start;
4213 start = pending_position;
4216 end = start + length;
4221 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4222 start_canvas_autoscroll (1);
4226 selection->replace (clicked_selection, start, end);
4229 drag_info.last_pointer_frame = pending_position;
4230 drag_info.first_move = false;
4232 if (selection_op == SelectionMove) {
4233 show_verbose_time_cursor(start, 10);
4235 show_verbose_time_cursor(pending_position, 10);
4240 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4242 if (!drag_info.first_move) {
4243 drag_selection (item, event);
4244 /* XXX this is not object-oriented programming at all. ick */
4245 if (selection->time.consolidate()) {
4246 selection->TimeChanged ();
4248 commit_reversible_command ();
4250 /* just a click, no pointer movement.*/
4252 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4254 selection->clear_time();
4259 /* XXX what happens if its a music selection? */
4260 session->set_audio_range (selection->time);
4261 stop_canvas_autoscroll ();
4265 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4268 TimeAxisView* tvp = clicked_trackview;
4269 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4271 if (tv && tv->is_audio_track()) {
4272 speed = tv->get_diskstream()->speed();
4275 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
4276 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
4277 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
4279 //drag_info.item = clicked_regionview->get_name_highlight();
4280 drag_info.item = item;
4281 drag_info.motion_callback = &Editor::trim_motion_callback;
4282 drag_info.finished_callback = &Editor::trim_finished_callback;
4284 start_grab (event, trimmer_cursor);
4286 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4287 trim_op = ContentsTrim;
4289 /* These will get overridden for a point trim.*/
4290 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4291 /* closer to start */
4292 trim_op = StartTrim;
4293 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4301 show_verbose_time_cursor(region_start, 10);
4304 show_verbose_time_cursor(region_end, 10);
4307 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4313 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4315 RegionView* rv = clicked_regionview;
4316 nframes_t frame_delta = 0;
4317 bool left_direction;
4318 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4320 /* snap modifier works differently here..
4321 its' current state has to be passed to the
4322 various trim functions in order to work properly
4326 TimeAxisView* tvp = clicked_trackview;
4327 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4328 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4330 if (tv && tv->is_audio_track()) {
4331 speed = tv->get_diskstream()->speed();
4334 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4335 left_direction = true;
4337 left_direction = false;
4341 snap_to (drag_info.current_pointer_frame);
4344 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4348 if (drag_info.first_move) {
4354 trim_type = "Region start trim";
4357 trim_type = "Region end trim";
4360 trim_type = "Region content trim";
4364 begin_reversible_command (trim_type);
4366 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4367 (*i)->fake_set_opaque(false);
4368 (*i)->region()->freeze ();
4370 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4372 arv->temporarily_hide_envelope ();
4374 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4375 insert_result = motion_frozen_playlists.insert (pl);
4376 if (insert_result.second) {
4377 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4382 if (left_direction) {
4383 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4385 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4390 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4393 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4394 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4400 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4403 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4404 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4411 bool swap_direction = false;
4413 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4414 swap_direction = true;
4417 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4418 i != selection->regions.by_layer().end(); ++i)
4420 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4428 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4431 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4434 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4438 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4439 drag_info.first_move = false;
4443 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4445 boost::shared_ptr<Region> region (rv.region());
4447 if (region->locked()) {
4451 nframes_t new_bound;
4454 TimeAxisView* tvp = clicked_trackview;
4455 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4457 if (tv && tv->is_audio_track()) {
4458 speed = tv->get_diskstream()->speed();
4461 if (left_direction) {
4462 if (swap_direction) {
4463 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4465 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4468 if (swap_direction) {
4469 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4471 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4476 snap_to (new_bound);
4478 region->trim_start ((nframes_t) (new_bound * speed), this);
4479 rv.region_changed (StartChanged);
4483 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4485 boost::shared_ptr<Region> region (rv.region());
4487 if (region->locked()) {
4491 nframes_t new_bound;
4494 TimeAxisView* tvp = clicked_trackview;
4495 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4497 if (tv && tv->is_audio_track()) {
4498 speed = tv->get_diskstream()->speed();
4501 if (left_direction) {
4502 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4504 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4508 snap_to (new_bound, (left_direction ? 0 : 1));
4511 region->trim_front ((nframes_t) (new_bound * speed), this);
4513 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4517 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4519 boost::shared_ptr<Region> region (rv.region());
4521 if (region->locked()) {
4525 nframes_t new_bound;
4528 TimeAxisView* tvp = clicked_trackview;
4529 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4531 if (tv && tv->is_audio_track()) {
4532 speed = tv->get_diskstream()->speed();
4535 if (left_direction) {
4536 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4538 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4542 snap_to (new_bound);
4544 region->trim_end ((nframes_t) (new_bound * speed), this);
4545 rv.region_changed (LengthChanged);
4549 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4551 if (!drag_info.first_move) {
4552 trim_motion_callback (item, event);
4554 if (!selection->selected (clicked_regionview)) {
4555 thaw_region_after_trim (*clicked_regionview);
4558 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4559 i != selection->regions.by_layer().end(); ++i)
4561 thaw_region_after_trim (**i);
4562 (*i)->fake_set_opaque (true);
4566 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4568 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4571 motion_frozen_playlists.clear ();
4573 commit_reversible_command();
4575 /* no mouse movement */
4581 Editor::point_trim (GdkEvent* event)
4583 RegionView* rv = clicked_regionview;
4584 nframes_t new_bound = drag_info.current_pointer_frame;
4586 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4587 snap_to (new_bound);
4590 /* Choose action dependant on which button was pressed */
4591 switch (event->button.button) {
4593 trim_op = StartTrim;
4594 begin_reversible_command (_("Start point trim"));
4596 if (selection->selected (rv)) {
4598 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4599 i != selection->regions.by_layer().end(); ++i)
4601 if (!(*i)->region()->locked()) {
4602 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4603 XMLNode &before = pl->get_state();
4604 (*i)->region()->trim_front (new_bound, this);
4605 XMLNode &after = pl->get_state();
4606 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4612 if (!rv->region()->locked()) {
4613 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4614 XMLNode &before = pl->get_state();
4615 rv->region()->trim_front (new_bound, this);
4616 XMLNode &after = pl->get_state();
4617 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4621 commit_reversible_command();
4626 begin_reversible_command (_("End point trim"));
4628 if (selection->selected (rv)) {
4630 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4632 if (!(*i)->region()->locked()) {
4633 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4634 XMLNode &before = pl->get_state();
4635 (*i)->region()->trim_end (new_bound, this);
4636 XMLNode &after = pl->get_state();
4637 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4643 if (!rv->region()->locked()) {
4644 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4645 XMLNode &before = pl->get_state();
4646 rv->region()->trim_end (new_bound, this);
4647 XMLNode &after = pl->get_state();
4648 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4652 commit_reversible_command();
4661 Editor::thaw_region_after_trim (RegionView& rv)
4663 boost::shared_ptr<Region> region (rv.region());
4665 if (region->locked()) {
4669 region->thaw (_("trimmed region"));
4670 XMLNode &after = region->playlist()->get_state();
4671 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4673 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4675 arv->unhide_envelope ();
4679 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4684 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4685 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4689 Location* location = find_location_from_marker (marker, is_start);
4690 location->set_hidden (true, this);
4695 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4701 drag_info.item = item;
4702 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4703 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4705 range_marker_op = op;
4707 if (!temp_location) {
4708 temp_location = new Location;
4712 case CreateRangeMarker:
4713 case CreateTransportMarker:
4714 case CreateCDMarker:
4716 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4717 drag_info.copy = true;
4719 drag_info.copy = false;
4721 start_grab (event, selector_cursor);
4725 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4730 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4732 nframes_t start = 0;
4734 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4736 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4737 snap_to (drag_info.current_pointer_frame);
4740 /* only alter selection if the current frame is
4741 different from the last frame position.
4744 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4746 switch (range_marker_op) {
4747 case CreateRangeMarker:
4748 case CreateTransportMarker:
4749 case CreateCDMarker:
4750 if (drag_info.first_move) {
4751 snap_to (drag_info.grab_frame);
4754 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4755 start = drag_info.current_pointer_frame;
4756 end = drag_info.grab_frame;
4758 end = drag_info.current_pointer_frame;
4759 start = drag_info.grab_frame;
4762 /* first drag: Either add to the selection
4763 or create a new selection.
4766 if (drag_info.first_move) {
4768 temp_location->set (start, end);
4772 update_marker_drag_item (temp_location);
4773 range_marker_drag_rect->show();
4774 range_marker_drag_rect->raise_to_top();
4780 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4781 start_canvas_autoscroll (1);
4785 temp_location->set (start, end);
4787 double x1 = frame_to_pixel (start);
4788 double x2 = frame_to_pixel (end);
4789 crect->property_x1() = x1;
4790 crect->property_x2() = x2;
4792 update_marker_drag_item (temp_location);
4795 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4796 drag_info.first_move = false;
4798 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4803 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4805 Location * newloc = 0;
4809 if (!drag_info.first_move) {
4810 drag_range_markerbar_op (item, event);
4812 switch (range_marker_op) {
4813 case CreateRangeMarker:
4814 case CreateCDMarker:
4816 begin_reversible_command (_("new range marker"));
4817 XMLNode &before = session->locations()->get_state();
4818 session->locations()->next_available_name(rangename,"unnamed");
4819 if (range_marker_op == CreateCDMarker) {
4820 flags = Location::IsRangeMarker|Location::IsCDMarker;
4823 flags = Location::IsRangeMarker;
4825 newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
4826 session->locations()->add (newloc, true);
4827 XMLNode &after = session->locations()->get_state();
4828 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4829 commit_reversible_command ();
4831 range_bar_drag_rect->hide();
4832 range_marker_drag_rect->hide();
4836 case CreateTransportMarker:
4837 // popup menu to pick loop or punch
4838 new_transport_marker_context_menu (&event->button, item);
4843 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4845 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
4850 start = session->locations()->first_mark_before (drag_info.grab_frame);
4851 end = session->locations()->first_mark_after (drag_info.grab_frame);
4853 if (end == max_frames) {
4854 end = session->current_end_frame ();
4858 start = session->current_start_frame ();
4861 switch (mouse_mode) {
4863 /* find the two markers on either side and then make the selection from it */
4864 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4868 /* find the two markers on either side of the click and make the range out of it */
4869 selection->set (0, start, end);
4878 stop_canvas_autoscroll ();
4884 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4886 drag_info.item = item;
4887 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4888 drag_info.finished_callback = &Editor::end_mouse_zoom;
4890 start_grab (event, zoom_cursor);
4892 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4896 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4901 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4902 snap_to (drag_info.current_pointer_frame);
4904 if (drag_info.first_move) {
4905 snap_to (drag_info.grab_frame);
4909 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4911 /* base start and end on initial click position */
4912 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4913 start = drag_info.current_pointer_frame;
4914 end = drag_info.grab_frame;
4916 end = drag_info.current_pointer_frame;
4917 start = drag_info.grab_frame;
4922 if (drag_info.first_move) {
4924 zoom_rect->raise_to_top();
4927 reposition_zoom_rect(start, end);
4929 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4930 drag_info.first_move = false;
4932 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4937 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4939 if (!drag_info.first_move) {
4940 drag_mouse_zoom (item, event);
4942 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4943 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4945 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4948 temporal_zoom_to_frame (false, drag_info.grab_frame);
4950 temporal_zoom_step (false);
4951 center_screen (drag_info.grab_frame);
4959 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4961 double x1 = frame_to_pixel (start);
4962 double x2 = frame_to_pixel (end);
4963 double y2 = full_canvas_height - 1.0;
4965 zoom_rect->property_x1() = x1;
4966 zoom_rect->property_y1() = 1.0;
4967 zoom_rect->property_x2() = x2;
4968 zoom_rect->property_y2() = y2;
4972 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4974 drag_info.item = item;
4975 drag_info.motion_callback = &Editor::drag_rubberband_select;
4976 drag_info.finished_callback = &Editor::end_rubberband_select;
4978 start_grab (event, cross_hair_cursor);
4980 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4984 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4991 /* use a bigger drag threshold than the default */
4993 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4997 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4998 if (drag_info.first_move) {
4999 snap_to (drag_info.grab_frame);
5001 snap_to (drag_info.current_pointer_frame);
5004 /* base start and end on initial click position */
5006 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5007 start = drag_info.current_pointer_frame;
5008 end = drag_info.grab_frame;
5010 end = drag_info.current_pointer_frame;
5011 start = drag_info.grab_frame;
5014 if (drag_info.current_pointer_y < drag_info.grab_y) {
5015 y1 = drag_info.current_pointer_y;
5016 y2 = drag_info.grab_y;
5018 y2 = drag_info.current_pointer_y;
5019 y1 = drag_info.grab_y;
5023 if (start != end || y1 != y2) {
5025 double x1 = frame_to_pixel (start);
5026 double x2 = frame_to_pixel (end);
5028 rubberband_rect->property_x1() = x1;
5029 rubberband_rect->property_y1() = y1;
5030 rubberband_rect->property_x2() = x2;
5031 rubberband_rect->property_y2() = y2;
5033 rubberband_rect->show();
5034 rubberband_rect->raise_to_top();
5036 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5037 drag_info.first_move = false;
5039 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5044 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5046 if (!drag_info.first_move) {
5048 drag_rubberband_select (item, event);
5051 if (drag_info.current_pointer_y < drag_info.grab_y) {
5052 y1 = drag_info.current_pointer_y;
5053 y2 = drag_info.grab_y;
5056 y2 = drag_info.current_pointer_y;
5057 y1 = drag_info.grab_y;
5061 Selection::Operation op = Keyboard::selection_type (event->button.state);
5064 begin_reversible_command (_("rubberband selection"));
5066 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5067 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5069 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5073 commit_reversible_command ();
5077 selection->clear_tracks();
5078 selection->clear_regions();
5079 selection->clear_points ();
5080 selection->clear_lines ();
5083 rubberband_rect->hide();
5088 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5090 using namespace Gtkmm2ext;
5092 ArdourPrompter prompter (false);
5094 prompter.set_prompt (_("Name for region:"));
5095 prompter.set_initial_text (clicked_regionview->region()->name());
5096 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5097 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5098 prompter.show_all ();
5099 switch (prompter.run ()) {
5100 case Gtk::RESPONSE_ACCEPT:
5102 prompter.get_result(str);
5104 clicked_regionview->region()->set_name (str);
5112 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5114 drag_info.item = item;
5115 drag_info.motion_callback = &Editor::time_fx_motion;
5116 drag_info.finished_callback = &Editor::end_time_fx;
5120 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5124 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5126 RegionView* rv = clicked_regionview;
5128 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5129 snap_to (drag_info.current_pointer_frame);
5132 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5136 if (drag_info.current_pointer_frame > rv->region()->position()) {
5137 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5140 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5141 drag_info.first_move = false;
5143 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5147 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5149 clicked_regionview->get_time_axis_view().hide_timestretch ();
5151 if (drag_info.first_move) {
5155 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5156 /* backwards drag of the left edge - not usable */
5160 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5161 #ifdef USE_RUBBERBAND
5162 float percentage = (float) ((double) newlen / (double) clicked_regionview->region()->length());
5164 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5167 begin_reversible_command (_("timestretch"));
5169 if (time_stretch (selection->regions, percentage) == 0) {
5170 session->commit_reversible_command ();
5175 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
5177 /* no brushing without a useful snap setting */
5180 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
5183 switch (snap_mode) {
5185 return; /* can't work because it allows region to be placed anywhere */
5190 switch (snap_type) {
5198 /* don't brush a copy over the original */
5200 if (pos == rv->region()->position()) {
5204 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
5206 if (atv == 0 || !atv->is_audio_track()) {
5210 boost::shared_ptr<Playlist> playlist = atv->playlist();
5211 double speed = atv->get_diskstream()->speed();
5213 XMLNode &before = playlist->get_state();
5214 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
5215 XMLNode &after = playlist->get_state();
5216 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5218 // playlist is frozen, so we have to update manually
5220 playlist->Modified(); /* EMIT SIGNAL */
5224 Editor::track_height_step_timeout ()
5227 struct timeval delta;
5229 gettimeofday (&now, 0);
5230 timersub (&now, &last_track_height_step_timestamp, &delta);
5232 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
5233 current_stepping_trackview = 0;