2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <pbd/error.h>
30 #include <gtkmm2ext/utils.h>
31 #include <pbd/memento_command.h>
33 #include "ardour_ui.h"
35 #include "time_axis_view.h"
36 #include "audio_time_axis.h"
37 #include "audio_region_view.h"
39 #include "streamview.h"
40 #include "region_gain_line.h"
41 #include "automation_time_axis.h"
44 #include "selection.h"
47 #include "rgb_macros.h"
49 #include <ardour/types.h>
50 #include <ardour/profile.h>
51 #include <ardour/route.h>
52 #include <ardour/audio_track.h>
53 #include <ardour/audio_diskstream.h>
54 #include <ardour/playlist.h>
55 #include <ardour/audioplaylist.h>
56 #include <ardour/audioregion.h>
57 #include <ardour/dB.h>
58 #include <ardour/utils.h>
59 #include <ardour/region_factory.h>
66 using namespace ARDOUR;
70 using namespace Editing;
73 Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
77 Gdk::ModifierType mask;
78 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas.get_window();
79 Glib::RefPtr<const Gdk::Window> pointer_window;
81 pointer_window = canvas_window->get_pointer (x, y, mask);
83 if (pointer_window == track_canvas.get_bin_window()) {
85 track_canvas.window_to_world (x, y, wx, wy);
86 in_track_canvas = true;
89 in_track_canvas = false;
91 if (pointer_window == time_canvas.get_bin_window()) {
92 time_canvas.window_to_world (x, y, wx, wy);
98 wx += horizontal_adjustment.get_value();
99 wy += vertical_adjustment.get_value();
102 event.type = GDK_BUTTON_RELEASE;
106 where = event_frame (&event, 0, 0);
111 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy) const
125 switch (event->type) {
126 case GDK_BUTTON_RELEASE:
127 case GDK_BUTTON_PRESS:
128 case GDK_2BUTTON_PRESS:
129 case GDK_3BUTTON_PRESS:
130 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
132 case GDK_MOTION_NOTIFY:
133 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
135 case GDK_ENTER_NOTIFY:
136 case GDK_LEAVE_NOTIFY:
137 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
140 case GDK_KEY_RELEASE:
141 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
144 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
148 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
149 position is negative (as can be the case with motion events in particular),
150 the frame location is always positive.
153 return pixel_to_frame (*pcx);
157 Editor::mouse_mode_toggled (MouseMode m)
159 if (ignore_mouse_mode_toggle) {
165 if (mouse_select_button.get_active()) {
171 if (mouse_move_button.get_active()) {
177 if (mouse_gain_button.get_active()) {
183 if (mouse_zoom_button.get_active()) {
189 if (mouse_timefx_button.get_active()) {
195 if (mouse_audition_button.get_active()) {
206 Editor::set_mouse_mode (MouseMode m, bool force)
208 if (drag_info.item) {
212 if (!force && m == mouse_mode) {
220 if (mouse_mode != MouseRange) {
222 /* in all modes except range, hide the range selection,
223 show the object (region) selection.
226 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
227 (*i)->set_should_show_selection (true);
229 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
230 (*i)->hide_selection ();
236 in range mode,show the range selection.
239 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
240 if ((*i)->get_selected()) {
241 (*i)->show_selection (selection->time);
246 /* XXX the hack of unsetting all other buttons should go
247 away once GTK2 allows us to use regular radio buttons drawn like
248 normal buttons, rather than my silly GroupedButton hack.
251 ignore_mouse_mode_toggle = true;
253 switch (mouse_mode) {
255 mouse_select_button.set_active (true);
256 current_canvas_cursor = selector_cursor;
260 mouse_move_button.set_active (true);
261 if (Profile->get_sae()) {
262 current_canvas_cursor = timebar_cursor;
264 current_canvas_cursor = grabber_cursor;
269 mouse_gain_button.set_active (true);
270 current_canvas_cursor = cross_hair_cursor;
274 mouse_zoom_button.set_active (true);
275 current_canvas_cursor = zoom_cursor;
279 mouse_timefx_button.set_active (true);
280 current_canvas_cursor = time_fx_cursor; // just use playhead
284 mouse_audition_button.set_active (true);
285 current_canvas_cursor = speaker_cursor;
289 ignore_mouse_mode_toggle = false;
292 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
297 Editor::step_mouse_mode (bool next)
299 switch (current_mouse_mode()) {
301 if (next) set_mouse_mode (MouseRange);
302 else set_mouse_mode (MouseTimeFX);
306 if (next) set_mouse_mode (MouseZoom);
307 else set_mouse_mode (MouseObject);
311 if (next) set_mouse_mode (MouseGain);
312 else set_mouse_mode (MouseRange);
316 if (next) set_mouse_mode (MouseTimeFX);
317 else set_mouse_mode (MouseZoom);
321 if (next) set_mouse_mode (MouseAudition);
322 else set_mouse_mode (MouseGain);
326 if (next) set_mouse_mode (MouseObject);
327 else set_mouse_mode (MouseTimeFX);
333 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
335 /* in object/audition/timefx mode, any button press sets
336 the selection if the object can be selected. this is a
337 bit of hack, because we want to avoid this if the
338 mouse operation is a region alignment.
340 note: not dbl-click or triple-click
343 if (((mouse_mode != MouseObject) &&
344 (mouse_mode != MouseAudition || item_type != RegionItem) &&
345 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
346 (mouse_mode != MouseRange)) ||
348 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
353 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
355 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
357 /* almost no selection action on modified button-2 or button-3 events */
359 if (item_type != RegionItem && event->button.button != 2) {
365 Selection::Operation op = Keyboard::selection_type (event->button.state);
366 bool press = (event->type == GDK_BUTTON_PRESS);
368 // begin_reversible_command (_("select on click"));
372 if (mouse_mode != MouseRange) {
373 set_selected_regionview_from_click (press, op, true);
374 } else if (event->type == GDK_BUTTON_PRESS) {
375 set_selected_track_as_side_effect ();
379 case RegionViewNameHighlight:
381 if (mouse_mode != MouseRange) {
382 set_selected_regionview_from_click (press, op, true);
383 } else if (event->type == GDK_BUTTON_PRESS) {
384 set_selected_track_as_side_effect ();
388 case FadeInHandleItem:
390 case FadeOutHandleItem:
392 if (mouse_mode != MouseRange) {
393 set_selected_regionview_from_click (press, op, true);
394 } else if (event->type == GDK_BUTTON_PRESS) {
395 set_selected_track_as_side_effect ();
399 case GainAutomationControlPointItem:
400 case PanAutomationControlPointItem:
401 case RedirectAutomationControlPointItem:
402 set_selected_track_as_side_effect ();
403 if (mouse_mode != MouseRange) {
404 set_selected_control_point_from_click (op, false);
409 /* for context click or range selection, select track */
410 if (event->button.button == 3) {
411 set_selected_track_as_side_effect ();
412 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
413 set_selected_track_as_side_effect ();
417 case AutomationTrackItem:
418 set_selected_track_as_side_effect (true);
426 const static double ZERO_GAIN_FRACTION = gain_to_slider_position(dB_to_coefficient(0.0));
429 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
431 track_canvas.grab_focus();
433 if (session && session->actively_recording()) {
437 button_selection (item, event, item_type);
439 if (drag_info.item == 0 &&
440 (Keyboard::is_delete_event (&event->button) ||
441 Keyboard::is_context_menu_event (&event->button) ||
442 Keyboard::is_edit_event (&event->button))) {
444 /* handled by button release */
448 switch (event->button.button) {
451 if (event->type == GDK_BUTTON_PRESS) {
453 if (drag_info.item) {
454 drag_info.item->ungrab (event->button.time);
457 /* single mouse clicks on any of these item types operate
458 independent of mouse mode, mostly because they are
459 not on the main track canvas or because we want
464 case PlayheadCursorItem:
465 start_cursor_grab (item, event);
469 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
470 hide_marker (item, event);
472 start_marker_grab (item, event);
476 case TempoMarkerItem:
477 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
478 start_tempo_marker_copy_grab (item, event);
480 start_tempo_marker_grab (item, event);
484 case MeterMarkerItem:
485 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
486 start_meter_marker_copy_grab (item, event);
488 start_meter_marker_grab (item, event);
498 case RangeMarkerBarItem:
499 start_range_markerbar_op (item, event, CreateRangeMarker);
503 case CdMarkerBarItem:
504 start_range_markerbar_op (item, event, CreateCDMarker);
508 case TransportMarkerBarItem:
509 start_range_markerbar_op (item, event, CreateTransportMarker);
518 switch (mouse_mode) {
521 case StartSelectionTrimItem:
522 start_selection_op (item, event, SelectionStartTrim);
525 case EndSelectionTrimItem:
526 start_selection_op (item, event, SelectionEndTrim);
530 if (Keyboard::modifier_state_contains
531 (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) {
532 // contains and not equals because I can't use alt as a modifier alone.
533 start_selection_grab (item, event);
534 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
535 /* grab selection for moving */
536 start_selection_op (item, event, SelectionMove);
538 /* this was debated, but decided the more common action was to
539 make a new selection */
540 start_selection_op (item, event, CreateSelection);
545 start_selection_op (item, event, CreateSelection);
551 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
552 event->type == GDK_BUTTON_PRESS) {
554 start_rubberband_select (item, event);
556 } else if (event->type == GDK_BUTTON_PRESS) {
559 case FadeInHandleItem:
560 start_fade_in_grab (item, event);
563 case FadeOutHandleItem:
564 start_fade_out_grab (item, event);
568 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
569 start_region_copy_grab (item, event);
570 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
571 start_region_brush_grab (item, event);
573 start_region_grab (item, event);
577 case RegionViewNameHighlight:
578 start_trim (item, event);
583 /* rename happens on edit clicks */
584 start_trim (clicked_regionview->get_name_highlight(), event);
588 case GainAutomationControlPointItem:
589 case PanAutomationControlPointItem:
590 case RedirectAutomationControlPointItem:
591 start_control_point_grab (item, event);
595 case GainAutomationLineItem:
596 case PanAutomationLineItem:
597 case RedirectAutomationLineItem:
598 start_line_grab_from_line (item, event);
603 case AutomationTrackItem:
604 start_rubberband_select (item, event);
607 /* <CMT Additions> */
608 case ImageFrameHandleStartItem:
609 imageframe_start_handle_op(item, event) ;
612 case ImageFrameHandleEndItem:
613 imageframe_end_handle_op(item, event) ;
616 case MarkerViewHandleStartItem:
617 markerview_item_start_handle_op(item, event) ;
620 case MarkerViewHandleEndItem:
621 markerview_item_end_handle_op(item, event) ;
624 /* </CMT Additions> */
626 /* <CMT Additions> */
628 start_markerview_grab(item, event) ;
631 start_imageframe_grab(item, event) ;
633 /* </CMT Additions> */
649 // start_line_grab_from_regionview (item, event);
652 case GainControlPointItem:
653 start_control_point_grab (item, event);
657 start_line_grab_from_line (item, event);
660 case GainAutomationControlPointItem:
661 case PanAutomationControlPointItem:
662 case RedirectAutomationControlPointItem:
663 start_control_point_grab (item, event);
674 case GainAutomationControlPointItem:
675 case PanAutomationControlPointItem:
676 case RedirectAutomationControlPointItem:
677 start_control_point_grab (item, event);
680 case GainAutomationLineItem:
681 case PanAutomationLineItem:
682 case RedirectAutomationLineItem:
683 start_line_grab_from_line (item, event);
687 // XXX need automation mode to identify which
689 // start_line_grab_from_regionview (item, event);
699 if (event->type == GDK_BUTTON_PRESS) {
700 start_mouse_zoom (item, event);
707 if (item_type == RegionItem) {
708 start_time_fx (item, event);
715 scrub_reverse_distance = 0;
716 last_scrub_x = event->button.x;
717 scrubbing_direction = 0;
718 track_canvas.get_window()->set_cursor (*transparent_cursor);
719 /* rest handled in motion & release */
728 switch (mouse_mode) {
730 if (event->type == GDK_BUTTON_PRESS) {
733 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
734 start_region_copy_grab (item, event);
736 start_region_grab (item, event);
741 case GainAutomationControlPointItem:
742 case PanAutomationControlPointItem:
743 case RedirectAutomationControlPointItem:
744 start_control_point_grab (item, event);
755 case RegionViewNameHighlight:
756 start_trim (item, event);
761 start_trim (clicked_regionview->get_name_highlight(), event);
772 if (event->type == GDK_BUTTON_PRESS) {
773 /* relax till release */
780 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
781 temporal_zoom_session();
783 temporal_zoom_to_frame (true, event_frame(event));
806 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
808 nframes_t where = event_frame (event, 0, 0);
810 /* no action if we're recording */
812 if (session && session->actively_recording()) {
816 /* first, see if we're finishing a drag ... */
818 if (drag_info.item) {
819 if (end_grab (item, event)) {
820 /* grab dragged, so do nothing else */
825 button_selection (item, event, item_type);
827 /* edit events get handled here */
829 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
835 case TempoMarkerItem:
836 edit_tempo_marker (item);
839 case MeterMarkerItem:
840 edit_meter_marker (item);
844 if (clicked_regionview->name_active()) {
845 return mouse_rename_region (item, event);
855 /* context menu events get handled here */
857 if (Keyboard::is_context_menu_event (&event->button)) {
859 if (drag_info.item == 0) {
861 /* no matter which button pops up the context menu, tell the menu
862 widget to use button 1 to drive menu selection.
867 case FadeInHandleItem:
869 case FadeOutHandleItem:
870 popup_fade_context_menu (1, event->button.time, item, item_type);
874 popup_track_context_menu (1, event->button.time, item_type, false, where);
878 case RegionViewNameHighlight:
880 popup_track_context_menu (1, event->button.time, item_type, false, where);
884 popup_track_context_menu (1, event->button.time, item_type, true, where);
887 case AutomationTrackItem:
888 popup_track_context_menu (1, event->button.time, item_type, false, where);
892 case RangeMarkerBarItem:
893 case TransportMarkerBarItem:
894 case CdMarkerBarItem:
897 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
901 marker_context_menu (&event->button, item);
904 case TempoMarkerItem:
905 tm_marker_context_menu (&event->button, item);
908 case MeterMarkerItem:
909 tm_marker_context_menu (&event->button, item);
912 case CrossfadeViewItem:
913 popup_track_context_menu (1, event->button.time, item_type, false, where);
916 /* <CMT Additions> */
918 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
920 case ImageFrameTimeAxisItem:
921 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
924 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
926 case MarkerTimeAxisItem:
927 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
929 /* <CMT Additions> */
940 /* delete events get handled here */
942 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
945 case TempoMarkerItem:
946 remove_tempo_marker (item);
949 case MeterMarkerItem:
950 remove_meter_marker (item);
954 remove_marker (*item, event);
958 if (mouse_mode == MouseObject) {
959 remove_clicked_region ();
963 case GainControlPointItem:
964 if (mouse_mode == MouseGain) {
965 remove_gain_control_point (item, event);
969 case GainAutomationControlPointItem:
970 case PanAutomationControlPointItem:
971 case RedirectAutomationControlPointItem:
972 remove_control_point (item, event);
981 switch (event->button.button) {
985 /* see comments in button_press_handler */
986 case PlayheadCursorItem:
989 case GainAutomationLineItem:
990 case PanAutomationLineItem:
991 case RedirectAutomationLineItem:
992 case StartSelectionTrimItem:
993 case EndSelectionTrimItem:
997 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
998 snap_to (where, 0, true);
1000 mouse_add_new_marker (where);
1003 case CdMarkerBarItem:
1004 // if we get here then a dragged range wasn't done
1005 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1006 snap_to (where, 0, true);
1008 mouse_add_new_marker (where, true);
1012 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1015 mouse_add_new_tempo_event (where);
1019 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1027 switch (mouse_mode) {
1029 switch (item_type) {
1030 case AutomationTrackItem:
1031 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->add_automation_event
1045 // Gain only makes sense for audio regions
1047 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1051 switch (item_type) {
1053 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1057 case AutomationTrackItem:
1058 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1059 add_automation_event (item, event, where, event->button.y);
1069 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1070 if (scrubbing_direction == 0) {
1071 /* no drag, just a click */
1072 switch (item_type) {
1074 play_selected_region ();
1080 /* make sure we stop */
1081 session->request_transport_speed (0.0);
1095 switch (mouse_mode) {
1098 switch (item_type) {
1100 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1102 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1105 // Button2 click is unused
1118 // x_style_paste (where, 1.0);
1138 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1144 switch (item_type) {
1145 case GainControlPointItem:
1146 if (mouse_mode == MouseGain) {
1147 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1148 cp->set_visible (true);
1152 at_y = cp->get_y ();
1153 cp->item->i2w (at_x, at_y);
1157 fraction = 1.0 - (cp->get_y() / cp->line.height());
1159 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1160 show_verbose_canvas_cursor ();
1162 if (is_drawable() && !_scrubbing) {
1163 track_canvas.get_window()->set_cursor (*fader_cursor);
1168 case GainAutomationControlPointItem:
1169 case PanAutomationControlPointItem:
1170 case RedirectAutomationControlPointItem:
1171 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1172 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1173 cp->set_visible (true);
1177 at_y = cp->get_y ();
1178 cp->item->i2w (at_x, at_y);
1182 fraction = 1.0 - (cp->get_y() / cp->line.height());
1184 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1185 show_verbose_canvas_cursor ();
1187 if (is_drawable()) {
1188 track_canvas.get_window()->set_cursor (*fader_cursor);
1194 if (mouse_mode == MouseGain) {
1195 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1197 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1198 if (is_drawable()) {
1199 track_canvas.get_window()->set_cursor (*fader_cursor);
1204 case GainAutomationLineItem:
1205 case RedirectAutomationLineItem:
1206 case PanAutomationLineItem:
1207 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1209 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1211 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1213 if (is_drawable()) {
1214 track_canvas.get_window()->set_cursor (*fader_cursor);
1219 case RegionViewNameHighlight:
1220 if (is_drawable() && mouse_mode == MouseObject) {
1221 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1225 case StartSelectionTrimItem:
1226 case EndSelectionTrimItem:
1227 /* <CMT Additions> */
1228 case ImageFrameHandleStartItem:
1229 case ImageFrameHandleEndItem:
1230 case MarkerViewHandleStartItem:
1231 case MarkerViewHandleEndItem:
1232 /* </CMT Additions> */
1234 if (is_drawable()) {
1235 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1239 case PlayheadCursorItem:
1240 if (is_drawable()) {
1241 track_canvas.get_window()->set_cursor (*grabber_cursor);
1245 case RegionViewName:
1247 /* when the name is not an active item, the entire name highlight is for trimming */
1249 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1250 if (mouse_mode == MouseObject && is_drawable()) {
1251 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1257 case AutomationTrackItem:
1258 if (is_drawable()) {
1259 Gdk::Cursor *cursor;
1260 switch (mouse_mode) {
1262 cursor = selector_cursor;
1265 cursor = zoom_cursor;
1268 cursor = cross_hair_cursor;
1272 track_canvas.get_window()->set_cursor (*cursor);
1274 AutomationTimeAxisView* atv;
1275 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1276 clear_entered_track = false;
1277 set_entered_track (atv);
1283 case RangeMarkerBarItem:
1284 case TransportMarkerBarItem:
1285 case CdMarkerBarItem:
1288 if (is_drawable()) {
1289 time_canvas.get_window()->set_cursor (*timebar_cursor);
1294 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1297 entered_marker = marker;
1298 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1300 case MeterMarkerItem:
1301 case TempoMarkerItem:
1302 if (is_drawable()) {
1303 time_canvas.get_window()->set_cursor (*timebar_cursor);
1306 case FadeInHandleItem:
1307 case FadeOutHandleItem:
1308 if (mouse_mode == MouseObject) {
1309 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1311 rect->property_fill_color_rgba() = 0;
1312 rect->property_outline_pixels() = 1;
1321 /* second pass to handle entered track status in a comprehensible way.
1324 switch (item_type) {
1326 case GainAutomationLineItem:
1327 case RedirectAutomationLineItem:
1328 case PanAutomationLineItem:
1329 case GainControlPointItem:
1330 case GainAutomationControlPointItem:
1331 case PanAutomationControlPointItem:
1332 case RedirectAutomationControlPointItem:
1333 /* these do not affect the current entered track state */
1334 clear_entered_track = false;
1337 case AutomationTrackItem:
1338 /* handled above already */
1342 set_entered_track (0);
1350 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1359 switch (item_type) {
1360 case GainControlPointItem:
1361 case GainAutomationControlPointItem:
1362 case PanAutomationControlPointItem:
1363 case RedirectAutomationControlPointItem:
1364 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1365 if (cp->line.npoints() > 1) {
1366 if (!cp->selected) {
1367 cp->set_visible (false);
1371 if (is_drawable()) {
1372 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1375 hide_verbose_canvas_cursor ();
1378 case RegionViewNameHighlight:
1379 case StartSelectionTrimItem:
1380 case EndSelectionTrimItem:
1381 case PlayheadCursorItem:
1382 /* <CMT Additions> */
1383 case ImageFrameHandleStartItem:
1384 case ImageFrameHandleEndItem:
1385 case MarkerViewHandleStartItem:
1386 case MarkerViewHandleEndItem:
1387 /* </CMT Additions> */
1388 if (is_drawable()) {
1389 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1394 case GainAutomationLineItem:
1395 case RedirectAutomationLineItem:
1396 case PanAutomationLineItem:
1397 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1399 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1401 line->property_fill_color_rgba() = al->get_line_color();
1403 if (is_drawable()) {
1404 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1408 case RegionViewName:
1409 /* see enter_handler() for notes */
1410 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1411 if (is_drawable() && mouse_mode == MouseObject) {
1412 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1417 case RangeMarkerBarItem:
1418 case TransportMarkerBarItem:
1419 case CdMarkerBarItem:
1423 if (is_drawable()) {
1424 time_canvas.get_window()->set_cursor (*timebar_cursor);
1429 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1433 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1434 location_flags_changed (loc, this);
1437 case MeterMarkerItem:
1438 case TempoMarkerItem:
1440 if (is_drawable()) {
1441 time_canvas.get_window()->set_cursor (*timebar_cursor);
1446 case FadeInHandleItem:
1447 case FadeOutHandleItem:
1448 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1450 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1452 rect->property_fill_color_rgba() = rv->get_fill_color();
1453 rect->property_outline_pixels() = 0;
1458 case AutomationTrackItem:
1459 if (is_drawable()) {
1460 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1461 clear_entered_track = true;
1462 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1474 Editor::left_automation_track ()
1476 if (clear_entered_track) {
1477 set_entered_track (0);
1478 clear_entered_track = false;
1484 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1486 if (event->motion.is_hint) {
1489 /* We call this so that MOTION_NOTIFY events continue to be
1490 delivered to the canvas. We need to do this because we set
1491 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1492 the density of the events, at the expense of a round-trip
1493 to the server. Given that this will mostly occur on cases
1494 where DISPLAY = :0.0, and given the cost of what the motion
1495 event might do, its a good tradeoff.
1498 track_canvas.get_pointer (x, y);
1501 if (current_stepping_trackview) {
1502 /* don't keep the persistent stepped trackview if the mouse moves */
1503 current_stepping_trackview = 0;
1504 step_timeout.disconnect ();
1507 if (session && session->actively_recording()) {
1508 /* Sorry. no dragging stuff around while we record */
1512 drag_info.item_type = item_type;
1513 drag_info.last_pointer_x = drag_info.current_pointer_x;
1514 drag_info.last_pointer_y = drag_info.current_pointer_y;
1515 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1516 &drag_info.current_pointer_y);
1518 switch (mouse_mode) {
1524 if (scrubbing_direction == 0) {
1526 session->request_locate (drag_info.current_pointer_frame, false);
1527 session->request_transport_speed (0.1);
1528 scrubbing_direction = 1;
1532 if (last_scrub_x > drag_info.current_pointer_x) {
1534 /* pointer moved to the left */
1536 if (scrubbing_direction > 0) {
1538 /* we reversed direction to go backwards */
1541 scrub_reverse_distance += (int) (last_scrub_x - drag_info.current_pointer_x);
1545 /* still moving to the left (backwards) */
1547 scrub_reversals = 0;
1548 scrub_reverse_distance = 0;
1550 delta = 0.01 * (last_scrub_x - drag_info.current_pointer_x);
1551 session->request_transport_speed (session->transport_speed() - delta);
1555 /* pointer moved to the right */
1557 if (scrubbing_direction < 0) {
1558 /* we reversed direction to go forward */
1561 scrub_reverse_distance += (int) (drag_info.current_pointer_x - last_scrub_x);
1564 /* still moving to the right */
1566 scrub_reversals = 0;
1567 scrub_reverse_distance = 0;
1569 delta = 0.01 * (drag_info.current_pointer_x - last_scrub_x);
1570 session->request_transport_speed (session->transport_speed() + delta);
1574 /* if there have been more than 2 opposite motion moves detected, or one that moves
1575 back more than 10 pixels, reverse direction
1578 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1580 if (scrubbing_direction > 0) {
1581 /* was forwards, go backwards */
1582 session->request_transport_speed (-0.1);
1583 scrubbing_direction = -1;
1585 /* was backwards, go forwards */
1586 session->request_transport_speed (0.1);
1587 scrubbing_direction = 1;
1590 scrub_reverse_distance = 0;
1591 scrub_reversals = 0;
1595 last_scrub_x = drag_info.current_pointer_x;
1602 if (!from_autoscroll && drag_info.item) {
1603 /* item != 0 is the best test i can think of for dragging.
1605 if (!drag_info.move_threshold_passed) {
1607 bool x_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1608 bool y_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1610 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1612 // and change the initial grab loc/frame if this drag info wants us to
1614 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1615 drag_info.grab_frame = drag_info.current_pointer_frame;
1616 drag_info.grab_x = drag_info.current_pointer_x;
1617 drag_info.grab_y = drag_info.current_pointer_y;
1618 drag_info.last_pointer_frame = drag_info.grab_frame;
1619 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1624 switch (item_type) {
1625 case PlayheadCursorItem:
1627 case GainControlPointItem:
1628 case RedirectAutomationControlPointItem:
1629 case GainAutomationControlPointItem:
1630 case PanAutomationControlPointItem:
1631 case TempoMarkerItem:
1632 case MeterMarkerItem:
1633 case RegionViewNameHighlight:
1634 case StartSelectionTrimItem:
1635 case EndSelectionTrimItem:
1638 case RedirectAutomationLineItem:
1639 case GainAutomationLineItem:
1640 case PanAutomationLineItem:
1641 case FadeInHandleItem:
1642 case FadeOutHandleItem:
1643 /* <CMT Additions> */
1644 case ImageFrameHandleStartItem:
1645 case ImageFrameHandleEndItem:
1646 case MarkerViewHandleStartItem:
1647 case MarkerViewHandleEndItem:
1648 /* </CMT Additions> */
1649 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1650 (event->motion.state & Gdk::BUTTON2_MASK))) {
1651 if (!from_autoscroll) {
1652 maybe_autoscroll (event);
1654 (this->*(drag_info.motion_callback)) (item, event);
1663 switch (mouse_mode) {
1668 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1669 (event->motion.state & GDK_BUTTON2_MASK))) {
1670 if (!from_autoscroll) {
1671 maybe_autoscroll (event);
1673 (this->*(drag_info.motion_callback)) (item, event);
1684 track_canvas_motion (event);
1685 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1693 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1695 if (drag_info.item == 0) {
1696 fatal << _("programming error: start_grab called without drag item") << endmsg;
1702 cursor = grabber_cursor;
1705 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1707 if (event->button.button == 2) {
1708 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
1709 drag_info.y_constrained = true;
1710 drag_info.x_constrained = false;
1712 drag_info.y_constrained = false;
1713 drag_info.x_constrained = true;
1716 drag_info.x_constrained = false;
1717 drag_info.y_constrained = false;
1720 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1721 drag_info.last_pointer_frame = drag_info.grab_frame;
1722 drag_info.current_pointer_frame = drag_info.grab_frame;
1723 drag_info.current_pointer_x = drag_info.grab_x;
1724 drag_info.current_pointer_y = drag_info.grab_y;
1725 drag_info.last_pointer_x = drag_info.current_pointer_x;
1726 drag_info.last_pointer_y = drag_info.current_pointer_y;
1727 drag_info.cumulative_x_drag = 0;
1728 drag_info.cumulative_y_drag = 0;
1729 drag_info.first_move = true;
1730 drag_info.move_threshold_passed = false;
1731 drag_info.want_move_threshold = false;
1732 drag_info.pointer_frame_offset = 0;
1733 drag_info.brushing = false;
1734 drag_info.copied_location = 0;
1736 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1738 event->button.time);
1740 if (session && session->transport_rolling()) {
1741 drag_info.was_rolling = true;
1743 drag_info.was_rolling = false;
1746 switch (snap_type) {
1747 case SnapToRegionStart:
1748 case SnapToRegionEnd:
1749 case SnapToRegionSync:
1750 case SnapToRegionBoundary:
1751 build_region_boundary_cache ();
1759 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1761 drag_info.item->ungrab (0);
1762 drag_info.item = new_item;
1765 cursor = grabber_cursor;
1768 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1772 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1774 bool did_drag = false;
1776 stop_canvas_autoscroll ();
1778 if (drag_info.item == 0) {
1782 drag_info.item->ungrab (event->button.time);
1784 if (drag_info.finished_callback) {
1785 drag_info.last_pointer_x = drag_info.current_pointer_x;
1786 drag_info.last_pointer_y = drag_info.current_pointer_y;
1787 (this->*(drag_info.finished_callback)) (item, event);
1790 did_drag = !drag_info.first_move;
1792 hide_verbose_canvas_cursor();
1795 drag_info.copy = false;
1796 drag_info.motion_callback = 0;
1797 drag_info.finished_callback = 0;
1798 drag_info.last_trackview = 0;
1799 drag_info.last_frame_position = 0;
1800 drag_info.grab_frame = 0;
1801 drag_info.last_pointer_frame = 0;
1802 drag_info.current_pointer_frame = 0;
1803 drag_info.brushing = false;
1805 if (drag_info.copied_location) {
1806 delete drag_info.copied_location;
1807 drag_info.copied_location = 0;
1814 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1816 drag_info.item = item;
1817 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1818 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1822 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1823 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1827 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1829 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1833 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1835 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1837 nframes_t fade_length;
1839 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1840 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1846 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1850 if (pos < (arv->region()->position() + 64)) {
1851 fade_length = 64; // this should be a minimum defined somewhere
1852 } else if (pos > arv->region()->last_frame()) {
1853 fade_length = arv->region()->length();
1855 fade_length = pos - arv->region()->position();
1857 /* mapover the region selection */
1859 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1861 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1867 tmp->reset_fade_in_shape_width (fade_length);
1870 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1872 drag_info.first_move = false;
1876 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1878 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1880 nframes_t fade_length;
1882 if (drag_info.first_move) return;
1884 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1885 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1890 if (pos < (arv->region()->position() + 64)) {
1891 fade_length = 64; // this should be a minimum defined somewhere
1892 } else if (pos > arv->region()->last_frame()) {
1893 fade_length = arv->region()->length();
1895 fade_length = pos - arv->region()->position();
1898 begin_reversible_command (_("change fade in length"));
1900 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1902 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1908 AutomationList& alist = tmp->audio_region()->fade_in();
1909 XMLNode &before = alist.get_state();
1911 tmp->audio_region()->set_fade_in_length (fade_length);
1913 XMLNode &after = alist.get_state();
1914 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1917 commit_reversible_command ();
1921 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1923 drag_info.item = item;
1924 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1925 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1929 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1930 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1934 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1936 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
1940 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1942 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1944 nframes_t fade_length;
1946 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1947 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1952 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1956 if (pos > (arv->region()->last_frame() - 64)) {
1957 fade_length = 64; // this should really be a minimum fade defined somewhere
1959 else if (pos < arv->region()->position()) {
1960 fade_length = arv->region()->length();
1963 fade_length = arv->region()->last_frame() - pos;
1966 /* mapover the region selection */
1968 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1970 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1976 tmp->reset_fade_out_shape_width (fade_length);
1979 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1981 drag_info.first_move = false;
1985 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1987 if (drag_info.first_move) return;
1989 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1991 nframes_t fade_length;
1993 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1994 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2000 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2004 if (pos > (arv->region()->last_frame() - 64)) {
2005 fade_length = 64; // this should really be a minimum fade defined somewhere
2007 else if (pos < arv->region()->position()) {
2008 fade_length = arv->region()->length();
2011 fade_length = arv->region()->last_frame() - pos;
2014 begin_reversible_command (_("change fade out length"));
2016 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2018 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2024 AutomationList& alist = tmp->audio_region()->fade_out();
2025 XMLNode &before = alist.get_state();
2027 tmp->audio_region()->set_fade_out_length (fade_length);
2029 XMLNode &after = alist.get_state();
2030 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
2033 commit_reversible_command ();
2037 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2039 drag_info.item = item;
2040 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2041 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2045 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2046 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2050 Cursor* cursor = (Cursor *) drag_info.data;
2052 if (cursor == playhead_cursor) {
2053 _dragging_playhead = true;
2055 if (session && drag_info.was_rolling) {
2056 session->request_stop ();
2059 if (session && session->is_auditioning()) {
2060 session->cancel_audition ();
2064 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2066 show_verbose_time_cursor (cursor->current_frame, 10);
2070 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2072 Cursor* cursor = (Cursor *) drag_info.data;
2073 nframes_t adjusted_frame;
2075 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2076 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2082 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2083 if (cursor == playhead_cursor) {
2084 snap_to (adjusted_frame);
2088 if (adjusted_frame == drag_info.last_pointer_frame) return;
2090 cursor->set_position (adjusted_frame);
2092 UpdateAllTransportClocks (cursor->current_frame);
2094 show_verbose_time_cursor (cursor->current_frame, 10);
2096 drag_info.last_pointer_frame = adjusted_frame;
2097 drag_info.first_move = false;
2101 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2103 if (drag_info.first_move) return;
2105 cursor_drag_motion_callback (item, event);
2107 _dragging_playhead = false;
2109 if (item == &playhead_cursor->canvas_item) {
2111 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2117 Editor::update_marker_drag_item (Location *location)
2119 double x1 = frame_to_pixel (location->start());
2120 double x2 = frame_to_pixel (location->end());
2122 if (location->is_mark()) {
2123 marker_drag_line_points.front().set_x(x1);
2124 marker_drag_line_points.back().set_x(x1);
2125 marker_drag_line->property_points() = marker_drag_line_points;
2128 range_marker_drag_rect->property_x1() = x1;
2129 range_marker_drag_rect->property_x2() = x2;
2134 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2138 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2139 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2145 Location *location = find_location_from_marker (marker, is_start);
2147 drag_info.item = item;
2148 drag_info.data = marker;
2149 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2150 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2154 _dragging_edit_point = true;
2156 drag_info.copied_location = new Location (*location);
2157 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2159 update_marker_drag_item (location);
2161 if (location->is_mark()) {
2162 // marker_drag_line->show();
2163 // marker_drag_line->raise_to_top();
2165 range_marker_drag_rect->show();
2166 range_marker_drag_rect->raise_to_top();
2170 show_verbose_time_cursor (location->start(), 10);
2172 show_verbose_time_cursor (location->end(), 10);
2175 Selection::Operation op = Keyboard::selection_type (event->button.state);
2178 case Selection::Toggle:
2179 selection->toggle (marker);
2181 case Selection::Set:
2182 selection->set (marker);
2184 case Selection::Extend:
2185 selection->add (marker);
2187 case Selection::Add:
2188 selection->add (marker);
2194 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2197 Marker* marker = (Marker *) drag_info.data;
2198 Location *real_location;
2199 Location *copy_location;
2201 bool move_both = false;
2204 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2205 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2210 nframes_t next = newframe;
2212 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2213 snap_to (newframe, 0, true);
2216 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2220 /* call this to find out if its the start or end */
2222 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2226 if (real_location->locked()) {
2230 /* use the copy that we're "dragging" around */
2232 copy_location = drag_info.copied_location;
2234 f_delta = copy_location->end() - copy_location->start();
2236 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2240 if (copy_location->is_mark()) {
2243 copy_location->set_start (newframe);
2247 if (is_start) { // start-of-range marker
2250 copy_location->set_start (newframe);
2251 copy_location->set_end (newframe + f_delta);
2252 } else if (newframe < copy_location->end()) {
2253 copy_location->set_start (newframe);
2255 snap_to (next, 1, true);
2256 copy_location->set_end (next);
2257 copy_location->set_start (newframe);
2260 } else { // end marker
2263 copy_location->set_end (newframe);
2264 copy_location->set_start (newframe - f_delta);
2265 } else if (newframe > copy_location->start()) {
2266 copy_location->set_end (newframe);
2268 } else if (newframe > 0) {
2269 snap_to (next, -1, true);
2270 copy_location->set_start (next);
2271 copy_location->set_end (newframe);
2276 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2277 drag_info.first_move = false;
2279 update_marker_drag_item (copy_location);
2281 LocationMarkers* lm = find_location_markers (real_location);
2282 lm->set_position (copy_location->start(), copy_location->end());
2283 edit_point_clock.set (copy_location->start());
2285 show_verbose_time_cursor (newframe, 10);
2289 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2291 if (drag_info.first_move) {
2292 marker_drag_motion_callback (item, event);
2296 _dragging_edit_point = false;
2298 Marker* marker = (Marker *) drag_info.data;
2301 begin_reversible_command ( _("move marker") );
2302 XMLNode &before = session->locations()->get_state();
2304 Location * location = find_location_from_marker (marker, is_start);
2308 if (location->locked()) {
2312 if (location->is_mark()) {
2313 location->set_start (drag_info.copied_location->start());
2315 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2319 XMLNode &after = session->locations()->get_state();
2320 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2321 commit_reversible_command ();
2323 marker_drag_line->hide();
2324 range_marker_drag_rect->hide();
2328 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2331 MeterMarker* meter_marker;
2333 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2334 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2338 meter_marker = dynamic_cast<MeterMarker*> (marker);
2340 MetricSection& section (meter_marker->meter());
2342 if (!section.movable()) {
2346 drag_info.item = item;
2347 drag_info.copy = false;
2348 drag_info.data = marker;
2349 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2350 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2354 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2356 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2360 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2363 MeterMarker* meter_marker;
2365 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2366 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2370 meter_marker = dynamic_cast<MeterMarker*> (marker);
2372 // create a dummy marker for visual representation of moving the copy.
2373 // The actual copying is not done before we reach the finish callback.
2375 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2376 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2377 *new MeterSection(meter_marker->meter()));
2379 drag_info.item = &new_marker->the_item();
2380 drag_info.copy = true;
2381 drag_info.data = new_marker;
2382 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2383 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2387 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2389 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2393 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2395 MeterMarker* marker = (MeterMarker *) drag_info.data;
2396 nframes_t adjusted_frame;
2398 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2399 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2405 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2406 snap_to (adjusted_frame);
2409 if (adjusted_frame == drag_info.last_pointer_frame) return;
2411 marker->set_position (adjusted_frame);
2414 drag_info.last_pointer_frame = adjusted_frame;
2415 drag_info.first_move = false;
2417 show_verbose_time_cursor (adjusted_frame, 10);
2421 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2423 if (drag_info.first_move) return;
2425 meter_marker_drag_motion_callback (drag_info.item, event);
2427 MeterMarker* marker = (MeterMarker *) drag_info.data;
2430 TempoMap& map (session->tempo_map());
2431 map.bbt_time (drag_info.last_pointer_frame, when);
2433 if (drag_info.copy == true) {
2434 begin_reversible_command (_("copy meter mark"));
2435 XMLNode &before = map.get_state();
2436 map.add_meter (marker->meter(), when);
2437 XMLNode &after = map.get_state();
2438 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2439 commit_reversible_command ();
2441 // delete the dummy marker we used for visual representation of copying.
2442 // a new visual marker will show up automatically.
2445 begin_reversible_command (_("move meter mark"));
2446 XMLNode &before = map.get_state();
2447 map.move_meter (marker->meter(), when);
2448 XMLNode &after = map.get_state();
2449 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2450 commit_reversible_command ();
2455 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2458 TempoMarker* tempo_marker;
2460 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2461 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2465 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2466 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2470 MetricSection& section (tempo_marker->tempo());
2472 if (!section.movable()) {
2476 drag_info.item = item;
2477 drag_info.copy = false;
2478 drag_info.data = marker;
2479 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2480 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2484 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2485 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2489 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2492 TempoMarker* tempo_marker;
2494 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2495 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2499 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2500 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2504 // create a dummy marker for visual representation of moving the copy.
2505 // The actual copying is not done before we reach the finish callback.
2507 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2508 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2509 *new TempoSection(tempo_marker->tempo()));
2511 drag_info.item = &new_marker->the_item();
2512 drag_info.copy = true;
2513 drag_info.data = new_marker;
2514 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2515 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2519 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2521 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2525 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2527 TempoMarker* marker = (TempoMarker *) drag_info.data;
2528 nframes_t adjusted_frame;
2530 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2531 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2537 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2538 snap_to (adjusted_frame);
2541 if (adjusted_frame == drag_info.last_pointer_frame) return;
2543 /* OK, we've moved far enough to make it worth actually move the thing. */
2545 marker->set_position (adjusted_frame);
2547 show_verbose_time_cursor (adjusted_frame, 10);
2549 drag_info.last_pointer_frame = adjusted_frame;
2550 drag_info.first_move = false;
2554 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2556 if (drag_info.first_move) return;
2558 tempo_marker_drag_motion_callback (drag_info.item, event);
2560 TempoMarker* marker = (TempoMarker *) drag_info.data;
2563 TempoMap& map (session->tempo_map());
2564 map.bbt_time (drag_info.last_pointer_frame, when);
2566 if (drag_info.copy == true) {
2567 begin_reversible_command (_("copy tempo mark"));
2568 XMLNode &before = map.get_state();
2569 map.add_tempo (marker->tempo(), when);
2570 XMLNode &after = map.get_state();
2571 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2572 commit_reversible_command ();
2574 // delete the dummy marker we used for visual representation of copying.
2575 // a new visual marker will show up automatically.
2578 begin_reversible_command (_("move tempo mark"));
2579 XMLNode &before = map.get_state();
2580 map.move_tempo (marker->tempo(), when);
2581 XMLNode &after = map.get_state();
2582 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2583 commit_reversible_command ();
2588 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2590 ControlPoint* control_point;
2592 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2593 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2597 // We shouldn't remove the first or last gain point
2598 if (control_point->line.is_last_point(*control_point) ||
2599 control_point->line.is_first_point(*control_point)) {
2603 control_point->line.remove_point (*control_point);
2607 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2609 ControlPoint* control_point;
2611 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2612 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2616 control_point->line.remove_point (*control_point);
2620 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2622 ControlPoint* control_point;
2624 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2625 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2629 drag_info.item = item;
2630 drag_info.data = control_point;
2631 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2632 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2634 start_grab (event, fader_cursor);
2636 // start the grab at the center of the control point so
2637 // the point doesn't 'jump' to the mouse after the first drag
2638 drag_info.grab_x = control_point->get_x();
2639 drag_info.grab_y = control_point->get_y();
2640 control_point->line.parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
2641 track_canvas.w2c(drag_info.grab_x, drag_info.grab_y,
2642 drag_info.grab_x, drag_info.grab_y);
2644 drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
2646 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2648 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2649 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2650 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2652 show_verbose_canvas_cursor ();
2656 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2658 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2660 double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
2661 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2663 if (event->button.state & Keyboard::SecondaryModifier) {
2668 double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
2669 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2671 // calculate zero crossing point. back off by .01 to stay on the
2672 // positive side of zero
2674 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line.height() - .01;
2675 cp->line.parent_group().i2w(_unused, zero_gain_y);
2677 // make sure we hit zero when passing through
2678 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2679 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2683 if (drag_info.x_constrained) {
2684 cx = drag_info.grab_x;
2686 if (drag_info.y_constrained) {
2687 cy = drag_info.grab_y;
2690 drag_info.cumulative_x_drag = cx - drag_info.grab_x;
2691 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2693 cp->line.parent_group().w2i (cx, cy);
2697 cy = min ((double) cp->line.height(), cy);
2699 //translate cx to frames
2700 nframes_t cx_frames = unit_to_frame (cx);
2702 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2703 snap_to (cx_frames);
2706 float fraction = 1.0 - (cy / cp->line.height());
2710 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2716 cp->line.point_drag (*cp, cx_frames , fraction, push);
2718 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2720 drag_info.first_move = false;
2724 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2726 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2728 if (drag_info.first_move) {
2732 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2733 reset_point_selection ();
2737 control_point_drag_motion_callback (item, event);
2739 cp->line.end_drag (cp);
2743 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2745 switch (mouse_mode) {
2747 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2748 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2756 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2760 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2761 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2765 start_line_grab (al, event);
2769 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2773 nframes_t frame_within_region;
2775 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2779 cx = event->button.x;
2780 cy = event->button.y;
2781 line->parent_group().w2i (cx, cy);
2782 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2784 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2785 current_line_drag_info.after)) {
2786 /* no adjacent points */
2790 drag_info.item = &line->grab_item();
2791 drag_info.data = line;
2792 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2793 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2795 start_grab (event, fader_cursor);
2797 double fraction = 1.0 - (cy / line->height());
2799 line->start_drag (0, drag_info.grab_frame, fraction);
2801 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2802 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2803 show_verbose_canvas_cursor ();
2807 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2809 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2811 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2813 if (event->button.state & Keyboard::SecondaryModifier) {
2817 double cx = drag_info.current_pointer_x;
2818 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2820 // calculate zero crossing point. back off by .01 to stay on the
2821 // positive side of zero
2823 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
2824 line->parent_group().i2w(_unused, zero_gain_y);
2826 // make sure we hit zero when passing through
2827 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2828 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2832 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2834 line->parent_group().w2i (cx, cy);
2837 cy = min ((double) line->height(), cy);
2840 fraction = 1.0 - (cy / line->height());
2844 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2850 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2852 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2856 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2858 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2859 line_drag_motion_callback (item, event);
2864 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2866 if (selection->regions.empty() || clicked_regionview == 0) {
2870 drag_info.copy = false;
2871 drag_info.item = item;
2872 drag_info.data = clicked_regionview;
2874 if (Config->get_edit_mode() == Splice) {
2875 drag_info.motion_callback = &Editor::region_drag_splice_motion_callback;
2876 drag_info.finished_callback = &Editor::region_drag_splice_finished_callback;
2878 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2879 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2885 TimeAxisView* tvp = clicked_trackview;
2886 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2888 if (tv && tv->is_audio_track()) {
2889 speed = tv->get_diskstream()->speed();
2892 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2893 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2894 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2895 // we want a move threshold
2896 drag_info.want_move_threshold = true;
2898 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2900 begin_reversible_command (_("move region(s)"));
2904 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2906 if (selection->regions.empty() || clicked_regionview == 0) {
2910 drag_info.copy = true;
2911 drag_info.item = item;
2912 drag_info.data = clicked_regionview;
2916 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2917 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
2920 if (atv && atv->is_audio_track()) {
2921 speed = atv->get_diskstream()->speed();
2924 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2925 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2926 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2927 // we want a move threshold
2928 drag_info.want_move_threshold = true;
2929 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2930 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2931 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2935 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2937 if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) {
2941 drag_info.copy = false;
2942 drag_info.item = item;
2943 drag_info.data = clicked_regionview;
2944 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2945 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2950 TimeAxisView* tvp = clicked_trackview;
2951 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2953 if (tv && tv->is_audio_track()) {
2954 speed = tv->get_diskstream()->speed();
2957 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2958 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2959 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2960 // we want a move threshold
2961 drag_info.want_move_threshold = true;
2962 drag_info.brushing = true;
2964 begin_reversible_command (_("Drag region brush"));
2968 Editor::possibly_copy_regions_during_grab (GdkEvent* event)
2970 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2972 drag_info.want_move_threshold = false; // don't copy again
2974 /* duplicate the region(s) */
2976 vector<RegionView*> new_regionviews;
2978 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2981 AudioRegionView* arv;
2986 if ((arv = dynamic_cast<AudioRegionView*>(rv)) == 0) {
2987 /* XXX handle MIDI here */
2991 nrv = new AudioRegionView (*arv);
2992 nrv->get_canvas_group()->show ();
2994 new_regionviews.push_back (nrv);
2997 if (new_regionviews.empty()) {
3001 /* reset selection to new regionviews */
3003 selection->set (new_regionviews);
3005 /* reset drag_info data to reflect the fact that we are dragging the copies */
3007 drag_info.data = new_regionviews.front();
3009 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3014 Editor::check_region_drag_possible (AudioTimeAxisView** tv)
3016 /* Which trackview is this ? */
3018 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
3019 (*tv) = dynamic_cast<AudioTimeAxisView*>(tvp);
3021 /* The region motion is only processed if the pointer is over
3025 if (!(*tv) || !(*tv)->is_audio_track()) {
3026 /* To make sure we hide the verbose canvas cursor when the mouse is
3027 not held over and audiotrack.
3029 hide_verbose_canvas_cursor ();
3036 struct RegionSelectionByPosition {
3037 bool operator() (RegionView*a, RegionView* b) {
3038 return a->region()->position () < b->region()->position();
3043 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3045 AudioTimeAxisView* tv;
3047 if (!check_region_drag_possible (&tv)) {
3051 if (!drag_info.move_threshold_passed) {
3057 if (drag_info.current_pointer_x - drag_info.grab_x > 0) {
3063 RegionSelection copy (selection->regions);
3065 RegionSelectionByPosition cmp;
3068 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
3070 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(*i)->get_time_axis_view());
3076 boost::shared_ptr<Playlist> playlist;
3078 if ((playlist = atv->playlist()) == 0) {
3082 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
3087 if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) {
3091 if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) {
3097 playlist->shuffle ((*i)->region(), dir);
3099 drag_info.grab_x = drag_info.current_pointer_x;
3104 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3109 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3113 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
3114 nframes_t pending_region_position = 0;
3115 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
3116 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
3117 bool clamp_y_axis = false;
3118 vector<int32_t> height_list(512) ;
3119 vector<int32_t>::iterator j;
3120 AudioTimeAxisView* tv;
3122 possibly_copy_regions_during_grab (event);
3124 if (!check_region_drag_possible (&tv)) {
3128 original_pointer_order = drag_info.last_trackview->order;
3130 /************************************************************
3132 ************************************************************/
3134 if (drag_info.brushing) {
3135 clamp_y_axis = true;
3140 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
3142 int32_t children = 0, numtracks = 0;
3143 // XXX hard coding track limit, oh my, so very very bad
3144 bitset <1024> tracks (0x00);
3145 /* get a bitmask representing the visible tracks */
3147 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3148 TimeAxisView *tracklist_timeview;
3149 tracklist_timeview = (*i);
3150 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
3151 list<TimeAxisView*> children_list;
3153 /* zeroes are audio tracks. ones are other types. */
3155 if (!atv2->hidden()) {
3157 if (visible_y_high < atv2->order) {
3158 visible_y_high = atv2->order;
3160 if (visible_y_low > atv2->order) {
3161 visible_y_low = atv2->order;
3164 if (!atv2->is_audio_track()) {
3165 tracks = tracks |= (0x01 << atv2->order);
3168 height_list[atv2->order] = (*i)->height;
3170 if ((children_list = atv2->get_child_list()).size() > 0) {
3171 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
3172 tracks = tracks |= (0x01 << (atv2->order + children));
3173 height_list[atv2->order + children] = (*j)->height;
3181 /* find the actual span according to the canvas */
3183 canvas_pointer_y_span = pointer_y_span;
3184 if (drag_info.last_trackview->order >= tv->order) {
3186 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
3187 if (height_list[y] == 0 ) {
3188 canvas_pointer_y_span--;
3193 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
3194 if ( height_list[y] == 0 ) {
3195 canvas_pointer_y_span++;
3200 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3201 RegionView* rv2 = (*i);
3202 double ix1, ix2, iy1, iy2;
3205 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3206 rv2->get_canvas_group()->i2w (ix1, iy1);
3207 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3208 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3210 if (atv2->order != original_pointer_order) {
3211 /* this isn't the pointer track */
3213 if (canvas_pointer_y_span > 0) {
3215 /* moving up the canvas */
3216 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
3218 int32_t visible_tracks = 0;
3219 while (visible_tracks < canvas_pointer_y_span ) {
3222 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3223 /* we're passing through a hidden track */
3228 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3229 clamp_y_axis = true;
3233 clamp_y_axis = true;
3236 } else if (canvas_pointer_y_span < 0) {
3238 /*moving down the canvas*/
3240 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3243 int32_t visible_tracks = 0;
3245 while (visible_tracks > canvas_pointer_y_span ) {
3248 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3252 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3253 clamp_y_axis = true;
3258 clamp_y_axis = true;
3264 /* this is the pointer's track */
3265 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3266 clamp_y_axis = true;
3267 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3268 clamp_y_axis = true;
3276 } else if (drag_info.last_trackview == tv) {
3277 clamp_y_axis = true;
3281 if (!clamp_y_axis) {
3282 drag_info.last_trackview = tv;
3285 /************************************************************
3287 ************************************************************/
3289 /* compute the amount of pointer motion in frames, and where
3290 the region would be if we moved it by that much.
3293 if (drag_info.move_threshold_passed) {
3295 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3297 nframes_t sync_frame;
3298 nframes_t sync_offset;
3301 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3303 sync_offset = rv->region()->sync_offset (sync_dir);
3305 /* we don't handle a sync point that lies before zero.
3307 if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) {
3308 sync_frame = pending_region_position + (sync_dir*sync_offset);
3310 /* we snap if the snap modifier is not enabled.
3313 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3314 snap_to (sync_frame);
3317 pending_region_position = rv->region()->adjust_to_sync (sync_frame);
3320 pending_region_position = drag_info.last_frame_position;
3324 pending_region_position = 0;
3327 if (pending_region_position > max_frames - rv->region()->length()) {
3328 pending_region_position = drag_info.last_frame_position;
3331 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3333 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3335 /* now compute the canvas unit distance we need to move the regionview
3336 to make it appear at the new location.
3339 if (pending_region_position > drag_info.last_frame_position) {
3340 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3342 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3345 drag_info.last_frame_position = pending_region_position;
3352 /* threshold not passed */
3357 /*************************************************************
3359 ************************************************************/
3361 if (x_delta == 0 && (pointer_y_span == 0)) {
3362 /* haven't reached next snap point, and we're not switching
3363 trackviews. nothing to do.
3370 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3372 RegionView* rv2 = (*i);
3374 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3376 double ix1, ix2, iy1, iy2;
3377 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3378 rv2->get_canvas_group()->i2w (ix1, iy1);
3387 /*************************************************************
3389 ************************************************************/
3393 if (drag_info.first_move) {
3394 if (drag_info.move_threshold_passed) {
3405 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3406 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3408 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3410 RegionView* rv = (*i);
3411 double ix1, ix2, iy1, iy2;
3412 int32_t temp_pointer_y_span = pointer_y_span;
3414 /* get item BBox, which will be relative to parent. so we have
3415 to query on a child, then convert to world coordinates using
3419 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3420 rv->get_canvas_group()->i2w (ix1, iy1);
3421 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3422 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3423 AudioTimeAxisView* temp_atv;
3425 if ((pointer_y_span != 0) && !clamp_y_axis) {
3428 for (j = height_list.begin(); j!= height_list.end(); j++) {
3429 if (x == canvas_atv->order) {
3430 /* we found the track the region is on */
3431 if (x != original_pointer_order) {
3432 /*this isn't from the same track we're dragging from */
3433 temp_pointer_y_span = canvas_pointer_y_span;
3435 while (temp_pointer_y_span > 0) {
3436 /* we're moving up canvas-wise,
3437 so we need to find the next track height
3439 if (j != height_list.begin()) {
3442 if (x != original_pointer_order) {
3443 /* we're not from the dragged track, so ignore hidden tracks. */
3445 temp_pointer_y_span++;
3449 temp_pointer_y_span--;
3451 while (temp_pointer_y_span < 0) {
3453 if (x != original_pointer_order) {
3455 temp_pointer_y_span--;
3459 if (j != height_list.end()) {
3462 temp_pointer_y_span++;
3464 /* find out where we'll be when we move and set height accordingly */
3466 tvp2 = trackview_by_y_position (iy1 + y_delta);
3467 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3468 rv->set_height (temp_atv->height);
3470 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3471 personally, i think this can confuse things, but never mind.
3474 //const GdkColor& col (temp_atv->view->get_region_color());
3475 //rv->set_color (const_cast<GdkColor&>(col));
3482 /* prevent the regionview from being moved to before
3483 the zero position on the canvas.
3488 if (-x_delta > ix1) {
3491 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3492 x_delta = max_frames - rv->region()->last_frame();
3496 if (drag_info.first_move) {
3498 /* hide any dependent views */
3500 rv->get_time_axis_view().hide_dependent_views (*rv);
3502 /* this is subtle. raising the regionview itself won't help,
3503 because raise_to_top() just puts the item on the top of
3504 its parent's stack. so, we need to put the trackview canvas_display group
3505 on the top, since its parent is the whole canvas.
3508 rv->get_canvas_group()->raise_to_top();
3509 rv->get_time_axis_view().canvas_display->raise_to_top();
3510 cursor_group->raise_to_top();
3511 rv->fake_set_opaque (true);
3514 if (drag_info.brushing) {
3515 mouse_brush_insert_region (rv, pending_region_position);
3517 rv->move (x_delta, y_delta);
3520 } /* foreach region */
3524 if (drag_info.first_move && drag_info.move_threshold_passed) {
3525 cursor_group->raise_to_top();
3526 drag_info.first_move = false;
3529 if (x_delta != 0 && !drag_info.brushing) {
3530 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3535 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3538 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3539 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3540 bool nocommit = true;
3542 RouteTimeAxisView* atv;
3543 bool regionview_y_movement;
3544 bool regionview_x_movement;
3545 vector<RegionView*> copies;
3547 /* first_move is set to false if the regionview has been moved in the
3551 if (drag_info.first_move) {
3558 /* The regionview has been moved at some stage during the grab so we need
3559 to account for any mouse movement between this event and the last one.
3562 region_drag_motion_callback (item, event);
3564 if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
3565 selection->set (pre_drag_region_selection);
3566 pre_drag_region_selection.clear ();
3569 if (drag_info.brushing) {
3570 /* all changes were made during motion event handlers */
3572 if (drag_info.copy) {
3573 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3574 copies.push_back (*i);
3581 /* adjust for track speed */
3584 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3585 if (atv && atv->get_diskstream()) {
3586 speed = atv->get_diskstream()->speed();
3589 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3590 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3592 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3593 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3597 if (drag_info.copy) {
3598 if (drag_info.x_constrained) {
3599 op_string = _("fixed time region copy");
3601 op_string = _("region copy");
3604 if (drag_info.x_constrained) {
3605 op_string = _("fixed time region drag");
3607 op_string = _("region drag");
3611 begin_reversible_command (op_string);
3613 if (regionview_y_movement) {
3615 /* moved to a different audio track. */
3617 vector<RegionView*> new_selection;
3619 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3621 RegionView* rv = (*i);
3623 double ix1, ix2, iy1, iy2;
3625 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3626 rv->get_canvas_group()->i2w (ix1, iy1);
3627 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3628 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3630 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3631 boost::shared_ptr<Playlist> to_playlist = atv2->playlist();
3633 where = (nframes_t) (unit_to_frame (ix1) * speed);
3634 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3636 /* undo the previous hide_dependent_views so that xfades don't
3637 disappear on copying regions
3640 rv->get_time_axis_view().reveal_dependent_views (*rv);
3642 if (!drag_info.copy) {
3644 /* the region that used to be in the old playlist is not
3645 moved to the new one - we make a copy of it. as a result,
3646 any existing editor for the region should no longer be
3650 rv->hide_region_editor();
3651 rv->fake_set_opaque (false);
3653 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3654 from_playlist->remove_region ((rv->region()));
3655 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3659 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3661 copies.push_back (rv);
3664 latest_regionviews.clear ();
3666 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3667 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3668 to_playlist->add_region (new_region, where);
3669 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3672 if (!latest_regionviews.empty()) {
3673 new_selection.insert (new_selection.end(), latest_regionviews.begin(), latest_regionviews.end());
3676 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3677 was selected in all of them, then removing it from the playlist will have removed all
3678 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3679 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3680 corresponding regionview, and the selection is now empty).
3682 this could have invalidated any and all iterators into the region selection.
3684 the heuristic we use here is: if the region selection is empty, break out of the loop
3685 here. if the region selection is not empty, then restart the loop because we know that
3686 we must have removed at least the region(view) we've just been working on as well as any
3687 that we processed on previous iterations.
3689 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3690 we can just iterate.
3693 if (drag_info.copy) {
3696 if (selection->regions.empty()) {
3699 i = selection->regions.by_layer().begin();
3704 selection->set (new_selection);
3708 /* motion within a single track */
3710 list<RegionView*> regions = selection->regions.by_layer();
3712 if (drag_info.copy) {
3713 selection->clear_regions();
3716 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3720 if (rv->region()->locked()) {
3725 if (regionview_x_movement) {
3726 double ownspeed = 1.0;
3727 atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3729 if (atv && atv->get_diskstream()) {
3730 ownspeed = atv->get_diskstream()->speed();
3733 /* base the new region position on the current position of the regionview.*/
3735 double ix1, ix2, iy1, iy2;
3737 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3738 rv->get_canvas_group()->i2w (ix1, iy1);
3739 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3743 where = rv->region()->position();
3746 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3748 assert (to_playlist);
3752 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3754 if (drag_info.copy) {
3756 boost::shared_ptr<Region> newregion;
3757 boost::shared_ptr<Region> ar;
3759 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3760 newregion = RegionFactory::create (ar);
3762 /* XXX MIDI HERE drobilla */
3768 latest_regionviews.clear ();
3769 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3770 to_playlist->add_region (newregion, (nframes_t) (where * atv->get_diskstream()->speed()));
3773 if (!latest_regionviews.empty()) {
3774 // XXX why just the first one ? we only expect one
3775 atv->reveal_dependent_views (*latest_regionviews.front());
3776 selection->add (latest_regionviews);
3779 /* if the original region was locked, we don't care for the new one */
3781 newregion->set_locked (false);
3785 /* just change the model */
3787 rv->region()->set_position (where, (void*) this);
3793 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3795 if (drag_info.copy) {
3796 copies.push_back (rv);
3804 commit_reversible_command ();
3807 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3813 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3815 /* Either add to or set the set the region selection, unless
3816 this is an alignment click (control used)
3819 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
3820 TimeAxisView* tv = &rv.get_time_axis_view();
3821 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3823 if (atv && atv->is_audio_track()) {
3824 speed = atv->get_diskstream()->speed();
3827 nframes64_t where = get_preferred_edit_position();
3831 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
3833 align_region (rv.region(), SyncPoint, (nframes_t) (where * speed));
3835 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
3837 align_region (rv.region(), End, (nframes_t) (where * speed));
3841 align_region (rv.region(), Start, (nframes_t) (where * speed));
3848 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3854 nframes_t frame_rate;
3861 switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3862 case AudioClock::BBT:
3863 session->bbt_time (frame, bbt);
3864 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3867 case AudioClock::SMPTE:
3868 session->smpte_time (frame, smpte);
3869 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3872 case AudioClock::MinSec:
3873 /* XXX this is copied from show_verbose_duration_cursor() */
3874 frame_rate = session->frame_rate();
3875 hours = frame / (frame_rate * 3600);
3876 frame = frame % (frame_rate * 3600);
3877 mins = frame / (frame_rate * 60);
3878 frame = frame % (frame_rate * 60);
3879 secs = (float) frame / (float) frame_rate;
3880 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3884 snprintf (buf, sizeof(buf), "%u", frame);
3888 if (xpos >= 0 && ypos >=0) {
3889 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3892 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3894 show_verbose_canvas_cursor ();
3898 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3905 nframes_t distance, frame_rate;
3907 Meter meter_at_start(session->tempo_map().meter_at(start));
3913 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3914 case AudioClock::BBT:
3915 session->bbt_time (start, sbbt);
3916 session->bbt_time (end, ebbt);
3919 /* XXX this computation won't work well if the
3920 user makes a selection that spans any meter changes.
3923 ebbt.bars -= sbbt.bars;
3924 if (ebbt.beats >= sbbt.beats) {
3925 ebbt.beats -= sbbt.beats;
3928 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3930 if (ebbt.ticks >= sbbt.ticks) {
3931 ebbt.ticks -= sbbt.ticks;
3934 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3937 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3940 case AudioClock::SMPTE:
3941 session->smpte_duration (end - start, smpte);
3942 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3945 case AudioClock::MinSec:
3946 /* XXX this stuff should be elsewhere.. */
3947 distance = end - start;
3948 frame_rate = session->frame_rate();
3949 hours = distance / (frame_rate * 3600);
3950 distance = distance % (frame_rate * 3600);
3951 mins = distance / (frame_rate * 60);
3952 distance = distance % (frame_rate * 60);
3953 secs = (float) distance / (float) frame_rate;
3954 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3958 snprintf (buf, sizeof(buf), "%u", end - start);
3962 if (xpos >= 0 && ypos >=0) {
3963 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3966 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3968 show_verbose_canvas_cursor ();
3972 Editor::collect_new_region_view (RegionView* rv)
3974 latest_regionviews.push_back (rv);
3978 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3980 if (clicked_regionview == 0) {
3984 /* lets try to create new Region for the selection */
3986 vector<boost::shared_ptr<AudioRegion> > new_regions;
3987 create_region_from_selection (new_regions);
3989 if (new_regions.empty()) {
3993 /* XXX fix me one day to use all new regions */
3995 boost::shared_ptr<Region> region (new_regions.front());
3997 /* add it to the current stream/playlist.
3999 tricky: the streamview for the track will add a new regionview. we will
4000 catch the signal it sends when it creates the regionview to
4001 set the regionview we want to then drag.
4004 latest_regionviews.clear();
4005 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4007 /* A selection grab currently creates two undo/redo operations, one for
4008 creating the new region and another for moving it.
4011 begin_reversible_command (_("selection grab"));
4013 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
4015 XMLNode *before = &(playlist->get_state());
4016 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
4017 XMLNode *after = &(playlist->get_state());
4018 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4020 commit_reversible_command ();
4024 if (latest_regionviews.empty()) {
4025 /* something went wrong */
4029 /* we need to deselect all other regionviews, and select this one
4030 i'm ignoring undo stuff, because the region creation will take care of it
4032 selection->set (latest_regionviews);
4034 drag_info.item = latest_regionviews.front()->get_canvas_group();
4035 drag_info.data = latest_regionviews.front();
4036 drag_info.motion_callback = &Editor::region_drag_motion_callback;
4037 drag_info.finished_callback = &Editor::region_drag_finished_callback;
4041 drag_info.last_trackview = clicked_trackview;
4042 drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4043 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4045 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4049 Editor::cancel_selection ()
4051 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4052 (*i)->hide_selection ();
4054 selection->clear ();
4055 clicked_selection = 0;
4059 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4061 nframes_t start = 0;
4068 drag_info.item = item;
4069 drag_info.motion_callback = &Editor::drag_selection;
4070 drag_info.finished_callback = &Editor::end_selection_op;
4075 case CreateSelection:
4076 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4077 drag_info.copy = true;
4079 drag_info.copy = false;
4081 start_grab (event, selector_cursor);
4084 case SelectionStartTrim:
4085 if (clicked_trackview) {
4086 clicked_trackview->order_selection_trims (item, true);
4088 start_grab (event, trimmer_cursor);
4089 start = selection->time[clicked_selection].start;
4090 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4093 case SelectionEndTrim:
4094 if (clicked_trackview) {
4095 clicked_trackview->order_selection_trims (item, false);
4097 start_grab (event, trimmer_cursor);
4098 end = selection->time[clicked_selection].end;
4099 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
4103 start = selection->time[clicked_selection].start;
4105 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4109 if (selection_op == SelectionMove) {
4110 show_verbose_time_cursor(start, 10);
4112 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4117 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4119 nframes_t start = 0;
4122 nframes_t pending_position;
4124 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4125 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4127 pending_position = 0;
4130 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4131 snap_to (pending_position);
4134 /* only alter selection if the current frame is
4135 different from the last frame position (adjusted)
4138 if (pending_position == drag_info.last_pointer_frame) return;
4140 switch (selection_op) {
4141 case CreateSelection:
4143 if (drag_info.first_move) {
4144 snap_to (drag_info.grab_frame);
4147 if (pending_position < drag_info.grab_frame) {
4148 start = pending_position;
4149 end = drag_info.grab_frame;
4151 end = pending_position;
4152 start = drag_info.grab_frame;
4155 /* first drag: Either add to the selection
4156 or create a new selection->
4159 if (drag_info.first_move) {
4161 begin_reversible_command (_("range selection"));
4163 if (drag_info.copy) {
4164 /* adding to the selection */
4165 clicked_selection = selection->add (start, end);
4166 drag_info.copy = false;
4168 /* new selection-> */
4169 clicked_selection = selection->set (clicked_trackview, start, end);
4174 case SelectionStartTrim:
4176 if (drag_info.first_move) {
4177 begin_reversible_command (_("trim selection start"));
4180 start = selection->time[clicked_selection].start;
4181 end = selection->time[clicked_selection].end;
4183 if (pending_position > end) {
4186 start = pending_position;
4190 case SelectionEndTrim:
4192 if (drag_info.first_move) {
4193 begin_reversible_command (_("trim selection end"));
4196 start = selection->time[clicked_selection].start;
4197 end = selection->time[clicked_selection].end;
4199 if (pending_position < start) {
4202 end = pending_position;
4209 if (drag_info.first_move) {
4210 begin_reversible_command (_("move selection"));
4213 start = selection->time[clicked_selection].start;
4214 end = selection->time[clicked_selection].end;
4216 length = end - start;
4218 start = pending_position;
4221 end = start + length;
4226 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4227 start_canvas_autoscroll (1);
4231 selection->replace (clicked_selection, start, end);
4234 drag_info.last_pointer_frame = pending_position;
4235 drag_info.first_move = false;
4237 if (selection_op == SelectionMove) {
4238 show_verbose_time_cursor(start, 10);
4240 show_verbose_time_cursor(pending_position, 10);
4245 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4247 if (!drag_info.first_move) {
4248 drag_selection (item, event);
4249 /* XXX this is not object-oriented programming at all. ick */
4250 if (selection->time.consolidate()) {
4251 selection->TimeChanged ();
4253 commit_reversible_command ();
4255 /* just a click, no pointer movement.*/
4257 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4259 selection->clear_time();
4264 /* XXX what happens if its a music selection? */
4265 session->set_audio_range (selection->time);
4266 stop_canvas_autoscroll ();
4270 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4273 TimeAxisView* tvp = clicked_trackview;
4274 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4276 if (tv && tv->is_audio_track()) {
4277 speed = tv->get_diskstream()->speed();
4280 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
4281 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
4282 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
4284 //drag_info.item = clicked_regionview->get_name_highlight();
4285 drag_info.item = item;
4286 drag_info.motion_callback = &Editor::trim_motion_callback;
4287 drag_info.finished_callback = &Editor::trim_finished_callback;
4289 start_grab (event, trimmer_cursor);
4291 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4292 trim_op = ContentsTrim;
4294 /* These will get overridden for a point trim.*/
4295 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4296 /* closer to start */
4297 trim_op = StartTrim;
4298 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4306 show_verbose_time_cursor(region_start, 10);
4309 show_verbose_time_cursor(region_end, 10);
4312 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4318 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4320 RegionView* rv = clicked_regionview;
4321 nframes_t frame_delta = 0;
4322 bool left_direction;
4323 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4325 /* snap modifier works differently here..
4326 its' current state has to be passed to the
4327 various trim functions in order to work properly
4331 TimeAxisView* tvp = clicked_trackview;
4332 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4333 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4335 if (tv && tv->is_audio_track()) {
4336 speed = tv->get_diskstream()->speed();
4339 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4340 left_direction = true;
4342 left_direction = false;
4346 snap_to (drag_info.current_pointer_frame);
4349 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4353 if (drag_info.first_move) {
4359 trim_type = "Region start trim";
4362 trim_type = "Region end trim";
4365 trim_type = "Region content trim";
4369 begin_reversible_command (trim_type);
4371 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4372 (*i)->fake_set_opaque(false);
4373 (*i)->region()->freeze ();
4375 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4377 arv->temporarily_hide_envelope ();
4379 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4380 insert_result = motion_frozen_playlists.insert (pl);
4381 if (insert_result.second) {
4382 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4387 if (left_direction) {
4388 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4390 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4395 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4398 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4399 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4405 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4408 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4409 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4416 bool swap_direction = false;
4418 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4419 swap_direction = true;
4422 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4423 i != selection->regions.by_layer().end(); ++i)
4425 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4433 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4436 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4439 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4443 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4444 drag_info.first_move = false;
4448 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4450 boost::shared_ptr<Region> region (rv.region());
4452 if (region->locked()) {
4456 nframes_t new_bound;
4459 TimeAxisView* tvp = clicked_trackview;
4460 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4462 if (tv && tv->is_audio_track()) {
4463 speed = tv->get_diskstream()->speed();
4466 if (left_direction) {
4467 if (swap_direction) {
4468 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4470 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4473 if (swap_direction) {
4474 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4476 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4481 snap_to (new_bound);
4483 region->trim_start ((nframes_t) (new_bound * speed), this);
4484 rv.region_changed (StartChanged);
4488 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4490 boost::shared_ptr<Region> region (rv.region());
4492 if (region->locked()) {
4496 nframes_t new_bound;
4499 TimeAxisView* tvp = clicked_trackview;
4500 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4502 if (tv && tv->is_audio_track()) {
4503 speed = tv->get_diskstream()->speed();
4506 if (left_direction) {
4507 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4509 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4513 snap_to (new_bound, (left_direction ? 0 : 1));
4516 region->trim_front ((nframes_t) (new_bound * speed), this);
4518 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4522 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4524 boost::shared_ptr<Region> region (rv.region());
4526 if (region->locked()) {
4530 nframes_t new_bound;
4533 TimeAxisView* tvp = clicked_trackview;
4534 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4536 if (tv && tv->is_audio_track()) {
4537 speed = tv->get_diskstream()->speed();
4540 if (left_direction) {
4541 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4543 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4547 snap_to (new_bound);
4549 region->trim_end ((nframes_t) (new_bound * speed), this);
4550 rv.region_changed (LengthChanged);
4554 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4556 if (!drag_info.first_move) {
4557 trim_motion_callback (item, event);
4559 if (!selection->selected (clicked_regionview)) {
4560 thaw_region_after_trim (*clicked_regionview);
4563 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4564 i != selection->regions.by_layer().end(); ++i)
4566 thaw_region_after_trim (**i);
4567 (*i)->fake_set_opaque (true);
4571 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4573 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4576 motion_frozen_playlists.clear ();
4578 commit_reversible_command();
4580 /* no mouse movement */
4586 Editor::point_trim (GdkEvent* event)
4588 RegionView* rv = clicked_regionview;
4589 nframes_t new_bound = drag_info.current_pointer_frame;
4591 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4592 snap_to (new_bound);
4595 /* Choose action dependant on which button was pressed */
4596 switch (event->button.button) {
4598 trim_op = StartTrim;
4599 begin_reversible_command (_("Start point trim"));
4601 if (selection->selected (rv)) {
4603 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4604 i != selection->regions.by_layer().end(); ++i)
4606 if (!(*i)->region()->locked()) {
4607 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4608 XMLNode &before = pl->get_state();
4609 (*i)->region()->trim_front (new_bound, this);
4610 XMLNode &after = pl->get_state();
4611 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4617 if (!rv->region()->locked()) {
4618 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4619 XMLNode &before = pl->get_state();
4620 rv->region()->trim_front (new_bound, this);
4621 XMLNode &after = pl->get_state();
4622 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4626 commit_reversible_command();
4631 begin_reversible_command (_("End point trim"));
4633 if (selection->selected (rv)) {
4635 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4637 if (!(*i)->region()->locked()) {
4638 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4639 XMLNode &before = pl->get_state();
4640 (*i)->region()->trim_end (new_bound, this);
4641 XMLNode &after = pl->get_state();
4642 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4648 if (!rv->region()->locked()) {
4649 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4650 XMLNode &before = pl->get_state();
4651 rv->region()->trim_end (new_bound, this);
4652 XMLNode &after = pl->get_state();
4653 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4657 commit_reversible_command();
4666 Editor::thaw_region_after_trim (RegionView& rv)
4668 boost::shared_ptr<Region> region (rv.region());
4670 if (region->locked()) {
4674 region->thaw (_("trimmed region"));
4675 XMLNode &after = region->playlist()->get_state();
4676 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4678 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4680 arv->unhide_envelope ();
4684 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4689 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4690 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4694 Location* location = find_location_from_marker (marker, is_start);
4695 location->set_hidden (true, this);
4700 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4706 drag_info.item = item;
4707 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4708 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4710 range_marker_op = op;
4712 if (!temp_location) {
4713 temp_location = new Location;
4717 case CreateRangeMarker:
4718 case CreateTransportMarker:
4719 case CreateCDMarker:
4721 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4722 drag_info.copy = true;
4724 drag_info.copy = false;
4726 start_grab (event, selector_cursor);
4730 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4735 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4737 nframes_t start = 0;
4739 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4741 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4742 snap_to (drag_info.current_pointer_frame);
4745 /* only alter selection if the current frame is
4746 different from the last frame position.
4749 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4751 switch (range_marker_op) {
4752 case CreateRangeMarker:
4753 case CreateTransportMarker:
4754 case CreateCDMarker:
4755 if (drag_info.first_move) {
4756 snap_to (drag_info.grab_frame);
4759 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4760 start = drag_info.current_pointer_frame;
4761 end = drag_info.grab_frame;
4763 end = drag_info.current_pointer_frame;
4764 start = drag_info.grab_frame;
4767 /* first drag: Either add to the selection
4768 or create a new selection.
4771 if (drag_info.first_move) {
4773 temp_location->set (start, end);
4777 update_marker_drag_item (temp_location);
4778 range_marker_drag_rect->show();
4779 range_marker_drag_rect->raise_to_top();
4785 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4786 start_canvas_autoscroll (1);
4790 temp_location->set (start, end);
4792 double x1 = frame_to_pixel (start);
4793 double x2 = frame_to_pixel (end);
4794 crect->property_x1() = x1;
4795 crect->property_x2() = x2;
4797 update_marker_drag_item (temp_location);
4800 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4801 drag_info.first_move = false;
4803 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4808 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4810 Location * newloc = 0;
4814 if (!drag_info.first_move) {
4815 drag_range_markerbar_op (item, event);
4817 switch (range_marker_op) {
4818 case CreateRangeMarker:
4819 case CreateCDMarker:
4821 begin_reversible_command (_("new range marker"));
4822 XMLNode &before = session->locations()->get_state();
4823 session->locations()->next_available_name(rangename,"unnamed");
4824 if (range_marker_op == CreateCDMarker) {
4825 flags = Location::IsRangeMarker|Location::IsCDMarker;
4828 flags = Location::IsRangeMarker;
4830 newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
4831 session->locations()->add (newloc, true);
4832 XMLNode &after = session->locations()->get_state();
4833 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4834 commit_reversible_command ();
4836 range_bar_drag_rect->hide();
4837 range_marker_drag_rect->hide();
4841 case CreateTransportMarker:
4842 // popup menu to pick loop or punch
4843 new_transport_marker_context_menu (&event->button, item);
4848 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4850 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
4855 start = session->locations()->first_mark_before (drag_info.grab_frame);
4856 end = session->locations()->first_mark_after (drag_info.grab_frame);
4858 if (end == max_frames) {
4859 end = session->current_end_frame ();
4863 start = session->current_start_frame ();
4866 switch (mouse_mode) {
4868 /* find the two markers on either side and then make the selection from it */
4869 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4873 /* find the two markers on either side of the click and make the range out of it */
4874 selection->set (0, start, end);
4883 stop_canvas_autoscroll ();
4889 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4891 drag_info.item = item;
4892 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4893 drag_info.finished_callback = &Editor::end_mouse_zoom;
4895 start_grab (event, zoom_cursor);
4897 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4901 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4906 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4907 snap_to (drag_info.current_pointer_frame);
4909 if (drag_info.first_move) {
4910 snap_to (drag_info.grab_frame);
4914 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4916 /* base start and end on initial click position */
4917 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4918 start = drag_info.current_pointer_frame;
4919 end = drag_info.grab_frame;
4921 end = drag_info.current_pointer_frame;
4922 start = drag_info.grab_frame;
4927 if (drag_info.first_move) {
4929 zoom_rect->raise_to_top();
4932 reposition_zoom_rect(start, end);
4934 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4935 drag_info.first_move = false;
4937 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4942 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4944 if (!drag_info.first_move) {
4945 drag_mouse_zoom (item, event);
4947 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4948 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4950 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4953 temporal_zoom_to_frame (false, drag_info.grab_frame);
4955 temporal_zoom_step (false);
4956 center_screen (drag_info.grab_frame);
4964 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4966 double x1 = frame_to_pixel (start);
4967 double x2 = frame_to_pixel (end);
4968 double y2 = full_canvas_height - 1.0;
4970 zoom_rect->property_x1() = x1;
4971 zoom_rect->property_y1() = 1.0;
4972 zoom_rect->property_x2() = x2;
4973 zoom_rect->property_y2() = y2;
4977 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4979 drag_info.item = item;
4980 drag_info.motion_callback = &Editor::drag_rubberband_select;
4981 drag_info.finished_callback = &Editor::end_rubberband_select;
4983 start_grab (event, cross_hair_cursor);
4985 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4989 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4996 /* use a bigger drag threshold than the default */
4998 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
5002 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5003 if (drag_info.first_move) {
5004 snap_to (drag_info.grab_frame);
5006 snap_to (drag_info.current_pointer_frame);
5009 /* base start and end on initial click position */
5011 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5012 start = drag_info.current_pointer_frame;
5013 end = drag_info.grab_frame;
5015 end = drag_info.current_pointer_frame;
5016 start = drag_info.grab_frame;
5019 if (drag_info.current_pointer_y < drag_info.grab_y) {
5020 y1 = drag_info.current_pointer_y;
5021 y2 = drag_info.grab_y;
5023 y2 = drag_info.current_pointer_y;
5024 y1 = drag_info.grab_y;
5028 if (start != end || y1 != y2) {
5030 double x1 = frame_to_pixel (start);
5031 double x2 = frame_to_pixel (end);
5033 rubberband_rect->property_x1() = x1;
5034 rubberband_rect->property_y1() = y1;
5035 rubberband_rect->property_x2() = x2;
5036 rubberband_rect->property_y2() = y2;
5038 rubberband_rect->show();
5039 rubberband_rect->raise_to_top();
5041 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5042 drag_info.first_move = false;
5044 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5049 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5051 if (!drag_info.first_move) {
5053 drag_rubberband_select (item, event);
5056 if (drag_info.current_pointer_y < drag_info.grab_y) {
5057 y1 = drag_info.current_pointer_y;
5058 y2 = drag_info.grab_y;
5061 y2 = drag_info.current_pointer_y;
5062 y1 = drag_info.grab_y;
5066 Selection::Operation op = Keyboard::selection_type (event->button.state);
5069 begin_reversible_command (_("rubberband selection"));
5071 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5072 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5074 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5078 commit_reversible_command ();
5082 selection->clear_tracks();
5083 selection->clear_regions();
5084 selection->clear_points ();
5085 selection->clear_lines ();
5088 rubberband_rect->hide();
5093 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5095 using namespace Gtkmm2ext;
5097 ArdourPrompter prompter (false);
5099 prompter.set_prompt (_("Name for region:"));
5100 prompter.set_initial_text (clicked_regionview->region()->name());
5101 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5102 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5103 prompter.show_all ();
5104 switch (prompter.run ()) {
5105 case Gtk::RESPONSE_ACCEPT:
5107 prompter.get_result(str);
5109 clicked_regionview->region()->set_name (str);
5117 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5119 drag_info.item = item;
5120 drag_info.motion_callback = &Editor::time_fx_motion;
5121 drag_info.finished_callback = &Editor::end_time_fx;
5125 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5129 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5131 RegionView* rv = clicked_regionview;
5133 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5134 snap_to (drag_info.current_pointer_frame);
5137 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5141 if (drag_info.current_pointer_frame > rv->region()->position()) {
5142 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5145 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5146 drag_info.first_move = false;
5148 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5152 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5154 clicked_regionview->get_time_axis_view().hide_timestretch ();
5156 if (drag_info.first_move) {
5160 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5161 /* backwards drag of the left edge - not usable */
5165 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5166 #ifdef USE_RUBBERBAND
5167 float percentage = (float) ((double) newlen / (double) clicked_regionview->region()->length());
5169 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5172 begin_reversible_command (_("timestretch"));
5174 // XXX how do timeFX on multiple regions ?
5177 rs.add (clicked_regionview);
5179 if (time_stretch (rs, percentage) == 0) {
5180 session->commit_reversible_command ();
5185 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
5187 /* no brushing without a useful snap setting */
5190 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
5193 switch (snap_mode) {
5195 return; /* can't work because it allows region to be placed anywhere */
5200 switch (snap_type) {
5208 /* don't brush a copy over the original */
5210 if (pos == rv->region()->position()) {
5214 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
5216 if (atv == 0 || !atv->is_audio_track()) {
5220 boost::shared_ptr<Playlist> playlist = atv->playlist();
5221 double speed = atv->get_diskstream()->speed();
5223 XMLNode &before = playlist->get_state();
5224 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
5225 XMLNode &after = playlist->get_state();
5226 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5228 // playlist is frozen, so we have to update manually
5230 playlist->Modified(); /* EMIT SIGNAL */
5234 Editor::track_height_step_timeout ()
5237 struct timeval delta;
5239 gettimeofday (&now, 0);
5240 timersub (&now, &last_track_height_step_timestamp, &delta);
5242 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
5243 current_stepping_trackview = 0;